Writing in the front
This is the seventh vuE2 series implementation from zero, implementing V-IF and V-for in YourVue.
This article will be first updated on our official account: BUPPT. Code repository: github.com/buppt/YourV…
The body of the
Add these two directives to the main.js template
<div v-if="count===1">count === 1</div>
<div v-else-if="count===2">count === 2</div>
<div v-else>count ! = 1 && count ! = 2</div>
<div v-for="(item, key, index) in items">
<p>{{item}}</p>
</div>
Copy the code
v-if
When vue parses a template, v-if executes the following function,
function processIf (el) {
var exp = getAndRemoveAttr(el, 'v-if'); // Get the if expression
if (exp) {
el.if = exp; // Add the if attribute
addIfCondition(el, { // Add the ifConditions property, whose value is an object
exp: exp, // exp indicates the current v-if value
block: el // Block is a reference to the current AST object
});
} else {
if (getAndRemoveAttr(el, 'v-else') != null) { // Add the el.else attribute
el.else = true;
}
var elseif = getAndRemoveAttr(el, 'v-else-if'); // Add the elseif attribute
if(elseif) { el.elseif = elseif; }}}function addIfCondition (el, condition) { // Add the ifConditions attribute
if(! el.ifConditions) { el.ifConditions = []; } el.ifConditions.push(condition); }Copy the code
Add the ifConditions argument, where exp is the judgment condition and block is a reference to the current AST.
{
type: 1.if: "count===1".ifConditions: [{exp: "count===1".block: {... }} {exp: "count===2".block: {... }} {exp: undefined.block: {... }}}]Copy the code
For V-ELSE and V-else -if, we don’t add our ast object to our ast tree. Instead, we add our ast object to the latest V-IF ifConditions as follows:
if (element.elseif || element.else) {
var prev = findPrevElement(parent.children);
if (prev && prev.if) {
addIfCondition(prev, {
exp: el.elseif,
block: el }); }}function findPrevElement (children) {
var i = children.length;
while (i--) {
if (children[i].type === 1) {
return children[i]
} else{ children.pop(); }}}Copy the code
Add a judgment in gencode, and if there is an if judgment, execute genIf().
function genElement(el){
if(el.for && ! el.forProcessed) {return genFor(el)
} else if(el.if && ! el.ifProcessed) {return genIf(el)
} else{... }}export function genIf (el){
el.ifProcessed = true // avoid recursion
return genIfConditions(el.ifConditions.slice())
}
function genIfConditions (conditions) {
if(! conditions.length) {return '_e()'
}
const condition = conditions.shift()
if (condition.exp) {
return ` (${condition.exp})?${ genElement(condition.block) }:${ genIfConditions(conditions) }`
} else {
return `${genElement(condition.block)}`}}Copy the code
GenIf didn’t use the new render function, just added judgment to the render function
(count===1)?
_c('div',{},[_v("count === 1")]):
(count===2)?
_c('div',{},[_v("count === 2")]):
_c('div',{},[_v("count ! = 1 && count ! = 2")])Copy the code
v-for
Similarly, v-for adds for, alias, iterator1, and iterator2 to the AST.
function parseFor (exp) {
var inMatch = exp.match(forAliasRE);
if(! inMatch) {return }
var res = {};
res.for = inMatch[2].trim();
var alias = inMatch[1].trim().replace(stripParensRE, ' ');
var iteratorMatch = alias.match(forIteratorRE);
if (iteratorMatch) { // v-for="(item, key, index) in items"
res.alias = alias.replace(forIteratorRE, ' '); // Get alias (item)
res.iterator1 = iteratorMatch[1].trim(); // get index 1 (key)
if (iteratorMatch[2]) {
res.iterator2 = iteratorMatch[2].trim(); // Get index 2 (index)}}else {
res.alias = alias;
}
return res;
}
Copy the code
Ast as follows
{
tag: "div"
type: 1.for: "items"
alias: "item"
iterator1: "key"
iterator2: "index"
}
Copy the code
In the Gencode phase, new render function _l() is introduced when genFor is executed
export function genFor (el) {
const exp = el.for
const alias = el.alias
const iterator1 = el.iterator1 ? `,${el.iterator1}` : ' '
const iterator2 = el.iterator2 ? `,${el.iterator2}` : ' '
el.forProcessed = true // avoid recursion
return `_l((${exp}), ` +
`function(${alias}${iterator1}${iterator2}) {` +
`return ${genElement(el)}` +
'}) '
}
Copy the code
The generated code function is as follows
_l( (items),
function(item,key,index){
return _c('div',{},[_c('p',{},[_v(_s(item))])])
}
)
Copy the code
The Render phase executes the renderList function
export function initRender(vm){
vm._l = renderList
}
function renderList (val, render){
let ret, i, l, keys, key
if (Array.isArray(val) || typeof val === 'string') {
ret = new Array(val.length)
for (i = 0, l = val.length; i < l; i++) {
ret[i] = render(val[i], i)
}
} else if (typeof val === 'number') {
ret = new Array(val)
for (i = 0; i < val; i++) {
ret[i] = render(i + 1, i)
}
} else {
keys = Object.keys(val)
ret = new Array(keys.length)
for (i = 0, l = keys.length; i < l; i++) {
key = keys[i]
ret[i] = render(val[key], key, i)
}
}
if(! ret) { ret = [] } ret._isVList =true
return ret
}
Copy the code
As you can see from the renderList implementation, V-for iterates over arrays of numbers, strings, numbers, and objects, and returns a VNode array.
When the parent element createElement is created, the VNode array is expanded using simpleNormalizeChildren as described in the fourth article, and the V-for is created and updated along with other elements of the same level.
This code: github.com/buppt/YourV…