Vue源码解析之AST语法树(三)
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语法树(三)相关推荐
- Vue源码解析(尚硅谷)
视频地址:Vue源码解析系列课程 一.Vue源码解析之mustache模板引擎 1. 什么是模板引擎 模板引擎是将数据要变为视图最优雅的解决方案 历史上曾经出现的数据变为视图的方法 2. mustac ...
- [Vue源码解析] patching算法
[Vue源码解析] patching算法 pathching算法:通过对比新旧VNode的不同,然后找出需要更新的节点进行更新 操作:1.创建新增节点 2.删除废弃节点 3.修改需要更新的节点 创建节 ...
- 【vuejs深入三】vue源码解析之二 htmlParse解析器的实现
写在前面 一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. 昨天博主分析了一下在vue中,最为基础核心的api,parse函数,它的作用是将vue的模板字符串转换成ast,从而 ...
- Vue源码解析(一)
前言:接触vue已经有一段时间了,前面也写了几篇关于vue全家桶的内容,感兴趣的小伙伴可以去看看,刚接触的时候就想去膜拜一下源码~可每次鼓起勇气去看vue源码的时候,当看到几万行代码的时候就直接望而却 ...
- loam源码解析5 : laserOdometry(三)
transformMaintenance.cpp解析 八.位姿估计 1. 雅可比计算 2. 矩阵求解 3. 退化问题分析 4. 姿态更新 5. 坐标转换 loam源码地址: https://githu ...
- Vue源码解析之Template转化为AST的实现方法
什么是AST 在Vue的mount过程中,template会被编译成AST语法树,AST是指抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree) ...
- Vue源码解析:虚拟dom比较原理
通过对 Vue2.0 源码阅读,想写一写自己的理解,能力有限故从尤大佬2016.4.11第一次提交开始读,准备陆续写: 模版字符串转AST语法树 AST语法树转render函数 Vue双向绑定原理 V ...
- Vue源码解析之数组变异
力有不逮的对象 众所周知,在 Vue 中,直接修改对象属性的值无法触发响应式.当你直接修改了对象属性的值,你会发现,只有数据改了,但是页面内容并没有改变. 这是什么原因? 原因在于: Vue 的响应式 ...
- Vue源码解析之函数入口
从入口开始看起 写博客就是记录自己撸码的过程和问题,好了~废话就不多说了,直接源码撸起,通过上一篇博客咱们大致知道了Vue源码目录设计,下面我们要一步步找到vue的入口 通过查看package.jso ...
最新文章
- maven如何在eclipse上加载
- 每天学一点Scala之 高阶函数 flatten
- Delphi数据类型
- 多用户文件系统java实现_为什么要有文件系统?文件系统都有那些种类?
- 事件 ID 1505,1508
- 【XML】我所知道的XML
- CSS border-style属性
- Net下的AppDomain编程 [摘录]
- tp3.2ajax上传文件,jquery - thinkphp3.2.3 ajax上传图片
- mysql扩容方案_MySQL分库分表:扩容方案
- 来看一看 Google 给你的标签是什么
- 计算摄影技术:身怀绝技的扫地僧
- linux tc 限制带宽,使用TC 对LINUX服务器网卡进行带宽限制的办法
- LICEcap 简洁易用的动画屏幕录制软件
- HDU6669 Game
- Kernel space lock contention配置及其使用
- PCB的ESD防护设计
- 全球值得关注的11家人脸识别公司与机构
- 如何将usb设置设为第一启动项
- 李宏毅机器学习之Deep Learning简介