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

Vite和Webpack的作用是什么?

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

就好比:

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

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

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

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

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

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

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

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

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

运行结果:

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

浏览器会将 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中配置:

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

js 复制代码
npm i ws
npm i chokidar

2、创建 Websocket 连接

js 复制代码
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 监听全局文件

js 复制代码
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 连接

js 复制代码
<!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");

    socket.addEventListener("open", function (event) {
      socket.send("Hello Server!");
    });

    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' 通知,客户端直接做刷新页面的操作

相关推荐
惊鸿2872 分钟前
Taro3+小程序Canvas动态生成海报和二维码分享到朋友圈
前端
蓝翔认证10级掘手7 分钟前
🤯 家人们谁懂啊!我的摸鱼脚本它...它成精了!🚀
javascript
做梦都在学习前端9 分钟前
发布一个monaco-editor 汉化包
前端·npm·vite
石小石Orz25 分钟前
为什么推荐前端学习油猴脚本开发?
前端
珵煜ini28 分钟前
wd-button组件阻止事件冒泡的
前端
炒毛豆29 分钟前
vue3.4中的v-model的用法~
前端·vue.js
用户408128120038130 分钟前
大文件分片上传和断点续传
前端
极客悟道30 分钟前
颠覆传统虚拟化:在Docker容器中运行Windows系统的开源黑科技
前端·后端
前端康师傅31 分钟前
JavaScript 中你不知道的按位运算
前端·javascript
小桥风满袖34 分钟前
Three.js-硬要自学系列38之专项学习缓冲几何体
前端·css·three.js