Vue源碼分析(6)--編譯過(guò)程分析(1)

前言

本文是vue2.x源碼分析的第六篇,主要講解編譯compile過(guò)程!

調(diào)用方式

var compiled = compile(template, options);

1 分析compile

//tips:請(qǐng)結(jié)合斷點(diǎn)調(diào)試,該函數(shù)位于閉包c(diǎn)reateCompiler中,有的變量是在上層函數(shù)中定義的
function compile (template, options) {
    var finalOptions = Object.create(baseOptions);
    var errors = [];
    var tips = [];
    finalOptions.warn = function (msg, tip$$1) {
      (tip$$1 ? tips : errors).push(msg);
    };
    if (options) {
      //合并自定義modules
      if (options.modules) {
        finalOptions.modules = (baseOptions.modules || []).concat(options.modules);
      }
      //合并自定義directives
      if (options.directives) {
        finalOptions.directives = extend(
          Object.create(baseOptions.directives),
          options.directives
        );
      }
      // copy other options
      for (var key in options) {
        if (key !== 'modules' && key !== 'directives') {
          finalOptions[key] = options[key];
        }
      }
    }
    /*以上都是處理finalOptions,到這里finalOptions如下:
    {
        delimiters:undefined,
        shouldDecodeNewlines:false,
        warn:function (msg, tip$$1),
        __proto__:Object
    }
    這個(gè)__proto__指向一個(gè)預(yù)先定義好的baseOptions對(duì)象,該對(duì)象長(zhǎng)這樣:
        var baseOptions = {
          expectHTML: true,
          modules: modules$1,//modules$1=[klass$1,style$1]
          directives: directives$1, //這里預(yù)先定義了html,text,model三個(gè)指令
          isPreTag: isPreTag,
          isUnaryTag: isUnaryTag,
          mustUseProp: mustUseProp,
          canBeLeftOpenTag: canBeLeftOpenTag,
          isReservedTag: isReservedTag,
          getTagNamespace: getTagNamespace,
          staticKeys: genStaticKeys(modules$1)
        };
    */
    var compiled = baseCompile(template, finalOptions); //主要函數(shù)
    {
      errors.push.apply(errors, detectErrors(compiled.ast));
    }
    compiled.errors = errors;
    compiled.tips = tips;
    return compiled
  }

來(lái)看看baseCompile(template, finalOptions)

