preface
Compiler is one of the three modules in Vue and is responsible for compiling templates. As we know, a render function is generated when the template is compiled and parsed, which is then called at Runtime to create the actual DOM tree. Vue3 template compilation in this site, we can see the actual render function generated by the string template after compiler.
The render function is generated by the template in compiler, but how does it work internally? PatchFlag, hoist, cacheHandlers… How do you do that?
Here we give a brief overview, and we will expand it in detail in later articles. In Compiler, there are three processes:
parse
: converts the string template toAST
transform
:AST
Optimize and add some property values in preparation for the next stepscodegen
Will:AST
Transgendered into onerender
function
In VUe2 there is no transform step, it is optimize. Both are optimizations to AST, but Transform has many new responsibilities, such as patchFLag mentioned above
The module of decoupling
Like runtime, compiler is also divided into Comolil-core and platform-specific compiler to improve extensibility. For example:
Compiler-dom, Compiler-SFC and Compiler-SSR, among which the compiler of each platform still calls the compiler-core method.
compiler-dom
Browser:compiler-sfc
: single file componentcompiler-ssr
: Server rendering
compiler-dom
Now start with compiler-DOM.
export function compile(template: string, options: CompilerOptions = {}) :CodegenResult {
return baseCompile(
template,
extend({}, parserOptions, options, {
nodeTransforms: [
ignoreSideEffectTags,
...DOMNodeTransforms,
...(options.nodeTransforms || [])
],
directiveTransforms: extend(
{},
DOMDirectiveTransforms,
options.directiveTransforms || {}
),
transformHoist: __BROWSER__ ? null : stringifyStatic // Whether the hoist is optimized}}))Copy the code
As you can see, baseCompile is called inside the compile function. BaseCompiler is derived from compiler-core.
When you look at the arguments passed in the call, you pass in template and options mixed with many objects. ParseOptions is an option provided by compiler-DOM for the DOM platform.
Let’s move on to the parseOptions provided by compiler-DOM
export const parserOptions: ParserOptions = {
isVoidTag, // Whether autistic and
isNativeTag: tag= > isHTMLTag(tag) || isSVGTag(tag), // Is the label of a platform element
isPreTag: tag= > tag === 'pre'.// Whether the label is pre
decodeEntities: __BROWSER__ ? decodeHtmlBrowser : decodeHtml, // Decode the content
// Check whether it is an internal label
isBuiltInComponent: (tag: string): symbol | undefined= > {},
// Get the namespace
getNamespace(tag: string, parent: ElementNode | undefined): DOMNamespaces {},
// Get the label's textMode, which affects parse
getTextMode({ tag, ns }: ElementNode): TextModes {}
}
Copy the code
It contains these properties and methods:
isVoidTag
: Indicates whether the device is closed automaticallyisNativeTag
: Indicates whether a native label is provided by the platformisPreTag
: whether it ispre
The labeldecodeEntities
: Decodes the contentisBuiltInComponent
: Checks whether the label is internalgetNamespace
: Gets the namespacegetTextMode
: To obtain the labeltextMode
Among them, textMode is very important, which affects the parsing of Prase. Different TextModes judge the end of parsing and have different specific parsing.
baseCompile
As we have seen above, the core of compiler-DOM’s compile function lies in internally calling the baseCompiler function provided by compiler-core. Now let’s see what the baseCompile function does
export function baseCompile(template: string | RootNode, options: CompilerOptions = {}) :CodegenResult {
// Check parameters
const onError = options.onError || defaultOnError
const isModuleMode = options.mode === 'module' // DOM platform is false
/* istanbul ignore if */
if (__BROWSER__) {
if (options.prefixIdentifiers === true) {
onError(createCompilerError(ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED))
} else if (isModuleMode) {
onError(createCompilerError(ErrorCodes.X_MODULE_MODE_NOT_SUPPORTED))
}
}
// mark code generation mode: function mode/module mode
constprefixIdentifiers = ! __BROWSER__ && (options.prefixIdentifiers ===true || isModuleMode)
if(! prefixIdentifiers && options.cacheHandlers) { onError(createCompilerError(ErrorCodes.X_CACHE_HANDLER_NOT_SUPPORTED)) }if(options.scopeId && ! isModuleMode) { onError(createCompilerError(ErrorCodes.X_SCOPE_ID_NOT_SUPPORTED)) }// 1. Convert the template string to ast
const ast = isString(template) ? baseParse(template, options) : template
Get the default transform method according to the prefixIdentifiers
const [nodeTransforms, directiveTransforms] = getBaseTransformPreset(
prefixIdentifiers
)
// 2. Optimize ast
transform(
ast,
extend({}, options, {
prefixIdentifiers,
nodeTransforms: [
...nodeTransforms,
...(options.nodeTransforms || []) // user transforms].directiveTransforms: extend(
{},
directiveTransforms,
options.directiveTransforms || {} // user transforms)}))// 3. Turn ast into render function
return generate(
ast,
extend({}, options, {
prefixIdentifiers
})
)
}
Copy the code
- Parameter verification is performed to mark code generation patterns
prefixIdentifiers
- call
baseParse
Converts the template string toast
- According to the
prefixIdentifiers
To get the defaultnodeTransforms
anddirectiveTransforms
- call
transform
rightast
To optimize - call
generate
According to theast
The generated code
You can see that all three procedures we mentioned above are called in baseCompile. We’ll take a closer look at each one in the following articles.