vue+element-ui使用babel-plugin-component按需載入元件及自定義主題對應的scss樣式

chavesgu發表於2018-06-29

作者的挖坑之旅

作者本人在工作之餘喜歡自己寫一些東西玩玩, 雖然不是資深程式設計師, 不過也還是能夠完成前端頁面+後端介面+服務端部署的整個流程了。

如果不願意看分析過程,可以直接拉到最後看結果。

開始入坑

歡迎來我的Vue技術群交流:Vue887516034

首先前端頁面使用了Vue作為前端開發框架,我相信點這個標題進來看文章的都用過這個框架的吧。。。

  1. 首先肯定是要引入element這個ui元件庫,但是作者是個很講究的人,雖然自己瞎倒騰的頁面沒多少東西,但是有些東西還是要規範處理的↓↓↓
  2. 為了專案整體內容不過於龐大,按需載入是許多第三方的庫和外掛必不可少的,於是使用了官方提供的按需載入外掛babel-plugin-component↓↓↓
  3. 先看看官方的程式碼按需載入
[
    "component",
    {
    "libraryName": "element-ui",
    "styleLibraryName": "theme-chalk"
    }
]
複製程式碼

這是.babelrc配置引數,component是babel外掛的名字,物件是引數。

import Vue from 'vue';
import {
    Dialog,
    Autocomplete,
    Dropdown,
    ...
} from 'element-ui';
Vue.use(Pagination);
Vue.use(Dialog);
Vue.use(Autocomplete);
...
//這裡提個醒
//MessageBox,Message,Notification這三個元件只能掛載Vue原型上呼叫,
//不能使用Vue.use();否則專案執行會預設執行一次,即使沒有使用它們
複製程式碼

這樣就可以愉快的按需載入使用自己想要的元件了,接下來給大家看一下這個按需載入外掛的部分原始碼,看它到底幹了什麼

  1. 位置在node_modules/babel-plugin-component/lib/core.js
var _options = options,
    _options$libDir = _options.libDir,//這是元件所在根目錄下的路徑element-ui/lib/
    libDir = _options$libDir === void 0 ? 'lib' : _options$libDir,
    _options$libraryName = _options.libraryName,//這是ui庫的名字--elementui
    libraryName = _options$libraryName === void 0 ? defaultLibraryName : _options$libraryName,
    _options$style = _options.style,
    style = _options$style === void 0 ? true : _options$style,
    styleLibrary = _options.styleLibrary,//這是引入元件時,所需要引入對應元件樣式的配置物件
    _options$root = _options.root,
    root = _options$root === void 0 ? '' : _options$root,
    _options$camel2Dash = _options.camel2Dash,
    camel2Dash = _options$camel2Dash === void 0 ? true : _options$camel2Dash;
    
    var styleLibraryName = options.styleLibraryName;//這是元件所需樣式的路徑(相對於上面的lib)
    var _root = root;
    var isBaseStyle = true;
    var modulePathTpl;
    var styleRoot;
    var mixin = false;
    var ext = options.ext || '.css';//這是載入樣式的字尾,預設css
複製程式碼

就這一部分程式碼,我們已經知道在執行按需載入時已經配置了對應樣式的載入,所以如果在.babelrc檔案配置過styleLibraryName屬性的,不要在全域性引入element的css樣式了,如果你不在乎打包體積的話,請無視我。

踩坑

  1. 作者當時第一次在看element官網時,就發現了定製主題,很有趣,改個scss變數,整個主題色就變了,來看下官方的程式碼
/* 改變主題色變數 */
$--color-primary: teal;

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

@import "~element-ui/packages/theme-chalk/src/index";//注意此處引入了所有元件的scss樣式
複製程式碼
  1. 看到這裡你們發現什麼了嗎?是的,沒錯,這裡引入了全部的scss,上面我們剛說babel-plugin-component會在按需載入元件時,同時引入對應元件的css樣式,有人會說那這裡就不引入這個index.scss檔案,如果沒有元件的scss,那這個$--color-primary變數會有效嗎? 答案當然是不可能有效的。

  2. 既然我們node_modules裡面有所有元件的scss樣式檔案,我們是不是就可以讓babel-plugin-component在引入元件時,就引入對應的scss檔案呢?答案是完全ojbk的,不然還寫這文章幹嘛。。。

