钩子函数在beforeCreate之前,主要是初始化一些vm的属性,initState主要为定义的data属性进行obsever以及处理一些props、watch和computed
Vue官网中的约束源码解释 -- 生命周期 · Issue #12 · muwoo/blogs
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, beforeCreate)
initInjections(vm) // resolve injections before data/props
initState(vm)
nitProvide(vm) // resolve provide after data/props
callHook(vm, created)
在beforeCreate和created之间,进行数据观测(data observer) ,也就是在这个时候开始监控data中的数据变化,同时初始化event/watcher 事件配置在created 和 beforeMount之间el选项的有无对生命周期过程的影响。首先系统会判断对象中有没有el选项,有el选项,则继续编译过程,没有el选项,则停止编译,也意味着暂时停止了生命周期,直到vm.$mount(el)template参数选项的有无对生命周期的影响。1.如果Vue实例对象中有template参数选项,则将其作为模板编译成render函数,2.如果没有template参数选项,则将外部的HTML作为模板编译(template),也就是说,template参数选项的优先级要比外部的HTML高,3.如果1,2条件都不具备,则报错为什么判断el要发生在判断template前面呢,Vue需要通过el的“选择器”找到对应的template在Vue中,有render函数这个选项,它以createElement作为参数,做渲染操作。当然你也可以不调用createElement,而直接嵌入JSX render选项参数比template更接近Vue解析器!所以综合排列如下:render函数选项 > template参数 > 外部HTML
new Vue({
el: #demo,
render (createElement) {
return (....)
}
})
Vue的编译过程——把模板编译成 render 函数,beforeMount和mounted钩子函数间的生命周期,el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用mounted钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。beforeMount 时 vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,数据值还未替换,mounted 时 会被替换为正确值beforeUpdate钩子函数和updated钩子函数间的生命周期,适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。重渲染(调用这两个钩子函数)的前提是被更改的数据已经被写入模板中beforeDestroy和destroyed钩子函数间的生命周期,beforeDestroy钩子函数在实例销毁之前调用。在这一步,实例仍然完全可用。destroyed钩子函数在Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
模板写在template参数选项
new Vue({
el: #app,
template: <div id="app"><p>模板在templated参数中找到了哟~</p></div>
})
外部HTML
<div id="app"><p>模板是在外部HTML中找到的~</p></div>
new Vue({
el: #app
})
Vue前端开发规范
组件名为多个单词
组件名应该始终是多个单词的,根组件 App 除外。
name 最好必须填写
export default {
// 正例
name: TodoItem,
// 反例
name: Todo,
}
组件数据
组件的定义只接受function
必须是返回一个对象的函数,对象必须是纯粹的对象 (含有零个或多个的 key/value 对)
// 正例
export default {
data () {
return {
foo: bar
}
}
}
// 在一个 Vue 的根实例上直接使用对象是可以的,
// 因为只存在一个这样的实例。
new Vue({
data: {
foo: bar
}
})
// 反例
export default {
data: {
foo: bar
}
}
Prop定义
一般只获取使用,不要对其重新赋值
props: {
status: String
}
// 更好的做法!
props: {
status: {
type: String,
required: true,
validator: function (value) {
return [
syncing,
synced,
version-conflict,
error
].indexOf(value) !== -1
}
}
}
// 反例
props: [status]
为v-for设置键值
建议尽可能在使用v-for时提供key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
<ul>
<li
v-for="todo in todos"
:key="todo.id"
>
{{ todo.text }}
</li>
</ul>
watch定义
当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的
watch:{
// 可用字符串 监听对象属性
a: {
handle(val) {},
// 深度监听
deep: true,
// 立即执行
immediately: true
}
}
避免 v-if 和 v-for 用在一起
当v-if与v-for一起使用时,v-for具有比v-if更高的优先级。
// 正例
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
为组件样式设置作用域
class 类名(使用 BEM 约定) 使用创建 独立作用域, 或者用scoped 创建
<template>
<button class="c-Button c-Button--close">X</button>
</template>
<!-- 使用 BEM 约定 -->
<style>
.c-Button {
border: none;
border-radius: 2px;
}
.c-Button--close {
background-color: red;
}
</style>
<style scoped>
.button {
border: none;
border-radius: 2px;
}
组件文件
只要有能够拼接文件的构建系统,就把每个组件单独分成文件,当你需要编辑一个组件或查阅一个组件的用法时,可以更快速的找到它。
components/
|- TodoList.vue
|- TodoItem.vue
紧密耦合的组件名
和父组件紧密耦合的子组件应该以父组件名作为前缀命名。
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
多个特性的元素
多个特性的元素应该分多行撰写,每个特性一行。
<img
src="https://vuejs.org/images/logo.png"
alt="Vue Logo"
>
: key 属性的使用
keep-alive主要是为了缓存
当创建和编辑的页面使用的是同一个component,默认情况下当这两个页面切换时并不会触发vue的created或者mounted钩子,官方说你可以通过watch $route的变化来做处理,但其实说真的还是蛮麻烦的。后来发现其实可以简单的在 router-view上加上一个唯一的key,来保证路由切换时都会重新渲染触发钩子了。这样简单的多了
<keep-alive>
<router-view :key="key"></router-view>
</keep-alive>
computed: {
key() {
// 或者 :key="$route.path" 只要保证key唯一就可以了
return this.$route.name !== undefined? this.$route.name + +new Date(): this.$route + +new Date()
}
}
简单的计算属性
简单的属性计算,
计算属性默认只有 getter ,不过在需要时你也可以提供一个 setter :
计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值
computed: {
basePrice: function () {
return this.manufactureCost / (1 - this.profitMargin)
},
discount: function () {
return this.basePrice * (this.discountPercent || 0)
},
finalPrice: function () {
return this.basePrice - this.discount
}
}
//
computed: {
price: function () {
var basePrice = this.manufactureCost / (1 - this.profitMargin)
return (
basePrice -
basePrice * (this.discountPercent || 0)
)
}
}
vs methods 每当触发重新渲染时,调用方法将总会再次执行函数
vs watch 命令式且重复的
<div id="demo">{{ fullName }}</div>
// watch
var vm = new Vue({
el: #demo,
data: {
firstName: Foo,
lastName: Bar,
fullName: Foo Bar
},
watch: {
firstName: function (val) {
this.fullName = val + + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + + val
}
}
})
// computed
var vm = new Vue({
el: #demo,
data: {
firstName: Foo,
lastName: Bar
},
computed: {
fullName: function () {
return this.firstName + + this.lastName
}
}
})
带引号的特性值
<app-sidebar :style="{ width: sidebarWidth + px }"></app-sidebar>
//
<app-sidebar :style={width:sidebarWidth+px}></app-sidebar>
单文件组件的顶级元素的顺序
单文件组件应该总是让<script>、<template> 和 <style> 标签的顺序保持一致。且 <style> 要放在最后,因为另外两个标签至少要有一个。
<!-- ComponentA.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
scoped 中的元素选择器
在 scoped 样式中,类选择器比元素选择器更好,因为大量使用元素选择器是很慢的
<template>
<button class="btn btn-close">X</button>
</template>
<style scoped>
.btn-close {
background-color: red;
}
</style>
vue组件划分
页面组件的定义仅仅是整张页面的统筹,页面组件也是由一个个功能组件堆叠而成的
功能组件的定义为,他的展示只受父组件通过props组件传值过来的数据影响,他的所有状态均为内部私有,与外部组件的交互有vue提供的props以及事件分发vm.$emit()。因为只有这也组件才能保证其完整的内部状态,在复用时不受其他因素的影响。
划分页面组件最简单的方式是由路由划分,每一个路由对应一个页面组件。 功能组件的划分需要对页面组件的功能进行分析分类。功能组件的划分可能根据不同人的理解各不相同,但是原则只有一个:
功能组件应该类似面向对象编程一样,一个功能组件应该只是完成一个功能
页面组件是无法复用的组件,功能组件是可以复用的组件。
拿销项2.0 的布局来举个例子
功能组件可能有以下几个:
1. 顶部组件, 里边包含 , nav 组件, 下拉组件等
2. 左侧菜单组件:
3. table组件:
4. 分页组件:
5. 按钮组件:
6. 各种form 表单组件
7. 底部组件
文末彩蛋:VSCode拓展插件推荐(HTML、Node、Vue、React开发均适用)
https://github.com/varHarrie/varharrie.github.io/issues/10
vscode 相关配置
{
// 控制字体系列。
"editor.fontFamily": "Consolas, Courier New, monospace,宋体",
// 以像素为单位控制字号。
"editor.fontSize": 18,
// 控制选取范围是否有圆角
"editor.roundedSelection": false,
// 建议小组件的字号
"editor.suggestFontSize": 16,
// 在“打开的编辑器”窗格中显示的编辑器数量。将其设置为 0 可隐藏窗格。
"explorer.openEditors.visible": 0,
// 是否已启用自动刷新
"git.autorefresh": false,
// 是否启用了自动提取。
"git.autofetch": false,
// 以像素为单位控制终端的字号,这是 editor.fontSize 的默认值。
"terminal.integrated.fontSize": 14,
// 控制终端游标是否闪烁。
"terminal.integrated.cursorBlinking": true,
// 一个制表符等于的空格数。该设置在 `editor.detectIndentation` 启用时根据文件内容进行重写。
"editor.tabSize": 2,
// Tab Size
"beautify.tabSize": 2
}
常用插件
Beautify;
git blame
git history
open in browser
vetur
vue 2 snippets
Vue VSCode Snippets
eslint
不好意思!耽误你的十分钟,让MVVM原理还给你 - 掘金