webpack 搭建本地开发服务器

文章目录

  • Webpack watch
  • webpack-dev-server
  • webpack 配置:devServer
    • static
    • host
      • localhost 和 0.0.0.0 的区别:
    • port、open、compress
  • 模块热替换(HMR)
    • 框架的HMR
    • HMR 的原理
  • Proxy
    • 基本使用
      • pathRewrite
      • changeOrigin
      • 常规配置
  • historyApiFallback

在此之前的 webpack 配置是必不可少的。但是开发体验并不好,一直需要 npm run build 手动打包。

我们希望可以做到,当文件发生变化时,可以自动的完成 编译 和 展示;
为了完成自动编译,webpack提供了几种可选的方式:

  • webpack watch mode;
  • webpack-dev-server(常用);
  • webpack-dev-middleware;

Webpack watch

webpack给我们提供了watch 模式。
在该模式下,webpack依赖图中的所有文件,只要有一个发生了更新,那么代码将被重新编译;
我们不需要手动去运行 npm run build指令了。
注意:它只是会自动编译打包,要在浏览器实时查看还得配合 live-server 打开 html。

如何开启 watch 呢?两种方式:

  • 方式一:在导出的配置文件中,也就是 webpack.config.js 中添加 watch: true;
  • 方式二:在启动 webpack 的命令中,添加 --watch 的标识;

我们可以在 npm 脚本中添加标识,因为 scripts 中的脚本,实际就是用 npm run 的方式帮你在命令行执行了后面的命令。这里的 webpack 也一样,在命令行执行,本质靠的还是 webpack-cli,包括 --watch 这种属性配置也是靠的 cli 添加到配置文件中。

{
  "scripts": {
    "build": "webpack --watch"
  },
}

webpack-dev-server

如果我们想不借助 live-server 的情况下,实现 live reloading(实时重新加载)的功能,我们可以使用 webpack-dev-server。
它将开启一个 express 服务器,所以浏览器打开,就和打开一般的网站一样,会先下载 index.html,然后再从 index.html 中去下载其他的文件。

事实上 webpack-dev-server 在编译之后不会写入任何输出文件。而是将 bundle 文件保留在内存中

安装:npm i webpack-dev-server -D

