将 AST 深度遍历,根据不同条件生成 _c(),_l(),_e(),_l(),_v() 等 render 代码字符串,最后通过 createFunction()方法转成函数 代码片段 CodegenState 定义 获取 modules 里面的函数,比如 genData,transformCode 等 genElement() 判断当前 ast 属性,执行不同代码,生成不同代码串 genIf()…

将 AST 深度遍历,根据不同条件生成 _c(),_l(),_e(),_l(),_v() 等 render 代码字符串,最后通过 createFunction()方法转成函数

// src/core/instance/render.js
// 创建vnode
// 参数:tag,data,children,Boolean
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false);
// src/core/instance/render-helpers/index.js
export function installRenderHelpers(target: any) {
  target._o = markOnce;
  target._n = toNumber;
  target._s = toString;
  // 渲染列表,对v-for指令进行解析
  target._l = renderList;
  target._t = renderSlot;
  target._q = looseEqual;
  target._i = looseIndexOf;
  target._m = renderStatic;
  target._f = resolveFilter;
  target._k = checkKeyCodes;
  target._b = bindObjectProps;
  // 创建文本vnode
  target._v = createTextVNode;
  // 创建空vnode
  target._e = createEmptyVNode;
  target._u = resolveScopedSlots;
  target._g = bindObjectListeners;
}

代码片段

CodegenState 定义

获取 modules 里面的函数,比如 genData,transformCode 等

export class CodegenState {
  options: CompilerOptions;
  warn: Function;
  transforms: Array<TransformFunction>;
  dataGenFns: Array<DataGenFunction>;
  directives: { [key: string]: DirectiveFunction };
  maybeComponent: (el: ASTElement) => boolean;
  onceId: number;
  staticRenderFns: Array<string>;

  constructor(options: CompilerOptions) {
    this.options = options;
    this.warn = options.warn || baseWarn;
    this.transforms = pluckModuleFunction(options.modules, 'transformCode');
    this.dataGenFns = pluckModuleFunction(options.modules, 'genData');
    this.directives = extend(extend({}, baseDirectives), options.directives);
    const isReservedTag = options.isReservedTag || no;
    this.maybeComponent = (el: ASTElement) => !isReservedTag(el.tag);
    this.onceId = 0;
    this.staticRenderFns = [];
  }
}
// src/compiler/codegen/index.js
export function generate(ast: ASTElement | void, options: CompilerOptions): CodegenResult {
  // 获取 modules 里面的函数,比如 genData,transformCode 等
  const state = new CodegenState(options);
  // 判断当前 ast 属性,执行不同代码,生成不同代码串,ast不存在,渲染一个div
  const code = ast ? genElement(ast, state) : '_c("div")';
  return {
    // 返回一个with
    render: `with(this){return ${code}}`,
    staticRenderFns: state.staticRenderFns
  };
}

genElement()

判断当前 ast 属性,执行不同代码,生成不同代码串

// src/compiler/codegen/index.js
export function genElement(el: ASTElement, state: CodegenState): string {
  if (el.staticRoot && !el.staticProcessed) {
    return genStatic(el, state);
  } else if (el.once && !el.onceProcessed) {
    return genOnce(el, state);
  } else if (el.for && !el.forProcessed) {
    // 从 ast 拿到 for 属性,返回一个代码串
    return genFor(el, state);
  } else if (el.if && !el.ifProcessed) {
    // 依次从 condition 中获取第一个 condition,如果 condition.exp 存在,调用 genElement(),如果不存在,递归调用 genIfConditions
    // 第一次genIf后,就把el.ifProcessed设为true,el.ifProcessed避免递归调用v-if
    return genIf(el, state);
  } else if (el.tag === 'template' && !el.slotTarget) {
    return genChildren(el, state) || 'void 0';
  } else if (el.tag === 'slot') {
    return genSlot(el, state);
  } else {
    // component or element
    let code;
    if (el.component) {
      code = genComponent(el.component, el, state);
    } else {
      // 根据 ast 元素节点构造出一个 data 对象字符串,作为_c()参数
      const data = el.plain ? undefined : genData(el, state);
      // 遍历 children 执行 genNode 方法,根据不同 type,执行不同逻辑
      const children = el.inlineTemplate ? null : genChildren(el, state, true);
      code = `_c('${el.tag}'${
        data ? `,${data}` : '' // data
      }${
        children ? `,${children}` : '' // children
      })`;
    }
    // module transforms
    for (let i = 0; i < state.transforms.length; i++) {
      code = state.transforms[i](el, code);
    }
    return code;
  }
}

genIf()

依次从 condition 中获取第一个 condition,如果 condition.exp 存在,调用 genElement(),如果不存在,递归调用 genIfConditions

// src/compiler/codegen/index.js
export function genIf(el: any, state: CodegenState, altGen?: Function, altEmpty?: string): string {
  el.ifProcessed = true; // avoid recursion
  // 执行genIfConditions()
  return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty);
}

