JavaScript 和 typeScript 中的 import、from

From:https://segmentfault.com/a/1190000018249137?utm_source=tag-newest

  • Github - allowSyntheticDefaultImports should be the default?
  • exports、module.exports和export、export deault到底是咋回事

JavaScript 中有多种 export 的方式,而 TypeScript 中针对这种情况做了多种 import 语法,最常见的就是 import * as path from 'path' 这种。下面来讲解 TypeScript 中不同的 import 具有什么意义。

1、从 export 说起

关于 TypeScript 中不同 import 的含义,最典型的就是下面的 import 语法:

import * as path from 'path'

不少人疑问这句代码究竟是什么意思,这里要先从 js 的 export 开始说。

首先,JavaScript 的模块化方案,在历史的演进中,有多种导出模块的方式:

  • exports
  • module.exports
  • export
  • export default

在 nodejs 中内置的模块遵循的都是 CommonJS 规范,语法为 module.exports 和 exports

// 模块中的 exports 变量指向 module.exports
// 这篇文章不会深入讲解 module.exports 和 exports 的关系module.exports = function () { }
exports.site = 'https://tasaid.com'
module.exports.name = 'linkFly'

例如 nodejs 内置的 events 模块的源码:

在 ECMAScript 6 中又新增了语法 export 和 export default:

export default function () { }
export const site = 'https://tasaid.com'
export const name = 'linkFly'

到这里画风还比较正常,而大名鼎鼎的 JavaScript 转码编译器 babel 针对 ECMAScript 6 新增的 export default 语法,搞了个 babel-plugin-transform-es2015-modules-commonjs 的转换插件,用于将 ECMAScript 6 转码为 CommonJs 规范的语法:

源码:export default 42;

编译后:

Object.defineProperty(exports, "__esModule", {value: true
});exports.default = 42;

到这里,我们看到有三种 export 默认值的语法:

module.exports = function () {}    // commonjs
exports.default = function () {}   // babel 转码
export default function () {}      // es6

2、JavaScript 中的 import

:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/import

静态的import 语句用于导入由另一个模块导出的绑定。无论是否声明了 strict mode ,导入的模块都运行在严格模式下。在浏览器中,import 语句只能在声明了 type="module" 的 script 的标签中使用。

此外,还有一个类似函数的动态 import(),它不需要依赖 type="module" 的script标签。

在 script 标签中使用 nomodule 属性,可以确保向后兼容。

在您希望按照一定的条件或者按需加载模块的时候,动态import() 是非常有用的。而静态型的 import 是初始化加载依赖项的最优选择,使用静态 import 更容易从代码静态分析工具和 tree shaking 中受益。

语法

