下一代前端构建工具Vite

2020-12-30 09:40:21 浏览数 (1)

一、背景

Vue作者尤雨溪在今年4月提出了一个由Vue3搭载的前端开发工具Vite。Vite主要提供了前端开发服务器的功能以及生产环境打包的功能,而其主要突破则是在前端开发服务器这一方面,提供了一种基于ES Module的快速的本地开发服务器。

二、Vite简介

2.1 什么是Vite

在本文编辑时,Vite版本仍处于1.0.0-rc.9,尚未正式发布,并且Vite目前主要支持Vue3项目,尚不识别Vue2语法。下面是引用尤雨溪在微博上对Vite的介绍。

Vite,一个基于浏览器原生 ES imports 的开发服务器。利用浏览器去解析 imports,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随起随用。同时不仅有 Vue 文件支持,还搞定了热更新,而且热更新的速度不会随着模块增多而变慢。针对生产环境则可以把同一份代码用 rollup 打。虽然现在还比较粗糙,但这个方向我觉得是有潜力的,做得好可以彻底解决改一行代码等半天热更新的问题。

2.1 如何使用Vite

使用下面的命令即可快速搭建一个使用Vite作为开发服务器的项目,使用十分方便,类似于Vue-cli。

代码语言:javascript复制
$  npm init vite-app <project-name> $  cd <project-name> $  npm install $  npm run dev

第一行命令的目的就是从npm仓库拉取 create-vite-app这个包,然后全局安装,最后使用它创建基于Vite的模板项目。

三、Vite特性

官方文档介绍,Vite主要有下面三个特性

  1. 快速的冷启动
  2. 及时的热模块更新
  3. 真正的按需加载 我们对比 create-vite-app和 Vue-cli创建的项目,来看一下Vite快在哪里,下图分别是使用Vite和 Vue-cli(webpack)启动本地开发服务器的过程。

可以看出Vite相对于Vue-cli(webpack)在本地服务器启动时省略了打包步骤,因而做到了冷启动秒开的效果,并且这个速度提升会随着项目模块增多而愈加明显。下面我们就看一下Vite是如何实现其快速的特性。

四、Vite原理

Vite 是基于浏览器原生 ES imports 的开发服务器。因而我们首先需要了解浏览器是如何支持ES Module的

4.1 ES Module

首先,我们来看一下在浏览器如何使用ES Module。打开浏览器调试面板, 清空network,使用下面代码在页面动态插入一段 <script>代码

代码语言:javascript复制
const script = document.createElement('script')script.setAttribute('type', 'module');script.innerHTML = 'import {test} from "./test.js"'document.body.append(script)

在运行上述代码后,浏览器向当前服务器目录发送了 http://km.oa.com/test.js的请求。我们都知道本地项目中我们使用ES import会从文件系统读取相应路径的模块,浏览器则是将模块路径转换为Url。

浏览器解析ES module的过程如上图所示。

  1. 识别带有熟悉 type="module"的 <script>标签
  2. 获取并解析该标签内的js内容。
  3. 识别 import语法,生成请求url,向服务器请求该地址的模块

可以说浏览器对于ES Module的支持实现了真正的按需加载,省略了前端打包的过程,对于减少首屏加载时间是有极大帮助的。但是我们要在生产环境中使用它必须知道浏览器的支持度到底如何。

下面是一张caniuse中说明的浏览器对于 ES Module的静态import语法的支持情况。可以看出除了IE外的主流浏览器基本上都支持了 ES Module的import语法。

那么,对于不支持ES Module的浏览器,难道我们就让项目跑不起来吗?

当然不是,在 script 标签中使用 nomodule 属性,可以确保向后兼容。

像上图一样提供ES Module方式和非ES Module方式的代码,对于支持ES Module的浏览器,其会忽视 nomodule类型的script,而对于无法识别 Es Module的浏览器则会直接使用 nomodule的script代码。因此我们只需提供一份打包好的代码,放在 nomodule标签内就可以实现向后兼容

