当前位置: 代码迷 >> 综合 >> part2-模块2-模块化开发(模块化演变过程 -- 模块化规范)
  详细解决方案

part2-模块2-模块化开发(模块化演变过程 -- 模块化规范)

热度:33   发布时间:2023-11-26 16:48:28.0

1: 模块化演变过程

目录

1: 模块化演变过程

2: 模块化规范总结

3: ES Module

1:  ES Module 基本特性

2:  ES Module 导入,导出

3: ES Module导入导出注意事项

4: ES Module导入方式 和 导出方式;

5: ES Module  浏览器环境 Polyfill


第一阶段:(基于文件的划分模块方式)

概念:将每个功能 和 相关数据状态 分别放在单独的文件里

           约定每个文件就是一个单独的模块,

           使用每个模块【这个模块引入到页面中】,然后直接调用模块的成员【方法/ 变量】。

缺点: 所有成员都可以在模块外部被修改和访问【所有模块都是直接在全局工作的,没有私有空间】

            模块一旦多了之后,命名会发生冲突,

            无法管理各个模块之间的依赖关系。

var  name = 'module-a'
function method1 () {cosnole.log(name + '#method1')
}
function method2 () {cosnole.log(name + '#method2')
}

第二阶段:【命名空间】

       概念:每个模块只暴露一个全局对象,所有模块成员都挂载到这个对象上

       做法:在第一阶段基础上,通过将 每个模块 【包裹成一个全局对象】的形式实现

类似于为 每个模块的成员添加 【命名空间】的感觉

       优点: 减少,命名冲突的发生

       缺点: 所有成员都可以在模块外部被修改和访问【所有模块都是直接在全局工作的,没有私有空间】。

                   无法管理各个模块之间的依赖关系。

var moduleA = {name: 'module-a',method1: function () {console.log(this.name + '#method1')},  method2: function () {console.log(this.name + '#method2')}
}

第三阶段:

       概念:使用立即执行函数表达式(IIFE)为模块提供私有空间

       做法:将每个模块成员都放在一个函数提供的私有作用域中,

                  对于需要向外暴露的成员,通过挂载到全局对象上的方式实现

       优点: 模块有了私有空间

       缺点:无法管理各个模块之间的依赖关系。

                  有了私有成员,私有成员只能在模块成员内通过闭包的形式访问

// (function(){})() : 自加载执行该方法
(function () {var  name = 'module-a'function method1 () {cosnole.log(name + '#method1')}function method2 () {cosnole.log(name + '#method2')}// 将需要向外暴露的成员,挂载到全局对象上window.moduleA = {method1: method1,method2: method2    }   
})()

第四阶段:

       概念:使用(IIFE)参数作为依赖声明使用

       做法:在第三阶段的基础上,利用立即执行函数的参数传递模块依赖项

       优点: 使得模块之间的关系变得更加明显

(function ($) {var  name = 'module-a'function method1 () {cosnole.log(name + '#method1')$('body').animate({ margin: '200px'})}function method2 () {cosnole.log(name + '#method2')}// 将需要向外暴露的成员,挂载到全局对象上window.moduleA = {method1: method1,method2: method2    }   
})(jQuery)

2: 模块化规范总结

1: CommonJS模块规范【以同步模式加载模块

        它是运行在node.js环境下的模块化规范,node的机制是在启动时,加载模块,执行时直接使用模块

        一个文件就是一个模块 每个模块都有单独的作用域

        通过module.exports 导出模块成员

        通过require函数载入模块

        该模块规范不适合浏览器原因??

         答:浏览器在加载页面时,如果需要同步加载所有模块,必然导致性能低下,所有早期的浏览器没有使用CommonJS规范

2AMD模块规范【异步模式加载模块】根据浏览器特性制造的规范

     通过define函数去定义模块;

     通过require 来加载模块(内部会自动创建一个script标签来加载模块) require.js是一个自动加载模块器 ,它提供了"AMD"模块化规范

3: ES Module  支持大部分浏览器,现阶段比较流行的模块化规范

3: ES Module

1:  ES Module 基本特性

