響應式
Object.defineProperty
Object.defineProperty(obj, prop, descriptor) // 對象、屬性、描述符
Object.defineProperty是es5新加的給對象屬性設置描述符的方法,可以用來監聽屬性值的變化
var obj ={};
var _name ='張三'
Object.defineProperty(obj,'name',{
get:function () {
return _name;
},
set:function (value) {
_name=value;
}
})
調用方式:
obj.name ="里斯"; alert(obj.name);
模擬Vue響應式(data的屬性代理到vm上)
var vm= {};
var data= {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
};
var i;
for (i in data){
if(data.hasOwnProperty(i)){
(function(i){ // 獨立函數作用域
Object.defineProperty(vm,i,{ //將data對象的屬性代理到vm
get: function () {
return data[i];
},
set:function (newVal) {
data[i]=newVal;
}
})
}(i))
}
}
vm.items[0].message='張三'
console.log(vm.items);
模板解析
- 本質:字符串
- 有邏輯: 例如v-if、v-for 等
- html模版是靜態的,Vue模板是動態
- 模板必須轉換成JS函數(render函數),進而轉換成html渲染頁面
with
//模板
function UserInfo() {
this.name = "kobe bryant";
this.age = "28";
this.gender = "boy";
}
var people=new UserInfo();
function fn(){
with(people)
{
var str = "姓名: " + name + "\n";
str += "年齡:" + age + "\n";
str += "性別:" + gender;
alert(str);
}
}
fn();
render 函數
<div class="main" :class="bindClass">
<div>{{text}}</div>
<div>hello world</div>
<div v-for="(item, index) in arr">
<p>{{item.name}}</p>
<p>{{item.value}}</p>
<p>{{index}}</p>
<p>---</p>
</div>
<div v-if="text">
{{text}}
</div>
<div v-else></div>
</div>
Vue 源碼將HTML string 轉換成AST

模版轉換成js
with(this){
return _c( 'div',
{
/*static class*/
staticClass:"main",
/*bind class*/
class:bindClass
},
[
_c( 'div', [_v(_s(text))]),
_c('div',[_v("hello world")]),
/*這是一個v-for循環*/
_l(
(arr),
function(item,index){
return _c( 'div', //_c創建標簽
[_c('p',[_v(_s(item.name))]), //_v 創建文本; _s 轉換成字符串
_c('p',[_v(_s(item.value))]),
_c('p',[_v(_s(index))]),
_c('p',[_v("---")])]
)
}
),
/*這是v-if*/
(text)?_c('div',[_v(_s(text))]):_c('div',[_v("no text")])],
2
)
}
其中,this 即使Vue構造函數對象(假定為vm),item即this.item,也是data中的item
針對於上一篇的隨筆:淺談Jquery和常用框架Vue變化,我們來解析一下它的render模板

<div id="example-1">
<input v-model="title" />
<button v-on:click="add">udto list</button>
<ul>
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
</div>
模板render解析
with (this) {
// noinspection JSAnnotator
return _c('div', {attrs: {"id": "example-1"}}, [ //div
_c('input', { //input
directives: [{
name: "model",
rawName: "v-model",
value: (title),
expression: "title"
}],
domProps: {"value": (title)}, //model 往 view
on: {
"input": function ($event) {
if ($event.target.composing) return;
title = $event.target.value //view 往 model
}
}
}), _v(" "), //換行
_c('button',
{
on: {
"click": add //綁定 methods add
}
},
[
_v("udto list") //文本子節點
]
), _v(" "), //換行
_c('ul',
_l((items), function (item) { // <li v-for="item in items"> _l v-for
return _c('li',
[
_v("\n " + _s(item.message) + "\n ") //item 對應 vm.item 即 data中的item
]
)
}))
])
}
渲染
從上面例子可以看出,vue通過借鑒改造snabbdom,h函數返回的vNode,vm._c返回的也是vNode,從Vue源碼中也驗證了這一點,從下面Vue源碼看出Vue是通過updateComponent 完成render渲染

var prevVnode = vm._vnode; //舊的vnode,initial render 第一次渲染 vnode 沒有存在,裝載在容器中,全部渲染
// updates
vm.$el = vm.__patch__(prevVnode, vnode); //第二次 新舊vNode對比

Vue 整個工作流程
- 將模板解析成Js,即render函數
- 監聽模板,通過MVVM響應式綁定model,並完成監聽
- render函數渲染成Virtual Node
- 初次渲染完成DOM節點的創建,再次渲染新舊Vittual Node對比