function baseCompile (template,options) {
  var ast = parse(template.trim(), options); //主要函數(shù)1
  optimize(ast, options);
  var code = generate(ast, options);         //主要函數(shù)2
  return {
    ast: ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
}

2 分析 parse(template.trim(), options);

//主要是調(diào)用parseHTML(html, options)解析html,返回結(jié)果ast是含有如下屬性的對(duì)象
    // attrs:Array
    // attrsList:Array
    // attrsMap:Object
    // children:Array
    // parent:undefined
    // plain:false
    // static:false
    // staticRoot:false
    // tag:"div"
    // type:1
    // __proto__:Object
function parse (template,options) {
  warn$2 = options.warn || baseWarn;
  platformGetTagNamespace = options.getTagNamespace || no;
  platformMustUseProp = options.mustUseProp || no;
  platformIsPreTag = options.isPreTag || no;
  //這個(gè)pluckModuleFunction函數(shù)作用就是從options.modules中取出key為'preTransformNode'的值
  preTransforms = pluckModuleFunction(options.modules, 'preTransformNode');
  transforms = pluckModuleFunction(options.modules, 'transformNode');
  postTransforms = pluckModuleFunction(options.modules, 'postTransformNode');
  delimiters = options.delimiters;
  var stack = [];
  var preserveWhitespace = options.preserveWhitespace !== false;
  var root;  //作為結(jié)果返回
  var currentParent;
  var inVPre = false;
  var inPre = false;
  var warned = false;
  function warnOnce (msg) {
    if (!warned) {
      warned = true;
      warn$2(msg);
    }
  }
  function endPre (element) {
    // check pre state
    if (element.pre) {
      inVPre = false;
    }
    if (platformIsPreTag(element.tag)) {
      inPre = false;
    }
  }
  //parseHTML第二個(gè)參數(shù)里有很重要的三個(gè)函數(shù):start,end,chars
  parseHTML(template, {
    warn: warn$2,
    expectHTML: options.expectHTML,
    isUnaryTag: options.isUnaryTag,
    canBeLeftOpenTag: options.canBeLeftOpenTag,
    shouldDecodeNewlines: options.shouldDecodeNewlines,
    //start和end函數(shù)負(fù)責(zé)構(gòu)建節(jié)點(diǎn)樹(shù)
    start: function start (tag, attrs, unary) {
      // check namespace.
      // inherit parent ns if there is one
      var ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag);
      // handle IE svg bug
      /* istanbul ignore if */
      if (isIE && ns === 'svg') {
        attrs = guardIESVGBug(attrs);
      }
      var element = { //節(jié)點(diǎn)
        type: 1,
        tag: tag,
        attrsList: attrs,
        attrsMap: makeAttrsMap(attrs),
        parent: currentParent,
        children: []
      };
      if (ns) {
        element.ns = ns;
      }
      //不處理style和script標(biāo)簽
      if (isForbiddenTag(element) && !isServerRendering()) {
        element.forbidden = true;
        "development" !== 'production' && warn$2(
          'Templates should only be responsible for mapping the state to the ' +
          'UI. Avoid placing tags with side-effects in your templates, such as ' +
          "<" + tag + ">" + ', as they will not be parsed.'
        );
      }
      // 猜測(cè):html如果用了其他的模板,如ejs等需要先轉(zhuǎn)換
      // apply pre-transforms
      for (var i = 0; i < preTransforms.length; i++) {
        preTransforms[i](element, options);
      }
      //處理v-pre指令
      if (!inVPre) {
        processPre(element);
        if (element.pre) {
          inVPre = true;
        }
      }
      if (platformIsPreTag(element.tag)) {
        inPre = true;
      }
      //如果含有v-pre指令,則直接調(diào)用processRawAttrs(element);處理原始屬性
      if (inVPre) {
        processRawAttrs(element);
      } else {
        processFor(element);//處理v-for指令,會(huì)將v-for='xx'替換成其他字符串
        processIf(element);//處理v-if指令
        processOnce(element);//處理v-once指令
        processKey(element);//處理key
        // determine whether this is a plain element after
        // removing structural attributes
        // 移除結(jié)構(gòu)性屬性后判斷該元素是不是plain元素
        element.plain = !element.key && !attrs.length;
        processRef(element);//處理ref
        processSlot(element);//處理slot
        processComponent(element);//處理component
        for (var i$1 = 0; i$1 < transforms.length; i$1++) {
          transforms[i$1](element, options); //對(duì)class和style屬性進(jìn)行處理
        }
        //以上處理了v-for,v-if,v-once,v-pre等指令,但還有其它指令,如v-on,v-bind,
        //以及它們的快捷寫法'@:',':',該函數(shù)就是處理這些指令以及普通元素,處理的結(jié)果就是
        //在element上加了一個(gè)attrs屬性,存放原始屬性
        processAttrs(element);
      }
      function checkRootConstraints (el) {
        {
          if (el.tag === 'slot' || el.tag === 'template') {
            warnOnce(
              "Cannot use <" + (el.tag) + "> as component root element because it may " +
              'contain multiple nodes.'
            );
          }
          if (el.attrsMap.hasOwnProperty('v-for')) {
            warnOnce(
              'Cannot use v-for on stateful component root element because ' +
              'it renders multiple elements.'
            );
          }
        }
      }
      // 經(jīng)過(guò)上述處理后,由于可能有v-if這種會(huì)改變樹(shù)結(jié)構(gòu)的指令,所以需要對(duì)結(jié)構(gòu)樹(shù)
      // 進(jìn)一步處理,至此第一輪while循環(huán)解析完成,接下來(lái)就是重復(fù)這個(gè)過(guò)程了
      if (!root) {
        root = element;
        checkRootConstraints(root); //根節(jié)點(diǎn)不能是slot/template元素,且不能含有v-for指令
      } else if (!stack.length) {
        // 允許根元素使用 v-if, v-else-if and v-else
        if (root.if && (element.elseif || element.else)) {
          checkRootConstraints(element);
          addIfCondition(root, {
            exp: element.elseif,
            block: element
          });
        } else {
          warnOnce(
            "Component template should contain exactly one root element. " +
            "If you are using v-if on multiple elements, " +
            "use v-else-if to chain them instead."
          );
        }
      }
      if (currentParent && !element.forbidden) {
        if (element.elseif || element.else) {
          processIfConditions(element, currentParent);
        } else if (element.slotScope) { // scoped slot
          currentParent.plain = false;
          var name = element.slotTarget || '"default"';(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element;
        } else {
          currentParent.children.push(element);
          element.parent = currentParent;
        }
      }
      if (!unary) {
        currentParent = element;
        stack.push(element);
      } else {
        endPre(element);
      }
      // apply post-transforms
      for (var i$2 = 0; i$2 < postTransforms.length; i$2++) {
        postTransforms[i$2](element, options);
      }
    },
    end: function end () {
      // 刪除尾隨空格
      var element = stack[stack.length - 1];
      var lastNode = element.children[element.children.length - 1];
      if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) {
        element.children.pop();
      }
      // pop stack
      stack.length -= 1;
      currentParent = stack[stack.length - 1];
      endPre(element);
    },
    chars: function chars (text) {
      if (!currentParent) {
        {
          if (text === template) {
            warnOnce(
              'Component template requires a root element, rather than just text.'
            );
          } else if ((text = text.trim())) {
            warnOnce(
              ("text \"" + text + "\" outside root element will be ignored.")
            );
          }
        }
        return
      }
      // IE textarea placeholder bug
      /* istanbul ignore if */
      if (isIE &&
          currentParent.tag === 'textarea' &&
          currentParent.attrsMap.placeholder === text) {
        return
      }
      var children = currentParent.children;
      text = inPre || text.trim()
        ? decodeHTMLCached(text)
        // only preserve whitespace if its not right after a starting tag
        : preserveWhitespace && children.length ? ' ' : '';
      if (text) {
        var expression;
        if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) {
          children.push({
            type: 2,
            expression: expression,
            text: text
          });
        } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') {
          children.push({
            type: 3,
            text: text
          });
        }
      }
    }
  });



  return root
}

