在项目中我们经常会做多个小的vue项目,这些项目页面不多,功能也不复杂,它们可以共用依赖,但是我们又不能每个小项目单独初始化vue项目,这个时候,我们就希望在一个vue脚手架下创建多个子项目,它们互不干扰,可以独立运行,但又能共用一套依赖;
其实这个需求尤大大早就替我们想到了,在vue-cli的官方文档中就有介绍;
pages
Type: Object
Default: undefined
在 multi-page 模式下构建应用。每个“page”应该有一个对应的 JavaScript 入口文件。其值应该是一个对象,对象的 key 是入口的名字,value 是:
module.exports = {
pages: {
index: {
// page 的入口
entry: 'src/index/main.js',
// 模板来源
template: 'public/index.html',
// 在 dist/index.html 的输出
filename: 'index.html',
// 当使用 title 选项时,
// template 中的 title 标签需要是 <%= htmlWebpackPlugin.options.title %>
title: 'Index Page',
// 在这个页面中包含的块,默认情况下会包含
// 提取出来的通用 chunk 和 vendor chunk。
chunks: ['chunk-vendors', 'chunk-common', 'index']
},
// 当使用只有入口的字符串格式时,
// 模板会被推导为 `public/subpage.html`
// 并且如果找不到的话,就回退到 `public/index.html`。
// 输出文件名会被推导为 `subpage.html`。
subpage: 'src/subpage/main.js'
}
}
提示
当在 multi-page 模式下构建时,webpack 配置会包含不一样的插件 (这时会存在多个 html-webpack-plugin 和 preload-webpack-plugin 的实例)。如果你试图修改这些插件的选项,请确认运行 vue inspect。
一个指定了 entry, template, filename, title 和 chunks 的对象 (除了 entry 之外都是可选的);
或一个指定其 entry 的字符串。
vue-cli官方文档地址
下面我们就来一步步操作,自己搭建一个分项目打包的项目模板
cd vue-cli3-template
根据个人需要去选择配置,在这里我们直接选择手动配置
待项目依赖安装好后,进入项目根目录,启动项目
cd vue-cli3-template
npm run serve
使用脚手架创建的vue项目均为单页面应用,但在实际生产应用中,我们常常需要用到多页面应用,接下来我们将项目改为多页面应用:
将项目目录调整如下:
在项目根目录下创建vue.config.js文件。
按照官方文档的配置,修改vue.config.js内容如下
module.exports = {
pages: {
projectA: {
// page 的入口
entry: 'src/pages/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/pages/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#/
但是这个时候打包会有问题,执行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/pages/" + 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/pages/*/*.js");
for (var i in items) {
var filepath = items[i];
var fileList = filepath.split("/");
var fileName = fileList[fileList.length - 2];
entries[fileName] = {
entry: `src/pages/${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
重新打包 ,结果符合我们的预期了
到此配置结束
vue-multi-module【多模块集成的vue项目,多项目共用一份配置,可以互相依赖,也可以独立打包部署】
使用业务场景
1.如果项目可能有对应多个不同UI界面;对于这样的场景你可能首先会想到,用样式主题就可以实现,基本的样式或者换肤是可以通过样式实现。但如果要实现更复杂的,比如不同两套UI的界面可能功能显示及样式都有差别,那就不得不单独拆成一个项目,但是这样又会造成每个项目很多冗余代码。
2.如果项目有多个子模块(同时子模块之间又存在互相依赖关系);对于这样的场景是可以把项目独立发布到npm仓库,但是这样又涉及到每个模块都需要独立编译好再发布,实际过程有显得有些繁琐(实际视情况而定)。
对于以上场景可以使用一个项目管理多个子模块也是一个不错的选择。
多页面和多模块区别
多页面:多页面是指一个项目有多个入口,打包是会生成多个html文件,实际开发过程中都是混合在一个项目中开发;
多模块:是指不同的业务模块可以进行拆分;各自独立运行、也可以互相引用,这一点和通过 npm 发布是类似的;对于一些项目本身不允许发布的情况下,既可以独立开发,又不需要发布到共有仓库(当然也可以通过建立私有仓库解决哈)
问题:
1,如何划分子模块;
2,如何分离可复用组件;
3,如何独立编译,每个子模块独立打包编译、运行;
优点
1,高复用性
2,统一管理依赖库
3,不同模块使用的依赖各自按需打包
4,模块之间相互独立运行、编译、打包
5,模块之间可以直接互相引用,不需要iframe(一般方式是通过iframe嵌入,这样的性能相当差)
一:开始创建项目
先用vue-lic
创建个demo项目:
vue init webpack vue-multi-module
先看看创建成功的项目目录:
创建完后先跑起来看看:
npm run dev
二:添加多项目模块
把src目录下的文件换成多模块的形式
build:编译、运行相关脚本文件
config:编译、运行相关配置文件
static:不需要编译的静态资源,放到对应的模块目录下
子模块1
子模块2
...
src:项目源码
comm:公共模块
script:公共js文件
components:公共组件
子模块1:
assets:样式、图片等资源文件
common:业务相关的公共文件
components:业务组件
router:路由配置
store:vuex相关
views:页面视图
子模块2:
子模块3:
...
项目模块结构安装上面的改动完毕之后,控制台会报一些路径错误之类的:
这是因为webpack.base.conf.js
里面的main.js
的路径发生改变导致的,之前项目是单模块只要一个main.js
,现在换成多模块之后每个模块都有自己独立的main.js
,所以要修改配置。
三:修改配置
1.增加 config/multi.conf.js
:多模块配置文件,定义各个模块自己的端口,名称,转发规则,静态资源
.............
// 多模块独立配置
var importModules = [
new MultiModule('project1', {
port: 8091,
statics: ['static1'],
proxyTable: {
'/servers1/': getProxyConfig(PROXY_DOMAIN_DEFAULT)
}
}),
new MultiModule('project2', {
port: 8092,
statics: ['static2'],
proxyTable: {
'/servers2/': getProxyConfig(PROXY_DOMAIN_DEFAULT)
}
}),
new MultiModule('project3', {
port: 8093,
statics: ['static3'],
proxyTable: {
'/servers3/': getProxyConfig(PROXY_DOMAIN_DEFAULT)
}
}),
new MultiModule('project4', {
port: 8094,
statics: ['static4'],
proxyTable: {
'/servers4/': getProxyConfig(PROXY_DOMAIN_DEFAULT)
}
})
]
..........
2.修改 build/webpack.base.conf.js
文件:修改启动项目的入口文件,添加各个模块的别名
..............
module.exports = {
context: path.resolve(__dirname, '../'),
entry: multiConfig.process.entry,
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@comm': resolve(`src/comm`),
'@': multiConfig.process.alias,
...multiConfig.moduleAlias
}
},
.............
以上两个配置加上之后之前的main.js
错误就消失了,但是又会报一些语法错误:
这是因为项目中的用了sass
语法和一些es6
语法缺少转码包,所以要加上对应的包:
到这里项目就可以正常启动了:
3.修改package.json
文件
这里默认启动的是project1,如果需要启动project2,project3,就需要在package.json
里面加对应的script
现在可以启动:npm run dev:project2
可能大家注意到了每次启动的时候端口都是一样的:
这样如果同时启动多个项目就没法区分谁是谁了,所以要在webpack.dev.conf.js
里面的compilationSuccessInfo/messages
里面加上对应项目的提示信息和自定义端口,以及每个项目的代理转发地址也不同,也要从multi.conf.js
获取,涉及到修改以下的两个配置文件
4.修改webpack.dev.conf.js
和config下的index.js
文件
webpack.dev.conf.js
:修改本地转发规则proxy(指向multi),修改启动后的描述文字
// Add FriendlyErrorsPlugin
let host = ['localhost', '127.0.0.1', '0.0.0.0'].includes(devWebpackConfig.devServer.host) ? 'localhost' : devWebpackConfig.devServer.host
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [chalk`{bold.rgb(255,255,0) [${pack.name} => ${multiConfig.process.name}]} App running at:\n - Local: {bold.cyan http://${host}:${port}${config.dev.assetsPublicPath}}\n - Network: {bold.cyan http://${getIPAdress()}:${port}${config.dev.assetsPublicPath}}`]
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})
index.js
:修改所有启动端口(port)和静态资源地址(static)
dev: {
// Paths
assetsSubDirectory: multiConfig.process.assetsSubDirectory,
assetsPublicPath: multiConfig.process.assetsPublicPath,
proxyTable: null,
// Various Dev Server settings
host: multiConfig.process.host, // can be overwritten by process.env.HOST
port: multiConfig.process.port, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
到这里本地启动的名称,端口,代理,静态资源都已经区分开了。接下来打包的时候也要按需打包每个模块项目自己的依赖的资源:
5.修改package.json
和config下的index.js
文件
package.json
:新加命令
"build": "npm install && npm run build:project1 && npm run build:project2 && npm run build:project3 && npm run build:project4",
"build:project1": "node build/build.js name=project1",
"build:project1:zip": "node build/build.js name=project1 zip",
"build:project2": "node build/build.js name=project2",
"build:project2:zip": "node build/build.js name=project2 zip",
"build:project3": "node build/build.js name=project3",
"build:project3:zip": "node build/build.js name=project3 zip",
"build:project4": "node build/build.js name=project4",
"build:project4:zip": "node build/build.js name=project4 zip",
config下的index.js
:各模块分开打包后的路径
build: {
// Template for index.html
index: multiConfig.process.index,
// Paths
assetsRoot: multiConfig.process.assetsRoot,
assetsSubDirectory: multiConfig.process.assetsSubDirectory,
assetsPublicPath: multiConfig.process.assetsPublicPath,
}
6.webpack.prod.conf.js
:打包的时候静态资源按需copy拉取(CopyWebpackPlugin),添加模块压缩添加packZip.js
。
new CopyWebpackPlugin(multiConfig.process.publics.filter(name => isDirectory(path.resolve(__dirname, `../static/${name}`))).map(name => {
return {
from: path.resolve(__dirname, `../static/${name}`),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
})),
// pack zip
...require('./packZip')
packZip.js
:依赖filemanager-webpack-plugin打包zip组件,package.json相应添加
.............
plugins.push(new FileManagerPlugin({
onEnd: {
delete: [
path.join(__dirname, `../${pack.name}_${multiConfig.process.name}_*.zip`)
],
archive: [{
source: path.join(__dirname, `../dist/${multiConfig.process.name}`),
destination: path.join(__dirname, zipPros[1] ? `../${pack.name}_${zipPros[1]}.zip` : `../${pack.name}_${multiConfig.process.name}_v${pack.version}_${datetime}.zip`)
}]
}
}))
}
到这里模块项目已经实现多模块启动,按需打包,压缩等功能