【HarmonyOS NEXT】ArkTs 模块的导出和引入

1. 前置学习文档

2.前言

什么是模块?
模块可以理解为函数代码块的功能,是封装对象的属性和方法的javascript代码,它可以是某单个文件,变量或者函数

  历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,这对开发大型的、复杂的项目形成了巨大障碍。
  在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD/CMD 三种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD/CMD 规范,成为浏览器和服务器通用的模块解决方案。
  ES6之后,官方制定了模块规范【ES6 Module】 —— ES6标准规范中,以export指令导出接口,以import引入模块,ArkTS中的模块化也是基于此规范来实现的

关于更多关于前端模块化信息可以看这篇文章:https://mp.weixin.qq.com/s/15sedEuUVTsgyUm1lswrKA

3. export命令

关于export命令的详细文档介绍:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/export

3.1 导出单个[类/函数/变量]

下面是一个ets[arkts]文件,里面使用export命令来导出变量,函数或类(class)

//A.ets

//导出变量
export let str = "export的内容";

//导出函数
export function add(x: number, y: number) {
  return x + y;
}

//导出类
export class Test {
  printHello() {
    console.log("你好 世界")
  }
}

3.2 导出列表[推荐写法]

除了上述的写法,还有一种推荐使用的,因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。

//A.ets

let str = "export的内容";

function add(x: number, y: number) {
  return x + y;
}

class Test {
  printHello() {
    console.log("你好 世界")
  }
}

//导出变量、函数、类
export {str,add,Test}

3.3 导出别名/重命名

通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。

//A.ets

let str = "export的内容";

function add(x: number, y: number) {
  return x + y;
}

class _Test {
  printHello() {
    console.log("你好 世界")
  }
}
let test=new Test()
//导出变量、函数、类,并重命名
export {
  str as Str, add as Add, Test as TT
}

4. import命令

关于import命令的详细文档介绍:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/import

使用export命令定义了模块的对外接口以后,其他 【ets/ts/js】 文件就可以通过import命令加载这个模块。
📢 单例模式:import 模块的代码只会执行一次,同一个url文件只会第一次导入时执行代码。后续任何地方import都不会执行模块代码了,也就是说,import语句是 Singleton 模式的。
📢 只读-共享:模块导入的接口的是只读的,不能修改。当然引用对象的属性值是可以修改的,不建议这么干,注意模块是共享的,导出的是一个引用,修改后其他方也会生效。

4.1 引入导出的内容

  使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。
下面代码的import命令,用于加载A.ets文件,引入后便可以在B.ets文件中使用A.ets文件中的变量、函数或类等。import命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(A.ets)对外接口的名称相同。

//A.ets
let str = "export的内容";

function add(x: number, y: number) {
  return x + y;
}

class Test {
  printHello() {
    console.log("你好 世界")
  }
}
//导出变量、函数、类,并重命名
export {
  str as Str, add as Add, Test as TT
}

//B.ets
import { Str, Add, TT } from './A';

class Test {
  print() {
    let string = Str //使用导出的变量
    let number = Add(0, 1) //使用导出的方法
    let class = new TT() //使用导出的类
  }
}

//也可以分开写两次,导入的时候带花括号,将每个变量函数名写清楚
import { Str } from './A';
import { Add} from './A';
import { TT } from './A';

//但是不能重复导入相同的,例如下面的例子
import { TT } from './A';
import { TT } from './A';

4.2 引入别名/重命名

如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名。

//A.ets
let str = "export的内容";

function add(x: number, y: number) {
  return x + y;
}

class Test {
  printHello() {
    console.log("你好 世界")
  }
}
//导出变量、函数、类,并重命名
export {
  str as Str, add as Add, Test as TT
}

//B.ets
import { Str as Str2, Add as Add2, TT as TT2 } from './A';

class Test {
  print() {
    let string = Str2 //使用导出的变量
    let number = Add2(0, 1) //使用导出的方法
    let class = new TT2() //使用导出的类
  }
}

4.3 批量引入

有的时候我们发现如果一个模块里面有多个导出的[类/函数/变量],使用的时候发现需要一个一个声明,比较繁琐,此时就可以使用 import * as name 语法,该语法可以导入所有导出接口,即导入模块整体

建议按需导出,不用,这样有利于构建工具的优化,剔除没用到的代码。*

//B.ets
import  * as moduleA  from './A';

class Test {
  print() {
    let zoo = new moduleA.Zoo()//使用非默认导出的类
    zoo.printZoo()

    let string = moduleA.Str //使用非默认导出的变量
    let number = moduleA.Add(0, 1) //使用非默认导出的方法
    let tt2 = new moduleA.TT() //使用非默认导出的类
  }
}

5. export default 命令

export default命令用于指定模块的默认输出,一个模块只能有一个默认输出,因此export default命令只能使用一次。
所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令。一个文件内不能有多个export default,另外,import的时候, 还可以起任何变量名表示引入变量

5.1 导出默认变量

//A.ets
export default "export的内容"
//或者可以声明一个变量,然后在导出这个变量
const xxx="export的内容"
export default xxx

//B.ets
import str from './A';

class Test {
  print() {
    let string = str //使用导出的变量
  }
}

5.2 导出默认函数

