徹底學會element-ui按需引入和純淨主題定製

wuwhs發表於2020-10-13

前言

手上有些專案用的element-ui,剛好有空琢磨一下怎麼減小打包檔案大小和打包速度方面,為了演示實驗,用 vue-cli 生成初始專案,在這僅對 element-ui 主題和元件方面來優化。

vue init webpack vuecli

完整引入

完整地將 ui 和樣式引入。

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

在頁面簡單使用 2 個元件,看看效果。

<el-tabs v-model="activeName" @tab-click="handleClick">
  <el-tab-pane label="使用者管理" name="first">使用者管理</el-tab-pane>
  <el-tab-pane label="配置管理" name="second">配置管理</el-tab-pane>
  <el-tab-pane label="角色管理" name="third">角色管理</el-tab-pane>
  <el-tab-pane label="定時任務補償" name="fourth">定時任務補償</el-tab-pane>
</el-tabs>

<el-steps :active="2" align-center>
  <el-step title="步驟1" description="這是一段很長很長很長的描述性文字"></el-step>
  <el-step title="步驟2" description="這是一段很長很長很長的描述性文字"></el-step>
  <el-step title="步驟3" description="這是一段很長很長很長的描述性文字"></el-step>
  <el-step title="步驟4" description="這是一段很長很長很長的描述性文字"></el-step
></el-steps>

元件效果

再看一下打包後的資源大小情況npm run build --report

Hash: 40db03677fe41f7369f6
Version: webpack 3.12.0
Time: 20874ms
                                                  Asset       Size  Chunks                    Chunk Names
    static/css/app.cb8131545d15085cee647fe45f1d5561.css     234 kB       1  [emitted]         app
                 static/fonts/element-icons.732389d.ttf      56 kB          [emitted]
               static/js/vendor.a753ce0919c8d42e4488.js     824 kB       0  [emitted]  [big]  vendor
                  static/js/app.8c4c97edfce9c9069ea3.js    3.56 kB       1  [emitted]         app
             static/js/manifest.2ae2e69a05c33dfc65f8.js  857 bytes       2  [emitted]         manifest
                static/fonts/element-icons.535877f.woff    28.2 kB          [emitted]
static/css/app.cb8131545d15085cee647fe45f1d5561.css.map     332 kB          [emitted]
           static/js/vendor.a753ce0919c8d42e4488.js.map    3.26 MB       0  [emitted]         vendor
              static/js/app.8c4c97edfce9c9069ea3.js.map    16.6 kB       1  [emitted]         app
         static/js/manifest.2ae2e69a05c33dfc65f8.js.map    4.97 kB       2  [emitted]         manifest
                                             index.html  506 bytes          [emitted]

發現打包後提取公共模組 static/js/vendor.js824kb

再看一下各個模組佔用情況:

各個模組佔用情況

發現 elment-ui.common.js 佔用最大。所有模組資源總共有 642kb。怎麼才能減小打包後的大小呢?很容易就會想到 ui 的引入和樣式的引入中,實際我們只使用了三個元件,卻整體都被打包了,在這裡引入這三個元件即可。

按需引入元件樣式

新建一個 element-variables.scss 檔案(為什麼是 SCSS 檔案,後面自定義主題會用到)。

/*icon字型路徑變數*/
$--font-path: "~element-ui/lib/theme-chalk/fonts";

/*按需引入用到的元件的scss檔案和基礎scss檔案*/
@import "~element-ui/packages/theme-chalk/src/base.scss";
@import "~element-ui/packages/theme-chalk/src/rate.scss";
@import "~element-ui/packages/theme-chalk/src/button.scss";
@import "~element-ui/packages/theme-chalk/src/row.scss";

按需引入元件

新建一個 element-config.js 檔案,將專案用到的 element 元件引入。

import {
  Tabs,
  TabPane,
  Steps,
  Step
} from 'element-ui'