來(lái)看看parseHTML(template,options)

/*解析過(guò)程中最重要的函數(shù),因此代碼量較大*/
function parseHTML (html, options) {  //將template傳給html
  var stack = [];
  var expectHTML = options.expectHTML;
  var isUnaryTag$$1 = options.isUnaryTag || no; //是否是一元標(biāo)簽
  var canBeLeftOpenTag$$1 = options.canBeLeftOpenTag || no;
  var index = 0;
  var last, lastTag;
  while (html) {       //通過(guò)while循環(huán)一步步處理html,每處理一步就縮短html,直至html為空
    last = html;
    // 不處理script/style/textarea元素
    if (!lastTag || !isPlainTextElement(lastTag)) {
      var textEnd = html.indexOf('<');
      if (textEnd === 0) {
        // 當(dāng)匹配到Comment,只對(duì)html推進(jìn),不做其他處理
        if (comment.test(html)) {
          var commentEnd = html.indexOf('-->');
          if (commentEnd >= 0) {
            advance(commentEnd + 3);
            continue
          }
        }
        //當(dāng)匹配到conditionalComment,同Comment一樣處理
        // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
        if (conditionalComment.test(html)) {
          var conditionalEnd = html.indexOf(']>')
          if (conditionalEnd >= 0) {
            advance(conditionalEnd + 2);
            continue
          }
        }
        //當(dāng)匹配到doctype,同Comment一樣處理,/^<!DOCTYPE [^>]+>/i
        var doctypeMatch = html.match(doctype);
        if (doctypeMatch) {
          advance(doctypeMatch[0].length);
          continue
        }
        // 當(dāng)匹配到end tag,同Comment一樣處理,/^<\/((?:[a-zA-Z_][\w\-\.]*\:)?[a-zA-Z_][\w\-\.]*)[^>]*>/
        var endTagMatch = html.match(endTag);
        if (endTagMatch) {
          var curIndex = index;
          advance(endTagMatch[0].length);
          parseEndTag(endTagMatch[1], curIndex, index);
          continue
        }
        // 除以上四種,就默認(rèn)以下處理
        var startTagMatch = parseStartTag();
        if (startTagMatch) {
          handleStartTag(startTagMatch);
          continue
        }
      }
      var text = (void 0), rest$1 = (void 0), next = (void 0);
      if (textEnd >= 0) {
        rest$1 = html.slice(textEnd);
        while (
          !endTag.test(rest$1) &&
          !startTagOpen.test(rest$1) &&
          !comment.test(rest$1) &&
          !conditionalComment.test(rest$1)
        ) {
          // < in plain text, be forgiving and treat it as text
          next = rest$1.indexOf('<', 1);
          if (next < 0) { break }
          textEnd += next;
          rest$1 = html.slice(textEnd);
        }
        text = html.substring(0, textEnd);
        advance(textEnd);
      }
      if (textEnd < 0) {
        text = html;
        html = '';
      }
      if (options.chars && text) {
        options.chars(text);
      }
    }
    else {
      var stackedTag = lastTag.toLowerCase();
      var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i'));
      var endTagLength = 0;
      var rest = html.replace(reStackedTag, function (all, text, endTag) {
        endTagLength = endTag.length;
        if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') {
          text = text
            .replace(/<!--([\s\S]*?)-->/g, '$1')
            .replace(/<!\[CDATA\[([\s\S]*?)]]>/g, '$1');
        }
        if (options.chars) {
          options.chars(text);
        }
        return ''
      });
      index += html.length - rest.length;
      html = rest;
      parseEndTag(stackedTag, index - endTagLength, index);
    }
    if (html === last) {
      options.chars && options.chars(html);
      if ("development" !== 'production' && !stack.length && options.warn) {
        options.warn(("Mal-formatted tag at end of template: \"" + html + "\""));
      }
      break
    }
  }
  // Clean up any remaining tags
  parseEndTag();
  function advance (n) {
    index += n;
    html = html.substring(n);
  }
  function parseStartTag () {
    //startTagOpen='/^<((?:[a-zA-Z_][\w\-\.]*\:)?[a-zA-Z_][\w\-\.]*)/'
    var start = html.match(startTagOpen);
    if (start) {
      var match = {
        tagName: start[1],
        attrs: [],
        start: index
      };
      advance(start[0].length);
      var end, attr;
      //開(kāi)始尋找屬性
      //startTagClose='/^\s*(\/?)>/'
      //attribute='/^\s*([^\s"'<>\/=]+)(?:\s*((?:=))\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/'
      while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
        advance(attr[0].length);
        match.attrs.push(attr);
      }
      if (end) {
        match.unarySlash = end[1];//若'/'存在,則賦值給unarySlash
        advance(end[0].length);
        match.end = index;
        return match   //至此,parseStartTag結(jié)束,接下來(lái)執(zhí)行handleStartTag(match);match此時(shí)長(zhǎng)這樣
        /*
        attrs:Array(1)
        end:14
        start:0
        tagName:"div"
        unarySlash:""
        __proto__:Object
         */
      }
    }
  }
  function handleStartTag (match) {
    var tagName = match.tagName;
    var unarySlash = match.unarySlash;
    if (expectHTML) {
      if (lastTag === 'p' && isNonPhrasingTag(tagName)) {
        parseEndTag(lastTag);
      }
      if (canBeLeftOpenTag$$1(tagName) && lastTag === tagName) {
        parseEndTag(tagName);
      }
    }
    var unary = isUnaryTag$$1(tagName) || tagName === 'html' && lastTag === 'head' || !!unarySlash;
    var l = match.attrs.length;
    //新建一個(gè)attrs屬性,遍歷match.attrs,使得attrs=[{name:'id',value:'app'}]這種map結(jié)構(gòu)
    var attrs = new Array(l);
    for (var i = 0; i < l; i++) {
      var args = match.attrs[i];
      // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778
      if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) {
        if (args[3] === '') { delete args[3]; }
        if (args[4] === '') { delete args[4]; }
        if (args[5] === '') { delete args[5]; }
      }
      var value = args[3] || args[4] || args[5] || '';
      attrs[i] = {
        name: args[1],
        value: decodeAttr(
          value,
          options.shouldDecodeNewlines
        )
      };
    }
    if (!unary) {
      stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs });
      lastTag = tagName;
    }
    //這個(gè)是最重要的函數(shù),對(duì)一些特殊的屬性做特殊處理,例如指令屬性v-text='message'
    if (options.start) {
      options.start(tagName, attrs, unary, match.start, match.end);//tips:返回到parse函數(shù)中看start執(zhí)行過(guò)程
    }
  }
  function parseEndTag (tagName, start, end) {
    var pos, lowerCasedTagName;
    if (start == null) { start = index; }
    if (end == null) { end = index; }
    if (tagName) {
      lowerCasedTagName = tagName.toLowerCase();
    }
    // Find the closest opened tag of the same type
    if (tagName) {
      for (pos = stack.length - 1; pos >= 0; pos--) {
        if (stack[pos].lowerCasedTag === lowerCasedTagName) {
          break
        }
      }
    } else {
      // If no tag name is provided, clean shop
      pos = 0;
    }
    if (pos >= 0) {
      // Close all the open elements, up the stack
      for (var i = stack.length - 1; i >= pos; i--) {
        if ("development" !== 'production' &&
            (i > pos || !tagName) &&
            options.warn) {
          options.warn(
            ("tag <" + (stack[i].tag) + "> has no matching end tag.")
          );
        }
        if (options.end) {
          options.end(stack[i].tag, start, end);
        }
      }
      // Remove the open elements from the stack
      stack.length = pos;
      lastTag = pos && stack[pos - 1].tag;
    } else if (lowerCasedTagName === 'br') {
      if (options.start) {
        options.start(tagName, [], true, start, end);
      }
    } else if (lowerCasedTagName === 'p') {
      if (options.start) {
        options.start(tagName, [], false, start, end);
      }
      if (options.end) {
        options.end(tagName, start, end);
      }
    }
  }
}

