introduction
Continue with the aboveparseHTML function source code analysis start hook function
Next, we will mainly explain how to create element description objects for text nodes when the parser encounters a text node, and how to do special treatments for text nodes.
parseHTML(template, { chars: function(){ //... }, //... })
chars source code:
chars: function chars(text) { if (!currentParent) { { if (text === template) { warnOnce( 'Component template requires a root element, rather than just text.' ); } else if ((text = ())) { warnOnce( ("text \"" + text + "\" outside root element will be ignored.") ); } } return } // IE textarea placeholder bug /* istanbul ignore if */ if (isIE && === 'textarea' && === text ) { return } var children = ; text = inPre || () ? isTextTag(currentParent) ? text : decodeHTMLCached(text) // only preserve whitespace if its not right after a starting tag : preserveWhitespace && ? ' ' : ''; if (text) { var res; if (!inVPre && text !== ' ' && (res = parseText(text, delimiters))) { ({ type: 2, expression: , tokens: , text: text }); } else if (text !== ' ' || ! || children[ - 1].text !== ' ') { ({ type: 3, text: text }); } } }
When the parser encounters a text node, the chars hook function as in the above code will be called and the text content of the text node will be received as a parameter.
Let's look at the code at the beginning of the chars hook function:
if (!currentParent) { { if (text === template) { warnOnce( 'Component template requires a root element, rather than just text.' ); } else if ((text = ())) { warnOnce( ("text \"" + text + "\" outside root element will be ignored.") ); } } return }
First, we judge whether the currentParent variable exists. We know that the currentParent variable points to the parent node of the current node:.
What is the problem if the currentParent variable does not exist?
- 1: No root element, only text.
- 2: The text is outside the root element.
When the first situation is encountered, the warning message is printed: "The template must have a root element", and the second situation is printed: "The text outside the root element will be ignored".
Next:
if (isIE && === 'textarea' && === text ) { return }
This code is used to solve the bugs that exist when rendering the placeholder property of the <textarea> tag in IE browser. You can find specific questions in thisissueCheck.
Next is a nested ternary expression:
var children = ; text = inPre || () ? isTextTag(currentParent) ? text : decodeHTMLCached(text) // only preserve whitespace if its not right after a starting tag : preserveWhitespace && ? ' ' : '';
This nested ternary expression determines the authenticity of the condition inPre || (). If true, it detects whether the parent node of the current text node is a text tag. If it is a text tag, the original text is directly used. Otherwise, the text is decoded using the decodeHTMLCached function.
inPre || () If false, check whether preserveWhitespace is true. preserveWhitespace is a boolean value that represents whether to keep spaces, and only if it is true. But even if the value of the preserveWhitespace constant is true, if the parent of the current node does not have child elements, the spaces will not be retained. In other words, the compiler will only retain those spaces that do not exist after the start label.
Next:
if (text) { var res; if (!inVPre && text !== ' ' && (res = parseText(text, delimiters))) { ({ type: 2, expression: , tokens: , text: text }); } else if (text !== ' ' || ! || children[ - 1].text !== ' ') { ({ type: 3, text: text }); } }
Here is quite simple if else if operation, the first if determines that the current element does not use the v-pre directive and the text is not empty. The parseText function is used to successfully parse the content of the current text node.
The first two conditions are easy to understand. The key is what the parseText function can successfully parse the content of the text node, as shown in the following example code:
<div> hello: {{ message }} </div>
As the text nodes in the above template contain literal expressions in Vue syntax, and the function of the parseText function is to parse this text containing literal expressions. The following code is executed to create an element description object of type 2 (type = 2):
({ type: 2, expression: , tokens: , text: text });
Note: The element description object of type 2 has three special properties, namely expression , tokens and text , where text is the original text content, and the values of expression and tokens are read from the results parsed by the parseText function.
Later, we will talk about the parseText function specifically. Next, we will continue to look at the three possibilities that occur if the judgment in the above column fails.
- The currently parsed element uses the v-pre directive
- text is empty
- parseText parse failed
As long as one of the above three situations occurs, the code will come to the judgment of the else...if branch, as follows:
else if (text !== ' ' || ! || children[ - 1].text !== ' ') { ({ type: 3, text: text }); }
If the condition in else if is met directly, create an element description object of type 3 (type = 3): the element description object of type 3 only has one attribute text to store the original text content.
Let’s see if you need to meet these conditions in else if!
- Text content is not a space
- The text content is a space, but the parent node of the text node does not have a child node yet (i.e. ! )
- The text content is a space, and the parent node of the text node has a child node, but the last child node is not a space
Next, let’s talk about the parseText function mentioned before.
parseText
function parseText( text, delimiters ) { var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE; if (!(text)) { return } var tokens = []; var rawTokens = []; var lastIndex = = 0; var match, index, tokenValue; while ((match = (text))) { index = ; // push text token if (index > lastIndex) { (tokenValue = (lastIndex, index)); ((tokenValue)); } // tag token var exp = parseFilters(match[1].trim()); (("_s(" + exp + ")")); ({ '@binding': exp }); lastIndex = index + match[0].length; } if (lastIndex < ) { (tokenValue = (lastIndex)); ((tokenValue)); } return { expression: ('+'), tokens: rawTokens } }
parseText receives two parameters text text to parse text, delimiters is a user-defined option for the compilerdelimiters, through which you can change the text insertion delimiter. That's why the following code is found.
var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE;
Here is a contest between the rules used to parse text. If delimiters has a value, call the buildRegex function. We have no value by default, and use defaultTagRE to parse text.
var defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g;
This rule is still very simple. Next, we will judge that if there is no text in the text that matches the rule, it will directly terminate the execution of the function.
if (!(text)) { return }
Let’s take a look at the code next.
var tokens = []; var rawTokens = []; var lastIndex = = 0; var match, index, tokenValue; while ((match = (text))) { index = ; // push text token if (index > lastIndex) { (tokenValue = (lastIndex, index)); ((tokenValue)); } // tag token var exp = parseFilters(match[1].trim()); (("_s(" + exp + ")")); ({ '@binding': exp }); lastIndex = index + match[0].length; } if (lastIndex < ) { (tokenValue = (lastIndex)); ((tokenValue)); } return { expression: ('+'), tokens: rawTokens }
This code is not difficult, and a series of variables are initially defined. Then start a while loop, use tagRE to regularly match the text content, and save the match result in the match variable, and will not terminate until the matching failure loop is completed, which means that all literal expressions have been processed.
At the end of this while loop, an object is returned, and expression and tokens store information during the parsing process respectively.
Assume the text is as follows:
<div >hello {{ message }}</div>
parseText The object returned after parsing the text.
{ expression: "'hello'+_s(message)", tokens: [ 'hello', { '@binding': 'message' } ] }
Next, let’s talk about the processing of the end tag.
end source code
end: function end() { // remove trailing whitespace var element = stack[ - 1]; var lastNode = [ - 1]; if (lastNode && === 3 && === ' ' && !inPre) { (); } // pop stack -= 1; currentParent = stack[ - 1]; closeElement(element); }
end hook function, when parsing html strings encounter the end tag. So what do you need to do in the end hook function?
In previous articles, we said that when the parser encounters a non-unary tag, it will set the element description object of the tag to the currentParent variable, which means that all tags encountered during subsequent parsing should be child nodes of the tag represented by the currentParent variable, and the element description object of the tag will also be added to the stack stack.
When encountering the end tag, it means that the tag represented by the currentParent variable and its children have all been parsed. At this time, we should modify the reference of the currentParent variable to the parent tag of the current tag, so that we restore the scope to the upper node to ensure the correct parent-child relationship during the parsing process.
The following code is to accomplish this:
-= 1; currentParent = stack[ - 1]; closeElement(element);
First, put the current node out of the stack: -= 1 What does it mean?
Just read a code to understand.
var arr = [1,2,3,4]; -=1; >arr [1,2,3]
Then read the last element in the stack after it is released as the value of the currentParent variable. So what is the closeElement function used for?
closeElement source code
function closeElement(element) { // check pre state if () { inVPre = false; } if (platformIsPreTag()) { inPre = false; } // apply post-transforms for (var i = 0; i < ; i++) { postTransforms[i](element, options); } }
closeElement has two functions: the first is to restore the data state, and the second is to call the post-processing conversion hook function.
Next, let’s take a look at the remaining code in the end function:
// remove trailing whitespace var element = stack[ - 1]; var lastNode = [ - 1]; if (lastNode && === 3 && === ' ' && !inPre) { (); }
The purpose of this code is to remove the last blank child node of the current element. When we explained the chars hook function, we learned that preserveWhitespace will only retain those spaces that are not after the start label, so when the blank exists as the last child node of the label, it will also be retained, as shown in the following code:
<div><span>test</span> <!-- Blank placeholder --> </div>
As in the above code, there is a blank space between the end tag of the <span> tag and the end tag of the <div> tag, and this blank space will be retained. If this blank is retained, it may have an impact on the layout, especially on elements within the line.
To eliminate the problems caused by these effects, it is a good practice to remove them, and code is used to do this.
comment comment node description object
Whether the parser will parse and retain comment nodes is determined by the shouldKeepComment compiler option. Developers can control the compiler's shouldKeepComment option by setting the value of the comments option when creating a Vue instance. By default, the value of the comments option is false , that is, no comment is retained. If it is set to true , the comment node will be retained when the parseHTML function encounters the comment hook function, at this time the parseHTML function's comment hook function will be called, as follows:
comment: function comment(text) { ({ type: 3, text: text, isComment: true }); }
It should be noted that the type of the element description object of the ordinary text node and the annotation node is the same as the element description object of the annotation node has the isComment attribute, and the value of the attribute is true, and the purpose is to distinguish it from the ordinary text node.
The above is the detailed content of vue parseHTML source code parse hars end comment hook function. For more information about vue parseHTML hook function, please pay attention to my other related articles!