zoukankan      html  css  js  c++  java
  • vue-compile概述

    来源 刘涛

    Vue的核心可以分为三个大块:数据处理和双向绑定模板编译虚拟dom

    前面我们对第一部分的主要内容双向绑定做了一个分析讲解,接下来我们说一说模板编译。

    这一部分的内容比较多,也比较复杂。由于所涉及的情况太多了,我也不可能把每一种情况都覆盖到。

    尽量做到既不啰嗦,又能分享更多的内容。前面我们也提到过,模板编译分为三个阶段:

    生成ast优化静态内容生成render

    官方文档中提到rendertemplate更加底层,许多人对render函数都不是很明白,

    相信通过这一部分的分析,你可以对render基本做到了如指掌

    如果你在创建对象时直接传入render函数,模板编译这一步就可以直接跳过,这样效率肯定更高,

    (react就是不用模板编译的, 如果vue比react效率高,那就是因为需要编译jsx?

    但同时我们编写代码的难度会增加很多。实际开发过程中,根据需要,恰当选择。

    Vue对象上有一个全局函数compile,在src/entries/web-runtime-with-compiler.js中。

    import { compileToFunctions } from 'web/compiler/index'
    ...
    ...
    ...
    Vue.compile = compileToFunctions

    该方法来自src/platforms/web/compiler/index文件。

    import { isUnaryTag, canBeLeftOpenTag } from './util'
    import { genStaticKeys } from 'shared/util'
    import { createCompiler } from 'compiler/index'
    
    import modules from './modules/index'
    import directives from './directives/index'
    
    import {
      isPreTag,
      mustUseProp,
      isReservedTag,
      getTagNamespace
    } from '../util/index'
    
    export const baseOptions: CompilerOptions = {
      expectHTML: true,
      modules,
      directives,
      isPreTag,
      isUnaryTag,
      mustUseProp,
      canBeLeftOpenTag,
      isReservedTag,
      getTagNamespace,
      staticKeys: genStaticKeys(modules)
    }
    
    const { compile, compileToFunctions } = createCompiler(baseOptions)
    export { compile, compileToFunctions }

    该文件中主要定义了一个baseOptions,它主要保存了解析模板时和平台相关的一些配置

    对应的src/platforms/weex/compiler/index中也有一份名称一样的配置。

    这里我们简单说说web相关的这些配置都是什么意思。

    • expectHTML。目前具体还不是很明白,weex中没有改项,从字面意思来看,应该是是否期望HTML

    • modules。包括klassstyle,对模板中类和样式的解析。

    • directives。这里包括modelv-model)、htmlv-html)、text(v-text)三个指令。

    • isPreTag。是否是pre标签。

    • isUnaryTag。是否是单标签,比如imginputiframe等。

    • mustUseProp。需要使用props绑定的属性,比如valueselected等。

    • canBeLeftOpenTag。可以不闭合的标签,比如trtd等。

    • isReservedTag。是否是保留标签html标签和SVG标签。

    • getTagNamespace。获取命名空间svgmath

    • staticKeys。静态关键词,包括staticClass,staticStyle

    上面这些方法或者属性,在编译模板时会用到。

    这里知识简单的列出来它们的用途,方便看一眼。

    我们来看createCompiler函数的实现。

    export function createCompiler (baseOptions: CompilerOptions) {
      const functionCompileCache: {
        [key: string]: CompiledFunctionResult;
      } = Object.create(null)
    
      // compile 函数的实现
    
      // compileToFunctions 函数的实现
    
      return {
        compile,
        compileToFunctions
      }
    }

    该函数只是compilecompileToFunctions的简单封装,开始定义了functionCompileCache

    它用来缓存编译之后的模板,方便之后复用

    因为compileToFunctions里面调用了compile,所以我们先看一下compile

      function compile (
        template: string,
        options?: CompilerOptions
      ): CompiledResult {
        const finalOptions = Object.create(baseOptions)
        const errors = []
        const tips = []
        finalOptions.warn = (msg, tip) => {
          (tip ? tips : errors).push(msg)
        }
    
        if (options) {
          // merge custom modules
          if (options.modules) {
            finalOptions.modules = (baseOptions.modules || []).concat(options.modules)
          }
          // merge custom directives
          if (options.directives) {
            finalOptions.directives = extend(
              Object.create(baseOptions.directives),
              options.directives
            )
          }
          // copy other options
          for (const key in options) {
            if (key !== 'modules' && key !== 'directives') {
              finalOptions[key] = options[key]
            }
          }
        }
    
        const compiled = baseCompile(template, finalOptions)
        if (process.env.NODE_ENV !== 'production') {
          errors.push.apply(errors, detectErrors(compiled.ast))
        }
        compiled.errors = errors
        compiled.tips = tips
        return compiled
      }

    我们从上往下,依次看看它都做了哪些事儿。

    它接收两个参数templateoptionstemplate不用过多解释,

    options在内部主要是用户自己定义的delimiters

    finalOptions继承自我们上面提到的baseOptions

    并添加了一个搜集错误的warn方法,然后合并了options传入的各种配置选项。

    modulesdirectives合并方法不同是因为modules是数组,而directives是一个对象。

    baseCompile中执行的就是模板编译的三个重要步骤,后面我们会详细讲解。

    最终,返回编译之后的对象。

    function compileToFunctions (
        template: string,
        options?: CompilerOptions,
        vm?: Component
      ): CompiledFunctionResult {
        options = options || {}
    
        ...
        // check cache
        const key = options.delimiters
          ? String(options.delimiters) + template
          : template
        if (functionCompileCache[key]) {
           return functionCompileCache[key]
        }
    
        // compile
        const compiled = compile(template, options)
    
        // check compilation errors/tips
        if (process.env.NODE_ENV !== 'production') {
          if (compiled.errors && compiled.errors.length) {
            warn(
              `Error compiling template:
    
    ${template}
    
    ` +
              compiled.errors.map(e => `- ${e}`).join('
    ') + '
    ',
              vm
            )
          }
          if (compiled.tips && compiled.tips.length) {
            compiled.tips.forEach(msg => tip(msg, vm))
          }
        }
    
        // turn code into functions
        const res = {}
        const fnGenErrors = []
        res.render = makeFunction(compiled.render, fnGenErrors)
        const l = compiled.staticRenderFns.length
        res.staticRenderFns = new Array(l)
    for (let i = 0; i < l; i++) { res.staticRenderFns[i] = makeFunction(compiled.staticRenderFns[i], fnGenErrors) } if (process.env.NODE_ENV !== 'production') {
    if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { warn( `Failed to generate render function: ` + fnGenErrors.map(({ err, code }) => `${err.toString()} in ${code} `).join(' '), vm ) } } return (functionCompileCache[key] = res) }

    compileToFunctions函数中,首先从缓存中获取编译结果,没有则调用compile函数来编译。

    在开发环境,我们在这里会抛出编译过程中产生的错误,

    最终返回一个含有render函数,和staticRenderFns数组的对象,并把它放在缓存中。

    这里我们使用makeFunction来创建函数。

    function makeFunction (code, errors) {
      try {
        return new Function(code)
      } catch (err) {
        errors.push({ err, code })
        return noop
      }
    }

    很简单,就是利用了我们new Function,并搜集了错误。

    compilecompileToFunctions两个方法的不同之处有以下几点。

    1、 compile返回的结果中render是字符串,staticRenderFns是字符串组成的数组,而compileToFunctions中把它们变成了函数。

    2、 compile返回的结果中,有模板生成的ast和搜集到的错误。而compileToFunctions对其结果进行了一些处理。

  • 相关阅读:
    [LeetCode] 952. Largest Component Size by Common Factor 按公因数计算最大部分大小
    [LeetCode] 951. Flip Equivalent Binary Trees 翻转等价二叉树
    [LeetCode] 950. Reveal Cards In Increasing Order 按递增顺序显示卡牌
    上周热点回顾(7.6-7.12)团队
    上周热点回顾(6.29-7.5)团队
    上周热点回顾(6.22-6.28)团队
    调用博客园 open api 的客户端示例代码团队
    【故障公告】阿里云 RDS 实例 CPU 100% 故障引发全站无法正常访问团队
    上周热点回顾(6.15-6.21)团队
    《证券投资顾问胜任能力考试》考试大纲
  • 原文地址:https://www.cnblogs.com/dhsz/p/7685056.html
Copyright © 2011-2022 走看看