前言

我们在用vue进行开发的时候,虽然模板是vue开发中最常用的部分,但是大多数人对他可能不是很了解,今天我们就一起来探讨以下,模板是什么?模板是如何编译的?

编译模板是什么?

  • 模板不是html,模板有表达式、插值、能实现判断、循环
  • html是标签语言,只有js代码才能实现判断、循环
  • 因此,模板一定是转换为某种js代码,即编译模板

编译模板的过程?

1. vue-template-compiler

vue中是通过vue-template-compiler将模板编译成render函数,然后执行render函数生成vnode,我们来看下面的内容,看一下==vue-template-compiler==是怎样将不同的模板编译的。

(1)插值

// index.js
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

// vue源码
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))])} 就可以看成是下面这种形式

// message就是vue实列下的message->this.message(vm.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,输出结果如下:

// imgSrc: this.imgSrc
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,输出结果如下:

// list: this.list
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,输出结果如下:

// handleClick: this.handleClick
with(this){return _c('div',{on:{"click":handleClick}})}

(7)v-model

...
const template = `<input type="text" v-model="value" />`
...

然后执行node index.js,输出结果如下:

// value: this.value
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,如下代码:

// template
Vue.component('Test', {
    template: `<h1>xxx<h1>`
})

// render
Vue.component('Test', {
    render: function(createElment) {
        return createElment('h1',{}, 'xxx')
    }
})

编译模板的结果?

  1. 模板编译为render函数,执行render函数返回vnode
  2. 基于vnode再执行patch和diff
Last modification:August 10th, 2021 at 09:53 pm
如果觉得我的文章对你有用,请随意赞赏