1. Module 模塊化
JavaScript 采用'共享一切'的方式加載代碼, 也就是在ES6 之前, JavaScript中定義的一切都共享同一個(gè)全局作用域, 這樣隨著程序復(fù)雜度提升的同時(shí),也會(huì)帶來(lái)命名沖突等諸多問(wèn)題. 因此ES6的一個(gè)目標(biāo)就是解決作用域問(wèn)題. 也為了使JavaScript 應(yīng)用程序顯得有序, 于是引進(jìn)了模塊
1.1 模塊功能
模塊功能主要由兩個(gè)命令構(gòu)成:export和import湾戳。
export命令用于規(guī)定模塊的對(duì)外接口。
import命令用于輸入其他模塊提供的功能。
一個(gè)模塊就是一個(gè)獨(dú)立的文件荧恍。該文件內(nèi)部的所有變量奸汇,外部無(wú)法獲取沥寥。如果你希望外部能夠讀取模塊內(nèi)部的某個(gè)變量卸察,就必須使用export關(guān)鍵字輸出該變量她倘。
2. 模塊的使用
注意如果需要測(cè)試模塊化需要放到服務(wù)器環(huán)境
<!-- module模塊化 import導(dǎo)入 -->
<script src="02.js"></script> <!-- 這里只是引入了腳本 -->
<!-- 在瀏覽器上運(yùn)行的基本上是前端模塊化
在服務(wù)器上運(yùn)行的基本是后端模塊化
后臺(tái)是給內(nèi)部員工使用璧微,用來(lái)添加數(shù)據(jù)的。前臺(tái)給用戶看的帝牡。都是前端
-->
<script type="module">
// module表示模塊化往毡。注意:模塊化只能在服務(wù)器環(huán)境運(yùn)行。服務(wù)器環(huán)境有端口號(hào)靶溜,IP地址开瞭,協(xié)議
//js的type類型通常是javascript,所以省略罩息,里面就直接寫腳本
import './01.js' // 這樣導(dǎo)入js嗤详,只是執(zhí)行了一遍該js文件,拿不到任何數(shù)據(jù)瓷炮,不能使用該js中的變量和函數(shù)
// console.log(a); //此時(shí)a是在01.js中定義聲明的葱色,這里沒(méi)法使用。
//導(dǎo)出模塊:想要使用a娘香,要在定義a的文件里寫上export let a =10; 導(dǎo)出的是模塊對(duì)象苍狰,再回到需要的地方拿到這個(gè)導(dǎo)出的模塊對(duì)象
// import * as aa from './01.js'; //導(dǎo)入理解:從路徑'./01.js'中導(dǎo)出模塊* ,aa作為這個(gè)模塊的別名
// console.log(aa);
// console.log('模塊對(duì)象里的變量a為',aa.a);
import { a } from './01.js'//導(dǎo)出的簡(jiǎn)單寫法,很像解構(gòu)
console.log(a); //很像解構(gòu)因?yàn)榍懊鎻暮竺媸菍?duì)象中拿到a
//對(duì)應(yīng)01.js文件中的代碼
export let a =10;
export let b=20;
2.1. export 導(dǎo)出語(yǔ)法
使用export 關(guān)鍵字將模塊中的一部分代碼暴露給其他模塊, 可以將export 關(guān)鍵字放在任何變量, 函數(shù),類聲明之前, 將其導(dǎo)出
導(dǎo)出模塊
export 東西
2.1.1 export 導(dǎo)出(暴露)
// 單個(gè)導(dǎo)出數(shù)據(jù)(變量)
export let a = 10; //導(dǎo)出變量
export let b = 20;
export function add(){ //導(dǎo)出函數(shù)
return a+b
}
let c = 'c30'
console.log('這里是01.js')
//整體導(dǎo)出烘绽,一次性導(dǎo)出多個(gè)數(shù)據(jù)
let a = 10;
let b = 20;
let c = 'c30'
function add(){
return a+b
}
add()
export { //不能按照對(duì)象的寫法淋昭,只能數(shù)據(jù)的羅列,類似es6的簡(jiǎn)寫方式
a,
b,
add
}
2.1.2 export`命令輸出函數(shù)或類(class)
// 導(dǎo)出函數(shù)
export function multiply(x, y) {
return x * y;
};
// 導(dǎo)出類
export class Penson{
constructor(name,age){
this.name = name;
this.age = age;
}
}
2.1.3 as關(guān)鍵字重命名
通常情況下export 輸出的變量, 函數(shù)或者類就是本來(lái)的名字安接,但是有時(shí),我們可能并不希望使用它們的原始名稱. 此時(shí)就可以通過(guò)as關(guān)鍵字修改導(dǎo)出元素的名稱.
function v1() { ... }
function v2() { ... }
export {
v1 as streamV1,
v2 as streamV2,
};
2.1.4 特別注意
需要特別注意的是翔忽,export命令規(guī)定的是對(duì)外的接口
// 報(bào)錯(cuò)
export 1;
// 報(bào)錯(cuò)
var m = 1;
export m;
//正確寫法
// 寫法一
export var m = 1;
// 寫法二
var m = 1;
export {m};
// 寫法三
var n = 1;
export {n as m};
同樣的,function的輸出,也必須遵守這樣的寫法
// 報(bào)錯(cuò)
function f() {}
export f;
// 正確
export function f() {};
// 正確
function f() {}
export {f};
動(dòng)態(tài)綁定
export語(yǔ)句輸出的接口歇式,與其對(duì)應(yīng)的值是動(dòng)態(tài)綁定關(guān)系驶悟,即通過(guò)該接口,可以取到模塊內(nèi)部實(shí)時(shí)的值材失。
<!-- 動(dòng)態(tài)數(shù)據(jù)導(dǎo)出痕鳍,異步異出 -->
<!-- 導(dǎo)入文件 -->
<script type='module'>
import { a, b } from './01.js';
console.log(a, b); //a,b不是新值
</script>
<!-- 導(dǎo)出文件 -->
let a = 10;
let b = 20;
function add() {
return a + b
}
setTimeout(() => { //3秒后導(dǎo)出
a = 100;
b = 200;
}, 3000)
export { a, b, add }
<!-- 動(dòng)態(tài)數(shù)據(jù)導(dǎo)出,異步異出 -->
<!-- 導(dǎo)入文件 -->
<script type='module'>
import { a, b } from './01.js';
console.log(a, b); //a,b不是新值
setTimeout(()=>{
console.log(a, b); //這里的值就是更新的了
},4000)
</script>
<!-- 導(dǎo)出文件 -->
let a = 10;
let b = 20;
function add() {
return a + b
}
setTimeout(() => { //3秒后導(dǎo)出
a = 100;
b = 200;
}, 3000)
export { a, b, add }
2.1.5 整體導(dǎo)出
發(fā)現(xiàn)導(dǎo)入都是需要花括號(hào)的,如果希望導(dǎo)入不用花括號(hào),那就需要改變導(dǎo)出方式
// 導(dǎo)出時(shí)使用export default
let a;
export default a = 10;
// 導(dǎo)入時(shí)可以不用花括號(hào)
<script type="module">
import a from './modules/1.js'
// 默認(rèn)導(dǎo)出 export default 注意一個(gè)模塊只能有一個(gè)默認(rèn)導(dǎo)出
// export default [ 'a', 'b', 'add' ] //也可以導(dǎo)出對(duì)象豺憔,函數(shù)等 只能有一個(gè)默認(rèn)導(dǎo)出
//export default function aa(){
// console.log(111);
//}
導(dǎo)入文件
<script type='module'>
import aa from './01.js';
console.log(aa);
</script>
// 同時(shí)有默認(rèn)導(dǎo)出 export default 和變量導(dǎo)出
export let a=10;
export let b =20;
export default function aa(){
console.log(111);
}
導(dǎo)入文件
<script type='module'>
// 導(dǎo)入方法一
import * as aa from './01.js';
console.log(aa);
console.log(aa.a);
console.log(aa.b);
console.log(aa.default);
// 導(dǎo)入方法二
import def, { a, b } from './01.js'; //大括號(hào)里面的是有名字的额获,外面的是默認(rèn)導(dǎo)出的
console.log(a);
console.log(b);
console.log(def);
</script>
2.2. 導(dǎo)入模塊
使用export命令定義了模塊的對(duì)外接口以后,其他 JS 文件就可以通過(guò)import命令加載這個(gè)模塊恭应。
引入模塊
import 模塊路徑
//正常導(dǎo)入
import * as js01 from './01.js'
console.log(js01); //打印導(dǎo)入的模塊對(duì)象
console.log(js01.a); //這樣導(dǎo)入的數(shù)據(jù)需要打點(diǎn)才能使用,通常不這么用
//類似解構(gòu)的簡(jiǎn)單的導(dǎo)入方式
import {a,b,add}from './01.js'
console.log(a); //不用打點(diǎn)耘眨,直接使用
console.log(a,b);
console.log(add());
<script type="module">
import "./modules/1.js"; // 這種寫法只是相當(dāng)于引入一個(gè)文件昼榛,也叫無(wú)綁定導(dǎo)入
import {a} from "./modules/1.js" // 導(dǎo)入模塊中導(dǎo)出的a
</script>
import {firstName, lastName, year} from './profile';
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}
上面代碼的import命令,用于加載profile.js文件剔难,并從中輸入變量胆屿。import命令接受一對(duì)大括號(hào),里面指定要從其他模塊導(dǎo)入的變量名偶宫。大括號(hào)里面的變量名非迹,必須與被導(dǎo)入模塊(profile.js)對(duì)外接口的名稱相同。
import {a,b,add}from './01.js'
a =100 //導(dǎo)入的數(shù)據(jù)相當(dāng)于常量const a =10 纯趋,不能隨意再賦值憎兽,會(huì)報(bào)錯(cuò)
2.2.1 as關(guān)鍵字改變量名
//導(dǎo)出文件里的內(nèi)容
let a = 10;
let b = 20;
function add(){
return a+b
}
let c = 'c30'
export {
a as aaa, //將變量a通過(guò)as改名為aaa,導(dǎo)入時(shí)全部使用aaa
b,
add as bdd
}
//導(dǎo)入文件里的內(nèi)容
import {aaa,b,bdd}from './01.js'
console.log(aaa); //使用as后的變量名
console.log(b);
console.log(bdd());
import后面的from指定模塊文件的位置吵冒,可以是相對(duì)路徑纯命,也可以是絕對(duì)路徑,.js后綴可以省略痹栖。
2.2.2 import 提升
注意亿汞,import命令具有提升效果,會(huì)提升到整個(gè)模塊的頭部揪阿,首先執(zhí)行疗我。
// 導(dǎo)入前引用
export let a =10;
導(dǎo)入文件
<script type='module'>
console.log('我在導(dǎo)入之前的', a * a); //導(dǎo)入前引用
import { a } from './01.js'; //程序會(huì)將import提升,但最好自己寫在最前面
</script>
foo();
import { foo } from 'my_module';
//import的執(zhí)行早于foo的調(diào)用南捂。這種行為的本質(zhì)是吴裤,import命令是編譯階段執(zhí)行的,在代碼運(yùn)行之前黑毅。
2.2.3 import不能使用表達(dá)式
由于import是靜態(tài)執(zhí)行嚼摩,所以不能使用表達(dá)式和變量,這些只有在運(yùn)行時(shí)才能得到結(jié)果的語(yǔ)法結(jié)構(gòu)。
// 報(bào)錯(cuò) 不能拆成表達(dá)式
import { 'f' + 'oo' } from 'my_module';
// 報(bào)錯(cuò) 導(dǎo)入不能使用表達(dá)式
let module = 'my_module';
import { foo } from module;
// 報(bào)錯(cuò) import只能使用在頂層
if (x === 1) {
import { foo } from 'module1';
} else {
import { foo } from 'module2';
}
import { foo } from 'my_module';
import { bar } from 'my_module';
// 等同于
import { foo, bar } from 'my_module';
2.2.4 模塊化整體加載
除了指定加載某個(gè)輸出值枕面,還可以使用整體加載愿卒,即用星號(hào)(*)指定一個(gè)對(duì)象,所有輸出值都加載在這個(gè)對(duì)象上面潮秘。
注意琼开,模塊整體加載所在的那個(gè)對(duì)象,不允許運(yùn)行時(shí)改變枕荞。下面的寫法都是不允許的柜候。
2.2.5 import 特點(diǎn)總結(jié)
- 可以是相對(duì)路徑也可以是絕對(duì)路徑
import "./modules/1.js"; - import模塊只會(huì)導(dǎo)入一次,無(wú)論你引入多少次。模塊的導(dǎo)入導(dǎo)出有緩存機(jī)制躏精,只有第一次導(dǎo)入會(huì)執(zhí)行一遍并且將數(shù)據(jù)放入緩存渣刷,后面的所有的導(dǎo)入直接到緩存中取數(shù)據(jù)使用,但不會(huì)再執(zhí)行了矗烛。
- import "./modules/1.js"; 如果這么用,就相當(dāng)于引入了一個(gè)js文件
- 導(dǎo)入模塊中的一個(gè)變量或常量
import {a,b,c} from "./modules/1.js"
5.導(dǎo)入時(shí)可以取別名使用
// 導(dǎo)入時(shí)也可以修改別名
export let a =0;
導(dǎo)入的文件
<script type='module'>
let a = 1234;//導(dǎo)入的變量與模塊中的變量a重名了
console.log('a還是原來(lái)的a',a); //a已經(jīng)存在辅柴,所以要改別名
import {a as aa} from './01.js';
console.log('aa是導(dǎo)入時(shí)修改的別名',aa);
</script>
import {a as aa,b as bb,c as cc} from "./modules/1.js"
6.導(dǎo)入這個(gè)模塊導(dǎo)出的對(duì)象
import * as obj from './modules/1.js';
// 這個(gè)時(shí)候你就以使用obj這個(gè)Module對(duì)象了
console.log(obj.a);
7.import 的導(dǎo)入語(yǔ)句會(huì)進(jìn)行提升,提升到最頂部,首先執(zhí)行
console.log(a + b); // 能正常獲取到a,b的值
import {a,b} from './1.js'
8.出去的模塊內(nèi)容,如果里面有定時(shí)器發(fā)生導(dǎo)出內(nèi)容的改變,改變的內(nèi)容會(huì)根據(jù)定時(shí)器自動(dòng)同步緩存中的內(nèi)容,所以外邊導(dǎo)入的內(nèi)容也會(huì)實(shí)時(shí)發(fā)生改變,不像Comment會(huì)緩存瞭吃。
2.2.6 import() 動(dòng)態(tài)引入
默認(rèn)的import語(yǔ)法不能寫在if之類的判斷里的,因?yàn)槭庆o態(tài)引入,先引入在使用
// 這種寫法是錯(cuò)的
let a = 12;
if(a == 12){
import {a} from './1.js'
}else{
import {a} from './2.js'
}
import()是動(dòng)態(tài)引入 類似于node里面的require
//動(dòng)態(tài)導(dǎo)入模塊(按需導(dǎo)入)import()返回promise
let flag=false;
if(flag){
import('./01.js')
.then(data=>{
console.log('data',data);
})
}else{
import('./02.js')
.then(res=>{
console.log('res',res);
})
}
//動(dòng)態(tài)導(dǎo)入模塊返回promise優(yōu)化寫法
let flag = true;
let url = flag ? './01.js' : './02.js'
import(url)
.then(res => {
console.log('res', res);
})
優(yōu)點(diǎn):
- 按需加載
- 可以寫在if里面,判斷加載
- 路徑都可以是動(dòng)態(tài)的
例子:
/ 這里是1.js
let a = 20;
let b = 30;
export {
a, b
}
// 這里是2.js
import { a, b } from './1.js';
console.log(`a的值是:${a}, b的值是:$碌嘀`);
const sum = () => {
console.log(`我是a,b的和:${a + b}`);
return a + b;
}
const show = () => {
console.log('show 執(zhí)行了')
return 1;
}
function Person(name, age) {
this.name = name;
this.age = age;
this.showName = function () {
return `我的名字是${this.name}`;
}
}
export {
sum,
show,
a,
b
}
export default { Person }
// 這里是HTML導(dǎo)入
import mod, { sum, show, a, b } from "./2.js"
console.log(mod);
let p = new mod.Person("wuwei", 18);
console.log(p);
console.log(p.showName());
show();
sum();
console.log(a, b);
// 所有導(dǎo)出只能在頂層導(dǎo)出
if(true){
export let a=10; //這不是頂層所以報(bào)錯(cuò),不能通過(guò)條件判斷和函數(shù)再導(dǎo)出歪架,嵌套導(dǎo)出會(huì)報(bào)錯(cuò)
}else{
export let a=200;
}
導(dǎo)入文件
<script type='module'>
import {a} from './01.js'; //大括號(hào)里面的是有名字的股冗,外面的是默認(rèn)導(dǎo)出的
console.log(a);
</script>
// 導(dǎo)出只能在頂層導(dǎo)出,可以這樣寫
let a =0;
if(true){
a=10;
}else{
a=200;
}
export{a} //整體導(dǎo)出才可以
導(dǎo)入文件
<script type='module'>
import {a} from './01.js'; //大括號(hào)里面的是有名字的和蚪,外面的是默認(rèn)導(dǎo)出的
console.log(a);
</script>