<script type="module">console.log(this) // 因为是module类型 所以是undefinedvar foo = 100console.log(foo)  // 100
</script>
<script type="module">console.log(foo) // 报错 foo没有定义,因为每个 ES Module 都是运行在单独的私有作用域中
</script>

   1: 自动采用严格模式, 忽略'use strict', 严格模式的标志就是不能直接使用this

   2: 每个ESM 模块都是单独的私有作用域

   3: ESM 是通过 CORS 去请求外部 JS 模块的

   4: ESM 的 script 标签回延迟执行脚本

2:  ES Module 导入,导出

     通过 export 导出模块成员

     通过 import  导入模块

var name = 'foo module'
// 默认导出name
export default name// 接受默认导出, 将默认导出 重命名为 fooName
import fooName from './module'
console.log(fooName)

3: ES Module导入导出注意事项

    1:  ES Module中的 { }是固定语法,

        import { name, age} = require('./module.js')

        CommonJS中是先将模块整体导入为一个对象,再从对象中解构出需要的成员

        export obj = {name, age}

        const { name, age } = require('./module.js')

   2: 导入成员并不是复制一个副本,而是直接导入模块成员的引用地址

       也就是说 export导出的变量 和 import 得到的变量,在内存中是同一个块空间

       所有一旦模块成员修改了,这里也会同步修改

   3:  导入的模块成员是只读的,不可以修改

        但是如果导入的是一个对象,对象的属性读写不受影响

        name = 'tom' // 报错

        obj.name = 'tom' // 正常

4: ES Module导入方式 和 导出方式;

// import { name, age } from 'module.js' // 不可以
// import { name, age} = require('./module') // 不可以
import { name, age } from './module.js'
import { name, age } from '/04.1-import/module.js'
import { name, age } from 'http://localhost:3002/04.1-import/module.js'console.log(name)

   1: import导入模块路径必须是完整路径,路径的  ./ 不能省略,路径可以是 绝对路径url

   2: 只加载模块,不提取成员【在导出一些不受外界影响的子功能模块中很有作用】        

import {} from './module.js' 
import './module.js'

    3: 用 * 导出模块所有成员,并将其放入一个对象中,从对象中提取需要的成员

 import * as mod from './module.js' console.log(mod)

   4: 如何动态去加载模块,  import() 返回一个Promise对象,等待异步模块加载完成后,会自动加载 then 所指定的回调函数 模块对象可以通过回调函数的参数获取

import('./module.js').then(function (module) { console.log(module) 
})

   5: 同时导入  其他成员 和 默认成员

import { name, age, default as title } from './module.js'
import { name, age, title } from './module.js'
import title, { name, age } from './module.js' // 最新写法

   6: 导出方式,可在导入时直接导出

export { default as Button } from './button.js'
export { Avatar } from './avatar.js'

5: ES Module  浏览器环境 Polyfill

         es-module-loader 将浏览器中不识别的ES Module代码读取出来,交给babel来转化为浏览器可识别的代码 
       在IE 浏览器执行会报错,因为IE浏览器不支持es6的新特性Promise
       所以需要给IE浏览器引入一个promise-polyfill让其支持Promise
       备注:
        如果支持promise-polyfill的浏览器会执行2次,

         1: 浏览器本身执行一次;2: ES Module loaderpolyfill会再执行一次

       解决办法??
            可以使用 script 的新属性 nomodule 来设置, 不支持 polyfill的加载,支持的不需要加载
        
       注意!
          在本地测试可以使用   nomodule,它的原理是:运行阶段动态的去解析脚本,效率非常低下
          在生成环境不要用: 生产环境应该提前将这些代码编译出来,让它可以直接在浏览器工作

<body><script nomodule src="https://unpkg.com/promise-polyfill@8.1.3/dist/polyfill.min.js"></script><script nomodule src="https://unpkg.com/promise-polyfill@8.1.3/dist/polyfill.min.js"></script><script nomodule src="https://unpkg.com/browser-es-module-loader@0.4.1/dist/babel-browser-build.js"></script><script type="module">import { foo } from './module.js'console.log(foo)</script>
</body>

  相关解决方案