亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文了解Vue 3 的 generate 是這樣生成 render 函數(shù)的

 更新時(shí)間:2024年06月07日 14:22:24   作者:前端歐陽(yáng)  
本文介紹generate階段是如何根據(jù)javascript AST抽象語(yǔ)法樹(shù)生成render函數(shù)字符串的,本文中使用的vue版本為3.4.19,感興趣的朋友跟隨小編一起看看吧

前言

在之前的 面試官:來(lái)說(shuō)說(shuō)vue3是怎么處理內(nèi)置的v-for、v-model等指令? 文章中講了transform階段處理完v-for、v-model等指令后,會(huì)生成一棵javascript AST抽象語(yǔ)法樹(shù)。這篇文章我們來(lái)接著講generate階段是如何根據(jù)這棵javascript AST抽象語(yǔ)法樹(shù)生成render函數(shù)字符串的,本文中使用的vue版本為3.4.19。

看個(gè)demo

還是一樣的套路,我們通過(guò)debug一個(gè)demo來(lái)搞清楚render函數(shù)字符串是如何生成的。demo代碼如下:

<template>
  <p>{{ msg }}</p>
</template>
<script setup lang="ts">
import { ref } from "vue";
const msg = ref("hello world");
</script>

上面這個(gè)demo很簡(jiǎn)單,使用p標(biāo)簽渲染一個(gè)msg響應(yīng)式變量,變量的值為"hello world"。我們?cè)跒g覽器中來(lái)看看這個(gè)demo生成的render函數(shù)是什么樣的,代碼如下:

import { toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "/node_modules/.vite/deps/vue.js?v=23bfe016";
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  return _openBlock(), _createElementBlock(
    "p",
    null,
    _toDisplayString($setup.msg),
    1
    /* TEXT */
  );
}

上面的render函數(shù)中使用了兩個(gè)函數(shù):openBlockcreateElementBlock。在之前的 vue3早已具備拋棄虛擬DOM的能力了文章中我們已經(jīng)講過(guò)了這兩個(gè)函數(shù):

  • openBlock的作用為初始化一個(gè)全局變量currentBlock數(shù)組,用于收集dom樹(shù)中的所有動(dòng)態(tài)節(jié)點(diǎn)。
  • createElementBlock的作用為生成根節(jié)點(diǎn)p標(biāo)簽的虛擬DOM,然后將收集到的動(dòng)態(tài)節(jié)點(diǎn)數(shù)組currentBlock塞到根節(jié)點(diǎn)p標(biāo)簽的dynamicChildren屬性上。

render函數(shù)的生成其實(shí)很簡(jiǎn)單,經(jīng)過(guò)transform階段處理后會(huì)生成一棵javascript AST抽象語(yǔ)法樹(shù),這棵樹(shù)的結(jié)構(gòu)和要生成的render函數(shù)結(jié)構(gòu)是一模一樣的。所以在generate函數(shù)中只需要遞歸遍歷這棵樹(shù),進(jìn)行字符串拼接就可以生成render函數(shù)啦!

加我微信heavenyjj0012回復(fù)「666」,免費(fèi)領(lǐng)取歐陽(yáng)研究vue源碼過(guò)程中收集的源碼資料,歐陽(yáng)寫(xiě)文章有時(shí)也會(huì)參考這些資料。同時(shí)讓你的朋友圈多一位對(duì)vue有深入理解的人。

generate函數(shù)

首先給generate函數(shù)打個(gè)斷點(diǎn),generate函數(shù)在node_modules/@vue/compiler-core/dist/compiler-core.cjs.js文件中。

然后啟動(dòng)一個(gè)debug終端,在終端中執(zhí)行yarn dev(這里是以vite舉例)。在瀏覽器中訪問(wèn)  http://localhost:5173/ ,此時(shí)斷點(diǎn)就會(huì)走到generate函數(shù)中了。在我們這個(gè)場(chǎng)景中簡(jiǎn)化后的generate函數(shù)是下面這樣的:

function generate(ast) {
  const context = createCodegenContext();
  const { push, indent, deindent } = context;
  const preambleContext = context;
  genModulePreamble(ast, preambleContext);
  const functionName = `render`;
  const args = ["_ctx", "_cache"];
  args.push("$props", "$setup", "$data", "$options");
  const signature = args.join(", ");
  push(`function ${functionName}(${signature}) {`);
  indent();
  push(`return `);
  genNode(ast.codegenNode, context);
  deindent();
  push(`}`);
  return {
    ast,
    code: context.code,
  };
}

