一、计算属性和监视
1、computed
- 1、computed是计算属性,也就是依赖某个值或者props通过计算得来得数据;
- 2、 computed的值是在getter执行之后进行缓存的,只有在它依赖的数据发生变化,会重新调用getter来计算;
- 3、 不支持异步,当computed内有异步操作时无效,无法监听数据的变化;
2、watch
- 1、watch是监听器,可以监听某一个数据,然后执行相应的操作;
- 2、不支持缓存,数据变直接会触发相应的操作;
- 3、监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
- 4、支持异步操作;
3、computed 和watch 适用场景?
- 1.当多个属性影响一个属性的时候,建议用computed,比如:
fullName(){
return this.firstName + this.secondName
}
fullName是由firstName和secondName影响的,这种情况就适合computed;
最典型的栗子: 购物车商品结算的时候
- 3.当一条数据影响多条数据的时候就需要用watch。
栗子:搜索数据
二、生命周期函数(钩子)
1、vue每个组件都是独立的,每个组件都有一个属于它的生命周期,从一个组件创建、数据初始化、挂载、更新、销毁,这就是一个组件所谓的生命周期。在组件中具体的方法有beforeCreate、created、beforeMount,mounted(beforeUpdate,updated)、beforeDestroy、destroyed
new Vue()
- new Vue({}) 实例化vue
- Init Events & Lifecycle 初始化事件 与生命周期 数据观测 (data observer)和 event/watcher 事件配置之前
1.beforeCreate(创建前)
- 3.在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
- 4.Init injections & reactivity 初始化我们的依赖注入和开始响应。
2 created(创建后)
-
5.created 在实例创建完成后被立即调用。
在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
- Has ‘el’ option? 是否具有el元素?
Yes==>直接走到7
No==> when app.$mounted is called? 表示实例是否已经被挂载了?! - Has ‘template’ option? 表示template是否具有?!
Yes==> compile template into render function 检查template属性中是否有具体的组件?
No ==>compile el’outerHTML as template 询问检查template是否是一个HTML格式的字符串?
3 beforeMount(载入前)
- 8.beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用。
4 mounted(载入后)
- 9.create app.$el and replace ‘el’ with it. 挂载#app这个html标签,实现实例数据挂载。
5 beforeUpdate(更新前)
- 10.when data changes? 当数据发生更新时
- 11.beforeUpdate 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
6 updated(更新后)
- 12.Virtual DOM re-render and patch 虚拟dom重新渲染和打补丁
- 13.updated 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。
7 beforeDestroy(销毁前)
- 14.when app.$destroy() is called 当实例打算销毁时
- 15.beforeDestroy()实例销毁之前调用。在这一步,实例仍然完全可用。
- 16.Teardown watchers , child components and event listeners 卸载和销毁数据观察者,子组件和事件监听者
8 destroyed(销毁后)
- 17.Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
生命周期函数总结
10 Vue父子组件生命周期执行顺序
10.1. 加载渲染过程
->父beforeCreate -> 父created -> 父beforeMount
->子beforeCreate -> 子created -> 子beforeMount -> 子mounted
-> 父mounted
10.2.子组件更新过程
->父beforeUpdate
-> 子beforeUpdate -> 子updated
-> 父updated
10.3. 父组件更新过程
父beforeUpdate -> 父updated
10.4.销毁过程
-> 父beforeDestroy
-> 子beforeDestroy -> 子destroyed
-> 父destroyed
三、vue的原理
- 1、建立虚拟DOM Tree,通过document.createDocumentFragment(),遍历指定根节点内部节点,根据{{ prop }}、v-model等规则进行compile;
- 2、通过Object.defineProperty()进行数据变化拦截;
- 3、截取到的数据变化,通过发布者-订阅者模式,触发Watcher,从而改变虚拟DOM中的具体数据;
- 4、通过改变虚拟DOM元素值,从而改变最后渲染dom树的值,完成双向绑定.
完成数据的双向绑定在于Object.defineProperty()
四、vue的虚拟dom(Virtual DOM)
Virtual DOM 其实就是一棵以 JavaScript 对象( VNode 节点)作为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。最终可以通过一系列操作使这棵树映射到真实环境上。
简单来说,可以把Virtual DOM 理解为一个简单的JS对象,并且最少包含标签名( tag)、属性(attrs)和子元素对象( children)三个属性。不同的框架对这三个属性的命名会有点差别。
对于虚拟DOM,咱们来看一个简单的实例,就是下图所示的这个,详细的阐述了模板 → 渲染函数 → 虚拟DOM树 → 真实DOM的一个过程
let element={
tagName:'ul',//节点标签名
props:{//dom的属性,用一个对象存储键值对
id:'list'
},
children:[//该节点的子节点
{tagName:'li',props:{class:'item'},children:['aa']},
{tagName:'li',props:{class:'item'},children:['bb']},
{tagName:'li',props:{class:'item'},children:['cc']}
]
}
对应的html写法是:
<ul id='list'>
<li class='item'>aa</li>
<li class='item'>aa</li>
<li class='item'>aa</li>
</ul>
五、双向绑定实现
5.1 概念
vue是一个mvvm框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。这也算是vue的精髓之处了。值得注意的是,我们所说的数据双向绑定,一定是对于UI控件来说的,非UI控件不会涉及到数据双向绑定。 单向数据绑定是使用状态管理工具(如redux)的前提。如果我们使用vuex,那么数据流也是单项的
5.2双向绑定实现
Object.defineProperty()函数可以定义对象的属性相关描述符, 其中的set和get函数对于完成数据双向绑定起到了至关重要的作用
var obj = {
foo: 'foo'
}
const proxy = Object.defineProperty(obj, 'foo', {
get: function () {
console.log('将要读取obj.foo属性');
},
set: function (newVal) {
console.log('当前值为', newVal);
}
});
obj.foo; // 将要读取obj.foo属性
obj.foo = 'name'; // 当前值为 name
proxy.foo //
六、diff算法
根据真实DOM生成一颗virtual DOM,当virtual DOM某个节点的数据改变后会生成一个新的Vnode,然后Vnode和oldVnode作对比,发现有不一样的地方就直接修改在真实的DOM上,然后使oldVnode的值为Vnode。
diff的过程就是调用名为patch的函数,比较新旧节点,一边比较一边给真实的DOM打补丁。
总结:深度优先遍历树,比较只会在同层级进行, 不会跨层级比较,找出差异,通过打补丁patch方式更新树。