美文网首页Vue技术探究Vue.js专区
Vue源码分析(6)--编译过程分析(1)

Vue源码分析(6)--编译过程分析(1)

作者: 风之化身呀 | 来源:发表于2017-07-08 16:44 被阅读105次

    前言

    本文是vue2.x源码分析的第六篇,主要讲解编译compile过程!

    调用方式

    var compiled = compile(template, options);
    

    1 分析compile

    //tips:请结合断点调试,该函数位于闭包createCompiler中,有的变量是在上层函数中定义的
    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
        }
        这个__proto__指向一个预先定义好的baseOptions对象,该对象长这样:
            var baseOptions = {
              expectHTML: true,
              modules: modules$1,//modules$1=[klass$1,style$1]
              directives: directives$1, //这里预先定义了html,text,model三个指令
              isPreTag: isPreTag,
              isUnaryTag: isUnaryTag,
              mustUseProp: mustUseProp,
              canBeLeftOpenTag: canBeLeftOpenTag,
              isReservedTag: isReservedTag,
              getTagNamespace: getTagNamespace,
              staticKeys: genStaticKeys(modules$1)
            };
        */
        var compiled = baseCompile(template, finalOptions); //主要函数
        {
          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); //主要函数1
      optimize(ast, options);
      var code = generate(ast, options);         //主要函数2
      return {
        ast: ast,
        render: code.render,
        staticRenderFns: code.staticRenderFns
      }
    }
    

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

    //主要是调用parseHTML(html, options)解析html,返回结果ast是含有如下属性的对象
        // 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;
      //这个pluckModuleFunction函数作用就是从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;  //作为结果返回
      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第二个参数里有很重要的三个函数:start,end,chars
      parseHTML(template, {
        warn: warn$2,
        expectHTML: options.expectHTML,
        isUnaryTag: options.isUnaryTag,
        canBeLeftOpenTag: options.canBeLeftOpenTag,
        shouldDecodeNewlines: options.shouldDecodeNewlines,
        //start和end函数负责构建节点树
        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 = { //节点
            type: 1,
            tag: tag,
            attrsList: attrs,
            attrsMap: makeAttrsMap(attrs),
            parent: currentParent,
            children: []
          };
          if (ns) {
            element.ns = ns;
          }
          //不处理style和script标签
          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.'
            );
          }
          // 猜测:html如果用了其他的模板,如ejs等需要先转换
          // 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指令,则直接调用processRawAttrs(element);处理原始属性
          if (inVPre) {
            processRawAttrs(element);
          } else {
            processFor(element);//处理v-for指令,会将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
            // 移除结构性属性后判断该元素是不是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); //对class和style属性进行处理
            }
            //以上处理了v-for,v-if,v-once,v-pre等指令,但还有其它指令,如v-on,v-bind,
            //以及它们的快捷写法'@:',':',该函数就是处理这些指令以及普通元素,处理的结果就是
            //在element上加了一个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.'
                );
              }
            }
          }
          // 经过上述处理后,由于可能有v-if这种会改变树结构的指令,所以需要对结构树
          // 进一步处理,至此第一轮while循环解析完成,接下来就是重复这个过程了
          if (!root) {
            root = element;
            checkRootConstraints(root); //根节点不能是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)

    /*解析过程中最重要的函数,因此代码量较大*/
    function parseHTML (html, options) {  //将template传给html
      var stack = [];
      var expectHTML = options.expectHTML;
      var isUnaryTag$$1 = options.isUnaryTag || no; //是否是一元标签
      var canBeLeftOpenTag$$1 = options.canBeLeftOpenTag || no;
      var index = 0;
      var last, lastTag;
      while (html) {       //通过while循环一步步处理html,每处理一步就缩短html,直至html为空
        last = html;
        // 不处理script/style/textarea元素
        if (!lastTag || !isPlainTextElement(lastTag)) {
          var textEnd = html.indexOf('<');
          if (textEnd === 0) {
            // 当匹配到Comment,只对html推进,不做其他处理
            if (comment.test(html)) {
              var commentEnd = html.indexOf('-->');
              if (commentEnd >= 0) {
                advance(commentEnd + 3);
                continue
              }
            }
            //当匹配到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
              }
            }
            //当匹配到doctype,同Comment一样处理,/^<!DOCTYPE [^>]+>/i
            var doctypeMatch = html.match(doctype);
            if (doctypeMatch) {
              advance(doctypeMatch[0].length);
              continue
            }
            // 当匹配到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
            }
            // 除以上四种,就默认以下处理
            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结束,接下来执行handleStartTag(match);match此时长这样
            /*
            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;
        //新建一个attrs属性,遍历match.attrs,使得attrs=[{name:'id',value:'app'}]这种map结构
        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;
        }
        //这个是最重要的函数,对一些特殊的属性做特殊处理,例如指令属性v-text='message'
        if (options.start) {
          options.start(tagName, attrs, unary, match.start, match.end);//tips:返回到parse函数中看start执行过程
        }
      }
      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);
          }
        }
      }
    }
    

    以上这个过程只是一个解析过程,将相应的属性放到相应的位置,但是还没有产生可执行代码,以下
    generate函数的作用就是根据这些属性来产生相应的代码。

    3 分析 generate(ast, options)

        // 温故下,返回结果ast是含有如下属性的对象
        // ```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")';  //主要函数,执行genElement(ast)
      staticRenderFns = prevStaticRenderFns;
      onceCount = prevOnceCount;
      return {
        render: ("with(this){return " + code + "}"),
        staticRenderFns: currentStaticRenderFns
      }
    }
    

    来看看 genElement(ast)

        //根据ast的属性是否有once,for,if,slot,component等属性执行不同函数,否则当普通元素处理并执行genData和genChildren函数,这两个函数都是在做字符串的拼装工作,最后返回拼装完成的code字符串
        function genElement (el) {
          if (el.staticRoot && !el.staticProcessed) {  //静态节点
            return genStatic(el)
          } else if (el.once && !el.onceProcessed) {    //v-once节点
            return genOnce(el)
          } else if (el.for && !el.forProcessed) {      //v-for节点
            return genFor(el)
          } else if (el.if && !el.ifProcessed) {        //v-if节点
            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) {                          //处理组件节点
              code = genComponent(el.component, el);
            } else {
              var data = el.plain ? undefined : genData(el);   //处理元素节点的data
              var children = el.inlineTemplate ? null : genChildren(el, true);//处理元素节点的子元素
              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)

        //整个函数都是在做data字符串的拼装工作,最后返回data
        function genData (el) {
          var data = '{';
          // 首先处理指令,因为在el产生之前,指令可能会改变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目标
          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) + "},";
          }
          // 处理内联模板
          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)  //只有一个子元素并且有v-for属性时,递归调用genElement
        }
        var normalizationType = checkSkip ? getNormalizationType(children) : 0;
        return ("[" + (children.map(genNode).join(',')) + "]" + (normalizationType ? ("," + normalizationType) : ''))
      }
    }
    

    来看看genNode

    function genNode (node) {
      if (node.type === 1) {  //type=1,表示是元素节点,递归调用genElement
        return genElement(node)
      } else {
        return genText(node)  //按照vue的用法,不是元素节点就只能是文本节点了
      }
    }
    /*
    function genText (text) {
      return ("_v(" + (text.type === 2
        ? text.expression // no need for () because already wrapped in _s()
        : transformSpecialNewlines(JSON.stringify(text.text))) + ")")
    }
     */
    

    4 小结

    • compile过程即baseCompile过程:
      1. 调用parse函数对原始模板进行解析得到ast;
      2. 调用generate函数处理ast得到最终render函数
    • 本篇偏向对编译的整体过程分析,没有对诸如指令到底是怎么编译的进行分析,后面章节将结合实例具体分析指令等编译过程,让我们先瞅瞅vue一共提供了哪些内置指令:
        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
    

    除此之外,还有三个特殊属性key,ref,slot以及内置组件component,transition,transition-group,keep-alive,slot,接下来的章节将对这些内容进行分析

    相关文章

      网友评论

        本文标题:Vue源码分析(6)--编译过程分析(1)

        本文链接:https://www.haomeiwen.com/subject/xzcjhxtx.html