美文网首页
日更挑战-Vue-cli之构建多模块项目

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

作者: 愿你我皆是黑马 | 来源:发表于2021-05-20 23:26 被阅读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数据丢失的问题。

    • 安装

      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


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

    相关文章

      网友评论

          本文标题:日更挑战-Vue-cli之构建多模块项目

          本文链接:https://www.haomeiwen.com/subject/fatejltx.html