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…