日更挑战-Vue-cli之构建多模块项目
越不懂的越爱装
大家都同等:IT世界没有难不难,只有是否了解过
挑战目录
什么是Vue-cli多模块打包?
在一个项目中,通过配置达到可以共用公共文件,且打包只属于当前功能的文件的项目包的场景。
Vue-cli多模块打包的用途?
- 比如我比较常用的:离线包模式。单独功能模块打成单独的离线包供安卓、IOS使用。
- 一个项目的不同版本的实现。多模块打包可以做到抽出公共部分专注不同部分。
- 等等
Webpack为什么可以实现多模块打包?
-
(打包?)由于:Vue-cli(使用webpack)会从入口js,通过导入语句自动寻找所依赖的模块进行打包
所以:只要通过不同的入口,执行打包命令就能打出不同的包。
-
(执行?)由于:脚本文件.js文件中可以获取如命令:node 脚本文件.js xxx xxx 类的命令的命令行参数
所以:可以实现通过在package.json文件配置node xxx.js的执行命令并传入参数自动实现配置化打包命令。
-
(配置?)由于:Vue-cli提供了配置参数pages用来配置多页应用。(当然webpack也有自己的一套,这里主要是使用Vue)
所以:可以通过解析命令行参数拼出pages所需配置格式,然后进行打包。
pages多页配置格式如下:
{ //下面最少存在一个包名 "包名1":{ // page 的入口 entry: `src/${包名1}/main.js`, // 模板来源 template: "public/index.html", // 在 dist/`${包名1}.html` 的输出 filename: `${包名1}.html`, / 当使用 title 选项时, // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title> title: 包名1, // 在这个页面中包含的块,默认情况下会包含 // 提取出来的通用 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] }, ... }
-
(执行?) 由于pages是配置在Vue-cli上的。Vue-cli提供了将其搜索配置的包名启动为服务和打包的功能。
所以: 只需要在配置vue.config.js的module.exports = {pages}之前动态配置pages,就可以启动一个或多个服务了。
//通过fs.readdirSync(path.resolve(__dirname, "../src"));读取指定目录的文件夹。拼装出上面所以的pages的配置 const MultiModulesConfig = require("./config/modules.config.js"); let pages = {}; //process.env.NODE_ENV会预先根据命令行参数赋值 //开发环境 启动全部的模块,如果每个包名启动一次。会导致本地出现多个端口服务。 //如果是打包则只打所以的包指定模块名,若模块名为all则表示一次性打包所以 if (process.env.NODE_ENV == "development"||pageName==="all" ) { pages = MultiModulesConfig; } else { pages[pageName] = MultiModulesConfig[pageName]; } module.exports = { pages: pages, }
-
pages配置多模块时,配置的多个模块的入口html会打包到同一个文件夹(module.exports = {outputDir: "dist/front/" + pageName,})下,所有启动的服务需要带具体哪个模块名去访问。当需要打多个文件夹的不同模块包是,需要分享设置pages和outputDir。
由上可知:基于Vue-cli脚手架后,多模块运行服务和多模块打包,简单到只要配置pages参数然后继续正常的步骤就行了。至于是一个或多个模块,就看pages里面配置的是多少模块而已。
多模块打包如何解决路由问题?
- 跨模块路由守卫拦截
1. 定义路由拦截。(即定义router.beforeEach(to, from, next) => {...}内部的参数)。
const loginIntercept=(to, from, next) => {
// ...
}
2. 分别将上述定义的拦截注册到模块内核模块外路由拦截中。(多个遍历执行多次即可)
router.beforeEach(loginIntercept); //模块内部拦截
MyRoutreIntercept.beforeCrossModule(interceptor); // 跨模块拦截
3. 通过Promise定义一个拦截器执行队列,用于串行执行所有拦截器
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. 区分跨模块跳转和正常跳转(这里也可以通过额外传是否跨模块参数确定)。对跨模块跳转进行拦截处理。
// 组装跨模块目标页面的数据结构
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;
//调用上面的串行执行拦截器方法
return this.syncInterceptorSeries(
to,
from,
this.crossModuleInterceptors
).then(
() => {
// 执行完队列未发生拦截行为
return { granted: true };
},
reject => {
// 发生拦截行为,此处reject为拦截器队列函数中传入next()函数的参数
if (reject === false) {
return { granted: false };
}
return { granted: false, redirect: true, params: reject };
}
);
- 跨模块路由跳转
push:跳转前调用上面的拦截,模块内和跨模块区分处理。
import * as _ from "lodash";
push(params) {
/** 跨模块跳转 */
if (_(params).get("crossModule")) {
//跳转前调用上面的拦截
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;
}
// 模块内
this.router.push(params);
}
//跨模块的跳转方法,fullPath参数通过encodeURIComponent处理后的拼上参数的地址
openFullPath(fullPath) {
window.location.href = fullPath;
}
replace: 和push同样处理,不同在于openFullPath方法。
openFullPath(fullPath) {
window.location.replace(fullPath);
}
openInNewWindow:打开新的页面
由于项目由手机和网站项目之分。所以该部分打开过程有如下判断
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到同一个地址报错在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);
};
- 跨模块数据传递
push,replace可以通过上面举例的是通过拼接URl的方式传递,
另外还可以自己维护Storage本地存储(略)
back的传参不太一致:(在被返回的页面上添加onBack方法,当返回时会自动回调并传递参数)
export const onBackGoMixinListener = function (Vue: any) {
Vue.mixin({
created() {
if (this.$options.name === this.$route.name) { //当前页面组件
localStorage.removeItem(RouteBackStorageKey)
}
},
beforeRouteEnter(to: any, from: any, next: any) {
next(((vue: any) => {
//当前页面组件
if (this.$options.name === this.$route.name) {
const isReturn = localStorage.getItem(RouteBackStorageKey);
//当前页面组件返回事件
if (isReturn) {
if (vue.onBack) {
//调用当前页面组件返回事件
vue.onBack(to, from, JSON.parse(isReturn || ""))
}
}
}
}
));
}
})
};
多模块打包如何解决devServer问题?
这个其实应该不会有太大问题,最后就是参数的配置不同而已。在执行node 自定义.js 脚本的时候,根据命令行参数或默认的命令行参数将要用到的数据都赋值给process.env.xxxx。用于全局获取使用。
其他的问题应该就是代理的配置问题,不属于该讨论范围内
多模块打包如何解决Vuex.Store问题?
这里使用vuex-persistedstate插件,解决多模块和刷新时VueX数据丢失的问题。
-
安装
npm install --save vuex-persistedstate
-
使用:store入口js文件引入并进行配置
import createPersistedState from "vuex-persistedstate";
const store = new Vuex.Store({
plugins: [createPersistedState()]
}); -
修改默认配置
默认使用localStorage存储,存储键名key是“vuex
参数 描述 key 存储数据的键名。(默认:vuex) paths 部分路径可部分保留状态的数组。如果没有给出路径,则将保留完整状态。如果给出一个空数组,则不会保留任何状态。必须使用点符号指定路径。如果使用模块,请包括模块名称(默认:[]) reducer 将根据给定路径调用以减少状态持久化的函数 storage 指定存储数据的方式。默认为localStorage ,也可以设置 sessionStorage getState 用来重新补充先前持久状态的功能,默认使用:storage定义获取的方式 setState 用以保持给定状态的函数。默认使用:storage定义的设置值方式 filter 一个将被调用以过滤setState最终将在存储中筛选过滤的函数。默认为() => true。 详细配置请参考源码 vuex-persistedstate
上面简单的提一下多模块打包项目的的几个方面,其他的以后在写吧