爬坑

  1. 我們回到按需載入外掛的原始碼,之前看到程式碼是配置了載入樣式的部分程式碼的,我們現在看一下具體怎麼載入的(這段程式碼是作者連蒙帶猜的。。有錯請指出)
if (styleLibrary && _typeof(styleLibrary) === 'object') {//這個是樣式的一些配置
  styleLibraryName = styleLibrary.name;
  isBaseStyle = styleLibrary.base;
  modulePathTpl = styleLibrary.path;
  mixin = styleLibrary.mixin;
  styleRoot = styleLibrary.root;
}

if (styleLibraryName) {//是否在.babelrc配置了styleLibraryName
  if (!cachePath[libraryName]) {//是否存在配置好的樣式獲取路徑
    var themeName = styleLibraryName.replace(/^~/, '');
    cachePath[libraryName] = styleLibraryName.indexOf('~') === 0 ?//路徑是否相對於element-ui/lib
    resolve(process.cwd(), themeName) : 
    "".concat(libraryName, "/").concat(libDir, "/").concat(themeName);
  }//如果是相對於lib   組合路徑---element-ui/lib/theme-chalk/   這個目錄下是75個css檔案
  //這裡將這一段路徑儲存在了cachePath[libraryName]  後續會用到

  if (libraryObjs[methodName]) {//作者也沒搞清楚這裡是什麼  不過沒關係,事實證明這裡走了false
    /* istanbul ingore next */
    if (cache[libraryName] === 2) {
      throw Error('[babel-plugin-component] If you are using both' + 'on-demand and importing all, make sure to invoke the' + ' importing all first.');
    }

    if (styleRoot) {//這裡預設是沒有配置的  所有走false
      path = "".concat(cachePath[libraryName]).concat(styleRoot).concat(ext);
    } else {
      path = "".concat(cachePath[libraryName]).concat(_root || '/index').concat(ext);
    }//這裡會預設先載入index.css  因為ext沒設定就會預設css

    cache[libraryName] = 1;
  } else {//走了else
    if (cache[libraryName] !== 1) {//這裡肯定是不等於1,因為上面一行才會賦值1
      /* if set styleLibrary.path(format: [module]/module.css) */
      var parsedMethodName = parseName(methodName, camel2Dash);

      if (modulePathTpl) {
        var modulePath = modulePathTpl.replace(/\[module]/ig, parsedMethodName);
        path = "".concat(cachePath[libraryName], "/").concat(modulePath);
      } else {//這裡走了else 也就是樣式路徑後續為模組名.[ext]
        path = "".concat(cachePath[libraryName], "/").concat(parsedMethodName).concat(ext);
      }//所有這裡的路徑就是element-ui/lib/

      if (mixin && !isExist(path)) {
        path = style === true ? "".concat(_path, "/style").concat(ext) : "".concat(_path, "/").concat(style);
      }

      if (isBaseStyle) {
        addSideEffect(file.path, "".concat(cachePath[libraryName], "/base").concat(ext));
      }

      cache[libraryName] = 2;
    }
  }

  addDefault(file.path, path, {
    nameHint: methodName
  });
} else {
  if (style === true) {
    addSideEffect(file.path, "".concat(path, "/style").concat(ext));
  } else if (style) {
    addSideEffect(file.path, "".concat(path, "/").concat(style));
  }
}
}
複製程式碼
  1. 好的,這就過了一遍了,連蒙帶猜知道是怎麼回事了,我們進入正題,如何變為載入對應元件的scss
