前端性能優化中有一個關鍵點就是:減小打包體積javascript
目前經常使用的按需打包組件的方式有以下幾種:html
手工import
全部須要打包的組件;目前大部分前端 ui 庫都使用的這種方式,在入口文件處導出全部的組件。前端
使用 Lerna 將每一個庫單獨打包,單獨發佈:vue-next
等都使用此種方式,在宿主工程手動引入所需packagevue
那麼還有沒有其餘的方式,作到按需打包呢?答案是有的java
現有以下一個工程目錄:webpack
├─App.vue
├─assets
│ └─logo.png
├─components
│ └─HelloWorld.vue
├─main.js
├─packages //存放全部組件
│ ├─button
│ │ ├─button-prop.vue //設計態組件的vue
│ │ └─button.vue //運行時組件的vue
│ └─input
│ ├─input-prop.vue
│ └─input.vue
├─require.js //使用webpack的require.context自動讀取packages目錄下的組件
└─widget-render.js //運行態組件打包
複製代碼
個人工程目錄下全部的組件都存放在packages
目錄下。每個組件又分爲:git
如今個人要求是:github
使用
require.context
自動讀取packages
目錄下的運行態組件,並打包發佈。不容許打包設計態組件web
// widget-render.js
import button from "./packages/button.vue";
import input from "./packages/input.vue";
...
複製代碼
顯然這不符合個人要求:使用 require.context
自動讀取vue-cli
使用 lerna 管理,此處不作詳情贅述;lerna 管理會將 packages 目錄下的組件單獨發佈成一個包。可是會讓用戶在用戶側手動import
多個組件(固然您也能夠說 本身建立一個 index.js 作全局引入)。可是這彷佛也不太符合咱們的要求。
首先咱們先查看一下widget-render.js
和require.js
的內容: 其主要工做就是使用 require.context 去加載全部的 vue 組件,並同步註冊發佈
//require.js
import endsWith from "lodash/endsWith";
/** * 加載組件和組件的屬性面板 * @param {*} ignoreProps */
export default function(ignoreProps = false) {
let components = [],
propComponents = [];
const requireAll = context => context.keys().map(context);
const component = require.context("./packages", true, /\.vue$/);
requireAll(component).forEach(file => {
let item = file.default;
if (endsWith(item.name, "prop") && !ignoreProps) {
propComponents.push({
key: item.name,
value: item
});
} else if (!endsWith(item.name, "prop") && item.name) {
components.push({
key: item.name,
value: item
});
}
});
return { components, propComponents };
}
複製代碼
import getAllWidgets from "./require";
// 若是你只須要打包組件的話,而忽略屬性面板
const widgets = getAllWidgets(true).components;
export { widgets };
export default {
widgets
};
複製代碼
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"lib": "vue-cli-service build --mode lib --target lib src/widget-render.js -name render.js --dest dist-lib"
}
複製代碼
運行
yarn lib --report
複製代碼
其打包的結果爲:是包含了prop.vue文件的
展現效果爲:
疑問?
爲何,明明打包出來的組件沒有
prop
,可是爲什麼 report 文件中卻存在prop.vue
相關文件呢?
答案
由於 webpack 的
require.context
會自動去加載packages
目錄下的指定文件,因爲上面寫的匹配規則爲.vue\。因此也會加載 prop 結尾的文件。並打包到 lib 中。
上面講到使用默認的 require.context 會將匹配到的文件都統一打包的,那麼確定有朋友說,我把匹配規則寫成匹配非 prop 結尾的文件。不就能夠了嗎? 歡迎嘗試。
解決辦法:
既然以 prop 結尾的文件被打包,那麼咱們是否是能夠利用 webpack 自帶的 externals 去排除掉呢?同時將全部以 prop 結尾的文件都設置別名。這樣打包便可。
//vue.config.js
module.exports = {
productionSourceMap: false,
configureWebpack: config => {
if (process.env.BUILD_TARGET === "lib") {
// 將全部匹配的文件都設置一個empty-widget的別名
config.resolve.alias[/^(\.\/).+(-prop.vue)/] = "empty-widget";
// 而後利用externals的function模式,對匹配到的文件設置external
config.externals = [].concat(config.externals || [], [
function(context, request, callback) {
if (/^(\.\/).+(-prop.vue)/.test(request)) {
return callback(null, "empty-widget");
}
callback();
}
]);
}
}
};
複製代碼
其打包的結果爲:不存在prop文件了
展現效果爲:
最後只須要在宿主工程引入一個yarn add empty-widget
便可。empty-widget
的體積 19B,能夠忽略不計哦。
最後: 其核心思想就是使用佔位符的方式,將不須要的打包組件進行佔位,從而減小打包體積