当前位置: 首页 > 科技观察

在浏览器中运行Vite!

时间:2023-03-19 22:35:14 科技观察

大家好,我是ssh。前几天在推特上冲浪的时候,看到FrancoisValdy宣布自己制作了browser-vite[1],并在浏览器中成功运行了Vite。这引起了我的兴趣,如何在浏览器上运行一个严重依赖node的Vite?下面就和小编一起一探究竟吧。原理简述ServiceWorker[2]:用于替代Vite的HTTP服务器。WebWorker[3]:运行browser-vite来处理主线程。文件系统被内存中的模拟文件系统取代。转换具有特殊扩展名(.ts、.tsx、.scss……)的导入。遇到的挑战没有真正的文件系统Vite[4]对文件系统做了很多工作。读取项目文件、监控文件变化、处理globs等……在浏览器模拟实现的内存文件系统中,这些是很难实现的,所以browser-vite删除了监控、globs和配置文件。降低复杂性。项目文件保存在内存文件系统中,因此broswer-vite和vite插件可以正常处理它们。没有“node_modules”Vite依赖于node_modules的存在来解决依赖。它们将被预先打包(DependingPre-Bundling)[5]以在启动时进行优化。同样为了降低复杂性,broswer-vite小心地从Vite中移除了node_modules解析和依赖预打包。所以使用browser-vite的用户需要创建一个Vite插件[6]来解析裸模块导入。正则表达式“LinebehindAssertions”Vite中的一些代码使用lookbehindassertions[7]。在Node.js中没问题,但在Safari中不行。所以作者重写了这些正则表达式。热更新(HMR)Vite使用WebSockets[8]同步服务器(节点)和客户端(浏览器)之间的代码更改。在browser-vite中,服务端是ServiceWorker+Viteworker,客户端是iframe。所以作者从WebSockets切换到使用iframe的post消息。如何使用截至本文撰写之时,该工具尚未开箱即用。如果要使用它,需要阅读大量Vite内部处理细节。有兴趣的可以关注browser-vite的README[9]了解最新的使用方法。安装安装browser-vitenpm包。$npminstall--savebrowser-vite或$npminstall--savevite@npm:browser-vite将“vite”导入重写为“browser-vite”iframe-browser-vite的窗口需要一个iframe来显示由browser-vite内部页面提供的。ServiceWorker-浏览器内Web服务器ServiceWorker,可捕获来自iframe的特定url请求。使用workbox[10]的示例:workbox.routing.registerRoute(/^https?:\/\/HOST/BASE_URL\/(\/.*)$/,async({request,params,url,}:import('workbox-routing/types/RouteHandler').RouteHandlerCallbackContext):Promise=>{constreq=request?.url||url.toString();const[pathname]=paramsassstring[];//sendtherequesttoviteworkerconstresponse=awaitpostToViteWorker(路径名)返回响应;});大多数情况下,postMessage[11]和broadcast-channel[12]用于向“ViteWorker”发送消息。ViteWorker-处理请求ViteWorker是一个WebWorker,它处理ServiceWorkers捕获的请求。创建Vite服务器的示例:import{transformWithEsbuild,ModuleGraph,transformRequest,createPluginContainer,createDevHtmlTransformFn,resolveConfig,generateCodeFrame,ssrTransform,ssrLoadModule,ViteDevServer,PluginOption}from'vite';exportasyncfunctioncreateServer=esync()=>{consyncfunctioncreateServer=esync()=>{consyncfunction[//virtualplugintoprovideviteclient/envspecialentries(seebelow)viteClientPlugin,//virtualplugintoresolveNPMdependencies,e.g.usingunpkg,skypackoranotherprovider(browser-viteonlyhandlesprojectfiles)nodeResolvePlugin,//addvitepluginsyouneedhere(e.g.vue,react,astro...)]base:BASE_URL/edinservice,/worknotreallyused,butneedstobedefinedtoenabledepoptimizationscacheDir:'browser',root:VFS_ROOT,//anyotherconfiguration(e.g.resolvealias)},'serve');constplugins=config.plugins;constpluginContainer=awaitcreatePluginContainer(config);constmoduleGraph=newModuleGraph((url)=>pluginContainer.resolveId(url));constwatcher:any={on(what:string,cb:any){returnwatcher;},add(){},};constserver:ViteDevServer={config,pluginContainer,moduleGraph,transformWithEsbuild,transformRequest(url,options){returntransformRequest(url,server,options);},ssrTransform,printUrls(){},_globImporters:{},ws:{send(data){//sendHMRdatatoviteclientiniframehoweveryouwant(post/broadcast-channel...)},asyncclose(){},on(){},off(){},},watcher,asyncssrLoadModule(url){returnssrLoadModule(url,server,loadModule);},ssrFixStacktrace(){},asyncclose(){},asyncrestart(){},_optimizeDepsMetadata:null,_isRunningOptimizer:false,_ssrExternals:[],_restartPromise:null,_forceOptimizeOnRestart:false,_pendingRequests:newMap(),};server.transformIndexHtml=createDevHtmlTransformFn(server);//applyserverconfigurationhooksfrompluginsconstpostHooks:((()=>void)|void)[]=[];for(constpluginofplugins){if(plugin.configureServer){postHooks.push(awaitplugin.configureServer(server));}}//runpostconfighooks//Thisisappliedbeforethehtmlmiddlewaresothatusermiddlewarecan//servecustomcontentinsteadofindex.html.postHooks.forEach((fn)=>fn&&fn());awaitpluginContainer.buildStart({});awaitrunOptimize(server);returnserver;}通过browser-vite处理请求的伪代码:import{transformRequest,isCSSRequest,isDirectCSSRequest,injectQuery,removeImportQuery,unwrapId,handleFileAddUnlink,handleHMRUpdate,}from'vite/dist/browser';...async(req)=>{let{url,accept}=reqconsthtml=accept?.includes('text/html');//strip?importurl=removeImportQuery(url);//Stripvalididprefix.ThisisprependedtoresolvedIdsthatare//notvalidbrowserimportspecifiersbytheimportAnalysisplugin.url=unwrapId(url);//对于CSS,我们需要区分普通的CSS请求和//importsif(isCSSRequest(url)&&'actext?.includes/css')){url=injectQuery(url,'direct');}letpath:string|undefined=url;try{letcode;path=url.slice(1);if(html){code=awaitserver.transformIndexHtml(`/${path}`,fs.readFileSync(path,'utf8'));}else{constret=awaittransformRequest(url,server,{html});code=ret?.code;}//Rreturncodereponse}catch(err:any){//Returnerrorresponse}}查看Vite内部中间件源代码[13]了解更多详情它与StackblitzWebContainers["WebContainers"](https://blog.stackblitz.com/posts/introducing-webcontainers/""WebContainers""):在浏览器中运行Node.jsStackblitz的WebContainers也可以在浏览器中运行Vite。你可以优雅地去vite.new有一个工作环境。作者声明自己不是WebContainers的专家,但简而言之,browser-vite在Vite层面模拟了FS和HTTPS服务器,WebContainers在Node.js层面模拟了FS等很多东西,Vite只是需要做一些额外的修改可以在上面运行。它可以将node_modules存储在浏览器的WebContainer中。但它不会直接运行npm或yarn,可能是因为它会占用太多空间。他们将这些命令链接到Turbo[14]——他们的包管理器。WebContainers还可以运行其他框架,例如Remix[15]、SvelteKit[16]或Astro[17]。这真太了不起了?这真让人兴奋??作者非常尊重WebContainer的团队,Stackblitz团队很棒!WebContainers的一个缺点是它目前只能在Chrome[18]上运行,但它可能很快就会在Firefox[19]上运行。browser-vite目前可用于Chrome、Firefox和Safari浏览器。简而言之,WebContainers在较低的抽象层次上运行Vite。browser-vite在更高的抽象层次上运行,非常接近Vite本身。比如对于那些复古玩家来说,browser-vite有点像UltraHLE(NintendoN64Emulator)????(*)gametechwiki.com:High/LowLevelEmulator[20]作者的下一个计划browser-vite在作者计划解决方案的核心。打算逐步推出他们的全线产品:Backlight.devComponents.studioWebComponents.devReplica.dev(新应用即将推出)展望未来,作者将继续在browser-vite中做出贡献并报告上游。上个月他们还宣布赞助EvanYou和Patak以支持Vite[21]以支持这个很棒的项目。想知道更多?GitHub存储库:browser-vite[22]使用#browser-vite频道加入Discord[23]。??参考https://divriots.com/blog/vite-in-the-browserhttps://github.com/divriots/browser-vitehttps://blog.stackblitz.com/posts/introducing-webcontainers/References[1]browser-vite:https://github.com/divriots/browser-vite[2]ServiceWorker:https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API[3]WebWorker:https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers[4]Vite:https://vitejs.dev/[5]预打包(取决于预捆绑):https://vitejs.dev/guide/dep-pre-bundling.html[6]Vite插件:https://vitejs.dev/guide/api-plugin.html[7]打包后断言:https://www.regular-expressions.info/lookaround.html[8]WebSockets:https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API[9]browser-vite的自述文件:https://github.com/divriots/browser-vite/blob/browser-vite/README.md#usage[10]workbox:https://developers.google.com/web/tools/workbox[11]postMessage:https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage[12]b广播频道:https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API[13]Vite内部中间件源码:https://github.com/vitejs/vite/tree/main/packages/vite/src/node/server/middlewares[14]Turbo:https://developer.stackblitz.com/docs/platform/turbo/[15]Remix:https://blog.stackblitz.com/posts/remix-runs-on-webcontainers/[16]SvelteKit:https://blog.stackblitz.com/posts/sveltekit-supported-in-webcontainers/[17]Astro:https://blog.stackblitz.com/posts/astro-support/[18]仅适用于Chrome:https://developer.stackblitz.com/docs/platform/browser-support[19]适用于Firefox:https://developer.stackblitz.com/docs/platform/browser-support/#testing-on-firefox[20]gametechwiki.com:高/低级仿真:https://emulation.gametechwiki.com/index.php/High/Low_level_emulation[21]感谢EvanYou并由Patak赞助支持Vite:https://divriots.com/blog/supporting-vitejs[22]browser-vite:https://github.com/divriots/browser-vite[23]Discord:https://d和谐。gg/XkQxSU9本文转载自微信公众号《前端从进阶到入学》,大家可以使用关注二维码转载本文,进阶到录取请联系前端公众号。