vue2-编译入口

简介: 前言在前几个月的时候,从0开始学习vue源码。侧重点在于vue的组件化实现,响应式原理,及渲染实现,主要分析的是vue的运行时代码逻辑。从现在开始我们将学习vue编译相关的代码,因为编译相关的代码主要是和AST的生成,转化有关,所以可能比较晦涩和考验JS操作。我们主要是了解其实现过程,知道其大概的实现逻辑和实现流程即可。本篇文章先从入口开始,先简单分析下其入口逻辑

「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」


前言


在前几个月的时候,从0开始学习vue源码。侧重点在于vue的组件化实现,响应式原理,及渲染实现,主要分析的是vue的运行时代码逻辑。从现在开始我们将学习vue编译相关的代码,因为编译相关的代码主要是和AST的生成,转化有关,所以可能比较晦涩和考验JS操作。我们主要是了解其实现过程,知道其大概的实现逻辑和实现流程即可。

本篇文章先从入口开始,先简单分析下其入口逻辑


入口


我们之前在分析vue实例化的时候知道vue的入口文件是从entry-runtime-with-compiler.js开始的,而我们的render函数也是从那边开始的


if (template) {
  // ...
  const { render, staticRenderFns } = compileToFunctions(template, {
    outputSourceRange: process.env.NODE_ENV !== 'production',
    shouldDecodeNewlines,
    shouldDecodeNewlinesForHref,
    delimiters: options.delimiters,
    comments: options.comments
  }, this)
  options.render = render
  options.staticRenderFns = staticRenderFns
  // ...
}
复制代码


可以发现当我们输入为template时,是通过compileToFunctions来生成render函数的,而在这传入了用户可选配置delimiters分隔符及comments是否保留注释。


compileToFunctions



接下来我们分析下compileToFunctions的实现,其定义在platform/web/compiler中


import { baseOptions } from './options'
import { createCompiler } from 'compiler/index'
const { compile, compileToFunctions } = createCompiler(baseOptions)
export { compile, compileToFunctions }
复制代码


其代码看起来很简单,就是引入了createCompiler并传入参数baseOptions。实际上这边就是将与平台相关的baseOptions定义在了platform目录下面,通过柯里化的实现将平台相关的配置分离在不同目录下,最后再调用统一的编译函数

createCompiler。


baseOptions中定义了和平台相关的配置,如modules(clsaa style model的编译),directives(text html model的编译)及平台相关的保留标签,特殊行为标签等。


createCompiler


接着我们进入到createCompiler实现的分析


export const createCompiler = createCompilerCreator(function baseCompile (
  template,
  options
) {
  // 1
  const ast = parse(template.trim(), options)
  // 2
  if (options.optimize !== false) {
    optimize(ast, options)
  }
  // 3
  const code = generate(ast, options)
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})
复制代码

在createCompiler函数中实际可以看到我们编译的全貌,编译三步曲


  1. parse:将字符粗模板template解析为AST

  2. optimize:优化AST,实际就是操作AST,对其内容进行转化

  3. generate:生成代码字符串,就是将AST再转化为字符串

实际上这三步也对应着我之前分析过的babel编译原理中的解析->转化->生成。

当然,我们分析的是baseCompile中的逻辑,我们最终的函数实际由createCompilerCreator生成的,所以我们还是回到入口的分析上。


createCompilerCreator

export function createCompilerCreator (baseCompile: Function): Function {
  return function createCompiler (baseOptions: CompilerOptions) {
    function compile (
      template,
      options
    ): CompiledResult {
      // 1
      const finalOptions = Object.create(baseOptions)
      const errors = []
      const tips = []
      let warn = (msg, range, tip) => {
        (tip ? tips : errors).push(msg)
      }
      if (options) {
        // ...
        // 2
        // 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 || null),
            options.directives
          )
        }
        // 3
        // copy other options
        for (const key in options) {
          if (key !== 'modules' && key !== 'directives') {
            finalOptions[key] = options[key]
          }
        }
      }
      finalOptions.warn = warn
      // 4
      const compiled = baseCompile(template.trim(), finalOptions)
      if (process.env.NODE_ENV !== 'production') {
        detectErrors(compiled.ast, warn)
      }
      compiled.errors = errors
      compiled.tips = tips
      return compiled
    }
    return {
      compile,
      compileToFunctions: createCompileToFunctionFn(compile)
    }
  }
}
复制代码

createCompilerCreator的代码算不上简短,但是逻辑和步骤比较明确且简单,主要是对参数做些预处理,最后再返回新的函数compileToFunctions


  1. 拷贝平台编译的默认配置baseOptions


  2. 合并开发者传入的配置选项modules及directives

  3. 开发者配置替代默认配置除modules和directives,可以看出不同的配置有不同的策略

  4. 将处理好的最终配置传入baseCompile并进行上面提到的编译三步曲

createCompileToFunctionFn


在上面我们分析的操作都是定义在compile函数中的逻辑,在最终的返回值中,实际还会将compile作为参数传给createCompileToFunctionFn。


我们接下来再看看createCompileToFunctionFn的实现


