您的位置:首页 > 编程语言

Webpack多环境代码打包(测试、预发、正式环境)

2019-04-11 18:21 134 查看

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

在package.json文件的scripts中,会提供开发环境与生产环境两个命令。但是实际使用中会遇见 测试版、预发布版以及正式版代码相继发布的情况,这样反复更改服务器地址,偶尔忘记更改url会给工作带来很多不必要的麻烦。这样就需要在生产环境中配置 测试版本打包命令、预发布版本打包命令与正式版本打包命令。

具体步骤如下:

1. Package.json 文件中 增加命令行命令,并指定路径。

"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",  //开发环境打包命令
"start": "npm run dev",
"test": "node build/build-test.js", //测试环境打包命令
"pre": "node build/build-pre.js",  //预发布环境打包命令
"build": "node build/build.js", //正式环境打包命令
},

2. 在build文件中添加相应文件

build-test.js

/**
* 测试版
*/

'use strict'
require('./check-versions')()

process.env.NODE_ENV = 'production-test'

const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
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'
))

console.log(chalk.yellow(
'  Tip: built files are meant to be served over an HTTP server.\n' +
' '+ process.env.NODE_ENV
))
})
})

build-pre.js

/**
* 预发布版
*/

'use strict'
require('./check-versions')()

process.env.NODE_ENV = 'production-pre'

const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
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'
))

console.log(chalk.yellow(
'  Tip: built files are meant to be served over an HTTP server.\n' +
' '+ process.env.NODE_ENV
))
})
})

build.js

/**
* 正式版
*/
require('./check-versions')()

process.env.NODE_ENV = 'production'

var ora = require('ora')
var rm = require('rimraf')
var path = require('path')
var chalk = require('chalk')
var webpack = require('webpack')
var config = require('../config')
var webpackConfig = require('./webpack.prod.conf')

var spinner = ora('building for production...')
spinner.start()

rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, function (err, stats) {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n')

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'
))
console.log(chalk.yellow(
'  Tip: built files are meant to be served over an HTTP server.\n' +
' '+ process.env.NODE_ENV
))

//gulp 正式站 生成 zip文件,
var fs = require('fs');
function getPackageJsonVersion () {
// 这里我们直接解析 json 文件而不是使用 require,这是因为 require 会缓存多次调用,这会导致版本号不会被更新掉
return JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
};
var gulp = require('gulp');
var zip = require('gulp-zip');
var  filename=(new Date()).toLocaleString().replace(/:/g,'');
console.log(chalk.yellow(
'  Tip: zipName.\n' +
' '+ filename +getPackageJsonVersion()
))
//创建一个文件到V.txt 到 dist 目录

fs.writeFile('./dist/ver.txt','版本号:'+getPackageJsonVersion(),function(err){
if(err)
return console.error(err);
console.log('写入文件成功');
});

gulp.task('zip', function() {
gulp.src(['dist/**','README.md'])
.pipe(zip(`Philips_production_${filename}_v${getPackageJsonVersion()}.zip`))
.pipe(gulp.dest('dist1'));
});
//  gulp.run('zip')
})
})

3、在config文件中增加环境变量配置

prod-test.env.js 增加环境变量

/**
* 测试环境配置
*/

module.exports = {
NODE_ENV: '"production-test"'
}

prod-pre.env.js 增加环境变量

/**
* 预发环境配置
*/

module.exports = {
NODE_ENV: '"production-pre"'
}

prod.env.js

/**
* 生产环境配置(正式)
*/

module.exports = {
NODE_ENV: '"production"'
}

index.js

'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.

'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.

const path = require('path')