export default {
  install (V) {
    V.use(Tabs)
    V.use(TabPane)
    V.use(Steps)
    V.use(Step)
  }
}

第一次優化後打包分析

將以上 element-variables.scsselement-config.js 引入到 main.js 中。

import ElementUI from '@/assets/js/element-config'
import '@/assets/css/element-variables.scss'

Vue.use(ElementUI)

貌似上面一切都很順理成章,打包後大小會減小。

Hash: 2ef987c23a5d612e00e1
Version: webpack 3.12.0
Time: 17430ms
                                                  Asset       Size  Chunks                    Chunk Names
    static/css/app.3c70d8d75c176393318b232a345e3f0f.css    38.8 kB       1  [emitted]         app
                 static/fonts/element-icons.732389d.ttf      56 kB          [emitted]
               static/js/vendor.caa5978bb1eb0a15b097.js     824 kB       0  [emitted]  [big]  vendor
                  static/js/app.5ebb19489355acc3167b.js    3.64 kB       1  [emitted]         app
             static/js/manifest.2ae2e69a05c33dfc65f8.js  857 bytes       2  [emitted]         manifest
                static/fonts/element-icons.535877f.woff    28.2 kB          [emitted]
static/css/app.3c70d8d75c176393318b232a345e3f0f.css.map    53.9 kB          [emitted]
           static/js/vendor.caa5978bb1eb0a15b097.js.map    3.26 MB       0  [emitted]         vendor
              static/js/app.5ebb19489355acc3167b.js.map      17 kB       1  [emitted]         app
         static/js/manifest.2ae2e69a05c33dfc65f8.js.map    4.97 kB       2  [emitted]         manifest
                                             index.html  506 bytes          [emitted]

結果可知,static/js/vendor.js 還是 824kb

再看各個模組佔用情況:

第一次優化後各個模組佔用情況

WHAT? 竟然模組都沒什麼變化,豈不是竹籃打水,事與願違。

再次打包優化嘗試

後來查到有人同樣遇到這個問題,提出一個issues#6362,原來只引入需要的element-ui元件,webpack還是把整體的 UI 庫和樣式都打包了,需要一個 webpackbabel 外掛 babel-plugin-component,這樣才能真正按需引入打包。這塊其實被寫到官方文件更換 自定義主題 的配置了。

於是 npm i babel-pugin-componet -D 安裝後,在增加 .babelrc 檔案外掛配置