以上這個(gè)過(guò)程只是一個(gè)解析過(guò)程,將相應(yīng)的屬性放到相應(yīng)的位置,但是還沒(méi)有產(chǎn)生可執(zhí)行代碼,以下
generate函數(shù)的作用就是根據(jù)這些屬性來(lái)產(chǎn)生相應(yīng)的代碼。

3 分析 generate(ast, options)

    // 溫故下,返回結(jié)果ast是含有如下屬性的對(duì)象
    // ```javascript
    // attrs:Array     //保存原始的html特性
    // attrsList:Array
    // attrsMap:Object
    // children:Array
    // parent:undefined
    // plain:false
    // static:false
    // staticRoot:false
    // tag:"div"
    // type:1
    // __proto__:Object
function generate (ast,options) {
  // save previous staticRenderFns so generate calls can be nested
  var prevStaticRenderFns = staticRenderFns;
  var currentStaticRenderFns = staticRenderFns = [];
  var prevOnceCount = onceCount;
  onceCount = 0;
  currentOptions = options;
  warn$3 = options.warn || baseWarn;
  transforms$1 = pluckModuleFunction(options.modules, 'transformCode');
  dataGenFns = pluckModuleFunction(options.modules, 'genData');
  platformDirectives$1 = options.directives || {};
  isPlatformReservedTag$1 = options.isReservedTag || no;
  var code = ast ? genElement(ast) : '_c("div")';  //主要函數(shù),執(zhí)行g(shù)enElement(ast)
  staticRenderFns = prevStaticRenderFns;
  onceCount = prevOnceCount;
  return {
    render: ("with(this){return " + code + "}"),
    staticRenderFns: currentStaticRenderFns
  }
}

