vue运行根据npm和yarn不同有不同的指令,不过大同小异,常用的一般只有本地运行和线上打包指令,vuecli3以下的为 npm run dev(本地运行)、npm run build(线上打包);vuecli3及以上的为 npm run serve(本地运行)、npm run build(线上打包)。
但是经常也会遇到一种情况:线上也会有不同的环境,常用的有开发环境、测试环境、UAT环境、正式生产环境等等,不同的线上环境最常见的区别往往是访问路径不同,请求地址不同,当然这些问题其实可以用Nginx反向代理解决,但还有一些特殊情况,比如某个功能只在正式生产环境开放,后端不方便用代码,那么此时就需要前端获取当前打包的环境,这就是自定义配置运行命令的意义所在,当你运行不同的指令生成不同环境的webpack包,必定可以获取到当前包的环境,可以在前端用代码控制实现不同环境有不同的显示操作
vue自定义配置运行命令有两种,一种是vuecli3以下,此时webpack配置文件在本地,可以根据修改本地webpack配置文件进行自定义;一种是在vuecli3以上,此时webpack配置在服务器上,不能通过修改本地配置文件来进行自定义,则需要通过新建vue.config.js和.env.XXX文件来进行自定义,下面我会分别记录操作方法
1、vuecli3以下
此时webpack配置文件在本地,修改即可,只要知道webpack的打包流程,引用了哪些文件,然后就可以据此新增修改
打包第一个文件肯定是package.json,每一个指令的执行都定义在此
package.json
"scripts": { "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "start": "npm run dev", "unit": "jest --config test/unit/jest.conf.js --coverage", "e2e": "node test/e2e/runner.js", "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs", "test": "node build/test.js", "build": "node build/build.js" },
首先看默认的npm run build打包,首先会执行build文件夹下的build.js文件
build/build.js
'use strict' require('./check-versions')() process.env.NODE_ENV = 'production' ... const webpack = require('webpack') const config = require('../config') const webpackConfig = require('./webpack.prod.conf') const spinner = ora('building for production...') spinner.start() rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { if (err) throw err webpack(webpackConfig, (err, stats) => { spinner.stop() if (err) throw err process.stdout.write(stats.toString({ colors: true, modules: false, children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. chunks: false, chunkModules: false }) + '\n\n') if (stats.hasErrors()) { console.log(chalk.red(' Build failed with errors.\n')) process.exit(1) } console.log(chalk.cyan(' Build complete.\n')) console.log(chalk.yellow( ' Tip: built files are meant to be served over an HTTP server.\n' + ' Opening index.html over file:// won\'t work.\n' )) }) })
主要关注这两条
const config = require('../config') const webpackConfig = require('./webpack.prod.conf')
这说明build.js打包依赖config文件夹和webpack.prod.conf.js文件
下面我们再看看webpack.prod.conf.js文件
webpack.prod.conf.js
'use strict' const utils = require('./utils') ...... const config = require('../config') ...... const baseWebpackConfig = require('./webpack.base.conf') ...... const env = process.env.NODE_ENV === 'test' ? require('../config/test.env') : require('../config/prod.env') const webpackConfig = merge(baseWebpackConfig, { module: { rules: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true, usePostCSS: true }) }, devtool: config.build.productionSourceMap ? config.build.devtool : false, output: { path: config.build.assetsRoot, filename: utils.assetsPath('js/[name].[chunkhash].js'), chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') }, plugins: [ ...... new OptimizeCSSPlugin({ cssProcessorOptions: config.build.productionSourceMap ? { safe: true, map: { inline: false } } : { safe: true } }), new HtmlWebpackPlugin({ filename: process.env.NODE_ENV === 'testing' ? 'index.html' : config.build.index, template: 'index.html', inject: true, minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true chunksSortMode: 'dependency' }), ...... // copy custom static assets new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../static'), to: config.build.assetsSubDirectory, ignore: ['.*'] } ]) ] }) if (config.build.productionGzip) { const CompressionWebpackPlugin = require('compression-webpack-plugin') webpackConfig.plugins.push( new CompressionWebpackPlugin({ asset: '[path].gz[query]', algorithm: 'gzip', test: new RegExp( '\\.(' + config.build.productionGzipExtensions.join('|') + ')$' ), threshold: 10240, minRatio: 0.8 }) ) } if (config.build.bundleAnalyzerReport) { const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin webpackConfig.plugins.push(new BundleAnalyzerPlugin()) } module.exports = webpackConfig
因为篇幅原因,我把一些注释和与打包无关的代码删掉了,这里重点关注三条
const utils = require('./utils') const config = require('../config') const baseWebpackConfig = require('./webpack.base.conf')
这说明webpack.prod.conf.js依赖config文件夹、webpack.base.conf.js文件和utils.js文件
我们再来看看build/utils.js文件
build/utils.js
'use strict' const path = require('path') const config = require('../config') ...... exports.assetsPath = function (_path) { let assetsSubDirectory = '' if (process.env.NODE_ENV === 'production') { assetsSubDirectory = config.build.assetsSubDirectory } else if (process.env.NODE_ENV === 'test') { assetsSubDirectory = config.test.assetsSubDirectory } else { assetsSubDirectory = config.dev.assetsSubDirectory } return path.posix.join(assetsSubDirectory, _path) } ......
可以看出utils.js依旧依赖config文件夹
build/webpack.base.conf.js
'use strict' const path = require('path') const utils = require('./utils') const config = require('../config') const vueLoaderConfig = require('./vue-loader.conf') ...... let outputPath = config.build.assetsRoot let outputPublicPath = '' if (process.env.NODE_ENV === 'production') { outputPublicPath = config.build.assetsPublicPath } else if (process.env.NODE_ENV === 'test') { outputPath = config.test.assetsRoot outputPublicPath = config.test.assetsPublicPath } else { outputPublicPath = config.dev.assetsPublicPath } module.exports = { context: path.resolve(__dirname, '../'), entry: { app: ['babel-polyfill', './src/main.js'] }, output: { path: outputPath, filename: '[name].js', publicPath: outputPublicPath }, resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), } }, module: { rules: [ ...(config.dev.useEslint ? [createLintingRule()] : []), { test: /\.vue$/, loader: 'vue-loader', options: vueLoaderConfig }, ...... ] }, node: { setImmediate: false, dgram: 'empty', fs: 'empty', net: 'empty', tls: 'empty', child_process: 'empty' } }
可以看出webpack.base.conf.js除了依赖config文件夹和utils.js文件还有一个vue-loader.conf.js文件
build/vue-loader.conf.js
'use strict' const utils = require('./utils') const config = require('../config') const isProduction = (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') let sourceMapEnabled = '' if (process.env.NODE_ENV === 'production') { sourceMapEnabled = config.build.productionSourceMap } else if (process.env.NODE_ENV === 'test') { sourceMapEnabled = config.test.productionSourceMap } else { sourceMapEnabled = config.dev.cssSourceMap } module.exports = { loaders: utils.cssLoaders({ sourceMap: sourceMapEnabled, extract: isProduction }), cssSourceMap: sourceMapEnabled, cacheBusting: config.dev.cacheBusting, transformToRequire: { video: ['src', 'poster'], source: 'src', img: 'src', image: 'xlink:href' } }
vue-loader.conf.js常规引用config文件夹和utils.js文件,就不单独讨论了
config文件夹
config文件夹下有多个文件,一般初始化至少有index.js、dev.env.js、prod.env.js,其中dev.env.js和prod.env.js里面是各自的专属配置,本文想要实现的功能就由此类文件来完成,每个文件里都可以配置不同的请求域名(当然也可以用Nginx反向代理),当前环境的标识等等
index.js
index.js里面就是config的常用配置,比如每个环境下的路径、目录、文件名等等配置都由此处配置
'use strict' const path = require('path') module.exports = { dev: { // Paths assetsSubDirectory: 'static', assetsPublicPath: '/', proxyTable: {}, // Various Dev Server settings host: '0.0.0.0', // can be overwritten by process.env.HOST port: 8080, // 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, useEslint: true, showEslintErrorsInOverlay: false, ...... }, build: { // Template for index.html index: path.resolve(__dirname, '../dist/index.html'), // Paths assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', assetsPublicPath: './', productionSourceMap: true, ...... }, test: { // Template for index.html index: path.resolve(__dirname, '../dist-test/index.html'), // Paths assetsRoot: path.resolve(__dirname, '../dist-test'), assetsSubDirectory: 'static', assetsPublicPath: './', ...... } }
vuecli3以下打包流程
上图就是vuecli3以下的webpack打包流程,可以看到webpack打包大概就用到这几个文件,当然背后还有更多的依赖,但是我们能控制的就这几个,那么想配置一个新的指令只要按照这个流程新增或者修改对应的文件就可以了,下面我以新增一个npm run test指令为例(因为我已经做完了这个,其实上面贴出来的代码细心的已经看到了)
新增npm run test指令
新增文件
需要新增的文件有build/test.js、build/webpack.test.conf.js、config/test.env.js
新增在我看来是最简单的,只需要照着build的文件复制一份改名,然后把代码里的config.build改为config.test,这里的config.build指的是config文件夹下index.js里的build对象,包含了打包时的一些配置,比如文件名,路径等等
修改文件
需要修改的文件有package.json、build/utils.js、build/webpack.base.conf.js、build/vue-loader.conf.js、config/index.js
package.json
package.json仿照build新增一条指令,运行build/test.js文件
build/utils.js
utils文件需要在以前判断的基础上加上一个test的判断
更改前
const assetsSubDirectory = process.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory
更改后
let assetsSubDirectory = '' if (process.env.NODE_ENV === 'production') { assetsSubDirectory = config.build.assetsSubDirectory } else if (process.env.NODE_ENV === 'test') { assetsSubDirectory = config.test.assetsSubDirectory } else { assetsSubDirectory = config.dev.assetsSubDirectory }
build/webpack.base.conf.js、build/vue-loader.conf.js、config/index.js等其他文件参照build/utils,将原本只对production和dev的判断加上一个test的判断
vuecli3以下自定义webpack打包指令小结
- 1、在package.json新增一条指令
- 2、根据新增指令在指定位置(通常是build文件夹)新增文件
- 3、新增config文件夹下自定义配置文件
- 4、修改config文件夹下index.js文件,新增对自定义配置
- 5、新增webpack conf文件
- 6、修改build/utils文件,增加对自定义的判断
- 7、修改webpack.base.conf.js文件。增加对自定义的判断
- 8、修改build/vue-loader.conf.js文件,增加对自定义的判断
2、vuecli3以上
此时webpack配置文件不在本地,想要修改打包配置需新建vue.config.js文件,vue.config.js文件和vuecli3以下时config文件夹下的index.js文件一样,保存默认的打包配置,当然如果没有.env.dev和.env.prod也可以新建,这两个.env文件和vuecli3以下时config文件夹下的.env文件一样,目的是保存当前环境下的配置。这几个文件都在vue项目第一级目录下,与src文件夹平级。
vue.config.js
const path = require('path'); let outputDir = '' if (process.env.VUE_APP_CURRENTMODE === 'prod') { // 为生产环境修改配置... outputDir = 'dist'; } else { // 为开发环境修改配置... outputDir = 'dist-test'; } module.exports = { // 基本路径 publicPath: './', // 输出文件目录 outputDir, // eslint-loader 是否在保存的时候检查 lintOnSave: true, configureWebpack: (config) => { config.entry.app = ["babel-polyfill", "./src/main.js"]; if (process.env.NODE_ENV === 'production') { // 为生产环境修改配置... config.mode = 'production'; } else { // 为开发环境修改配置... config.mode = 'development'; } Object.assign(config, { // 开发生产共同配置 resolve: { alias: { '@': path.resolve(__dirname, './src'), '@a': path.resolve(__dirname, './src/api'), '@u': path.resolve(__dirname, './src/utils'), '@p': path.resolve(__dirname, './src/pages'), '@c': path.resolve(__dirname, './src/components'), } } }); }, // 生产环境是否生成 sourceMap 文件 productionSourceMap: true, // enabled by default if the machine has more than 1 cores parallel: require('os').cpus().length > 1, // PWA 插件相关配置 // see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa pwa: {}, // webpack-dev-server 相关配置 devServer: { open: process.platform === 'darwin', host: '0.0.0.0', port: 8888, https: false, hotOnly: false, }, // 第三方插件配置 pluginOptions: { // ... } };
新增npm run test指令
新增文件
新增.env.test文件,仿照.env.prod新建,NODE_ENV最好和prod一样,为做区分可以配置一个字段如VUE_APP_CURRENTMODE为test与prod区分开,如果需要判断当前环境可以用此字段判别
NODE_ENV = 'production' VUE_APP_CURRENTMODE = 'test'
修改文件
修改vue.config.js,增加对VUE_APP_CURRENTMODE的判断
修改前
const path = require('path'); module.exports = { // 基本路径 publicPath: './', // 输出文件目录 outputDir, ...... }
修改后
const path = require('path'); let outputDir = '' if (process.env.VUE_APP_CURRENTMODE === 'prod') { // 为生产环境修改配置... outputDir = 'dist'; } else { // 为开发环境修改配置... outputDir = 'dist-test'; } module.exports = { // 基本路径 publicPath: './', // 输出文件目录 outputDir, ...... }
vuecli3以上自定义webpack打包指令小结
1、默认需有.env.dev、.env.prod、vue.config.js文件,如无可新建
2、新增.env.test文件
3、修改vue.config.js文件
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。