最近使用ElementUI做項目的時候用Babel的插件babel-plugin-component做按需加載,使得組件打包的JS和CSS包體積大大縮小,加載速度也大大提升,所有想模仿做一個組件庫也來做下按需加載。
首先用Vue CLI 3.0新建一個項目
vue create bes-ui
注意的是cli3的腳手架用的Babel7的配置,只有babel.config.js文件,所以要自己添加.babelrc文件。
項目結構
新建項目之后,可以按照自己的想法建文件結構,也可以按照babel-plugin-component官方文檔的目錄構建:
上面有兩種方式,前一種是組件單一主題方式,后一種是主題庫的方式,大家可以自己選擇,下圖是bes-ui的目錄結構:
這里大概介紹下文件用途:
-dist:組件庫測試項目打包文件
-examples:組件庫的測試項目
-lib:組件庫的源碼
-local:組件庫的國際化文件
-package:組件庫打包后的壓縮的js,css文件
-static:組件庫的靜態資源(css主題)
其他文件就不介紹了,都是腳手架生成的文件
組件介紹
這里組件庫寫了兩個樣例:component1,component2 。 每個組件都添加了自己的初始化install方法(用於按需加載時候獨立使用),install方面里面加個日志,方便后面看看組件加載記錄。
最外面添加整體加載用的index.js,用於一次性加載所有項目
最后就是組件的樣式文件了(這里的base.css和index.css都是必須的,官方的api有有標注):
大家注意的一點是組件包名一定要叫 “ lib ” ,樣式文件的路徑和名字大家可以隨意,到時候在配置文件里面引用對應的路徑就行,我這里叫static,里面一定要加 base.css和index.css,這都是babel-plugin-component的API里面標注了,當然大家也可以看babel-plugin-component的源碼core.js【位置在node_modules/babel-plugin-component/lib/core.js】里面有涉及到獲取對應文件:
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));
}
}
}
轉回正題,文件創建好了之后,就可以發包到npm了,這里提供了兩種方式:1是將組件庫打包壓縮成css和js,暴露出去(這種方式無法做按需,因為所以代碼壓縮在一起了);2是將lib下的index暴露出去(這種方式可以做按需加載);如下(package文件):
組件使用
組件放上去后,就可以在項目里面使用了,這里用vue create添加了一個example項目:
項目加載bes-ui:
項目使用bes-ui(這里只加載了component2):
啟動項目后,觀察加載的項目日志:
這里日志打印的只加載了component2,如果還不放心的話可以看下style標簽是不是只有component2的css:
這里確實只有component2的樣式,這里有個問題就是樣式加載了兩遍 這里主要是組件內部加載了一次樣式,babel-plugin-component的按需又加載了一次,這里主要是為了測試兩種不同方式,(組件內的加載樣式是給壓縮js和css用的)如果做按需的話就可以去掉了:
這里貼下Babel的配置:
總結
最后可以打個測試項目包,看下里面確實沒有其他組件的代碼和樣式,這里就不貼圖了,大家可以自己嘗試,總結下這里的bes-ui其實就是模仿ElementUI的一個簡易的組件庫(雖然目前只加了按需加載,后續可以把國際化和主題加上),項目里面的一些配置和文件都大同小異,如果有興趣構建自己的vue組件庫的時候,bes-ui是個不錯的模版。
GitHub項目地址:https://github.com/BothEyes1993/bes-ui