国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

vue-loader 源碼解析系列之 selector

miqt / 3124人閱讀

摘要:當前正在處理的節(jié)點,以及該節(jié)點的和等信息。源碼解析之一整體分析源碼解析之三寫作中源碼解析之四寫作中作者博客作者作者微博

筆者系 vue-loader 貢獻者之一(#16)

前言

vue-loader 源碼解析系列之一,閱讀該文章之前,請大家首先參考大綱 vue-loader 源碼解析系列之 整體分析

selector 做了什么
const path = require("path")
const parse = require("./parser")
const loaderUtils = require("loader-utils")

module.exports = function (content) {
  // 略
  const query = loaderUtils.getOptions(this) || {}
  // 略
  const parts = parse(content, filename, this.sourceMap, sourceRoot, query.bustCache)
  let part = parts[query.type]
  // 略
  this.callback(null, part.content, part.map)
}

大家可以看到,selector的代碼非常簡單,
通過 parser 將 .vue 解析成對象 parts, 里面分別有 style, script, template。可以根據(jù)不同的 query, 返回對應的部分。
很明顯那么這個 parser 完成了分析分解 .vue 的工作,那么讓我們繼續(xù)深入 parser

parser 做了什么
const compiler = require("vue-template-compiler")
const cache = require("lru-cache")(100)

module.exports = (content, filename, needMap, sourceRoot, bustCache) => {
  const cacheKey = hash(filename + content)
  // 略
  let output = cache.get(cacheKey)
  if (output) return output
  output = compiler.parseComponent(content, { pad: "line" })
  if (needMap) {
    // 略去了生成 sourceMap 的代碼
  }
  cache.set(cacheKey, output)
  return output
}

同樣的,為了方便讀者理解主要流程,筆者去掉了部分代碼。

從上面代碼可以看到,.vue 解析的工作其實是交給了 compiler.parseComponent 去完成,那么我們需要繼續(xù)深入 compiler。
注意,這里 vue-template-compiler 并不是 vue-loader 的一部分,從 vue-template-compiler 的 npm 主頁可以了解到, vue-template-compiler 原來是 vue 本體的一部分
并不是一個多帶帶的 package。通過查看文檔可知,compiler.parseComponent 的邏輯在 vue/src/sfc/parser.js 里。

源碼如下

parseComponent 做了什么
/**
 * Parse a single-file component (*.vue) file into an SFC Descriptor Object.
 */
export function parseComponent (
  content: string,
  options?: Object = {}
 ): SFCDescriptor {
  const sfc: SFCDescriptor = {
    template: null,
    script: null,
    styles: [],
    customBlocks: []
  }
  let depth = 0
  let currentBlock: ?(SFCBlock | SFCCustomBlock) = null

  function start (
    tag: string,
    attrs: Array,
    unary: boolean,
    start: number,
    end: number
  ) {
    // 略
  }

  function checkAttrs (block: SFCBlock, attrs: Array) {
    // 略
  }

  function end (tag: string, start: number, end: number) {
    // 略
  }

  function padContent (block: SFCBlock | SFCCustomBlock, pad: true | "line" | "space") {
    // 略
  }

  parseHTML(content, {
    start,
    end
  })

  return sfc
}

parseComponent 里面有以下變量

處理對象 sfc

把 .vue 里的 css, javaScript, html 抽離出來之后,存放到找個這個對象里面

變量 depth

當前正在處理的節(jié)點的深度,比方說,對于 來說,處理到 foo 時,當前深度就是 3, 處理到

時,當前深度就是 2 。

currentBlock

當前正在處理的節(jié)點,以及該節(jié)點的 attr 和 content 等信息。

函數(shù) start

遇到 openTag 節(jié)點時,對 openTag 的相關(guān)處理。邏輯不是很復雜,讀者可以直接看源碼。有一點值得注意的是,style 是用 array 形式存儲的

函數(shù) end

遇到 closeTag 節(jié)點時,對 closeTag 的相關(guān)處理。

函數(shù) checkAttrs

對當前節(jié)點的 attrs 的相關(guān)處理

函數(shù) parseHTML

這是和一個外部的函數(shù),傳入了 content (其實也就是 .vue 的內(nèi)容)以及由 start和 end 兩個函數(shù)組成的對象。看來,這個 parseHTML 之才是分解分析 .vue 的關(guān)鍵

跟之前一樣,我們要繼續(xù)深入 parseHTML 函數(shù)來分析,它到底對 .vue 做了些什么,源碼如下

parseHTML 做了什么
export function parseHTML (html, options) {
  const stack = []
  const expectHTML = options.expectHTML
  const isUnaryTag = options.isUnaryTag || no
  const canBeLeftOpenTag = options.canBeLeftOpenTag || no
  let index = 0
  let last, lastTag
  while (html) {
    last = html
    if (!lastTag || !isPlainTextElement(lastTag)) {
      // 這里分離了template
    } else {
      // 這里分離了style/script
  }

  // 略

  // 前進n個字符
  function advance (n) {
    // 略
  }

  // 解析 openTag 比如