0

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

    2023.06.11 | admin | 178次围观

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

    本文 webpack 版本 ^5.73.0、webpack-cli 版本 ^4.10.0

    简介

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

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

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

    mode

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

    mode的值有none | development | production三个

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

    引用官网的一张图:

    mode默认值

    mode默认值是production。

    配置方式

    配置方式主要有两种

    直接定义

    module.exports = {
      mode: 'development',
    };
    

    从 CLI 参数中传递:

    $ webpack --mode=development
    

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

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

    获取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;
    };
    

    前面说了,仅仅两个模式不能满足日常开发需求jq浏览器版本太低请升级,所以我们需要再定义其它的参数来进行更细化的区分。

    自定义多种环境

    这里我们需要使用到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

    npm run staging

    npm run build

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

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

    // 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的值,我们可以在我们的源码里面获取到。

    如果mode为development则process.env.NODE_ENV的值为development,如果mode为production则process.env.NODE_ENV的值为production。

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

    // index.js
    console.log(process.env.NODE_ENV);
    

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

    npm run dev

    npm run staging

    npm run build

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

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

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

    我们再来试试

    // index.js
    console.log(process.env.NODE_ENV);
    console.log(process.env.MODE);
    

    再次构建,发现居然是undefined

    这就有问题啦,如果我们代码中需要根据不同环境做不同处理那应该怎么办?其实我们可以用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

    npm run staging

    npm run build

    可以看到,process.env.NODE_ENV和process.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默认值在生产环境下jq浏览器版本太低请升级,也就是mode为production,值是false。在开发环境下也就是mode为development,值是eval。

    devtool种类

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

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

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

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

    // index.js
    // ...
    // 定义一个错误
    console.log(e);
    

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

    false

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

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

    inline

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

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

    cheap

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

    会单独生成.map文件

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

    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的时候就可以代理到:3000上去了。

    mock数据

    我们还可以利用webpack-dev-server来mock数据。

    我们在我们的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的数据输出来了。

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

    系列文章

    webpack入门之css处理(css预处理器和css后置处理器)

    webpack入门之图片、字体、文本、数据文件处理

    webpack入门之js处理(babel、babel polyfill)

    webpack入门之ts处理(ts-loadr和babel-loader的选择)

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

    后记

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

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

    版权声明

    本文仅代表作者观点。
    本文系作者授权发表,未经许可,不得转载。

    发表评论