generate中主要分為四部分:

  • 生成context上下文對(duì)象。
  • 執(zhí)行genModulePreamble函數(shù)生成:import { xxx } from "vue";
  • 生成render函數(shù)中的函數(shù)名稱和參數(shù),也就是function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  • 生成render函數(shù)中return的內(nèi)容

context上下文對(duì)象

context上下文對(duì)象是執(zhí)行createCodegenContext函數(shù)生成的,將斷點(diǎn)走進(jìn)createCodegenContext函數(shù)。簡(jiǎn)化后的代碼如下:

function createCodegenContext() {
  const context = {
    code: ``,
    indentLevel: 0,
    helper(key) {
      return `_${helperNameMap[key]}`;
    },
    push(code) {
      context.code += code;
    },
    indent() {
      newline(++context.indentLevel);
    },
    deindent(withoutNewLine = false) {
      if (withoutNewLine) {
        --context.indentLevel;
      } else {
        newline(--context.indentLevel);
      }
    },
    newline() {
      newline(context.indentLevel);
    },
  };
  function newline(n) {
    context.push("\n" + `  `.repeat(n));
  }
  return context;
}

為了代碼具有較強(qiáng)的可讀性,我們一般都會(huì)使用換行和鎖進(jìn)。context上下文中的這些屬性和方法作用就是為了生成具有較強(qiáng)可讀性的render函數(shù)。

code屬性:當(dāng)前生成的render函數(shù)字符串。

  • indentLevel屬性:當(dāng)前的鎖進(jìn)級(jí)別,每個(gè)級(jí)別對(duì)應(yīng)兩個(gè)空格的鎖進(jìn)。
  • helper方法:返回render函數(shù)中使用到的vue包中export導(dǎo)出的函數(shù)名稱,比如返回openBlock、createElementBlock等函數(shù)
  • push方法:向當(dāng)前的render函數(shù)字符串后插入字符串code。
  • indent方法:插入換行符,并且增加一個(gè)鎖進(jìn)。
  • deindent方法:減少一個(gè)鎖進(jìn),或者插入一個(gè)換行符并且減少一個(gè)鎖進(jìn)。
  • newline方法:插入換行符。

生成import {xxx} from "vue"

我們接著來(lái)看generate函數(shù)中的第二部分,生成import {xxx} from "vue"。將斷點(diǎn)走進(jìn)genModulePreamble函數(shù),在我們這個(gè)場(chǎng)景中簡(jiǎn)化后的genModulePreamble函數(shù)代碼如下:

function genModulePreamble(ast, context) {
  const { push, newline, runtimeModuleName } = context;
  if (ast.helpers.size) {
    const helpers = Array.from(ast.helpers);
    push(
      `import { ${helpers
        .map((s) => `${helperNameMap[s]} as _${helperNameMap[s]}`)
        .join(", ")} } from ${JSON.stringify(runtimeModuleName)}
`,
      -1 /* End */
    );
  }
  genHoists(ast.hoists, context);
  newline();
  push(`export `);
}

其中的ast.helpers是在transform階段收集的需要從vue中import導(dǎo)入的函數(shù),無(wú)需將vue中所有的函數(shù)都import導(dǎo)入。在debug終端看看helpers數(shù)組中的值如下圖:

helpers

從上圖中可以看到需要從vue中import導(dǎo)入toDisplayStringopenBlock、createElementBlock這三個(gè)函數(shù)。

在執(zhí)行push方法之前我們先來(lái)看看此時(shí)的render函數(shù)字符串是什么樣的,如下圖:

before-import

從上圖中可以看到此時(shí)生成的render函數(shù)字符串還是一個(gè)空字符串,執(zhí)行完push方法后,我們來(lái)看看此時(shí)的render函數(shù)字符串是什么樣的,如下圖:

after-import

從上圖中可以看到此時(shí)的render函數(shù)中已經(jīng)有了import {xxx} from "vue"了。

這里執(zhí)行的genHoists函數(shù)就是前面 搞懂 Vue 3 編譯優(yōu)化:靜態(tài)提升的秘密文章中講過(guò)的靜態(tài)提升的入口。

