Vue.js source code analysis instructions v-for instructions

We can use the v-for instruction to render a list based on an array. There are five ways to use it, as follows:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
</head>
<body>
    <script>
        Vue.config.productionTip=false;
        Vue.config.devtools=false;
    </script>
    <div id="app">
        <p v-for="item in items">{{item}}</p>                                   <!--Array format 1, rendering result:<p>11</p><p>12</p>    -->         
        <p v-for="(item,index) in items">{{index}}->{{item}}</p>                <!--Array format 2, rendering result:<p>0->11</p><p>1->12</p>-->
        <p v-for="item in infos">{{item}}</p>                                   <!--Object format 1, rendering results:<p>gege</p><p>12</p>-->
        <p v-for="(item,key) in infos">{{key}}:{{item}}</p>                     <!--Object format 2, rendering results:<p>name:gege</p><p>age:12</p>-->
        <p v-for="(item,key,index) in infos">{{index}}:{{key}}:{{item}}</p>     <!--Object format 3, rendering results:<p>0:name:gege</p><p>1:age:12</p>-->
    </div>
    <script>
        var app = new Vue({
            data(){
                return {
                    items:[11,12],                        //v-for Can be an object
                    infos:{name:'gege',age:12}            //It can also be an array
                }
            },
            el:'#app'
        })
    </script> 
</body>
</html>

Very simple. As long as an interface is provided in the background to return an array or object, the front end can be rendered through v-for. Let's take the third format of the above object as an example to talk about the source code, as follows:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
</head>
<body>
    <script>
        Vue.config.productionTip=false;
        Vue.config.devtools=false;
    </script>
    <div id="app">
        <p v-for="(item,key,index) in infos">{{index}}:{{key}}:{{item}}</p>    
    </div>
    <script>
        var app = new Vue({
            data(){return {infos:{name:'gege',age:12}}},
            el:'#app'
        })
    </script> 
</body>
</html>

 

Source code analysis

When parsing the template, Vue's processfor() - > parsefor() function will parse these four variables according to the different contents of v-for, and save them to the properties of AST object:

function processFor (el) {            //Line 9367 for instructions
  var exp;
  if ((exp = getAndRemoveAttr(el, 'v-for'))) {    //If you get v-for attribute
    var res = parseFor(exp);                      //call parseFor Function resolves the property
    if (res) {                                    //If res existence
      extend(el, res);                              //Then call extend()Add to AST On object
    } else {
      warn$2(
        ("Invalid v-for expression: " + exp)
      );
    }
  }
}

parseFor() is used to parse the value of v-for and return an object, as follows:

function parseFor (exp) {               //Line 9383 parsing v-for attribute exp:v-for Value ;for example:"(item,key,index) in infos"
  var inMatch = exp.match(forAliasRE);                      //Using regular matching  forAliasRE Defined on line 9403 equal to:/([^]*?)\s+(?:in|of)\s+([^]*)/; 
  if (!inMatch) { return }                                  //If not, return false
  var res = {};
  res.for = inMatch[2].trim();                              //for Is equal to:infos
  var alias = inMatch[1].trim().replace(stripParensRE, ''); //Remove the brackets on both sides, and alias Be equal to:item,key,index
  var iteratorMatch = alias.match(forIteratorRE);           //Match alias and index
  if (iteratorMatch) {                                      //If it matches, this is the format:v-for="(item,index) in data"
    res.alias = alias.replace(forIteratorRE, '');               //Get aliases
    res.iterator1 = iteratorMatch[1].trim();                    //Get index
    if (iteratorMatch[2]) {
      res.iterator2 = iteratorMatch[2].trim();
    }
  } else {
    res.alias = alias;
  }
  return res                                                 //Return objects, such as:{alias: "item",for: "infos",iterator1: "key",iterator2: "index"}
}

After execution, the v-for in the example stores four attributes related to v-for, as follows:

Next, when generate generates the rendre function, it will call genFor() to generate the corresponding function, as follows:

function genFor (         //Rendering v-for instructions
  el,
  state,
  altGen,
  altHelper
) {
  var exp = el.for;                                                 //Obtain for Value
  var alias = el.alias;                                             //Get aliases
  var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : '';       //Get index(v-for When the value of is an object key)
  var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : '';       ///Get index(v-for When the value of is an object))

  if ("development" !== 'production' &&
    state.maybeComponent(el) &&
    el.tag !== 'slot' &&
    el.tag !== 'template' &&
    !el.key
  ) {
    state.warn(
      "<" + (el.tag) + " v-for=\"" + alias + " in " + exp + "\">: component lists rendered with " +
      "v-for should have explicit keys. " +
      "See https://vuejs.org/guide/list.html#key for more info.",
      true /* tip */
    );
  }

  el.forProcessed = true; // avoid recursion
  return (altHelper || '_l') + "((" + exp + ")," +                //Piece together_l function
    "function(" + alias + iterator1 + iterator2 + "){" +
      "return " + ((altGen || genElement)(el, state)) +
    '})'
}

The last generated render function is equal to:

with(this){return _c('div',{attrs:{"id":"app"}},_l((infos),function(item,key,index){return _c('p',[_v(_s(index)+":"+_s(key)+":"+_s(item))])}))}

The v-for is as follows:

_l((infos),function(item,key,index){return _c('p',[_v(_s(index)+":"+_s(key)+":"+_s(item))

_The first parameter of l is the target of our v-for, that is, infos, which will traverse the object later.

When rendering to generate VNode, the  l function inside Vue, that is, the global renderList, is executed as follows:

function renderList (     //Line 3691 rendering v-for instructions
  val,  
  render
) {
  var ret, i, l, keys, key;
  if (Array.isArray(val) || typeof val === 'string') {    //If val It's an array.
    ret = new Array(val.length);                            //take ret Cheng Yi Cheng val Same size array
    for (i = 0, l = val.length; i < l; i++) {                 //ergodic val array
      ret[i] = render(val[i], i);                                 //Call in turn render Function, parameter 1 is the value, parameter 2 is the index return VNode,And the results VNode Save to ret inside
    }
  } else if (typeof val === 'number') {
    ret = new Array(val);
    for (i = 0; i < val; i++) {
      ret[i] = render(i + 1, i);
    }
  } else if (isObject(val)) {
    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 (isDef(ret)) {                                       //If ret existence(Successfully called)
    (ret)._isVList = true;                                  //Add an_isVList Tag with value true
  }
  return ret                                                //Last return ret
}

Finally, it is packed into VNode array and returned as the child node of other elements (﹤ c's third parameter).

Keywords: PHP Vue npm Attribute

Added by Randy Jackson on Sun, 03 Nov 2019 05:40:19 +0200