Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the promotion of Vue3.0 static nodes

2025-01-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

Today, I would like to share with you the relevant knowledge of what Vue3.0 static node promotion is. The content is detailed and the logic is clear. I believe most people still know too much about this knowledge, so share this article for your reference. I hope you can get something after reading this article. Let's take a look.

Preface

"static node promotion" is an optimization point put forward by "Vue3" to solve the performance problem of VNode update process. As we all know, in large-scale application scenarios, the patchVNode process of "Vue2.x", that is, the diff process is very slow, which is a very troublesome problem.

Although, to some extent, the diff process that is often asked in the interview reduces the direct operation of DOM. However, "this reduction has a certain cost". Because, if it is a complex application, then there will be a very complex parent-child relationship of VNode, and this is the pain point of diff, it will constantly recursively call patchVNode, stacked for a few milliseconds, eventually causing VNode updates to be slow.

Therefore, this is one of the reasons why the large-scale applications we see, such as Aliyun, adopt a technology stack based on "React". Therefore, "Vue3" also changed the past, rewrote the whole Compiler process, and put forward static promotion, targeted update and other optimization points to improve the patchVNode process.

So, back to today's topic, let's take a look at the "Vue3" static node promotion in the entire compilation process from the source point of view.

What is patchFlag?

Because the patchFlag attribute on AST Element is mentioned during the transfrom phase of the compile process. So, before we formally get to know complie, let's figure out a concept, what is patchFlag?

PatchFlag is the "optimized logo" marked on the transform phase parsing AST Element of complier. And, as the name implies, the word patchFlag,patch means that it will provide a basis for patchVNode when runtime, so as to achieve the effect of targeted update of VNode. Therefore, in this way, the familiar Vue3 skillfully combines runtime and compiler to achieve targeted updates and static enhancements.

In the source code, patchFlag is defined as a "numeric enumeration type"

Moreover, it is worth mentioning that patchFlag as a whole can be divided into two categories:

When the value of patchFlag is "greater than" 0, it means that the corresponding element can be optimally generated or updated at patchVNode or render.

When the value of patchFlag is "less than" 0, it means that when the corresponding element is in patchVNode, it needs to be full diff, that is, the comparison and update process of recursively traversing VNode tree.

In fact, there are two special types of flag:shapeFlag and slogFlag, which I will not start to do here. Students who are interested can learn by themselves.

Comparison of Compile compilation process with Vue2.x compilation process

For those of you who know the source code of "Vue2.x", I think you all know that the Compile process in "Vue2.x" will be like this:

The parse compilation template generates the original AST.

Optimize optimizes the original AST, marking AST Element as a static root node or static node.

Generate generates executable code, such as _ c, _ l, and so on, based on the optimized AST.

In "Vue3", the overall Compile process is still three stages, but unlike "Vue2.x", the second stage is replaced by the normal compiler will exist in the phase transform.

In the source code, its corresponding pseudo code would look like this:

Export function baseCompile (template: string | RootNode, options: CompilerOptions = {}): CodegenResult {... Const ast = isString (template)? BaseParse (template, options): template... Transform (ast, extend ({}, options, {....}) return generate (ast, extend ({}, options, {prefixIdentifiers}))}

So, I think at this time you might ask, why transform? What is its duty?

By simply comparing the optimize of the second stage of the "Vue2.x" compilation process, it is clear that transform is not "cooking without rice", it still has the function of "optimizing" the original AST, and the specific responsibilities will be shown as follows:

Add codegen attributes to all AST Element to help generate generate "optimal" executable code more accurately.

Add the hoists attribute to the static AST Element to achieve the "separate creation" of the static node.

...

In addition, transform also identifies attributes such as isBlock and helpers to generate the best executable code, which we will not discuss in detail here, and students who are interested can understand it for themselves.

BaseParse builds primitive Abstract Grammar Tree (AST)

As the name implies, baseParse plays the role of "parsing". Its performance is the same as the parse of "Vue2.x". It is the parsing template tempalte to generate "original AST".

Suppose, at this point, we have a template like template:

Hi vue3 {{msg}}

Then, the AST it generates after baseParse processing will look like this:

{cached: 0, children: [{… }], codegenNode: undefined, components: [], directives: [], helpers: [], hoists: [], imports: [], loc: {start: {… }, end: {... }, source: "hi vue3 {{msg}}"}, temps: 0, type: 0}

If you have learned about the compilation process of "Vue2.x", you should be familiar with most of the attributes of the above AST. The essence of AST is to describe "DSL" (domain-specific language) with objects, such as:

What is stored in children is the offspring of the outermost div.

Loc is used to describe the position information of the AST Element in the entire template.

Type is used to describe the type of element (for example, 5 for interpolation, 2 for text), and so on.

Also, you can see that AST is different from "Vue2.x", where we have more attributes such as helpers, codegenNode, hoists, and so on. Instead, these attributes are assigned accordingly in the transform phase, which in turn helps the generate phase generate "better" executable code.

Transfrom optimizes the original abstract grammar tree (AST)

For the transform phase, if you know the workflow of the "compiler", you should know that the workflow of a complete compiler would be like this:

First, parse parses the original code string to generate an abstract syntax tree AST.

Second, transform transforms the abstract syntax tree into a structure closer to the target "DSL".

Finally, codegen generates executable code for the target "DSL" based on the transformed abstract syntax tree.

After "Vue3" uses Monorepo to manage the project, the corresponding capability of compile is a compiler. Therefore, transform is also the top priority of the whole compilation process. In other words, without transform to do many levels of transformation of AST, "Vue" would still be hanging in the "much criticized" process of diff.

In contrast, the compilation phase of "Vue2.x" does not have a complete transform, but optimize optimizes AST. It is conceivable that it will be "so popular" at the beginning of the design of "Vue".

So, let's look at the definition in the source code of the transform function:

Function transform (root: RootNode, options: TransformOptions) {const context = createTransformContext (root, options) traverseNode (root, context) if (options.hoistStatic) {hoistStatic (root, context)} if (! options.ssr) {createRootCodegen (root) Context)} / / finalize meta information root.helpers = [... context.helpers] root.components = [... context.components] root.directives = [... context.directives] root.imports = [... context.imports] root.hoists = context.hoists root.temps = context.temps root.cached = context.cached}