生成render函數(shù)中的函數(shù)名稱和參數(shù)

執(zhí)行完genModulePreamble函數(shù)后,已經(jīng)生成了一條import {xxx} from "vue"了。我們接著來(lái)看generate函數(shù)中render函數(shù)的函數(shù)名稱和參數(shù)是如何生成的,代碼如下:

const functionName = `render`;
const args = ["_ctx", "_cache"];
args.push("$props", "$setup", "$data", "$options");
const signature = args.join(", ");
push(`function ${functionName}(${signature}) {`);

上面的代碼很簡(jiǎn)單,都是執(zhí)行push方法向render函數(shù)中添加code字符串,其中args數(shù)組就是render函數(shù)中的參數(shù)。我們?cè)趤?lái)看看執(zhí)行完上面這塊代碼后的render函數(shù)字符串是什么樣的,如下圖:

before-genNode

從上圖中可以看到此時(shí)已經(jīng)生成了render函數(shù)中的函數(shù)名稱和參數(shù)了。

生成render函數(shù)中return的內(nèi)容

接著來(lái)看generate函數(shù)中最后一塊代碼,如下:

indent();
push(`return `);
genNode(ast.codegenNode, context);

首先調(diào)用indent方法插入一個(gè)換行符并且增加一個(gè)鎖進(jìn),然后執(zhí)行push方法添加一個(gè)return字符串。

接著以根節(jié)點(diǎn)的codegenNode屬性為參數(shù)執(zhí)行genNode函數(shù)生成return中的內(nèi)容,在我們這個(gè)場(chǎng)景中genNode函數(shù)簡(jiǎn)化后的代碼如下:

function genNode(node, context) {
  switch (node.type) {
    case NodeTypes.SIMPLE_EXPRESSION:
      genExpression(node, context)
      break
    case NodeTypes.INTERPOLATION:
      genInterpolation(node, context);
      break;
    case NodeTypes.VNODE_CALL:
      genVNodeCall(node, context);
      break;
  }
}

這里涉及到SIMPLE_EXPRESSION、INTERPOLATIONVNODE_CALL三種AST抽象語(yǔ)法樹(shù)node節(jié)點(diǎn)類型:

  • INTERPOLATION:表示當(dāng)前節(jié)點(diǎn)是雙大括號(hào)節(jié)點(diǎn),我們這個(gè)demo中就是:{{msg}}這個(gè)文本節(jié)點(diǎn)。
  • SIMPLE_EXPRESSION:表示當(dāng)前節(jié)點(diǎn)是簡(jiǎn)單表達(dá)式節(jié)點(diǎn),在我們這個(gè)demo中就是雙大括號(hào)節(jié)點(diǎn){{msg}}中的更里層節(jié)點(diǎn)msg
  • VNODE_CALL:表示當(dāng)前節(jié)點(diǎn)是虛擬節(jié)點(diǎn),比如我們這里第一次調(diào)用genNode函數(shù)傳入的ast.codegenNode(根節(jié)點(diǎn)的codegenNode屬性)就是虛擬節(jié)點(diǎn)。

genVNodeCall函數(shù)

由于當(dāng)前節(jié)點(diǎn)是虛擬節(jié)點(diǎn),第一次進(jìn)入genNode函數(shù)時(shí)會(huì)執(zhí)行genVNodeCall函數(shù)。在我們這個(gè)場(chǎng)景中簡(jiǎn)化后的genVNodeCall函數(shù)代碼如下:

const OPEN_BLOCK = Symbol(`openBlock`);
const CREATE_ELEMENT_BLOCK = Symbol(`createElementBlock`);
function genVNodeCall(node, context) {
  const { push, helper } = context;
  const { tag, props, children, patchFlag, dynamicProps, isBlock } = node;
  if (isBlock) {
    push(`(${helper(OPEN_BLOCK)}(${``}), `);
  }
  const callHelper = CREATE_ELEMENT_BLOCK;
  push(helper(callHelper) + `(`, -2 /* None */, node);
  genNodeList(
    // 將參數(shù)中的undefined轉(zhuǎn)換成null
    genNullableArgs([tag, props, children, patchFlag, dynamicProps]),
    context
  );
  push(`)`);
  if (isBlock) {
    push(`)`);
  }
}

