日更挑战-Vue-cli之构建多模块项目

2021-05-20  本文已影响0人  愿你我皆是黑马

越不懂的越爱装
大家都同等:IT世界没有难不难,只有是否了解过

挑战目录

什么是Vue-cli多模块打包?

在一个项目中,通过配置达到可以共用公共文件,且打包只属于当前功能的文件的项目包的场景。


Vue-cli多模块打包的用途?

  1. 比如我比较常用的:离线包模式。单独功能模块打成单独的离线包供安卓、IOS使用。
  2. 一个项目的不同版本的实现。多模块打包可以做到抽出公共部分专注不同部分。
  3. 等等

Webpack为什么可以实现多模块打包?

  1. (打包?)由于:Vue-cli(使用webpack)会从入口js,通过导入语句自动寻找所依赖的模块进行打包

    所以:只要通过不同的入口,执行打包命令就能打出不同的包。

  2. (执行?)由于:脚本文件.js文件中可以获取如命令:node 脚本文件.js xxx xxx 类的命令的命令行参数

    所以:可以实现通过在package.json文件配置node xxx.js的执行命令并传入参数自动实现配置化打包命令。

  3. (配置?)由于: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]
        },
        ...
    }
    
  4. (执行?) 由于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,
    }
    
  5. 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数据丢失的问题。


上面简单的提一下多模块打包项目的的几个方面,其他的以后在写吧
上一篇下一篇

猜你喜欢

热点阅读