五、webpack常见的插件和模式 及 webpack搭建本地服务器

2022/8/18 前端工程化webpackwebpack常见插件webpack模式配置webpack搭建本地服务开启HMR模块热替换区分生产开发环境

webpack整个演练案例代码 (opens new window)

# 1、webpack常见的插件Plugin

# 1.1.认识Plugin

  • webpack插件描述 (opens new window)

  • webpack插件一览 (opens new window)

  • Webpack的另一个核心是Plugin,官方有这样一段对Plugin的描述:

    • While loaders are used to transform certain types of modules, plugins can be leveraged to perform a wider range of tasks like bundle optimization, asset management and injection of environment variables.
  • 上面表达的含义翻译过来就是:

    • Loader是用于特定的模块类型进行转换;
    • Plugin可以用于执行更加广泛的任务,比如打包优化、资源管理、环境变量注入等;

1.png

# 1.2.CleanWebpackPlugin插件

  • 前面我们演示的过程中,每次修改了一些配置,重新打包时,都需要手动删除dist文件夹:

  • 我们可以借助于一个插件来帮助我们完成,这个插件就是CleanWebpackPlugin;

    • 首先,我们先安装这个插件:
    npm install clean-webpack-plugin -D
    
    1
    • 之后在插件中配置即可

2.png

module.exports = {
  //...
  output: {
    clean: true, // 在发出(打包完成)之前清理输出目录
  },
};
1
2
3
4
5
6

# 1.3.HtmlWebpackPlugin插件

  • 另外还有一个不太规范的地方:
    • 我们的HTML文件是编写在根目录下的,而最终打包的dist文件夹中是没有index.html文件的。
    • 在进行项目部署的时,必然也是需要有对应的入口文件index.html;
    • 所以我们也需要对index.html进行打包处理;
  • 对HTML进行打包处理我们可以使用另外一个插件:HtmlWebpackPlugin;
npm install html-webpack-plugin -D
1
  • 之后在插件中配置即可

3.png

# 1.4.生成index.html分析

  • 我们会发现,现在自动在dist文件夹中,生成了一个index.html的文件:
    • 该文件中也自动添加了我们打包的bundle.js文件;

4.png

  • 这个文件是如何生成的呢?
    • 默认情况下是根据ejs的一个模板来生成的;
    • 在html-webpack-plugin的源码中,有一个default_index.ejs模块;

5.png

# 1.5.自定义HTML模板

  • 如果我们想在自己的模块中加入一些比较特别的内容:
    • 比如添加一个noscript标签,在用户的JavaScript被关闭时,给予响应的提示;
    • 比如在开发vue或者react项目时,我们需要一个可以挂载后续组件的根标签 ;
  • 这个我们需要一个属于自己的index.html模块:
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

65866ba6763d59312.png

# 1.6.自定义模板数据填充

  • 上面的代码中,会有一些类似这样的语法<% 变量 %>,这个是EJS模块填充数据的方式。
  • 在配置HtmlWebpackPlugin时,我们可以添加如下配置:
    • template:指定我们要使用的模块所在的路径;
    • title:在进行htmlWebpackPlugin.options.title读取时,就会读到该信息;

7.png

# 1.7.DefinePlugin插件的介绍

  • 但是,这个时候编译还是会报错,因为在我们的模块中还使用到一个BASE_URL的常量:

8.png

  • 这是因为在编译template模块时,有一个BASE_URL:
    • <link rel="icon" href="<%= BASE_URL %>favicon.ico">;
    • 但是我们并没有设置过这个常量值,所以会出现没有定义的错误;
  • 这个时候我们可以使用DefinePlugin插件;

# 1.8.DefinePlugin的使用

  • DefinePlugin允许在编译时创建配置的全局常量,是一个webpack内置的插件(不需要单独安装):
  • 这个时候,编译template就可以正确的编译了,会读取到BASE_URL的值;

9.png

  • 打包后生成的html

10.png

# 2、Mode配置(模式配置)

# 2.1.Mode配置

  • 前面我们一直没有讲mode。但是你会发现打包成功了,有时候会有一些警告;其中就有mode的警告
    • 但是当你把模式设置成开发模式时:mode:"development"
    • 开发模式不会有警告;因为不像生产模式一样要上线展示给大量用户,默认webpack打包忽略性能警告;但是如果是生产模式,如果打包生成的包过大,会有性能相关提示警告;
    • 如下:就是生产模式时打包后的警告

11aa1affa2e5b283eb.png

  • Mode配置选项,可以告知webpack使用相应模式的内置优化:
    • 默认值是production(生产模式)(什么都不设置的情况下);
    • 可选值有:'none' | 'development' | 'production';【development -> 开发模式】
  • 这几个选项有什么样的区别呢?

12.png

  • mode的配置
