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

可能改变前端工程未来的特性:ESMLoaderHooks

时间:2023-03-13 18:27:57 科技观察

大家好,我是Kason。在最近发布的Nodev18.6.0中,带来了一个实验性的特性ESMLoaderHooksAPI[1]。如果他最终落地,很可能会成为改变前端工程未来的一个特性。这篇文章就来说说他吧。本文参考:CustomESMloaders:Who,what,when,where,why,how[2]。特性介绍用过webpack的朋友一定知道webpack中有一个loader的概念,用来加载和处理不同类型的文件,比如css-loader和url-loader。loader的执行顺序取决于webpack内部解析和遍历文件树的顺序。今天要介绍的ESMLoaderHooks和webpackloader类似,只是文件树的解析和遍历是由Node.js原生支持的ESM规范决定的(而不是打包工具)。通过定义不同的加载器,可以在不使用工程工具的情况下处理项目中的每个ESM模块。例如,在命令行通过--experimental-loader命令启用该特性后,执行如下语句:processed”,而.mjs后缀表示该文件是ESM模块(相应地,.cjs后缀表示CJS模块)。--loader用于指定一个自定义的ESMLoader,这里指定redirect.mjs,app.mjs会被redirect.mjs处理。redirect.mjs代码如下://redirect.mjsexportfunctionresolve(specifier,context,nextResolve){letredirect='app.prod.mjs';switch(process.env.NODE_ENV){case'development':redirect='app.dev.mjs';休息;case'test':redirect='app.test.mjs';休息;}returnnextResolve(redirect);}redirect.mjs会根据“Node的当前环境”重写文件的导入路径。比如在开发环境中(process.env.NODE_ENV==='development'),app.mjs经过redirect.mjs处理后会重定向到app.dev.mjs。ESMLoaderHooksAPI中之所以包含Hooks这个词,是因为每个“自定义ESMLoader”都可以像钩子(Hooks)一样连接到其他“自定义ESMLoader”(或者Node.js默认提供的ESMLoader).例如,在下面的语句中:$>node--loaderc.mjs--loaderb.mjs--loadera.mjsapp.mjsapp.mjs将依次被三个“自定义ESM加载器”abc处理。整个过程就像一个promise.then链(事实上,每个ESM加载器确实返回一个promise)。实际示例要查看更接近日常开发的示例,请考虑以下ESM模块://app.tsximportReactDOMfrom'react-dom/client';import{BrowserRouter,useRoutes,}from'react-router-dom';importApp来自'./AppHeader.tsx';从'https://example.com/routes.json'导入路由断言{type:'json'};import'./global.css'assert{type:'css'};constroot=ReactDOM.createRoot(document.getElementById('root'));root.render(

{useRoutes(路由)}
);其中包括很多Node.js无法处理的部分,例如:TS语法(需要编译成JS,将文件描述符处理成Node.js可识别的形式)。JSX转换(需要编译为React.createElement或jsxRuntime.jsx)。需要处理导入的CSS文件。远程引入的模块(代码中引入路由的语句)需要处理。处理CSS文件以处理CSS文件为例,假设CSS文件内容如下:.Container{border:1pxsolidblack;}.SomeInnerPiece{background-color:blue;}为了测试,只需要生成一个类名对应的快照,所以你可以实现一个简单的CSS加载器,处理输入的CSS文件,并将结果输出为Node.js可执行JSON格式:{"Container":"Container","SomeInnerPiece":"SomeInnerPiece”}参考:CSSloader易于实现[3]。处理远程导入模块再以“处理远程导入模块”为例。当识别到以https://开头的文件描述符(即import语句或import()表达式中的字符串部分)时,可以使用https模块发起请求,并返回对应的promise请求:从'https'导入{get};exportfunctionload(url,context,nextLoad){if(url.startsWith('https://')){returnnewPromise((resolve,reject)=>{get(url,(res)=>{让数据='';res.on('data',chunk=>data+=chunk);res.on('end',()=>resolve({format:'module',shortCircuit:true,source:data,}));}).on('错误',err=>reject(err));});}returnnextLoad(url,context);}参考:Httpsloader[4]的简单实现。综上所述,当ESMLoaderHooks特性趋于稳定,配套的loader生态足够丰富时,很多原本需要打包工具才能实现的工程需求,都可以通过Node.js原生解决。例如,要处理上面提到的app.tsx文件,只需执行以下命令:$>node--loadertypescript-loader--loadercss-loader--loadernetwork-loaderapp.tsx你觉得这个特性有用吗对未来前端工程有多大影响?参考文献[1]ESMLoaderHooksAPI:https://nodejs.org/en/blog/release/v18.6.0/。[2]自定义ESM加载程序:谁、什么、何时、何地、为什么、如何:https://dev.to/jakobjingleheimer/custom-esm-loaders-who-what-when-where-why-how-4i1o。[3]CSS加载器的简单实现:https://github.com/JakobJingleheimer/demo-css-loader/blob/main/loader.mjs。[4]Https加载器的简单实现:https://github.com/nodejs/loaders-test/blob/835506a638c6002c1b2d42ab7137db3e7eda53fa/https-loader/loader.js。