歡迎您光臨本站 註冊首頁

將Vue組件庫更換為按需加載的方法步驟

←手機掃碼閱讀     madbeef @ 2020-05-06 , reply:0

本文介紹了將Vue組件庫更換為按需加載的方法步驟,分享給大家,具體如下:
按需加載DEMO倉庫地址
背景
我司前端團隊擁有一套支撐公司業務系統的UI組件庫,經過多次迭代後,組件庫體積非常龐大。
組件庫依賴在npm上管理,組件庫以項目根目錄的 index.js 作為出口導出,文件中導入了項目中所有的組件,並提供組件安裝方法。
index.js
import Button from "./button"; import Table from "./table"; import MusicPlayer from "./musicPlayer"; import utils from "../utils" import * as directive from "../directive"; import * as filters from "../filters"; const components = { Button, Table, MusicPlayer } const install = (Vue) => { Object.keys(components).forEach(component => Vue.use(component)); // 此處繼續完成一些服務的掛載 } if (typeof window !== 'undefined' && window.Vue) { install(Vue, true); } export default { install, ...components }
組件庫並不導出編譯完成後的依賴文件,業務系統使用時,安裝依賴並導入,就能註冊組件。
import JRUI from 'jr-ui'; Vue.use(JRUI);
組件庫的編譯是交由業務系統的編譯服務順帶編譯的。
即組件庫項目本身不會編譯,僅作為組件導出。node_module 就像一個免費的雲盤,用於存儲組件庫代碼。
因為經業務系統編譯,在業務系統中。組件庫代碼能夠和本地文件一樣,直接調試。而且非常簡單粗暴,並不需要做一些依賴導出的額外配置。
但也存在缺點
組件庫中無法使用更為特殊的代碼
vue-cli會靜態編譯在 node_module 引用的 .vue 文件,但不會編譯 node_module 中的其他文件,一旦組件庫代碼存在特殊的語法擴展(JSX),或者特殊的語言(TypeScript)。此時項目啟動會運行失敗。
組件庫中使用 webpack 的特殊變量將不起效
組件庫中的 webpack 配置不會被業務系統去執行,所以組件庫中的路徑別名等屬性無法使用
組件庫依賴每次都是全量加載
index.js 本身就是全量的組件導入,所以即使業務系統只使用了部分組件, index.js 也會將所有的組件文件(圖片資源,依賴)都打包進去,依賴體積總是全量大小的。
業務系統並不存在只使用一兩個組件的情況,每個業務系統都需要絕大部分組件。
幾乎每個項目都會使用比如 按鈕,輸入框,下拉選項,表格 等常見基礎組件。
只有部分組件僅在少數特殊業務線使用,例如 富文本編輯器,音樂播放器。
組件分類
為了解決上述問題,及完成按需引入的效果。提供兩種組件導出方式,全量導出,基礎導出。
將組件導出分為兩種類型。基礎組件,按需引入組件。
按需引入組件的評定標準為:
較少業務系統使用
組件中包含體積較大或資源文件較多的第三方依賴
未被其他組件內部引用
全量導出模式導出全部組件,基礎導出僅導出基礎組件。在需要使用按需引入組件時,需要自行引入對應組件。
調整為按需引入
參考 element-ui 的導出方案,組件庫導出的組件依賴,要提供每個組件單獨打包的依賴文件。
全量導出 index.js 文件無需改動,在 index.js 同級目錄增加新文件 base.js,用於導出基礎組件。
base.js
import Button from "./Button"; import Table from "./table"; const components = { Button, Table } const install = (Vue) => { Object.keys(components).forEach(component => Vue.use(component)); } export default { install, ...components }
修改組件庫腳手架工具,增加額外打包配置。用於編譯組件文件,輸出編譯後的依賴。
vue.config.js
const devConfig = require('./build/config.dev'); const buildConfig = require('./build/config.build'); module.exports = process.env.NODE_ENV === 'development' ? devConfig : buildConfig;
config.build.js
const fs = require('fs'); const path = require('path'); const join = path.join; // 獲取基於當前路徑的目標文件 const resolve = (dir) => path.join(__dirname, '../', dir); /** * @desc 大寫轉橫槓 * @param {*} str */ function upperCasetoLine(str) { let temp = str.replace(/[A-Z]/g, function (match) { return "-" + match.toLowerCase(); }); if (temp.slice(0, 1) === '-') { temp = temp.slice(1); } return temp; } /** * @desc 獲取組件入口 * @param {String} path */ function getComponentEntries(path) { let files = fs.readdirSync(resolve(path)); const componentEntries = files.reduce((fileObj, item) => { // 文件路徑 const itemPath = join(path, item); // 在文件夾中 const isDir = fs.statSync(itemPath).isDirectory(); const [name, suffix] = item.split('.'); // 文件中的入口文件 if (isDir) { fileObj[upperCasetoLine(item)] = resolve(join(itemPath, 'index.js')) } // 文件夾外的入口文件 else if (suffix === "js") { fileObj[name] = resolve(`${itemPath}`); } return fileObj }, {}); return componentEntries; } const buildConfig = { // 輸出文件目錄 outputDir: resolve('lib'), // webpack配置 configureWebpack: { // 入口文件 entry: getComponentEntries('src/components'), // 輸出配置 output: { // 文件名稱 filename: '[name]/index.js', // 構建依賴類型 libraryTarget: 'umd', // 庫中被導出的項 libraryExport: 'default', // 引用時的依賴名 library: 'jr-ui', } }, css: { sourceMap: true, extract: { filename: '[name]/style.css' } }, chainWebpack: config => { config.resolve.alias .set("@", resolve("src")) .set("@assets", resolve("src/assets")) .set("@images", resolve("src/assets/images")) .set("@themes", resolve("src/themes")) .set("@views", resolve("src/views")) .set("@utils", resolve("src/utils")) .set("@mixins", resolve("src/mixins")) .set("jr-ui", resolve("src/components/index.js")); } } module.exports = buildConfig;
此時我們的 npm run build 命令,執行的便是以上這段 webpack 配置。
配置中,會尋找組件目錄的所有入口文件。對每個入口文件根據設置進行編譯輸出到指定路徑。
configureWebpack: { // 入口文件 entry: getComponentEntries('src/components'), // 輸出配置 output: { // 文件名稱 filename: '[name]/index.js', // 輸出依賴類型 libraryTarget: 'umd', // 庫中被導出的項 libraryExport: 'default', // 引用時的依賴名 library: 'jr-ui', } }, css: { sourceMap: true, extract: { filename: '[name]/style.css' } }
function getComponentEntries(path) { let files = fs.readdirSync(resolve(path)); const componentEntries = files.reduce((fileObj, item) => { // 文件路徑 const itemPath = join(path, item); // 在文件夾中 const isDir = fs.statSync(itemPath).isDirectory(); const [name, suffix] = item.split('.'); // 文件中的入口文件 if (isDir) { fileObj[upperCasetoLine(item)] = resolve(join(itemPath, 'index.js')) } // 文件夾外的入口文件 else if (suffix === "js") { fileObj[name] = resolve(`${itemPath}`); } return fileObj; }, {}); return componentEntries; }
項目中的組件目錄為如下,配置將會將每個組件打包編譯導出到 lib 中
components 

組件文件目錄 

│ │― button 

│ │― button.vue button組件 

│ └─ index.js button組件導出文件 

│ │― input │ │― input.vue input組件 

│ └─ index.js input組件導出文件 

│ │― musicPlayer 

│ │― musicPlayer.vue musicPlayer組件 

│ └─ index.js musicPlayer組件導出文件 

│ │ base.js 基礎組件的導出文件 

└─ index.js 所有組件的導出文件 lib 編譯後的文件目錄 

│ │― button 

│ │― style.css button組件依賴樣式 

│ └─ index.js button組件依賴文件 

│ │― input 

│ │― style.css input組件依賴樣式 

│ └─ index.js input組件依賴文件 

│ │― music-player 

│ │― style.css musicPlayer組件依賴樣式 

│ └─ index.js musicPlayer組件依賴文件 

│ │― base 

│ │― style.css 基礎組件依賴樣式 

│ └─ index.js 基礎組件依賴文件 

│ └─ index 

│― style.css 所有組件依賴樣式 

└─ index.js 所有組件依賴文件
獲取組件全部入口時,對入口名稱做駝峰轉橫槓處理 upperCasetoLine,是因為 babel-plugin-import 在按需引入時,如組件名稱為駝峰命名,路徑會轉換為橫槓分隔。
例如業務系統引入
import { MusicPlayer } from "jr-ui" // 轉化為 var MusicPlayer = require('jr-ui/lib/music-player'); require('jr-ui/lib/music-player/style.css');
因為組件庫命名約定,組件文件夾命名大小寫並不以橫槓隔開。但為了讓 babel-plugin-import 正確運行,所以此處對每個文件的入口文件名稱做了轉換處理。
如不經過方法轉換名稱,也可以配置 babel.config.js 中的plugin-import配置 camel2DashComponentName 為 false,來禁用名稱轉換。
babel-plugin-import路徑命名issue
業務系統使用時
全量導出默認導出全部組件
// 全量導出 import JRUI from "jr-ui"; import "jr-ui/lib/index/index.css"; Vue.use(JRUI);
基礎導出僅導出基礎組件,如需要使用額外組件,需要安裝 babel-plugin-import 插件且配置 babel.config.js 來完成導入語句的轉換
npm i babel-plugin-import -D
業務系統――babel.config.js配置
module.exports = { presets: ["@vue/app", ["@babel/preset-env", { "modules": false }]], plugins: [ [ "import", { "libraryName": "jr-ui", "style": (name) => { return `${name}/style.css`; } } ] ] }
基礎導出
import JRUI_base from "jr-ui/lib/base"; import "jr-ui/lib/base/index.css"; Vue.use(JRUI_base); // 按需使用額外引入的組件 import { MusicPlayer } from "jr-ui"; Vue.use(MusicPlayer);
業務系統中調試組件庫代碼
如果仍然想調試組件庫代碼,在引入組件時,直接引入組件庫依賴內的 components 下的組件導出文件並覆蓋安裝。就能調試目標組件。
import button from "jr-ui/src/components/button"; Vue.use(button);
優化效果
在組件庫較大的情況下,優化效果非常明顯。在使用基礎組件時,體積小了一兆。而且還減少了很多組件內不必要的第三方依賴文件資源。
案例倉庫地址,如有疑問和錯誤的地方,歡迎大家提問或指出。
祝你有個快樂的勞動節假期 :)
Have a nice day.
參考資料
vue-cli執行解析
babel-plugin-import


[madbeef ] 將Vue組件庫更換為按需加載的方法步驟已經有268次圍觀

http://coctec.com/docs/vue-js/show-post-233121.html