越不懂的越愛裝
大家都同等:IT世界沒有難不難,只有是否了解過
挑戰(zhàn)目錄
什么是Vue-cli多模塊打包?
在一個(gè)項(xiàng)目中,通過配置達(dá)到可以共用公共文件,且打包只屬于當(dāng)前功能的文件的項(xiàng)目包的場景。
Vue-cli多模塊打包的用途?
- 比如我比較常用的:離線包模式。單獨(dú)功能模塊打成單獨(dú)的離線包供安卓、IOS使用。
- 一個(gè)項(xiàng)目的不同版本的實(shí)現(xiàn)。多模塊打包可以做到抽出公共部分專注不同部分。
- 等等
Webpack為什么可以實(shí)現(xiàn)多模塊打包?
-
(打包?)由于:Vue-cli(使用webpack)會(huì)從入口js,通過導(dǎo)入語句自動(dòng)尋找所依賴的模塊進(jìn)行打包
所以:只要通過不同的入口,執(zhí)行打包命令就能打出不同的包。
-
(執(zhí)行?)由于:腳本文件.js文件中可以獲取如命令:node 腳本文件.js xxx xxx 類的命令的命令行參數(shù)
所以:可以實(shí)現(xiàn)通過在package.json文件配置node xxx.js的執(zhí)行命令并傳入?yún)?shù)自動(dòng)實(shí)現(xiàn)配置化打包命令。
-
(配置?)由于:Vue-cli提供了配置參數(shù)pages用來配置多頁應(yīng)用。(當(dāng)然webpack也有自己的一套,這里主要是使用Vue)
所以:可以通過解析命令行參數(shù)拼出pages所需配置格式,然后進(jìn)行打包。
pages多頁配置格式如下:
{ //下面最少存在一個(gè)包名 "包名1":{ // page 的入口 entry: `src/${包名1}/main.js`, // 模板來源 template: "public/index.html", // 在 dist/`${包名1}.html` 的輸出 filename: `${包名1}.html`, / 當(dāng)使用 title 選項(xiàng)時(shí), // template 中的 title 標(biāo)簽需要是 <title><%= htmlWebpackPlugin.options.title %></title> title: 包名1, // 在這個(gè)頁面中包含的塊,默認(rèn)情況下會(huì)包含 // 提取出來的通用 chunk 和 vendor chunk chunks: ["chunk-vendors", "chunk-common", 包名1] }, "包名2":{ entry: `src/${包名2}/main.js`, template: "public/index.html", filename: `${包名2}.html`, title: 包名2, chunks: ["chunk-vendors", "chunk-common", 包名2] }, ... } -
(執(zhí)行?) 由于pages是配置在Vue-cli上的。Vue-cli提供了將其搜索配置的包名啟動(dòng)為服務(wù)和打包的功能。
所以: 只需要在配置vue.config.js的module.exports = {pages}之前動(dòng)態(tài)配置pages,就可以啟動(dòng)一個(gè)或多個(gè)服務(wù)了。
//通過fs.readdirSync(path.resolve(__dirname, "../src"));讀取指定目錄的文件夾。拼裝出上面所以的pages的配置 const MultiModulesConfig = require("./config/modules.config.js"); let pages = {}; //process.env.NODE_ENV會(huì)預(yù)先根據(jù)命令行參數(shù)賦值 //開發(fā)環(huán)境 啟動(dòng)全部的模塊,如果每個(gè)包名啟動(dòng)一次。會(huì)導(dǎo)致本地出現(xiàn)多個(gè)端口服務(wù)。 //如果是打包則只打所以的包指定模塊名,若模塊名為all則表示一次性打包所以 if (process.env.NODE_ENV == "development"||pageName==="all" ) { pages = MultiModulesConfig; } else { pages[pageName] = MultiModulesConfig[pageName]; } module.exports = { pages: pages, } pages配置多模塊時(shí),配置的多個(gè)模塊的入口html會(huì)打包到同一個(gè)文件夾(module.exports = {outputDir: "dist/front/" + pageName,})下,所有啟動(dòng)的服務(wù)需要帶具體哪個(gè)模塊名去訪問。當(dāng)需要打多個(gè)文件夾的不同模塊包是,需要分享設(shè)置pages和outputDir。
由上可知:基于Vue-cli腳手架后,多模塊運(yùn)行服務(wù)和多模塊打包,簡單到只要配置pages參數(shù)然后繼續(xù)正常的步驟就行了。至于是一個(gè)或多個(gè)模塊,就看pages里面配置的是多少模塊而已。
多模塊打包如何解決路由問題?
- 跨模塊路由守衛(wèi)攔截
1. 定義路由攔截。(即定義router.beforeEach(to, from, next) => {...}內(nèi)部的參數(shù))。
const loginIntercept=(to, from, next) => {
// ...
}
2. 分別將上述定義的攔截注冊到模塊內(nèi)核模塊外路由攔截中。(多個(gè)遍歷執(zhí)行多次即可)
router.beforeEach(loginIntercept); //模塊內(nèi)部攔截
MyRoutreIntercept.beforeCrossModule(interceptor); // 跨模塊攔截
3. 通過Promise定義一個(gè)攔截器執(zhí)行隊(duì)列,用于串行執(zhí)行所有攔截器
import { clone,isUndefined } from "lodash"
syncInterceptorSeries = function(to, from, interceptors) {
return new Promise((resolve, reject) => {
const cloneArr = clone(interceptors);
let exe = function(x) {
if (!isUndefined(x)) {
reject(x);
}
let fn = cloneArr.shift();
if (!fn) {
resolve();
return;
}
fn(to, from, next);
};
exe();
});
};
4. 區(qū)分跨模塊跳轉(zhuǎn)和正常跳轉(zhuǎn)(這里也可以通過額外傳是否跨模塊參數(shù)確定)。對(duì)跨模塊跳轉(zhuǎn)進(jìn)行攔截處理。
// 組裝跨模塊目標(biāo)頁面的數(shù)據(jù)結(jié)構(gòu)
const to = {
fullPath: res.path || "",
hash: "",
matched: [{}],
meta: res.meta || {},
name: res.name || "",
params: param.param || {},
path: res.path,
query: {
...param.query,
crossModule: true,
moduleName: param.moduleName
},
crossModule: true,
moduleName: param.moduleName,
fullUrl: this.getCrossModulePath(param)
};
const from = this.router.currentRoute;
//調(diào)用上面的串行執(zhí)行攔截器方法
return this.syncInterceptorSeries(
to,
from,
this.crossModuleInterceptors
).then(
() => {
// 執(zhí)行完隊(duì)列未發(fā)生攔截行為
return { granted: true };
},
reject => {
// 發(fā)生攔截行為,此處reject為攔截器隊(duì)列函數(shù)中傳入next()函數(shù)的參數(shù)
if (reject === false) {
return { granted: false };
}
return { granted: false, redirect: true, params: reject };
}
);
- 跨模塊路由跳轉(zhuǎn)
push:跳轉(zhuǎn)前調(diào)用上面的攔截,模塊內(nèi)和跨模塊區(qū)分處理。
import * as _ from "lodash";
push(params) {
/** 跨模塊跳轉(zhuǎn) */
if (_(params).get("crossModule")) {
//跳轉(zhuǎn)前調(diào)用上面的攔截
this.beforeCrossModuleAction(params).then(res => {
if (res.granted) {
this.openFullPath(this.getCrossModulePath(params));
return;
}
if (res.redirect) {
// 重定向
if (
res.params.path === params.path ||
res.params.name === params.name
) {
// 重定向頁面為原始頁面
this.openFullPath(this.getCrossModulePath(params));
return;
}
this.push(res.params);
return;
}
});
return;
}
// 模塊內(nèi)
this.router.push(params);
}
//跨模塊的跳轉(zhuǎn)方法,fullPath參數(shù)通過encodeURIComponent處理后的拼上參數(shù)的地址
openFullPath(fullPath) {
window.location.href = fullPath;
}
replace: 和push同樣處理,不同在于openFullPath方法。
openFullPath(fullPath) {
window.location.replace(fullPath);
}
openInNewWindow:打開新的頁面
由于項(xiàng)目由手機(jī)和網(wǎng)站項(xiàng)目之分。所以該部分打開過程有如下判斷
openInNewWindow(fullPath) {
if (Native.isApp()) {
Native.openNewWindow({
url: fullPath
});
} else {
// window.open(fullPath, "_blank");
window.location.href = fullPath;
}
}
back|go(n):
back(params){
localStorage.setItem(RouteBackStorageKey,Json.stringify(params));
window.history.back();
}
PS:
為了防止push到同一個(gè)地址報(bào)錯(cuò)在main.js里面重寫push方法。
Router.prototype.push = function push(location, onResolve, onReject) {
if (onResolve || onReject){
return originalPush.call(this, location, onResolve, onReject);
}
return originalPush.call(this, location).catch(err => err);
};
- 跨模塊數(shù)據(jù)傳遞
push,replace可以通過上面舉例的是通過拼接URl的方式傳遞,
另外還可以自己維護(hù)Storage本地存儲(chǔ)(略)
back的傳參不太一致:(在被返回的頁面上添加onBack方法,當(dāng)返回時(shí)會(huì)自動(dòng)回調(diào)并傳遞參數(shù))
export const onBackGoMixinListener = function (Vue: any) {
Vue.mixin({
created() {
if (this.$options.name === this.$route.name) { //當(dāng)前頁面組件
localStorage.removeItem(RouteBackStorageKey)
}
},
beforeRouteEnter(to: any, from: any, next: any) {
next(((vue: any) => {
//當(dāng)前頁面組件
if (this.$options.name === this.$route.name) {
const isReturn = localStorage.getItem(RouteBackStorageKey);
//當(dāng)前頁面組件返回事件
if (isReturn) {
if (vue.onBack) {
//調(diào)用當(dāng)前頁面組件返回事件
vue.onBack(to, from, JSON.parse(isReturn || ""))
}
}
}
}
));
}
})
};
多模塊打包如何解決devServer問題?
這個(gè)其實(shí)應(yīng)該不會(huì)有太大問題,最后就是參數(shù)的配置不同而已。在執(zhí)行node 自定義.js 腳本的時(shí)候,根據(jù)命令行參數(shù)或默認(rèn)的命令行參數(shù)將要用到的數(shù)據(jù)都賦值給process.env.xxxx。用于全局獲取使用。
其他的問題應(yīng)該就是代理的配置問題,不屬于該討論范圍內(nèi)
多模塊打包如何解決Vuex.Store問題?
這里使用vuex-persistedstate插件,解決多模塊和刷新時(shí)VueX數(shù)據(jù)丟失的問題。
-
安裝
npm install --save vuex-persistedstate
-
使用:store入口js文件引入并進(jìn)行配置
import createPersistedState from "vuex-persistedstate";
const store = new Vuex.Store({
plugins: [createPersistedState()]
}); -
修改默認(rèn)配置
默認(rèn)使用localStorage存儲(chǔ),存儲(chǔ)鍵名key是“vuex
參數(shù) 描述 key 存儲(chǔ)數(shù)據(jù)的鍵名。(默認(rèn):vuex) paths 部分路徑可部分保留狀態(tài)的數(shù)組。如果沒有給出路徑,則將保留完整狀態(tài)。如果給出一個(gè)空數(shù)組,則不會(huì)保留任何狀態(tài)。必須使用點(diǎn)符號(hào)指定路徑。如果使用模塊,請(qǐng)包括模塊名稱(默認(rèn):[]) reducer 將根據(jù)給定路徑調(diào)用以減少狀態(tài)持久化的函數(shù) storage 指定存儲(chǔ)數(shù)據(jù)的方式。默認(rèn)為localStorage ,也可以設(shè)置 sessionStorage getState 用來重新補(bǔ)充先前持久狀態(tài)的功能,默認(rèn)使用:storage定義獲取的方式 setState 用以保持給定狀態(tài)的函數(shù)。默認(rèn)使用:storage定義的設(shè)置值方式 filter 一個(gè)將被調(diào)用以過濾setState最終將在存儲(chǔ)中篩選過濾的函數(shù)。默認(rèn)為() => true。 詳細(xì)配置請(qǐng)參考源碼 vuex-persistedstate
上面簡單的提一下多模塊打包項(xiàng)目的的幾個(gè)方面,其他的以后在寫吧