是什么尤大选择放弃Webpack?——vite 原理解析

2022-12-06 0 965

责任编辑并行在提早布局写手:「淡黄二十世纪」对个人网志shymean.com上

提早布局书名镜像:

https://juejin.im/post/5ea2361de51d454714428b44

博纳瓦县孔布龙在Vue 3.0 beta现场直播中提及了两个vite的辅助工具,其叙述是:特别针对Vue单网页模块的无装箱合作开发伺服器,能间接在应用程序运转允诺的vue文档,对其基本原理较为钟爱,因而新体验并写出了责任编辑,主要就主要就包括vite同时实现基本原理预测和许多思索。

trained科学知识

vite轻度倚赖module sciprt的优点,因而须要提早作成课外,参照:JavaScript modules 模块 – MDN。module sciprt容许在应用程序中间接运转原生植物全力支持模块

<script type=“module”> // index.js能透过export求出模块,也能在当中竭尽全力采用import读取其它倚赖 import App from ./index.js </script>

当邂逅import倚赖时,会间接发动http允诺相关联的模块文档。

合作开发自然环境

责任编辑采用的版为[email protected],附github工程项目门牌号~现阶段那个工程项目虽说每晚都在预览 具体来说布季夫库房

git clone https://github.com/vuejs/vite cd vite && yarn

自然环境加装完后在工程项目下建立examples产品目录,追加index.html和Comp.vue文档,这儿间接用README.md中的范例

具体来说是inidex.html