function genIfConditions(conditions: ASTIfConditions, state: CodegenState, altGen?: Function, altEmpty?: string): string {
  if (!conditions.length) {
    return altEmpty || '_e()';
  }
  // 依次从condition中获取第一个condition
  const condition = conditions.shift();
  // 如果condition.exp存在,调用genElement(),如果不存在,递归调用genIfConditions
  if (condition.exp) {
    return `(${condition.exp})?${genTernaryExp(condition.block)}:${genIfConditions(conditions, state, altGen, altEmpty)}`;
  } else {
    return `${genTernaryExp(condition.block)}`;
  }

  // v-if with v-once should generate code like (a)?_m(0):_m(1)
  function genTernaryExp(el) {
    return altGen ? altGen(el, state) : el.once ? genOnce(el, state) : genElement(el, state);
  }
}

genFor()

从 ast 拿到 for 属性,返回一个代码串

// src/compiler/codegen/index.js
export function genFor(el: any, state: CodegenState, altGen?: Function, altHelper?: string): string {
  const exp = el.for;
  const alias = el.alias;
  const iterator1 = el.iterator1 ? `,${el.iterator1}` : '';
  const iterator2 = el.iterator2 ? `,${el.iterator2}` : '';
  //  对于组件来说,在编译的时候,必须要传一个key
  if (process.env.NODE_ENV !== 'production' && state.maybeComponent(el) && el.tag !== 'slot' && el.tag !== 'template' && !el.key) {
    state.warn(`<${el.tag} v-for="${alias} in ${exp}">: component lists rendered with ` + `v-for should have explicit keys. ` + `See https://vuejs.org/guide/list.html#key for more info.`, true /* tip */);
  }

  el.forProcessed = true; // avoid recursion
  return `${altHelper || '_l'}((${exp}),` + `function(${alias}${iterator1}${iterator2}){` + `return ${(altGen || genElement)(el, state)}` + '})';
}

genData()

根据 ast 元素节点构造出一个 data 对象字符串,作为_c()参数

// src/compiler/codegen/index.js
export function genData(el: ASTElement, state: CodegenState): string {
  let data = '{';

  // directives first.
  // directives may mutate the el's other properties before they are generated.
  const dirs = genDirectives(el, state);
  if (dirs) data += dirs + ',';

  // key
  if (el.key) {
    data += `key:${el.key},`;
  }
  // ref
  if (el.ref) {
    data += `ref:${el.ref},`;
  }
  if (el.refInFor) {
    data += `refInFor:true,`;
  }
  // pre
  if (el.pre) {
    data += `pre:true,`;
  }
  // record original tag name for components using "is" attribute
  if (el.component) {
    data += `tag:"${el.tag}",`;
  }
  // 获取所有modules中的genData函数
  for (let i = 0; i < state.dataGenFns.length; i++) {
    data += state.dataGenFns[i](el);
  }
  // attributes
  if (el.attrs) {
    data += `attrs:{${genProps(el.attrs)}},`;
  }
  // DOM props
  if (el.props) {
    data += `domProps:{${genProps(el.props)}},`;
  }
  // event handlers
  if (el.events) {
    data += `${genHandlers(el.events, false, state.warn)},`;
  }
  if (el.nativeEvents) {
    data += `${genHandlers(el.nativeEvents, true, state.warn)},`;
  }
  // slot target
  // only for non-scoped slots
  if (el.slotTarget && !el.slotScope) {
    data += `slot:${el.slotTarget},`;
  }
  // scoped slots
  if (el.scopedSlots) {
    data += `${genScopedSlots(el.scopedSlots, state)},`;
  }
  // component v-model
  if (el.model) {
    data += `model:{value:${el.model.value},callback:${el.model.callback},expression:${el.model.expression}},`;
  }
  // inline-template
  if (el.inlineTemplate) {
    const inlineTemplate = genInlineTemplate(el, state);
    if (inlineTemplate) {
      data += `${inlineTemplate},`;
    }
  }
  data = data.replace(/,$/, '') + '}';
  // v-bind data wrap
  if (el.wrapData) {
    data = el.wrapData(data);
  }
  // v-on data wrap
  if (el.wrapListeners) {
    data = el.wrapListeners(data);
  }
  return data;
}

genChildren()

遍历 children 执行 genNode 方法,根据不同 type,执行不同逻辑

// src/compiler/codegen/index.js
export function genChildren(el: ASTElement, state: CodegenState, checkSkip?: boolean, altGenElement?: Function, altGenNode?: Function): string | void {
  const children = el.children;
  if (children.length) {
    const el: any = children[0];
    if (children.length === 1 && el.for && el.tag !== 'template' && el.tag !== 'slot') {
      return (altGenElement || genElement)(el, state);
    }
    const normalizationType = checkSkip ? getNormalizationType(children, state.maybeComponent) : 0;
    const gen = altGenNode || genNode;
    return `[${children.map((c) => gen(c, state)).join(',')}]${normalizationType ? `,${normalizationType}` : ''}`;
  }
}
function genNode(node: ASTNode, state: CodegenState): string {
  if (node.type === 1) {
    return genElement(node, state);
  }
  if (node.type === 3 && node.isComment) {
    return genComment(node);
  } else {
    return genText(node);
  }
}

暂无评论