webpack入门之开发环境(mode、dev-server、devtool)

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情

简介

虽然开发、测试、生产的代码都是使用webpack构建工具来构建,但是对于不同的环境对构建后代码的要求肯定是不一样的。

比如,我们肯定想本地开发的时候需要构建的快、能快速定位到源码错误位置。对于生产环境我们肯定希望构建后的代码体积要足够小,图片能压缩、代码能分割等等。

对于这些要求,肯定就会涉及到不同的环境的配置。今天我们就来说说webpack的环境配置。

mode

mode 配置选项,告知 webpack 使用相应模式的内置优化。

mode的值有none | development | production三个

对于不同的mode值,webpack有不同的优化策略。

引用官网的一张图:

image.png

mode默认值

mode默认值是production

配置方式

配置方式主要有两种

  1. 直接定义
module.exports = {
  mode: 'development',
};
复制代码
  1. CLI 参数中传递:
$ webpack --mode=development
复制代码

可以看到第一种方式不够灵活,不能根据不同环境自动变更。第二种方式通过命令行参数进行传递,相对更灵活,但是由于mode只有developmentproduction两个值,不太能满足我们日常开发的需要,所以我们需要另外再改造下。

一般我们的环境会有本地开发环境、测试环境、生产环境。所以我们就需要定义三种环境,不同的环境使用不同的配置。

获取mode值

对于第二种通过命令行参数传递mode值,我们怎么才能在webpack.config.js中获取到该值呢?

其实我们只要把文件形式改写下就可以了,由导出对象形式改为导出函数就能获取到了。

// webpack.config.js

const config = {
  entry: './app.js',
  mode: 'production'
  //...
};

module.exports = (env, argv) => {
  console.log(argv.mode) // development、none、production

  return config;
};
复制代码

前面说了,仅仅两个模式不能满足日常开发需求,所以我们需要再定义其它的参数来进行更细化的区分。

自定义多种环境

这里我们需要使用到cross-env,它的作用就是能跨环境传递环境参数,也就是能同时兼容mac、windows、linux

我们先安装下

npm i cross-env
复制代码

然后我们在package.json里面配置构建命令

{
  "scripts": {
    "dev": "cross-env MODE=development webpack",
    "staging": "cross-env MODE=staging webpack",
    "build": "cross-env MODE=production webpack",
  }
}
复制代码

dev构建开发环境,staging构建测试环境,build构建生产环境。

三个环境传递三个不同的MODE值,我们可以在webpack.config.js中通过process.env.MODE获取,然后进行不同的配置啦。

// webpack.config.js

console.log("process.env.MODE=", process.env.MODE);

module.exports = (env, argv) => {
  //...

  return config;
};
复制代码

我们来试试

npm run dev

image.png

npm run staging

image.png

npm run build

image.png

可以看到,我们通过不同的命令就能获取到不同的MODE值啦。有了不同的MODE值我们就可以进行不同环境的配置啦。

比如,我们需要不同的环境定义不同的mode,开发环境设置modedevelopment,测试和生产环境我们设置modeproduction,进行不同的优化。

// webpack.config.js

console.log("process.env.MODE=", process.env.MODE);

module.exports = (env, argv) => {
  if (process.env.MODE === "development") {
    config.mode = "development";
  } else {
    config.mode = "production";
  }

  return config;
};
复制代码

前面说了,mode定义后,会同步设置process.env.NODE_ENV的值,我们可以在我们的源码里面获取到。

如果modedevelopmentprocess.env.NODE_ENV 的值为 development,如果modeproductionprocess.env.NODE_ENV 的值为 production

我们啦测试下,定义入口文件index.js,在里面输出process.env.NODE_ENV的值。

// index.js
console.log(process.env.NODE_ENV);
复制代码

我们分别构建,然后运行构建后的js

npm run dev

image.png

npm run staging

image.png

npm run build

image.png

可以看到,开发环境process.env.NODE_ENV的值为development,测试和生产环境的值是production

这个没问题,那么我们上面设置的process.env.MODE的值能获取到吗?

代码中获取自定义环境变量值

我们再来试试

// index.js
console.log(process.env.NODE_ENV);
console.log(process.env.MODE);
复制代码

再次构建,发现居然是undefined

image.png

这就有问题啦,如果我们代码中需要根据不同环境做不同处理那应该怎么办?其实我们可以用process.env.NODE_ENV的思路,其实它就是根据mode的不同使用DefinePlugin插件来动态设置值的,所以我们也使用这个插件处理下。

const webpack = require("webpack");

// ...

module.exports = (env, argv) => {
  if (process.env.MODE === "development") {
    config.mode = "development";
  } else {
    config.mode = "production";
  }
  // 根据参数,重新定义三种模式
  config.plugins.push(
    new webpack.DefinePlugin({
      "process.env.MODE": JSON.stringify(process.env.MODE),
    })
  );

  return config;
};
复制代码

我们分别构建,然后运行构建后的js

npm run dev

image.png

npm run staging

image.png

npm run build

image.png

可以看到,process.env.NODE_ENVprocess.env.MODE的值都能在我们的代码中正确获取到了,这样我们不仅能根据不同环境配置我们的webpack.config.js,还能在我们的代码中根据不同的环境进行不同的处理。

这里我们根据不同的环境只设置了mode,其实还有很多属性都可以在不同的环境进行不同的配置,比如我们下面要说的devtool

devtool

devtool用于控制是否生成,以及如何生成 source map

什么是 sourceMap

SourceMap 是一个映射关系。能够帮我们更好的定位源码的错误。

