vue 快速入門 系列 —— vue loader 上

彭加李發表於2021-07-01

其他章節請看:

vue 快速入門 系列

vue loader 上

通過前面“webpack 系列”的學習,我們知道如何用 webpack 實現一個不成熟的腳手架,比如提供開發環境和生成環境,開發環境提供本地伺服器,有熱模組替換,能使用 sass、es6等開發專案。

實際工作中我們可能會使用宣告式框架 vue 或 react 來開發專案,而它們都提供了相應的腳手架。在學習 vue-cli(vue官方的腳手架)之前,我們先來玩一下 vue loader。

Tip:本篇也可以稱之為“vue loader 官網”筆記。

:本文很多配置都參考筆者的另一篇文章webpack 快速入門 系列 —— 實戰一,比如babel、postcss、圖片、css提取等配置。

介紹

Vue Loader 是什麼

Vue Loader 是一個 webpack 的 loader,它允許你以一種名為單檔案元件 (SFCs)的格式撰寫 Vue 元件。

<template>
  <div class="example">{{ msg }}</div>
</template>

<script>
export default {
  data () {
    return {
      msg: 'Hello world!'
    }
  }
}
</script>

<style>
.example {
  color: red;
}
</style>

Tip: 更詳細的介紹請看下面的“Vue 單檔案元件 (SFC) 規範”章節。

Vue Loader 還提供了很多酷炫的特性:

  • 允許為 Vue 元件的每個部分使用其它的 webpack loader,例如在 <style> 的部分使用 Sass 和在 <template> 的部分使用 Pug;
  • 允許在一個 .vue 檔案中使用自定義塊,並對其運用自定義的 loader 鏈;
  • 使用 webpack loader 將 <style><template> 中引用的資源當作模組依賴來處理;
  • 為每個元件模擬出 scoped CSS;
  • 在開發過程中使用熱過載來保持狀態。

簡而言之,webpack 和 Vue Loader 的結合為你提供了一個現代、靈活且極其強大的前端工作流,來幫助撰寫 Vue.js 應用。

Tip: 上面這些特性,下文都會詳細介紹。

環境準備

直接參考"實戰一->準備本篇的環境"一節,搭建好 test-vue-loader 專案。

附上專案:

test-vue-loader        
  - src                 // 專案原始碼
    - a.css
    - b.js
    - c.js
    - index.html        // 頁面模板
    - index.js          // 入口
  - package.json        // 存放了專案依賴的包
  - webpack.config.js   // webpack配置檔案

hello-world

現在我們要把 App.vue 這個單檔案元件跑起來:

// src/App.vue
<template>
  <div class="example">{{ msg }}</div>
</template>

<script>
export default {
  data () {
    return {
      msg: 'Hello world!'
    }
  }
}
</script>

<style>
.example {
  color: red;
}
</style>

請看操作:

首先將 vue-loader 和 vue-template-compiler 一起安裝。

// vue 包當然也需要
> npm install -D vue@2 vue-loader@15 vue-template-compiler@2

每個 vue 包的新版本釋出時,一個相應版本的 vue-template-compiler 也會隨之釋出。每次升級專案中的 vue 包時,也應該匹配升級 vue-template-compiler。

接著修改配置檔案,核心程式碼如下:

const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
  module: {
    rules: [
      // ... 其它規則
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    // 請確保引入這個外掛!
    new VueLoaderPlugin()
  ]
}

在將 src/index.js 改為:

//引入Vue
import Vue from 'vue';
//引入元件
import App from './App.vue';

new Vue({    
    el: "body",    
    template: '<App/>',    
    components: {App}
});

通過 npm run dev 啟動,瀏覽器控制檯報錯,資訊如下:

