likes
comments
collection
share

"Vite" 为什么比 "Webpack" 快? 他们之间的差距在什么地方?

作者站长头像
站长
· 阅读数 4

下篇文章介绍,你不知道的webpack的入口起点和出口文件

Vite和Webpack的作用是什么?

大家都知道,两个都是打包工具,是将代码打包好,好在浏览器上执行。因为浏览器在执行代码的时候,本身没有一个很好的方式去读懂我们的项目中的各个文件引入关系。

就好比:

浏览器说:“这个项目代码这么多文件,不知道这些文件是干啥的,烦死了”

Webpack 对浏览器说:“你别纠结了,我把所有的文件引入关系都梳理好了,并且将项目中所有文件的代码打包在了一起,你就去执行文件吧!”

浏览器说:“好的大哥,谢谢大哥”,然后毫无压力的哐哐运行

Webpack打包方式,是将整个项目文件进行打包,不管能不能用得上,一旦项目过大,等待打包过程执行的时间过长,体验非常差。

Webpack打包慢、整体打包的这个问题,一直困扰着前端开发?一直到浏览器进步了,开始具备读懂一些模块化的引入语法的特性,结合这新的特性,于是 bundless 的打包思路就诞生了,Vite 正是借助了刚刚我们说的浏览器的这一特性,将项目中的各种文件引入处理成网络请求,当浏览器执行到了模块依赖的代码,便自己去请求所需要的代码资源。

Vite的实现思想:通过浏览器运行时发送的http请求来实现文件的按需加载

浏览器处理模块化的引入语法

浏览器是可以正常运行文件中的方法:

"Vite" 为什么比 "Webpack" 快? 他们之间的差距在什么地方?

浏览器可以正常运行文件模块中的代码:

"Vite" 为什么比 "Webpack" 快? 他们之间的差距在什么地方?

"Vite" 为什么比 "Webpack" 快? 他们之间的差距在什么地方?

"Vite" 为什么比 "Webpack" 快? 他们之间的差距在什么地方?

运行结果:

"Vite" 为什么比 "Webpack" 快? 他们之间的差距在什么地方?

看一眼浏览器的是怎么处理这些文件引入关系的:

"Vite" 为什么比 "Webpack" 快? 他们之间的差距在什么地方?

浏览器会将 import 语句处理成一个个HTTP网络请求,去获取 import 引入的各种模块, 就因为浏览器现在可以通过 type="module" 这种方式读懂项目中文件的模块化引入,所以,bundless 的思想得以发展

Webpack打包原理

实现一个webpack的思路主要有三步:

1、读取入口文件内容

2、分析入口文件,递归的方式去读取模块所依赖的文件并且生成AST语法树

3、根据AST语法树生成浏览器可以运行的代码(遍历AST树,做依赖收集、将es6转es5)

通俗的说:Webpack 通过分析js中的 require 语句,分析出当前 js 文件所有的依赖文件,通过递归的方式层层分析后,得到整个项目的依赖关系图(AST树),对图中不同的文件执行不同的 loader,比如使用 css-loader 解析css代码,最后基于这个依赖关系图读取到整个项目中的所有文件代码,对其进行依赖收集和转化成ES5,最后打包处理交给浏览器执行。

Webpack还能通过配置,来进行热重载;但是热重载又会重新自动打包一次,这对于大型项目是极不友好的,这时间估计等的花都要谢了!

在vue.config.js或者webpack.config.js中配置:

devServer: { 
    static:{
      directory: path.resolve(__dirname, './dist')
    },
    port:8080,  //端口
    hot: true, //自动打包
    host:'localhost', 
    open:true //自动跳到浏览器
  }

vite打包原理

当声明一个 script 标签类型为 module 时,浏览器会对其内部的 import 引用发起 HTTP 请求获取模块内容。那么,vite 会劫持这些请求并进行相应处理。因为浏览器只会对用到的模块发送http请求,所以vite不用对项目中所有文件都打包,而是按需加载,大大减少了AST树的生成和代码转换,降低服务启动的时间和项目复杂度的耦合,提升了开发者的体验。

Vite也是可以实现热更新的,也不会有打包时间过长的问题,但是新的问题又产生了,Vite 服务端就要想办法主动通知浏览器,或者说主动将变更内容发送给浏览器?

上面的问题就是想实现,浏览器不发请求,服务端主动通知?这不就是 WebSocket 嘛!!!

项目代码变更,只要有办法感知到代码变更了,直接 WebSocket 推送给浏览器就好了。可是要怎么才能感知代码更改了呢? chokidar库可以实现这个效果。

1、先安装 WSchokidar

npm i ws
npm i chokidar

2、创建 Websocket 连接

const WebSocket = require('ws');
const wss = new WebSocket.Server({ server });  // server 部分代码省略了

wss.on('connection', (ws) => {
  // 在客户端连接时执行的代码
  console.log('Client connected');

  // 在连接关闭时执行的代码
  ws.on('close', () => {
    console.log('Client disconnected');
  });
});

3、 增加 chokidar 监听全局文件

const WebSocket = require('ws');
const wss = new WebSocket.Server({ server });
const chokidar = require('chokidar');

wss.on('connection', (ws) => {
  // 在客户端连接时执行的代码
  console.log('Client connected');

  // 在连接关闭时执行的代码
  ws.on('close', () => {
    console.log('Client disconnected');
  });
});


const watcher = chokidar.watch('.', {
  ignored: ['**/node_modules/**', '**/.git/**'],
  persistent: true, // 持久监听
});

watcher.on('change', (path) => {
  // 发送热更新通知给所有连接的客户端
  wss.clients.forEach((client) => {
    // 表示客户端已经建立了连接并且处于打开状态
    if (client.readyState === WebSocket.OPEN) {
      client.send('update');
    }
  });
});

4、 客户端 index.html 增加 socket 连接

<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Vite + Vue</title>
</head>

<body>
  <div id="app"></div>

  <script>
    // 创建连接
    const socket = new WebSocket("ws://localhost:8080");
    // 添加一个 `open` 事件监听器,当 WebSocket 连接成功时触发
    socket.addEventListener("open", function (event) {
      socket.send("Hello Server!");
    });
    // 添加一个 `message` 事件监听器,当接收到服务器的消息时触发
    socket.addEventListener("message", function (event) {
      console.log("Message from server ", event.data);
      window.location.reload() // 接收到后端的推送后直接刷新页面
    });

    // 这是一个环境变量的设置
    window.process = {
      env: { // 配置环境变量
        NODE_ENV: 'dev'
      }
    }

  </script>
  <script type="module" src="/src/main.js"></script>
</body>

</html>

如此一来便实现了前后端的 socket 的连接,当 chokidar 监听到文件变更时,客户端会接收到一个 'update' 通知,客户端直接做刷新页面的操作

  • type="module":指定了嵌入的 JavaScript 代码是一个模块。这意味着在该文件中可以使用 ES6 模块语法(例如 import 和 export)。

  • src="/src/main.js":指定了要嵌入的 JavaScript 模块文件的路径。在这里,src 属性指定了文件的相对路径或绝对路径,/src/main.js 表示文件位于根目录下的 src 文件夹中,文件名为 main.js