4

前篇文章 2020年,《深入浅出Webpack》(吴浩麟2018/01)学习总结

我写了学习webpack的目的和过程,正所谓程序员的价值,往往通过他写的代码带给社会的价值来体现。那么这篇文章,我就结合自己目前所做的项目,介绍如何在修改更少代码的情况下建立多个开发环境及多个生产环境。

前言

业务需求背景:笔者所服役的公司是一个小型外包,前后端并行开发,团队都是按项目情况从公司其他地方的员工分配过来的人员,默契不算太高,连产品经理都是其他公司的人员。

笔者这么个还算新人的前端小白,也被误打误撞成了前端的负责人(当然,工资待遇是不会提高的,只有工作时间会提高)

使用的是vue-cli2.9的模认模版,(vue-cli2之前的模板都还是用了原生的webpack,现在最新的vue-cli3、vue-cli4都是用了vue团队做的插件来布置工作环境,有很多配置不但要懂webpack,还要去学习vue-cli了。)

需求:新建立一个用于mock数据模拟的dev环境,修改build打包异步请求的地址,建立5个不同的打包地址,以供甲方不同部门的审查,并且开发环境与生产环境的NODE_ENV值不变,还分别是developmentproduction

项目开发完成度已接近70%,不宜修改过多的业务代码,并且前端是多人开发,新环境应新增启动命令,而不是用旧命令去执行新功能。

设计

1. dev环境设计

项目处于一边开发,一边对接接口,这是由于很多数据由甲方提供模型,再由后端同事处理返回(同时,测试也是用后端的测试数据)个别接口数据后端开发比前端快,前端用devServer,跟据返回数据再开发而无需使用mock模拟。如遇后端还没有提供接口的请况,则启用mock模拟的环境

npm run dev //运行普通dev环境,可用于对接现有接口
npm run dev:mock //运行专门的mock模拟环境

2. production环境设计

由于项目经常要打包多个不同的axios.baseUrl地址发给不同的人(本公司项目负责人,甲方测试,甲方测试2,甲方的甲方服务地址(所以我其实是乙方的乙方的乙方)共有5个地)

分别运行不同的地址,打包到不同的文件夹。

npm run build //普通打包,以放代码其他人接手后不看文档
npm run build:none //打包地址寻打当前服务器目录 => '/'
npm run build:193  //打包为http://193.xxx.xx.xxx:8080/xxx/xx
npm run build:192  //打包为http://192.xxx.xx.xxx:8080/xxx/xx
npm run build:113  //打包为http://113.xxx.xx.xxx:8080/xxx/xx
npm run build:10  //打包为http://10.xxx.xx.xxx:8080/xxx/xx

然后,最后的包分别放在项目跟目录下的: dist 、dist-none、dist-193、dist-192、dist-113、dist-10

开始

1. Development的修改

根据上一篇文章对webpack的学习( 2020年,《深入浅出Webpack》(吴浩麟2018/01)学习总结),webpack可以通过在package.json的script中通过命令向node环境传递参数,写法:

//package.json
{
    ”script“: {
        "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
        "dev:mock": "ENV_NUM=0 npm run dev",
    }
}

这样,如果运行 npm run dev:mock就会将ENV-NUM的值传给process.env,在项目中就可以通过process.env.ENV_NUM 拿到值,为 ’0‘;

但是webpack 的NPM.script 传参写法在不同系统上兼容不一样,有可能会导致系统卡死。(我是用mac的,我的同事全是用windows,说不定给甲方的代码也会有用lunix的大神,当然如果是用服务器打包,那是lunix多)

网友介绍了用cross-env插件来处理

安装 npm i cross-env --save

同时package.json修改如下:

//package.json
{
    ”script“: {
        "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
        "dev:mock": "cross-env ENV_NUM=0 npm run dev",
    }
}

在ENV_NUM=0前加多了一个cross-env

