Vue2.x 的响应式/模版解析/渲染

date
Jan 3, 2019
slug
vue2.x-reactivity-template-render
status
Published
tags
Vue2.x
Vuejs
summary
Vue2.x 的响应式/模版解析/渲染
type
Post
对于使用 vue 的同学来说,响应式、模版解析/渲染应该都很熟悉,vue 开发离不开这些特性。用了 vue 挺长时间了,写写总结。

响应式

创建一个普通的对象:
let t = {
    name: 't1'
    age: 19
}
console.log(t.age) // 19 那么如何监听age属性的访问与设置呢?
在es5中加入了一个api叫 Object.defineProperty,该api允许创建对象的属性自定义 get/set 函数。修改上面代码:
let t = {}
let name = 't1'
Object.defineProperty(obj, 'name', {
	get() {
		console.log('get')
		return name
	},
	set(newVal) {
		console.log('set')
		name = newVal
	}
})
console.log(obj.name)
// get
// t1
obj.name = 'george'
// set
在vue中我们可以模拟一下,大概过程如下:
let vm = {}
let data = {
    name: 'lyn',
    age: 20
}

for(let v in data) {
    (function(v) {
        Object.defineProperty(vm, v, {
            get() {
        		return data[v]
        	},
        	set(newVal) {
        		data[v] = newVal
        	}
        })
    })(v)
}
为什么要将data的值全部代理至vm对象上?vue中就是这么写的,而且模版解析的时候会用到。

模板解析

我们经常在vue中使用vue模板,比如:
<div id="app">
  <input v-model="title">
  <button @click="add"></button>
</div>
<ul>
    <li v-for="v in list">{{ v }}</li>
</ul>
模板的本质是字符串,其特点有:
  • 包含 if/for 等逻辑指令
  • 动态的(动态生成 dom )
  • 最终转换为 html
综上,要将模板转换为 html ,只能使用 js 来操作。在 html/css/js 中,只有js能处理逻辑和渲染页面。

渲染

在当前文件引入vue文件,打开控制台,在源码中搜索code.render 在 return 前打印 code.render,观察模板被解析后生成的render函数,render函数最终返回vnode。如下:
with(this){  // this 就是 vm
    return _c(
        'div',
        {
            attrs:{"id":"app"}
        },
        [_c(
            'div',
            [
                _c(
                    'input',
                    {
                        directives:[
                        {
                       name:"model",
                       rawName:"v-model",
                       value:(title),  // vm.title 在这里就将model中的的值通过vm与view中的input数据联系了起来
                       expression:"title"
                            }
                        ],
                        domProps:{
                            "value":(title)
                        },
                        on:{
                            "input":function($event){
                          // 在这里可以发现v-model 其实在vue模版内被封装了进去
                                if($event.target.composing)return;
                                title=$event.target.value
                                在这里就将view中的input中的值通过vm与model中的数据联系了起来与上面相对应实现双向绑定
                            }
                        }
                    }
                ),
                _v(" "),
                // 因为html中俩个标签间我们换行所以这里是空
                _c(
                    'button',
                    {
                        on:{
                            "click":add
                            // 相当于 vm.add这里直接调用methods中定义的add函数
                        }
                    },
                    [_v("submit")]
                )
            ]),
        _v(" "),
        _c('div',
            [
                _c(
                    'ul',
                   //v-for这里使用内部定义的 _l函数来处理
                    _l((list),function(item){return _c('li',[_v(_s(item))])})
                )
            ]
        )]
    )
}
_c 创建标签,_v创建文本元素,_s toString(),这个_c 和 snabbdom 中的 h 函数很像,那vue中更新的操作会不会和 snabbdom 中的逻辑页相似呢? vue 中的更新函数:
vm._update(vnode) {
    const prevNode = vm._vnode
    vm._node = vnode
    if(!prevNode) {
        vm.$el = vm.__patch___(vm.$el, vnode)
    }else{
        vm.$el = vm.__patch__(prevNode, vnode)
    }
}

function updateComponent() {
    vm._update(vm._render())
    // vm._render 即生成上面包含 _c/_v/_s的模板解析函数
}
也就是说每次改变data,通过Object.defineProperty监听修改操作,一旦修改就调用 updateComponent 函数,利用vm._render方法生成新的 vnode。通过 vm.patch将vnode转化为html并更新至原生dom中。

总结

  • 解析模板生成render函数
  • 响应式开始监听
  • 首次渲染,显示页面且绑定依赖
  • data属性变化,再次渲染

© i7eo 2017 - 2022