首先判斷當(dāng)前節(jié)點(diǎn)是不是block節(jié)點(diǎn),由于此時(shí)的node為根節(jié)點(diǎn),所以isBlock為true。將斷點(diǎn)走進(jìn)helper方法,我們來(lái)看看helper(OPEN_BLOCK)返回值是什么。helper方法的代碼如下:

const helperNameMap = {
  [OPEN_BLOCK]: `openBlock`,
  [CREATE_ELEMENT_BLOCK]: `createElementBlock`,
  [TO_DISPLAY_STRING]: `toDisplayString`,
  // ...省略
};
helper(key) {
  return `_${helperNameMap[key]}`;
}

helper方法中的代碼很簡(jiǎn)單,這里的helper(OPEN_BLOCK)返回的就是_openBlock

將斷點(diǎn)走到第一個(gè)push方法,代碼如下:

push(`(${helper(OPEN_BLOCK)}(${``}), `);

執(zhí)行完這個(gè)push方法后在debug終端看看此時(shí)的render函數(shù)字符串是什么樣的,如下圖:

after-block

從上圖中可以看到,此時(shí)render函數(shù)中增加了一個(gè)_openBlock函數(shù)的調(diào)用。

將斷點(diǎn)走到第二個(gè)push方法,代碼如下:

const callHelper = CREATE_ELEMENT_BLOCK;
push(helper(callHelper) + `(`, -2 /* None */, node);

同理helper(callHelper)方法返回的是_createElementBlock,執(zhí)行完這個(gè)push方法后在debug終端看看此時(shí)的render函數(shù)字符串是什么樣的,如下圖:

after-createElementBlock

從上圖中可以看到,此時(shí)render函數(shù)中增加了一個(gè)_createElementBlock函數(shù)的調(diào)用。

繼續(xù)將斷點(diǎn)走到genNodeList部分,代碼如下:

genNodeList(
  genNullableArgs([tag, props, children, patchFlag, dynamicProps]),
  context
);

其中的genNullableArgs函數(shù)功能很簡(jiǎn)單,將參數(shù)中的undefined轉(zhuǎn)換成null。比如此時(shí)的props就是undefined,經(jīng)過(guò)genNullableArgs函數(shù)處理后傳給genNodeList函數(shù)的props就是null。

genNodeList函數(shù)

繼續(xù)將斷點(diǎn)走進(jìn)genNodeList函數(shù),在我們這個(gè)場(chǎng)景中簡(jiǎn)化后的代碼如下:

function genNodeList(nodes, context, multilines = false, comma = true) {
  const { push } = context;
  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i];
    if (shared.isString(node)) {
      push(node);
    } else {
      genNode(node, context);
    }
    if (i < nodes.length - 1) {
      comma && push(", ");
    }
  }
}

我們先來(lái)看看此時(shí)的nodes參數(shù),如下圖:

nodes

這里的nodes就是調(diào)用genNodeList函數(shù)時(shí)傳的數(shù)組:[tag, props, children, patchFlag, dynamicProps],只是將數(shù)組中的undefined轉(zhuǎn)換成了null。

  • nodes數(shù)組中的第一項(xiàng)為字符串p,表示當(dāng)前節(jié)點(diǎn)是p標(biāo)簽。
  • 由于當(dāng)前p標(biāo)簽沒(méi)有props,所以第二項(xiàng)為null的字符串。
  • 第三項(xiàng)為p標(biāo)簽子節(jié)點(diǎn):{{msg}}
  • 第四項(xiàng)也是一個(gè)字符串,標(biāo)記當(dāng)前節(jié)點(diǎn)是否是動(dòng)態(tài)節(jié)點(diǎn)。

在講genNodeList函數(shù)之前,我們先來(lái)看一下如何使用h函數(shù)生成一個(gè)<p>{{ msg }}</p>標(biāo)簽的虛擬DOM節(jié)點(diǎn)。根據(jù)vue官網(wǎng)的介紹,h函數(shù)定義如下:

// 完整參數(shù)簽名
function h(
  type: string | Component,
  props?: object | null,
  children?: Children | Slot | Slots
): VNode

h函數(shù)接收的第一個(gè)參數(shù)是標(biāo)簽名稱或者一個(gè)組件,第二個(gè)參數(shù)是props對(duì)象或者null,第三個(gè)參數(shù)是子節(jié)點(diǎn)。

所以我們要使用h函數(shù)生成demo中的p標(biāo)簽虛擬DOM節(jié)點(diǎn)代碼如下:

