>  기사  >  웹 프론트엔드  >  모듈이란 무엇입니까? 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 소총을 페이지로 간주하면 다음과 같이 비유할 수 있습니다.

총 본체-><main></main>

소음기-> ;header>

Multiplier-> <nav></nav>

그립->
    Butt-> <footer></footer>
  1. 좋아요, 방금 m4 소총을 다섯 조각으로 분해하는 작업을 완료했습니다. 분할하는 부분은

    모듈【모듈】
  2. 이고, 분할하는 과정은
  3. 모듈화

    입니다.

  4. 모듈화는
  5. 작업을 분할하고 복잡한 문제를 단순화

    하는 것이 핵심인 프로그래밍 아이디어입니다. 이는 여러 사람 간의 작업 분담과 협업을 촉진할 뿐만 아니라 문제를 빠르게 찾는 데도 도움이 됩니다

    • 작업 분담과 여러 사람 간의 협업을 촉진합니다. - 여러 사람이 서로 다른 모듈을 개발한 다음 이를 결합하여 팀 효율성을 크게 높일 수 있습니다.

    빠르게 찾을 수 있도록 도와주세요. 문제 - 반동력이 너무 높습니다. 소리가 너무 크면 80%는 스톡이나 그립에 문제가 있고, 소리가 너무 크면 80%는 소음기에 문제가 있습니다. .

    1.2 모듈화의 피비린내 나는 역사다음은 모듈화의 개발 역사에 대해 이야기하는 작은 밤입니다.Gong 씨와 Sheng 씨는 함께 프로젝트를 맡았고 두 사람 모두 구현해야 했습니다. 몇몇 기능은 따로 간단해요.

    콘솔이 자체 변수 a

    를 꺼내서 둘이서 공 선생님의 코드를 script1.js에 따로 작성하도록 정리했는데, 그리고 Mr. Sheng의 코드는 script2.js로 작성된에 별도로 작성하고 스크립트 태그를 사용하여 별도로 도입했습니다

    // 命名导出
    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 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); // 异步的导入
    // name.js文件
    let names = [&#39;gongxiansheng&#39;,&#39;pengxiansheng&#39;]
    export default names
    모듈이란 무엇입니까? ES6 모듈에 대한 깊은 이해곧 그들은 첫 번째 문제에 직면했습니다 - 변수 이름 충돌

    특히 🎜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가 전역 변수🎜로 인해 발생하므로 해결 방법도 매우 명확합니다. 로컬 변수 만들기🎜

    로컬 변수

    🎜ES5 시대에는 로컬 변수 생성을 위해 즉시 실행 함수를 사용했습니다🎜
    // 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内的代码当做模块来对待-->
    🎜ES6 시대에는 블록 수준 범위+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를 연결합니다. js🎜🎜
    // 报错
    {
      export let foo = &#39;bar&#39;;
    }
    console.log(foo)
    import {foo} from &#39;./script1.js&#39;
    rrreee🎜 이때 매우 중요한 점은 window가 🎜전역 변수🎜이며 🎜공용 창고🎜 역할을 한다는 점입니다. code>저장 [내보내기] 및 Get [종속성]🎜rrreeerrreee

    종속성 로딩 순서

    🎜 짜증나는 제품이 요구사항을 다시 변경하고 이름을 부여했습니다. js 파일🎜rrreee🎜요구사항 이제 공 선생님과 성 선생님, 내 이름을 알려줄 콘솔이 필요합니다🎜간단하지 않나요? 작성하는 데 몇 초가 걸렸습니다🎜rrreeerrreeerrreeerrreee🎜하지만 곧 콘솔 출력이 모두 정의되지 않음이라는 것을 발견했습니다🎜프론트엔드 상사가 문제를 한눈에 보고 그들에게 말했습니다🎜코드 당신은 반드시 당신의 코드 앞에 삽입해야 합니다. 그렇지 않으면 내 control.js가 당신의 코드 뒤에 삽입되는지 확인하세요. 왜냐하면 내가 당신의 코드를 사용했기 때문입니다.🎜 이런, 그렇군요. js 파일 로딩 순서에 문제가 있을 수 있으니 바꿔보겠습니다🎜rrreee🎜하지만 사람이 많아지면 누가 누구에게 의존하는지 알 수 없게 되니 안심하세요. 다 로드하면 성능낭비다 프론트엔드 사장이 고개를 저으며 한숨을 쉬었다🎜🎜2.ES6 모듈🎜🎜2.1 ES6 이전 모듈화의 문제점🎜🎜🎜🎜Variable 충돌🎜🎜🎜🎜각 모듈을 연결하려면 창을 사용해야 합니다🎜🎜🎜🎜 모든 종속 항목을 로드해야 합니다🎜🎜🎜🎜로딩 순서에도 주의해야 합니다🎜🎜🎜🎜모듈화는 이 게임의 가장 큰 특징 중 하나입니다. ES6, 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    성명:
    본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.