当前位置: 首页 > news >正文

江西建网站温州做网站最好的

江西建网站,温州做网站最好的,易县网站建设,北京网首页文章目录 一、编译流程1. 解读入口文件 packgages/vue/index.ts2. compile函数的运行流程 二、AST 解析器1. ast 的生成2. 创建ast的根节点3. 解析子节点 parseChildren#xff08;关键#xff09;4. 解析模版元素 Element模版元素解析-举例分析 一、编译流程 1. 解读入口文… 文章目录 一、编译流程1. 解读入口文件 packgages/vue/index.ts2. compile函数的运行流程 二、AST 解析器1. ast 的生成2. 创建ast的根节点3. 解析子节点 parseChildren关键4. 解析模版元素 Element模版元素解析-举例分析 一、编译流程 1. 解读入口文件 packgages/vue/index.ts 首先从Vue对象的入口开始packgages/vue/index.ts文件中只有compileToFunction函数 依赖注入编译函数至runtimeregisterRuntimeCompiler(compileToFunction)runtime 调用编译函数compileToFunction返回包含code的编译结果将code作为参数传入Function 的构造函数将生成函数赋值给render变量。将render函数作为编译结果返回 下面这个简单的模版 templatedivHello World/div /template经过编译后code返回的字符串为 const _Vue Vue return function render(_ctx, _cache) {with(_ctx) {const {openBlock: _openBlock, createBlock:_createBlock} _Vue;return (_openBlock(), _createBlock(div, null, Hello World)) } }拿到这个代码字符串的结果后第25行声明了一个render变量并将生成的代码字符串code 作为参数传入了new Function 构造函数生成了render函数。可以将上面的code字符串格式化。这里的render显而易见是一个柯里化的函数返回了一个函数函数内部通过with来扩展作用域链。最后入口文件返回了render变量并顺手缓存了render函数。在第一行入口文件创建了一个compileCache 对象用以缓存compileToFunction 函数生成的render 函数将template 参数作为缓存的key并在11行进行if分支做缓存判断如果该模版之前被缓存过则不再进行编译直接返回缓存中的render函数以此提高性能。 2. compile函数的运行流程 compile函数涉及到compile-dom 和compile-core 两个模块。 compile的运行流程 baseCompile命名理由因为compile-core是编译的核心模块接收外部的参数来按照规则完成编译而compile-dom是专门处理浏览器场景下的编译在这个模块下导出的compile函数是入口文件真正接收的编译函数。而compile-dom中的compile函数相对baseCompile也是一个更高阶的编译器。例如当Vue在weex或iOS或Android这些Native App中工作时compile-dom可能会被相关的移动端编译库来取代。baseCompile函数 从函数声明中看baseCompile接收template模版以及上层高阶编译器处理过的options编译选项最终返回一个CodegenResult类型的编译结果。 export interface CodegenResult {code: stringpreamble: stringast: RootNodemap?: RawSourceMap }看上方源码的第12行判断template模版是否为字符串如果是的话则会对字符串进行解析否则直接将template作为AST。我们平时写的vue代码都是以字符串的形式传递进去的。然后是第16行调用了transform函数以及传入了指令转换、节点等工具函数对由模版生成的AST进行转换。最后32行将转换好的ast传入进generate生成CodegenResult类型的返回结果。 二、AST 解析器 1. ast 的生成 ast的生成有一个三目运算符的判断如果传进来的template模版是一个字符串那么则调用baseParse解析模版字符串否则直接将template作为ast对象。 baseParse 函数 export function baseParse(content: string,options: ParserOptions {} ): RootNode {const context createParserContext(content, options) // 创建解析的上下文对象const start getCursor(context) // 生成记录解析过程的游标信息return createRoot( // 生成并返回 root 根节点parseChildren(context, TextModes.DATA, []), // 解析子节点作为 root 根节点的 children 属性getSelection(context, start)) }首先会创建解析的上下文根据上下文获取游标信息由于还未进行解析所以游标中的column、line、offset属性对应的都是template的起始位置。之后就是创建根节点并返回根节点至此ast树生成解析完成。 2. 创建ast的根节点 export function createRoot(children: TemplateChildNode[],loc locStub ): RootNode {return {type: NodeTypes.ROOT,children,helpers: [],components: [],directives: [],hoists: [],imports: [],cached: 0,temps: 0,codegenNode: undefined,loc} }该函数返回了一个RootNode类型的根节点对象其中我们传入的children参数会被作为根节点的children参数。 3. 解析子节点 parseChildren关键 function parseChildren(context: ParserContext,mode: TextModes,ancestors: ElementNode[] ): TemplateChildNode[] {const parent last(ancestors) // 获取当前节点的父节点const ns parent ? parent.ns : Namespaces.HTMLconst nodes: TemplateChildNode[] [] // 存储解析后的节点// 当标签未闭合时解析对应节点while (!isEnd(context, mode, ancestors)) {/* 忽略逻辑 */}// 处理空白字符提高输出效率let removedWhitespace falseif (mode ! TextModes.RAWTEXT mode ! TextModes.RCDATA) {/* 忽略逻辑 */}// 移除空白字符返回解析后的节点数组return removedWhitespace ? nodes.filter(Boolean) : nodes }parseChildren函数接收三个参数context解析器上下文mode文本数据类型ancestors祖先节点数据。函数执行首先会从祖先节点中获取当前节点的父节点确定命名空间以及创建一个空数组用来存储解析后的节点。之后会有一个while循环判断是否到达了标签的关闭位置如果不是需要关闭的标签则在循环体内对源模版字符串进行分类解析。之后会有一段处理空白字符的逻辑处理完成后返回解析好的nodes数组。 while循环内的逻辑函数的核心 在while中会判断文本数据的类型只有当TextModes为DATA或RCDATA时会继续往下解析。 第一种情况就是判断是否需要解析Vue模版语法中的Mustache语法如果当前上下文中没有v-pre指令来跳过表达式并且源模版字符串是以我们指定的分隔符开头的就会进行双大括号的解析。接下来会判断如果第一个字符是并且第二个字符是! 会尝试解析注释标签!DOCTYPE和!CDATA这三种情况对于DOCTYPE会进行忽略解析成注释。之后会判断当第二个字符是/的情况/已经满足了一个闭合标签的条件了所以会尝试匹配闭合标签。当第三个标签是缺少了标签名字会报错并让解析器的进度前进三个字符跳过/。如果是/并且第三个字符是小写英文字符解析器会解析结束标签。如果源模版字符串的第一个字符是第二个字符是小写英文字符开头会调用parseElement函数来解析对应的标签。当这个判断字符串字符的分支条件结束并且没有解析出任何node节点则会将node作为文本类型调用parseText进行解析。最后将生成的节点添加进nodes数组在函数结束时返回。 while循环的源码如下 while (!isEnd(context, mode, ancestors)) {const s context.sourcelet node: TemplateChildNode | TemplateChildNode[] | undefined undefinedif (mode TextModes.DATA || mode TextModes.RCDATA) {if (!context.inVPre startsWith(s, context.options.delimiters[0])) {/* 如果标签没有 v-pre 指令源模板字符串以双大括号 {{ 开头按双大括号语法解析 */node parseInterpolation(context, mode)} else if (mode TextModes.DATA s[0] ) {// 如果源模板字符串的第以个字符位置是 !if (s[1] !) {// 如果以 !-- 开头按注释解析if (startsWith(s, !--)) {node parseComment(context)} else if (startsWith(s, !DOCTYPE)) {// 如果以 !DOCTYPE 开头忽略 DOCTYPE当做伪注释解析node parseBogusComment(context)} else if (startsWith(s, ![CDATA[)) {// 如果以 ![CDATA[ 开头又在 HTML 环境中解析 CDATAif (ns ! Namespaces.HTML) {node parseCDATA(context, ancestors)}}// 如果源模板字符串的第二个字符位置是 /} else if (s[1] /) {// 如果源模板字符串的第三个字符位置是 那么就是自闭合标签前进三个字符的扫描位置if (s[2] ) {emitError(context, ErrorCodes.MISSING_END_TAG_NAME, 2)advanceBy(context, 3)continue// 如果第三个字符位置是英文字符解析结束标签} else if (/[a-z]/i.test(s[2])) {parseTag(context, TagType.End, parent)continue} else {// 如果不是上述情况则当做伪注释解析node parseBogusComment(context)}// 如果标签的第二个字符是小写英文字符则当做元素标签解析} else if (/[a-z]/i.test(s[1])) {node parseElement(context, ancestors)// 如果第二个字符是 ?当做伪注释解析} else if (s[1] ?) {node parseBogusComment(context)} else {// 都不是这些情况则报出第一个字符不是合法标签字符的错误。emitError(context, ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME, 1)}}}// 如果上述的情况解析完毕后没有创建对应的节点则当做文本来解析if (!node) {node parseText(context, mode)}// 如果节点是数组则遍历添加进 nodes 数组中否则直接添加if (isArray(node)) {for (let i 0; i node.length; i) {pushNode(nodes, node[i])}} else {pushNode(nodes, node)} }4. 解析模版元素 Element parseElement精简源码如下 function parseElement(context: ParserContext,ancestors: ElementNode[] ): ElementNode | undefined {// 解析起始标签const parent last(ancestors)const element parseTag(context, TagType.Start, parent)// 如果是自闭合的标签或者是空标签则直接返回。voidTag例如 img, br, hrif (element.isSelfClosing || context.options.isVoidTag(element.tag)) {return element}// 递归的解析子节点ancestors.push(element)const mode context.options.getTextMode(element, parent)const children parseChildren(context, mode, ancestors)ancestors.pop()element.children children// 解析结束标签if (startsWithEndTagOpen(context.source, element.tag)) {parseTag(context, TagType.End, parent)} else {emitError(context, ErrorCodes.X_MISSING_END_TAG, 0, element.loc.start)if (context.source.length 0 element.tag.toLowerCase() script) {const first children[0]if (first startsWith(first.loc.source, !--)) {emitError(context, ErrorCodes.EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT)}}}// 获取标签位置对象element.loc getSelection(context, element.loc.start)return element }首先会获取当前节点的父节点再调用parseTag()函数解析。 parseTag()函数执行流程 匹配标签名解析元素中的attribute属性存储至props属性检测是否存在v-pre属性如果存在则修改context上下文中的inVPre属性为true。检测自闭合标签如果是自闭合则将isSelfClosing属性置为true。判断tagType是Element还是component组件或slot插槽。返回生成的element对象 获取到 element对象后会判断element是否是自闭合标签或空标签例如img、br、hr如果是这种情况直接返回element对象。然后解析element的子节点把element压入栈中然后递归调用parseChildren来解析子节点。 const parent last(ancestors) 在将element入栈后拿到的父节点就是当前节点。 解析完毕后调用ancestors.pop()让当前解析完子节点的element对象出栈将解析后的children对象赋值给element的children属性完成element的子节点解析。最后匹配结束标签设置element的Ioc位置信息返回解析完毕的 element 对象。 模版元素解析-举例分析 divpHello World/p /div
http://wiki.neutronadmin.com/news/130845/