It can be said that what the transform function does is "at a glance" in its definition. Here we mention two things that play a decisive role in static improvement:

Assign the AST Element corresponding to the static node in the original AST to the hoists attribute of the root AST.

Get the key name corresponding to the helpers required by the original AST, which is used to get the corresponding functions for generating executable code in the generate phase, such as createTextVNode, createStaticVNode, renderList, and so on.

In addition, specific transform functions are applied to AST Element in the traverseNode function, which can be roughly divided into two categories:

Static node transform application, that is, the node does not contain interpolation, instruction, props, dynamic binding and so on.

Dynamic node transform application, that is, the node contains interpolation, instruction, props, dynamic style binding and so on.

So, let's look at how transform is applied to static nodes.

Static node transform application

Here, for the chestnut we mentioned above, the static node is this part:

Hi vue3

Before it carries on the transform application, its corresponding AST will be like this:

{children: [{content: "hi vue3" loc: {start: {… }, end: {... }, source: "hi vue3"} type: 2}], codegenNode: undefined, isSelfClosing: false, loc: {start: {… }, end: {... }, source: "hi vue3"}, ns: 0, props: [], tag: "div", tagType: 0, type: 1}

As you can see, its codegenNode is undefined at this time. In the source code, all kinds of transform functions are defined as plugin, which will "recursively apply" the corresponding plugin according to the AST generated by baseParse. Then, create a codegen object corresponding to AST Element.

So, at this point we will hit the logic of the two plugin transformElement and transformText.

"transformText"

As the name implies, transformText is related to "text". Obviously, the type of AST Element at this time is Text. So, let's first take a look at the pseudo code corresponding to the transformText function:

