谈谈对js中import 和export的理解

概述:

今天读antd源码读到了components/index.tsx文件,看见里面大量的使用export type ** from ‘./’ 和 export ** from ‘./’, image.png
谈谈对js中import 和export的理解_第1张图片

感觉和以前的export 的用法不太一样

image.png


那么真实的export改怎么用:

在创建 JavaScript 模块时,export 语句用于从模块中导出实时绑定的函数、对象或原始值,以便其他程序可以通过 import 语句使用它们。被导出的绑定值依然可以在本地进行修改。在使用 import 进行导入时,这些绑定值只能被导入模块所读取,但在 export 导出模块中对这些绑定值进行修改,所修改的值也会实时地更新。

语法

存在两种 exports 导出方式:
1、命名导出(每个模块包含任意数量)

// 导出单个特性
export let name1, name2,, nameN; // also var, const
export let name1 =, name2 =,, nameN; // also var, const
export function FunctionName(){...}
export class ClassName {...}

// 导出列表
export { name1, name2,, nameN };

// 重命名导出
export { variable1 as name1, variable2 as name2,, nameN };

// 解构导出并重命名
export const { name1, name2: bar } = o;

// 默认导出
export default expression;
export default function () {} // also class, function*
export default function name1() {} // also class, function*
export { name1 as default,};

// 导出模块合集
export * from …; // does not set the default export
export * as name1 from …; // Draft ECMAScript® 2O21
export { name1, name2,, nameN } from …;
export { import1 as name1, import2 as name2,, nameN } from …;
export { default } from …;


2、默认导出(每个模块包含一个)

// 导出事先定义的特性
export { myFunction, myVariable };

// 导出单个特性(可以导出 var,let,
//const,function,class)
export let myVariable = Math.sqrt(2);
export function myFunction() { ... };


export from又是什么?:

为了使模块导入变得可用,在一个父模块中“导入/导出”这些不同模块也是可行的。也就是说,你可以创建单个模块,集中多个模块的多个导出。

export { default as function1,
         function2 } from 'bar.js';

可以转换成常规写法

import { default as function1,
         function2 } from 'bar.js';
export { function1, function2 };

在导入默认导出的文件时需要注意

import DefaultExport from 'bar.js'; // 有效的

export DefaultExport from 'bar.js'; // 无效的

export { default as DefaultExport } from 'bar.js'; 有效



再说说export type吧:

通过前面的了解,export type可以写成如下

export type { AffixProps } from './affix';

转换
import type { AffixProps } from './affix';

export AffixProps

为什么要使用import type呢?直接使用import 不香吗

首先说tsc的编译, 在我们日常写代码的过程中, 通常会使用import去导入一些类型或者值, 比如下面的写法:

// ./foo.ts
interface Options {
  // ...
}
export function doThing(options: Options) {
  // ...
}
// ./bar.ts
import { doThing, Options } from "./foo.js";

function doThingBetter(options: Options) {
  // do something twice as good
  doThing(options);
  doThing(options);
}

上面的代码中, doThing是作为一个值被导入, Options作为一个类型被导入, 这样同时导入其实很方便, 因为我们不用担心我们导入的是什么, 只需要知道我要用它, import就完事了, 哪怕我同时import的是一个类型和一个实际值也没有关系.

但我们能够同时import一个值和一个类型, 是因为一个叫**import elision**(导入省略)的功能在起作用.

Typescript进行代码编译时, 发现Options是作为一个类型被导入的, 就会自动在生成的JS代码中删除掉它的导入, 所以最终生成的是类似于(类似, 用于解释说明, 但可能非实际输出代码)下面的JS代码:

// ./foo.js
export function doThing(options: Options) {
  // ...
}

// ./bar.js
import { doThing } from "./foo.js";

function doThingBetter(options) {
  // do something twice as good
  doThing(options);
  doThing(options);
}

可见, 所有跟类型相关的代码, 都在最终编译生成的文件里被删除了, 所以我们直接通过importexport来导入/导出值和类型的写法是很方便的, 但是这么写, 也会存在一些问题.

问题1: 容易产生一些模棱两可的语句

利用3.8 release notes中例子来做说明, 在某些情况下, 可能会出现一些比较模棱两可的代码:

// ./some-module.ts
export interface MyThing {
  name: string;
}

// ./src.ts
import { MyThing } from "./some-module.ts";

export { MyThing };
复制代码

比如上面的例子, 如果把我们的分析仅仅限定在这个文件里, 仅凭文件里的这两行代码, 我们是难以分析出MyThing到底是应该是一个值, 还是一个类型的.

如果MyThing仅仅是作为一个类型而存在, 那么使用Babel或者ts.transpileModuleAPI这样的工具最终编译出的javascript代码是不能够正确的运行的, 编译生成的代码如下:

// ./some-module.js
----empty----

// ./src.js
Object.defineProperty(exports, "MyThing", {
  enumerable: true,
  get: function () {
    return _test.MyThing;
  }
});

var _test = require("./test1");
复制代码

最终生成的src.js文件中对MyThing的导入和导出都会被保留, 而在some-module.js文件中, MyThing仅作为一个类型而存在, 会在编译过程中被删除, 所以最终some-module.js文件是空的(可能会存在别的编译过程中的代码, 但是MyThing的定义会被删除), 这样的代码会在运行时产生报错.

问题2 导入省略将删除引入副作用的代码

Typescriptimport elision功能会删除掉仅作为类型使用的import导入, 这在导入某些具有副作用的模块时, 会造成特别明显的影响, 让使用者不得不再写一个额外的语句去专门导入副作用:

// 因为import elision的功能, 这行代码在最终生成的js代码中会被删除
import { SomeTypeFoo, SomeOtherTypeBar } from "./module-with-side-effects";

// 这行代码要一直存在
import "./module-with-side-effects";
复制代码

为什么要引入import type

基于上面的问题, Typescript 3.8中引入了import type, 希望能够用一种更加清晰易懂的方式来控制某一个导入是否要被删除掉.

import { MyThing } from "./some-module.ts";

export { MyThing };
复制代码

上面的例子就是我们在问题1中介绍过的, 像Babel这样的编译工具是不能够准确的识别MyThing到底是一个值还是类型的, 因为Babel在编译的过程中, 一次只会处理一个文件.

所以这种时候, 我们就需要一种方式, 来准确的告诉正在编译这个文件的编译工具, 现在使用import typeexport type导入和导出的MyThing就是一个类型, 你完全可以在编译的时候把这段代码省略(删除)掉.

import type { MyThing } from "./some-module.ts";

export type MyThing;
复制代码

使用import typeexport type导入和导出的类型只能在类型上下文中使用, 不能作为一个值来使用.

总结

最后关于import type部分我主要是引用于https://juejin.cn/post/7111203210542448671

你可能感兴趣的:(javascript,前端,vue.js)