0202年了,再谈谈使用TS改造react项目

1,638 阅读3分钟


搭建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实现异步下载机制