使用:webpack serve启动一下就行,通常我们也是加入 npm 脚本中使用
"serve": "webpack serve --config webpack.config.js"
![image.png](https://img-blog.csdnimg.cn/img_convert/837a142e0f97f384517e44336321762c.png#clientId=ua2e6ddea-1eb8-4&errorMessage=unknown error&from=paste&height=142&id=u3e7b03df&name=image.png&originHeight=222&originWidth=1055&originalType=binary&ratio=1&rotation=0&showTitle=false&size=42403&status=error&style=none&taskId=ud0771a41-16ac-4b79-bfad-aaff037763f&title=&width=675.2)

webpack-dev-server 服务查找文件的路径是:

  • http://[devServer.host]:[devServer.port]/[output.publicPath]/[output.filename]
output: {
  filename: "js/bundle.js",
  path: path.resolve(__dirname, "./dist"),
  publicPath: "hhh"
}

默认主机和端口是 localhost:8080,如果按如上配置 output,则请求 js 的 url 就是

  • http://127.0.0.1:8080/hhh/js/bundle.js

webpack 配置:devServer

如果我们想要配置一下本地开发服务器,可以在 wepback 配置对象的顶层选项devServer中配置。当然里面已经有很多已经默认配置了。

另外还可以添加一个顶层选项 target 标识打包的产物使用环境。target: "web"|"node"

static

devServer 中可以配置很多属性,contentBase 就是其中之一。
contentBase:表示 dev server 将告知浏览器那些 webpack 没有打包的文件在哪个目录,这样浏览器就可以直接去那个目录里面获取文件,而不是在打包好的包里面下载。
比如 favorite.ico ,生产环境肯定是要复制到打包的文件夹,但是开发阶段又不需要复制,就可以指定 ./public 为contentBase 来方便浏览器加载该图标。
有些额外的文件很大,这样不需要打包,就提升了开发打包效率。
然而它已经被弃用了。
webpack 搭建本地开发服务器_第1张图片
提示信息中已经标出了所有 devServer 能进行配置的选项,当然很多选项都有默认配置。

现在想要达到上面 contentBase 一样的效果,需要使用 static选项指定静态文件目录。目录下的文件相当于被添加到打包的出口目录中。

module.exports = {
  devServer: {
    static: "./img"
  }
}
<img src="./1.png" alt="">

打开浏览器查看,会发现图片 1.png 是链接到本地 img 文件夹的,没有被打包。
webpack 搭建本地开发服务器_第2张图片

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 去访问到别人的服务器。因为请求压根不会被网卡发送出去,这也是把 127.0.0.1 叫本地主机的原因。

0.0.0.0:监听IPV4上所有的地址,再根据端口找到不同的应用程序;
比如我们开启 0.0.0.0 的服务,在同一个网段下的主机中,别人可以通过 0.0.0.0 访问到我的服务器。因为它是个广播地址,请求会发给网段下的所有主机。

port、open、compress

port:设置监听的端口,默认情况下是8080
open:是否打开浏览器:

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

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

注意:有些端口在浏览器的黑名单里,比如:6666。这些端口不能设置。

模块热替换(HMR)

什么是HMR呢?

  • HMR的全称是Hot Module Replacement,翻译为模块热替换;
  • 模块热替换是指在 应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面

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

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

如何使用HMR呢?
默认情况下,webpack-dev-server 已经支持 HMR,并且默认开启;**devServer: { hot: true }**
在不开启 HMR 的情况下,当我们修改了源代码之后,整个页面会自动刷新,使用的是live reloading

image.png
WDS :webpack Dev Server

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

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

框架的HMR

有一个问题:在开发其他项目时,我们是否需要经常手动去写入 module.hot.accpet相关的API呢?
比如开发Vue、React项目,我们修改了组件,希望进行热更新,这个时候应该如何去操作呢?

事实上社区已经针对这些有很成熟的解决方案了:
比如vue开发中,我们使用vue-loader,此loader支持vue组件的HMR,提供开箱即用的体验;
比如react开发中,有React Hot Loader,实时调整react组件(目前React官方已经弃用了,改成使用react-refresh)

HMR 的原理

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

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

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

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

Proxy

proxy 是我们开发中非常常用的一个配置选项,它的目的设置代理来解决跨域访问的问题:

  • 比如我们的一个api请求是 http://192.163.0.66/12345,但是本地启动服务器的域名是 http://localhost:8080,这个时候发送网络请求就会出现跨域的问题;
  • 那么我们可以将请求先发送到一个代理服务器,代理服务器和API服务器没有跨域的问题,就可以解决我们的跨域问题了
  • webpack 启动的代理服务器是 express

跨域

基本使用

在 devServer 中设置一个占位符,占位符代替请求的目标服务器地址
比如本机 webpack 启动的服务源是http://localhost/8080,现在想要去请求目标源是http://192.163.0.66:12345,api 是/get

fetch("http://192.163.0.66:12345")
  .then(response => response.json())
  .then(result => console.log(result))

显然这必定发生跨域,因为端口号不一样。这时我们就要使用代理,具体做法就是让占位符代替目标源:

fetch("/api/get") // ‘/api’ 为占位符
	.then(response => response.json())
  .then(result => console.log(result))

但是现在有个问题,我们依然请求不到数据。因为占位符它不会被删除,会被加入到实际请求服务器数据的 url中。
webpack 搭建本地开发服务器_第3张图片

pathRewrite

现在的请求架构是,浏览器请求 --> 代理服务器请求 --> 真正的服务器。
我们查看请求头 url,一直是http://localhost/8080/api/get没有发生跨域,但是代理服务器发送给真正服务器的请求中也带上了占位符 /api,可实际上真实服务器上并没有对应的资源,所以就会返回 404。
这时就**必须重写代理服务器的请求 url,删除占位符。 ****pathRewrite: { "^/api-hy": "" }**

changeOrigin

changeOrigin 可以修改代理请求中的 headers 中的 host 属性,修改为 target 对应的目标请求源。
代理服务器向真实服务器发送请求,因为服务器又没有同源策略,所以任何请求源都可以随便发。比如 webpack 启动的代理服务器向真实服务器发送请求的源就是http://localhost:3000
但是真实服务器那边可能会做限制,比如防范爬虫之类的,校验请求头的 url,必须和真实服务器的源一致。则可以将 changeOrigin 设置为 true 即可修改。如上述图所示。

常规配置

汇总:

  • target:表示的是要代理到的目标地址,
  • pathRewrite:路径重写。
  • secure:默认情况下不接收转发到 https 的服务器上,如果希望支持,可以设置为false;
  • changeOrigin:它表示是否更新代理后请求的 headers 中 host 地址;
devServer: {
    proxy: {
      "/api-hy": { // 占位符
        target: "http://192.163.0.66/12345",
        pathRewrite: {
          "^/api-hy": ""
      	},
        secure: false,
        changeOrigin: true
      }
    }
  }
}

historyApiFallback

historyApiFallback是开发中一个非常常见的属性,它主要的作用是解决SPA页面在路由跳转之后,进行页面刷新
时,返回404的错误。
因为刷新就是拿着地址栏上的 url 向服务器发送一次请求,但是当前这个路由在 SPA 页面中都是前端设置的,服务器这个 url 对应的资源,所以就会 404。

historyApiFallback 两种取值:

  • boolean值:默认是false

如果设置为true,那么在刷新时,返回404错误时,会自动返回 index.html 的内容;

  • object类型的值,可以配置rewrites属性:

可以配置from来匹配路径,决定要跳转到哪一个页面;

事实上devServer中实现historyApiFallback功能是通过connect-history-api-fallback库的:
可以查看 connect-history-api-fallback文档

你可能感兴趣的:(工程化,webpack,服务器,javascript,前端,网络)