h("p", null, msg)

h函數(shù)生成虛擬DOM實(shí)際就是調(diào)用的createBaseVNode函數(shù),而我們這里的createElementBlock函數(shù)生成虛擬DOM也是調(diào)用的createBaseVNode函數(shù)。兩者的區(qū)別是createElementBlock函數(shù)多接收一些參數(shù),比如patchFlagdynamicProps。

現(xiàn)在我想你應(yīng)該已經(jīng)反應(yīng)過(guò)來(lái)了,為什么調(diào)用genNodeList函數(shù)時(shí)傳入的第一個(gè)參數(shù)nodes為:[tag, props, children, patchFlag, dynamicProps]。這個(gè)數(shù)組的順序就是調(diào)用createElementBlock函數(shù)時(shí)傳入的參數(shù)順序。

所以在genNodeList中會(huì)遍歷nodes數(shù)組生成調(diào)用createElementBlock函數(shù)需要傳入的參數(shù)。

先來(lái)看第一個(gè)參數(shù)tag,這里tag的值為字符串"p"。所以在for循環(huán)中會(huì)執(zhí)行push(node),生成調(diào)用createElementBlock函數(shù)的第一個(gè)參數(shù)"p"。在debug終端看看此時(shí)的render函數(shù),如下圖:

arg1

從上圖中可以看到createElementBlock函數(shù)的第一個(gè)參數(shù)"p"

接著來(lái)看nodes數(shù)組中的第二個(gè)參數(shù):props,由于p標(biāo)簽中沒(méi)有props屬性。所以第二個(gè)參數(shù)props的值為字符串"null",在for循環(huán)中同樣會(huì)執(zhí)行push(node),生成調(diào)用createElementBlock函數(shù)的第二個(gè)參數(shù)"null"。在debug終端看看此時(shí)的render函數(shù),如下圖:

arg2

從上圖中可以看到createElementBlock函數(shù)的第二個(gè)參數(shù)null

接著來(lái)看nodes數(shù)組中的第三個(gè)參數(shù):children,由于children是一個(gè)對(duì)象,所以以當(dāng)前children節(jié)點(diǎn)作為參數(shù)執(zhí)行genNode函數(shù)。

這個(gè)genNode函數(shù)前面已經(jīng)執(zhí)行過(guò)一次了,當(dāng)時(shí)是以根節(jié)點(diǎn)的codegenNode屬性作為參數(shù)執(zhí)行的?;仡櫼幌?code>genNode函數(shù)的代碼,如下:

function genNode(node, context) {
  switch (node.type) {
    case NodeTypes.SIMPLE_EXPRESSION:
      genExpression(node, context)
      break
    case NodeTypes.INTERPOLATION:
      genInterpolation(node, context);
      break;
    case NodeTypes.VNODE_CALL:
      genVNodeCall(node, context);
      break;
  }
}

前面我們講過(guò)了NodeTypes.INTERPOLATION類型表示當(dāng)前節(jié)點(diǎn)是雙大括號(hào)節(jié)點(diǎn),而我們這次執(zhí)行genNode函數(shù)傳入的p標(biāo)簽children,剛好就是{{msg}}雙大括號(hào)節(jié)點(diǎn)。所以代碼會(huì)走到genInterpolation函數(shù)中。

genInterpolation函數(shù)

將斷點(diǎn)走進(jìn)genInterpolation函數(shù)中,genInterpolation代碼如下:

function genInterpolation(node, context) {
  const { push, helper } = context;
  push(`${helper(TO_DISPLAY_STRING)}(`);
  genNode(node.content, context);
  push(`)`);
}

首先會(huì)執(zhí)行push方法向render函數(shù)中插入一個(gè)_toDisplayString函數(shù)調(diào)用,在debug終端看看執(zhí)行完這個(gè)push方法后的render函數(shù),如下圖:

toDisplayString

從上圖中可以看到此時(shí)createElementBlock函數(shù)的第三個(gè)參數(shù)只生成了一半,調(diào)用_toDisplayString函數(shù)傳入的參數(shù)還沒(méi)生成。

接著會(huì)以node.content作為參數(shù)執(zhí)行genNode(node.content, context);生成_toDisplayString函數(shù)的參數(shù),此時(shí)代碼又走回了genNode函數(shù)。

