Vue 源码
你想要的 vue 源码分析 · 28
将 AST 深度遍历,根据不同条件生成 _c(),_l(),_e(),_l(),_v() 等 render 代码字符串,最后通过 createFunction()方法转成函数 代码片段 CodegenState 定义 获取 modules 里面的函数,比如 genData,transformCode 等 genElement() 判断当前 ast 属性,执行不同代码,生成不同代码串 genIf()…
将 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;
}
获取 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
};
}
判断当前 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;
}
}
依次从 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);
}
}
从 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)}` + '})';
}
根据 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;
}
遍历 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);
}
}
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论