//在webpack.config.js文件中

module.exports = {
  mode:"development"
  //其他配置省略
}
1
2
3
4
5
6

# 2.2.Mode配置代表什么?

  • 如下图,设置一个模式,相当于设置了如下红色字体的这些配置;

13.png

# 3、开启本地服务器

# 3.1.为什么要搭建本地服务器?

  • 目前我们开发的代码,为了运行需要有两个操作:
    • 操作一:npm run build,编译相关的代码;
    • 操作二:通过live server或者直接通过浏览器,打开index.html代码,查看效果;
  • 这个过程经常操作会影响我们的开发效率,我们希望可以做到,当文件发生变化时,可以自动的完成 编译 和 展示;
  • 为了完成自动编译,webpack提供了几种可选的方式:
    • webpack watch mode;
    • webpack-dev-server(常用);
    • webpack-dev-middleware;

# 3.2.webpack-dev-server

  • 上面的方式可以监听到文件的变化,但是事实上它本身是没有自动刷新浏览器的功能的:
    • 当然,目前我们可以在VSCode中使用live-server来完成这样的功能;
    • 但是,我们希望在不适用live-server的情况下,可以具备live reloading(实时重新加载)的功能;
  • 安装webpack-dev-server
npm install webpack-dev-server -D
1
  • 修改如下图配置文件,启动时加上serve参数:

    //在webpack.config.js文件中
    module.exports = {
      //其他配置...
      devServer: {//该项可配置,可不配置,都会开启本地服务
        //hot: true,//开启热模块替换 -> 后面有讲
        // host: "0.0.0.0",
        // port: 8888,
        //open: true//启动本地服务成功后,自动打开对应网页
        // compress: true
      },
    }
    
    //在package.json文件夹中  加上serve 配置  
    //其他配置...
    {
        "scripts": {
          "build": "webpack --config wk.config.js",
          "serve": "webpack serve --config wk.config.js" 
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

14.png

  • webpack-dev-server 在编译之后不会写入到任何输出文件,而是将 bundle 文件保留在内存中:
    • 事实上webpack-dev-server使用了一个库叫memfs(memory-fs webpack自己写的)

# 4、devServer配置

# 4.1.host配置

  • host设置主机地址:

    • 默认值是localhost;
    • 如果希望其他地方也可以访问,可以设置为 0.0.0.0;
  • localhost 和 0.0.0.0 的区别:

    • localhost:本质上是一个域名,通常情况下会被解析成127.0.0.1;

    • 127.0.0.1:回环地址(Loop Back Address),表达的意思其实是我们主机自己发出去的包,直接被自己接收;

      • 正常的数据库包经常 应用层 - 传输层 - 网络层 - 数据链路层 - 物理层 ;
      • 而回环地址,是在网络层直接就被获取到了,是不会经常数据链路层和物理层的;
      • 比如我们监听 127.0.0.1时,在同一个网段下的主机中,通过ip地址是不能访问的;
    • 0.0.0.0:监听IPV4上所有的地址,再根据端口找到不同的应用程序;

      • 比如我们监听 0.0.0.0时,在同一个网段下的主机中,通过ip地址是可以访问的

# 4.2.port、open、compress

  • port设置监听的端口,默认情况下是8080
  • open是否打开浏览器:
    • 默认值是false,设置为true会打开浏览器;
    • 也可以设置为类似于 Google Chrome等值;
  • compress是否为静态文件开启gzip compression:
    • 默认值是false,可以设置为true;

15faea5809adde25b7.png

# 5、HMR热模块替换

# 5.1.认识模块热替换(HMR)

  • 什么是HMR呢?
    • HMR的全称是Hot Module Replacement,翻译为模块热替换;
    • 模块热替换是指在 应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面;
  • HMR通过如下几种方式,来提高开发的速度:
    • 不重新加载整个页面,这样可以保留某些应用程序的状态不丢失;
    • 只更新需要变化的内容,节省开发的时间;
    • 修改了css、js源代码,会立即在浏览器更新,相当于直接在浏览器的devtools中直接修改样式;
  • 如何使用HMR呢?
    • 默认情况下,webpack-dev-server已经支持HMR,我们只需要开启即可(默认已经开启);
    • 在不开启HMR的情况下,当我们修改了源代码之后,整个页面会自动刷新,使用的是live reloading;

# 5.2.开启HMR

  • 修改webpack的配置:
//在webpack.config.js文件中
module.exports = {
  //其他配置...
  devServer: {
    hot: true,//开启热模块替换 
  },
}
1
2
3
4
5
6
7
  • 浏览器可以看到如下效果:

162b6ef18f6868dddd.png

  • 但是你会发现,当我们修改了某一个模块的代码时,依然是刷新的整个页面:
    • 这是因为我们需要去指定哪些模块发生更新时,进行HMR;如下图红框中进行的指定:
      • 如demo.js模块,我们通过下图发现,demo模块更新没有刷新整个页面,因为该模块开启了HMR模块热替换

178b114c84167ad1ff.png

# 5.3.框架的HMR

  • 有一个问题:在开发其他项目时,我们是否需要经常手动去写入 module.hot.accpet相关的API呢?
    • 比如开发Vue、React项目,我们修改了组件,希望进行热更新,这个时候应该如何去操作呢?
  • 事实上社区已经针对这些有很成熟的解决方案了:
    • 比如vue开发中,我们使用vue-loader,此loader支持vue组件的HMR,提供开箱即用的体验;
    • 比如react开发中,有React Hot Loader,实时调整react组件(目前React官方已经弃用了,改成使用react-refresh);

# 6、Proxy(Vue项目学习)

  • proxy是我们开发中非常常用的一个配置选项,它的目的设置代理来解决跨域访问的问题:
    • 比如我们的一个api请求是 http://localhost:8888,但是本地启动服务器的域名是 http://localhost:8000,这个时候发送网络请求就会出现跨域的问题;
    • 那么我们可以将请求先发送到一个代理服务器,代理服务器和API服务器没有跨域的问题,就可以解决我们的跨域问题了;
  • 我们可以进行如下的设置:
    • target:表示的是代理到的目标地址,比如 /api-hy/moment会被代理到http://localhost:8888/api-hy/moment
    • pathRewrite:默认情况下,我们的 /api-hy 也会被写入到URL中,如果希望删除,可以使用pathRewrite;
    • secure:默认情况下不接收转发到https的服务器上,如果希望支持,可以设置为false;
    • changeOrigin:它表示是否更新代理后请求的headers中host地址;

# 7、changeOrigin的解析(Vue项目学习)

  • 这个 changeOrigin官方说的非常模糊,通过查看源码我发现其实是要修改代理请求中的headers中的host属性:
    • 因为我们真实的请求,其实是需要通过 http://localhost:8888来请求的;
    • 但是因为使用了代码,默认情况下它的值时 http://localhost:8000;
    • 如果我们需要修改,那么可以将changeOrigin设置为true即可;

180eb4cc010704019b.png

# 8、historyApiFallback (Vue项目学习)

  • historyApiFallback是开发中一个非常常见的属性,它主要的作用是解决SPA页面在路由跳转之后,进行页面刷新时,返回404的错误。
  • boolean值:默认是false
  • 如果设置为true,那么在刷新时,返回404错误时,会自动返回 index.html 的内容;
  • object类型的值,可以配置rewrites属性:
  • 可以配置from来匹配路径,决定要跳转到哪一个页面;
  • 事实上devServer中实现historyApiFallback功能是通过connect-history-api-fallback库的:
  • 可以查看connect-history-api-fallback 文档

# 9、区分开发环境

# 9.1.如何区分开发环境

  • 目前我们所有的webpack配置信息都是放到一个配置文件中的:webpack.config.js
    • 当配置越来越多时,这个文件会变得越来越不容易维护;
    • 并且某些配置是在开发环境需要使用的,某些配置是在生成环境需要使用的,当然某些配置是在开发和生成环境都会使用的;
    • 所以,我们最好对配置进行划分,方便我们维护和管理;
  • 那么,在启动时如何可以区分不同的配置呢?
    • 方案一:编写两个不同的配置文件,开发和生成时,分别加载不同的配置文件即可;
    • 方式二:使用相同的一个入口配置文件,通过设置参数来区分它们;

19406d5d5367883d21.png

# 9.2.入口文件解析

  • 我们之前编写入口文件的规则是这样的:./src/index.js,但是如果我们的配置文件所在的位置变成了 config 目录,我们是否应该变成 ../src/index.js呢?
    • 如果我们这样编写,会发现是报错的,依然要写成 ./src/index.js;
    • 这是因为入口文件其实是和另一个属性时有关的 context;
  • context的作用是用于解析入口(entry point)和加载器(loader):
    • 官方说法:默认是当前路径(但是经过我测试,默认应该是webpack的启动目录)
    • 另外推荐在配置中传入一个值

208101ec700f5b959c.png

# 9.3.区分开发和生成环境配置

  • 这里我们创建三个文件:
    • webpack.comm.config.js
    • webpack.dev.config.js
    • webpack.prod.config.js
  • 安装webpack-merge;完成上面文件的合并
    • webpack.comm.config.js和webpack.dev.config.js合并成开发环境配置
    • webpack.dev.config.js和webpack.prod.config.js 合并成生产环境配置
pnpm add webpack-merge -D
1
最后更新时间: 2022/08/21, 17:31:13
彩虹
周杰伦