模板编译
模板不是 html , 有指令、插值、JS表达式,能实现判断循环,html 是标签语言,只有 JS 才能实现判断、循环(图灵完备的),因此,模板一定是转换为某种 JS 代码,既模板编译。
使用 webpack vue-loader ,会在开发环境下编译模板。
模板编译前置知识点-with
- 改变 {} 内自由变量的查找规则,当做 obj 属性来查找。
- 如果找不到匹配的 obj 属性,就会报错。
- with 要慎用,它打破了作用域规则,易读性变差。
with (obj) {
console.log(a)
console.log(b)
console.log(c) // 会报错 !
}
const obj = { a: 100, b: 200 }
console.log(obj.a)
console.log(obj.b)
console.log(obj.c) // undefined
模板转换为JS
模板编译为 render 函数,执行 render 函数返回 vnode, 基于vnode再执行patch和diff。
依赖库:vue-template-compiler@2.6.14(vue 2.0)
测试
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 message found' }}</p>`;
// with(this){return _c('p',[_v(_s(flag ? message : 'no message found'))])}
// 属性和动态属性
// 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}})])}
// 条件
// 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="clickHandler" >submit</button>`;
// with(this){return _c('button',{on:{"click":clickHandler}},[_v("submit")])}
// 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}}})}
// 编译
const res = compiler.compile(template);
console.log(res.render);
/**
* vue 源码中找到缩写函数含义 渲染辅助函数(installRenderHelpers)
*
target._o = markOnce 一次性渲染节点
target._n = toNumber 转换数值
target._s = toString 转换为字符串类型
target._l = renderList 渲染v-for指令
target._t = renderSlot 处理slot
target._q = looseEqual 判断两个变量是否相等
target._i = looseIndexOf 判断数组是否存在某值,并返回索引
target._m = renderStatic 渲染静态节点树
target._f = resolveFilter 处理filters过滤器
target._k = checkKeyCodes 检查config的keyCodes
target._b = bindObjectProps 处理v-bind指令
target._v = createTextVNode 创建文本vnode
target._e = createEmptyVNode 创建空vnode
target._u = resolveScopedSlots 处理slot
target._g = bindObjectListeners 处理v-on指令
target._d = bindDynamicKeys 绑定event、props
target._p = prependModifier 处理绑定事件修饰符
_c = createElement
*/
vue 组件中使用 render 代替 template
在有些复杂情况中,不能用 template , 可以考虑用 render,
对比react一直都用 render(没有模板),和下方示例一样。
<script>
Vue.component('heading', {
// template: `xxx`
render: function (createElement) {
return createElement(
'h' + this.level,
[
createElement('a', {
attrs: {
name: 'headerId',
href: '#' + 'headerId'
}
}, 'this is a tag')
]
)
}
})
</script>