首页 文章详情

vue中指令的编译过程

前端精髓 | 143 2021-09-15 23:21 0 0 0
UniSMS (合一短信)


首先生成的 ast 会加上一些属性,每个 element 元素可以看作是一个 ast 对象,整颗 DOM 树可以看作是包含依赖关系的 ast 对象。

v-if 指令

源码在 processIf(element) 函数里面处理

// <span v-if="msg">6</span>// (msg)?_c('span',[_v("6")]):_e()let ast = [  {    type: 1,    tag: 'span',    attrsList: [],    attrsMap: { 'v-if': 'msg' },    rawAttrsMap: {},    parent: {      type: 1,      tag: 'div',      attrsList: [],      attrsMap: {},      rawAttrsMap: {},      parent: undefined,      children: [],      plain: true,      static: false,      staticRoot: false    },    children: [],    if: 'msg',    ifConditions: [ { exp: 'msg', block: el } ],    plain: true,    static: false,    staticRoot: false,    pre: undefined,    ifProcessed: true  }]


先 processIf 再 genIf,生成 render:

function genIf (  el,  state,  altGen,  altEmpty) {  el.ifProcessed = true; // avoid recursion  return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty)}
function genIfConditions ( conditions, // [ { exp: 'msg', block: el } ] state, altGen, // altEmpty //) { if (!conditions.length) { return altEmpty || '_e()' }
var condition = conditions.shift(); if (condition.exp) { // 主要看这里,通过三元运算符?和:拼接字符串 return ("(" + (condition.exp) + ")?" + (genElement(el, state)) + ":" + (genIfConditions(conditions, state, altGen, altEmpty))) } else { return ("" + (genTernaryExp(condition.block))) }}

v-for 指令

源码在 processFor(element) 函数里面处理

// _l((list), function(item){return _c('span',[_v("6")])})// <span v-for="item in list">6</span>let ast = [  {    type: 1,    tag: 'span',    attrsList: [],    attrsMap: { 'v-for': 'item in list' },    rawAttrsMap: {},    parent: {      type: 1,      tag: 'div',      attrsList: [],      attrsMap: {},      rawAttrsMap: {},      parent: undefined,      children: [],      plain: true,      static: false,      staticRoot: false    },    children: [],    for: 'list',    alias: 'item',    plain: true,    static: false,    staticRoot: false,    pre: undefined,    forProcessed: true  }]


我们简单想象一下 for 指令需要包装成一个函数,方便之后的循环遍历,比如下面这样:

function _l(list, callback) { list.forEach(item => callback(item))  }


具体的生成过程:

function genFor (  el,  state,  altGen, //  altHelper //) {  var exp = el.for;  var alias = el.alias;  var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : '';  var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : '';  // 主要看这里,包装成一个function函数  el.forProcessed = true; // avoid recursion  return ('_l') + "((" + exp + ")," +    "function(" + alias + iterator1 + iterator2 + "){" +      "return " + ((genElement)(el, state)) +    '})'}

v-once 指令

源码在 processOnce(element) 函数里面处理

// <div>11<span v-once="msg">6</span></div>// render: `with(this){return _c('div',[_v("11"),_m(0)])}`,// staticRenderFns: [ `with(this){return _c('span',[_v("6")])}` ],let ast = [  {    type: 1,    tag: 'span',    attrsList: [],    attrsMap: { 'v-once': '' },    rawAttrsMap: {},    parent: {      type: 1,      tag: 'div',      attrsList: [],      attrsMap: {},      rawAttrsMap: {},      parent: undefined,      children: [],      plain: true,      static: false,      staticRoot: false    },    children: [],    once: true,    plain: true,    static: false,    staticInFor: false,    staticRoot: false,    pre: undefined,    onceProcessed: true,    staticProcessed: true  }]


v-once 只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。



function genOnce (el, state) {  el.onceProcessed = true;  return genStatic(el, state)}
function genStatic (el, state) { el.staticProcessed = true; var originalPreState = state.pre; state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}")); state.pre = originalPreState; return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")")}


生成 render 函数的目的就是为了当绑定的变量值发生改变的时候,可以重新执行 render 函数,最后生成真实的 DOM,其中_c 和 _v 等等这些辅助函数就是处理真实 DOM 的函数,关于 render 函数生成 DOM 我们之后再分析。


good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter