网上有很多关于vue多页面应用的配置文章,本文重点记录下开发过程中遇到的打包编译后运行时问题。
正常清晰点的多页面项目目录应该像下图这样的结果,十分清晰:
├── README.md
├── build
│ ├── build.js
│ ├── check-versions.js
│ ├── dev-client.js
│ ├── dev-server.js
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js
│ └── webpack.prod.conf.js
├── config
│ ├── dev.env.js
│ ├── index.js
│ └── prod.env.js
├── package.json
├── src
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ ├── Hello.vue
│ │ └── cell.vue
│ └── pages
│ ├── cell
│ │ ├── cell.html
│ │ ├── cell.js
│ │ └── cell.vue
│ └── index
│ ├── index.html
│ ├── index.js
│ ├── index.vue
│ └── router
│ └── index.js
└── static
此次自己操作的项目由于前期是单页面项目,前期改多页面时也是别人做的,稍显不一样:
|---build
|---node_modules
|---public
| |---img
| |---app.html
| |---favicon.ic
| |---index.html
|---src
| |---api
| | |---app_request.js //h5应用的接口存放处
| | |---user.js //之前单页面项目的接口存放处
| |---asssets
| |---layout
| |---page_app
| | |---home //h5应用的页面存放处
| | |---App.vue //h5应用的
| | |---router.js //h5应用的路由
| |---router // 之前单页面应用的存放处
| |---store
| |---styles
| |---utils
| |---views //之前单页面应用的页面存放处
|---app.main.js //h5应用的
|---App.vue //之前单页面应用的
|---main.js //之前单页面应用的
|---permission.js
|---settings.js
|---package.json
|---vue.config.js
多页面最主要的配置还是在module.exports中配置pages,具体可以查看文档及参照下面vue.config.js文件中
重点讲下打包编译后运行出现如下图的报错,网上资料很少。现在推测的原因是打包编译后,在vue的template模板来源文件index.html和app.html中会出现 rel=preload rel=prefetch预加载资源,所以需要在配置中取消runtime.js的内联及预加载
关键代码:
chainWebpack(config) {
config.plugins.delete('preload') // TODO: need test
config.plugins.delete('prefetch') // TODO: need test
//两处写法应该是都可以,没有亲测区别,保险起见都写在了配置文件中
Object.keys(pages).forEach(page => {
config.plugins.delete('preload-${page}')
config.plugins.delete('prefetch-${page}')
})
另外打包编译运行时,可能还会遇到以下的警告,屏幕显示空白:
具体原因是配置pages时,对于各自页面chunks的配置,只要把响应报警告的js文件名添加进chunks即可。
具体此次项目vue.config.js配置如下:
'use strict'
const path = require('path')
const defaultSettings = require('./src/settings.js')
const ipFile = require('./src/utils/git-ip')
const fs = require('fs')
// const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin')
function resolve(dir) {
return path.join(__dirname, dir)
}
const name = defaultSettings.title || 'Storm System' // page title
// If your port is set to 80,
// use administrator privileges to execute the command line.
// For example, Mac: sudo npm run
// eslint-disable-next-line no-unused-vars
const port = 9527 // dev port
const outputDirStr = '../fy-php/public'
const outputDirStrTestDev = '../../data/wwwroot/dev/public'
const outputOnline = '../online/public'
const outputDirDebug = '../../data/wwwroot/debug/public'
let outputDir = ''
let publicPath = ''
let indexPath = ''
// 删除静态资源
function delDir(path) {
let files = []
if (fs.existsSync(path)) {
files = fs.readdirSync(path)
files.forEach((file, index) => {
const curPath = path + '/' + file
if (fs.statSync(curPath).isDirectory()) {
delDir(curPath)
} else {
fs.unlinkSync(curPath) // 删除文件
}
})
fs.rmdirSync(path)
}
}
const pages = {
index: {
entry: 'src/main.js',
template: 'public/index.html',
filename: 'index.html',
chunks: ['chunk-libs', 'chunk-commons', 'chunk-elementUI', 'index', 'runtime', 'manifest']
},
app: {
entry: 'src/app.main.js',
template: 'public/app.html',
filename: 'app.html',
chunks: ['chunk-libs', 'chunk-commons', 'chunk-elementUI', 'app', 'runtime', 'manifest']
}
}
if (process.env.ENV === 'production') {
// console.log('production运行')
delDir(outputDirStr + '/css')
delDir(outputDirStr + '/fonts')
delDir(outputDirStr + '/img')
delDir(outputDirStr + '/js')
delDir(outputDirStr + '/apidoc')
delDir(outputDirStr + '/appdoc')
delDir(outputDirStr + '/dist')
outputDir = outputDirStr
publicPath = '/'
indexPath = '../resources/views/index.blade.php'
} else if (process.env.ENV === 'dev') {
// console.log('dev运行')
delDir(outputDirStrTestDev + '/css')
delDir(outputDirStrTestDev + '/fonts')
delDir(outputDirStrTestDev + '/img')
delDir(outputDirStrTestDev + '/js')
outputDir = outputDirStrTestDev
publicPath = '/'
indexPath = '../resources/views/index.blade.php'
} else if (process.env.ENV === 'debug') {
// console.log('debug环境运行')
delDir(outputDirDebug + '/css')
delDir(outputDirDebug + '/fonts')
delDir(outputDirDebug + '/img')
delDir(outputDirDebug + '/js')
outputDir = outputDirDebug
publicPath = '/'
indexPath = '../resources/views/index.blade.php'
} else if (process.env.ENV === 'online') {
// console.log('online运行')
delDir(outputOnline + '/css')
delDir(outputOnline + '/fonts')
delDir(outputOnline + '/img')
delDir(outputOnline + '/js')
delDir(outputOnline + '/apidoc')
delDir(outputOnline + '/appdoc')
outputDir = outputOnline
publicPath = '/'
indexPath = '../resources/views/index.blade.php'
} else if (process.env.ENV === 'staging') {
// console.log('staging运行')
outputDir = 'dist'
publicPath = '/'
indexPath = 'index.html'
} else {
// console.log('其它运行')
outputDir = 'dist'
publicPath = '/dist/'
indexPath = 'index.html'
}
module.exports = {
publicPath: publicPath,
outputDir: outputDir,
indexPath: indexPath,
lintOnSave: process.env.NODE_ENV === 'development',
pages,
productionSourceMap: false,
runtimeCompiler: true,
devServer: {
port: port,
open: false,
host: ipFile,
overlay: {
warnings: false,
errors: true
},
proxy: {
// change xxx-api/login => mock/login
// detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: {
target: 'http://****',
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
}
}
// after: require('./mock/mock-server.js')
},
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) {
config.plugins.delete('preload') // TODO: need test
config.plugins.delete('prefetch') // TODO: need test
// 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()
// set preserveWhitespace
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(options => {
options.compilerOptions.preserveWhitespace = true
return options
})
.end()
config
// https://webpack.js.org/configuration/devtool/#development
.when(process.env.NODE_ENV === 'development',
config => config.devtool('cheap-source-map')
)
config
.when(process.env.NODE_ENV !== 'development',
config => {
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
}
}
})
config.optimization.runtimeChunk('single')
}
)
Object.keys(pages).forEach(page => {
config.plugins.delete('preload-${page}')
config.plugins.delete('prefetch-${page}')
})
}
}
最近发现基于上面配置还有可能出现报错现象,github上有回答是注释掉下面的代码:亲测有效
config.plugin('ScriptExtHtmlWebpackPlugin').after('html').use('script-ext-html-webpack-plugin', [{
// `runtime` must same as runtimeChunk name. default is `runtime`
// inline: /runtime\..*\.js$/
}]).end()