当前位置: 首页 > Web前端 > HTML

Web前端实训原理Vite源码分析

时间:2023-03-28 20:00:46 HTML

背景这里的背景介绍将从与Vite密切相关的两个概念的发展史说起,一个是JavaScript模块化标准,一个是前端构建工具。共存的模块化标准为什么JavaScript有多个共存的模块化标准?由于js在设计之初并没有模块化的概念,随着前端业务的复杂度不断增加,模块化越来越受到开发者的关注,社区中也出现了多种模块化的解决方案。他们互相学习,也有争议,形成多派,从CommonJS开始,到ES6中ESModules规范的正式推出结束,所有争论终于成为历史,ESModules也成为重要的前端基础设施。CommonJS:主要用于Node.js(Node@13.2.0开始支持直接使用ESModule)AMD:require.js依赖于前端,市场存货不建议使用CMD:sea.js是附近执行,市场股票不推荐使用ESModule:ES语言规范、标准、趋势、未来构建工具。前端工程这几年发展很快,各种搭建工具层出不穷。目前,Webpack仍然占据主导地位,npm的每周下载量超过2000万。下面是npmreleasetimeline_front-end培训中列出的一些开发者熟悉的构建工具。当前工程痛点常用的构建工具如Webpack,主要是通过抓取-编译-构建整个应用的代码(也就是常说的打包过程),生成一个编译优化好的兼容各种浏览器的代码生产环境代码。开发环境中的流程基本相同,需要先构建并打包整个应用,然后将打包后的代码交给devserver(开发服务器)。Webpack等构建工具的诞生,给前端开发带来了极大的便利,但随着前端业务的复杂化,js代码量成倍增长,打包构建时间越来越长,devserver(开发服务器)性能遇到瓶颈:服务启动慢:在大型项目中,devserver启动时间达到几十秒甚至几分钟。HMR热更新慢:即使采用HMR模式,其热更新速度也会随着应用规模的增长而明显下降,已经达到性能瓶颈,优化空间不大。缓慢的开发环境大大降低了开发者的幸福感。Vite就是在上述背景下应运而生的。什么是维特?基于esbuild和Rollup,依托浏览器自带的ESM编译功能,是实现极致开发体验的新一代构建工具!概念首先介绍一些下文中经常提到的基本概念:依赖:指开发过程中不会发生变化的部分(npm包、UI组件库),由esbuild进行预构建。源码:浏览器无法直接执行的非js代码(.jsx、.css、.vue等),vite只有在浏览器请求相关源码时才进行转换,提供ESM源码。开发环境使用浏览器原生的ESModule编译能力,省去耗时的编译过程,直接提供浏览器开发环境源码,devserver只提供轻量级服务。浏览器在执行ESM导入时,会向开发服务器发起模块的ajax请求,服务器简单处理后将源代码返回给浏览器。Vite中的HMR在原生ESM上执行。在编辑文件时,Vite只需要精确地去激活被编辑的模块,这样无论应用大小,HMR都能始终保持快速更新。使用esbuild处理项目依赖关系。esbuild是用go写的,比一般的node.js写的编译器快几个数量级。生产环境集成Rollup将生产环境代码打包,依托于其成熟稳定的生态和更加简洁的插件机制。处理流程对比Webpack先将整个应用打包,然后将打包后的代码提供给开发服务器,开发者才能开始开发。Vite直接将源代码交付给浏览器,实现开发服务器秒开。当浏览器显示需要相关模块的页面时,它会向开发服务器发送请求。服务器简单处理后返回给浏览器,实现真正的按需加载。基本使用创建vite项目$npmcreatevite@latest选择模板Vite内置了6个常用模板和对应的TS版本,可以满足大部分前端开发场景。您可以单击下表中的模板,直接在StackBlitz中试用。start{"scripts":{"dev":"vite",//启动开发服务器,别名:vitedev,viteserve"build":"vitebuild",//为生产环境构建产品"preview":"vitepreview"//生产构建产品的本地预览}}实现原理ESbuild使用go编译esbuild,在CPU密集的情况下更有性能优势,编译速度更快。以下是官网的构建速度对比:Browser:"Haveyoustartedyet?"服务员:“结束了。”开发商:“太快了,太喜欢了!!”依赖预构建的模块化兼容性:如开头后台所写,目前仍然存在多种模块化标准代码并存,Vite将依赖于.性能优化:npm包中大量ESM代码和大量导入请求会造成网络拥塞。Vite使用esbuild将具有大量内部模块的ESM关系转换为单个模块,以减少导入模块请求的次数。按需加载服务器只有在收到导入请求时才会编译相应的文件,并将ESM源代码返回给浏览器,实现真正的按需加载。CacheHTTP缓存:充分利用http缓存进行优化,对依赖使用max-age和immutable强缓存(不会改变的代码),对源代码使用304协商缓存,提高页面打开速度。文件系统缓存:Vite在预构建阶段将构建的依赖缓存到node_modules/.vite,只有在相关配置发生变化或手动控制时才进行重建,提高预构建速度。重写模块路径browserimport只能导入相对/绝对路径,开发代码中经常使用npm包名直接导入node_module中的模块,需要转换为browser_web前端训练。es-module-lexer扫描导入语法magic-string重写模块的导入路径//开发代码import{createApp}from'vue'//转换后import{createApp}from'/node_modules/vue/dist/vue.js的源码分析类似于Webpack-dev-server。Vite也使用WebSocket与客户端建立连接,实现热更新。源码实现基本上可以分为两部分。源码位置为:vite/packages/vite/src/clientclient(客户端)vite/packages/vite/src/nodeserver(开发服务器)客户端代码会在服务启动时注入客户端,供客户端处理WebSocket消息(如更新页面上的一个模块,刷新页面);服务器代码是服务器端的逻辑,用于处理代码的构建和页面模块的请求。简单看了下源码(vite@2.7.2),核心功能主要是以下几个方法(以下是源码截取,删除了部分逻辑):命令行启动服务后npmrundev,源码执行cli.ts,调用createServer方法创建http服务,监听开发服务器端口。//源码位置vite/packages/vite/src/node/cli.tsconst{createServer}=awaitimport('./server')try{constserver=awaitcreateServer({root,base:options.base,...})if(!server.httpServer){thrownewError('HTTPservernotavailable')}awaitserver.listen()}1。createServer方法的执行做了很多工作,比如整合配置项,创建http服务(早期由koa创建),创建WebSocket服务,创建源码文件监听,插件执行,优化优化等标记在下面的注释中。//源码位置vite/packages/vite/src/node/server/index.tsexportasyncfunctioncreateServer(inlineConfig:InlineConfig={}):Promise{//Vite配置集成constconfig=awaitresolveConfig(inlineConfig,'serve','development')constroot=config.rootconstserverConfig=config.server//创建http服务consthttpServer=awaitresolveHttpServer(serverConfig,middlewares,httpsOptions)//创建ws服务constws=createWebSocketServer(httpServer,config,httpsOptions)//创建一个watcher并设置代码文件进行监听constwatcher=chokidar.watch(path.resolve(root),{ignored:['/node_modules/','/.git/',...(Array.isArray(ignored)?ignored:[ignored])],...watchOptions})asFSWatcher//创建服务器对象constserver:ViteDevServer={config,middlewares,httpServer,watcher,ws,moduleGraph,listen,...}//文件监听变化,websocket与前端通信watcher.on('change',async(file)=>{...handleHMRUpdate()})//中间一大堆waremiddlewares.use(...)//optimizeconstrunOptimize=async()=>{...}returnserver}2.使用chokidar监听文件变化,绑定监听事件//源码位置vite/packages/vite/src/node/server/index.tsconstwatcher=chokidar.watch(path.resolve(root),{ignored:['/node_modules/','/.git/',...(Array.isArray(ignored)?ignored:[ignored])],ignoreInitial:true,ignorePermissionErrors:true,disableGlobbing:true,...watchOptions})作为FSWatcher3。通过ws来创建一个WebSocket服务,该服务用于在检测到文件更改时触发热更新并向客户端发送消息。//源码位置vite/packages/vite/src/node/server/ws.tsexportfunctioncreateWebSocketServer(...){letwss:WebSocketconsthmr=isObject(config.server.hmr)&&config.server.hmrconstwsServer=(hmr&&hmr.server)||serverif(wsServer){wss=newWebSocket({noServer:true})wsServer.on('upgrade',(req,socket,head)=>{//服务就绪if(req.headers['sec-websocket-protocol']===HMR_HEADER){wss.handleUpgrade(req,socketasSocket,head,(ws)=>{wss.emit('connection',ws,req)})}})}else{...}//当服务就绪后,可以在浏览器控制台看到熟悉的打印[vite]connected.wss.on('connection',(socket)=>{socket.send(JSON.stringify({type:'connected'}))...})//失败wss.on('error',(e:Error&{code:string})=>{...})//返回ws对象return{on:wss.on.bind(wss),off:wss.off.bind(wss),//向客户端发送信息//多个客户端触发发送(payload:HMRPayload){conststringified=JSON.stringify(payload)wss.clients.forEach((client)=>{//readyState1表示连接是openclient.send(stringified)})}}}4.激活后会向浏览器注入代码处理客户端收到的WebSocket消息,如重新发起模块请求,刷新页面//源码位置vite/packages/vite/src/client/client.tsasync函数handleMessage(payload:HMRPayload){switch(payload.type){case'connected':console.log([vite]connected.)breakcase'update':notifyListeners('vite:beforeUpdate',payload)...breakcase'custom':{notifyListeners(payload.eventasCustomEventName,payload.data)...break}case'full-reload':notifyListeners('vite:beforeFullReload',payload)...breakcase'prune':notifyListeners('vite:beforePrune',payload)...breakcase'error':{notifyListeners('vite:error',payload)...break}default:{constcheck:never=payloadreturncheck}}}优点快!快的!非常快!!高度集成,开箱即用。基于ESM的快速热更新,无需打包编译。基于esbuild的依赖预处理比Webpack等node编写的编译器快几个数量级。兼容Rollup庞大的插件机制,插件开发更简洁。不绑定Vue,支持React等其他框架,是一个独立的构建工具。内置SSR支持。自然支持TS。Vue不足还是第一优先支持,量身定制的编译插件,对React的支持没有Vue强大。虽然2.0正式版已经上线,可以用于正式的线上制作,但目前市场上实践的很少。生产环境集成了Rollup打包,与开发环境最终执行的代码不一致。与webpack对比由于Vite在开发环境注重极致体验,在生产环境集成了Rollup,所以这里的对比主要是Webpack-dev-server和Vite-dev-server的对比:长期以来,Webpack都有在前端工程领域占据领先地位。霸道,Vite上线以来备受关注,社区活跃,GitHubstar数激增,目前达到37.4K。webpack配置丰富,使用极其灵活,但入门成本高。Vite开箱即用的配置高度集成。webpack启动服务需要打包构建,速度较慢。Vite免编译,秒开。Webpack热更新需要打包构建。速度很慢。Vite以毫秒为单位响应。Webpack成熟稳定,资源丰富,有大量实战案例。Vite的实践较少。Vite使用esbuild编译,构建速度比webpack快几个数量级兼容性默认目标浏览器支持原生ESM和script标签上的原生ESM动态导入。可以使用官方插件@vitejs/plugin-legacy将其转换为繁体版本和对应的polyfill。文章来自前端开发