前端新玩具:Vite 及相关思考

2022-12-05 0 838

嗨,我是圈圈。那时我要跟你撷取两个有意思的后端新玩偶:Vite。

Vite 的表述

Vite 是面向全国当代应用领域程序的两个轻巧、更快的 Web 应用领域应用领域软件,核心理念如前所述 ECMAScript 国际标准原生植物组件控制系统(ES Modules)同时实现。

从虚无机能上看,Vite 能替代如前所述 Webpack 的 vue-cli 或是 cra 的全机能应用领域软件,提供更多崭新的一类合作开发新体验。

具体内容技术细节往上看。

Vite 的来历

在此之后,如果他们所合作开发的应用领域非常复杂(标识符量偏高),采用 Webpack 的合作开发操作过程相较没所以「柔滑」,其二为下列三点:

Webpack Dev Server UAC天数会较为长,稍大一点儿的工程项目开启合作开发服务工程项目都须要等候 10 – 20 秒;Webpack HMR 热预览的响应速度很慢,修正完标识符须要等候C++全数校对顺利完成就可以已经开始并行到应用领域程序;

加速上手

这儿他们话不多说,先上手新体验呵呵 Vite,接着再来分析其外部的路子和设想。

Vite 非官方现阶段提供更多了两个非常简单的钢架:create-vite-app,能采用那个钢架加速建立两个采用 Vite 构筑的 Vue.js 应用领域

$ npm init viteapp <projectname> $ cd <projectname> $ npm install $ npm run dev

假如采用 yarn:

$ yarn create viteapp <projectname> $ cd <projectname> $ yarn $ yarn dev

P.S.

npm init 或是 yarn create 是这两个包管理工具提供更多的新机能,其外部就是自动去安装两个 create-<xxx> 的组件(临时),接着自动执行那个组件中的 bin。例如:yarn create react-app my-react-app 就相当于先 yarn global add create-react-app,然后自动执行 create-react-app my-react-app。

对比差异点

打开生成的工程项目过后,你会发现就是两个很普通的 Vue.js 应用领域,没太多特殊的地方。

不过相比于之前 vue-cli 建立的工程项目或是是如前所述 Webpack 搭建的 Vue.js 工程项目,这儿的合作开发依赖非常简单,只有 vite 和 @vue/compiler-sfc。

{ “name”: “vite-demo”, “version”: “0.0.0”, “scripts”: { “dev”: “vite”, “build”: “vite build” }, “dependencies”: { “vue”: “^3.0.0-rc.1” }, “devDependencies”: { “vite”: “^1.0.0-rc.1”, “@vue/compiler-sfc”: “^3.0.0-rc.1” } }

Vite 就是他们那时要介绍的主角,而 @vue/compiler-sfc 就是用来校对他们工程项目中 .vue 结尾的单文件组件(SFC),它替代的就是 Vue.js 2.x 时采用的 vue-template-compiler。

再者就是 Vue.js 的版本是 3.0。这儿尤其须要注意:Vite 现阶段只支持 Vue.js 3.0 版本。

假如你想,在后面介绍完同时实现原理过后,你也能改造 Vite 让它支持 Vue.js 2.0。

基础新体验

这儿他们所安装的 vite 组件提供更多了两个子命令:

serve:开启两个用于合作开发的服务工程项目器build:构筑整个工程项目(上线)

当他们执行 vite serve 的时候,你会发现响应速度非常快,几乎就是秒开。

可能单独新体验你不会有太明显的感觉,你能对比采用 vue-cli-service(外部还是 Webpack)开启合作开发服务工程项目器。

当他们对比采用 vue-cli-service serve 的时候,你会有更明显的感觉。因为 Webpack Dev Server 在开启时,须要先 build 一遍,而 build 的操作过程是须要耗费很多天数的。

前端新玩具:Vite 及相关思考

而 Vite 则完全不同,当他们执行 vite serve 时,外部直接开启了 Web Server,并不会先校对所有的标识符文件。

那仅仅是开启 Web Server,速度上自然就快了很多。

前端新玩具:Vite 及相关思考

但是像 Webpack 这类工具的做法是将所有组件提前校对、打包进 bundle 里,换句话说,不管模块是否会被执行,都要被校对和打包到 bundle 里。随着工程项目越来越大打包后的 bundle 也越来越大,打包的速度自然也就越来越慢。

Vite 利用当代应用领域程序原生植物支持 ESM 特性,省略了对组件的打包。对于须要校对的文件,Vite 采用的是另外一类模式:即时校对。也就是说,只有具体内容去请求某个文件时才会校对那个文件。所以,这种「即时校对」的好处主要体现在:按需校对。

Optimize

Vite 还提供更多了两个现阶段在帮助列表中并没呈现的两个子命令:optimize。

那个命令的作用就是单独的去「优化依赖」。

所谓的「优化依赖」,指的就是自动去把标识符中依赖的第三方组件提前校对出来。

例如,他们在标识符中通过 import 载入了 vue 那个组件,那通过那个命令就会自动将那个组件打包成两个单独的 ESM bundle, 放到 node_modules/.vite_opt_cache 目录中。

这样后续请求那个文件时就不须要再即时去加载了。

HMR

同样也是模式的问题,热预览的时候,Vite 只须要立即校对当前所修正的文件即可,所以响应速度非常快。

而 Webpack 修正某个文件过后,会自动以那个文件为入口重写 build 一次,所有的涉及到的依赖也都会被加载一遍,所以响应速度会慢很多。

Build

Vite 在生产模式下打包,须要采用 vite build 命令。

那个命令外部采用的是 Rollup 顺利完成的应用领域打包,最终还是会把文件都提前校对并打包到一起。

