vue2-模板编译

295 阅读2分钟

vue模板template在静态html上新增了指令,插值,js表达式等,将html转为js代码片段。开发过程中用render代替template

with语法

with会改变{}内自由变量查找方式,将其当作obj属性查找,若找不到就error

vue template complier

将模板编译为render函数,也就是将模板转为js代码,render函数返回vnode,基于vnode再去执行diff算法,通过响应式更新vnode。每次数据变化生成新的vnode。

模板转为js代码实现指令,插值,js表达式。涉及到的变量都会从vue实例上查找,也就是data中的数据。

html是标签语言只能通过css控制显隐,需要将模板转为js才能实现指令/插值/js表达式,因此模板必须转为js代码,转换的过程称为模板编译。

举例:

通过complie函数将template模板编译成js代码,返回

with(this){return _c('p',[_v(_s(message))])}

this: vm实例,const vm = new Vue({})

{}:内部自由变量查找都变为查找this上的属性,data中的message挂在this实例上。

_c代表createElement,就是diff算法的h函数,返回vnode

_v代表createTextVNode

_s代表toString()

代码举例

const compiler = require('vue-template-compiler')
// 插值
// const template = `<p>{{message}}</p>`
// with (this) { return _c('p', [_v(_s(message))]) }


// 表达式
// const template = `<p>{{flag? message:'no'}}</p>`
// with(this){return _c('p',[_v(_s(flag? message:'no'))])}


// 属性和动态属性
// const template = `<div id="div1" class="container"><img :src="imgUrl"/></div>`
// with(this){return _c('div',
//  { staticClass: "container", attrs: { "id": "div1" } },
//  [_c('img', { attrs: { "src": imgUrl } })])}
// src是动态属性,imgUrl是data中的变量,通过this语法查找this.imgUrl
// id是class是静态属性字符串


// 条件
// const template = `
// <div>
// <p v-if="flag==='a'">A</p>
// <p v-else>B</p>
// </div>`
// with(this){return _c('div',[(flag==='a')?_c('p',[_v("A")]):_c('p',[_v("B")])])}
// 子节点变成三元表达式



// 循环
// const template = `
// <ul>
// <li v-for="item in list" :key="item.id">{{item.title}}</li>
// </ul>`
// with(this){return _c('ul',
//     _l((list),
//     function (item) {return _c('li', { key: item.id }, [_v(_s(item.title))])}),
//     0)}
// 事件
// const template = `
// <button @click="clickHadler">submit</button>`
// with(this){return _c('button',{on:{"click":clickHadler}},[_v("submit")])}  on代表所有事件,函数名clickHadler是变量取this.clickHadler


// v-model(重要 双向绑定原理)
const template = `
<input type="text" v-model="name"></input>`
// with(this){return _c('input',{
// directives: [{ name: "model", rawName: "v-model", value: (name), expression: "name" }],
// attrs: { "type": "text" }, domProps: { "value": (name) },
// on: { "input": function($event) { if ($event.target.composing) return; name = $event.target.value } }})}
// input:on监听input事件
// 生成input时候挂载了一个监听事件,通过$event.target.value取到input上的值赋值给this.name
// domProps中value显示this.name

// 编译
const res = compiler.compile(template)
console.log(res.render)
// ------------结果分析---------------------
// this:vm实例,new Vue({...})   _c _v _s message查找this属性
// return:类似h('p',{},[...]) , 源码中_c就是h函数返回vnode


// ------------源码中缩写函数含义------------
function installRenderHelpers(target) {
    target._o = markOnce;
    target._c = createElement;//也就是h函数
    target._n = toNumber;
    target._s = toString;
    target._l = renderList;
    target._t = renderSlot;
    target._q = looseEqual;
    target._i = looseIndexOf;
    target._m = renderStatic;
    target._f = resolveFilter;
    target._k = checkKeyCodes;
    target._b = bindObjectProps;
    target._v = createTextVNode;
    target._e = createEmptyVNode;
    target._u = resolveScopedSlots;
    target._g = bindObjectListeners;
    target._d = bindDynamicKeys;
    target._p = prependModifier;
}