來(lái)看看 genElement(ast)

    //根據(jù)ast的屬性是否有once,for,if,slot,component等屬性執(zhí)行不同函數(shù),否則當(dāng)普通元素處理并執(zhí)行g(shù)enData和genChildren函數(shù),這兩個(gè)函數(shù)都是在做字符串的拼裝工作,最后返回拼裝完成的code字符串
    function genElement (el) {
      if (el.staticRoot && !el.staticProcessed) {  //靜態(tài)節(jié)點(diǎn)
        return genStatic(el)
      } else if (el.once && !el.onceProcessed) {    //v-once節(jié)點(diǎn)
        return genOnce(el)
      } else if (el.for && !el.forProcessed) {      //v-for節(jié)點(diǎn)
        return genFor(el)
      } else if (el.if && !el.ifProcessed) {        //v-if節(jié)點(diǎn)
        return genIf(el)
      } else if (el.tag === 'template' && !el.slotTarget) {
        return genChildren(el) || 'void 0'
      } else if (el.tag === 'slot') {               //處理slot
        return genSlot(el)
      } else {
        // component or element
        var code;
        if (el.component) {                          //處理組件節(jié)點(diǎn)
          code = genComponent(el.component, el);
        } else {
          var data = el.plain ? undefined : genData(el);   //處理元素節(jié)點(diǎn)的data
          var children = el.inlineTemplate ? null : genChildren(el, true);//處理元素節(jié)點(diǎn)的子元素
          code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")";
        }
        // module transforms
        for (var i = 0; i < transforms$1.length; i++) {
          code = transforms$1[i](el, code);
        }
        return code
      }
    }

來(lái)看看genData(el)

    //整個(gè)函數(shù)都是在做data字符串的拼裝工作,最后返回data
    function genData (el) {
      var data = '{';
      // 首先處理指令,因?yàn)樵趀l產(chǎn)生之前,指令可能會(huì)改變el的其他屬性
      var dirs = genDirectives(el);
      if (dirs) { data += dirs + ','; }
      // 處理key屬性
      if (el.key) {
        data += "key:" + (el.key) + ",";
      }
      // 處理ref屬性
      if (el.ref) {
        data += "ref:" + (el.ref) + ",";
      }
      if (el.refInFor) {
        data += "refInFor:true,";
      }
      // 處理v-pre指令
      if (el.pre) {
        data += "pre:true,";
      }
      // record original tag name for components using "is" attribute
      if (el.component) {      //處理組件
        data += "tag:\"" + (el.tag) + "\",";
      }
      // 處理class和style屬性
      for (var i = 0; i < dataGenFns.length; i++) {
        data += dataGenFns[i](el);
      }
      //處理特性 attributes
      if (el.attrs) {
        data += "attrs:{" + (genProps(el.attrs)) + "},";
      }
      //處理屬性 DOM property
      if (el.props) {
        data += "domProps:{" + (genProps(el.props)) + "},";
      }
      //處理事件
      if (el.events) {
        data += (genHandlers(el.events)) + ",";
      }
      //處理本地事件
      if (el.nativeEvents) {
        data += (genHandlers(el.nativeEvents, true)) + ",";
      }
      處理slot目標(biāo)
      if (el.slotTarget) {
        data += "slot:" + (el.slotTarget) + ",";
      }
      //處理scoped的slot
      if (el.scopedSlots) {
        data += (genScopedSlots(el.scopedSlots)) + ",";
      }
      //處理v-model
      if (el.model) {
        data += "model:{value:" + (el.model.value) + ",callback:" + (el.model.callback) + ",expression:" + (el.model.expression) + "},";
      }
      // 處理內(nèi)聯(lián)模板
      if (el.inlineTemplate) {
        var inlineTemplate = genInlineTemplate(el);
        if (inlineTemplate) {
          data += inlineTemplate + ",";
        }
      }
      data = data.replace(/,$/, '') + '}';
      // v-bind data wrap 處理v-bind
      if (el.wrapData) {
        data = el.wrapData(data);
      }
      return data
    }

來(lái)看看genDirectives(el)

    function genDirectives (el) {
      var dirs = el.directives;
      if (!dirs) { return }
      var res = 'directives:[';
      var hasRuntime = false;
      var i, l, dir, needRuntime;
      for (i = 0, l = dirs.length; i < l; i++) {
        dir = dirs[i];
        needRuntime = true;
        var gen = platformDirectives$1[dir.name] || baseDirectives[dir.name];
        if (gen) {
          // compile-time directive that manipulates AST.
          // returns true if it also needs a runtime counterpart.
          needRuntime = !!gen(el, dir, warn$3);
        }
        if (needRuntime) {
          hasRuntime = true;
          res += "{name:\"" + (dir.name) + "\",rawName:\"" + (dir.rawName) + "\"" + (dir.value ? (",value:(" + (dir.value) + "),expression:" + (JSON.stringify(dir.value))) : '') + (dir.arg ? (",arg:\"" + (dir.arg) + "\"") : '') + (dir.modifiers ? (",modifiers:" + (JSON.stringify(dir.modifiers))) : '') + "},";
        }
      }
      if (hasRuntime) {
        return res.slice(0, -1) + ']'
      }
    }

