搭建ts+react的常见场景
- 新项目---使用
creat-react-app创建---脚手架编译ts使用的是babel - 新项目---使用
ts-loader创建---脚手架编译ts使用的是ts-loader - 老项目---自行选择loader,修改.js为.ts。 ...)此处不展开
建议优先选择使用ts-loader,配置更简单
导入模块该四种写法的异同
const path = require('path')
这种写法在ts中是非常不建议的,因为这种 commonjs 写法导出来的对象是 any,没有类型支持
import * as path from 'path' / import { resolve } from 'path'
这种写法基本比较常见,都是对整个模块的导出,
有一点需要注意的是 ts.config中的一个配置 "esModuleInterop":true对此处编译的影响
有test.ts 文件:
import * as path from 'path'
console.log(path);
未配置esModuleInterop的情况 编译后的 test.js 文件:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const path = require("path");
console.log(path);
配置esModuleInterop:true的情况 编译后的 test.js 文件:
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const path = __importStar(require("path"));
console.log(path);
增加这个配置的目的是 为了防止如果你用 import 导入的项目内的其他源文件,由于原先 commonjs 写法,会提示你文件“/path/to/project/src/mod.ts”不是模块。ts(2306),此时,需要将被导入的模块修改为 ES6 的 export 写法
import mod from 'mod'
这个语法是导出默认值,要特别注意
test.ts文件如下
import path from 'path'
console.log(path)
编译后的 test.js 文件:
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = __importDefault(require("path"));
console.log(path_1.default);
可以看到针对 import mod 这种写法,在编译成 commonjs 后包裹了一个
__importDefault工具函数,其作用是:如果导入模块__esModule为 true,则直接返回module.exports。否则返回
{default:module.exports}。这个是针对没有默认导出的模块的一种兼容,fs 模块是 commonjs,并没有__esModule属性,使用modules.exports导出。上述代码中的path_1实际是{default:module.exports},因此path_1.default指向的是原 path 模块,可以看出转换是正常的。但这种方式是有个
陷阱,举个例子,如果有第三方模块,其文件是用 babel 或者也是 ts 转换过的,那其模块代码很有可能包含了 __esModule 属性,但同时没有exports.default导出,此时就会出现 mod.default 指向的是undefined。更要命的是,IDE和编译器没有任何报错。如果这个最基本的类型检查都解决不了,那我要 TypeScript 何用?
所幸,tsconfig 提供了一个配置
allowSyntheticDefaultImports,意思是允许从没有设置默认导出的模块中默认导入,需要注意的是,这个属性并不会对代码的生成有任何影响,仅仅是给出提示。另外,在配置"module": "commonjs"时,其值是和esModuleInterop同步的,也就是说我们前面设置了"esModuleInterop":true,相当于同时设置了"allowSyntheticDefaultImports":true。这个允许也就是不会提示。
手动修改
"allowSyntheticDefaultImports":false后,会发现 ts 文件中import path from 'path'处出现提示模块“"path"”没有默认导出。ts(1192),通过这个提示,我们将其修改为import * as path from path,可以有效避免上述陷阱。
举个栗子🌰
以目前正在做的透镜系统为例发现其
1 所有模块引入只能使用import * as path from 'path' / import { resolve } from 'path'写法,无法使用es6模块形式导入
通过增加 esModuleInterop":true 可解决
2 不支持懒加载
1修改ts.config 配置 "module": "esnext",
2升级typescript版本,目前 typescript": "^3.4.5",实测升级到3.74可解决
下面简要分析一下懒加载问题
react中提供了 React.lazy实现懒加载最终编译成 import()动态引入,这只有es6模块支持,
常用的是 require.ensure实现懒加载(如今此语法在新版webpack已不推荐使用),实际上是依赖的webpack提供的支持commojs的语法。
所以react中使用 React.lazy实现懒加载 需要配置"module": "esnext"
下一期谈谈webpack打包后代码优化,cmd规范+jsonp实现异步下载机制