anchors
入口
- 通过loader加载的vue文件都是已经编译过得.
- createCompilerCreator, 缓存了baseCompile(主要编译过程)
- createCompiler, 缓存了baseOptions(平台差异选项); 内部compile方法在处理options(module,directive)后调用baseCompile
- createCompileToFunctionFn对传入option做处理, (缓存编译的render function), 调用createCompiler的内部compile方法, 为ast创建function, 处理编译错误信息等.123456789101112var ref$1 = createCompiler(baseOptions);var compile = ref$1.compile;var compileToFunctions = ref$1.compileToFunctions;var ref = compileToFunctions(template, {outputSourceRange: process.env.NODE_ENV !== 'production',shouldDecodeNewlines: shouldDecodeNewlines,shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref,delimiters: options.delimiters,comments: options.comments}, this);var render = ref.render;
|
|
|
|
parse
parse流程
- 通过正则表达式读取标签,属性, 文本注释等信息
- 开始/结束标签通过栈配对.
|
|
parseHTML
- 从头到尾按正则匹配出开始,结束标签, 文本注释等
- ast node type: 1:元素节点 2:expression text node 3:plain text node
- parse->stack用ast node层级关系缓存, parseHTML->stack用ast node层级关系缓存
- 一个读数标签属性, 一个构建ast. 都是类似层级结构
- handleStartTag
stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs });
- start
stack.push(element);
|
|
startElement
parseStartTag
通过正则获取属性,标签等信息, 开始标签的结尾index.handleStartTag
处理自闭合标签, attrs转为{name, value}
格式, 记录lastTag, 调用options.start
options.start
创建ast节点, 往ast上添加所有属性的, 处理层级关系- preTransforms处理
src/platforms/web/compiler/modules
里module的preTransforms
、transforms
处理在processElement
里 - 处理for,if,once指令
- 处理层级关系1234currentParent = elementstack.push(element)currentParent = elementstack.push(element)
- preTransforms处理
|
|
|
|
|
|
|
|
endElement
- 通过stack找到闭合标签对应的开始标签的ast节点, 调用
options.end
123456789101112131415161718192021222324252627282930313233343536// 匹配后调用parseEndTag处理const endTagMatch = html.match(endTag)if (endTagMatch) {const curIndex = indexadvance(endTagMatch[0].length)parseEndTag(endTagMatch[1], curIndex, index)continue}function parseEndTag (tagName, start, end) {let pos, lowerCasedTagNameif (start == null) start = indexif (end == null) end = index// 找到最近的开始标签if (tagName) {for (pos = stack.length - 1; pos >= 0; pos--) {if (stack[pos].lowerCasedTag === lowerCasedTagName) {break}}} else {pos = 0}if (pos >= 0) {for (let i = stack.length - 1; i >= pos; i--) {if (options.end) {options.end(stack[i].tag, start, end)}}stack.length = poslastTag = pos && stack[pos - 1].tag}// br p标签处理}
|
|
text
- 截取文本, 创建(表达式/普通)文本节点
|
|
|
|
conerCases
- h5元素嵌套规则
- 块级元素不能放在
里面,
<p><div></div></p>
=><p></p><div></div><p></p>
- lastTag是p, div不是phrasing tag, 就插入结束标签, 调用
parseEndTag(lastTag);
把p从stack移除12345678910111213141516171819202122232425262728function handleStartTag (match) {var tagName = match.tagName;var unarySlash = match.unarySlash;if (expectHTML) {if (lastTag === 'p' && isNonPhrasingTag(tagName)) {parseEndTag(lastTag);}if (canBeLeftOpenTag$$1(tagName) && lastTag === tagName) {parseEndTag(tagName); // p里嵌套p也是一样的处理}}var unary = isUnaryTag$$1(tagName) || !!unarySlash;if (!unary) {stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs });lastTag = tagName;}if (options.start) {options.start(tagName, attrs, unary, match.start, match.end);// if (!unary) {// currentParent = element;// stack.push(element);// }}}
- lastTag是p, div不是phrasing tag, 就插入结束标签, 调用
|
|
optimize
- ast优化, 标记静态节点, 静态子树. 避免不会变化的节点重新渲染.
- 提取未常量
- 跳过patch
- 静态节点, 有一个子节点不是静态, 这个节点也就不是静态. 此节点有ifCondition也需要所有的判断是静态的
- 判断规则
isStatic
: 如果是表达式,就是非静态;如果是纯文本,就是静态;…
- 判断规则
- 静态子树
- 至少一个非文本静态节点
- 所有子节点和当前节点标记了静态
- 深度遍历123456789101112131415161718192021222324252627/*** Goal of the optimizer: walk the generated template AST tree* and detect sub-trees that are purely static, i.e. parts of* the DOM that never needs to change.** Once we detect these sub-trees, we can:** 1. Hoist them into constants, so that we no longer need to* create fresh nodes for them on each re-render;* 2. Completely skip them in the patching process.*/function optimize (root, options) {if (!root) { return }isStaticKey = genStaticKeysCached(options.staticKeys || '');isPlatformReservedTag = options.isReservedTag || no;// first pass: mark all non-static nodes.markStatic$1(root);// second pass: mark static roots.markStaticRoots(root, false);}function genStaticKeys$1 (keys) {return makeMap('type,tag,attrsList,attrsMap,plain,parent,children,attrs' +(keys ? ',' + keys : ''))}
|
|
|
|
codegen
- gencode创建vnode相关的util方法在
function installRenderHelpers (target){}
- 通过深度遍历VST创建render函数字符串.1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950function generate (ast,options) {var state = new CodegenState(options);var code = ast ? genElement(ast, state) : '_c("div")';return {render: ("with(this){return " + code + "}"),staticRenderFns: state.staticRenderFns}}function genElement (el, state) {if (el.parent) {el.pre = el.pre || el.parent.pre;}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) {return genFor(el, state)} else if (el.if && !el.ifProcessed) {return genIf(el, state)} else if (el.tag === 'template' && !el.slotTarget && !state.pre) {return genChildren(el, state) || 'void 0'} else if (el.tag === 'slot') {return genSlot(el, state)} else {// component or elementvar code;if (el.component) {code = genComponent(el.component, el, state);} else {var data;if (!el.plain || (el.pre && state.maybeComponent(el))) {data = genData$2(el, state);}var children = el.inlineTemplate ? null : genChildren(el, state, true);code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")";}// module transformsfor (var i = 0; i < state.transforms.length; i++) {code = state.transforms[i](el, code);}return code}}
v-if/v-for generate code
- v-if 创建的模式就是`(condition) ? genElement(condition.block) : genIfConditions(剩余的condition)’
|
|
genElement(condition.block)
部分代码执行如下:
|
|
- genData处理if部分代码
|
|
处理子节点, 处理v-for和其他子节点
1234567891011121314151617181920212223242526272829function genChildren (el,state,checkSkip,altGenElement,altGenNode) {var children = el.children;if (children.length) {var el$1 = children[0];// optimize single v-forif (children.length === 1 &&el$1.for &&el$1.tag !== 'template' &&el$1.tag !== 'slot') {var normalizationType = checkSkip? state.maybeComponent(el$1) ? ",1" : ",0": "";return ("" + ((altGenElement || genElement)(el$1, state)) + normalizationType)}var normalizationType$1 = checkSkip? getNormalizationType(children, state.maybeComponent): 0;var gen = altGenNode || genNode;return ("[" + (children.map(function (c) { return gen(c, state); }).join(',')) + "]" + (normalizationType$1 ? ("," + normalizationType$1) : ''))}}v-for 创建的模式就是
_l(data, function(item, index){return genElement)(el,state) })?
, 之后又是genData, genChildren
- genChildren会调用
genNode
- genChildren会调用
|
|