{
  "presets": [
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  "plugins": [
    "transform-vue-jsx",
    "transform-runtime",
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

頁面執行正常,再次打包。

Hash: f182f70cb4ceee63b5d5
Version: webpack 3.12.0
Time: 10912ms
                                                  Asset       Size  Chunks             Chunk Names
    static/css/app.95c94c90ab11fdd4dfb413718f444d0c.css    39.9 kB       1  [emitted]  app
                 static/fonts/element-icons.732389d.ttf      56 kB          [emitted]
               static/js/vendor.befb0a8962f74af4b7e2.js     157 kB       0  [emitted]  vendor
                  static/js/app.5343843cc20a78e80469.js    3.86 kB       1  [emitted]  app
             static/js/manifest.2ae2e69a05c33dfc65f8.js  857 bytes       2  [emitted]  manifest
                static/fonts/element-icons.535877f.woff    28.2 kB          [emitted]
static/css/app.95c94c90ab11fdd4dfb413718f444d0c.css.map    93.5 kB          [emitted]
           static/js/vendor.befb0a8962f74af4b7e2.js.map     776 kB       0  [emitted]  vendor
              static/js/app.5343843cc20a78e80469.js.map    17.1 kB       1  [emitted]  app
         static/js/manifest.2ae2e69a05c33dfc65f8.js.map    4.97 kB       2  [emitted]  manifest
                                             index.html  506 bytes          [emitted]

static/js/vendor.js 確實變小了,157kB。再來看各個模組分析圖。

再次優化後各個模組分析圖

模組總共 157.93KB,少了 5 倍!

更換主題-覆蓋樣式

element-uitheme-chalk 使用 SCSS 編寫,如果在自己的專案中也是用 SCSS,那麼可以直接在專案中改變樣式變數,因此可以在前面新建的 element-variables.scss 檔案用新的主題顏色變數覆蓋即可。

/**
* 覆蓋主題色
*/
/*主題顏色變數*/
$--color-primary: #f0f;

/*icon字型路徑變數*/
$--font-path: '~element-ui/lib/theme-chalk/fonts';

/* 引入全部預設樣式 會引入沒用到的元件樣式 */
// @import '~element-ui/packages/theme-chalk/src/index';

/* 按需引入用到的元件的scss檔案和基礎scss檔案 */
@import '~element-ui/packages/theme-chalk/src/base.scss';
@import '~element-ui/packages/theme-chalk/src/rate.scss';
@import '~element-ui/packages/theme-chalk/src/button.scss';
@import '~element-ui/packages/theme-chalk/src/row.scss';

現在我們的主題就變成了預期效果

主題改變了

可能你已經注意到了,這裡推薦的是分別引入用到的元件樣式,而不是引入全部預設樣式,因為這樣會導致引入沒有使用到的元件樣式。比如當前案例中我們沒有使用到 ColorPicker 元件,在打包輸出的 css 檔案中確有該元件樣式。

打包樣式表出現沒有使用的樣式

更換主題-純淨樣式

通過以上優化可以按需的將所用到元件打包,排除沒用到的元件,減少包的大小。但是,還是存在一個小瑕疵:一個用到的元件樣式會被兩次打包,一次是預設的樣式,一次是覆蓋的樣式。

還存在預設樣式

出現這個問題是由於我們在兩個地方對樣式進行引入了,一個是在 .babelrc 檔案中通過 babel-plugin-component 外掛按需引入 element-ui 元件及其預設樣式,一個是在 element-variables.scss 檔案中覆蓋預設樣式生成的自定義樣式。

所以怎樣將二者結合,即babel-plugin-component 外掛按需引入的元件樣式改成使用者自定義樣式,達成純淨樣式目標呢?這裡就要用到 element-ui 的主題工具進行深層次的主題定製。

主題和主題工具安裝

首先安裝主題工具 element-theme,可以全域性安裝也可安裝在專案目錄。這裡推薦安裝在專案錄,方便別人 clone 專案時能直接安裝依賴並啟動。

npm i element-theme -D

然後安裝白堊主題,可以從 npm 安裝或者從 GitHub 拉取最新程式碼。

# 從 npm
npm i element-theme-chalk -D

# 從 GitHub
npm i https://github.com/ElementUI/theme-chalk -D

主題構建

element-theme 支援的構建有 Node APICLI 方式。

通過 CLI 構建方式

如果全域性安裝可以在命令列裡通過 et 呼叫工具,如果安裝在當前目錄下,需要通過 node_modules/.bin/et 訪問到命令。執行 -i--init) 初始化變數檔案。預設輸出到 element-variables.scss,當然你可以傳引數指定檔案輸出目錄。如果你想啟用 watch 模式,實時編譯主題,增加 -w--watch) 引數;如果你在初始化時指定了自定義變數檔案,則需要增加 -c--config) 引數,並帶上你的變數檔名。預設情況下編譯的主題目錄是放在 ./theme 下,你可以通過 -o--out) 引數指定打包目錄。

# 初始化變數檔案
et --init [file path]

# 實時編譯
et --watch [--config variable file path] [--out theme path]

# 編譯
et [--config variable file path] [--out theme path] [--minimize]

通過 Node API 構建方式

引入 element-theme 通過 Node API 形式構建

var et = require('element-theme')

// 實時編譯模式
et.watch({
  config: 'variables/path',
  out: 'output/path'
})

