模板编译

模板不是 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>
最后更新时间:
贡献者: DESKTOP-ER5718D\zt