vue cli4 vue2 多页面打包配置与坑点
vue cli4 vue2 多页面打包配置与坑点
前言:
一直以来都没用上多页面打包,这次遇到个业务特殊情况,想到用多页面打包的方案来解决,于是按照vue cli 官方文档在自己的项目上配置跑了一下,果然一跑就是一堆问题。只能说事情总是比我们想象的要曲折的多。然后捯饬半天,算是搞定了。想想网上很多配置也是星星点点,很碎,说的也不全,希望后面有需要的人看到能少踩坑,也方便自己不时之需,于是心一狠就有了这篇文章~。好了,话不多说,进入正题
1. 按照官网说明,先贴出自己 vue.config.js 里的pages
配置
let pages = {
index: {
// page 的入口
entry: 'src/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-libs', 'chunk-vant', 'chunk-commons', 'runtime', 'index'],
},
subpage: {
entry: 'src/main.js',
template: 'public/index-wx.html',
filename: 'index-wx.html',
chunks: ['chunk-libs', 'chunk-vant', 'chunk-commons', 'runtime', 'subpage'],
},
}
module.exports = {
// 其他配置项...
pages
}
2. 这里要注意点就是 chunks
这个属性,我也是主要在这踩了半天
如果你打包配置了代码分割splitChunks
,就是有下面这样的配置
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-vant', // split vant 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[\\/]_?vant(.*)/, // 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,
},
},
})
config.optimization.runtimeChunk('single')
那么上面那个chunks
数组里一定要配置splitChunks里面的name的值,可以对照上面示例看看,
另外就是这个单独的runtime
,如果你配置了单独内嵌vue 的runtime,这个也一定要写到chunks里面,不然页面也是白屏或者其他问题。至于提取runtime的配置,这里不做说明,这个网上很多,后面我会贴出我整体的vue.config.js的配置,需要的可以瞅瞅
3. 另外一点要注意的就是,这里引用下官网原文:
当在 multi-page 模式下构建时,webpack 配置会包含不一样的插件 (这时会存在多个 html-webpack-plugin
和 preload-webpack-plugin 的实例)。如果你试图修改这些插件的选项,请确认运行 vue inspect。
具体到代码里就是和那两个插件相关的配置要结合多页面pages里面的名称使用,不明白什么意思的直接看下面代码配置示例:
Object.keys(pages).forEach((element) => {
config.plugin('preload-' + element).tap(() => [
{
rel: 'preload',
// to ignore runtime.js
fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
include: 'initial',
},
])
config
.plugin('ScriptExtHtmlWebpackPlugin')
.after('html-' + element)
.use('script-ext-html-webpack-plugin', [
{
// `runtime` must same as runtimeChunk name. default is `runtime`
inline: /runtime\..*\.js$/,
},
])
.end()
config.plugins.delete('prefetch-' + element)
})
原来没有多页面配置时,是这样的:
config.plugin('preload').tap(() => [
{
rel: 'preload',
// to ignore runtime.js
fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
include: 'initial',
},
])
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.plugins.delete('prefetch')
})
对比下就知道了,多页面配置下,html插件的实例名称会和页面名称结合变成html-index
、html-subpage
这种形式,preload插件也是如此
然后到这基本要说的和注意点就这么多了,其他的都很简单,看看文档就明白了,跑起来应该没问题了。我这里只用了两个页面打包配置,要是有更多的页面,配置也是大同小异,只要注意好上面这几点就行了。还有及时多页面的cdn什么的配置,我这暂时没用上,就不说了,网上也多,如果后面踩坑了再给来更新…。如果我说的对你有帮助,方便帮我点个赞哦~
最后,贴上目前项目使用的整体vue.config.js
配置,各位看官好有个整体感知
const path = require('path')
const resolve = (dir) => {
return path.join(__dirname, dir)
}
//读取npm指令 process.argv 获取⾃定义参数
let argvs = process.argv.filter((e) => e.includes('='))
for (const iterator of argvs) {
let diyArg = iterator.split('=')
process.env['VUE_APP_' + diyArg[0].slice(2)] = diyArg[1]
}
process.env.VUE_APP_ENV = process.env.VUE_APP_ENV || process.env.NODE_ENV
const env = require('./env')
process.env.VUE_APP_TITLE = require('./package.json').description
let pages = {
index: {
// page 的入口
entry: 'src/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-libs', 'chunk-vant', 'chunk-commons', 'runtime', 'index'],
},
subpage: {
entry: 'src/main.js',
template: 'public/index-wx.html',
filename: 'index-wx.html',
chunks: ['chunk-libs', 'chunk-vant', 'chunk-commons', 'runtime', 'subpage'],
},
}
module.exports = {
publicPath: env.BASE_URL || '/',
outputDir: 'h5', // 打包名称
// 打包时不生成.map文件
productionSourceMap: false,
pages,
// 如果你不需要使用eslint,把lintOnSave设为false即可
lintOnSave: false,
// 这里写你调用接口的基础路径,来解决跨域,如果设置了代理,那你本地开发环境的axios的baseUrl要写为 '' ,即空字符串
devServer: {
host: '0.0.0.0',
port: 8082,
// 自动打开浏览器
open: true,
proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy
['/api']: {
target: env.BASE_API,
changeOrigin: true,
pathRewrite: {
['^/api']: '',
},
},
},
disableHostCheck: true,
},
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: [path.resolve(__dirname, './src/styles/varibles.less')],
},
},
css: {
loaderOptions: {
less: {
modifyVars: {
// 直接覆盖变量
// 或者可以通过 less 文件覆盖(文件路径为绝对路径)
hack: `true; @import "${path.resolve(__dirname, './src/styles/varibles.less')}";`,
},
},
},
},
configureWebpack: {
resolve: {
alias: {
'@': resolve('src'),
},
},
},
chainWebpack(config) {
// set svg-sprite-loader
config.module
.rule('svg')
.exclude.add(resolve('src/assets/icons'))
.end()
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/assets/icons/svg'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
// icon symbolId, 文件目录结构 => 命名: svg本级下的svg直接使用文件名,eg: icon-文件名;svg子目录下的svg使用需要带上该子目录文件夹名 eg:icon-文件夹名-文件名
// # src/icons/svg
// - icon1.svg => icon-icon1
// - icon2.svg => icon-icon2
// - dir/icon1.svg => icon-dir-icon1
symbolId: (filePath) => {
let split = filePath.includes('/') ? '/icons/svg' : '\\icons\\svg'
let str = filePath.slice(filePath.lastIndexOf(split) + split.length, -4) // eg: '/ase/as
return 'icon' + str.replace(/[\/\\]/g, '-') // icon-aa-ss
},
// symbolId: 'icon-[name]',
})
.end()
config.when(process.env.NODE_ENV !== 'development', (config) => {
Object.keys(pages).forEach((element) => {
config.plugin('preload-' + element).tap(() => [
{
rel: 'preload',
// to ignore runtime.js
fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
include: 'initial',
},
])
config
.plugin('ScriptExtHtmlWebpackPlugin')
.after('html-' + element)
.use('script-ext-html-webpack-plugin', [
{
// `runtime` must same as runtimeChunk name. default is `runtime`
inline: /runtime\..*\.js$/,
},
])
.end()
config.plugins.delete('prefetch-' + element)
})
// config.plugin('preload').tap(() => [
// {
// rel: 'preload',
// // to ignore runtime.js
// fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
// include: 'initial',
// },
// ])
// config.plugins.delete('prefetch')
// 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.minimizer('terser').tap((options) => {
options[0].terserOptions.compress.drop_console = true
options[0].terserOptions.compress.drop_debugger = true
return options
})
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-vant', // split vant 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[\\/]_?vant(.*)/, // 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,
},
},
})
config.optimization.runtimeChunk('single')
})
},
}
更多推荐
所有评论(0)