<div id=“app”></div> <script type=“module”> import { createApp } from vue import Comp from ./Comp.vue createApp(Comp).mount( #app ) </script>

然后是Comp.vue

<template> <button @click=“count++”>{{ count }} times</button> </template> <script> export default { data: () => ({ count: 0}) }</script> <style scoped> button { color: red } </style>

然后在exmples产品目录下运转

../bin/vite.js

即可在应用程序http://localhost:3000打开预览,同时全力支持文档热预览哦~

如果须要调试源码,启动npm run dev即可,会开启tsc -w –p监听src产品目录的改动并实时输出到dist产品目录下,接下来就能开启欢乐的源码时间~

入口文档

现阶段那个工程项目迭代非常频繁(昨天还有historyFallbackMiddleware那个中间件呢今天虽说就没了),但是大概的同时实现思路应该是基本确定了,因而先确定本次源码阅读目标:「了解如何在不使用webpack等装箱辅助工具的前提下间接运转vue文档」。基于那个目的,主要就是了解同时实现思路,理清整体结构,不用拘泥于具体细节。

从入口bin/vite.js开始

const server = require( ../dist/server ).createServer(argv)

能看见createServer方法,间接定位到src/server/client.tx。vite采用的是Koa构建服务端,在createServer中主要就透过中间件注册相关功能

// src/index.ts // 提早预告这四个插件的作用 constinternalPlugins: Plugin[] = [ modulesPlugin,// 处理入口html文档script标签和每个vue文档的模块倚赖 vuePlugin, // vue单网页模块导出,将template、script、style导出成不同的响应内容,能理解为简易版的vue-loader hmrPlugin, // 采用websocket同时实现文档热预览 servePlugin // koa配置插件,现阶段看来主要就是配置协商缓存相关 ] export function createServer({ root = process.cwd(), middlewares: userMiddlewares = [] }: ServerConfig = {}): Server { const app = new Koa() constserver = http.createServer(app.callback())// 预留了userMiddlewares方便提供后续API ;[…userMiddlewares, …middlewares].forEach((m) => m({ root, app, server }) )return server }

vite是透过下面这种middleware的形式注册koa中间件,

export const modulesPlugin: Plugin = ({ root, app }) => { // 每个插件实际上是注册koa中间件 app.use(async(ctx, next) => {}) }

看起来跟Vue2的源码结构较为类似,透过装饰器逐步添加功能~现阶段只须要理清这四个插件的作用就能了。

// vue2源码结构 initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue) moduleResolverMiddleware

那个中间件的作用编译index.html和SFC等文档内容,处理相关的倚赖。

比如上面的html文档script标签内容,透过rewriteImports等方法的处理会被编译成

import { createApp } from /__modules/vue // 之前是import { createApp } from vue import Comp from ./Comp.vue createApp(Comp).mount( #app )

这样当应用程序导出并运转那个module类型的script标签时,就会允诺相关联的模块文档,当中

/__modules/vue是 koa 伺服器的静态资源产品目录文档,./Comp.vue是我们编写的单网页模块文档此外虽说还会提供sourcemap等功能

对于入口文档而言,须要script标签下相关倚赖。对于单网页模块而言,在vue-loader中,也须要处理tmplate、script和style标签;在vite中,这些倚赖都会被当做css和js文件允诺的方式进行读取。

单网页模块主要就包含template、script和style标签,当中script标签内代码的求出会被编译成

// 读取热预览模块客户端,后面会提及 import “/__hmrClient” let __script; export default (__script = { data: () => ({ count: 0 }) }) // 根据type进行区分,样式文档type=style import “/Comp.vue?type=style&index=0” // 保留css scopeID __script.__scopeId = “data-v-92a6df80” // render函数文档type=template import { render as __render } from “/Comp.vue?type=template”__script.render = __render __script.__hmrId =“/Comp.vue”

而style及template标签会被重写成/Comp.vue?type=xxx的形式,重新发送http允诺,那个透过query参数的形式区分并加载SFC文档各个模块内容的方式,与vue-loader中透过webpack的resourceQuery配置进行处理如出一辙,如果了解vue-loader运转基本原理的同学看到这儿估计就已经恍然大悟了,之前写过一篇从vue-loader源码预测CSS-Scoped的同时实现,里面也介绍了vue-loader的大致基本原理。

回到vite,现在我们清楚了moduleResolverMiddleware的作用,主要就就是重写模块路径,将SFC文档的倚赖透过query参数进行区分,方便应用程序透过url读取实际模块。打开应用程序控制台,能查看具体的文档允诺

11111111111111111111

VuePlugin

前面提到单网页模块的template和style会被处理成单独的的import路径,透过query.type区分,那么当伺服器接收到相关联的url允诺时,如何返回正确的资源内容呢?答案就在第二个插件VuePlugin中。

单网页文档的允诺有个特点,都是以*.vue作为允诺路径结尾,当伺服器接收到这种特点的http允诺,主要就处理

根据ctx.path确定允诺具体的vue文档采用parseSFC导出该文档,获得descriptor,两个descriptor包含了那个模块的基本信息,主要就包括template、script和styles等属性下面是Comp.vue文档经过处理后获得的descriptor{ filename: /Users/Txm/source_code/vite/examples/Comp.vue , template: { type: template , content: <button @click=”count++”>{{ count }} times1</button>, loc: { source: <button @click=”count++”>{{ count }} times1</button>, start: [Object], end: [Object] }, attrs: {}, map: { version: 3, sources: [Array], names: [], mappings: ;AACA , file: /Users/Txm/source_code/vite/examples/Comp.vue , sourceRoot: , sourcesContent: [Array] } }, script: { type: script , content: export default {data: () => ({ count: 0 })}, loc: { source: export default {data: () => ({ count: 0 })}, start: [Object], end: [Object] }, attrs: {}, map: { version: 3, sources: [Array], names: [], mappings: ;AAKA;AACA;AACA , file: /Users/Txm/source_code/vite/examples/Comp.vue , sourceRoot: , sourcesContent: [Array] } }, styles: [ { type: style , content: button { color: red }, loc: [Object], attrs: [Object], scoped: true, map: [Object] } ], customBlocks: [] }然后根据descriptor和ctx.query.type优先选择相关联类型的方法,处理后返回ctx.bodytype为空时表示处理script标签,采用compileSFCMain方法返回js内容type为template时表示处理template标签,采用compileSFCTemplate方法返回render方法type为styles时表示处理style标签,采用compileSFCStyle方法返回css文档内容

回头整理一下流程

入口文档倚赖Comp.vue的script代码Com.vue倚赖tempplate编译的render方法,倚赖style标签编译的css代码,这两个文档放在script的编译代码中进行倚赖声明// Comp.vue返回的文档内容,能看见跟入口文档的script标签内容较为相似 import { updateStyle } from “/__hmrClient” const__script = {data: () => ({ count: 0 }) } // style标签内容导出后的css代码 updateStyle(“92a6df80-0”, “/Comp.vue?type=style&index=0”) __script.__scopeId = “data-v-92a6df80” // temlpate标签内容导出后的render import { render as__render }from “/Comp.vue?type=template” __script.render = __render __script.__hmrId = “/Comp.vue” export default __script

每个标签内容导出完成之后,会透过LRUCache缓存起来,方便下次重复采用

export const vueCache = new LRUCache<string, CacheEntry>({ max:65535 })

至此,我们就大致了解了vite是如何透过koa间接运转vue文档的,其思路跟vue-loader较为类似,借助module script处理文档倚赖,然后透过拼接不同的query.type处理单网页文档导出后的各个资源文档,最后响应给应用程序进行渲染。

hmrPlugin

前面提及vite也是全力支持文档热预览的,既然没有采用webpack,那该是如何做到的呢?答案就是自己同时实现两个哈哈哈~

热预览主要就透过webSocket同时实现,主要就包括ws服务端和ws客户端两个部分,hmrPlugin主要就负责ws服务端的部分,ws客户端在src/client.ts中同时实现,并透过在第一步处理模块倚赖时import “/__hmrClient”将服务端和客户端关联起来。

现阶段主要就定义了下面几种消息类型

reloadrerenderstyle-updatestyle-removefull-reload

当文档发生变化时,服务端在handleVueSFCReload方法中会根据变化的类型推送不同的消息,当客户端接收到相关联消息时,会结合vue.HMRRuntime进行处理或者重新读取新的资源。

热预览这儿现阶段还有不少TODO,感觉是两个学习热预览基本原理的不错案例,先码一下后面回头重新细读。

关于热预览的基本原理,社区有不少基本原理预测了,不妨移步阅读

Webpack 热预览轻松理解webpack热预览基本原理

servePlugin

那个插件主要就用于同时实现许多koa允诺和响应的配置。

经过上面的预测,每次允诺时,都会从入口文档开始,依次预测每个倚赖

对于普通文档,间接查找伺服器静态资源,透过servePlugin中配置koa-static同时实现对于vue文档,会重新拼接http允诺,对于每个允诺,主要就包括path和query,当中path用于确定模块文档,query.type用于确定具体采用啥方法来返回响应内容

在上面这一步,很明显对于每个vue文档而言,都会发送多个http允诺,然后执行查找和导出的操作是很频繁的,如果不配置缓存,伺服器的性能负担较为大,koa-conditional-get和koa-etag应该就是为了解决那个问题,不过现阶段看起来还没有同时实现。

小结

至此,就完成了vite源码的基础阅读,由于本地阅读源码的主要就目的是了解整个辅助工具的同时实现基本原理和大致功能,因而并没有深入了解每个函数的同时实现细节,几个较为重要的方法主要就包括rewriteImports、compileSFCMain、compileSFCTemplate、compileSFCStyle、updateStyle等均没有展示具体代码同时实现,主要就的收获是了解了

结合module script和query.type同时实现一套类似于vue-loader的机制,间接在服务端运转vue文档采用websocket手动同时实现热预览,由于时间关系这儿并没有细读~

刚看见vite介绍时就觉得这会是两个非常有趣的辅助工具,虽然还没有正式发布,耐不住去看了一下。感觉主要就的作用有

采用vite快速合作开发demo,而不必加装一大堆倚赖类似于jsfiddle等在线预览vue文档,方便合作开发、测试和分发单文档模块

现阶段看来vite还缺少装箱等重要优点,应该是没法替代webpack等辅助工具的。不过感觉vite应该也不是用来替换现有合作开发辅助工具的,所以后面大概也不会添加装箱等功能吧~

相关文章

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

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