淺析Vue原理(部分源碼解析)


響應式

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); 

模板解析

  1. 本質:字符串
  2. 有邏輯: 例如v-if、v-for 等
  3. html模版是靜態的,Vue模板是動態
  4. 模板必須轉換成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 整個工作流程

  1.  將模板解析成Js,即render函數
  2.    監聽模板,通過MVVM響應式綁定model,並完成監聽
  3.    render函數渲染成Virtual Node
  4.    初次渲染完成DOM節點的創建,再次渲染新舊Vittual Node對比

源碼地址

https://github.com/10086XIAOZHANG/VirtualDOMDemo  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM