耳边有财|数字经济能从“热点”走向“长牛”吗?
据中央广播电视总台经济之声《视听大会》报道,《数字中国建设整体布局规划》(下称《规划》)日前出台,数字经济迎来重要的顶层设计。《规划
在Vue $mount过程中,我们需要把模版编译成render函数,整体实现可以分为三部分:
parse:解析模版 template生成 AST语法树optimize:优化 AST语法树,标记静态节点codegen:把优化后的 AST语法树转换生成render方法代码字符串,利用模板引擎生成可执行的 render函数( render执行后返回的结果就是虚拟DOM,即以 VNode节点作为基础的树 )Vue.js 提供了 2 个版本,一个是 Runtime + Compiler 的,一个是 Runtime only 的,前者是包含编译代码的,可以把编译过程放在运行时做,后者是不包含编译代码的,需要借助 webpack 的 vue-loader 事先把模板编译成 render函数。
下一章我们将介绍 render 和 patch 过程。关于 render函数如何生成虚拟DOM,以及如何将 vnode转化成真实DOM并挂载?
(资料图片)
Vue.prototype.$mount = function (el) { ... // 这里需要对模板进行编译 const render = compileToFunction(template)}export function compileToFunction(template) { // 1.解析模版template生成 AST语法树 let ast = parseHTML(template) // 2.优化AST语法树,标记静态节点 optimize(ast) // 3.把优化后的 AST语法树转换生成render方法代码字符串,利用模板引擎生成可执行的 render函数回的结果就是 虚拟DOM) let code = codegen(ast) code = `with(this){return ${code}}` let render = new Function(code) return render}
parseAST做的是语法层面的转化,就是用对象去描述语法本身,例如经过 parse过程后,对 html的描述如下
可以看到,生成的 AST 是一个树状结构,每一个节点都是一个 ast element,除了它自身的一些属性,还维护了它的父子关系,如 parent指向它的父节点,children指向它的所有子节点
我们也可以利用AST的可视化工具网站 - AST Exploer ,使用各种parse对代码进行AST转换
在 Vue的 $mount过程中,编译过程首先就是调用 parseHTML方法,解析 template模版,生成 AST语法树
在这个过程,我们会用到正则表达式对字符串解析,匹配开始标签、文本内容和闭合标签等
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`const qnameCapture = `((?:${ncname}\\:)?${ncname})`// 匹配的是 第一个分组就是结束标签的名字const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)// 分组1: 属性的key 分组2: = 分组3/分组4/分组5: value值const attribute = /^\s*([^\s""<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|"([^"]*)"+|([^\s""=<>`]+)))?/ // 匹配属性const startTagClose = /^\s*(\/?)>/ // 匹配开始标签的结束 > 或 />
使用 while 循环html字符串,利用正则去匹配开始标签、文本内容和闭合标签,然后执行 advance方法将匹配到的内容在原html字符串中剔除,直到html字符串为空,结束循环
export function parseHTML(html) { // 创建一颗抽象语法树 function createASTElement(tag, attrs) { } // 处理开始标签,利用栈型结构来构造一颗树 function start(tag, attrs) { } // 处理文本 function chars(text) { } // 处理结束标签 function end(tag) { } // 剔除 template 已匹配的内容 function advance(n) { html = html.substring(n) } // 解析开始标签 function parseStartTag() { const start = html.match(startTagOpen) if (start) { const match = { tagName: start[1], // 标签名 attrs: [], } advance(start[0].length) let attr, end // 如果不是开始标签的结束 就一直匹配下去 while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { advance(attr[0].length) match.attrs.push({ name: attr[1], value: attr[3] || attr[4] || attr[5] || true }) } // 如果不是开始标签的结束 if (end) { advance(end[0].length) } return match } return false } // 循环html字符串,直到其为空停止 while (html) { // 如果textEnd = 0 说明是一个开始标签或者结束标签 // 如果textEnd > 0 说明就是文本的结束位置 let textEnd = html.indexOf("<") if (textEnd == 0) { // 开始标签的解析結果,包括 标签名 和 属性 const startTagMatch = parseStartTag() if (startTagMatch) { start(startTagMatch.tagName, startTagMatch.attrs) continue } // 匹配结束标签 let endTagMatch = html.match(endTag) if (endTagMatch) { advance(endTagMatch[0].length) end(endTagMatch[1]) continue } } if (textEnd > 0) { let text = html.substring(0, textEnd) // 截取文本内容 if (text) { chars(text) advance(text.length) } } } return root}
当我们使用正则匹配到开始标签、文本内容和闭合标签时,分别执行start、chars、end方法去处理,利用 stack 栈型数据结构,最终构造一颗AST树,即root
匹配到开始标签时,就创建一个 ast元素,判断如果有 currentParent,会把当前 ast元素 push到 currentParent.chilldren 中,同时把 ast元素的 parent 指向 currentParent,ast元素入栈并更新 currentParent匹配到文本时,就给 currentParent.children push一个文本 ast元素匹配到结束标签时,就弹出栈中最后一个 ast元素,更新 currentParentcurrentParent:指向的是栈中的最后一个 ast节点
注意:栈中的当前 ast节点永远是下一个 ast节点的父节点
const ELEMENT_TYPE = 1 // 元素类型const TEXT_TYPE = 3 // 文本类型const stack = [] // 用于存放元素的栈let currentParent // 指向的是栈中的最后一个let root// 最终需要转化成一颗抽象语法树function createASTElement(tag, attrs) { return { tag, // 标签名 type: ELEMENT_TYPE, // 类型 attrs, // 属性 parent: null, children: [], }}// 处理开始标签,利用栈型结构 来构造一颗树function start(tag, attrs) { let node = createASTElement(tag, attrs) // 创造一个 ast节点 if (!root) { root = node // 如果root为空,则当前是树的根节点 } if (currentParent) { node.parent = currentParent // 只赋予了parent属性 currentParent.children.push(node) // 还需要让父亲记住自己 } stack.push(node) currentParent = node // currentParent为栈中的最后一个}// 处理文本function chars(text) { text = text.replace(/\s/g, "") // 文本直接放到当前指向的节点中 if (text) { currentParent.children.push({ type: TEXT_TYPE, text, parent: currentParent, }) }}// 处理结束标签function end(tag) { stack.pop() // 弹出栈中最后一个ast节点 currentParent = stack[stack.length - 1]}
当 AST 树构造完毕,下一步就是 optimize 优化这颗树
optimeize当我们解析 template模版,生成 AST语法树之后,需要对这棵树进行 optimize优化,在编译阶段把一些 AST 节点优化成静态节点
深度遍历这个 AST 树,去检测它的每一颗子树是不是静态节点,如果是静态节点则标记 static: true
为什么要有优化过程,因为我们知道 Vue 是数据驱动,是响应式的,但是我们的模板并不是所有数据都是响应式的,也有很多数据是首次渲染后就永远不会变化的,那么这部分数据生成的 DOM 也不会变化,我们可以在 patch 的过程跳过对他们的比对,这对运行时对模板的更新起到极大的优化作用。
codegen编译的最后一步就是把优化后的 AST树转换成可执行的 render代码。此过程包含两部分,第一部分是使用 codegen方法生成 render代码字符串,第二部分是利用模板引擎转换成可执行的 render代码
render方法代码字符串格式如下
_c: 执行 createElement创建虚拟节点;_v: 执行 createTextVNode创建文本虚拟节点;_s: 处理变量我们会在Vue原型上扩展这些方法
让我们来实现一个简单的codegen方法,深度遍历AST树去生成render代码字符串
function codegen(ast) { let children = genChildren(ast.children) let code = `_c("${ast.tag}",${ast.attrs.length > 0 ? genProps(ast.attrs) : "null"}${ast.children.length ? `,${children}` : ""})` return code}// 根据ast语法树的 children对象 生成相对应的 children字符串function genChildren(children) { return children.map(child => gen(child)).join(",")}const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g // 匹配到的内容就是我们表达式的变量,例如 {{ name }}function gen(node) { if (node.type === 1) { // 元素 return codegen(node) } else { // 文本 let text = node.text if (!defaultTagRE.test(text)) { // _v("hello") return `_v(${JSON.stringify(text)})` } else { //_v( _s(name) + "hello" + _s(age)) ... 拼接 _s return `_v(${tokens.join("+")})` } }}// 根据ast语法树的 attrs属性对象 生成相对应的属性字符串function genProps(attrs) { let str = "" for (let i = 0; i < attrs.length; i++) { let attr = attrs[i] str += `${attr.name}:${JSON.stringify(attr.value)},` // id:"app",class:"app-inner", } return `{${str.slice(0, -1)}}`}
模板引擎的实现原理就是 with + new Function(),转换成可执行的函数,最终赋值给vm.options.render
let code = codegen(ast)code = `with(this){return ${code}}`let render = new Function(code)
尤大大亲自解读: Vue2模板编译为何使用with
with 的作用域和模板的作用域正好契合,可以极大地简化模板编译过程。用 with 代码量可以很少,而且把作用域的处理交给 js 引擎来做也更可靠用 with 的主要副作用是生成的代码不能在 strict mode / ES module 中运行,但直接在浏览器里编译的时候因为用了 new Function(),等同于 eval,不受这一点影响
参考文档编译 | Vue.js 技术揭秘
关键词:
据中央广播电视总台经济之声《视听大会》报道,《数字中国建设整体布局规划》(下称《规划》)日前出台,数字经济迎来重要的顶层设计。《规划
1、妊娠晚期子宫收缩分为生理性子宫收缩和真性子宫收缩。2、生理性子宫收缩也被称为假性收缩或无效收缩。3、这样的宫缩只感觉
1、红眼的完美变化是厄运6加暴腿,然后等级堆到20,最完美。没有绿皮,所以还是比较完美的。2、上衣是55级重甲上衣。3、
发生热传递的条件必须是两个物体,并且存在温度差。只有在温度不同时,两物体之间才会发生热传递,热传递是热从温度高的物体传到温度低的物体,
就目前的市场状态而言,新能源车企经历过数次车轮战后,目前已经进入到了淘汰赛环节。无论是传统车企的新能源业务,还是对造车新势力来说,如
1、详见国家建筑标准设计图集《11G101-1》具体加密高度还应依据结构设计并应满足规范要求。2、梁箍筋加密范围:加密范
近日,北京、天津、河北省消协组织联合向外卖餐饮服务企业,发出推广使用“推行网络外卖‘食安封签’提振消费信心”的倡议,建议企业施行为外
1、《灵与肉》主要由于小伟、孙茜、黄小蕾、周迅、赵毅、赵立新等人主演。该剧改编自张贤亮的同名小说,讲述了以许灵均为代表的一代支边知识分
1、北京汽车摇号是北京市从2011年开始采取摇号分配车辆指标的措施。实施小客车数量控制措施,按照“公开、公平、公正”的原则,2、对符合条件
1、合唱比赛、歌手赛、选秀、画画、团答辩、团支部评比、优秀团支书评比、其根据政策精神搞。本文就讲到这里,希望大家会喜欢。
1、nian(通二十。2、和‘念’同音)。
2018年平昌冬季奥运会,即第23届冬季奥林匹克运动会,于2018年02月09日至2018年02月25日在韩国平昌举行2011年07月06日在南非德班举行的国际
1、给QQ我慢慢教篇幅限nmm用(强烈建议)另外能用启项加载用nmm。本文到此分享完毕,希望对大家有所帮助。
本文目录一览:1、如何评估预测分数?2、高考估分是什么意思估分需要注意哪些事项3、高考之后如何估分
新京报贝壳财经讯(记者王琳琳)3月25日,施耐德电气董事长、首席执行官赵国华在“中国发展高层论坛2023”上表示,互联网改变了我们的生活,物
截至2023年3月24日收盘长城科技603897报收于217元上涨289换手率189成交量389万手成交额837836万元3月24日的资金流向数据方面主力资金净流入656
1、ALIASMENTALRAY最好的渲染软件FOR3DMAYA,这是相当专业的渲染软件,有ALIAS公司出品,和同公司的MAYA软件以及AUTODE
1、西耀高速是西安至铜川新区至黄陵高速公路是国家高速公路规划网包头至茂名(G65w)纵向线在陕西的重要路段,也是陕西省“
3月24日北向资金增持33 79万股翰宇药业。近5个交易日中,获北向资金增持的有3天,累计净增持165 6万股。近20个交易日中,获北向资金增持的有13
贷款延期合同范本第1篇编号:______________借款单位:________(以下简称甲方)贷款银行:____________中国人民建设银行(以下简称乙方)甲方
抗生素的耐药性已成为全球关注的重点问题。24日晚间,国内“超级抗生素”第一股盟科药业(688373 SH)披露了上市后首份年报,2022年公司实现营收
1、《休妻》,作者是七钱。2、《双世宠妃原著:爷我等你休妻》,作者是梵缺。3、《邪帝休妻:狂后乱天下》,作者是北肆晏。4、《一品休妻》,
春风和煦,万物复苏,在广袤的神州大地上,各式鲜花由南至北次第绽放,空气中弥漫着淡淡的花香。春的浪漫,是朵朵白云,是青青田野,是碧水青
Github:https: github com chikuairi bohetuchuang_api
现在养花的朋友越来越多,尤其是在室外,养的花品种繁多,到了开花的季节,五颜六色,非常好看。那么室外种植的花卉有哪些?