如何理解MVVM?
MVVM是由MVC创新而来。
MVC(Model,View,Controller)
- M - Model 数据
- V - View 视图、界面
- C - Controller 控制器、逻辑处理
MVVM(Model,View,ViewMOdel)
- M - Model 模型、数据
- V - View 视图、模板(视图和模型是分离的)
- VM - ViewModel 连接器,连接Model和View
MVVM框架实现的三大要素?
- 响应式:vue如何监听到data的每个属性变化?
- 模板引擎:vue的模板如何被解析,指令如何处理?
- 渲染:vue的模板如何被渲染成html?以及渲染过程
vue中如何实现响应式?
- 通过Object.defineProperty
- 将data的属性代理到vm上
var obj = {}
var _name = 'zhangsan'
Object.defineProperty(obj, '_name', {
get: function(){
console.log('get', _name) // 监听
return _name
},
set: function(newValue){
console.log('set', _name) // 监听
return _name = newValue
}
})
模拟vue
var vm = {}
var data = {
name: 'zhangsan',
age: 20
}
var key, value
for(key in data){
(function(){
Object.defineProperty(vm, key, {
get: function(){
console.log('get', key)
return data[key]
},
set: function(newValue){
console.log('set', newValue)
return data[key] = newValue
}
})
})(key)
}
vue中如何解析模板?
模板是什么?
- 本质:字符串
- 有逻辑,如v-if,v-for等,可以嵌入JS变量
- 与html格式很像,但有很大区别(html是静态的,vue模板是动态的)
- 最终还要转换为html来显示
- 模板最终必须转换成JS代码,因为:
- 有逻辑(v-if, v-for),必须用JS才能实现
- 转换为html渲染页面,必须用JS才能实现
- 因此,模板最重要转换成一个JS函数(render函数)
render函数
// 模板中
<div id="app">
<p>{{price}}</p>
</div>
var vm = new Vue({
el: '#app',
data: {
price: 100
}
})
// render函数中
witch(this){
return _c(
'div',
{
attrs: {"id": "app"}
},
[
_c('p', [_v(_s(price))])
]
)
}
- 模板中所有信息都包含在了render函数中
- this即vm
- price即this.price即vm.price,即data中的price
- _c即this._c即vm._c
render函数与vdom
- vm._c其实就相当于snabbdom中的h函数
- render函数执行之后,返回的是vnode
- updateComponent中实现了vdom的patch
- 页面首次渲染执行updateComponent
- data中每次修改属性,执行updateComponent
vm._update(vnode){
const prevVnode = vm._vnode
vm._vnode = vnode
if(!prevVnode){
vm.$el = vm.__patch__(vm.$el, vnode)
} else {
vm.$el = vm.__patch__(prevVnode, vnode)
}
}
function updateComponent(){
// vm._render即上面的render函数,返回vnode
vm._update(vm._render())
}
vue的整个实现流程
(1)解析模板成render函数
- with的用法
- 模板中的所有信息都被render函数包含
- 模板中用到的data中的属性,都变成了JS变量
- 模板中的v-model,v-for,v-on都变成了JS逻辑
- render函数返回vnode
(2)响应式开始监听
- 通过Object.defineProperty监听到get,set
- 将data的属性代理到vm上
(3)首次渲染,显示页面,且绑定依赖
- 初次渲染,执行updateComponent,执行vm._render()
- 执行render函数,会访问到vm.list和vm.title
- 会被响应式的get方法监听到
- 执行updateComponent,会走到vdom的patch 方法
- patch将vnode渲染成DOM,初次渲染完成
为何要监听get,直接监听set不行吗?
- data中有很多属性,有些被用到,有些可能不被用到
- 被用到的会走到get,不会用到的不会走到get
- 未走到get中的属性,set的时候我们也无需关心
- 避免不必要的重复渲染
(4)data属性变化,触发rerender
- 修改属性,被响应式的set监听到
- set中执行updateComponent
- updateComponent会重新执行vm._render()
- 生成的vnode和prevVnode,通过patch进行对比
- 渲染到html中