1.vue data属性里面的getter和setter
data的每个属性都有两个相对应的get和set属性。
ES5的对象原型有两个新的属性__defineGetter__和__defineSetter__,专门用来给对象绑定get和set。可以这样书写:
vue双向绑定原理是由数据劫持结合发布者-订阅者模式实现的。
vue的数据劫持是通过Object.defineProperty()来对对象的setter和getter属性进行操作,在数据进行变动时,进行你想要的操作。
语法:Object.defineProperty(obj,prop,descriptor)
参数:
obj:要在其上定义属性的对象。
prop:要定义或修改的属性名称。
descriptor:将被定义或修改的属性描述。
返回值:
被传递给函数的对象。
也就是说他可以控制一个对象属性的一些特有操作,比如读写或是否可枚举等,这里组要看set和get。
2.vue.js计算属性computed(getter,setter)
在Vue中,computed的属性可以被视为是data一样,可以读取和设置,因此在computed中可以分成getter和setter,一般情况下没有setter,computed预设只有getter,也就是只能读取,不能改变设值。
vue.js计算属性默认只有getter,因为是默认值所以我们也常常忽略不写,如下代码:
完整写法:
计算属性getter的出发时间:
如果我们改变上边代码里的2个输入框的值firstName或则lastName,都会触发computed以及updated(),也就是说会执行:console.log('computed')和console.log('updated');
需要注意的是,不是说我们更改了getter里使用的变量,就会触发computed的更新,前提是computed里的值必须要在模板里面使用才行。如果把上面代码中的p标签注释掉,就算改变input的值也不会触发computed。
2.vue生命周期。
1new Vue{
router,
store,
//components: { App } vue1.0的写法
render: h => h(App) vue2.0的写法
}).$mount('#app')
1.首先需要了解这是 es 6 的语法,表示 Vue 实例选项对象的 render 方法作为一个函数,接受传入的参数 h 函数,返回 h(App) 的函数调用结果。
2.其次,Vue 在创建 Vue 实例时,通过调用 render 方法来渲染实例的 DOM 树。
3.最后,Vue 在调用 render 方法时,会传入一个 createElement 函数作为参数,也就是这里的 h 的实参是 createElement 函数,然后 createElement 会以 APP 为参数进行调用,关于 createElement 函数的参数说明参见:Element-Arguments。
先了解一下vue的虚拟dom:
Vitual DOM是一种虚拟dom技术,本质上是基于javascript实现的,相对于dom对象,javascript对象更简单,处理速度更快,dom树的结构,属性信息都可以很容易的用javascript对象表示:
Virtual DOM并没有完全实现DOM,最主要还是保留了Element之间的层次关系和一些基本属性。你给我一个数据,我根据数据生成一个全新的Virtual DOM,然后跟我上一次生成的Virtual DOM去diff,然后通过patch方法挂载。
我们可以通过javascript对象表示的树结构来构建真正的DOM树,当数据发声变化时,可以直接修改这个javascript对象,接着对比修改后的对象,记录下需要对页面做的dom操作,然后将其应用到真正的DOM树,实现视图的更新。
VNode生成最关键点是通过render函数,由2种生成方式,第一种是直接在vue对象的option种添加render字段。第二种是写一个模板或指定el跟元素,它会首先转换成模板,经过html语法解析器生成一个ast抽象语法树,对语法树做优化,然后把语法树转换成代码片段,最后通过代码片段生成function添加到option的render字段中。
ast语法优化过程,主要做了2件事:
1.会检测出静态的class名和attributes,这样它们在初始化渲染后永远不会再被对比了。
2.会检测出最大的静态子树,并且从渲染函数中萃取出来。这样在每次重渲染时,它们会直接重用相同的vnode,同时跳过比对。
creteelment方法的功能是给一个Vnode对象添加若干个Vnode,因为整个Vritual DOM是一种树状结构,每个节点都可能会有若干子节点。然后创建一个Vnode对象,如果是一个reserved tag(html,head等一些合法的html标签)则会创建普通的DOM Vnode,如果是一个component tag(通过vue注册的自定义component),则会创建Component Vnode对象,它的VnodeComponentOptions不为null。
function patch(oldVnode, vnode, hydrating, removeOnly, parentElm, refElm){
创建好Vnode,下一步就是要把虚拟dom渲染成真正的dom,是通过patch来实现。patch支持3个参数,其中oldNode是一个真实DOM或则一个Vnode对象,它表示当前的VNode,vnode是VNode对象类型,它表示待替换的VNode,hydration是bool类型,它表示是否直接使用服务器端渲染的DOM元素,下面流程图表示patch的运行逻辑:
patch运行逻辑看上去比较复杂,有2个方法createElm和patchVnode是生成dom的关键.
createElm方法会根据vnode的数据结构创建真实的DOM节点,如果vnode有children,则会遍历这些子节点,递归调用createElm方法,InsertedVnodeQueue是记录子节点创建顺序的队列,每创建一个DOM元素就会往这个队列中插入当前的VNode,当整个VNode对象全部转换成为真实的DOM树时,会依次调用这个队列中的VNode hook的insert方法
1.了解vue的$mount所作的工作大体分为3部:
1.如果你的option里面没有render函数,那么,通过complieToFunctions将HTML模板编译成可以生成VNode的Render函数。
2.new一个Watcher实例,触发updateComponent方法。
3.生成vnode,经过path,把vnode更新到dom上。
从上面的代码中可以看到,首先判断option里面有没有render函数,没有的话,进一步判断有没有template,没有的话就用dom元素的outerHTML。得到template以后干了什么呢?如下图:
我们可以看到,调用了complieToFunction将template转成render函数。这里面有两个过程:
将template解析成ast语法树。
通过ast语法树生成render函数。
下一步就开始mountConmponet了。
从上图可以看出,程序声明了一个updateComponent 方法,这个是将要被Watcher实例调用的更新组件的方法。
vm:当前的vm实例。
updateComponent 这个非常重要,用来在后面将vnode更新到dom上。
noop无意义的函数。
null option选项没有则为null。
true主要是用来判断是哪个watcher。因为computed计算属性和如果你要在options里面配置watch了同样也是使用了 new Watcher ,加上这个用以区别这三者。
if(isRenderWatcher) {
vm._watcher = this;
}
可以看到,如果声明这个watcher的上下文是用来渲染视图的,也就是说在mountComponent这里调用new Watcher的时候,才会把this赋值给_watcher。然后把 watcher push到 _watchers 里面,目的是等到组件销毁时顺便把watcher也销毁掉。
接下来,就是赋值给 getter , this.getter = expOrFn 。还记得刚才传过来的 updateComponent 函数么,没错,就是这个赋值给我 getter 。然后我们就到了:
我们可以看到,首先它执行的是 pushTarget(this) ,pushTarget(this) 代码如下:
也就是说如果当前有 Dep.target 的话,就把target放到 targetStack 里面,如果没有的话,就设为当前的target,也就是这个watcher。 接着,就是执行了它的 getter 属性,也就是刚刚传入 updateComponent 函数。
官方vue生命周期图:
从图可以看出在vue整个的生命周期中会有很多的钩子函数,提供给我们在vue生命周期不同的时刻进行操作。没一个组件或则实例都会经历一个完整的生命周期,总共分为三个阶段:初始化、运行中、销毁。
1.实例、组件通过new Vue()创建出来之后会初始化事件和生命周期,然后会执行beforeCreted钩子函数,这个时候数据还没有挂载,只是一个空壳,无法访问到数据和真实的dom,一般不做操作。
2.挂载数据,绑定事件等等,然后执行creted函数,这个时候已经可以只用到数据,也可以更改数据,在这里更改数据不会触发updated函数,在这里可以在渲染前倒数第二次更改数据的机会,不会触发其它钩子函数,一般可以在这里做初始数据的获取。
3.接下来开始找实例或则组建对应的模板,编译模板为虚拟dom放入到render函数中准备渲染,然后执行beforeMount钩子函数,在这个函数中虚拟dom已经创建完成,马上就要渲染,在这里可以更改数据,不会触发updated,在这里可以在渲染前最后一次更改数据的机会。
4.接下来开始render,渲染出真实dom,然后执行mounted钩子函数,此时,组件已经出现在页面中,数据、真实dom都已经处理好了,事件都已经挂载好了,可以在这里操作真实dom。
5.当组件或实例的数据更改之后,会立即执行beforeUpdate,然后vue的虚拟dom机制就会重新构建虚拟dom树利用diff算法进行对比后重新渲染。
6.当更新完成后,执行updated,数据已经更改完成,dom也重新render完成,可以操作更新后的虚拟dom。
7.当经过某种途径调用$destroy方法后,立即执行beforeDestroy,一般在这里做一些善后工作,例如清除计时器、清除非指令绑定的事件等等
8.组件的数据绑定、监听...去掉后只剩下dom空壳,这个时候,执行destroyed,在这里做善后工作也可以
先列出所有的钩子函数,再一一详解:
*beforeCreate
*created
*beforeMount
*mounted
*beforeUpdate
*updated
*beforeDsetroy
*destroyed