首頁 >web前端 >js教程 >什麼是模組?深刻理解ES6模組

什麼是模組?深刻理解ES6模組

php是最好的语言
php是最好的语言原創
2018-08-09 16:13:122297瀏覽

1.模組

1.1 什麼是模組?什麼是模組化?

玩過FPS遊戲的朋友應該知道,一把組裝完整的M4步槍,一般是槍身 消音器 倍鏡 握把 槍托

如果把M4​​步槍看成是一個頁面的話,那麼我們可以做如下類比
槍身-> <main></main>
#消音器-> <header></header>
倍鏡-> <nav></nav>
#握把-> <aside></aside>
槍托-> <footer></footer>

#OK,你剛剛做了一件事情,就是把m4步槍拆成了五個部分,你拆分的每一個部分就是一個模組【module】,你拆分的這個過程就是模組化【modularization】

模組化是一種程式設計思想,其核心就是分割任務,把複雜問題簡單化,這樣一來既方便多人分工協作,又可以幫助我們快速定位問題

  • 方便多人分工協作- 可以不同的人開發不同的模組,再組合,大大增加團隊效率

  • 幫助我們快速定位問題- 後座力太大,那八成是槍託或握把的問題;聲音過大,那八成是消音器的問題。

1.2 模組化的血淚史

下面用一個小栗子講一講模組化的發展史

龔先生和棚先生一起接了一個項目,他們兩個需要分別實現一些功能,很簡單,就是Console出來自己的變數a

於是他們倆一合計,安排龔先生的程式碼單獨放在 script1.js裡寫,棚先生的程式碼單獨放在script2.js裡寫,然後用script標籤分別引入

// script1.js文件

var a = 1
console.log(a)
// script2.js文件

var a = 2
console.log(a)
<!--HTML文件-->

<script src="./script1.js"></script>
<script src="./script2.js"></script>

很快他們遇到了第一個問題—— 變數命名衝突
尤其是包含了異步的時候,會出現如下情況

// script1.js文件

var a = 1
setTimeout(()=>{
  console.log(a)  // 我们想console出来1,却console出了2
},1000)
// script2.js文件

var a = 2
console.log(a)

上面的問題明顯是由於a是一個全域變數導致的,所以解決想法也很明確-造一個局部變數唄

局部變數

ES5時代使用立即執行函數製造局部變數
// script1.js文件
!function(){
    var a = 1
    setTimeout(()=>{
      console.log(a)  // 这下是2了
    },1000)
}()
// 下面有5000行代码
// script2.js文件

console.log(2)
ES6時代直接使用區塊層級作用域let
#
// script1.js文件
{
    let a = 1
    setTimeout(()=>{
      console.log(a)  // 这下是2了
    },1000)
}
// script2.js文件
{
    let a = 2
    console.log(a)
}

透過window連接各個模組

後來公司招募了一個前端大佬,說現在只能由他來控制什麼時候console變量,於是他新建了一個control.js檔案
透過window物件連接script1.js和scirpt2.js

#
// script1.js文件
{
    let a = 1
    window.module1 = function() {
        console.log(a)
    }
}
// script2.js文件
{
    let a = 2
    window.module2 = function() {
        console.log(a)
    }
}
// control.js文件
setTimeout(()=>{
    window.module1()
},1000)

window.module2()
這個時候,非常重要的一點就是window是一個全域變數並且充當了一個公用倉庫,這個倉庫有兩個關鍵作用,存【導出】取【依賴】
// script1.js文件
{
    let a = 1
    // 把这个函数存放进window,就是导出到window
    window.module1 = function() {
        console.log(a)
    }
}
// control.js文件
setTimeout(()=>{
    // 我们从window里取出module1函数进行调用,就是依赖了script1.js文件
    window.module1()
},1000)

window.module2()

依賴載入的順序

煩人的產品對需求又進行了更改,給了一個name.js文件

