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

前言

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

調(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
  }

來看看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)樹
    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)過上述處理后,由于可能有v-if這種會(huì)改變樹結(jié)構(gòu)的指令,所以需要對(duì)結(jié)構(gòu)樹
      // 進(jìn)一步處理,至此第一輪while循環(huán)解析完成,接下來就是重復(fù)這個(gè)過程了
      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
}

來看看parseHTML(template,options)

/*解析過程中最重要的函數(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) {       //通過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;
      //開始尋找屬性
      //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é)束,接下來執(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í)行過程
    }
  }
  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è)過程只是一個(gè)解析過程,將相應(yīng)的屬性放到相應(yīng)的位置,但是還沒有產(chǎn)生可執(zhí)行代碼,以下
generate函數(shù)的作用就是根據(jù)這些屬性來產(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
  }
}

來看看 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
      }
    }

來看看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
    }

來看看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) + ']'
      }
    }

來看看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) : ''))
  }
}

來看看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過程即baseCompile過程:
    1. 調(diào)用parse函數(shù)對(duì)原始模板進(jìn)行解析得到ast;
    2. 調(diào)用generate函數(shù)處理ast得到最終render函數(shù)
  • 本篇偏向?qū)幾g的整體過程分析,沒有對(duì)諸如指令到底是怎么編譯的進(jìn)行分析,后面章節(jié)將結(jié)合實(shí)例具體分析指令等編譯過程,讓我們先瞅瞅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,接下來的章節(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ù)。

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

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