举个例子,现在我们发现打包出来的 dist 目录下的 main.js 的 97 行报错了,但因为他是打包后的文件,我们知道 main.js 第几行报错其实没有任何意义。这个时候 sourcemap 就出来帮我们解决了这个问题,因为他是打包文件和源码的一个映射关系,它知道 dist 目录下 main.js 文件的 97 行 实际上对应的 src 目录下的 index.js 文件的第几行,这样我们就能够快速定位问题,并进行修复了。

devtool默认值

devtool默认值在生产环境下,也就是modeproduction,值是false。在开发环境下也就是modedevelopment,值是eval

devtool种类

其实devtool的值还有很多,不同的值会生成不同的SourceMap

image.png

从上图中我们可以看到 devtool 有非常多的配置,不同的配置构建的速度会有一些差异,中间的很多参数都是可以穿插使用的。

更多值可以参看devtool官方文档

我们简单来测试下,在源码index.js文件中使用一个未定义的变量

// index.js

// ...
// 定义一个错误
console.log(e);
复制代码

总的来说说,其实devtool主要分为以下几类,我们分别测试下

false

false就是不使用SourceMap,所以它不会生成SourceMap文件。

image.png

因此它就不能准确定位到源码出错位置。

image.png

inline

inine 有这个的配置,直接会将 .map 文件以 base64 的形式直接打包到对应的 js 中去,而不会单独生成.map文件,从而加快相应的速度。

image.png

可以看到这种方式它能精准定位到源码行数

image.png

cheap

cheap 有这个的配置,意思是 map 文件只会帮你定为到具体的某一行,并不会把代码定位到 具体的 某一行 某一列,从而加快速度;cheap 还有一个作用,就是这个选项只使针对业务代码,也就是说只能定位到业务代码里面的错误,并不能定位到我们引用的第三方文件(比如说 loader,第三方模块)的错误。

会单独生成.map文件

image.png

image.png

定位的稍微有点问题,不那么准确

image.png

module

module 有这个的配置,意思是 它不仅会帮我们定位 自己的业务代码中的错误,还会同时帮我们定位第三方模块的错误。

eval

eval 有这个的配置,使用eval包裹模块代码,并且使用 //@sourceURL引入SourceMap文件,这个是打包速度最快,性能最好的的一种方式,但是有的时候,对于代码比较复杂的情况,它提示出来的错误可能不够全面。

hidden

生成 SourceMap 文件,但不使用。

最佳实践

有了上面的基础,我们就能根据不同环境进行不同devtool的配置啦。

const webpack = require("webpack");

// ...

module.exports = (env, argv) => {
  if (process.env.MODE === "development") {
    config.mode = "development";
    config.devtool = "eval-cheap-module-source-map"
  } else if(process.env.MODE === "staging") {
    config.mode = "production";
    config.devtool = "source-map"
  } else {
    config.mode = "production";
    config.devtool = false
  }
  // 根据参数,重新定义三种模式
  config.plugins.push(
    new webpack.DefinePlugin({
      "process.env.MODE": JSON.stringify(process.env.MODE),
    })
  );

  return config;
};
复制代码

本地开发我们配置eval-cheap-module-source-map"使用最详细的SourceMap

测试环境我们有可能需要在线调试错误,所以配置source-map

生产环境直接禁用SourceMap,一是能缩小构建后包的体积,其次能防止别人窃取源码。

dev-server

前面讲的,开发环境每次修改代码还要执行npm run dev,这样效率太低了,有没有更好的更快的方法呢?

当然是有的,我们可以使用webpack-dev-server

webpack-dev-server支持热部署、跨域代理、数据模拟等等。

安装

我们先来安装下

npm i webpack-dev-server -D
复制代码

配置

使用webpack-dev-server后我们的构建命令不再是webpack了,而是webpack-dev-server,所以我们首先修改下构建命令。

"dev": "cross-env MODE=development webpack-dev-server"
复制代码

接下来,我们在webpack.config.js里面进行配置

const config = {
  // ...
  devServer: {
    // 新版 配置static而不是
    static: path.resolve(__dirname, "./devdist"), // 静态文件目录
    port: 8080, // 端口号
    open:true  // 是否自动打开浏览器
  },
}
复制代码

static就是服务的目录,这里我们定位到构建后的目录。

我们执行npm run dev,就会自动打开浏览器,在8080端口启动服务了。我们修改代码就能实时编译了。

如果有调用后端api需求的话,我们还可以配置代理

配置代理

const config = {
  // ...
  devServer: {
    // 新版 配置static而不是
    static: path.resolve(__dirname, "./devdist"), // 静态文件目录
    port: 8080, // 端口号
    open:true,  // 是否自动打开浏览器
    
    proxy: {
      '/api': 'http://localhost:3000',
      // 接受在 HTTPS 上运行且证书无效的后端服务器
      secure: false,
    },
  },
}
复制代码

当我们请求/api的时候就可以代理到http://localhost:3000上去了。

mock数据

我们还可以利用webpack-dev-servermock数据。

我们在我们的index.js添加请求

// index.js

fetch("/user")
  .then((res) => res.json())
  .then((data) => {
    console.log(data);
  });
复制代码

然后使用devServer进行数据mock

devServer: {
  // 新版 配置static而不是
  static: path.resolve(__dirname, "./devdist"), // 静态文件目录
  compress: true, //是否启动压缩 gzip
  port: 8080, // 端口号
  // open:true  // 是否自动打开浏览器
  
  // 新版不是before,而是onBeforeSetupMiddleware
  onBeforeSetupMiddleware(devServer) {
    devServer.app.get("/user", (req, res) => {
      res.json({ name: "randy" });
    });
  },
},
复制代码

构建,在浏览器控制台可以看到我们mock的数据输出来了。

image.png

当然devServer远不止这些功能,大家可以看看devServer官方文档

后记

好了,关于webpack环境相关的笔者今天先讲到这里啦。

感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!

猜你喜欢

转载自juejin.im/post/7127576450378842119