[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

(found in <Root>)

// 翻譯
您正在使用 Vue 的僅執行時構建,其中模板編譯器不可用。 要麼將模板預編譯為渲染函式,要麼使用包含編譯器的構建。

告訴我們,正使用“只包含執行時版”,可以將模板編譯為渲染函式,或者使用包含編譯器的構建。

Tip:vue 有不同的版本,例如:

  • 完整版:同時包含編譯器和執行時的版本。
  • 編譯器:用來將模板字串編譯成為 JavaScript 渲染函式的程式碼。
  • 執行時:用來建立 Vue 例項、渲染並處理虛擬 DOM 等的程式碼。基本上就是除去編譯器的其它一切。

在“模組(module)”一文中介紹了 require() 方法載入第三方模組的規則,所以 import Vue from 'vue'; 就會去載入 test-vue-loader\node_modules\vue\package.json 中 main 指向的檔案("main": "dist/vue.runtime.common.js"),確實是執行時版。

可以通過以下兩種方式修改 index.js 來解決這個問題。

  • 使用完整版本
// inidex.js

- import Vue from 'vue';
// 前面會匹配是否核心包、第三方包、路徑查詢(./ 或 ../ 或 /),最後讀取到專案目錄下 node_modules 包裡的包
// vue.esm.js ES Module (基於構建工具使用),並且是完整版
+ import Vue from 'vue/dist/vue.esm.js'
...

Tip:這種方式在瀏覽器中會有如下警告:

[Vue warn]: Do not mount Vue to <html> or <body> - mount to normal elements instead.

所以將 el 中的 body 改為 #app 這種形式即可。

  • 改為渲染函式
// index.js
import Vue from 'vue';
import App from './App.vue';

new Vue({    
    el: "body", 
    render: h => h(App)
});

瀏覽器頁面顯示紅色文字”Hello world!“,單檔案元件解析成功。

處理資源路徑

當 Vue Loader 編譯單檔案元件中的 <template> 塊時,它也會將所有遇到的資源 URL 轉換為 webpack 模組請求。

讓 App.vue 引入一張圖片:

<template>
  <div class="example">
    {{ msg }}
    <!-- 增加圖片 -->
    <img src="./6.68kb.png"></img>
  </div>
</template>

終端報錯:

...
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.

// 翻譯
您可能需要一個合適的載入器來處理此檔案型別,目前沒有配置載入器來處理此檔案。

下載包,並修改配置:

> npm i -D url-loader@4 file-loader@6
// webpack.config.js -> module.rules
{
  test: /\.(png|jpg|gif)$/i,
  use: [
    {
      loader: 'url-loader',
      options: {
        // 調整的比 6.68 要小,這樣圖片就不會打包成 base64 
        limit: 1024*6,
      },
    },
  ],
},

再次執行,瀏覽器還是看不到圖片,檢視原始碼:

<div class="example">
  Hello world!
  <img src="[object Module]">
</div>

圖片的 src 有問題,這是因為 url-loader 預設採用 ES 模組語法,而 Vue 生成的是 CommonJS 模組語法,即 require('./6.68kb.png'),解決方法是讓二者採用相同的模板語法,下面將 url-loader 的 es-module 關閉:

// webpack.config.js
module: {
    rules: [
      ...
      {
        test: /\.(png|jpg|gif)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 1024*6,
              // +
              esModule: false,
            },
          },
        ],
      },
    ]
},

再次執行,圖片正常顯示。控制檯檢視:

<div class="example">
  Hello world!
  <img src="26bd867dd65e26dbc77d1e151ffd36e0.png">
</div>

轉換規則

  • 如果路徑是絕對路徑 (例如 /6.68kb.png),則會原樣保留。測試如下:

修改 App.vue,將其改為絕對路徑

<img src="/6.68kb.png"></img>

瀏覽器頁面中圖片沒出來,檢查元素:

<div class="example">
  Hello world!
  <img src="/6.68kb.png">
</div>

通過執行 npm run build 發現 dist 目錄中也沒有生成 6.68kb.png。於是我們知道使用絕對路徑,不僅會原樣保留,也不會將該資源打包出去。

  • 如果路徑以 ~ 開頭,其後的部分將會被看作模組依賴。這意味著你可以用該特性來引用一個 Node 依賴中的資源。測試如下:

將圖片 6.68kb.png 拷貝到一個模組 node_modules/vue 中

修改圖片引用

<img src="~vue/6.68kb.png"></img>

圖片正常顯示

  • 如果路徑以 @ 開頭,也會被看作模組依賴。如果你的 webpack 配置中給 @ 配置了 alias,這就很有用了。所有 vue-cli 建立的專案都預設配置了將 @ 指向 /src。測試如下:

將路徑改為以 @ 開頭,終端報錯:

// 以 @ 開頭
<img src="@vue/6.68kb.png"></img>

// 終端報錯
Module not found: Error: Can't resolve '@vue/6.68kb.png' in '....\test-vue-loader\src'

@ 是指向 /src 嗎?還是報錯:

<img src="@/6.68kb.png"></img>

// 終端報錯
Module not found: Error: Can't resolve '@/6.68kb.png' in...

給 @ 配置 alias,圖片正常顯示。請看程式碼:

<img src="@/6.68kb.png"></img>