//A.ets
export default function() {
  console.log('我是默认函数');
}
//或者还可以声明一个函数,然后导出这个函数 【推荐写法】
let add= (x: number, y: number) => {
  return x + y;
}

export default add

//或者还可以这样写
function add(x: number, y: number) {
  return x + y;
}
//等同于 export default add;
export {add as default};

//B.ets
import func from './A';

class Test {
  print() {
    let string = func(0, 1)//使用导出的方法
  }
}

5.3 导出默认类

//A.ets
export default class Test {
  printHello() {
    console.log("你好 世界")
  }
}

//或者还可以声明一个类,然后导出这个类
class Test {
  printHello() {
    console.log("你好 世界")
  }
}

export default Test


//B.ets
import Classics from './A';

class Test {
  print() {
    let classics = new Classics()
    classics.printHello()
  }
}

5.4 导出默认模块+非默认模块

//A.ets
let str = "export的内容";

function add(x: number, y: number) {
  return x + y;
}

class Test {
  printHello() {
    console.log("你好 世界")
  }
}

export class Zoo {
  printZoo() {
    console.log("你好 大象")
  }
}

export {
  str as Str, add as Add, Test as TT
}

export default Test

//B.ets
import Classics, { Str as Str2, Add as Add2, TT as TT2, Zoo } from './A';


class Test {
  print() {
    let classics = new Classics()//使用默认导出的类
    classics.printHello()
  }
}

5.5 批量导出

  这种场景在使用ArkTS中的hsp模块时非常有用。可能被多个业务module使用的har库,可以使用hsp模块进行管理,然后使用hsp进行批量导出har对外暴露的模块.

//A.ets

let str = "export的内容";

function add(x: number, y: number) {
  return x + y;
}

class Test {
  printHello() {
    console.log("你好 世界")
  }
}

export class Zoo {
  printZoo() {
    console.log("你好 大象")
  }
}

export {
  str as Str, add as Add, Test as TT
}

export default Test


//B.ets
export * from './A';

//C.ets
import * as ModuleA  from './B';

class Test {
  print() {
    let zoo = new ModuleA.Zoo()//使用非默认导出的类
    zoo.printZoo()

    let string = ModuleA.Str //使用非默认导出的变量
    let number = ModuleA.Add(0, 1) //使用非默认导出的方法
    let tt2 = new ModuleA.TT() //使用非默认导出的类
  }
}

6. 一些常见模块化导出和引入的写法

6.1 写法1 [默认写法]

//Person.ets
class _Person {
  age: number

  constructor() {
    this.age = 18
  }

  getAge(): number {
    return this.age
  }
}

const Person = new _Person();

export default Person;

import Person from './Person.ets'
Person.age
Person.getAge()

6.2 写法2[默认写法]

//Person.ts
export default {
  age: 0,
  getAge(): number {
    return this.age
  }
};

import Person from './Person.ts'
Person.age
Person.getAge()

6.3 写法3 [别名写法]

//Person.ets
export let age = 0;
export function getAge(): number {
  return age
}

import * as Person from './Person.ets'

Person.age
Person.getAge()

6.4 写法4 [分组批量导出写法]

这个方法的好处是不用再维护library的index.ets ,只需要维护要导出模块文件夹下的index.ets 即可

//【1】批量导出login 模块下的所有方法

// 1.1、src/main/ets/login/Login.ets
export class Login
{

}

export class PwdLogin
{

}

//1.2、src/main/ets/login/index.ets
export  * from './Login'


//【2】批量导出login 模块下的所有方法

// 2.1、src/main/ets/login/Login.ets
export class Register
{

}

export class RegisterPwd
{

}

//2.2、src/main/ets/login/index.ets
export  * from './Register'


//【3】批量导出所有模块下的所有方法 

//3.1 src/index.ets
export * from './src/main/ets/login'



7. 疑问

7.1 为什么export default 只能有一个

  • 因为export default本质上是别名【as】的语法糖
    本质上,下面的,A.ets 文件的export default输出一个叫做default的变量,然后系统允许你为它取任意名字。 自然default只能有一个值,所以一个文件内不能有多个export default。
// A.ets
// 导出函数
export default function() {}

// 等效于:
function a() {};
export {a as default};

//B.ets
import a from './A'; // a 是 {defalut as a}的替代写法。


//另外一个例子
//A.ets

let a = 1;
let b = 2;
export { a };
export default b;

//B.ets
import * as all from "./A";

console.log(JSONUtils.bean2Json(all)) //输出 {"a":1,"default":2}

7.2 导出new Class(),内存引用是否是一个?

  • 因为ES6模块输出的是值的引用,所以如果export new class,则多个位置引用的其实是相同的class 实例
//MyClass.ets
class MyClass {
    count: number
    constructor() {
        this.count = 0;
    }
    increment():number {
        this.count++;
        return this.count
    }
}
// 导出类的对象
export const obj = new MyClass();

//Test.est
import { obj as objA } from './TestClass' new MyClass();
import { obj as objB } from './TestClass' new MyClass();
console.log(objA.increment() + "")
console.log(objB.increment() + "")

因为export 输出的是值的引用,所以此处输出的是
1
2
而不是
1
1
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,192评论 6 511
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,858评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,517评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,148评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,162评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,905评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,537评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,439评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,956评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,083评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,218评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,899评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,565评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,093评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,201评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,539评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,215评论 2 358

推荐阅读更多精彩内容