细心的我已经发现了,vue-cli的项目中,在设定webpack.config.js时,它又在代码里从新修改了一下process.env的值,所以在npm.script中是设定了一个ENV-NUM,但是会在接下来被配置中的设定从新赋值,从而导致在项目代码中找不到。

plugins: [
    new webpack.DefinePlugin({
      'process.env': require('../config/dev.env'),//在此对process.env进行了重新赋值
    }),
    ...
]

所以,在config目录下,

修改dev.env.js,注意加多了ENV_NUM: '"1"'

'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  ENV_NUM: '"1"'
})

同时,新建一个文件 dev.mock.js,ENV_NUM的值为'"0"'

'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  ENV_NUM: '"0"'
})

再修改build/webpack.dev.conf.js

//新增env
const env = process.env.ENV_NUM === '0' 
  ? require('../config/mock.env') 
  : require('../config/dev.env');
//plugins修改为
plugins: [
    new webpack.DefinePlugin({
      'process.env': env
    }),
    //其他不变
    ...

axios 的url处设定为一个数组,并且数组的第一个元素为mock接口,用于dev:mock。第二个元素为真实后台接口,用于dev和build

例:

axios.request({
    // /mock/user 用于模拟
    url: ['/mock/user','/user'],
})

同时,在axios的请求拦截处区分传入的url

// src/libs/axios.js
 //请求拦截
  service.interceptors.request.use(
    config => {
      let url = getNowUrl(config.url);
      config.url = url;
      return config;
    },
    error => {
      console.log(error);
      
      return Promise.reject(error)
    }
  );
  
//src/libs/utils.js
export const getNowUrl = (url) => {
  let num; 
  //0使用数据模拟,1不使用数据模拟
  num = process.env.ENV_NUM;

  if(Array.isArray(url)){
    if(url.length === 1){
      num = "0";
    }
    return url[num];
  }else{
    return url;
  }

}

这样,当我们运行npm run dev:mock时,它会请求URL数组中的第一个,而运行npm run dev或npm run build时,就会请求第二个,并且当url不是数组或数组只有一个元素,都能打到可用的地址。

2. production的修改

按照development的方法只要给process.env加入多一个参数就可以在项目中区分不同的地址了。但是由于build只是改了打包地址并且地址有很多个,如果每个地址再新建一个prod.env.js文件,大量的重复代码会让项目显得臃肿。

于是我想到,在webpack修改process.env之后,再将参数注入process.env中

根据设计,只需要修改process.env 和打包后输出文件夹,再在代码中跟据process.env中约定的字段的不同值来选择不同的baseUrl。

先来看看vue-cli的默认配置:

baseUrl地址写在:src/config/index.js

baseUrl: {
    dev: '',
    pro: 'http://193.xxx.xx.xxx:8080/',//生产环境的地址
    test: '',
}

在axios的配置文件中选择

//src/libs/axois.js
const baseUrl = process.env.NODE_ENV === 'development' 
? config.baseUrl.dev
: config.baseUrl.pro

const service = axios.create({
  baseURL:  baseUrl,
  timeout: 10000,
})

修改开始:

package.json中加入BASE_URL,值为数字形式

{
    "scripts": {
        ...
        "dev:mock": "cross-env ENV_NUM=0 npm run dev",
        "build:none": "cross-env BASE_URL=0 npm run build",
        "build:193": "cross-env BASE_URL=1 npm run build",
        "build:113": "cross-env BASE_URL=2 npm run build",
        "build:192": "cross-env BASE_URL=3 npm run build",
        "build:10": "cross-env BASE_URL=4 npm run build"
    }
}

src/config/index.js加入一个属性prodUrl为数组

baseUrl: {
    dev: '',
    pro: 'http://193.xxx.xx.xxx:8080/',
    test: '',
    prodUrl:[
      '/',
      'http://193.xxx.xx.xxx:8080/',
      'http://113.xxx.xxx.xxx:8082/xxxx/xxx/',
      'http://192.xxx.xx.xxx:8082/xxxx/xxx/',
      'http://10.xx.xxx.xxx:8082/xxxx/xxx/',
    ]
  },

修改webpack的production配置

//build/webpack.prod.conf.js

const env = process.env.NODE_ENV === 'testing'
  ? require('../config/test.env')
  : require('../config/prod.env');
  
//paths
let assetsRoot = ''; //新增
let htmlIndex = '/index.html'; //新增

//新增
console.log('env',env)
if(env.NODE_ENV === '"production"'){//注意,此处有两个引号
  env.BASE_URL = utils.findBaseUrl();
  assetsRoot = utils.findPaths();
  htmlIndex = assetsRoot + htmlIndex;
}
console.log('env2',env);
console.log('assetroot',assetsRoot);
console.log('htmlIndex',htmlIndex);
...
 plugins: [
    new webpack.DefinePlugin({
      'process.env': env
    }),
    ...
    //修改这个对象的配置
    new HtmlWebpackPlugin({
      filename: process.env.NODE_ENV === 'testing'
        ? 'index.html'
        : (assetsRoot 
          ? path.resolve(__dirname,htmlIndex) 
          : config.build.index),
      template: 'index.html',
      inject: true,
      ...
     }
     ...
  ]
// build/utils.js 导出如下两个函数
exports.findBaseUrl = function(){
// 特别注意此处的-1有要用两个不同的引号(或JSON.stringify('-1'))
  let baseUrlNum = '"-1"';
  if(process.env.BASE_URL){
    baseUrlNum = process.env.BASE_URL;
  };
  return baseUrlNum;
}

exports.findPaths = function(){
  let path = '../dist';
  if(process.env.BASE_URL){
    switch(process.env.BASE_URL) {
      case '0':
        path = '../dist-none';
        break;
      case '1':
        path = '../dist-193';
        break;
      case '2':
        path = '../dist-113';
        break;
      case '3':
        path = '../dist-192';
        break;
      case '4':
        path = '../dist-10';
        break;
      default:
        path = '../dist';
        break;
    }
  };
  return path;
}

上面的方法修改了webpack打包后输出的地址,避免了不同的地址全都输出在dist文件夹。

接下来修改axios的baseURL

// src/libs/axios.js

import { getNowUrl, findBaseUrl } from './util';

const service = axios.create({
  baseURL:  findBaseUrl(),
  timeout: 10000,
})
// src/libs/utils.js

export function findBaseUrl(){
  let baseUrl = '/';
  if(process.env.NODE_ENV === 'development'){
    baseUrl = config.baseUrl.dev;
  }else{
    if(process.env.BASE_URL){
      baseUrl = process.env.BASE_URL === '-1' 
      ? config.baseUrl.pro
      : config.baseUrl.prodUrl[process.env.BASE_URL];
    }else{
      baseUrl = config.baseUrl.pro
    }
  };
  return baseUrl;
};

OK完成!

在控制台输入npm run build:none试一下:

1ya63R.png

打印的路径都正常了!

再看看最后输出

1ydK2R.png

总结

特别注意的是webpack环境变量接收的值是一个由两种引号括起的字符串,如'"development"'所以在设定环境变里时必须按文档的规范进行,或者用JSON.stringify()。~~~~

以上两种环境的配置都没有改变默认的开发方式,不影响其他开发人员。这次的修改也是在新型肺炎多出的假期中。

找了很久的资料,都没有找到很符合的案例,网上大多是加多一个test环境,所以只能自己多多学习,看完了一本webpack的书,再结合项目慢慢摸索出来的。在这期间也踩过很多的坑,同时也希望能帮到大家。

我想,大部分的软件开发者都是在一线城市工作吧!一线城市的人员都来自四面八方,正式回岗复工后希望大家都能小心点,同时2月3号至2月9号在家上班的这几天的工资都能拿到。

如果你觉得有什么更好的方法,也可以评论讨论。如果你觉得有用,给我一个小心心吧。


maYunLaoXi
65 声望3 粉丝

前端开发工程师,自由摄影师