Exemplo n.º 1
0
export function parse (
  template: string,
  options: CompilerOptions
): ASTElement | void {
  /*警告函数,baseWarn是Vue 编译器默认警告*/
  warn = options.warn || baseWarn 
  platformGetTagNamespace = options.platformGetTagNamespace || no
  platformMustUseProp = options.mustUseProp || no
  /*检测是否是<pre>标签*/
  platformIsPreTag = options.isPreTag || no
  preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
  transforms = pluckModuleFunction(options.modules, 'transformNode')
  postTransforms = pluckModuleFunction(options.modules, 'postTransformNode')
  delimiters = options.delimiters

  /*存放ele*/
  const stack = []
  const preserveWhitespace = options.preserveWhitespace !== false
  let root
  let currentParent
  /*标志位,是否有v-pre属性*/
  let inVPre = false
  /*标志位,是否是pre标签*/
  let inPre = false
  let warned = false

  /*只发出一次的warning*/
  function warnOnce (msg) {
    if (!warned) {
      warned = true
      warn(msg)
    }
  }

  function endPre (element) {
    // check pre state
    /*是否有v-pre属性,存在则标志位变为false,因为这里已经是结束end,存在v-pre时在start中会被标志为true*/
    if (element.pre) {
      inVPre = false
    }
    /*检测是否是<pre>标签*/
    if (platformIsPreTag(element.tag)) {
      inPre = false
    }
  }

  /*解析HTML*/
  parseHTML(template, {
    warn,
    expectHTML: options.expectHTML,
    isUnaryTag: options.isUnaryTag,
    canBeLeftOpenTag: options.canBeLeftOpenTag,
    shouldDecodeNewlines: options.shouldDecodeNewlines,
    start (tag, attrs, unary) {
      // check namespace.
      // inherit parent ns if there is one
      const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag)

      // handle IE svg bug
      /* istanbul ignore if */
      /*处理IE的svg bug*/
      if (isIE && ns === 'svg') {
        attrs = guardIESVGBug(attrs)
      }

      const element: ASTElement = {
        type: 1,
        tag,
        attrsList: attrs,
        attrsMap: makeAttrsMap(attrs),
        parent: currentParent,
        children: []
      }
      if (ns) {
        element.ns = ns
      }

      /*如果是被禁止的标签或者是服务端渲染*/
      if (isForbiddenTag(element) && !isServerRendering()) {
        element.forbidden = true
        process.env.NODE_ENV !== 'production' && warn(
          '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.'
        )
      }

      // apply pre-transforms
      for (let i = 0; i < preTransforms.length; i++) {
        preTransforms[i](element, options)
      }

      if (!inVPre) {
        /*
          处理v-pre属性
          v-pre元素及其子元素被跳过编译
          https://cn.vuejs.org/v2/api/#v-pre
        */
        processPre(element)
        if (element.pre) {
          inVPre = true
        }
      }
      /*检测是否是<pre>标签*/
      if (platformIsPreTag(element.tag)) {
        inPre = true
      }
      /*如果有v-pre属性,元素及其子元素不会被编译*/
      if (inVPre) {
        processRawAttrs(element)
      } else {
        /*匹配v-for属性*/
        processFor(element)
        /*匹配if属性,分别处理v-if、v-else以及v-else-if属性*/
        processIf(element)
        /*处理v-once属性,https://cn.vuejs.org/v2/api/#v-once*/
        processOnce(element)
        /*处理key属性 https://cn.vuejs.org/v2/api/#key*/
        processKey(element)

        // determine whether this is a plain element after
        // removing structural attributes
        /*去掉属性后,确定这是一个普通元素。*/
        element.plain = !element.key && !attrs.length

        /*处理ref属性 https://cn.vuejs.org/v2/api/#ref*/
        processRef(element)
        /*处理slot属性 https://cn.vuejs.org/v2/api/#slot*/
        processSlot(element)
        /*处理组件*/
        processComponent(element)
        /*转换*/
        for (let i = 0; i < transforms.length; i++) {
          transforms[i](element, options)
        }
        /*处理属性*/
        processAttrs(element)
      }

      /*监测根级元素的约束*/
      function checkRootConstraints (el) {
        if (process.env.NODE_ENV !== 'production') {
          /*slot以及templete不能作为根级元素*/
          if (el.tag === 'slot' || el.tag === 'template') {
            warnOnce(
              `Cannot use <${el.tag}> as component root element because it may ` +
              'contain multiple nodes.'
            )
          }
          /*以及根级元素不能有v-for*/
          if (el.attrsMap.hasOwnProperty('v-for')) {
            warnOnce(
              'Cannot use v-for on stateful component root element because ' +
              'it renders multiple elements.'
            )
          }
        }
      }

      // tree management
      if (!root) {
        root = element
        /*检测根级元素的约束*/
        checkRootConstraints(root)
      } else if (!stack.length) {
        // allow root elements with v-if, v-else-if and v-else
        /*
          根级元素是可以用v-if、v-else来写多个条件下的多个根级元素的
          比如说
          <template>
            <div v-if="fff">aaa</div>
            <div v-else>bbb</div>
          </template>
          是完全允许的
        */
        if (root.if && (element.elseif || element.else)) {
          /*监测根级元素的约束*/
          checkRootConstraints(element)
          /*在el的ifConditions属性中加入condition*/
          addIfCondition(root, {
            exp: element.elseif,
            block: element
          })
        } else if (process.env.NODE_ENV !== 'production') {
          /*在根级元素包含多个ele的时候,有不含v-else的ele则报出打印*/
          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.`
          )
        }
      }
      /*forbidden标志是否是被禁止的标签(style标签或者script标签)*/
      if (currentParent && !element.forbidden) {
        if (element.elseif || element.else) {
          /*当遇到当前ele有v-else或者v-elseif属性的时候,需要处理if属性,在其上级兄弟元素中必然存在一个v-if属性*/
          processIfConditions(element, currentParent)
        } else if (element.slotScope) { // scoped slot
          currentParent.plain = false
          /*slot如果没有则是默认的default*/
          const name = element.slotTarget || '"default"'
          /*
              scopedSlots中存放slot元素 https://cn.vuejs.org/v2/api/#vm-scopedSlots
          */
          ;(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 (let i = 0; i < postTransforms.length; i++) {
        postTransforms[i](element, options)
      }
    },

    end () {
      // remove trailing whitespace
      /*从stack中取出最后一个ele*/
      const element = stack[stack.length - 1]
      /*获取该ele的最后一个子节点*/
      const lastNode = element.children[element.children.length - 1]
      /*该子节点是非<pre>标签的文本*/
      if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) {
        element.children.pop()
      }
      // pop stack
      /*ele出栈*/
      stack.length -= 1
      currentParent = stack[stack.length - 1]
      endPre(element)
    },

    chars (text: string) {
      if (!currentParent) {
        if (process.env.NODE_ENV !== 'production') {
          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
      }
      const children = currentParent.children
      text = inPre || text.trim()
        ? isTextTag(currentParent) ? text : decodeHTMLCached(text)
        // only preserve whitespace if its not right after a starting tag
        : preserveWhitespace && children.length ? ' ' : ''
      if (text) {
        let expression
        if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) {
          children.push({
            type: 2,
            expression,
            text
          })
        } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') {
          children.push({
            type: 3,
            text
          })
        }
      }
    }
  })
  return root
}
Exemplo n.º 2
0
export function parse (
  template: string,
  options: CompilerOptions
): ASTElement | void {
  warn = options.warn || baseWarn

  platformIsPreTag = options.isPreTag || no
  platformMustUseProp = options.mustUseProp || no
  platformGetTagNamespace = options.getTagNamespace || no

  transforms = pluckModuleFunction(options.modules, 'transformNode')
  preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
  postTransforms = pluckModuleFunction(options.modules, 'postTransformNode')

  delimiters = options.delimiters

  const stack = []
  const preserveWhitespace = options.preserveWhitespace !== false
  let root
  let currentParent
  let inVPre = false
  let inPre = false
  let warned = false

  function warnOnce (msg) {
    if (!warned) {
      warned = true
      warn(msg)
    }
  }

  function endPre (element) {
    // check pre state
    if (element.pre) {
      inVPre = false
    }
    if (platformIsPreTag(element.tag)) {
      inPre = false
    }
  }

  parseHTML(template, {
    warn,
    expectHTML: options.expectHTML,
    isUnaryTag: options.isUnaryTag,
    canBeLeftOpenTag: options.canBeLeftOpenTag,
    shouldDecodeNewlines: options.shouldDecodeNewlines,
    shouldKeepComment: options.comments,
    start (tag, attrs, unary) {
      // check namespace.
      // inherit parent ns if there is one
      const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag)

      // handle IE svg bug
      /* istanbul ignore if */
      if (isIE && ns === 'svg') {
        attrs = guardIESVGBug(attrs)
      }

      const element: ASTElement = {
        type: 1,
        tag,
        attrsList: attrs,
        attrsMap: makeAttrsMap(attrs),
        parent: currentParent,
        children: []
      }
      if (ns) {
        element.ns = ns
      }

      if (isForbiddenTag(element) && !isServerRendering()) {
        element.forbidden = true
        process.env.NODE_ENV !== 'production' && warn(
          '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.'
        )
      }

      // apply pre-transforms
      for (let i = 0; i < preTransforms.length; i++) {
        preTransforms[i](element, options)
      }

      if (!inVPre) {
        processPre(element)
        if (element.pre) {
          inVPre = true
        }
      }
      if (platformIsPreTag(element.tag)) {
        inPre = true
      }
      if (inVPre) {
        processRawAttrs(element)
      } else {
        processFor(element)
        processIf(element)
        processOnce(element)
        processKey(element)

        // determine whether this is a plain element after
        // removing structural attributes
        element.plain = !element.key && !attrs.length

        processRef(element)
        processSlot(element)
        processComponent(element)
        for (let i = 0; i < transforms.length; i++) {
          transforms[i](element, options)
        }
        processAttrs(element)
      }

      function checkRootConstraints (el) {
        if (process.env.NODE_ENV !== 'production') {
          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.'
            )
          }
        }
      }

      // tree management
      if (!root) {
        root = element
        checkRootConstraints(root)
      } else if (!stack.length) {
        // allow root elements with 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 if (process.env.NODE_ENV !== 'production') {
          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
          const 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 (let i = 0; i < postTransforms.length; i++) {
        postTransforms[i](element, options)
      }
    },

    end () {
      // remove trailing whitespace
      const element = stack[stack.length - 1]
      const 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 (text: string) {
      if (!currentParent) {
        if (process.env.NODE_ENV !== 'production') {
          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
      }
      const children = currentParent.children
      text = inPre || text.trim()
        ? isTextTag(currentParent) ? text : decodeHTMLCached(text)
        // only preserve whitespace if its not right after a starting tag
        : preserveWhitespace && children.length ? ' ' : ''
      if (text) {
        let expression
        if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) {
          children.push({
            type: 2,
            expression,
            text
          })
        } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') {
          children.push({
            type: 3,
            text
          })
        }
      }
    },
    comment (text: string) {
      currentParent.children.push({
        type: 3,
        text,
        isComment: true
      })
    }
  })
  return root
}
Exemplo n.º 3
0
export function parse (
  template: string,
  options: CompilerOptions
): ASTElement | void {
  // 对options的处理
  warn = options.warn || baseWarn

  //下面的都是平台相关的
  platformIsPreTag = options.isPreTag || no // 是否pre标签
  platformMustUseProp = options.mustUseProp || no // 针对某些标签的某个属性的绑定需要使用prop进行绑定(weex平台为fasle)
  platformGetTagNamespace = options.getTagNamespace || no // 对svg和math标签的获取

  // 对选项的模块进行处理(这三个包含平台相关的什么??)
  transforms = pluckModuleFunction(options.modules, 'transformNode')
  preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
  postTransforms = pluckModuleFunction(options.modules, 'postTransformNode')

  delimiters = options.delimiters // 分隔符

  const stack = []
  const preserveWhitespace = options.preserveWhitespace !== false // 是否保留空白字符
  let root
  let currentParent
  let inVPre = false
  let inPre = false
  let warned = false

  // 错误处理的
  function warnOnce (msg) {
    if (!warned) {
      warned = true
      warn(msg)
    }
  }
  // 标签闭合处理的函数
  function closeElement (element) {
    // check pre state // 两个都是检查preTag
    if (element.pre) {
      inVPre = false
    }
    if (platformIsPreTag(element.tag)) {
      inPre = false
    }
    // apply post-transforms
    for (let i = 0; i < postTransforms.length; i++) {
      postTransforms[i](element, options) // 调用平台相关的model处理函数对选项模块进行处理
    }
  }


  // 解析template模板真正的函数
  parseHTML(template, {
    warn, // 错误处理
    expectHTML: options.expectHTML, // 预期的HTML
    isUnaryTag: options.isUnaryTag, // 是否是一元标签
    canBeLeftOpenTag: options.canBeLeftOpenTag, // 能够是左开标签
    shouldDecodeNewlines: options.shouldDecodeNewlines,// 解决ie兼容
    shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref, // 解决chrome  a[href]
    shouldKeepComment: options.comments, // 是否保留注释节点
    start (tag, attrs, unary) { // 匹配标签开始处理的函数
      // check namespace. 检查命名空间
      // inherit parent ns if there is one 继承父级的命名空间
      const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag)

      // handle IE svg bug
      /* istanbul ignore if */
      if (isIE && ns === 'svg') {
        attrs = guardIESVGBug(attrs) // 处理ie SVG的 bug
      }

      // 创建一个一开始的astElement
      let element: ASTElement = createASTElement(tag, attrs, currentParent)
      if (ns) {
        element.ns = ns //设置命名空间
      }

      // 如果是禁止标签(script、style)而且不是服务端渲染,警告和添加forbidden属性
      if (isForbiddenTag(element) && !isServerRendering()) {
        element.forbidden = true
        process.env.NODE_ENV !== 'production' && warn(
          '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.'
        )
      }

      // apply pre-transforms // 同样调用平台相关的model
      for (let i = 0; i < preTransforms.length; i++) {
        element = preTransforms[i](element, options) || element
      }

      // 对pre指令或者标签的处理
      if (!inVPre) {
        processPre(element)
        if (element.pre) {
          inVPre = true
        }
      }
      if (platformIsPreTag(element.tag)) {
        inPre = true
      }
      if (inVPre) { // pre属性为true的时候直接从attrsList生成attrs
        processRawAttrs(element)
        // processed(该element是否已经进行过加工)
      } else if (!element.processed) {
        // structural directives 结构指令的处理(v-for、v-if、v-once)
        processFor(element)
        processIf(element)
        processOnce(element)
        // element-scope stuff 最后再处理元素
        processElement(element, options)
      }

      function checkRootConstraints (el) { // 检查root容器
        if (process.env.NODE_ENV !== 'production') {
          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.'
            )
          }
        }
      }

      // tree management (树管理)
      if (!root) {
        root = element // 第一次,设置根节点
        checkRootConstraints(root)
      } else if (!stack.length) { // 为什么要栈当中无元素(如果是这样应该是根的第一个children)
        // allow root elements with v-if, v-else-if and v-else
        // 允许根元素带有v-if、v-else-if和v-else
        if (root.if && (element.elseif || element.else)) {
          checkRootConstraints(element)
          // 看到这里就懂了,就是有2个或以上的根
          addIfCondition(root, {
            exp: element.elseif,
            block: element
          })
          // 如果不是有if判断的情况那么就是多个根,报错
        } else if (process.env.NODE_ENV !== 'production') {
          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) { // elseif、else处理
          processIfConditions(element, currentParent)
        } else if (element.slotScope) { // scoped slot 作用域槽
          currentParent.plain = false
          const name = element.slotTarget || '"default"'
          ;(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element
        } else {
          // 父子树的建立(AST)
          currentParent.children.push(element)
          element.parent = currentParent
        }
      }
      if (!unary) { // 非一元标签
        currentParent = element
        stack.push(element) // 形成栈用于后续 end的处理(对元素进行出栈、校验、形成DOM树)
      } else {
        closeElement(element) // 一元标签直接闭合(img、br等)
      }
    },

    end () { // 匹配标签结束处理的函数
      // remove trailing whitespace 删除尾随空格
      const element = stack[stack.length - 1]
      const lastNode = element.children[element.children.length - 1]
      if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) {
        element.children.pop()
      }
      // pop stack //对匹配元素进行出栈,并且closeElement闭合元素
      stack.length -= 1 // 好方法,比pop快
      currentParent = stack[stack.length - 1]
      closeElement(element)
    },

    chars (text: string) { // 处理标签中的文本内容
      if (!currentParent) { // 如果匹配到text文本没有父级报警告,并返回不编译
        if (process.env.NODE_ENV !== 'production') {
          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 // 针对ie的textare [placeholder] 的bug进行处理
      }
      /** 真正的对text进行处理 */
      // 因为text文本属于Children级(父级的下一级),所以要对Children进行操作
      const children = currentParent.children 
      text = inPre || text.trim() // 对text文本的处理(这个先处理的有点陌生)
        ? isTextTag(currentParent) ? text : decodeHTMLCached(text)
        // only preserve whitespace if its not right after a starting tag
        // 仅在开始标记后保留空白(下面空白字符的保留)
        : preserveWhitespace && children.length ? ' ' : ''
      if (text) { // 经过第一次解析还有text
        let res
        // 解析含有变量的text文本如:({{ 变量 }})
        if (!inVPre && text !== ' ' && (res = parseText(text, delimiters))) {
          children.push({
            type: 2,
            expression: res.expression, // 解析出的表达式
            tokens: res.tokens, // 解析出的tokens
            text
          })
        // 普通纯静态文本
        } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') {
          children.push({
            type: 3,
            text
          })
        }
      }
    },
    comment (text: string) { // 处理注释节点的函数
      /** 简单的在其父级推入一个文本Children,而这个Children的isComment为true */
      currentParent.children.push({
        type: 3,
        text,
        isComment: true
      })
    }
  })
  return root
}