// name.js文件
window.names = [&#39;gongxiansheng&#39;,&#39;pengxiansheng&#39;]

要求現在龔先生和棚先生需要Console出自己的名字
這還不簡單?幾秒鐘寫好

// script1.js文件
{
    window.module1 = function() {
        console.log(window.names[0])
    }
}
// script2.js文件
{
    window.module2 = function() {
        console.log(window.names[1])
    }
}
// control.js文件
setTimeout(()=>{
    window.module1()
},1000)

window.module2()
<!--HTML文件-->

<script src="./script1.js"></script>
<script src="./script2.js"></script>

但很快他們發現,console出來的都是undefined
前端大佬一眼看出了問題,對他們倆說
你們所依賴的程式碼一定要在你們自己的程式碼前引入,不然是取不到值的;你看我的control.js是不是在你們兩個的程式碼後面引入的,因為我用到了你們兩個的程式碼了呀
噢噢,原來是js檔案載入順序問題,改一下吧

<!--HTML文件-->

<script src="./name.js"></script>
<script src="./script1.js"></script>
<script src="./script2.js"></script>
<script src="./control.js"></script>

但是在人多了以後,我們到時候會搞不清楚到底誰依賴了誰,保險起見只能全部都加載,性能浪費了太多,前端大佬搖頭嘆息道

#2. ES6的模組

2.1 ES6之前模組化的痛點

  1. 變數衝突

  2. 要用window連接各個模組

  3. 依賴需要全部載入

  4. 還要TMD注意載入順序

模組化是ES6的最大的亮點之一,因為在ES6之前的語法裡從未有過模組化的體系這對開發大型的、複雜的專案形成了巨大障礙。因為我們無法對專案進行拆分,無法更好地進行多人協作開發。更重要的是,其它大部分語言都支援模組化

既然語言不支持,那麼如何將模組化引入JS呢?

前端社群就自己制定了一些模組載入方案-這也是CommonJS【伺服器】和AMD、CMD【瀏覽器】的由來。
什麼是模組?深刻理解ES6模組

#
但是现在ES6引入了模块化的功能,实现起来非常简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

2.2 import和export的用法

import和export语法较为简单,大家去MDN可以看非常详细的讲解,笔者在这里知识用注释简单介绍一下

export语法

// 命名导出
export { name1, name2, …, nameN };
export { variable1 as name1, variable2 as name2, …, nameN };
export let name1, name2, …, nameN; // also var
export let name1 = …, name2 = …, …, nameN; // also var, const
export function FunctionName() {...}
export class ClassName {...}

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

// 将其它模块内的导出作为当前文件的导出
export * from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;

import用法

import defaultExport from "module-name"; // 导入默认默认变量
import * as name from "module-name"; // 将模块内所有变量导出,并挂载到name下【name是一个module对象】。什么要有as——为了防止export出来的变量命名冲突
import { export } from "module-name"; // 导入某一个变量
import { export as alias } from "module-name"; // 导入某一个变量并重命名
import { export1 , export2 } from "module-name"; // 导入两个变量
import { export1 , export2 as alias2 , [...] } from "module-name"; // 导入多个变量,同时可以给导入的变量重命名
import defaultExport, { export [ , [...] ] } from "module-name"; // 导入默认变量和多个其它变量
import defaultExport, * as name from "module-name"; // 导入默认变量并重新命名
import "module-name"; // 导入并加载该文件【注意文件内的变量必须要通过export才能被使用】
var promise = import(module-name); // 异步的导入

使用import和export改写第一节的代码

// name.js文件
let names = [&#39;gongxiansheng&#39;,&#39;pengxiansheng&#39;]
export default names
// script1.js
import names from &#39;./name.js&#39;

let module1 = function () {
  console.log(names[0])
}
export default module1
// script2.js
import names from &#39;./name.js&#39;

let module2 = function() {
  console.log(names[1])
}
export default module2
// control.js
import module1 from &#39;./script1.js&#39;
import module2 from &#39;./script2.js&#39;

setTimeout(() => {
  module1()
}, 1000)
module2()
<!--HTML文件-->

<script type="module" src="./control.js"></script>
<!--注意一定要加上type="module",这样才会将这个script内的代码当做模块来对待-->

2.3 拓展:import和export的一些运行原理

2.3.1 ES6 模块输出的是值的引用,输出接口会动态绑定

其实就是按照数据类型里的引用类型的概念去理解。
这一点与 CommonJS 规范完全不同。
CommonJS 模块输出的是值的缓存,不存在动态更新。
// module1.js
export var foo = &#39;bar&#39;;
setTimeout(() => foo = &#39;baz&#39;, 500);
// module2.js
import {foo} from &#39;./module1.js&#39;
console.log(foo)
setTimeout(() => console.log(foo), 1000);

// console的结果
// bar
// baz

2.3.2 export可以出现在模块内的任何位置,但不能处于块级作用域内

// 报错
{
  export let foo = &#39;bar&#39;;
}

2.3.3 import具有提升效果(类似于var),会提升到整个模块的头部,首先执行

console.log(foo)
import {foo} from &#39;./script1.js&#39;
参考资料:ECMAScript 6 入门
本文纯属原创,为了方便大家理解,小故事,小栗子都是笔者自己想的。如果您觉得对你有帮助,麻烦给个赞,给作者灰暗的生活挥洒挥洒积极向上的正能量,谢谢啦^_^。

相关推荐:

彻底搞懂JS无缝滚动代码

彻底弄懂CSS盒子模式(DIV布局)

以上是什麼是模組?深刻理解ES6模組的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn