社区精选 | 可能改变前端工程化未来的特性:ESM Loader Hooks

业界 作者:SegmentFault 2022-07-19 14:46:19

今天给各位小伙伴推荐的是社区作者 卡颂 的文章,在这篇文章中他为大家介绍了 Node v18.6.0 中的新特性:ESM Loader Hooks。


让我们一起来了解一下吧~



大家好,我卡颂。


在最近发布的 Node v18.6.0 中,带来了一个试验特性 ESM Loader Hooks API

地址:

https://nodejs.org/en/blog/release/v18.6.0/


如果他最终落地,很可能会成为改变前端工程化未来的特性。本文我们来聊聊他。


本文参考:

Custom ESM loaders: Who, what, when, where, why, how

https://dev.to/jakobjingleheimer/custom-esm-loaders-who-what-when-where-why-how-4i1o


特性简介



用过 webpack 的朋友一定知道 webpack 中有个 loader 的概念,用于加载并处理不同类型文件,比如 css-loaderurl-loader


loader 的执行顺序取决于 webpack 内部对文件树解析、遍历的顺序。


今天要介绍的 ESM Loader Hooks 与 webpack loader 类似,只不过对文件树的解析、遍历是由 Node.js 原生支持的 ESM 规范(而不是打包工具)确定的。


通过定义不同 loader,就能在不使用工程化工具的前提下,对项目中各个 ESM 模块进行处理。


举个例子,在命令行通过 --experimental-loader 指令开启特性后,执行如下语句:


$> node --loader redirect.mjs app.mjs


其中,app.mjs 待处理的源文件.mjs 后缀指代该文件是个 ESM 模块(相对应的,.cjs 后缀指 CJS 模块)。


--loader 用于指定自定义的 ESM Loader,这里指定的是 redirect.mjsapp.mjs 会交由 redirect.mjs 处理。


redirect.mjs 代码如下:


// redirect.mjs
export function resolve(specifier, context, nextResolve) {  
  let redirect = 'app.prod.mjs';  
  
  switch(process.env.NODE_ENV) {    
    case 'development':
      redirect = 'app.dev.mjs';      
      break;    
    case 'test':
      redirect = 'app.test.mjs';      
      break;
  }  
  
  return nextResolve(redirect);
}


redirect.mjs 会根据 Node 当前所属环境改写文件的引入路径。


比如在开发环境(process.env.NODE_ENV === 'development'),app.mjs 经由 redirect.mjs 处理,会重定向到 app.dev.mjs


ESM Loader Hooks API 中之所以带 Hooks 字眼,是因为每个自定义 ESM Loader,都可以像钩子(Hooks)一样连接其他自定义 ESM Loader(或者 Node.js 提供的默认 ESM Loader)。


比如在如下语句中:


$> node --loader c.mjs --loader b.mjs --loader a.mjs app.mjs

app.mjs 会依次经过 a b c 三个自定义 ESM Loader 处理。


整个过程就像一个 promise.then 链条(事实上,每个 ESM loader 确实会返回一个 promise)。


实际例子



来看一个更接近日常开发的例子,考虑如下 ESM 模块:


// app.tsx
import ReactDOM from 'react-dom/client';
import {  
  BrowserRouter,
  useRoutes,
} from 'react-router-dom';

import App from './AppHeader.tsx';

import routes from 'https://example.com/routes.json' assert { type'json' };

import './global.css' assert { type'css' };

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(  
  <BrowserRouter>
    <App />
    <main>{useRoutes(routes)}</main>
  </BrowserRouter>
  );

其中包括很多 Node.js 不能处理的部分,比如:


  • TS 语法(需要编译成 JS,并处理文件描述符为 Node.js 可识别的形式)

  • JSX 转换(需要编译成 React.createElement 或 jsxRuntime.jsx

  • 需要处理引入的 CSS 文件

  • 需要处理远程引入的模块(代码中引入 routes 的语句)


处理 CSS 文件


以处理 CSS 文件举例,假设 CSS 文件内容如下:

.Container {  
  border: 1px solid black;
}

.SomeInnerPiece {  
  background-color: blue;
}

如果为了测试目的,仅需要生成类名对应快照即可,所以可以实现一个简易的 CSS loader,处理输入的 CSS 文件,将结果输出为 Node.js 可执行的 JSON 格式:


  "Container""Container"
  "SomeInnerPiece""SomeInnerPiece"
}

参考:CSS loader 的简易实现
https://github.com/JakobJingleheimer/demo-css-loader/blob/main/loader.mjs

处理远程引入的模块


再以处理处理远程引入的模块举例。

当识别到 https:// 开头的文件描述符(即 import 声明或 import() 表达式中字符串的部分)时, 可以利用 https 模块发起请求,返回请求对应 promise

import { get } from 'https';

export function load(url, context, nextLoad) {     if (url.startsWith('https://')) {    
    return new Promise((resolve, reject) => {         get(url, (res) => {        
        let data = '';
        res.on('data', chunk => data += chunk);
        res.on('end', () => resolve({          
          format: 'module',          
          shortCircuit: true,          
          source: data,
        }));
      }).on('error', err => reject(err));
    });
  }  
  
  return nextLoad(url, context);
}

参考:Https loader 的简易实现

https://github.com/nodejs/loaders-test/blob/835506a638c6002c1b2d42ab7137db3e7eda53fa/https-loader/loader.js


总结



当 ESM Loader Hooks 特性趋于稳定,配套的 loader 生态足够丰富后,很多原来需要打包工具才能实现的工程化需求都能用 Node.js 原生解决。

比如,要处理上述提到的 app.tsx 文件,只需执行如下命令:

$> node --loader typescript-loader --loader css-loader --loader network-loader app.tsx

你觉得这个特性对未来前端工程化会有多大影响呢?



SegmentFault 思否社区小编说


自 2022-07-01 起 SegmentFault 思否公众号改版啦!之后将陆续推出新的栏目和大家见面!(请拭目以待呀~


在「社区精选」栏目中,我们将为广大开发者推荐来自 SegmentFault 思否开发者社区的优质技术文章,这些文章全部出自社区中充满智慧的技术创作者哦!


希望通过这一栏目,大家可以共同学习技术干货,GET 新技能和各种花式技术小 Tips。


欢迎越来越多的开发者加入创作者的行列,我们将持续甄选出社区中优质的内容推介给更多人,让闪闪发光的技术创作者们走到聚光灯下,被更多人认识。


「社区精选」投稿邮箱:pr@segmentfault.com

投稿请附上社区文章地址




点击左下角阅读原文,到 SegmentFault 思否社区 和文章作者展开更多互动和交流,公众号后台回复“ 入群 ”即可加入我们的技术交流群,收获更多的技术文章~

- END -

关注公众号:拾黑(shiheibook)了解更多

赞助链接:

关注数据与安全,洞悉企业级服务市场:https://www.ijiandao.com/
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接