10-devServer与watch

watch

监听源代码的变化

如何开启watch呢?两种方式

  • 方式一:在导出的配置中,添加 watch: true;
  • 方式二:在启动webpack的命令中,添加 --watch的标识;

缺点:

  • 通过live-server插件提供本地服务
  • 对所有的源代码都重新进行编译
  • 编译成功之后,都会生成新的文件(文件操作 file system)
  • live-server属于vscode插件,不属于webpack解决方案。
  • live-server每次都会重新刷新整个页面

webpack-dev-server

webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中。事实上webpack-dev-server使用了一个库叫memfs。

webpack-dev-middleware

webpack-dev-middleware 是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 server。

  • webpack-dev-server 在内部使用了它,然而它也可以作为一个单独的 package 来使用,以便根据需求进行 更多自定义设置;
  • 我们可以搭配一个服务器来使用它,比如express;npm install --save-dev express webpack-dev-middleware
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');

const app = express();

const config = require("./webpack.config");

// 传入配置信息, webpack根据配置信息进行编译
const compiler =  webpack(config);

const middleware = webpackDevMiddleware(compiler);
app.use(middleware);

app.listen(3000, () => {
  console.log("服务已经开启在3000端口上~");
});

认识模块热替换

模块热替换是指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面;

HMR通过如下几种方式,来提高开发的速度:

  • 不重新加载整个页面,这样可以保留某些应用程序的状态不丢失;
  • 只更新需要变化的内容,节省开发的时间
  • 修改了css、js源代码,会立即在浏览器更新,相当于直接在浏览器的devtools中直接修改样式;

如何使用HMR呢?

  • 默认情况下,webpack-dev-server已经支持HMR,我们只需要开启即可;
  • 在不开启HMR的情况下,当我们修改了源代码之后,整个页面会自动刷新,使用的是live reloading;

开启HMR

devServer: {
    hot: true,
},

但是你会发现,当我们修改了某一个模块的代码时,依然是刷新的整个页面:
这是因为我们需要去指定哪些模块发生更新时,进行HMR;

//index.js
if (module.hot) {
  module.hot.accept("./math.js", () => {
    console.log("math模块发生了更新~");
  });
}

框架的HMR

  • vue开发中,我们使用vue-loader,此loader支持vue组件的HMR,提供开箱即用的体验
  • react开发中,有React Hot Loader,实时调整react组件(目前React官方已经弃用了,改成使用react-refresh);

Vue的HMR

Vue的加载我们需要使用vue-loader,而vue-loader加载的组件默认会帮助我们进行HMR的处理

安装加载vue所需要的依赖:npm install vue-loader vue-template-compiler -D

const VueLoaderPlugin = require('vue-loader/lib/plugin')
rules:{
        test: /\.vue$/i,
        use: 'vue-loader',
      },
plugins: [
    new HtmlWebpackPlugin({
      template: './index.html',
    }),
    //new ReactRefreshWebpackPlugin(),
    new VueLoaderPlugin(),
  ],

HMR的原理

那么HMR的原理是什么呢?如何可以做到只更新一个模块中的内容呢?

  • webpack-dev-server会创建两个服务:提供静态资源的服务(express)和Socket服务(net.Socket)
  • express server负责直接提供静态资源的服务(打包后的资源直接被浏览器请求和解析)

HMR Socket Server,是一个socket的长连接:

  • 长连接有一个最好的好处是建立连接后双方可以通信(服务器可以直接发送文件到客户端)
  • 当服务器监听到对应的模块发生变化时,会生成两个文件.json(manifest文件)和.js文件(update chunk)
  • 通过长连接,可以直接将这两个文件主动发送给客户端(浏览器);
  • 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新。

output的publicPath 和devServer的publicPath

output中的path的作用是告知webpack之后的输出目录

  • 比如静态资源的js、css等输出到哪里,常见的会设置为dist、build文件夹等

output中还有一个publicPath属性,该属性是指定index.html文件打包引用的一个基本路径

  • 它的默认值是一个空字符串,所以我们打包后引入js文件时,路径是 bundle.js
  • 在开发中,我们也将其设置为 / ,路径是 /bundle.js,那么浏览器会根据所在的域名+路径去请求对应的资源
  • 如果我们希望在本地直接打开html文件来运行,会将其设置为 ./,路径时 ./bundle.js,可以根据相对路径去 查找资源

devServer中也有一个publicPath的属性,该属性是指定本地服务所在的文件夹

  • 它的默认值是 /,也就是我们直接访问端口即可访问其中的资源 http://localhost:8080;
  • 如果我们将其设置为了 /abc,那么我们需要通过 http://localhost:8080/abc才能访问到对应的打包后的资源
  • 并且这个时候,我们其中的bundle.js通过 http://localhost:8080/bundle.js也是无法访问的:
    • 所以必须将output.publicPath也设置为 /abc;
    • 官方其实有提到,建议 devServer.publicPath 与 output.publicPath相同;

devServer的contentBase

devServer中contentBase对于我们直接访问打包后的资源其实并没有太大的作用,它的主要作用是如果我们打包后的资源,又依赖于其他的一些资源,那么就需要指定从哪里来查找这个内容:

  • 比如在index.html中,我们需要依赖一个 abc.js 文件,这个文件我们存放在 public文件 中;

  • 在index.html中,我们应该如何去引入这个文件呢

    • 比如代码是这样的:

      <script src="./public/abc.js">script>
      
    • 但是这样打包后浏览器是无法通过相对路径去找到这个文件夹的;

    • 所以代码是这样的:

      ;
      
    • 但是我们如何让它去查找到这个文件的存在呢? 设置contentBase即可

  • 当然在devServer中还有一个可以监听contentBase发生变化后重新编译的一个属性:watchContentBase。添加之后才会刷新,否则修改静态资源不会被刷新。

hotOnly、host、port、open、compress配置

hotOnly是当代码编译失败时,是否刷新整个页面

  • 默认情况下当代码编译失败修复后,我们会重新刷新整个页面
  • 如果不希望重新刷新整个页面,可以设置hotOnly为true;

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地址是可以访问的

port设置监听的端口,默认情况下是8080

open是否打开浏览器

  • 默认值是false,设置为true会打开浏览器
  • 也可以设置为类似于 Google Chrome等值

compress是否为静态文件开启gzip compression

  • 默认值是false,可以设置为true
 devServer: {
    hot: true,
    hotOnly: true,
    // host: "0.0.0.0",
    // port: 7777,
    // open: true,
    compress: true,
    contentBase: path.resolve(__dirname, "./why"),
    watchContentBase: true,
    // publicPath: "/abc"
 },

### Proxy代理

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

你可能感兴趣的:(webpack,webpack,vscode,javascript)