Vue模板是怎样编译的

本文深入探讨Vue模板的解析编译,主要涉及parse函数,解析过程包括:通过正则匹配文本、注释和标签,逐步构建AST抽象语法树。解析字符串时,使用split字符方法逐步解析,并调用相应钩子函数进行处理,最终生成AST。
摘要由CSDN通过智能技术生成

这一章我们开始讲模板解析编译:总结来说就是通过compile函数把tamplate解析成render Function形式的字符串compiler/index.js

import {
    parse } from './parser/index'
import {
    optimize } from './optimizer'
import {
    generate } from './codegen/index'
import {
    createCompilerCreator } from './create-compiler'

// `createCompilerCreator` allows creating compilers that use alternative
// parser/optimizer/codegen, e.g the SSR optimizing compiler.
// Here we just export a default compiler using the default parts.
export const createCompiler = createCompilerCreator(function baseCompile (
  template: string,
  options: CompilerOptions
): CompiledResult {
   
  const ast = parse(template.trim(), options)
  if (options.optimize !== false) {
   
    optimize(ast, options)
  }
  const code = generate(ast, options)
  return {
   
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})

我们可以看出createCompiler函数内部运行的是parseoptimizegenerate三个函数,而生成的是astrenderstaticRenderFns三个对象

parse

export function parse (
  template: string,
  options: CompilerOptions
): ASTElement | void {
   
  /**   * 有自定义warn用自定义没有用基础: console.error(`[Vue compiler]: ${msg}`)   */
  warn = options.warn || baseWarn
  // 检查标签是否需要保留空格
  platformIsPreTag = options.isPreTag || no
  // 检查属性是否应被绑定
  platformMustUseProp = options.mustUseProp || no
  // 检查标记的名称空间
  platformGetTagNamespace = options.getTagNamespace || no

  /**   * 获取modules中的值   */
  transforms = pluckModuleFunction(options.modules, 'transformNode')
  preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
  postTransforms = pluckModuleFunction(options.modules, 'postTransformNode')

  delimiters = options.delimiters

  const stack = []
  // 是否保留elements直接的空白
  const preserveWhitespace = options.preserveWhitespace !== false
  let root //return 出去的AST
  let currentParent //当前父节点
  let inVPre = false
  let inPre = false
  let warned = false
  /**   * 单次警告   */
  function warnOnce (msg) {
   
    if (!warned) {
   
      warned = true
      warn(msg)
    }
  }

  function closeElement (element) {
   
    // check pre state
    if (element.pre) {
   
      inVPre = false
    }
    if (platformIsPreTag(element.tag)) {
   
      inPre = false
    }
    // apply post-transforms
    for (let i = 0; i < postTransforms.length; i++) {
   
      postTransforms[i](element, options)
    }
  }

  parseHTML(template, {
   
    warn,
    expectHTML: options.expectHTML,
    isUnaryTag: options.isUnaryTag,
    canBeLeftOpenTag: options.canBeLeftOpenTag,
    shouldDecodeNewlines: options.shouldDecodeNewlines,
    shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref,
    shouldKeepComment: options.comments,
    start (tag, attrs, unary) {
   
      // check namespace.
      // inherit parent ns if there is one
      /**       * 检查命名空间。如果有父nanmespace,则继承父nanmespace       */
      const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag)

      // handle IE svg bug
      /* istanbul ignore if */
      // IE的另类bug
      if (isIE && ns === 'svg') {
   
        attrs = guardIESVGBug(attrs)
      }
      // 返回应对的AST
      let element: ASTElement = createASTElement(tag, attrs, currentParent)
      if (ns) {
   
        element.ns = ns
      }
      /**       * 不是服务段渲染的时候,template 应该只负责渲染UI部分       * 不应该包含syle, script 的标签       */
      if (isForbiddenTag(element) && !isServerRendering()) {
   
        element.forbidden = true
        process.env.NODE_ENV !== 'production' && warn(
          'Templates should only be responsible for mapping the state to the ' +
          'UI. Avoid placing tags with side-effects in your templates, such as ' +
          `<${
     tag}>` + ', as they will not be parsed.'
        )
      }

      // apply pre-transforms
      // 预处理
      for (let i = 0; i < preTransforms.length; i++) {
   
        element = preTransforms[i](element, options) || element
      }

      if (!inVPre) {
   
        processPre(element)
        if (element.pre) {
   
          inVPre = true
        }
      }
      // 检测该标签是否需要保留空格
      if (platformIsPreTag(element.tag)) {
   
        inPre = true
      }
      if (inVPre) {
   
        // 当不需要转译时
        processRawAttrs(element)
      } else if (!element.processed) {
   
        // structural directives
        // 给AST加上v-for响应属性
        processFor(element)
        // 给AST加上v-if v-else v-else-if相应属性
        processIf(element)
        // 判断是否含有v-once
        processOnce(element)
        // element-scope stuff
        processElement(element, options)
      }

      function checkRootConstraints (el) {
   
        if (process.env.NODE_ENV !== 'production') {
   
          // 根标签不应该是slot和template
          if (el.tag === 'slot' || el.tag === 'template') {
   
            warnOnce(
              `Cannot use <${
     el.tag}> as component root element because it may ` +
              'contain multiple nodes.'
            )
          }
          // 根标签不应该含有v-for
          if (el.attrsMap.hasOwnProperty('v-for')) {
   
            warnOnce(
              'Cannot use v-for on stateful component root element because ' +
              'it renders multiple elements.'
            )
          }
        }
      }

      // tree management
      // 赋值给跟标签
      if (!root) {
   
        root = element
        //  用于检查根标签
        checkRootConstraints(root)
        // 缓存中是否有值
      } else if (!stack.length) {
   
        // allow root elements with v-if, v-else-if and v-else
        // 如果根元素有v-if, v-else-if and v-else 则打上响应记号
        if (root.if && (element.elseif || element.else)) {
   
          checkRootConstraints(element)
          addIfCondition(root, {
   
            exp: element.elseif,
            block: element
          })
        } else if (process.env.NODE_ENV !== 'production') {
   
          warnOnce(
            `Component template should contain exactly one root element. ` +
            `If you are using v-if on multiple elements, ` +
            `use v-else-if to chain them instead.`
          )
        }
      }
      if (currentParent && !element.forbidden) {
   
        if (element.elseif || element.else) {
   
          processIfConditions(element, currentParent)
        } else if (element.slotScope) {
    // scoped slot
          // 处理slot, scoped传值
          currentParent.plain = false
          const name = element.slotTarget || '"default"'
          ;(currentParent.scopedSlots || (currentParent.scopedSlots = {
   }))[name] = element
        } else {
   
          currentParent
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值