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

其餘章節請看:javascript

vue 快速入門 系列css

vue loader 上

經過前面「webpack 系列」的學習,咱們知道如何用 webpack 實現一個不成熟的腳手架,好比提供開發環境和生成環境,開發環境提供本地服務器,有熱模塊替換,能使用 sass、es6等開發項目。html

實際工做中咱們可能會使用聲明式框架 vue 或 react 來開發項目,而它們都提供了相應的腳手架。在學習 vue-cli(vue官方的腳手架)以前,咱們先來玩一下 vue loader。前端

Tip:本篇也能夠稱之爲「vue loader 官網」筆記。經過本篇文章,咱們能學會編寫一個簡單的,用於單文件組件開發的腳手架;以及對單文件組件規範有一個初步的認識和理解。vue

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

介紹

Vue Loader 是什麼

Vue Loader 是一個 webpack 的 loader,它容許你以一種名爲單文件組件 (SFCs)的格式撰寫 Vue 組件。node

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

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

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

Tip: 更詳細的介紹請看下面的「Vue 單文件組件 (SFC) 規範」章節。react

Vue Loader 還提供了不少酷炫的特性:webpack

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

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

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 快速入門 系列

相關文章
相關標籤/搜索