Export const transformText: NodeTransform = (node, context) = > {if (node.type = NodeTypes.ROOT | | node.type = NodeTypes.ELEMENT | | node.type = NodeTypes.FOR | | node.type = NodeTypes.IF_BRANCH) {return () = > {const children = node.children let currentContainer: CompoundExpressionNode | undefined = undefined let hasText = false for (let I = 0; I

< children.length; i++) { // {1} const child = children[i] if (isText(child)) { hasText = true ... } } if ( !hasText || (children.length === 1 && (node.type === NodeTypes.ROOT || (node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.ELEMENT))) ) { // {2} return } ... } }} 可以看到,这里我们会命中 「{2}」 的逻辑,即如果对于「节点含有单一文本」 transformText 并不需要进行额外的处理,即该节点仍然在这里仍然保留和「Vue2.x」版本一样的处理方式。 而 transfromText 真正发挥作用的场景是当模板中存在这样的情况: ab {a} {b} 此时 transformText 需要将两者放在一个「单独的」 AST Element 下,在源码中它被称为「Compound Expression」,即「组合的表达式」。这种组合的目的是为了 patchVNode 这类 VNode 时做到「更好地定位和实现 DOM 的更新」。反之,如果是一个文本节点和插值动态节点的话,在 patchVNode 阶段同样的操作需要进行两次,例如对于同一个 DOM 节点操作两次。 「transformElement」 transformElement 是一个所有 AST Element 都会被执行的一个 plugin,它的核心是为 AST Element 生成最基础的 codegen 属性。例如标识出对应 patchFlag,从而为生成 VNode 提供依据,例如 dynamicChildren。 而对于静态节点,同样是起到一个初始化它的 codegenNode 属性的作用。并且,从上面介绍的 patchFlag 的类型,我们可以知道它的 patchFlag 为默认值 0。所以,它的 codegenNode 属性值看起来会是这样: { children: { content: "hi vue3" loc: {start: {…}, end: {…}, source: "hi vue3"} type: 2 }, directives: undefined, disableTracking: false, dynamicProps: undefined, isBlock: false, loc: {start: {…}, end: {…}, source: "hi vue3"}, patchFlag: undefined, props: undefined, tag: ""div"", type: 13}generate 生成可执行代码 generate 是 compile 阶段的最后一步,它的作用是将 transform 转换后的 AST 生成对应的「可执行代码」,从而在之后 Runtime 的 Render 阶段时,就可以通过可执行代码生成对应的 VNode Tree,然后最终映射为真实的 DOM Tree 在页面上。 同样地,这一阶段在「Vue2.x」也是由 generate 函数完成,它会生成是诸如 _l、_c 之类的函数,这本质上是对 _createElement 函数的封装。而相比较「Vue2.x」版本的 generate,「Vue3」改变了很多,其 generate 函数对应的伪代码会是这样: export function generate( ast: RootNode, options: CodegenOptions & { onContextCreated?: (context: CodegenContext) =>

Void} = {}): CodegenResult {const context = createCodegenContext (ast, options) if (options.onContextCreated) options.onContextCreated (context) const {mode, push, prefixIdentifiers, indent, deindent, newline, scopeId, ssr} = context. GenFunctionPreamble (ast, context)... If (! ssr) {... Push (`function render (_ ctx, _ cache$ {optimizeSources}) {`)}.... Return {ast, code: context.code, / / SourceMapGenerator does have toJSON () method but it's not in the types map: context.map? (context.map as any) .toJSON (): undefined}}

So, next, let's "take a look" at the process of generating executable code with AST corresponding to static nodes.

CodegenContext code generation context

As you can see from the pseudo code of the generate function above, a call to createCodegenContext at the beginning of the function generates a context for the current AST. The entire execution of the generate function "relies on" the ability of a CodegenContext to "generate code context" (object), which is generated by the createCodegenContext function. The interface definition of CodegenContext would be as follows:

Interface CodegenContext extends Omit {source: string code: string line: number column: number offset: number indentLevel: number pure: boolean map?: SourceMapGenerator helper (key: symbol): string push (code: string, node?: CodegenNode): void indent (): void deindent (withoutNewLine?: boolean): void newline (): void}

You can see that there are methods such as push, indent, newline, and so on in the CodegenContext object. Their function is to "implement line wrapping", "add code", "indent" and other functions when generating code according to AST. Thus, the executable code is eventually formed, known as the render function, and it is returned as the value of the code property of CodegenContext.

Next, let's take a look at the core of executable code generation for static nodes, which is called the Preamble preamble.

Preparation before genFunctionPreamble generation

The executable code generation of the entire static promotion is done in the genFunctionPreamble function section. Moreover, we carefully consider the word "static promotion". We do not have to look at the word static, but the word "ascension" expresses directly that it (static node) has been "improved".

Why do you say it has improved? Because the embodiment in the source code is really improved. In the previous generate function, we can see that genFunctionPreamble is added to context.code before the render function, so in the Render phase of Runtime, it is executed before the render function.

GeneFunctionPreamble function (pseudo code):

Function genFunctionPreamble (ast: RootNode, context: CodegenContext) {const {ssr, prefixIdentifiers, push, newline, runtimeModuleName, runtimeGlobalName} = context. Const aliasHelper = (s: symbol) = > `$ {helperNameMap [s]}: _ ${helperNameMap [s]} `if (ast.helpers.length > 0) {... If (ast.hoists.length) {const staticHelpers = [CREATE_VNODE, CREATE_COMMENT, CREATE_TEXT, CREATE_STATIC] .filter (helper = > ast.helpers.includes (helper)) .map (aliasHelper) .join (',') push (`const {${staticHelpers}} = _ Vue\ n`)}}. GenHoists (ast.hoists, context) newline () push (`return `)}

As you can see, the length of the hoists attribute that we mentioned earlier in the transform function is determined here. Obviously, for the chestnut mentioned above, its ast.hoists.length length is greater than 0. Therefore, the corresponding executable code is generated based on the AST in hoists. So, at this point, the generated executable code will look like this:

Const _ Vue = Vueconst {createVNode: _ createVNode} = _ Vue// static promotion part const _ hoisted_1 = _ createVNode ("div", null, "hi vue3",-1 / * HOISTED * /) / / the render function will be here above all the content of this article "what is Vue3.0 static node promotion", thank you for reading! I believe you will gain a lot after reading this article. The editor will update different knowledge for you every day. If you want to learn more knowledge, please pay attention to the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report