ホームページ  >  記事  >  ウェブフロントエンド  >  モジュールとは何ですか? ES6 モジュールについての深い理解

モジュールとは何ですか? ES6 モジュールについての深い理解

php是最好的语言
php是最好的语言オリジナル
2018-08-09 16:13:122236ブラウズ

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>

但是在人多了以后,我们到时候会搞不清楚到底谁依赖了谁,保险起见只能全部都加载,性能浪费了太多

M4 ライフルをページとみなすと、次のように例えることができます

gun body-> <main></main>

silencer-

Multiplier-><nav></nav>

Grip-><aside><ol class=" list-paddingleft-2"> <code>Butt-> <footer></footer>
  • OK、やったことは 1 つだけです。それは、M4 ライフルを 5 つの部分に分解することです それぞれのパーツ

    分割した部分が
  • モジュール【モジュール】
  • で、分割した処理が

    モジュール化

    です。
  • モジュール化はプログラミングのアイデアであり、その核心は

    タスクを分割し、複雑な問題を単純化すること

    です。これにより、複数の人の間での分業とコラボレーションが容易になるだけでなく、問題を迅速に特定するのにも役立ちます
    • 複数人の間での分業とコラボレーションが容易になります - 異なる人が異なるモジュールを開発し、それらを組み合わせることでチームの効率が大幅に向上します

    • 問題 - 反動力が大きすぎる 音が大きすぎる場合は、80% の確率でストックまたはグリップに問題があります。音が大きすぎる場合は、80% の確率でサイレンサーに問題があります。 。

    1.2 モジュール化の血塗られた歴史以下はモジュール化の発展の歴史について語る小さな栗ですGong 氏と Sheng 氏は一緒にプロジェクトを引き受け、二人とも実装する必要がありました。いくつかの関数を個別に作成するのは簡単です。
    Console が独自の変数 a


    を出力するだけです。そこで、2 人はそれをまとめて、Gong 氏のコードが script1.js に別々に記述されるように手配しました。 、Sheng 氏のコードは、script2.js

    で記述された に個別に記述され、スクリプト タグを使用して個別にインポートされます<img src="https://img.php.cn//upload/image/459/497/718/1533802095965273.png" title="1533802095965273.png" alt="モジュールとは何ですか? ES6 モジュールについての深い理解"><pre class="brush:js;toolbar:false;">// 命名导出 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 …;</pre><pre class="brush:js;toolbar:false;">import defaultExport from &quot;module-name&quot;; // 导入默认默认变量 import * as name from &quot;module-name&quot;; // 将模块内所有变量导出,并挂载到name下【name是一个module对象】。什么要有as——为了防止export出来的变量命名冲突 import { export } from &quot;module-name&quot;; // 导入某一个变量 import { export as alias } from &quot;module-name&quot;; // 导入某一个变量并重命名 import { export1 , export2 } from &quot;module-name&quot;; // 导入两个变量 import { export1 , export2 as alias2 , [...] } from &quot;module-name&quot;; // 导入多个变量,同时可以给导入的变量重命名 import defaultExport, { export [ , [...] ] } from &quot;module-name&quot;; // 导入默认变量和多个其它变量 import defaultExport, * as name from &quot;module-name&quot;; // 导入默认变量并重新命名 import &quot;module-name&quot;; // 导入并加载该文件【注意文件内的变量必须要通过export才能被使用】 var promise = import(module-name); // 异步的导入</pre><pre class="brush:js;toolbar:false;">// name.js文件 let names = [&amp;#39;gongxiansheng&amp;#39;,&amp;#39;pengxiansheng&amp;#39;] export default names</pre>すぐに最初の問題、<code>変数名の競合 に遭遇しました。 >🎜特に 🎜async🎜 が含まれている場合、次の状況が発生します🎜
    // 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
    🎜 上記の問題は明らかに 🎜a がグローバル変数🎜であることが原因であるため、解決策も非常に明確です - ローカル変数を作成する code>🎜<h3>ローカル変数</h3>🎜ES5時代は即時実行関数を使ってローカル変数を作成🎜<pre class="brush:js;toolbar:false;">// control.js import module1 from &amp;#39;./script1.js&amp;#39; import module2 from &amp;#39;./script2.js&amp;#39; setTimeout(() =&gt; { module1() }, 1000) module2()</pre><pre class="brush:js;toolbar:false;">&lt;!--HTML文件--&gt; &lt;script type=&quot;module&quot; src=&quot;./control.js&quot;&gt;&lt;/script&gt; &lt;!--注意一定要加上type=&quot;module&quot;,这样才会将这个script内的代码当做模块来对待--&gt;</pre>🎜ES6時代は<code>ブロックレベルのscope+letを直接使う🎜
    // 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

    ウィンドウを介してさまざまなモジュールを接続

    🎜 その後、会社はフロントエンドの責任者を雇い、今では変数をいつコンソールするかしか制御できないと言い、新しい control.js ファイルを作成しました。 window オブジェクトを介して script1.js と scirpt2 を接続します。 code>save [export] と Get [Dependency]🎜
    // 报错
    {
      export let foo = &#39;bar&#39;;
    }
    console.log(foo)
    import {foo} from &#39;./script1.js&#39;

    Dependency 読み込み順序

    🎜 迷惑な製品は要件を再度変更し、name.js ファイルを指定しました🎜rrreee🎜要件 さて、ゴンさんとシェン卿、コンソールで私の名前を教えてもらう必要があります🎜これは簡単ではありませんか?書くのに数秒かかった🎜rrreeerrreeerreerree🎜しかしすぐに、コンソール出力がすべて未定義であることがわかりました🎜フロントエンドの責任者は一目で問題を見て、彼らに言いました🎜コードあなたが依存するものはあなた自身のコードの前に導入される必要があります。そうしないと、私のcontrol.jsがあなたのコードの後に​​導入されているかどうかを確認してください。🎜 おっと、それが判明しました。 js ファイルのロード順序に問題があるため、変更しましょう🎜rrreee🎜しかし、人数が増えると、誰が誰に依存しているのかが分からなくなります。念のため変更してください。すべてをロードするだけです。これはパフォーマンスの無駄です。 が多すぎると、フロントエンドの上司は首を振ってため息をつきました🎜🎜2. ES6 モジュール🎜🎜2.1 ES6 以前のモジュール化の問題点🎜🎜🎜🎜競合🎜🎜🎜🎜 各モジュールを接続するにはウィンドウを使用する必要があります🎜🎜🎜🎜 すべての依存関係をロードする必要があります🎜🎜🎜🎜 ロード順序にも注意する必要があります🎜🎜🎜🎜 モジュール化は の最大のハイライトの 1 つですES6 🎜 より前の構文にはモジュラー システムが存在しなかったため、🎜このペア 大規模で複雑なプロジェクトの開発には大きな障害 🎜 が生じます。プロジェクトを分割して複数人でより良い共同開発を行うことができないからです。さらに重要なのは、🎜他のほとんどの言語はモジュール性をサポートしていることです🎜。 🎜🎜言語がサポートしていないので、JSにモジュール性を導入するにはどうすればよいですか? 🎜🎜 フロントエンド コミュニティは独自のモジュール読み込み計画をいくつか開発しました。これは CommonJS [サーバー]、AMD、CMD [ブラウザ] の起源でもあります。 🎜🎜🎜
    但是现在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 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。