將斷點(diǎn)再次走進(jìn)genNode函數(shù),看看此時(shí)的node是什么樣的,如下圖:

simple-expression

從上圖中可以看到此時(shí)的node節(jié)點(diǎn)是一個(gè)簡(jiǎn)單表達(dá)式節(jié)點(diǎn),表達(dá)式為:$setup.msg。所以代碼會(huì)走進(jìn)genExpression函數(shù)。

genExpression函數(shù)

接著將斷點(diǎn)走進(jìn)genExpression函數(shù)中,genExpression函數(shù)中的代碼如下:

function genExpression(node, context) {
  const { content, isStatic } = node;
  context.push(
    isStatic ? JSON.stringify(content) : content,
    -3 /* Unknown */,
    node
  );
}

由于當(dāng)前的msg變量是一個(gè)ref響應(yīng)式變量,所以isStaticfalse。所以會(huì)執(zhí)行push方法,將$setup.msg插入到render函數(shù)中。

執(zhí)行完push方法后,在debug終端看看此時(shí)的render函數(shù)字符串是什么樣的,如下圖:

after-expression

從上圖中可以看到此時(shí)的render函數(shù)基本已經(jīng)生成了,剩下的就是調(diào)用push方法生成各個(gè)函數(shù)的右括號(hào)")"和右花括號(hào)"}"。將斷點(diǎn)逐層走出,直到generate函數(shù)中。代碼如下:

function generate(ast) {
  // ...省略
  genNode(ast.codegenNode, context);

  deindent();
  push(`}`);
  return {
    ast,
    code: context.code,
  };
}

執(zhí)行完最后一個(gè) push方法后,在debug終端看看此時(shí)的render函數(shù)字符串是什么樣的,如下圖:

render

從上圖中可以看到此時(shí)的render函數(shù)終于生成啦!

總結(jié)