// 給 @ 配置 alias
module.exports = {
  ...
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src/'),
    },
  },
}

使用前處理器

在 webpack 中,所有的前處理器需要匹配對應的 loader。Vue Loader 允許你使用其它 webpack loader 處理 Vue 元件的某一部分。它會根據 lang 特性以及你 webpack 配置中的規則自動推斷出要使用的 loader。

sass

給 App.vue 增加 sass 程式碼:

// App.vue 尾部增加如下sass程式碼
<style lang="scss">
$size: 3em;
.example {
  font-size: $size;
}
</style>

為了能讓 sass/scss 生效,需要安裝依賴包:

> npm i -D sass-loader@10 node-sass@6

修改配置檔案:

// webpack.config.js -> module.rules

// 普通的 `.scss` 檔案和 `*.vue` 檔案中的
// `<style lang="scss">` 塊都應用它
{
  test: /\.scss$/,
  use: [
    'vue-style-loader',
    'css-loader',
    'sass-loader'
  ]
}

重啟服務,你會發現頁面中的 ”hello world“ 字號變大,sass 編譯成功。

Tip: vue-style-loader 是一個基於 style-loader 的 fork。 與 style-loader 類似,您可以將其連結在 css-loader 之後,以將 CSS 作為樣式標籤動態注入文件。 但是,由於它作為依賴項包含在 vue-loader 中並預設使用,因此在大多數情況下,您不需要自己配置此載入器,即無需下載 vue-style-loader 即可使用。

Sass vs SCSS

sass-loader 預設處理不基於縮排的 scss 語法。

將 sass 改為縮排語法,終端會報錯:

// 縮排語法
<style lang="sass">
$size: 3em
.example 
  font-size: $size;
</style>

// 終端報錯
SassError: Invalid CSS after "$size: 3em": expected expression (e.g. 1px, bold), was ".example "...

:需要將 lang 從 scss 改為 sass,配合下面的 rule 工作。

為了使用基於縮排的 sass 語法,你需要向這個 loader 傳遞選項:

// webpack.config.js -> module.rules
{
  test: /\.sass$/,
  use: [
      'vue-style-loader',
      'css-loader',
      {
          loader: 'sass-loader',
          options: {
              // sass-loader version >= 8
              sassOptions: {
                  indentedSyntax: true
              }
          }
      }
  ]
},

重啟伺服器,縮排語法生效了。

共享全域性變數

sass-loader 也支援一個 prependData 選項,這個選項允許你在所有被處理的檔案之間共享常見的變數,而不需要顯式地匯入它們。請看示例:

// webpack.config.js -> module.rules
{
  test: /\.sass$/,
  use: [
      ...
      {
          loader: 'sass-loader',
          options: {
              ...,
              additionalData: `$size: 3em;`,
          }
      }
  ]
},

App.vue 中直接使用 $size,而無需定義:

...
<style lang="sass">
.example 
  font-size: $size;
</style>

less

若直接在 App.vue 中增加如下 less 的樣式,會報錯:

// 給 App.vue 增加 less 語法
<style lang="less">
@size: 2em;
.example {
  font-size: @size
}
</style>
// 終端報錯:
ERROR in ./src/App.vue?vue&type=style&index=2&lang=less&..
Module parse failed: Unexpected character '@' (29:0)...

安裝依賴,並增加 rule,重啟服務即可生效。

> npm i -D less@4 less-loader@7
// webpack.config.js -> module.rules
{
  test: /\.less$/,
  use: [
    'vue-style-loader',
    'css-loader',
    'less-loader'
  ]
}

Stylus

若直接在 App.vue 中增加如下 stylus 的樣式,會報錯:

// 給 App.vue 增加 stylus 語法
<style lang="stylus">
/* stylus 語法 */
$size = 3em
.example
  font-size: $size
</style>
// 終端報錯:
ERROR in ./src/App.vue?vue&type=style&index=3&lang=stylus&..

安裝依賴,並增加 rule,重啟服務即可生效。

> npm i  -D stylus@0 stylus-loader@4
// webpack.config.js -> module.rules
{
  test: /\.styl(us)?$/,
  use: [
    'vue-style-loader',
    'css-loader',
    'stylus-loader'
  ]
}

PostCSS

tip:Vue Loader v15 不再預設應用 PostCSS 變換。你需要通過 postcss-loader 使用 PostCSS。

我們的 vue loader 是 15.9.7,滿足該條件。

postcss-loader 可以和上述其它前處理器結合使用。下面我們就給 less 前處理器新增 postcss。