export function createCompileToFunctionFn (compile: Function): Function {
  // 1
  const cache = Object.create(null)
  return function compileToFunctions (
    template: string,
    options?: CompilerOptions,
    vm?: Component
  ): CompiledFunctionResult {
    options = extend({}, options)
    const warn = options.warn || baseWarn
    delete options.warn
    // ...
    // 2
    // check cache
    const key = options.delimiters
      ? String(options.delimiters) + template
      : template
    if (cache[key]) {
      return cache[key]
    }
    // 3
    // compile
    const compiled = compile(template, options)
    // ...
    // 4
    // turn code into functions
    const res = {}
    const fnGenErrors = []
    res.render = createFunction(compiled.render, fnGenErrors)
    res.staticRenderFns = compiled.staticRenderFns.map(code => {
      return createFunction(code, fnGenErrors)
    })
    // ...
    return (cache[key] = res)
  }
}
复制代码


为了避免贴的源码过长,我省略了一些在开发环境中的错误提示代码,但感觉还是有必要说一说省略的三处逻辑



  1. 检查当前运行环境是否能运行new Function,如果不能(配置了无法运行new Function的CSP)则报错。因为编译将字符串转化成函数就是通过new Function实现的。


  2. 检查compiled的errors/tips配置,应该是和sourceMap相关。

  3. 编译中出现错误的抛出,例如Failed to generate render function


说完了被我们打上省略号的步骤,我们再来分析下createCompileToFunctionFn的主要逻辑。


  1. 定义了闭包变量cache用于存储模板编译结果,因为template是实际是不可变的字符串,无论数据如何变化,模板是一样的,所以我们可以存下它的编译结果,只在第一次进行编译就行。

  2. 模板的缓存逻辑,在这可以看到对于同一段模板,我们将用户配置delimiters也存进缓存的key,因为这有可能会影响同一个模板编译结果。

  3. 实际是将上步骤中定义的函数compile在此进行真正的执行
  4. 将compile的结果字符串通过new Function生成函数


我们最先在入口看到的函数实际就是执行了本步返回值compileToFunctions,通过函数名就能知道。只是vue通过了不断的闭包,函数返回函数,将不同的步骤拆分到不同的函数中执行,所以刚开始分析的时候会感觉藏得挺深的。


梳理


我们前面从入口开始,通过一步步的分析最后纠出最终的编译函数compileToFunctions,有些层层递进的感觉,也有点云里雾里的感觉。那我们再来做个全面的梳理,进一步理解各函数的逻辑关系。


56.png



结语

编译入口的分析比较简单,主要是了解下vue中不同的模块中对入口进行了不同的预处理及配置传入。后面我们将继续分析下vue中编译实现的三个主要逻辑。



相关文章
|
17天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
8488 24
|
16天前
|
人工智能 并行计算 PyTorch
秋叶 ComfyUI 2026 整合包 v3.2 完整部署教程:Python 3.13 + Torch 2.13 全栈升级
秋叶aaaki ComfyUI 2026年8月整合包v3.2正式发布!全面升级Python 3.13.11、PyTorch 2.13.0+cu130及ComfyUI v0.30.2,原生支持MiniMax H3、Wan 2.2、Qwen-Image-2.1等2026主流音视频/图像模型,解压即用,无需环境配置。
2852 14
|
15天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
2024 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
14天前
|
人工智能 编解码 并行计算
MiniMax-H3 一键整合包技术文档:8G 显存运行 AI 漫剧制作 —— 角色替换 / 动作迁移 / 文图生视频部署与调参指南
MiniMax H3 是 MiniMax 开源的全模态视频生成模型,支持文/图/音/视多条件输入,输出最高2K、15秒带双声道音频视频。本文档详述其Int8量化版在8GB显存下的本地一键部署、三段式工作流(EDIT/REPLACE/CONTINUE)、参数调优及常见问题排查。(239字)
|
10天前
|
人工智能 Linux 开发者
【2026国内使用】Codex安装过程一篇讲透(Win/Mac/Linux全支持)
Codex是OpenAI推出的AI编程智能体,可读取本地项目、理解需求并自动修改代码。支持桌面GUI、命令行(CLI)及VS Code/Cursor插件三种形态,覆盖可视化操作、终端高效开发与编辑器无缝集成场景,助开发者用自然语言驱动编码全流程。(239字)
【2026国内使用】Codex安装过程一篇讲透(Win/Mac/Linux全支持)
|
4天前
|
人工智能 JSON Linux
【全网最详细】ComfyUI使用教程:下载+本地部署+配置+工作流搭建一篇搞定(2026最新版)
ComfyUI是一款免费开源的本地AI绘图工具,采用节点式工作流设计,支持文生图、图生图、局部重绘、放大、换脸等多种功能。可离线运行,依赖显卡加速,无需联网。支持自定义流程保存与分享,插件生态丰富,适合进阶用户。(239字)
|
10天前
|
人工智能 JSON 编解码
【2026最新版】ComfyUI本地部署教程,新手也能看懂!
ComfyUI是本地运行的AI绘画工具,采用节点式工作流设计:通过拖拽连接“加载模型”“提示词编码”“采样”“解码”等模块,实现高度可控的文生图。新手推荐使用秋叶整合包,一键启动、内置模型管理与插件安装器,轻松上手。(239字)

热门文章

最新文章