从这个API能看到整个前端的缩影
The following article is from 魔术师卡颂 Author 卡颂
出处:魔术师卡颂(ID:gh_52d0bec584f9)
如果要从JS
中找一个API
作为整个前端的缩影,ESM
规范中的import
再合适不过了。
本文我们从这个API
出发,来聊聊web
的发展。
web的本质是开放
在所有JS
运行时中,web
是最开放的(紧随其后的可能是deno
)。这一点可以从import
语法的「模块说明符」窥探一丝端倪。
// 模块说明符为 './a.js'
import xxx from './a.js'
在ES
规范中只明确「模块说明符是一个字符串字面量」,并没有限制「如何解析模块说明符」,所以「解析模块说明符」的任务就交给了宿主环境。
在web
的HTML
规范中,「模块说明符」可以是如下形式:
绝对路径的 url
,比如:
import confetti from 'https://cdn.skypack.dev/canvas-confetti';
以 /
、./
、../
开头的相对路径,比如:
import xxx from './a.js'
定义模块名到 url
的映射,再以模块名的形式引入,比如:
<script type="importmap">
{
"imports": {
"moment": "/node_modules/moment/src/moment.js"
}
}
</script>
再引入模块:
import moment from "moment";
PS:这种方式被称为import-maps[1],当前浏览器兼容性还不高:
可以发现,这三种方式对「模块说明符」的来源都很开放。反观Node.js
运行时,如果以包名的形式引入模块,比如:
import moment from "moment";
背后是一套指向node_modules
,并最终指向npm
库的机制。npm
是家私人公司,被github
收购,而github
被微软
收购。
所以,如果某一天国内无法直接安装npm
包,也不必惊讶,毕竟他的背后是一家私人公司。
与之相对,web
的开放让他不会面临这种囧境。
兼容性的迭代
web
的发展史,就是一部「新三年、旧三年、修修补补又三年」的兼容史。很多API
的兼容性问题可以通过polyfill
解决。
所以,很自然的,库作者在面对模块规范的兼容性问题时,也想替用户做到最好。但是,这份努力也让代码行为变得更扑朔迷离。
比如:在ESM
模块中是可以引入CJS
模块的。对于如下CJS
模块:
// a.js cjs模块
exports.hello = () => {
console.log('hello')
}
在同级的ESM
模块中引入,并通过解构
或者对象方法
来使用hello
:
import utils from './a.js';
const {hello} = utils;
// 或者
const hello = utils.hello;
为什么不能直接以「具名引入」的形式使用hello
呢:
// 不能这样
import {hello} from './a.js'
这是因为ESM
规范的导入声明都是静态的,而CJS
规范的导出是动态的,所以当ESM
模块引入CJS
模块时,在编译时是没法知道有哪些导出的。
这很符合规范,但看起来有点不符合直觉。
比如,React
只提供了CJS
规范的包,所以在ESM
模块中正确的引入方式是:
import React from 'react';
const { useState } = React;
而大家日常开发显然下面这种方式用的更多:
import { useState } from 'react';
之所以这么引入不会报错,是因为库作者(比如vite
、babel
)在编译时默默做了转换。
为了方便开发者而违背规范,这其实是个很不好的事(类似的事还有npm
、yarn
的影子依赖)。
但开发者喜闻乐见的API
就是好API
,整个web
的发展就是修修补补螺旋向上的。
bundle将长期存在
在vite
横空出世,带来极致的开发时速度后,社区就掀起一股「bundle vs bundleless」的讨论。
既然bundleless
能为开发环境带来提速,同样的优势能不能也带到生产环境?或者更极端点,未来前端会逐渐抛弃打包工具么?
从ESM
规范的角度出发,答案是否定的。有两个刚需现阶段bundleless
还无法解决:
tree shaking
ESM
模块过多,导致发起大量请求
所以,在未来很长一段时间内,打包工具仍会存在。
总结
在我的技术群中,经常看到新人前端发出感叹:「不知道该学啥」。
究其原因,当前的前端开发,主要是使用「集成了最佳实践的各种大型框架」(比如Nuxt
、Next.js
、UmiJS
...)。而这些封装完备的框架为了降低上手门槛,隐藏了大量技术细节。
如果你也有这种迷茫,我建议你从ESM
规范开始学起。
他就像一张地图,能够串联起前端的方方面面。
参考资料
import-maps: https://github.com/WICG/import-maps