module.exports = {
dev: {  // dev开发 环境
evn:require('./dev.env'),
// 静态资源文件夹
assetsSubDirectory: 'static',
// 发布路径
assetsPublicPath: '/',
//配置代理(可跨域)
proxyTable: {
'/api': {
target: "http://sfe.crmclick.com",// 接口的域名
// secure: false,  // 如果是https接口,需要配置这个参数
changeOrigin: true,// 如果接口跨域,需要进行这个参数配置
pathRewrite: {
'^/api': '/'
}
},
// 注意: '/api' 为匹配项,target 为被请求的地址,因为在 ajax 的 url 中加了前缀 '/api',而原本的接口是没有这个前缀的,
//所以需要通过 pathRewrite 来重写地址,将前缀 '/api' 转为 '/'。如果本身的接口地址就有 '/api' 这种通用前缀,就可以把 pathRewrite 删掉。
},

// Various Dev Server settings
host: 'localhost', // 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, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-

/**
* Source Maps
*/

// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-module-eval-source-map',

// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,

cssSourceMap: true
},

build: { // 测试 预发布 正式生产 环境
evn:process.env.NODE_ENV == "production"
? require('./prod.env')
: process.env.NODE_ENV == "production-pre"
? require('./prod-pre.env')
: process.env.NODE_ENV == "production-test"
? require('./prod-test.env')
: require('./prod-test.env'), // 使用 config/prod.env.js 中定义的编译环境
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'), // 编译输入的 index.html 文件

// Paths
assetsRoot: path.resolve(__dirname, '../dist'), // 编译输出的静态资源路径
assetsSubDirectory: 'static', // 编译输出的二级目录
assetsPublicPath: '/', // 编译发布的根目录,可配置为资源服务器域名或 CDN 域名

/**
* Source Maps
*/

productionSourceMap: false, // 是否开启 cssSourceMap
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',

// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false, // 是否开启 gzip
productionGzipExtensions: ['js', 'css'], // 需要使用 gzip 压缩的文件扩展名

// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
}

4. 修改 build 文件夹下的 webpack.prod.conf.js

webpack.prod.conf.js

"use strict";
const path = require("path");
const utils = require("./utils");
const webpack = require("webpack");
const config = require("../config");
const merge = require("webpack-merge");
const baseWebpackConfig = require("./webpack.base.conf");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const OptimizeCSSPlugin = require("optimize-css-assets-webpack-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");

// const env = require('../config/prod.env')

console.log('------------------+'+process.env.NODE_ENV)

const env =
process.env.NODE_ENV === "production"
? require("../config/prod.env")
: process.env.NODE_ENV === "production-test"
? require("../config/prod-test.env")
: process.env.NODE_ENV === "production-pre"
? require("../config/prod-pre.env")
: require("../config/dev.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: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
"process.env": env
}),
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath("css/[name].[contenthash].css"),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: true
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.build.index,
template: "index.html",
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: "dependency"
}),
// keep module.id stable when vendor modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
minChunks(module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(path.join(__dirname, "../node_modules")) === 0
);
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: "manifest",
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: "app",
async: "vendor-async",
children: true,
minChunks: 3
}),

// 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;

5. 修改 build 文件夹下的 webpack.base.conf.js

webpack.base.conf.js

"use strict";
const path = require("path");
const utils = require("./utils");
const config = require("../config");
const vueLoaderConfig = require("./vue-loader.conf");

function resolve(dir) {
return path.join(__dirname, "..", dir);
}

module.exports = {
context: path.resolve(__dirname, "../"),
entry: {
app: "./src/main.js"
},
output: {
path: config.build.assetsRoot,
filename: "[name].js",
publicPath:
process.env.NODE_ENV === "development"
? config.dev.assetsPublicPath
: config.build.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
vue$: "vue/dist/vue.esm.js",
"@": resolve("src")
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: "babel-loader",
include: [
resolve("src"),
resolve("test"),
resolve("node_modules/webpack-dev-server/client")
]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: "url-loader",
options: {
limit: 10000,
name: utils.assetsPath("img/[name].[hash:7].[ext]")
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: "url-loader",
options: {
limit: 10000,
name: utils.assetsPath("media/[name].[hash:7].[ext]")
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: "url-loader",
options: {
limit: 10000,
name: utils.assetsPath("fonts/[name].[hash:7].[ext]")
}
},
{
test: /\.less$/,
loader: "style-loader!css-loader!less-loader"
}
]
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: "empty",
fs: "empty",
net: "empty",
tls: "empty",
child_process: "empty"
}
};

6.创建一个公用的页面,用来增加环境变量判断 以及 对axios进行配置 & 拦截

untils/config.js

import axios from 'axios';
import qs from 'qs';
import VueRouter from 'vue-router';
import routers from '../router';

const router = new VueRouter({
routers
});

// 设置接口地址
let baseUrl = '';
// 上传图片文件路径
let uploadBaseUrl = '';
// alert(process.env.NODE_ENV)
if (process.env.NODE_ENV == 'development' || process.env.NODE_ENV == 'production-test') {
// 开发/测试 环境
baseUrl = 'https://sfe.crmclick.com';
uploadBaseUrl = 'http://sfe.crmclick.com/uploadNoZip.aspx';
}else if(process.env.NODE_ENV == 'production-pre'){
// 预发 环境
baseUrl = 'xxxx';
uploadBaseUrl = 'xxxx';
}else if(process.env.NODE_ENV == 'production'){
// 正式环境
baseUrl = 'xxxx';
uploadBaseUrl = 'xxxx';
}

// axios 配置 & 拦截
// 响应时间
axios.defaults.timeout = 20000;
// 是否允许携带cookie
// withCredentials为true的情况下,后端要设置Access-Control-Allow-Origin为你的源地址,例如http://localhost:8080,不能是*,
// 而且还要设置header('Access-Control-Allow-Credentials: true');
axios.defaults.withCredentials = false;
// 配置请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
// POST传参序列化(添加请求拦截器)
axios.interceptors.request.use((config) => {
//在发送请求之前强制降低复杂请求
if(config.method  === 'post'){
config.data = qs.stringify(config.data);
}
return config;
},(error) =>{
return Promise.reject(error);
});
// 返回状态判断(添加响应拦截器)
axios.interceptors.response.use((res) =>{
//对响应数据做些事
if(!res.data){
return Promise.reject(res);
}

if(res.data.respCode == '111'){
// 登陆 过期
sessionStorage.removeItem('User');
alert("登陆已失效,请重新登陆");
router.go('/');
return
}
return res;
}, (error) => {
return Promise.reject(error);
});

export {
baseUrl,
uploadBaseUrl,
}

7、在页面import baseUrl axios等 即可针对不同环境进行接口调用

<script>
import axios from 'axios';
import { baseUrl } from '../untils/config';
import HeaderMenu from './common/HeaderMenu';//引用子组件

export default {
name: 'Gift',
data() {
return {
title:"礼品展示",
PageIndex:1,
PageSize:10,
GiftList:[],
loading:false,
LoadMore:true,
}
},
components: {
"V-Menu": HeaderMenu,
},
created() {
this.GetGiftList();
},
mounted() {

},
methods: {
GetImg(url){
return baseUrl + url;
},
GetGiftList() {
axios.post(baseUrl+"/suzhou/Handler/Points.ashx",{
op: "GiftList",
PageIndex: this.PageIndex,
PageSize:this.PageSize
}).then(res => {
if (res.data.state == 1) {
if(this.PageIndex==1){
this.GiftList = res.data.rows;
}else{
var newGiftArr = this.GiftList.slice(0);
if(res.data.rows.length>0){
for(var i=0;i<res.data.rows.length;i++){
newGiftArr.push(res.data.rows[i]);
}
}
this.GiftList = newGiftArr;
}
if(res.data.rows.length < this.PageSize){
this.LoadMore = false;
}
}else if(res.data.rows == -1){
this.$toast.warning(res.data.msg);
}
})
.catch(error => {
debugger
this.$toast.error(error);
});
},
load:function(){
if(this.LoadMore){
this.loading = true;
setTimeout(function() {
this.loading = false;
this.PageIndex ++;
this.GetGiftList();
}, 2000)
}
},
}
}
</script>

8、打包命令如下

npm run dev      开发环境
npm run test     测试环境
npm run pre      预发环境
npm run build    正式环境
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Webpack iOS Axios Chalk