修改 App.vue,給 less 中增加明天的 css 語法:

// lch 是明天的CSS
<style lang="less">
@size: 2em;
.example {
  color: lch(100 100 100);
  font-size: @size
}
</style>

瀏覽器檢視樣式,發現 color: lch(100 100 100) 沒生效。

安裝依賴包,並修改配置檔案:

> npm i -D postcss-loader@4 postcss-preset-env@6
// webpack.config.js
// +
const postcssLoader = { 
  loader: 'postcss-loader', 
  options: {
    // postcss 只是個平臺,具體功能需要使用外掛
    postcssOptions:{
      plugins:[
        [
          "postcss-preset-env",
          {
            browsers: 'ie >= 8, chrome > 10',
          },
        ],
      ]
    }
  } 
}

module: {
    rules: [
    {
        test: /\.less$/,
        use: [
            'vue-style-loader',
            'css-loader',
            // +
            postcssLoader,
            'less-loader'
        ]
    },

重新啟動伺服器,”Hello World!“ 顯示黃色。lch 也編譯成了 color: rgb(255, 255, 0)

Babel

首先編寫箭頭函式,如果打包後能轉為普通函式,則說明 babel 配置成功。

給 App.vue 增加箭頭函式:

<script>
export default {
  ...
};
// 箭頭函式
const sum = (a, b) => (a + b);
console.log(sum(1, 10));
</script>

瀏覽器的控制檯輸出 11,但在瀏覽器中的源中檢視 mian.js,發現箭頭函式沒有轉為普通函式。

const sum = (a, b) => (a + b);\r\nconsole.log(sum(1, 10));\r\n\n\n/

安裝依賴,並修改配置:

> npm i -D babel-loader@8 @babel/preset-env@7
// webpack.config.js -> module.rules
{
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
      loader: 'babel-loader',
      options: {
        presets: [
          ['@babel/preset-env']
        ]
      }
    }
}

重新啟動伺服器,箭頭函式就變成普通函式:

var sum = function sum(a, b) {\n  return a + b;\n};\n\nconsole.log(sum(1, 10));
排除 node_modules

exclude: /node_modules/ 在應用於 .js 檔案的 JS 轉譯規則 (例如 babel-loader) 中是蠻常見的。鑑於 v15 中的推導變化,如果你匯入一個 node_modules 內的 Vue 單檔案元件,它的 <script> 部分在轉譯時將會被排除在外。

我們將 src/App.vue 拷貝一份到 node_modules/vue 目錄中,並修改 index.js 中 App.vue 的引入方式:

- import App from './App.vue';
+ import App from 'vue/App.vue';

在瀏覽器中的源中檢視 mian.js,發現箭頭函式沒有轉為普通函式:

const sum = (a, b) => (a + b);\r\nconsole.log(sum(11, 10));

為了確保 js 的轉譯應用到 node_modules 的 Vue 單檔案元件,你需要通過使用一個排除函式將它們加入白名單:

{
    test: /\.js$/,
    exclude: file => (
        /node_modules/.test(file) &&
        !/\.vue\.js/.test(file)
    ),
    ...
}

重啟服務即可生效

:進行下面測試之前,別忘了還原 App.vue 的引入

import App from './App.vue'

TypeScript

給 App.vue 寫入 ts 程式碼,終端報錯:

// 修改 App.vue 的 script
<script lang='ts'>
export default {
  ...
}

...
/* typescript */
class Greeter<T> {
    greeting: T;
    constructor(message: T) {
        this.greeting = message;
    }
    greet() {
        return this.greeting;
    }
}

let greeter = new Greeter<string>("Hello, world");
console.log(greeter.greet())
</script>
// 終端報錯
...
You may need an additional loader to handle the result of these loaders.
|
| /* typescript */
> class Greeter<T> {
|     greeting: T;
|     constructor(message: T) {

安裝依賴包,並修改配置:

> npm i -D typescript@4 ts-loader@7
// webpack.config.js
module.exports = {
  resolve: {
    // 將 `.ts` 新增為一個可解析的副檔名。
    extensions: ['.ts', '.js']
  },
  module: {
    rules: [
      // ... 忽略其它規則
      {
        test: /\.ts$/,
        loader: 'ts-loader',
        options: { appendTsSuffixTo: [/\.vue$/] }
      }
    ]
  },
  ...
}

重啟服務,終端報錯:

[tsl] ERROR
      TS18002: The 'files' list in config file 'tsconfig.json' is empty.

新建 test-vue-loader/tsconfig.json,內容如下:

{
  "compilerOptions": {
    "sourceMap": true
  }
}

重啟服務,終端報錯資訊變為:

TS18003: No inputs were found in config file 'tsconfig.json'. Specified 'include' paths were '["**/*"]' and 'exclude' paths were '[]'.

可以給 tsconfig.json 增加 allowJs:

{
  "compilerOptions": {
    "allowJs": true, 
    "sourceMap": true
  }
}

重啟伺服器,終端沒有丟擲錯誤,瀏覽器控制檯成功輸出 ”hello, TypeScript 解析成功。

Pug

Pug 是一個高效能模板引擎,深受 Haml 影響,使用 JavaScript 實現,適用於 Node.js 和瀏覽器;

Pug 是一種用於編寫 html 的乾淨、對空格敏感的語法

在 App.vue 中使用 pug,重新打包,停住了:

<template>
  <div class="example">
    ...
  </div>
</template>

<!-- 多個 template,會以最後一個 template 為準-->
<template lang="pug">
div
  h1 I am pug!
</template>
// 打包
test-vue-loader> npm run build

> test-vue-loader@1.0.0 build
> webpack

不動了...

猜測可能是沒有配置 pug 導致的。於是安裝依賴,並修改配置:

> npm i -D pug@3 pug-plain-loader@1
// webpack.config.js -> module.rules
{
  test: /\.pug$/,
  loader: 'pug-plain-loader'
}

重新啟動伺服器,瀏覽器頁面顯示 I am pug!,pug 解析成功。

// 瀏覽器檢視原始碼
<div>
  <h1>I am pug!</h1>
</div>

Scoped Css

Tip: 為了減少影響,方便學習和測試,可以將 App.vue 的程式碼全部註釋,就像這樣<!-- App.vue 的所有程式碼 -->

<style> 標籤有 scoped 屬性時,它的 CSS 只作用於當前元件中的元素,它有一些注意事項,但不需要任何 polyfill。

修改 App.vue 內容,給 style 增加 scoped:

<template>
  <div class="example">hi</div>
</template>

<style scoped>
.example {
  color: red;
}
</style>

通過瀏覽器檢查:

.example[data-v-7ba5bd90] {
    color: red;
}
<div data-v-7ba5bd90="" class="example">hi</div>

Tip:文件說它通過使用 PostCSS 來實現轉換,但目前我的 postcss 只結合 less 使用,這裡使用的明顯是 css,所以猜想 postCss 難道內建了!

混用本地和全域性樣式

<style scoped>
.example {
  color: red;
}
</style>

<style>
.example {
  font-size: 2em;
}
</style>

轉換結果:

<style>
.example[data-v-7ba5bd90] {
  color: red;
}
</style>

<style>
.example {
  font-size: 2em;
}

子元件的根元素

使用 scoped 後,父元件的樣式將不會滲透到子元件中。不過一個子元件的根節點會同時受其父元件的 scoped CSS 和子元件的 scoped CSS 的影響。這樣設計是為了讓父元件可以從佈局的角度出發,調整其子元件根元素的樣式。—— 官網

什麼意思?我們做個測試就明白了

新建一個子元件:

// Box.vue
<template>
<div class='m-box'>
    children
    <p>m-box p1</p>
</div>
</template>

在 App.vue 中引用子元件:

<template>
  <div class="s-c1">
    <p>hi</p>
    <Box/>
  </div>
</template>

<script>
import Box from './Box.vue'
export default {
  data () {
    return {
      msg: 'Hello world!'
    }
  },
  components:{
    Box
  }
}
</script>

<style scoped>
.s-c1 {
  color: red;
}
</style>

頁面中三行文字全是紅色。

hi
children
m-box p1

子元件明明沒有寫樣式,而且父元件的樣式也寫在 scope 中,為什麼子元件的文字也變成紅色?

瀏覽器檢視程式碼:

<style>
.s-c1[data-v-7ba5bd90] {
  color: red;
}
</style>

<div data-v-7ba5bd90="" class="s-c1">
    <p data-v-7ba5bd90="">hi</p> 
    <div data-v-1461803c="" data-v-7ba5bd90="" class="m-box">
        children
        <p data-v-1461803c="">m-box p1</p>
    </div>
</div>

原來我們寫的程式碼轉成這種形式,子元件的文字確實應該是紅色。

而上面提到:”不過一個子元件的根節點會同時受其父元件的 scoped CSS 和子元件的 scoped CSS 的影響“

指的應該是子元件的根元素上既有子元件的標記,也有父元件的標記,即同時有 data-v-1461803c=""data-v-7ba5bd90=""

將 App.vue 的 style 改成下面程式碼,在頁面中會看得更清晰:

<style scoped>
.s-c1 {
  color: red;
  margin:10px;padding:10px;
}
div{
  border: 1px solid blue;
}
</style>

不僅父元件有邊框,子元件的的根(div)也會有藍色邊框。

深度作用選擇器

如果你希望 scoped 樣式中的一個選擇器能夠作用得“更深”,例如影響子元件,你可以使用 >>> 操作符:

只調整父元件中 p 元素的字號,可以這麼寫:

<style scoped>
...
div p{font-size:2em;}
</style>

轉換成:

div p[data-v-7ba5bd90]{font-size:2em;}

如果也希望調整子元件中 p 元素的字號:

div >>> p{font-size:2em;}

轉換成:

div[data-v-7ba5bd90] p{font-size:2em;}

如果希望只作用於 div 的孩子節點:

div >>> > p{font-size:2em;}

轉換成:

div[data-v-7ba5bd90] > p{font-size:2em;}

>>>>不會生效

有些像 Sass 之類的前處理器無法正確解析 >>>。這種情況下你可以使用 /deep/ 或 ::v-deep 操作符取而代之——兩者都是 >>> 的別名,同樣可以正常工作。

動態生成的內容

通過 v-html 建立的 DOM 內容不受 scoped 樣式影響,但是你仍然可以通過深度作用選擇器來為他們設定樣式。

我們做個測試

我們給 App.vue 和 Box.vue 都增加 v-html,程式碼如下:

// App.vue
<template>
  <div class="s-c1">
    <p>hi</p>
    <!-- + -->
    <span v-html='aHtml'></span>
    <Box/>
  </div>
</template>

<script>
...
export default {
  data () {
    return {
      // +
      aHtml: '<p>i am aHtml</p>'
    }
  },
}
</script>

<style scoped>
...
div  >>>  p{font-size:2em;}
</style>
// Box.vue
<template>
<div class='m-box'>
    children
    <span v-html='bHtml'></span>
    <p>m-box p1</p>
</div>
</template>

<script>
export default {
  data () {
    return {
      bHtml: '<p>i am bHtml</p>'
    }
  }
}
</script>

i am aHtmli am bHtml字號都是 2em

瀏覽器檢視轉換後的程式碼:

div[data-v-7ba5bd90] p{font-size:2em;}
<div data-v-7ba5bd90="" class="s-c1" style="margin: 10px; padding: 10px;">
  <p data-v-7ba5bd90="">hi</p>
  <span data-v-7ba5bd90="">
    <p>i am aHtml</p>
  </span>
  <div data-v-1461803c="" data-v-7ba5bd90="" class="m-box">children2
    <span data-v-1461803c="">
      <p>i am bHtml</p>
    </span>
    <p data-v-1461803c="">m-box p1</p></div>
</div>

將深度作用選擇器刪除後測試:

div p{font-size:2em;}

i am aHtmli am bHtml字號都不在是 2em。

轉換後的程式碼是:

div p[data-v-7ba5bd90]{font-size:2em;}
<div data-v-7ba5bd90="" class="s-c1">
  <p data-v-7ba5bd90="">hi</p> 
      <span data-v-7ba5bd90="">
      <p>i am aHtml</p>
  </span>
  <div data-v-7ba5bd90="" class="m-box">
      children
      <span>
          <p>i am bHtml</p>
      </span>
      <p>m-box p1</p>
  </div>
</div>

v-html生成的元素都不會有特殊的標記,比如這裡的 data-v-7ba5bd90

至此,我們就理解了開頭的話。

還有一些要留意

Scoped 樣式不能代替 class。考慮到瀏覽器渲染各種 CSS 選擇器的方式,當 p { color: red } 是 scoped 時 (即與特性選擇器組合使用時) 會慢很多倍。如果你使用 class 或者 id 取而代之,比如 .example { color: red },效能影響就會消除。

div{}
.div{}

轉換成:

div[data-v-7ba5bd90]{}
.div[data-v-7ba5bd90]{}

在遞迴元件中小心使用後代選擇器!

  • 對選擇器 .a .b 中的 CSS 規則來說,如果匹配 .a 的元素包含一個遞迴子元件,則所有的子元件中的 .b 都將被這個規則匹配。

其他章節請看:

vue 快速入門 系列

相關文章