多入口打包
适用场景
移动端开发,一般情况,我们项目开发只需要一个配置一个入口,一个模版文件。
但是有时候又明显不够用,移动端,同一个业务下,同类型的项目,就很适合放在一个项目下做多入口打包。
比如以下场景:
- 活动项目,同类型活动多期迭代,不同类型的活动,新增一个活动就重新建立一套项目,维护成本很大,此时就很适合一个大的活动项目里,去新增多个活动页面入口。
- 当我们去写App的内嵌h5页面时,每个流程其实都是相互独立的,都是在一个app下,也很适合,多入口,维护在一个项目里。
优势
- 多入口,项目内各个入口独立,低耦合,不会相互影响。
- 发布成本低,可以维护在一个项目里,不用新建多个项目。
- 结构清晰,可维护性比较好。
如果我们全部使用单入口,也就是vue官方提供的框架,可以设想一下,如果是活动页面,我们把所有活动的页面都定义在一个入口里,router,store全部在一块,项目耦合会越来越严重,路由名也会重合率越来越高!
项目结构
如下图所示,我们在src目录下面,新增了module目录,用来存放我们的多个项目入口,page1 和page2 就是我用来演示的两个项目文件。
前置依赖库
node 的 glob库
glob 其实是linux shell默认的通配符,主要是做文件匹配,
glob 模式通常用来匹配目录以及文件 这里介绍的是node的一个方法库glob
,一般用于文件系统中定位文件 主要用到了同步搜索文件API
js
glob.sync(pattern, [options])
- pattern {String} 待匹配的模式
- options {Object}
- return: {Array<String>} 匹配模式的文件名
1
2
3
4
5
2
3
4
5
vue-cli的 pages配置
vue-cli 接收一个参数pages,用来控制输入文件,输出文件
详情可参考文档pages
官方例子如下
比较重要的三个参数就是 entry template filename
page 的入口,模板来源, 在 dist/index.html 的输出
下面这端代码是vue-cli的官方说明
js
module.exports = {
pages: {
index: {
// page 的入口
entry: 'src/index/main.js',
// 模板来源
template: 'public/index.html',
// 在 dist/index.html 的输出
filename: 'index.html',
// 当使用 title 选项时,
// template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></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'
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
devServer 的setupMiddlewares
提供执行自定义函数和应用自定义中间件的能力。
我们可以在devServer 启动项目之前,做一些自定义的处理
我在启动时渲染了一段 html
js
devServer: {
host: '0.0.0.0',
setupMiddlewares: (middleware, devServer) => {
devServer.app.get('', (_, response) => {
response.write(html)
response.end()
})
return middleware
},
proxy: {}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
核心代码实现
js
const entryName = ''
const entry1 = glob.sync('src/main.js') // 单入口
const entry2 = glob.sync('src/module/*/main.js') // 多入口
const entryList = [...entry1, ...entry2]
const pages = {} //pages 参数
let html = '' // 要渲染的html
entryList.forEach(filePath => {
const name1 = filePath.match(/src/main.js$/) // 单入口
const name2 = filePath.match(/src/module/(\w+)/main.js$/) // 多入口
const name = name1 ? name1[1] : name2[1]
const filename = name1 ? name + '/index' : name
if (filename === entryName || !entryName) {
const pagePath = glob.sync(`src/module/${name}/index.html`)[0]
// 读文件夹下的index.html文件
pages[name] = {
entry: filePath,
template: pagePath || 'public/index.html',
// 如果没有自动去取public下的通用index.html
filename: filename + '/index.html'
}
// 构造html 结构
html += `<a href="${filename}/">${filename}/</a><br>`
}
})
console.log(pages)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
pages 结构
hehehhe 项目的 index.html 文件是取自public,其他的是取自本项目目录下
考虑到部分页面会有在入口文件引用第三方的库的情况,比如swiper,jq。
所以这里设置的比较灵活,可以直接用通用的项目模版,也可以在项目里自定义模版。
js
{
hehehe: {
entry: 'src/module/hehehe/main.js',
template: 'public/index.html',
filename: 'hehehe/index.html'
},
page1: {
entry: 'src/module/page1/main.js',
template: 'src/module/page1/index.html',
filename: 'page1/index.html'
},
page2: {
entry: 'src/module/page2/main.js',
template: 'src/module/page2/index.html',
filename: 'page2/index.html'
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17