值得注意的是,浏览器只能解析以’/’, ‘./’, 或 '…/'开头的模块路径,对于像引用nodemodules中的模块,比如像下面引用Vue的方式,浏览器无法识别,会报错。因此对于nodemodules的引用,需要另外处理,而Vite也给出了解决方案。

代码语言:javascript复制
import Vue from 'vue'
4.2 Vite开发服务器如何使用ES Module

我们启动Vite本地开发服务器,用浏览器打开入口页面,观察浏览器的NetWork面板.如下图所示,

  1. 浏览器加载了入口html,解析发现 main.js
  2. 发现 main.js中包含ES Module, 解析 import语法,发现有三个 import
  3. 根据 import,发出所依赖的模块的Http请求
  4. 依次类推,边解析边请求。

对比源码和网络请求,我们会发现网络请求数明显要多于源码中 import的个数。多出的网络请求主要是两类

  1. .vue文件相关 对于一个Vue组件SFC(Single File Components),其主要包含三类代码,模板、script、样式。由于浏览器是无法识别vue文件的,一个vue文件会被拆分为三个请求 .vue.vue?type=template.vue?type=style,这些都需要借助Vite的本地服务器实现,具体实现方法下文会详细阐述。
  2. 热更新相关 我们看到请求中有个 clent.js,还有websocket请求,这些都是为热更新服务的,而在代码中插入建立websocket连接需要的 clent.js逻辑也是由Vite开发服务器实现的。
4.3 Vite 开发服务器模块处理

对于浏览器不识别的node_module引用如何处理?对于 .vue文件如何处理都是由Vite开发服务器实现的。首先我们看一下Vite开发服务器架构图

Vite开发服务器是基于Koa框架的,利用Koa中间件实现模块解析以及热更新的主要功能。这一节我们主要看一下Vite是如何处理模块的。

nodemodules 模块处理过程 对nodemodules的处理主要由中间件 serverPluginModuleRewrite完成,其主要过程如下

  1. 在 koa 中间件里获取请求 body
  2. 通过 es-module-lexer 解析资源 ast 拿到 import 的内容
  3. 判断 import 的资源是否是绝对路径,绝对视为 npm 模块
  4. 返回处理后的资源路径:“vue” => “/@modules/vue”

vue文件处理过程 对vue组件的处理由 serverPluginVue来实现,其处理流程如下

  1. 分析请求路径,是否包含查询字段type
  2. 不包含type的请求视为script内容请求, 返回类型为 js
  3. 对于type为template请求则返回.vue文件的 template内容的渲染函数,返回类型为 js
  4. 对于type为style的请求则返回.vue文件中 style标签内样式的动态插入函数
4.4 Vite 热更新

如上图所示,Vite热更新也是基于Websocket。在Vite服务器启动时,Vite利用中间件 serverPluginHtml在html中插入 client.js. 这个js文件主要用于在建立浏览器和Vite服务器之间的Websocket通信。热更新的步骤如下

  1. Vite服务器监听本地文件更新
  2. 对比缓存中的文件和变动后的文件,组织更新内容
  3. 服务器通过PostMessage向浏览器通知更新消息,更新消息包含跟新类型,更新后模块的最新地址,时间戳
  4. 浏览器请求热更新文件
  5. 根据跟新类型处理返回的文件

clientjs监听的更新消息类型

  • connected: WebSocket 连接成功
  • vue-reload: Vue 组件重新加载(当你修改了 script 里的内容时)
  • vue-rerender: Vue 组件重新渲染(当你修改了 template 里的内容时)
  • style-update: 样式更新
  • style-remove: 样式移除
  • js-update: js 文件更新
  • full-reload: fallback 机制,网页重刷新

总结

Vite 提供了一个更快的开发环境服务器, 其实现原理基于ES模块,通过开发环境去打包将构建时间从 O(n) 减少到 O(1), 其搭载Vue3发布,借助Vue生态,在未来有更广泛的使用场景。

0 人点赞