這是我畫(huà)的我們這個(gè)場(chǎng)景中generate生成render函數(shù)的流程圖:

  • 執(zhí)行genModulePreamble函數(shù)生成:import { xxx } from "vue";
  • 簡(jiǎn)單字符串拼接生成render函數(shù)中的函數(shù)名稱和參數(shù),也就是function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  • 以根節(jié)點(diǎn)的codegenNode屬性為參數(shù)調(diào)用genNode函數(shù)生成render函數(shù)中return的內(nèi)容。
    • 此時(shí)傳入的是虛擬節(jié)點(diǎn),執(zhí)行genVNodeCall函數(shù)生成return _openBlock(), _createElementBlock(和調(diào)用genNodeList函數(shù),生成createElementBlock函數(shù)的參數(shù)。
    • 處理p標(biāo)簽的tag標(biāo)簽名和props,生成createElementBlock函數(shù)的第一個(gè)和第二個(gè)參數(shù)。此時(shí)render函數(shù)return的內(nèi)容為:return _openBlock(), _createElementBlock("p", null
    • 處理p標(biāo)簽的children也就是{{msg}}節(jié)點(diǎn),再次調(diào)用genNode函數(shù)。此時(shí)node節(jié)點(diǎn)類型為雙大括號(hào)節(jié)點(diǎn),調(diào)用genInterpolation函數(shù)。
    • genInterpolation函數(shù)中會(huì)先調(diào)用push方法,此時(shí)的render函數(shù)return的內(nèi)容為:return _openBlock(), _createElementBlock("p", null, _toDisplayString(。然后以node.content為參數(shù)再次調(diào)用genNode函數(shù)。
    • node.content$setup.msg,是一個(gè)簡(jiǎn)單表達(dá)式節(jié)點(diǎn),所以在genNode函數(shù)中會(huì)調(diào)用genExpression函數(shù)。執(zhí)行完genExpression函數(shù)后,此時(shí)的render函數(shù)return的內(nèi)容為:return _openBlock(), _createElementBlock("p", null, _toDisplayString($setup.msg
    • 調(diào)用push方法生成各個(gè)函數(shù)的右括號(hào)")"和右花括號(hào)"}",生成最終的render函數(shù)

到此這篇關(guān)于原來(lái) Vue 3 的 generate 是這樣生成 render 函數(shù)的的文章就介紹到這了,更多相關(guān)原來(lái)vue3中template使用ref無(wú)需.value是因?yàn)檫@個(gè)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue組件之極簡(jiǎn)的地址選擇器的實(shí)現(xiàn)

    Vue組件之極簡(jiǎn)的地址選擇器的實(shí)現(xiàn)

    這篇文章主要介紹了Vue組件之極簡(jiǎn)的地址選擇器的實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • Vue項(xiàng)目導(dǎo)入導(dǎo)出文件功能以及導(dǎo)出文件后亂碼問(wèn)題及解決

    Vue項(xiàng)目導(dǎo)入導(dǎo)出文件功能以及導(dǎo)出文件后亂碼問(wèn)題及解決

    這篇文章主要介紹了Vue項(xiàng)目導(dǎo)入導(dǎo)出文件功能以及導(dǎo)出文件后亂碼問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • vue.js 解決v-model讓select默認(rèn)選中不生效的問(wèn)題

    vue.js 解決v-model讓select默認(rèn)選中不生效的問(wèn)題

    這篇文章主要介紹了vue.js 解決v-model讓select默認(rèn)選中不生效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-07-07
  • 談?wù)剬?duì)vue響應(yīng)式數(shù)據(jù)更新的誤解

    談?wù)剬?duì)vue響應(yīng)式數(shù)據(jù)更新的誤解

    本篇文章主要介紹了談?wù)剬?duì)vue響應(yīng)式數(shù)據(jù)更新的誤解,深入了解了vue響應(yīng)式數(shù)據(jù),有興趣的可以了解一下
    2017-08-08
  • Vue之Pinia狀態(tài)管理

    Vue之Pinia狀態(tài)管理

    這篇文章主要介紹了Vue中Pinia狀態(tài)管理,Pinia開(kāi)始于大概2019年,其目的是設(shè)計(jì)一個(gè)擁有 組合式 API 的 Vue 狀態(tài)管理庫(kù),Pinia本質(zhì)上依然是一個(gè)狀態(tài)管理庫(kù),用于跨組件、頁(yè)面進(jìn)行狀態(tài)共享,感興趣的同學(xué)可以參考閱讀
    2023-04-04
  • vue3?setup中使用響應(yīng)式的方法

    vue3?setup中使用響應(yīng)式的方法

    文章主要介紹了Vue3中的響應(yīng)式數(shù)據(jù)處理機(jī)制,包括ref和reactive函數(shù)的使用,它們的區(qū)別,以及如何使用watch和watchEffect來(lái)監(jiān)聽(tīng)數(shù)據(jù)變化,文章最后提到了Vue3的生命周期鉤子和自定義hooks的概念,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • vue中Npm run build 根據(jù)環(huán)境傳遞參數(shù)方法來(lái)打包不同域名

    vue中Npm run build 根據(jù)環(huán)境傳遞參數(shù)方法來(lái)打包不同域名

    這篇文章主要介紹了vue項(xiàng)目中Npm run build 根據(jù)環(huán)境傳遞參數(shù)方法來(lái)打包不同域名,使用npm run build --xxx,根據(jù)傳遞參數(shù)xxx來(lái)判定不同的環(huán)境,給出不同的域名配置,具體內(nèi)容詳情大家參考下本文
    2018-03-03
  • Vue3 如何使用CryptoJS加密

    Vue3 如何使用CryptoJS加密

    CryptoJS是一個(gè)強(qiáng)大的JavaScript庫(kù),它提供了多種加密解密功能,尤其是AES加密方法,使用CryptoJS,我們可以有效地保護(hù)數(shù)據(jù)安全,防止信息泄露,通過(guò)簡(jiǎn)單的安裝和函數(shù)編寫(xiě),本文給大家介紹Vue3 如何使用CryptoJS加密,感興趣的朋友一起看看吧
    2024-10-10
  • vue-openlayers實(shí)現(xiàn)地圖坐標(biāo)彈框效果

    vue-openlayers實(shí)現(xiàn)地圖坐標(biāo)彈框效果

    這篇文章主要為大家詳細(xì)介紹了vue-openlayers實(shí)現(xiàn)地圖坐標(biāo)彈框效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-09-09
  • 在vue中v-bind使用三目運(yùn)算符綁定class的實(shí)例

    在vue中v-bind使用三目運(yùn)算符綁定class的實(shí)例

    今天小編就為大家分享一篇在vue中v-bind使用三目運(yùn)算符綁定class的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-09-09

最新評(píng)論