//第一步不用說了 先把ext字尾改為scss
[
    "component",
    {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk",
        "ext":".scss"
    }
]
//-------------------
//第二步呢  就是配置路徑  比較重要的
//有一段程式碼需要看的  雖然作者也看不懂,不過大概知道這一段是載入模組的
if (libraryObjs[methodName]) {
  path = "".concat(libraryName, "/").concat(libDir).concat(_root);
    //需要注意這裡的libDir,之前的程式碼我們看到  載入樣式也會基於libDir
    //所以我們無法通過.babelrc的option去修改libDir,
    //那樣的話元件載入就有問題我們不能影響最基本的元件載入
  if (!_root) {
    importAll[path] = true;
  }
} else {
  path = "".concat(libraryName, "/").concat(libDir, "/").concat(parseName(methodName, camel2Dash));
}
//所以我們只能通過修改core.js的原始碼解決,這是我目前的辦法
複製程式碼
  1. 既然我們要修改按需載入對應的scss樣式,那首先先找到檔案位置,這裡我直接說了element-ui/packages/theme-chalk/src/ 和預設載入css一樣75個檔案,哇,檔案數量都一樣,是不是瞬間感覺很放心了呢。
  2. 我們對比一下,
  • 預設的是element-ui/lib/theme-chalk
  • 要修改成element-ui/packages/theme-chalk/src
  1. libDir是不能改的 我們說了 所以看原始碼
//這是上面提到的三目運算
if (!cachePath[libraryName]) {
    var themeName = styleLibraryName.replace(/^~/, '');
    cachePath[libraryName] = styleLibraryName.indexOf('~') === 0 ?
    resolve(process.cwd(), themeName) : 
    "".concat(libraryName, "/").concat(libDir, "/").concat(themeName);
}//我們把這裡------------------------libDir修改為 "packages"

現在路徑從element-ui/lib/theme-chalk--->element-ui/packages/theme-chalk
複製程式碼
  1. 我們再看看,還少了個src
  • element-ui/packages/theme-chalk
  • element-ui/packages/theme-chalk/src
  1. 老規矩,看原始碼
//咳咳,還是那個三目運算
if (!cachePath[libraryName]) {
    var themeName = styleLibraryName.replace(/^~/, '');
    cachePath[libraryName] = styleLibraryName.indexOf('~') === 0 ?
    resolve(process.cwd(), themeName) : 
    "".concat(libraryName, "/").concat("packages", "/").concat(themeName);
}
//我們看packages後面的路徑是個變數themeName
//這個themeName就是styleLibraryName,你懂了嗎,你懂怎麼修改了嗎
複製程式碼
  1. 如下
[
  "component",
  {
    "libraryName": "element-ui",
    "styleLibraryName": "theme-chalk/src",//這裡把theme-chalk-->theme-chalk/src
    "ext":".scss"
  }
]
複製程式碼
  1. 至此,已經完成。快去跑專案玩玩看吧。

總結

.babelrc

"plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk/src",
        "ext":".scss"
      }
    ]
]
複製程式碼

node_modules/babel-plugin-component/lib/core.js

//95-98行左右
if (!cachePath[libraryName]) {
    var themeName = styleLibraryName.replace(/^~/, '');
    cachePath[libraryName] = styleLibraryName.indexOf('~') === 0 ? resolve(process.cwd(), themeName) : "".concat(libraryName, "/").concat("packages", "/").concat(themeName);
}
複製程式碼

由於隨便修改官方提供的外掛原始碼並不合理,作者我fork了官方的npm包,並且修改了對應位置的程式碼,合理的做法是安裝我提供的babel-plugin-component-scss,當不需要scss時,仍可使用官方的外掛

需要注意的是,當使用babel-plugin-component-scss時,babel.config.jsor.babelrc的配置需要如下:

"plugins": [
    [
      "component-scss",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk/src",
        "ext":".scss"
      }
    ]
]
複製程式碼

over

歡迎來我的Vue技術群交流:Vue887516034

如果覺得對你有用,就打賞一下吧。

vue+element-ui使用babel-plugin-component按需載入元件及自定義主題對應的scss樣式 vue+element-ui使用babel-plugin-component按需載入元件及自定義主題對應的scss樣式

相關文章