我司的一个系统要重构,新系统计划用vue开发,但是由于旧系统过于庞大,在开发时使用分包开发(多系统),所以老大要求在开发新系统时也要分成多个系统。但是本人又不想在多个系统中维护一堆同样的代码(为保持风格一致,需要使用公共的样式、组件等),遂研究如何在一个vue项目中创建多个系统。
先根据这篇文章搭建一个可以本地访问的多系统前端项目
创建新项目
vue create multi-project
创建时的选项可以自己选择,为简洁计,我只在基本的选择上添加了router
和store
创建完成的项目结构如下:
使用脚手架创建的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 标签需要是 <%= htmlWebpackPlugin.options.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 标签需要是 <%= htmlWebpackPlugin.options.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
上,访问结果如下:
跟上面是同样的问题,请求的
js
报404
看了下打包后的文件,是打包后的文件中
js
和css
等文件引用路径错误,手动修改后可以正常访问,所以只要修改打包配置,使文件引用路径符合要求就可以了
此时需要参考官网修改vue.config.js
文件
publicPath: '/', // 原配置
publicPath: '/' + projectname + '/', // 修改后
之后再打包的文件引用路径就没有问题了
把之前的单项目修改为多项目
由于我们之前已经有一个小项目(vue单项目-修改自开源项目vue-admin-template),考虑到修改为多项目的代码修改量不是很大,所以继续在之前的项目中进行修改。
首先根据上面的步骤修改目录结构和vue.config.js
文件,由于现有的项目采用后端返回路由,前端进行解析的方式动态加载路由,所以除了views
外,api
、layout
、router
和store
目录也都在子项目目录中存在。其中store
中的公共部分提取到父项目中,子项目中需要用到的路由部分留在子项目中。
目前为止,项目可以正常访问,只是公共组件SvgIcon
无法使用,原因是把之前项目的vue.config.js
文件中的内容给修改了,再加回去就好了。
直接把加载svg
的配置加进去会报错Error: Cannot call .tap() on a plugin that has not yet been defined. Call plugin('preload').use(
:
参考文章修改如下:
修改前:
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
文件找不到
找了半天没找到好方法,最后把
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')
// }
// )
}
}