美文网首页vue
vue创建多项目工程

vue创建多项目工程

作者: 3e2235c61b99 | 来源:发表于2022-02-25 17:23 被阅读0次

    我司的一个系统要重构,新系统计划用vue开发,但是由于旧系统过于庞大,在开发时使用分包开发(多系统),所以老大要求在开发新系统时也要分成多个系统。但是本人又不想在多个系统中维护一堆同样的代码(为保持风格一致,需要使用公共的样式、组件等),遂研究如何在一个vue项目中创建多个系统。

    先根据这篇文章搭建一个可以本地访问的多系统前端项目

    创建新项目

    vue create multi-project
    

    创建时的选项可以自己选择,为简洁计,我只在基本的选择上添加了routerstore
    创建完成的项目结构如下:

    初始目录结构

    使用脚手架创建的vue项目均为单页应用,但在此处我们需要把它修改为多页应用。
    将目录结构调整如下:

    多系统工程目录结构
    官方文档中给出的解决方法是在vue.config.js配置项pages
    官方文档
    vue.config.js配置参考文档
    修改vue.config.js文件为:
    module.exports = {
      pages: {
        projectA: {
          // page 的入口
          entry: 'src/modules/projectA/main.js',
          // 模板来源
          template: 'public/index.html',
          // 在 dist/index.html 的输出
          filename: 'projectA.html',
          // 当使用 title 选项时,
          // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
          title: 'project A Page',
          // 在这个页面中包含的块,默认情况下会包含
          // 提取出来的通用 chunk 和 vendor chunk。
          chunks: ['chunk-vendors', 'chunk-common', 'projectA']
        },
        // 当使用只有入口的字符串格式时,
        // 模板会被推导为 `public/subpage.html`
        // 并且如果找不到的话,就回退到 `public/index.html`。
        // 输出文件名会被推导为 `subpage.html`。
        projectB: {
          // page 的入口
          entry: 'src/modules/projectB/main.js',
          // 模板来源
          template: 'public/index.html',
          // 在 dist/index.html 的输出
          filename: 'projectB.html',
          // 当使用 title 选项时,
          // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
          title: 'project B Page',
          // 在这个页面中包含的块,默认情况下会包含
          // 提取出来的通用 chunk 和 vendor chunk。
          chunks: ['chunk-vendors', 'chunk-common', 'projectB']
        }
      }
    }
    

    重新启动项目,访问下面两个地址即可看到多页面的效果。
    http://localhost:8080/projectA/
    http://localhost:8080/projectB/
    此时可以正确看到两个项目的App页面,但在App通过router-vue暴露出去的页面却看不到,查看network报错为We're sorry but multi-project3 doesn't work properly without JavaScript enabled. Please enable it to continue.
    查资料后,把router的history修改为hash模式即可

    打包部署到服务器

    此时执行npm run build打包,打包结果如下(自己当时没有截图,借用别人的图):

    打包结构混乱
    两个不同的项目的文件各自编译打包,但并没有按项目分成不同的文件夹,我们进一步修改配置,将其改为按项目打包。
    修改vue.config.js代码如下:
    var projectname = process.argv[3]
    var glob = require('glob')
    
    function getEntry() {
      var entries = {}
      if (process.env.NODE_ENV == 'production') {
        entries = {
          index: {
            // page的入口
            entry: 'src/modules/' + projectname + '/main.js',
            // 模板来源
            template: 'public/index.html',
            // 在 dist/index.html 的输出
            filename: 'index.html',
            title: projectname,
            chunks: ['chunk-vendors', 'chunk-common', 'index']
          }
        }
      } else {
        var items = glob.sync( './src/modules/*/*.js')
        for (var i in items) {
          var filepath = items[i]
          var fileList = filepath.split('/');
          var fileName = fileList[fileList.length-2];
          entries[fileName] = {
            entry: `src/modules/${fileName}/main.js`,
            // 模板来源
            template: `public/index.html`,
            // 在 dist/index.html 的输出
            filename: `${fileName}.html`,
            // 提取出来的通用 chunk 和 vendor chunk。
            chunks: ['chunk-vendors', 'chunk-common', fileName]
          }
        }
      }
      return entries
    }
    
    var pages = getEntry()
    module.exports = {
      productionSourceMap: false, // 生产禁止显示源代码
      outputDir: 'dist/' + projectname,
      pages: pages
    }
    

    现在执行以下命令进行打包:

    #  npm run build [projectFileName]
    npm run build projectA
    npm run build projectB
    

    打包结果如下:


    分项目打包

    把打包好的文件放到nginx上,访问结果如下:

    image.png
    跟上面是同样的问题,请求的js404
    看了下打包后的文件,是打包后的文件中jscss等文件引用路径错误,手动修改后可以正常访问,所以只要修改打包配置,使文件引用路径符合要求就可以了

    此时需要参考官网修改vue.config.js文件

    publicPath: '/',  // 原配置
    publicPath: '/' + projectname + '/',  // 修改后
    

    之后再打包的文件引用路径就没有问题了

    把之前的单项目修改为多项目

    由于我们之前已经有一个小项目(vue单项目-修改自开源项目vue-admin-template),考虑到修改为多项目的代码修改量不是很大,所以继续在之前的项目中进行修改。
    首先根据上面的步骤修改目录结构和vue.config.js文件,由于现有的项目采用后端返回路由,前端进行解析的方式动态加载路由,所以除了views外,apilayoutrouterstore目录也都在子项目目录中存在。其中store中的公共部分提取到父项目中,子项目中需要用到的路由部分留在子项目中。
    目前为止,项目可以正常访问,只是公共组件SvgIcon无法使用,原因是把之前项目的vue.config.js文件中的内容给修改了,再加回去就好了。
    直接把加载svg的配置加进去会报错Error: Cannot call .tap() on a plugin that has not yet been defined. Call plugin('preload').use(<Plugin>) first.:

    image.png
    参考文章修改如下:
    修改前:
    config.plugin('preload').tap(() => [
          {
            rel: 'preload',
            fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
            include: 'initial'
          }
        ])
    

    修改后:

    const PreloadWebpackPlugin = require("@vue/preload-webpack-plugin");
    
    config.plugin('preload').use(PreloadWebpackPlugin).tap(() => [
          {
            rel: 'preload',
            // to ignore runtime.js
            // https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171
            fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
            include: 'initial'
          }
        ])
    

    此时便可以正常使用SvgIcon组件了
    此时vue.config.js文件如下:

    'use strict'
    
    const projectname = process.argv[3]
    const glob = require('glob')
    
    const PreloadWebpackPlugin = require("@vue/preload-webpack-plugin");
    
    function getEntry() {
      let entries = {}
      if (process.env.NODE_ENV == 'production') {
        entries = {
          index: {
            // page的入口
            entry: 'src/modules/' + projectname + '/main.js',
            // 模板来源
            template: 'public/index.html',
            // 在 dist/index.html 的输出
            filename: 'index.html',
            title: projectname,
            chunks: ['chunk-vendors', 'chunk-common', 'index']
          }
        }
      } else {
        let items = glob.sync( './src/modules/*/*.js')
        for (let i in items) {
          let filepath = items[i]
          let fileList = filepath.split('/');
          let fileName = fileList[fileList.length-2];
          entries[fileName] = {
            entry: `src/modules/${fileName}/main.js`,
            // 模板来源
            template: `public/index.html`,
            // 在 dist/index.html 的输出
            filename: `${fileName}.html`,
            // 提取出来的通用 chunk 和 vendor chunk。
            chunks: ['chunk-vendors', 'chunk-common', fileName]
          }
        }
      }
      return entries
    }
    
    let pages = getEntry()
    
    const path = require('path')
    const defaultSettings = require('./src/settings.js')
    
    function resolve(dir) {
      return path.join(__dirname, dir)
    }
    
    const name = defaultSettings.title || 'vue Admin Template' // page title
    
    // If your port is set to 80,
    // use administrator privileges to execute the command line.
    // For example, Mac: sudo npm run
    // You can change the port by the following methods:
    // port = 9528 npm run dev OR npm run dev --port = 9528
    const port = process.env.port || process.env.npm_config_port || 9528 // dev port
    
    // All configuration item explanations can be find in https://cli.vuejs.org/config/
    module.exports = {
      /**
       * You will need to set publicPath if you plan to deploy your site under a sub path,
       * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,
       * then publicPath should be set to "/bar/".
       * In most cases please use '/' !!!
       * Detail: https://cli.vuejs.org/config/#publicpath
       */
      outputDir: 'dist/' + projectname,
      publicPath: '/' + projectname + '/',
      pages: pages,
      assetsDir: 'static',
      lintOnSave: process.env.NODE_ENV === 'development',
      productionSourceMap: false,
      devServer: {
        host: '0.0.0.0',
        port: port,
        open: true,
        https: true,
        overlay: {
          warnings: false,
          errors: true
        },
        before: require('./mock/mock-server.js'),
        proxy: {
          '/downloadFile': {
            // target: 'http://10.141.128.102:8401',
            // target: 'http://dhr-basic-service.apps.uat.taikangcloud.com/', //test
            target: 'http://127.0.0.1:88', //dev
            changeOrigin: true,
            ws: true,
            pathRewrite: {
              '^/downloadFile': '/'
            }
          },
        },
      },
      configureWebpack: {
        // provide the app's title in webpack's name field, so that
        // it can be accessed in index.html to inject the correct title.
        name: name,
        resolve: {
          alias: {
            '@': resolve('src')
          }
        }
      },
      chainWebpack(config) {
        // it can improve the speed of the first screen, it is recommended to turn on preload
        config.plugin('preload').use(PreloadWebpackPlugin).tap(() => [
          {
            rel: 'preload',
            // to ignore runtime.js
            // https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171
            // fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/], // 文件黑名单
            fileBlacklist: [/\.map$/, /hot-update\.js$/],
            include: 'initial'
          }
        ])
    
        // when there are many pages, it will cause too many meaningless requests
        config.plugins.delete('prefetch')
    
        // set svg-sprite-loader
        config.module
          .rule('svg')
          .exclude.add(resolve('src/icons'))
          .end()
        config.module
          .rule('icons')
          .test(/\.svg$/)
          .include.add(resolve('src/icons'))
          .end()
          .use('svg-sprite-loader')
          .loader('svg-sprite-loader')
          .options({
            symbolId: 'icon-[name]'
          })
          .end()
    
        config
          .when(process.env.NODE_ENV !== 'development',
            config => {
              config
                .plugin('ScriptExtHtmlWebpackPlugin')
                .after('html')
                .use('script-ext-html-webpack-plugin', [{
                // `runtime` must same as runtimeChunk name. default is `runtime`
                  inline: /runtime\..*\.js$/
                }])
                .end()
              config
                .optimization.splitChunks({
                  chunks: 'all',
                  cacheGroups: {
                    libs: {
                      name: 'chunk-libs',
                      test: /[\\/]node_modules[\\/]/,
                      priority: 10,
                      chunks: 'initial' // only package third parties that are initially dependent
                    },
                    elementUI: {
                      name: 'chunk-elementUI', // split elementUI into a single package
                      priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
                      test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
                    },
                    commons: {
                      name: 'chunk-commons',
                      test: resolve('src/components'), // can customize your rules
                      minChunks: 3, //  minimum common number
                      priority: 5,
                      reuseExistingChunk: true
                    }
                  }
                })
              // https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk
              config.optimization.runtimeChunk('single')
            }
          )
      }
    }
    

    但是,我又双叕发现问题啦!!!
    打包部署到nginx后,有一个runtime.**.js文件找不到

    找不到runtime.**.js
    找了半天没找到好方法,最后把vue.config.js文件中的最后一个config注释掉后好了
    但是这个配置这儿不太熟,别人写的配置也看不太懂,之后可能会遇到其他问题,到时候自求多福吧
    修改后的vue.config.js文件如下:
    'use strict'
    
    const projectname = process.argv[3]
    const glob = require('glob')
    
    const PreloadWebpackPlugin = require("@vue/preload-webpack-plugin");
    
    function getEntry() {
      let entries = {}
      if (process.env.NODE_ENV == 'production') {
        entries = {
          index: {
            // page的入口
            entry: 'src/modules/' + projectname + '/main.js',
            // 模板来源
            template: 'public/index.html',
            // 在 dist/index.html 的输出
            filename: 'index.html',
            title: projectname,
            chunks: ['chunk-vendors', 'chunk-common', 'index']
          }
        }
      } else {
        let items = glob.sync( './src/modules/*/*.js')
        for (let i in items) {
          let filepath = items[i]
          let fileList = filepath.split('/');
          let fileName = fileList[fileList.length-2];
          entries[fileName] = {
            entry: `src/modules/${fileName}/main.js`,
            // 模板来源
            template: `public/index.html`,
            // 在 dist/index.html 的输出
            filename: `${fileName}.html`,
            // 提取出来的通用 chunk 和 vendor chunk。
            chunks: ['chunk-vendors', 'chunk-common', fileName]
          }
        }
      }
      return entries
    }
    
    let pages = getEntry()
    
    const path = require('path')
    const defaultSettings = require('./src/settings.js')
    
    function resolve(dir) {
      return path.join(__dirname, dir)
    }
    
    const name = defaultSettings.title || 'vue Admin Template' // page title
    
    // If your port is set to 80,
    // use administrator privileges to execute the command line.
    // For example, Mac: sudo npm run
    // You can change the port by the following methods:
    // port = 9528 npm run dev OR npm run dev --port = 9528
    const port = process.env.port || process.env.npm_config_port || 9528 // dev port
    
    // All configuration item explanations can be find in https://cli.vuejs.org/config/
    module.exports = {
      /**
       * You will need to set publicPath if you plan to deploy your site under a sub path,
       * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,
       * then publicPath should be set to "/bar/".
       * In most cases please use '/' !!!
       * Detail: https://cli.vuejs.org/config/#publicpath
       */
      outputDir: 'dist/' + projectname,
      publicPath: '/' + projectname + '/',
      pages: pages,
      assetsDir: 'static',
      lintOnSave: process.env.NODE_ENV === 'development',
      productionSourceMap: false,
      devServer: {
        host: '0.0.0.0',
        port: port,
        open: true,
        https: true,
        overlay: {
          warnings: false,
          errors: true
        },
        before: require('./mock/mock-server.js'),
        proxy: {
          '/downloadFile': {
            // target: 'http://10.141.128.102:8401',
            // target: 'http://dhr-basic-service.apps.uat.taikangcloud.com/', //test
            target: 'http://127.0.0.1:88', //dev
            changeOrigin: true,
            ws: true,
            pathRewrite: {
              '^/downloadFile': '/'
            }
          },
        },
      },
      configureWebpack: {
        // provide the app's title in webpack's name field, so that
        // it can be accessed in index.html to inject the correct title.
        name: name,
        resolve: {
          alias: {
            '@': resolve('src')
          }
        }
      },
      chainWebpack(config) {
        // it can improve the speed of the first screen, it is recommended to turn on preload
        config.plugin('preload').use(PreloadWebpackPlugin).tap(() => [
          {
            rel: 'preload',
            // to ignore runtime.js
            // https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171
            fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/], // 文件黑名单
            include: 'initial'
          }
        ])
    
        // when there are many pages, it will cause too many meaningless requests
        config.plugins.delete('prefetch')
    
        // set svg-sprite-loader
        config.module
          .rule('svg')
          .exclude.add(resolve('src/icons'))
          .end()
        config.module
          .rule('icons')
          .test(/\.svg$/)
          .include.add(resolve('src/icons'))
          .end()
          .use('svg-sprite-loader')
          .loader('svg-sprite-loader')
          .options({
            symbolId: 'icon-[name]'
          })
          .end()
    
        // config
        //   .when(process.env.NODE_ENV !== 'development',
        //     config => {
        //       config
        //         .plugin('ScriptExtHtmlWebpackPlugin')
        //         .after('html')
        //         .use('script-ext-html-webpack-plugin', [{
        //         // `runtime` must same as runtimeChunk name. default is `runtime`
        //           inline: /runtime\..*\.js$/
        //         }])
        //         .end()
        //       config
        //         .optimization.splitChunks({
        //           chunks: 'all',
        //           cacheGroups: {
        //             libs: {
        //               name: 'chunk-libs',
        //               test: /[\\/]node_modules[\\/]/,
        //               priority: 10,
        //               chunks: 'initial' // only package third parties that are initially dependent
        //             },
        //             elementUI: {
        //               name: 'chunk-elementUI', // split elementUI into a single package
        //               priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
        //               test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
        //             },
        //             commons: {
        //               name: 'chunk-commons',
        //               test: resolve('src/components'), // can customize your rules
        //               minChunks: 3, //  minimum common number
        //               priority: 5,
        //               reuseExistingChunk: true
        //             }
        //           }
        //         })
        //       // https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk
        //       config.optimization.runtimeChunk('single')
        //     }
        //   )
      }
    }
    

    相关文章

      网友评论

        本文标题:vue创建多项目工程

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