提示:本博客用于分享本人学习心得,如有错误之处欢迎大家指出。
前言
我们在用vue进行开发的时候,虽然模板是vue开发中最常用的部分,但是大多数人对他可能不是很了解,今天我们就一起来探讨以下,模板是什么?模板是如何编译的?
编译模板是什么?
- 模板不是html,模板有表达式、插值、能实现判断、循环
- html是标签语言,只有js代码才能实现判断、循环
- 因此,模板一定是转换为某种js代码,即编译模板
编译模板的过程?
1. vue-template-compiler
vue中是通过vue-template-compiler将模板编译成render函数,然后执行render函数生成vnode,我们来看下面的内容,看一下vue-template-compiler是怎样将不同的模板编译的。
(1)插值
const compiler = require('vue-template-compiler')
const template = "<p>{{message}}</p>"
const res = compiler.compile(template)
console.log(res.render)
然后执行node index.js,输出结果如下:
with(this){return _c('p',[_v(_s(message))])}
在vue中,上面with里的this代表的是vue实列
在vue源码中 _c 代表的是 createElement _v 代表的是 createTextVNode _s 代表的是 toString
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
export function installRenderHelpers (target: any) {
target._o = markOnce
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
}
因此,with(this){return _c(‘p’,[_v(_s(message))])} 就可以看成是下面这种形式
with(this){return createElement('p',[createTextVNode(toString(message))])}
(2)表达式
...
const template = "<p>{{flag ? message : 'message test'}}</p>"
...
然后执行node index.js,输出结果如下:
with(this){return _c('p',[_v(_s(flag ? message : 'message test'))])}
(3)动态属性
...
const template = "<div class='container'><img :src='imgSrc'></img></div>"
...
然后执行node index.js,输出结果如下:
with(this){
return _c('div',{staticClass:"container"},[_c('img',{attrs:{"src":imgSrc}})])
}
(4)条件
...
const template =
`<div>
<div v-if="flag === A">A</div>
<div v-else>B</div>
</div>`
...
然后执行node index.js,输出结果如下:
with(this){return _c('div',[(flag === A)?_c('div',[_v("A")]):_c('div',[_v("B")])])}
flag===A创建A节点,否则创建B节点
(5)循环
...
const template =
`<ul><li v-for="item in list" :key="item.id">{{item.name}}</li></ul>`
...
然后执行node index.js,输出结果如下:
with(this){
return _c(
'ul',_l(
(list),function(item){
return _c(
'li',{key:item.id},[_v(_s(item.name))])}),0)}
_s 代表的是 renderList
(6)事件
...
const template = `<div @click="handleClick"></div>`
...
然后执行node index.js,输出结果如下:
with(this){return _c('div',{on:{"click":handleClick}})}
(7)v-model
...
const template = `<input type="text" v-model="value" />`
...
然后执行node index.js,输出结果如下:
with(this){
return _c(
'input',{
directives:[
{name:"model",rawName:"v-model",value:(value),expression:"value"}
],
attrs:{"type":"text"},
domProps:{"value":(value)},
on:{
"input":function($event){
if($event.target.composing)return;
value=$event.target.value
}
}
}
)
}
在模板编译的时候,给input框监听了input事件, value=$event.target.value将当前的input值赋值给value变量,value指的是vm.value,然后 domProps:{“value”:(value)} 将value值显示出来
2. vue组件可用render代替template
通过上面的内容,我们知道了vue是将模板编译成render函数,因此,我们也可以用render代替template,如下代码:
Vue.component('Test', {
template: `<h1>xxx<h1>`
})
Vue.component('Test', {
render: function(createElment) {
return createElment('h1',{}, 'xxx')
}
})
编译模板的结果?
- 模板编译为render函数,执行render函数返回vnode
- 基于vnode再执行patch和diff
|