This is the 18th day of my participation in the First Challenge 2022.
Previously on & Background
The previous essay describes the specific logic of the tools and methods used in the parseHTML process.
advance
: maintenanceindex
和html
.index
Recorded in the originalhtml
Processing position in,html
Gradually shorten into untreated parts;parseStartTag
: Matches the start tag with the retagName
和attrs
And the end of the start tag>
or/>
, and pushedstack
Information about the current start tag;handleStartTag
: Further processingparsetStartTag
To get thematch
Object, transformattrs
The array;parseEndTag
: Maintains when the end tag is matchedstack
To makestack
The start tag corresponding to the current end tag is out of the stack;
Again and again, parseHTML uses < as a flag in the while(HTML) loop to separate templates into comments, conditional comments, document declarations, plain text, opening tags, closing tags, Then call options.comment, options.chars, options.start, and options.end to process the corresponding content types and convert them into AST nodes of fixed types.
This composition will focus on the methods passed in from options. I have not talked about this part before, because when I look at the source code, various methods are called back and forth, plus JS is an expert in callback methods, it makes people feel confused. This is because opitons are the callback method passed in when parseHTML is called from within the parse method.
Although I advocate looking at the source code in accordance with the order of the execution of the serial organization of small composition, but also to the scene, here is obviously not applicable to this method. Even so, I’ll do it the old-fashioned way — review calls and arguments first, and then cover method positions, arguments, and functions.
Second, the options.com ment
Method location: the callback method passed in when parseHTML is called within the parse method as follows:
export function parse (template: string, options: CompilerOptions) :ASTElement | void {
/ /...
parseHTML(template, {
warn,
/ /...
outputSourceRange: options.outputSourceRange,
comment (text: string, start, end) {
}
})
// Returns the generated AST object
return root
}
Copy the code
Method parameters:
text
, comment textstart
: indicates the starting index positionend
: Indicates the end index position
Methods:
- According to the
currentParent
Existence determines whether the current comment node is the same as the root node. Comments that are the same as the root node are ignored.currentParent
If no, it is level with root node. currentParent
If it exists, it is createdAST
Node, and put itpush
到currentParent
In theThe children in the
;
export function parse (template: string, options: CompilerOptions) :ASTElement | void {
/ /...
parseHTML(template, {
warn,
/ /...
outputSourceRange: options.outputSourceRange,
comment (text: string, start, end) {
// Prohibit adding any nodes as siblings of root. Comments are allowed, but are ignored when creating the AST
if (currentParent) {
// This is the ast node of the comment, isComment: true, and the comment content is text
const child: ASTText = {
type: 3,
text,
isComment: true
}
if(process.env.NODE_ENV ! = ='production' && options.outputSourceRange) {
// Start index and end index of ast node in non-production environment
child.start = start
child.end = end
}
// Put the current comment node into the children of the node
currentParent.children.push(child)
}
}
})
// Returns the generated AST object
return root
}
Copy the code
Third, the options. Chars
Method location: the callback method passed in when parseHTML is called within the parse method as follows:
export function parse (template: string, options: CompilerOptions) :ASTElement | void {
/ /...
parseHTML(template, {
// ...
shouldKeepComment: options.comments,
chars (text: string, start: number, end: number) {
}
})
// Returns the generated AST object
return root
}
Copy the code
Method parameters:
text
The textstring
start
, the starting index positionend
To end the index position
Methods:
- If the current text does not
currentParent
, indicating that the text has no parent element, ignores and prompts inroot
Text outside the element - The next step is case by case
text
Condition: Whether inpre
Tags, whether to compress newlines and other operations - After the second step
text
Not empty, no longerpre
In, compress successive Spaces and then create textast
Node object, if present in textVue
Template binding syntax{{xxx}}
This,ast
的type
为2
, plain textast
The object’stype
为3
- Will generate the text above
ast
push
到children
export function parse (template: string, options: CompilerOptions) :ASTElement | void {
parseHTML(template, {
//
chars (text: string, start: number, end: number) {
// currentParent does not exist, indicating that the text has no parent element and is outside the root element
if(! currentParent) {if(process.env.NODE_ENV ! = ='production') {
// Prompts text outside the root element
}
return
}
// IE textarea placeholder bug
// Get an array of all the children of the current parent element
const children = currentParent.children
// Process text,
// Remove whitespace characters or if the whitespaceOptions option exists, text is assigned to an empty string or space
if (inPre || text.trim()) {
// The text inside the pre tag or after trim is not empty
text = isTextTag(currentParent) ? text : decodeHTMLCached(text)
} else if(! children.length) {// If this line goes here,
Text.trim () is empty, and the current parent element has no child node
// The text value is null
text = ' '
} else if (whitespaceOption) {
/ / compression
if (whitespaceOption === 'condense') {
// in condense mode, remove the whitespace node if it contains
// line break, otherwise condense to a single space
// In compression mode, remove blank nodes containing newlines, otherwise compress to a space
text = lineBreakRE.test(text) ? ' ' : ' '
} else {
text = ' ' / / space}}else {
text = preserveWhitespace ? ' ' : ' '
}
// After the previous processing, text is not empty
if (text) {
if(! inPre && whitespaceOption ==='condense') {
// Does not exist on the Pre label and condense multiple Spaces into a single condense configuration item
// condense consecutive whitespaces into single space
text = text.replace(whitespaceRE, ' ')}let res
// Generate ast objects based on text
letchild: ? ASTNodeif(! inVPre && text ! = =' ' && (res = parseText(text, delimiters))) {
// There are expressions in the text, i.e. {{}} Vue's data binding syntax
child = {
type: 2.expression: res.expression,
tokens: res.tokens,
text
}
} else if(text ! = =' '| |! children.length || children[children.length -1].text ! = =' ') {
// Plain text node
child = {
type: 3,
text
}
}
// Add child to the parent element's children.
// Push into the currentParent.children array
if (child) {
if(process.env.NODE_ENV ! = ='production' && options.outputSourceRange) {
child.start = start
child.end = end
}
children.push(child)
}
}
},
// Returns the generated AST object
return root
}
Copy the code
Four,
Options.com ment, options.chars; options.chars; options.chars; options.chars;
Options.com ment is used to process comments. The method ignores the comment node at the root level, and then creates an AST node for the comment content and adds it to the currentParent. Children array.
Options. chars processes characters and determines whether to compress them according to the configured character options. Then, ast nodes of different types are created based on whether Vue’s dynamic binding syntax {{xx}} exists
Then there are the two most important methods, options.start and options.end. These two methods are the most important ones, so they will be presented in separate sections.