// 編譯
et.run({
  config: 'variables/path', // 配置引數檔案路徑 預設`./element-variables.css`
  out: 'output/path', // 輸出目錄 預設`./theme`
  minimize: false, // 壓縮檔案
  browsers: ['ie > 9', 'last 2 versions'], // 瀏覽器支援
  components: ['button', 'input'] // 選定元件構建自定義主題
})

應用 Node API 構建自定義主題

在這裡,為了讓主題的構建更加直觀和被專案共享,採用 Node API 方式構建,在專案根目錄下新建 theme.js檔案。

const et = require('element-theme')
// 第一步生成樣式變數檔案
// et.init('./src/theme.scss')
// 第二步根據實際需要修改該檔案
// ...
// 第三步根據該變數檔案編譯出自定義的主題樣式檔案
et.run({
  config: './src/theme.scss',
  out: './src/theme'
})

package.json 中增加 scripts 指令

{
  "scripts": {
    "theme": "node theme.js"
  }
}

這樣就可以通過 npm run theme 指令來編譯主題了。編譯過程:

  • 執行該指令初始化主題變數檔案 theme.scss
  • 根據實際需要修改這個檔案裡主題樣式。
  • 再執行該指令編譯輸出自定義的主題樣式檔案放在 theme 目錄下。

這樣就完成了所有自定義主題樣式的構建。要想將這些自定義樣式隨著元件按需引入,需要將 .babelrc 檔案中按需引入外掛 babel-plugin-component 引數 styleLibraryName 從原本的 element-ui 預設樣式目錄變成現在自定義目錄 ~src/theme

"plugins": [
    "transform-vue-jsx",
    "transform-runtime",
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "~src/theme"
      }
    ]
  ]

一切準備就緒,專案打包,打包後的 css 檔案中只有唯一自定義樣式,沒有了預設樣式,也不存在沒被引入元件的樣式,實現了我們預期的純淨的自定義樣式!

不存在預設樣式

Hash: c442bcf9d471bddfdccf
Version: webpack 3.12.0
Time: 10174ms
                                                  Asset       Size  Chunks             Chunk Names
    static/css/app.52d411d0c1b344066ec1f456355aa7b9.css    38.8 kB       1  [emitted]  app
                static/fonts/element-icons.535877f.woff    28.2 kB          [emitted]
               static/js/vendor.befb0a8962f74af4b7e2.js     157 kB       0  [emitted]  vendor
                  static/js/app.43c09c1f16b24d371e07.js    3.82 kB       1  [emitted]  app
             static/js/manifest.2ae2e69a05c33dfc65f8.js  857 bytes       2  [emitted]  manifest
                 static/fonts/element-icons.732389d.ttf      56 kB          [emitted]
static/css/app.52d411d0c1b344066ec1f456355aa7b9.css.map    81.3 kB          [emitted]
           static/js/vendor.befb0a8962f74af4b7e2.js.map     776 kB       0  [emitted]  vendor
              static/js/app.43c09c1f16b24d371e07.js.map    17.1 kB       1  [emitted]  app
         static/js/manifest.2ae2e69a05c33dfc65f8.js.map    4.97 kB       2  [emitted]  manifest
                                             index.html  506 bytes          [emitted]

由於樣式是純淨的,css 檔案大小從原來完全引入的 234KB 變成 38.8KB,進一步減小了打包大小。

總結

通過以上實驗分析我們可以得知,element-ui 要想實現按需引入和純淨的主題樣式:

  • 首先通過 babel-plugin-component 外掛進行按需引入。
  • 再用 element-theme 工具生成樣變數檔案。
  • 然後根據專案需求修改自定義樣式,依據該檔案構建生成所有樣式。
  • 最後將按需引入樣式 styleLibraryName 指向自定義樣式目錄。

如果對樣式提取要求不高,可直接採取變數覆蓋形式(同時存在預設樣式)。
還有不清楚可以戳這裡檢視案例原始碼,贈人 star,手有餘香。

完~ps:個人見解有限,歡迎指正。

相關文章