import defaultExport from "module-name";
import * as name from "module-name";
import { export } from "module-name";
import { export as alias } from "module-name";
import { export1 , export2 } from "module-name";
import { foo , bar } from "module-name/path/to/specific/un-exported/file";
import { export1 , export2 as alias2 , [...] } from "module-name";
import defaultExport, { export [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";
import "module-name";var promise = import("module-name"); //这是一个处于第三阶段的提案。
  • defaultExport    导入模块的默认导出接口的引用名。
  • module-name    要导入的模块。通常是包含目标模块的.js文件的相对或绝对路径名,可以不包括.js扩展名。某些特定的打包工具可能允许或需要使用扩展或依赖文件,它会检查比对你的运行环境。只允许单引号和双引号的字符串。
  • name    导入模块对象整体的别名,在引用导入模块时,它将作为一个命名空间来使用。
  • export, exportN    被导入模块的导出接口的名称。
  • alias, aliasN    将引用指定的导入的名称。

2.1 描述

name 参数是 "导入模块对象" 的名称,它将用一种名称空间来引用导入模块的接口。export 参数指定单个的命名导出,而 import * as name 语法导入所有导出接口,即导入模块整体。

导入整个模块的内容 ( import * as myModule from 'xxx.js' )

这将 myModule 插入当前作用域,其中包含来自位于 /modules/my-module.js 文件中导出的所有接口。

import * as myModule from '/modules/my-module.js';

在这里,访问导出接口意味着使用模块名称(在本例为 "myModule" )作为命名空间。例如,如果上面导入的模块包含一个接口 doAllTheAmazingThings(),你可以这样调用:

myModule.doAllTheAmazingThings();

导入单个接口 ( import {myExport} from 'xxx.js'; )

给定一个名为 myExport 的对象或值,它已经从模块 my-module 导出(因为整个模块被导出)或显式地导出(使用 export 语句),将 myExport 插入当前作用域。

import {myExport} from '/modules/my-module.js';

导入多个接口 ( import {foo, bar} from 'xxx.js'; )

这将 foo 和 bar 插入当前作用域。

import {foo, bar} from '/modules/my-module.js';

导入带有别名的接口 ( import {x as y} from 'zzz.js'; )

可以在导入时重命名接口。例如,将 shortName 插入当前作用域。

import {reallyLongModuleExportName as shortName} from '/modules/my-module.js';

导入时重命名多个接口

使用别名导入模块的多个接口。

import {reallyReallyLongModuleMemberName as shortName,anotherLongModuleName as short
} from '/modules/my-module.js';

仅为副作用而导入一个模块 ( import 'xxx.js'; )

整个模块仅为副作用(中性词,无贬义含义)而导入,而不导入模块中的任何内容(接口)。 这将运行模块中的全局代码, 但实际上不导入任何值。

import '/modules/my-module.js';

导入默认值

引入模块可能有一个 default export(无论它是对象,函数,类等)可用。然后可以使用 import 语句来导入这样的默认接口。最简单的用法是直接导入默认值:

import myDefault from '/modules/my-module.js';

也可以同时将 default 语法与上述用法(命名空间导入或命名导入)一起使用。在这种情况下,default 导入必须首先声明。 例如:

import myDefault, * as myModule from '/modules/my-module.js';
// myModule used as a namespace

或者

import myDefault, {foo, bar} from '/modules/my-module.js';
// specific, named imports

当用动态导入的方式导入默认导出时,其工作方式有所不同。你需要从返回的对象中解构并重命名 "default" 键。

(async () => {if (somethingIsTrue) {const { default: myDefault, foo, bar } = await import('/modules/my-module.js');}
})();

动态 import

标准用法的 import 导入的模块是静态的,会使所有被导入的模块,在加载时就被编译(无法做到按需编译,降低首页加载速度)。有些场景中,你可能希望根据条件导入模块或者按需导入模块,这时你可以使用动态导入代替静态导入。下面的是你可能会需要动态导入的场景:

  • 当静态导入的模块很明显的降低了代码的加载速度且被使用的可能性很低,或者并不需要马上使用它。
  • 当静态导入的模块很明显的占用了大量系统内存且被使用的可能性很低。
  • 当被导入的模块,在加载时并不存在,需要异步获取
  • 当导入模块的说明符,需要动态构建。(静态导入只能使用静态说明符)
  • 当被导入的模块有副作用(这里说的副作用,可以理解为模块中会直接运行的代码),这些副作用只有在触发了某些条件才被需要时。(原则上来说,模块不能有副作用,但是很多时候,你无法控制你所依赖的模块的内容)

请不要滥用动态导入(只有在必要情况下采用)。静态框架能更好的初始化依赖,而且更有利于静态分析工具和 tree shaking 发挥作用

关键字 import 可以像调用函数一样来动态的导入模块。以这种方式调用,将返回一个 promise

import('/modules/my-module.js').then((module) => {// Do something with the module.});

这种使用方式也支持 await 关键字。

let module = await import('/modules/my-module.js');

2.2 示例

标准导入

下面的代码将会演示如何从辅助模块导入以协助处理 AJAX JSON 请求。

模块:file.js

function getJSON(url, callback) {let xhr = new XMLHttpRequest();xhr.onload = function () {callback(this.responseText)};xhr.open('GET', url, true);xhr.send();
}export function getUsefulContents(url, callback) {getJSON(url, data => callback(JSON.parse(data)));
}

主程序:main.js

import { getUsefulContents } from '/modules/file.js';getUsefulContents('http://www.example.com',data => { doSomethingUseful(data); });

动态导入

此示例展示了如何基于用户操作去加载功能模块到页面上,在例子中通过点击按钮,然后会调用模块内的函数。当然这不是能实现这个功能的唯一方式,import()函数也可以支持await

const main = document.querySelector("main");
for (const link of document.querySelectorAll("nav > a")) {link.addEventListener("click", e => {e.preventDefault();import('/modules/my-module.js').then(module => {module.loadPageInto(main);}).catch(err => {main.textContent = err.message;});});
}

2.3 规范

Specification
"function-like" dynamic import() proposal
ECMAScript (ECMA-262)Imports

Report problems with this compatibility data on GitHub

3、TypeScript 中的 import

在 TypeScript 中,也有多种 import 的方式。

import * as xx from 'xx'   // commonjs 模块
import xx from 'xx'        // es6 模块
import xx = require('xx')  // commonjs 模块,类型声明为 export = xx
const xx = require('xx')   // 没有类型声明,默认导入 any 类型

在 tsconfig.json 中,allowSyntheticDefaultImports 会影响到 import 语法的类型检查规则,这个下面再说。

import * as xx from 'xx'

import * as xx from 'xx' 的语法来一般都是用来导入使用 module.exports 导出的模块。

import * as path from 'path'

因为 nodejs 中的模块大部分都是通过 module.exportsexports.xx 语法进行导出的。

import xx from 'xx'

默认情况下,import xx from 'xx' 的语法只适用于 ECMAScript 6 的 export default 导出:

模块 foo:

export default function () { console.log('https://tasaid.com') 
}

ES6 模块的导入:

import foo from './foo'
foo()

而前面我们说了,babel 会将 es6 的模块的 export default 语法编译为 exports.default 语法。

而 TypeScript 默认是不识别这种语法的,如果一个模块的导出是 exports.default 导出,如果使用 import xx from 'xx' 的语法导入是会报错的。

所以在 tsconfig.json 中,有个 allowSyntheticDefaultImports 选项,就是针对这种语法做兼容。

如果设定 allowSyntheticDefaultImports 为 true,则检测导入的模块是否是 ES6 模块,如果不是,则查找模块中是否有 exports.default 导出。

从而达到针对 exports.default 的兼容。

 

效果参见这个动画:

allowSyntheticDefaultImports 选项的,一般情况下我采取的方式是将 deafult 重新命名:

import { default as foo } from 'foo'

import xx = require('xx')

import xx = require('xx') 是用来导入 commonjs 模块的库,特殊的地方在于这个库的类型声明是 export = xx 这种方式导出的:

foo.js 源码:

module.exports = () => { console.log('https://tasaid.com') 
}

foo.d.ts 类型声明文件源码:

declare function foo(): void;
export = foo

bar.ts 引用:

import foo = require('./foo')foo()

在 《[JavaScript 和 TypeScript 交叉口 —— 类型定义文件(*.d.ts)
](https://tasaid.com/blog/20171...》中讲述过 TypeScript 类型声明文件对导入导出的影响 

const xx = require('xx')

当一个模块没有类型声明文件的时候,可以使用 commonjs 原始的 require() 方式来导入模块,这样会默认该模块为 any。

总  结

最后我们整体总结下,在 TypeScript 中,有多种 import 的方式,分别对应了 JavaScript 中不同的 export。

import * as xx from 'xx'     // commonjs 模块
import xx from 'xx'          // 标准 es6 模块
import xx = require('xx')    // commonjs 模块,类型声明为 export = xx
const xx = require('xx')     // 没有类型声明,默认导入 any 类型

针对 babel 编译出来的 exports.default 语法,ts 提供了 allowSyntheticDefaultImports 选项可以支持,只不过个人不太推荐。

个人建议将 default 重命名。

import { default as foo } from 'foo'

关于 TypeScript 中类型声明文件(*.d.ts) 对 import 和 export 的影响,参考 《[JavaScript 和 TypeScript 交叉口 --- 类型定义文件](https://tasaid.com/blog/20171...》。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/495106.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度丨建立合资公司,深度参与运营:详解景驰的无人驾驶生意经

来源:亿欧摘要: 对于景驰科技而言,最终实现商业价值的做法绝对不仅仅是把改装好的无人车卖出高价。在该公司看来,切入无人驾驶需求最旺盛的出租车市场,与出行服务公司、主机厂乃至政府产业基金成立合资公司&#xff0c…

使用SQL Server存储ASP.NET Session变量

创建和配置ASP.NET Session状态数据库在基于NLB(网络负载平衡)环境下的ASP.NET Web应用程序开发,我们需要将Session存储在数据库中供多个Web应用程序调用,以下为配置方法及注意事项。1.创建用于存储ASP.NET Session的数据库&#…

学界 | DeepMind想用IQ题测试AI的抽象思维能力,进展还不错

来源:大数据文摘摘要:抽象理解能力一直是人类引以为豪的智慧来源。阿基米德基于对物体体积的抽象理解,悟到了物体的体积与物体浮力之间的关系。这就是抽象推理的魔力。基于神经网络的机器学习模型取得了惊人的成绩,但是测量其推理…

frida hook so层、protobuf 数据解析

手机安装 app ,设置代理,然后开始抓包。 发现数据没法解密,查看请求的 url 是 http://lbs.jt.sh.cn:8082/app/rls/monitor,使用 jadx 反编译 app 后搜索这个 url(提示:可以只搜索 url 中一部分,…

大数据技术与应用解读及案例分析(PPT)

来源:网络大数据摘要:大数据是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。大数据就是未来的石油。未来智能实验室是人工智能学家与科学院相关机构联合成立的人工智能,互联网和脑科学…

pywin32库 : Python 操作 windows 系统 API

Python 模块虽多,但也不可能满足所有需求。而且,模块为了便于使用,通常都封装过度,有些功能无法灵活使用,必须直接调用Windows API 来实现。要完成这一目标,有两种办法,一种是使用 C 编写 Pytho…

华为秘密作战计划曝光,重注研发AI芯片挑战英伟达,轮值董事长挂帅

雷刚 问耕 发自 凹非寺 量子位 报道 | 公众号 QbitAI摘要:据报道,对于如何把AI引入所有业务,华为内部已经开启了代号为“达芬奇”的作战计划,并且成为华为高层每月一次讨论会的重点议题,也有不少华为高管以“D项目”来…

Python --- ctypes库的使用

ctypes 的官方文档 英文文档:https://docs.python.org/3/library/ctypes.html中文文档:https://docs.python.org/zh-cn/3.10/library/ctypes.html Python--ctypes(数据类型详细踩坑指南):https://zhuanlan.zhihu.com/p/145165873…

机器学习近年来之怪现象

来源:网络大数据人工智能领域的发展离不开学者们的贡献,然而随着研究的进步,越来越多的论文出现了「标题党」、「占坑」、「注水」等现象,暴增的顶会论文接收数量似乎并没有带来更多技术突破。最近,来自卡耐基梅隆大学…

PySide6 : Qt for Python 教程

Qt for Python 教程:https://doc.qt.io/qtforpython/tutorials/index.html 官方文档示例:https://doc.qt.io/qtforpython/examples/index.html Qt for Python 提供了一系列带有演练指南的教程,以帮助新用户入门。 其中一些文档是从 C 移植到…

PySide6 信号、槽

信号与槽的入门应用(一):https://blog.csdn.net/jia666666/article/details/81774175 信号与槽的再细分(二):https://blog.csdn.net/jia666666/article/details/81774858 信号与槽的高级玩法(三…

芯片业又起波澜!博通189亿美元收购遭质疑,股价一度跌近19%

来源:华尔街见闻摘要:如果说博通上次收购高通面临的最大“对手”是特朗普政府,这次收购CA的“对手”可能是华尔街。如果说博通上次收购高通面临的最大“对手”是特朗普政府,这次收购CA的“对手”可能是华尔街。美东时间周三&#…

JavaScript常用技巧专题五

文章目录 一、使用适当的命名和注释来提高代码可读性二、优雅的写条件判断代码2.1、普通的if else2.2、三元运算符2.3、多个if else2.4、switch case2.5、对象写法2.6、Map写法 三、封装条件语句四、函数应该只做一件事五、Object.assign给默认对象赋默认值六、函数参数两个以下…

HTML 页面的生命周期、HTML 事件

From:https://blog.csdn.net/WuLex/article/details/101016936 1、页面生命周期 HTML页面的生命周期有以下三个重要事件,每个事件都有特定的用途 DOMContentLoaded : 浏览器已经完全加载 HTML,DOM 树已经构建完毕,js …

腾讯再次海选AI项目,1500进40,医疗零售机器人成新风向

雷刚 发自 凹非寺 量子位 报道 | 公众号 QbitAI一年一度风向标,腾讯又一次海选AI项目。去年第一期腾讯AI加速器结业后,鹅厂就马不停蹄开启了第二期报名,而且这一次报名企业更多、竞争更激烈、最终录取概率甚至不到3%。第二期AI加速器通过初试…

The Human Touch 将人工智能和机器人用于病人工作的实际和伦理意义

来源:IEEE电气电子工程师学会摘要:我们生活在一个科幻小说可以很快成为科学事实的时代。在一代人的时间里,互联网已经从技术奇迹变成了实用工具,移动电话重新定义了我们的交流方式。我们生活在一个科幻小说可以很快成为科学事实的…

渗透测试 ( 0 ) --- XSS、CSRF、文件上传、文件包含、反序列化漏洞

漏洞数据库:https://www.exploit-db.com/google-hacking-database 1、渗透测试 实用 浏览器插件 chrome、edge 插件:搜索 cookie,安装 cookie editor,打开插件,可以 导出 cookie HackBar :Hackbar是网络安…

专访盛大创始人陈天桥:未来的杀手级应用必将诞生于脑科学

翻译丨于波 校对丨其奇来源丨Medium 神经科技初见陈天桥,他穿着带有花纹的短袖衬衫,休闲款式的蓝色牛仔裤,迷彩色的运动鞋,仿佛是个享受退休生活的人。过去的他可不是这样。1999年,陈天桥创建盛大游戏公司&#xff0c…

动手解决jar转txt软件的一个缺陷

代码: import java.io.BufferedOutputStream;import java.io.BufferedReader;import java.io.DataOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStreamReade…

渗透测试 ( 2 ) --- 渗透测试系统、靶机、GoogleHacking、kali工具

操作系统:https://zhuanlan.zhihu.com/p/162865015 1、基于 Windows、Linux、Android 的渗透测试系统 1.1 基于 Linux 的系统 Kali:https://www.kali.org/get-kali/Parrot Security OS:Parrot Securitybackbox:https://www.backbo…