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

compiler-core核心原理

1.怎么使用 compiler-core

import { baseCompile } from '@vue/compiler-core';

const templates = `<div @click="handleClick">Hello, {{ name }}!</div>`;
const result = baseCompile(templates, {  prefixIdentifiers: true});
console.log(result)

运行之后的结果是一个对象,属性 ast 表示抽象语法树,属性 code 表示根据 ast 生成出来的可执行代码也就是 render 函数。preamble 和 map 暂时用不到。

{
  ast: {
    type: 0,
    source: '<div @click="handleClick">Hello, {{ name }}!</div>',
    children: [ [Object] ],
    helpers: Set(3) {
      Symbol(toDisplayString),
      Symbol(openBlock),
      Symbol(createElementBlock)
    },
    components: [],
    directives: [],
    hoists: [],
    imports: [],
    cached: [],
    temps: 0,
    codegenNode: {
      type: 13,
      tag: '"div"',
      props: [Object],
      children: [Object],
      patchFlag: 9,
      dynamicProps: '["onClick"]',
      directives: undefined,
      isBlock: true,
      disableTracking: false,
      isComponent: false,
      loc: [Object]
    },
    loc: {
      start: [Object],
      end: [Object],
      source: '<div @click="handleClick">Hello, {{ name }}!</div>'
    },
    transformed: true,
    filters: []
  },
  code: 'const { toDisplayString: _toDisplayString, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue\n' +
    '\n' +
    'return function render(_ctx, _cache) {\n' +
    '  return (_openBlock(), _createElementBlock("div", { onClick: _ctx.handleClick }, "Hello, " + _toDisplayString(_ctx.name) + "!", 9 /* TEXT, PROPS */, ["onClick"]))\n' +
    '}',
  preamble: '',
  map: undefined
}
  1. 生成的 render 函数能用来干嘛?
import * as Vue from 'vue';
const render = new Function('Vue', result.code)(Vue);

const app = createApp({
  setup(props, { expose }) {
    const name = ref('world');
    const handleClick = () => {
      name.value = 'hello'
    };
    return () => {
      return render({ name, handleClick })
    }
  }
});
app.mount('#app')

了解 baseCompile 编译原理

源码位置:https://github.com/vuejs/core/blob/main/packages/compiler-core/src/compile.ts

export function baseCompile(
  source: string | RootNode,
  options: CompilerOptions = {},
): CodegenResult {

  const resolvedOptions = extend({}, options, {
    prefixIdentifiers,
  })
  const ast = isString(source) ? baseParse(source, resolvedOptions) : source
  const [nodeTransforms, directiveTransforms] =
    getBaseTransformPreset(prefixIdentifiers)

  transform(
    ast,
    extend({}, resolvedOptions, {
      nodeTransforms: [
        ...nodeTransforms,
        ...(options.nodeTransforms || []), // user transforms
      ],
      directiveTransforms: extend(
        {},
        directiveTransforms,
        options.directiveTransforms || {}, // user transforms
      ),
    }),
  )

  return generate(ast, resolvedOptions)
}

步骤1,通过 baseParse 函数是把 HTML 字符串处理为ast语法树,但是这个时候没有处理指令语法。
步骤2,通过 transform 函数遍历 ast 上的每个节点,处理节点上的指令信息。

这个地方有点是vue3比较好的设计思路,把生成ast和处理ast的逻辑分开了,框架维护起来也方便。这个思路根 babel 很类似。

Babel 的遍历实现

Babel 的遍历是通过 @babel/traverse 模块实现的。这个模块提供了一个 traverse 函数,用于递归地遍历 AST,并在每个节点上调用相应的访问者方法。

使用 traverse 函数

const babel = require("@babel/core");
const traverse = require("@babel/traverse").default;

const code = `function square(n) { return n * n; }`;
const ast = babel.parseSync(code);

traverse(ast, {
  enter(path) {
    console.log(path.node.type);
  }
});

了解 NodeTransform 转换规则

vue 的每个指令都是一个 NodeTransform,比如下面的 transformOnce 处理的是 v-once

export function getBaseTransformPreset(
  prefixIdentifiers?: boolean,
): TransformPreset {
  return [
    [
      transformOnce,
      transformIf,
      transformMemo,
      transformFor,
      ...(__COMPAT__ ? [transformFilter] : []),
      ...(!__BROWSER__ && prefixIdentifiers
        ? [
            // order is important
            trackVForSlotScopes,
            transformExpression,
          ]
        : __BROWSER__ && __DEV__
          ? [transformExpression]
          : []),
      transformSlotOutlet,
      transformElement,
      trackSlotScopes,
      transformText,
    ],
    {
      on: transformOn,
      bind: transformBind,
      model: transformModel,
    },
  ]
}

了解 v-once 的转换原理

const seen = new WeakSet()

export const transformOnce: NodeTransform = (node, context) => {
  if (node.type === NodeTypes.ELEMENT && findDir(node, 'once', true)) {
    if (seen.has(node) || context.inVOnce || context.inSSR) {
      return
    }
    seen.add(node)
    context.inVOnce = true
    context.helper(SET_BLOCK_TRACKING)
    return () => {
      context.inVOnce = false
      const cur = context.currentNode as ElementNode | IfNode | ForNode
      if (cur.codegenNode) {
        cur.codegenNode = context.cache(
          cur.codegenNode,
          true /* isVNode */,
          true /* inVOnce */,
        )
      }
    }
  }
}

http://www.kler.cn/a/428858.html

相关文章:

  • Linux 高级路由与流量控制-用 tc qdisc 管理 Linux 网络带宽
  • IO进程----进程
  • go语言zero框架通过chromedp实现网页在线截图的设计与功能实现
  • 聚铭网络6款产品入选CCIA《网络安全专用产品指南》
  • Java 接口安全指南
  • SpringBoot错误码国际化
  • 机器学习—学习过程
  • [笔记] Windows 上 Git 安装详细教程:从零开始,附带每个选项解析
  • 常见算法java语法
  • JavaScript中todolist操作--待办事项的添加 删除 完成功能
  • 实例教程:BBDB为AHRS算法开发提供完善的支撑环境(下)
  • RPA在IT运维中的实践:自动化监控与维护
  • 瑞芯微RK3566/RK3568开发板安卓11固件ROOT教程,Purple Pi OH演示
  • 【包教包会】CocosCreator3.x——重写Sprite,圆角、3D翻转、纹理循环、可合批调色板、不影响子节点的位移旋转缩放透明度
  • 【python】集合
  • 第3章:文本样式 --[CSS零基础入门]
  • 组件开发的环境准备
  • Linux用户与权限、IP地址与远程管理详解及命令
  • 【Java】类似王者荣耀游戏
  • MONI后台管理系统-数据库设计
  • 【机器学习】支持向量机(SVM)详解:原理与优化
  • Unity 使用LineRenderer制作模拟2d绳子
  • 力扣--LCR 172.统计目标成绩的出现次数
  • 解密时序数据库的未来:TDengine Open Day技术沙龙精彩回顾
  • leetcode 62.不同路径
  • 26备战秋招day17——机器学习基础