一、说一下对 MVVM 的理解
Model - 模型、数据
View - 视图、模板(视图和模型是分离的)
ViewModel - 连接 Model 和 View
Vue三要素
一、响应式:vue 如何监听到 data 的每个属性变化?
1、什么是响应式
修改 data 属性之后,vue 立刻监听到
data 属性被代理到 vm 上
2、Vue如何实现响应式(Object.defineProperty)
var vm = {}
var data = {
name: 'zhangsan',
age: 20
}
var key, value
for (key in data) {
(function (key) {
Object.defineProperty(vm, key, {
get: function () {
console.log('get', data[key])
return data[key]
},
set: function (newVal) {
console.log('set', newVal)
data[key] = newVal
}
})
})(key)
}
二、模板引擎:vue 的模板如何被解析,指令如何处理?
1、模板是什么
<div id="app">
<h2>{{ news.title }}</h2>
<ul>
<li v-for="(value,index) in news.list">{{ value }}</li>
</ul>
</div>
本质:字符串
有逻辑,如v-if v-for等
与html格式很像,但有很大区别
最终还要转换为html来显示
模板最终必须转换成JS代码,因为:
有逻辑(v-if v-for),必须用JS才能实现
转换为html渲染页面,必须用JS才能实现
因此,模板最重要转换成一个JS函数(render函数)
<div id="app">{{ price }}</div>
var vm = new Vue({
el: '#app',
data: {
price: 100
}
})
function render() {
with (this) { // this就是vm
// _c 创建元素 (第一个参数:元素,第二个参数:元素属性,第三个参数:子元素)
// ƒ (a, b, c, d) { return createElement(vm, a, b, c, d, false); }
// _v创建一个文本节点
// ƒ createTextVNode(val) {
// return new VNode(undefined, undefined, undefined, String(val))
// }
// _s toString
// ƒ toString(val) {
// return val == null
// ? ''
// : typeof val === 'object'
// ? JSON.stringify(val, null, 2)
// : String(val)
// }
return _c(
'div',
{
attrs: { 'id': 'app' }
},
[
_c('p', [_v(_s(price))]) // 将price转换成字符串,再创建成p的文本节点
]
)
}
}
不使用with
function render1() {
return vm._c(
'div',
{
attrs: { 'id': 'app' }
},
[
vm._c('p', [vm._v(vm._s(price))])
]
)
}
补充:如何查看模板的render方法(vue.js非压缩版本源码)
三、渲染:vue 的模板如何被渲染成 html ?以及渲染过程
四、vue的整个实现流程
<div id="app">
<div>
<input type="text" v-model="title">
<button @click="add">submit</button>
</div>
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
</div>
var vm = new Vue({
el: '#app',
data: {
title: '',
list: []
},
methods: {
add() {
this.list.push(this.title)
this.title = ''
}
}
})
第一步:解析模板成render函数
with (this) {
return _c(
'div',
{ attrs: { "id": "app" } },
[
_c(
'div',
[
_c(
'input',
{
directives: [
{
name: "model",
rawName: "v-model",
value: (title),
expression: "title"
}
],
attrs: { "type": "text" },
domProps: {
"value": (title)
},
on: {
"input": function ($event) {
if ($event.target.composing) return;
title = $event.target.value
}
}
}
),
_v(" "),
_c(
'button',
{ on: { "click": add } },
[_v("submit")])
]
),
_v(" "),
_c(
'ul',
_l((list), function (item) { return _c('li', [_v(_s(item))]) }) // _l for循环
)
]
)
}
with的用法
模板中的所有信息都被render函数包含
模板中用到的data中的属性,都变成了JS变量
模板中的v-model v-for v-on 都变成了JS逻辑
render返回vnode
第二步:响应式开始监听
Object.defineProperty
第一步中,将data代理到vm上
第三步:首次渲染、显示页面、且绑定依赖
初次渲染,执行 updateComponent,执行 vm._render()
执行 render 函数,会访问到 vm.list vm.title
会被响应式的 get 方法监听到(后面详细讲)
执行 updateComponent ,会走到 vdom 的 patch 方法
patch 将 vnode 渲染成 DOM ,初次渲染完成
第四步:data属性变化,触发render
修改属性,被响应式的 set 监听到
set 中执行 updateComponent
updateComponent 重新执行 vm._render()
生成的 vnode 和 prevVnode ,通过 patch 进行对比
渲染到 html 中