1.esModuleInterop 到底做了什么?
2.苯苯的嗷呜-CyberRt 源码解读(十五)
esModuleInterop 到底做了什么?
很多 React 开发者在从 JavaScript迁移到 TypeScript(TS)时,会遇到一个关于导入问题的困惑。在 JavaScript中,引入React模块通常是这样的:
然而,在 TypeScript中,引入方式却变成了这样:
当尝试在TypeScript中模仿JavaScript的服饰APP源码导入方式时,编辑器会报错,指出该模块是由 "export =" 声明的,仅在启用 "esModuleInterop" 标志时与默认导入一起使用。要解决这个问题,需要在 `tsconfig.json` 文件中设置 `compilerOptions.esModuleInterop` 为 `true`。
理解这一问题的关键在于了解JavaScript的模块系统。常用的JavaScript模块系统有三种,其中AMD模块系统已经较为少见,期货指标源码详解故略过。在TypeScript和Babel编译器中,更倾向于使用CommonJS(CJS)模块。默认情况下,代码中表示的ES模块(ESM)都会被转换为CJS模块。
回到开头的问题,打开React库的`index.js`文件,可以发现React基于CJS模块,等效于:
而`index.ts`文件中,写入一段代码:
编译后的代码为:
因此,打印结果为`undefined`,因为`react`模块的`module.exports`中没有`default`属性,后续获取`React.createElement`和`React.Component`等函数时自然会报错。操盘黑马线源码
这一问题引申出的是,大量现有的第三方库大多使用UMD或CJS模块,而前端代码几乎都是使用ESM模块。因此,ESM和CJS模块之间需要一套规则来实现兼容。
在TypeScript中,默认的导入转换规则为:
而对于`export`变量的转换规则为:
在启用`esModuleInterop`属性后,TypeScript对于导入的转换规则发生了变化(`export`规则保持不变):
这里,对于默认导入和命名空间(`*`)导入,TypeScript使用了两个辅助函数来协助转换。
首先,`__importDefault`函数做的事情是:
比如上面的导入语句,编译后再层层翻译:
这样就成功获取了`react`模块的macd线指标源码`module.exports`。
接下来是`__importStar`函数,它做的事情是:
(对`__importStar`的层层翻译分析过程省略)
在默认情况下,Babel的转换规则与启用`esModuleInterop`的TypeScript情况相似,同样通过两个辅助函数来处理。
关于`_interopRequireDefault`和`_interopRequireWildcard`函数,它们分别类似`__importDefault`和`__importStar`。
在特殊的Webpack环境中,通常情况下,Babel和TypeScript会一起使用Webpack。而Webpack与TypeScript的结合有两种方式:
如果使用`ts-loader`,Webpack会先将源代码交给TypeScript编译器(tsc)进行编译,然后处理编译后的代码。编译后的cys指标源码大全所有模块都会变为CJS模块,因此Babel不会进行处理,直接交给Webpack以CJS方式处理模块。
如果使用`@babel/preset-typescript`,Webpack不会调用tsc,忽略`tsconfig.json`配置,而是直接使用Babel编译TS文件。这个编译过程相比调用tsc轻得多,因为Babel只会简单移除所有TS相关代码,不做类型检查。在这种情况下,一个TS模块通过Babel的`@babel/preset-env`和`@babel/preset-typescript`两个预设处理。后者的工作很简单,仅去除所有TS相关代码,不处理模块,前者则将ESM转换为CJS。然而,Webpack的`babel-loader`在调用`babel.transform`时,传入了`caller`选项:
这导致Babel保留了ESM的`import`和`export`语法。
Webpack为模块提供了一个runtime机制,使得Webpack在模块闭包中注入代表`module require`和`exports`的变量,因此Webpack处理模块对于自身而言较为自由。
在CJS引用ESM的场景中,Webpack的编译机制较为特别,通过`_webpack_require__`类似于`require`,返回目标模块的`module.exports`对象。`_webpack_require__.n`函数接收一个参数对象,返回一个对象,该返回对象的`a`属性(其确切名称未知)会被设定为参数对象。因此,上述源代码的`console.log(cjs)`会打印出`cjs.js`的`module.exports`。
总结:当前许多常用的包基于CJS/UMD开发,而前端代码主要使用ESM,常见场景是ESM导入CJS库。由于ESM和CJS在概念上存在差异,最大的差异在于ESM有`default`概念而CJS没有,因此在`default`上会遇到问题。TypeScript、Babel、Webpack都有各自的处理机制来解决这个兼容问题,核心思想基本都是通过添加和读取`default`属性来实现。
苯苯的嗷呜-CyberRt 源码解读(十五)
本文将深入解读CyberRt源码中的环境相关文件。首先回顾上一篇文章中对common/log部分的解读,欲知详情请自行点击链接查阅。接下来,让我们聚焦于environment.h文件,其主要内容涵盖了环境相关的核心定义与接口。环境是程序运行的基础,其定义与配置直接影响了程序的行为与性能。
接着,我们转向file.h/cc文件的分析。file头文件通常仅包含函数声明,而其cc源文件则提供了实际的实现逻辑。尽管file头文件中的声明简洁明了,但在cc文件中,我们能发现这些声明所对应的具体函数实现,以及它们在程序中如何与环境、数据交互等关键细节。文件操作是程序中常见的需求,深入理解这些实现细节对于优化性能与确保程序的正确性至关重要。
综上所述,通过本篇文章的解读,我们对环境与文件操作的核心逻辑有了更深入的理解。环境的配置与管理、文件的读写与操作,都是构建可靠、高效程序的基础。希望这些内容能够对您理解与使用CyberRt源码有所帮助,也期待您的持续关注与深入探索。