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

做者的挖坑之旅

做者本人在工做之餘喜歡本身寫一些東西玩玩, 雖然不是資深程序員, 不過也仍是可以完成前端頁面+後端接口+服務端部署的整個流程了。javascript

若是不肯意看分析過程,能夠直接拉到最後看結果。

開始入坑css

歡迎來個人Vue技術羣交流:Vue887516034前端

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

  1. 首先確定是要引入element這個ui組件庫,可是做者是個很講究的人,雖然本身瞎倒騰的頁面沒多少東西,可是有些東西仍是要規範處理的↓↓↓
  2. 爲了項目總體內容不過於龐大,按需加載是許多第三方的庫和插件必不可少的,因而使用了官方提供的按需加載插件babel-plugin-component↓↓↓
  3. 先看看官方的代碼按需加載
[
    "component",
    {
    "libraryName": "element-ui",
    "styleLibraryName": "theme-chalk"
    }
]
複製代碼

這是.babelrc配置參數,component是babel插件的名字,對象是參數。vue

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();不然項目運行會默認執行一次,即便沒有使用它們
複製代碼

這樣就能夠愉快的按需加載使用本身想要的組件了,接下來給你們看一下這個按需加載插件的部分源碼,看它到底幹了什麼java

  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樣式了,若是你不在意打包體積的話,請無視我。node

踩坑git

  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的,否則還寫這文章幹嗎。。。github

爬坑npm

  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

若是以爲對你有用,就打賞一下吧。

相關文章
相關標籤/搜索