相关文章:

  • 网站一般做多大的全面的郑州网站建设
  • 教育网站 怎么做吸引人安徽天长建设局网站
  • 网站建设和空间网站建设网站营销网站托管一体化
  • 做网站需要用什麼服务器免费ppt模板下载知乎
  • 做的网站怎么发网上长沙网页设计培训服务好长沙大计校区
  • 河北省正定县城乡建设网站成都网站建设有名的公司
  • 网站建设 事项互联网公司排名朗玛
  • 帮传销做网站广告设计工资高吗
  • 在微信上做彩票网站广告传媒公司名字取名
  • 网站开发四川网站中flash
  • 售卖网站建设实验报告wordpress两步验证码
  • 一个网站大概多少钱做网站便宜的公司
  • 昔阳做网站公司微商营销技巧
  • 网站建设公司无锡做网站的收益来源
  • 网站制作框架php网站后台程序
  • 建设厅网站公司人员管理系统
  • 电子商务 网站模板设备技术支持东莞网站建设
  • 从电子商务网站f型眼球轨迹分析其网站布局百度入口网站
  • 免费承接网站建设html网站系统
  • 什么是分类信息网站营销装修公司网页设计模板
  • 做图文网站要什么配置的服务器百度问答兼职怎么做
  • 用什么网站做浏览器主页北京seo网络推广
  • 郑州高端做网站博客一号WordPress
  • 免费设计图网站南宁网页制作步骤
  • 建设工程合同模板seo兼职工资一般多少
  • 北京网站设计开发公司大学网站的设计方案
  • 网站图片翻页效果如何做移动网站建站视频教程
  • 朝阳区网站建设推广seo出口订单哪里找
  • 游民星空是谁做的网站简书 wordpress
  • 微信网站设计建网站需要什么要求