來(lái)看看genChildren

function genChildren (el, checkSkip) {
  var children = el.children;
  if (children.length) {
    var el$1 = children[0];
    // optimize single v-for
    if (children.length === 1 &&
        el$1.for &&
        el$1.tag !== 'template' &&
        el$1.tag !== 'slot') {
      return genElement(el$1)  //只有一個(gè)子元素并且有v-for屬性時(shí),遞歸調(diào)用genElement
    }
    var normalizationType = checkSkip ? getNormalizationType(children) : 0;
    return ("[" + (children.map(genNode).join(',')) + "]" + (normalizationType ? ("," + normalizationType) : ''))
  }
}

來(lái)看看genNode

function genNode (node) {
  if (node.type === 1) {  //type=1,表示是元素節(jié)點(diǎn),遞歸調(diào)用genElement
    return genElement(node)
  } else {
    return genText(node)  //按照vue的用法,不是元素節(jié)點(diǎn)就只能是文本節(jié)點(diǎn)了
  }
}
/*
function genText (text) {
  return ("_v(" + (text.type === 2
    ? text.expression // no need for () because already wrapped in _s()
    : transformSpecialNewlines(JSON.stringify(text.text))) + ")")
}
 */

4 小結(jié)

  • compile過(guò)程即baseCompile過(guò)程:
    1. 調(diào)用parse函數(shù)對(duì)原始模板進(jìn)行解析得到ast;
    2. 調(diào)用generate函數(shù)處理ast得到最終render函數(shù)
  • 本篇偏向?qū)幾g的整體過(guò)程分析,沒(méi)有對(duì)諸如指令到底是怎么編譯的進(jìn)行分析,后面章節(jié)將結(jié)合實(shí)例具體分析指令等編譯過(guò)程,讓我們先瞅瞅vue一共提供了哪些內(nèi)置指令:
    v-text
    v-html
    v-show
    v-if
    v-else
    v-else-if
    v-for
    v-on
    v-bind
    v-model
    v-pre
    v-cloak
    v-once

除此之外,還有三個(gè)特殊屬性key,ref,slot以及內(nèi)置組件component,transition,transition-group,keep-alive,slot,接下來(lái)的章節(jié)將對(duì)這些內(nèi)容進(jìn)行分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評(píng)論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,740評(píng)論 3 420
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 176,856評(píng)論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 63,175評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,931評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 55,321評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評(píng)論 3 443
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 42,533評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,082評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,891評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,067評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,319評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 34,732評(píng)論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 35,987評(píng)論 1 289
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,794評(píng)論 3 394
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,076評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容

  • vue2.0和1.0模板渲染的區(qū)別 Vue 2.0 中模板渲染與 Vue 1.0 完全不同,1.0 中采用的 Do...
    我是上帝可愛(ài)多閱讀 1,310評(píng)論 0 4
  • 本文基于vue-2.4.4源碼進(jìn)行分析 模板編譯是Vue 2.0中很重要的一個(gè)環(huán)節(jié),它將template編譯成re...
    fehysunny閱讀 1,036評(píng)論 0 0
  • 深夜一點(diǎn)四十五分,我躺在床上,耳朵里塞著耳機(jī),聽(tīng)著梁靜茹的《會(huì)過(guò)去的》,突然想簡(jiǎn)單的用文字記錄下這個(gè)時(shí)刻,記載著此...
    dandelio鯨閱讀 98評(píng)論 0 0
  • 參加《做自己的CEO》的時(shí)間比較晚,導(dǎo)致前期的很多事情都沒(méi)有做,當(dāng)然也包括時(shí)間開(kāi)銷記錄。不知道前期的熱身活動(dòng)有沒(méi)有...
    汪雋子閱讀 323評(píng)論 0 2