parse解析完之后,将生成的ast返回到baseCompile,接下来就是调用optimize方法对ast进行优化。

var createCompiler = createCompilerCreator(function baseCompile (template,options
) {//获取astvar ast = parse(template.trim(), options);if (options.optimize !== false) {optimize(ast, options);}var code = generate(ast, options);return {ast: ast,render: code.render,staticRenderFns: code.staticRenderFns}
});

optimize

function optimize (root, options) {if (!root) { return }//对静态标签进行缓存isStaticKey = genStaticKeysCached(options.staticKeys || '');isPlatformReservedTag = options.isReservedTag || no;//标记所有非静态节点markStatic$1(root);//标记所有静态root节点markStaticRoots(root, false);
}

optimize方法通过genStaticKeysCached缓存了所有静态标签,调用markStatic$1(root)标记所有非静态节点,调用markStaticRoots(root, false)标记静态root节点。

function markStatic$1 (node) {//判断是否是静态节点,isStatic代码在下面node.static = isStatic(node);if (node.type === 1) {//过滤掉slot标签和template标签//原因:组件不能变成slot节点//静态的slot节点内容不能热加载if (!isPlatformReservedTag(node.tag) &&node.tag !== 'slot' &&node.attrsMap['inline-template'] == null) {return}//循环递归标记节点for (var i = 0, l = node.children.length; i < l; i++) {var child = node.children[i];markStatic$1(child);if (!child.static) {node.static = false;}}if (node.ifConditions) {/**/}}
}
function isStatic (node) {if (node.type === 2) { // 判断是不是类似{{message}}这样的表达式return false}if (node.type === 3) { // 判断是不是纯文本return true}return !!(node.pre || (!node.hasBindings && // 是否动态绑定!node.if && !node.for && //是否v-if or v-for or v-else!isBuiltInTag(node.tag) && // not a built-inisPlatformReservedTag(node.tag) && // not a component!isDirectChildOfTemplateFor(node) &&Object.keys(node).every(isStaticKey) //遍历判断属性是否静态))
}

markStaticRoots

function markStaticRoots (node, isInFor) {if (node.type === 1) {if (node.static || node.once) {node.staticInFor = isInFor;}// 作为静态节点 必须有子节点并且不为纯文本 否则更新消耗较大if (node.static && node.children.length && !(node.children.length === 1 &&node.children[0].type === 3)) {node.staticRoot = true;return} else {node.staticRoot = false;}//进行递归标记if (node.children) {for (var i = 0, l = node.children.length; i < l; i++) {markStaticRoots(node.children[i], isInFor || !!node.for);}}if (node.ifConditions) {/**/}}
}

经过optimize函数,ast对象增加加了两个属性,如图:


接下来调用generate方法,将ast对象转换成Vue自定义的字符串形式。

generate

function generate (ast,options
) {//根据options创建CodegenState对象var state = new CodegenState(options);//调用genElement将ast对象转换为字符串var code = ast ? genElement(ast, state) : '_c("div")';return {render: ("with(this){return " + code + "}"),staticRenderFns: state.staticRenderFns}
}

genElement

function genElement (el, state) {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) {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 {//本例子进入这里,调用genData$2var data = el.plain ? undefined : 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}
}

genData$2

function genData$2 (el, state) {var data = '{';// 首先对directives进行处理// directives可能会对el上的其他属性有影响,所以先处理var dirs = genDirectives(el, state);if (dirs) { data += dirs + ','; }//根据本文的例子,没有执行的判断,代码都省略了/*处理key,ref,refInFor,pre,component*/// module data generation functionsfor (var i = 0; i < state.dataGenFns.length; i++) {data += state.dataGenFns[i](el);//调用genData对class进行处理}if (el.attrs) {//进入该判断,调用genProps,对el的属性进行处理data += "attrs:{" + (genProps(el.attrs)) + "},";}/*处理props,events,nativeEvents,slotTarget,scopedSlots,model,inlineTemplate*/data = data.replace(/,$/, '') + '}';// v-bind data wrapif (el.wrapData) {data = el.wrapData(data);}// v-on data wrapif (el.wrapListeners) {data = el.wrapListeners(data);}return data
}

genData$2跳过了大多数的判断,直接进入attrs,调用genProps函数

function genProps (props) {var res = '';// 将属性名,属性值拼接成 "属性名":"属性值"形式的字符串//本文例子"id":"test"for (var i = 0; i < props.length; i++) {var prop = props[i];/* istanbul ignore if */{res += "\"" + (prop.name) + "\":" + (transformSpecialNewlines(prop.value)) + ",";}}return res.slice(0, -1)
}

genData$2最终返回的data:

接下来开始对子节点进行处理

genChildren

function genChildren (el,state,checkSkip,altGenElement,altGenNode
) {var children = el.children;if (children.length) {var el$1 = children[0];//对v-for进行简单优化if (children.length === 1 &&el$1.for &&el$1.tag !== 'template' &&el$1.tag !== 'slot') {return (altGenElement || genElement)(el$1, state)}var normalizationType = checkSkip? getNormalizationType(children, state.maybeComponent): 0;var gen = altGenNode || genNode;return ("[" + (children.map(function (c) { return gen(c, state); }).join(',')) + "]" + (normalizationType ? ("," + normalizationType) : ''))}
}function genNode (node, state) {if (node.type === 1) {return genElement(node, state)} if (node.type === 3 && node.isComment) {return genComment(node)} else {return genText(node)}
}function genText (text) {return ("_v(" + (text.type === 2? text.expression // no need for () because already wrapped in _s(): transformSpecialNewlines(JSON.stringify(text.text))) + ")")
}function genComment (comment) {return ("_e(" + (JSON.stringify(comment.text)) + ")")
}

最终转换后的字符串的结果为:


正好对应Vue对v-model的解析过程:https://segmentfault.com/a/11...

code:

compileToFunction

接下来compileToFunction将生成的code字符串代码转化为函数

// turn code into functionsvar res = {};var fnGenErrors = [];//createFunction就返回了new Function(code)//这里的render就是上文的code字符串res.render = createFunction(compiled.render, fnGenErrors);res.staticRenderFns = compiled.staticRenderFns.map(function (code) {return createFunction(code, fnGenErrors)});

compileToFunction返回的对象信息:

$amount

var ref = compileToFunctions(template, {shouldDecodeNewlines: shouldDecodeNewlines,shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref,delimiters: options.delimiters,comments: options.comments}, this);var render = ref.render;var staticRenderFns = ref.staticRenderFns;options.render = render;options.staticRenderFns = staticRenderFns;

$amount调用compileToFunctions后,将返回的对象包含的render函数和staticRenderFns属性,挂载到options参数上,然后再次调用mount。
整个函数调用过程:

Vue源码解析之AST语法树(三)相关推荐

  1. Vue源码解析(尚硅谷)

    视频地址:Vue源码解析系列课程 一.Vue源码解析之mustache模板引擎 1. 什么是模板引擎 模板引擎是将数据要变为视图最优雅的解决方案 历史上曾经出现的数据变为视图的方法 2. mustac ...

  2. [Vue源码解析] patching算法

    [Vue源码解析] patching算法 pathching算法:通过对比新旧VNode的不同,然后找出需要更新的节点进行更新 操作:1.创建新增节点 2.删除废弃节点 3.修改需要更新的节点 创建节 ...

  3. 【vuejs深入三】vue源码解析之二 htmlParse解析器的实现

    写在前面 一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. 昨天博主分析了一下在vue中,最为基础核心的api,parse函数,它的作用是将vue的模板字符串转换成ast,从而 ...

  4. Vue源码解析(一)

    前言:接触vue已经有一段时间了,前面也写了几篇关于vue全家桶的内容,感兴趣的小伙伴可以去看看,刚接触的时候就想去膜拜一下源码~可每次鼓起勇气去看vue源码的时候,当看到几万行代码的时候就直接望而却 ...

  5. loam源码解析5 : laserOdometry(三)

    transformMaintenance.cpp解析 八.位姿估计 1. 雅可比计算 2. 矩阵求解 3. 退化问题分析 4. 姿态更新 5. 坐标转换 loam源码地址: https://githu ...

  6. Vue源码解析之Template转化为AST的实现方法

    什么是AST 在Vue的mount过程中,template会被编译成AST语法树,AST是指抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree) ...

  7. Vue源码解析:虚拟dom比较原理

    通过对 Vue2.0 源码阅读,想写一写自己的理解,能力有限故从尤大佬2016.4.11第一次提交开始读,准备陆续写: 模版字符串转AST语法树 AST语法树转render函数 Vue双向绑定原理 V ...

  8. Vue源码解析之数组变异

    力有不逮的对象 众所周知,在 Vue 中,直接修改对象属性的值无法触发响应式.当你直接修改了对象属性的值,你会发现,只有数据改了,但是页面内容并没有改变. 这是什么原因? 原因在于: Vue 的响应式 ...

  9. Vue源码解析之函数入口

    从入口开始看起 写博客就是记录自己撸码的过程和问题,好了~废话就不多说了,直接源码撸起,通过上一篇博客咱们大致知道了Vue源码目录设计,下面我们要一步步找到vue的入口 通过查看package.jso ...

最新文章

  1. maven如何在eclipse上加载
  2. 每天学一点Scala之 高阶函数 flatten
  3. Delphi数据类型
  4. 多用户文件系统java实现_为什么要有文件系统?文件系统都有那些种类?
  5. 事件 ID 1505,1508
  6. 【XML】我所知道的XML
  7. CSS border-style属性
  8. Net下的AppDomain编程 [摘录]
  9. tp3.2ajax上传文件,jquery - thinkphp3.2.3 ajax上传图片
  10. mysql扩容方案_MySQL分库分表:扩容方案
  11. 来看一看 Google 给你的标签是什么
  12. 计算摄影技术:身怀绝技的扫地僧
  13. linux tc 限制带宽,使用TC 对LINUX服务器网卡进行带宽限制的办法
  14. LICEcap 简洁易用的动画屏幕录制软件
  15. HDU6669 Game
  16. Kernel space lock contention配置及其使用
  17. PCB的ESD防护设计
  18. 全球值得关注的11家人脸识别公司与机构
  19. 如何将usb设置设为第一启动项
  20. 李宏毅机器学习之Deep Learning简介

热门文章

  1. NSURLSession
  2. 软件开发的核心是技术人员吗?
  3. Netbeans自定义mode
  4. DLL内线程同步主线程研究(子线程代码放到主线程执行)
  5. 《Effective C#》的读书笔记
  6. 前端教程丨手把手教你用 Next.js 搭建个人博客,从入门到吃鸡
  7. 多对多(many-to-many)
  8. MySQL分组查询的介绍
  9. container常见操作
  10. 设计模式在Netty 中的应用-策略模式源码举例