对于 Code Splitting 需求,Vite 外部采用的就是原生植物 Dynamic imports 特性同时实现的,所以打包结果还是只能够支持当代应用领域程序。

不过好在 Dynamic imports 特性是能有 Polyfill 的:

https://github.com/GoogleChromeLabs/dynamic-import-polyfillgithub.com/GoogleChromeLabs/dynamic-import-polyfill

也就是说,只要你想,它也能运行在相较低版本的应用领域程序中。

打包 or 不打包

Vite 的出现,引发了另外两个值得他们思索的问题:究竟还有没必要打包应用领域?

之前他们采用 Webpack 打包应用领域标识符,使之成为两个 bundle.js,主要有两个原因:

应用领域程序环境并不支持组件化零散的组件文件会产生大量的 HTTP 请求

随着应用领域程序的对 ES 国际标准支持的逐渐完善,第两个问题已经慢慢不存在了。现阶段绝大多数应用领域程序都是支持 ES Modules 的。

前端新玩具:Vite 及相关思考

零散组件文件确实会产生大量的 HTTP 请求,而大量的 HTTP 请求在应用领域程序端就会并发请求资源的问题;

前端新玩具:Vite 及相关思考

如上图所示,红色圈出来的请求就是并行请求,但是后面的请求就因为域名链接数已超过限制,而被挂起等候了一段天数。

在 HTTP 1.1 的国际标准下,每次请求都须要单独建立 TCP 链接,经过完整的通讯操作过程,非常耗时;

前端新玩具:Vite 及相关思考

而且每次请求除了请求体中的内容,请求头中也会包含很多数据,大量请求的情况下也会浪费很多资源。

但是这些问题随着 HTTP 2 的出现,也就不复存在了。

前端新玩具:Vite 及相关思考

关于 HTTP 1.1 与 HTTP 2 之间的差异,能通过那个链接新体验:

直观感受下 HTTP/2 比 HTTP/1 到底快了多少。

而且不打包也有两个好处,就是能把按需加载同时实现到极致。

关于 HTTP 2 的详细介绍,能参考:

https://blog.fundebug.com/2019/03/07/understand-http2-and-http3/blog.fundebug.com/2019/03/07/understand-http2-and-http3/
HTTP/1.1 vs HTTP/2: Whats the Difference? | DigitalOceanwww.digitalocean.com/community/tutorials/http-1-1-vs-http-2-what-s-the-difference前端新玩具:Vite 及相关思考

开箱即用

TypeScript – 内置支持less/sass/stylus/postcss – 内置支持(须要单独安装所对应的C++)

特性小结

Vite 带来的优势主要体现在提升合作开发者在合作开发操作过程中的新体验。

Dev Server 无需等候,即时开启;几乎实时的组件热预览;所需文件按需校对,避免校对用不到的文件;开箱即用,避免各种 Loader 和 Plugin 的配置;

同时实现原理

Vite 的核心理念机能:Static Server + Compile + HMR

核心理念路子:

将当前工程项目目录作为静态文件服务工程项目器的根目录拦截部分文件请求处理标识符中 import node_modules 中的组件处理 vue 单文件组件(SFC)的校对

3.通过 WebSocket 同时实现 HMR

手写同时实现

详细参考:

https://github.com/zce/vite-essentialsgithub.com/zce/vite-essentials
#!/usr/bin/env node const path = require(path) const { Readable } = require(stream) const Koa = require(koa) const send = require(koa-send) const compilerSfc = require(@vue/compiler-sfc) const cwd = process.cwd() const streamToString = stream => new Promise((resolve, reject) => { const chunks = [] stream.on(data, chunk => chunks.push(chunk)) stream.on(end, () => resolve(Buffer.concat(chunks).toString(utf8))) stream.on(error, reject) }) const app = new Koa() // 重写请求路径,/@modules/xxx => /node_modules/ app.use(async (ctx, next) => { if (ctx.path.startsWith(/@modules/)) { const moduleName = ctx.path.substr(10) // => vue const modulePkg = require(path.join(cwd, node_modules, moduleName, package.json)) ctx.path = path.join(/node_modules, moduleName, modulePkg.module) } await next() }) // 根据请求路径得到相应文件 /index.html app.use(async (ctx, next) => { // ctx.path // http://localhost:3080/ // ctx.body = my-vite await send(ctx, ctx.path, { root: cwd, index: index.html }) // 有可能还须要额外处理相应结果 await next() }) // .vue 文件请求的处理,即时校对 app.use(async (ctx, next) => { if (ctx.path.endsWith(.vue)) { const contents = await streamToString(ctx.body) const { descriptor } = compilerSfc.parse(contents) let code if (ctx.query.type === undefined) { code = descriptor.script.content code = code.replace(/export\s+default\s+/, const __script =) code += ` import { render as __render } from “${ctx.path}?type=template” __script.render = __render export default __script` // console.log(code) ctx.type = application/javascript ctx.body = Readable.from(Buffer.from(code)) } else if (ctx.query.type === template) { const templateRender = compilerSfc.compileTemplate({ source: descriptor.template.content }) code = templateRender.code } ctx.type = application/javascript ctx.body = Readable.from(Buffer.from(code)) } await next() }) // 替换标识符中特殊位置 app.use(async (ctx, next) => { if (ctx.type === application/javascript) { const contents = await streamToString(ctx.body) ctx.body = contents .replace(/(from\s+[“])(?![\.\/])/g, $1/@modules/) .replace(/process\.env\.NODE_ENV/g, “production”) } }) app.listen(3080) console.log(Server running @ http://localhost:3080)

本文转载自公号:圈圈的后端世界

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务