关于javascript:JS模块化

54次阅读

共计 5198 个字符,预计需要花费 13 分钟才能阅读完成。

JS 模块化

背景

js 自身简略的页面设计:页面动画 + 表单提交
并无模块化 or 命名空间的概念

JS 模块化需要日益增长

幼年期:无模块化

  1. 开始须要在页面中减少一些不同的 js:动画、表单、格式化
  2. 多种 js 文件被分在不同的文件中
  3. 不同的文件又被同一个模板饮用
// 过后是被认可的:文件拆散是最根底的
<script src="jquery.js"> </script>
<script src="main.js"> </script>
<script src="dep1.js"> </script>
<script src="dep2.js"> </script>

问题:

  • 净化全局作用域 => 不利于大型项目的开发以及多人团队的共建

成长期:模块化的雏形 – IIFE(立刻函数、语法侧的优化)

作用域的把控

例子:

// 定义一个全局变量
let count = 0
// 代码块 1
const increase = () => ++count
// 代码块 2
const reset = () => {count = 0}

increase()
reset()

利用函数的块级作用域

(() => {
    let conut = 0
    // ...
})

仅定义了一个函数,并没有执行,如果立刻执行

(() => {
    let conut = 0
    // ...
})()

初步实现了一个最简略的模块(通过立刻执行函数)

尝试去定一个最简略的模块

const iifeModule = (() => {
    let count = 0
    // 代码块 1
    const increase = () => ++count
// 代码块 2
    const reset = () => {count = 0}
    return {
        // 代码块 1
         increase: () => ++count,
        // 代码块 2
        reset: () => {count = 0}
    }
})()

iifeModule.increase()
iifeModule.reset()

诘问:如果有额定依赖,如何优化 iife?– 参数的调用

优化 1:依赖其余模块的 IIFE

const iifeModule = ((dependencyModule1, dependencyModule2) => {
    let count = 0
    return {
        // 代码块 1
         increase: () => ++count,
        // 代码块 2
        reset: () => {count = 0}
    }
})(dependencyModule1, dependencyModule2)

iifeModule.increase()
iifeModule.reset()

问:理解 jquery 的依赖解决以及模块加载计划吗?/ 理解传统 IIFE 是如何解决多方依赖的问题吗?
答:IIFE + 传参调配

实际上,jquery 等框架其实利用了 revealing module(揭示模式) 的写法

const iifeModule = ((dependencyModule1, dependencyModule2) => {
    let count = 0
    // 代码块 1
    const increase = () => ++count
    // 代码块 2
    const reset = () => {count = 0}
    // 仅裸露接口,不裸露办法
    return {
        increase,
        reset
    }
   
})(dependencyModule1, dependencyModule2)

iifeModule.increase()
iifeModule.reset()

成熟期

CJS – CommonJS

由 node 制订的一套计划
特色:

  • 通过 module + exports 去对外裸露借口
  • 通过 require 来调用其余模块

模块组织形式
main.js 文件中

// 援用
const dependencyModule1 = require(dependencyModule1)
const dependencyModule2 = require(dependencyModule2)

// 解决
let count = 0
// 代码块 1
const increase = () => ++count
// 代码块 2
const reset = () => {count = 0}

// 做一些跟引入依赖相干事宜……

// 裸露接口
exports.increase = increase
exports.reset = reset

module.exports = {
    increase,
    reset
}

模块应用形式

const {increase, reset} = require(main.js)

increase()
reset()

可能被问到的货色

理论执行解决
转化成立即执行函数
CJS 和 IIFE 的联合

// 须要穿参
(function () {const dependencyModule1 = require(dependencyModule1)
    const dependencyModule2 = require(dependencyModule2)
    
    // 业务逻辑……
}).call(thisValue, exports, require, module)

CJS 长处:
CommonJS 率先在服务端实现了,从框架层面解决依赖、全局变量净化的问题

CJS 毛病:
次要针对了服务端的解决方案,都是同步引入。对于异步拉取依赖整合没有那么敌对。

新的问题 —— 异步依赖

AMD 标准

通过异步加载 + 容许定制回调函数
经典实现框架:require.js

新增定义形式:

// 通过 deifine 来定义一个模块,而后 require 进行加载
/**
 * id: 模块名
 * [depends]:依赖模块
 * callback:工厂办法
 */
define(id, [depends], callback)
require([module], callback)

模块定义形式:

define('amdModule', ['dependencyModule1', 'dependencyModule2'], (dependencyModule1,dependencyModule1) => {
    // 解决
    let count = 0
    // 代码块 1
    const increase = () => ++count
    // 代码块 2
    const reset = () => {count = 0}
    
    return {increase, reset}
})

引入模块:

require(['amdModule'], amdModule => {amdModule.increase()
})

问:如果在 amdModule 中想兼容已有代码,怎么办?

define('amdModule', [], require => {
    // 援用
    const dependencyModule1 = require(dependencyModule1)
    const dependencyModule2 = require(dependencyModule2)

// 解决
    let count = 0
// 代码块 1
    const increase = () => ++count
// 代码块 2
    const reset = () => {count = 0}

// 做一些跟引入依赖相干事宜……

// 裸露接口
    exports.increase = increase
    exports.reset = reset

    module.exports = {
        increase,
        reset
    }
})

问:AMD 中应用 revealing

define('amdModule', [], (require, exports, module) => {
    // 援用
    const dependencyModule1 = require(dependencyModule1)
    const dependencyModule2 = require(dependencyModule2)

    // 解决
    let count = 0
    // 代码块 1
    const increase = () => ++count
    // 代码块 2
    const reset = () => {count = 0}

    // 做一些跟引入依赖相干事宜……

    // 裸露接口
    exports.increase = increase
    exports.reset = reset
})

define(require => {const otherModule = require('amdModule')
    otherModule.increase()
    otherModule.reset()})

问:兼容 AMD & CommonJS?答:UMD
UMD 的呈现

(define('amdModule', [], (require, exports, module) => {
    // 援用
    const dependencyModule1 = require(dependencyModule1)
    const dependencyModule2 = require(dependencyModule2)

    // 解决
    let count = 0
    // 代码块 1
    const increase = () => ++count
    // 代码块 2
    const reset = () => {count = 0}

    // 做一些跟引入依赖相干事宜……

    // 裸露接口
    exports.increase = increase
    exports.reset = reset
}))(
    //  指标是一次性辨别 CommonJS or AMD
    typeof module === "object"
    && module.exports
    && typeof define !== "function"
     ? // 是 CommonJS
    factory => module.exports = factory(require, exports, module) 
        : // 是 AMD
    define
)
  • 长处:适宜在浏览器中加载异步模块,能够并行加载多个模块
  • 毛病:会有引入老本,不能按需加载

CMD 标准

按需加载
次要利用的框架 sea.js

define('module', [], (require, exports, module) => {let $ = require('jquery');
    // jquery 相干的代码
    
    let dependencyModule1 = require('dependencyModule1')
    //  dependencyModule1 相干逻辑
})
  • 长处:按需加载,依赖就近
  • 毛病:依赖打包,加载逻辑存在每个模块中,扩充模块体积

ES6 模块化

走向新时代

新增定义:
引入关键字 —— import
导出关键字 —— export

模块引入、导出和定义
esModule.js

// 引入
import dependencyModule1 from 'dependencyModule1'
import dependencyModule2 from 'dependencyModule2'

// 实现
let count = 0
export const increase = () => ++count
const reset = () => {count = 0}

// 导出
export default {reset}

模板引入的中央:

<script type="module" src="esModule.js"></script>

node 中:

import {reset} from 'esModule.js'
reset()

import esModule from 'esModule.js'
esModule.reset()
  • 长处(重要性):通过一种最对立的状态整合 js 模块化
  • 毛病(局限性):实质上还是运行了依赖剖析

解决模块化的新思路 —— 前端工程化

背景

基本问题 — 运行时进行依赖剖析

前端的模块化解决计划依赖于运行时剖析

解决方案:线下执行
grunt、gulp、webpack

<!doctype html>
    <script src="main.js"></script>
    <script>
    //    给构建工具一个标示位
        require.config(__FRAME_CONFIG__)
    </script>
    <script>
    require(['a', 'e'], () => {// 业务逻辑})
    </script>
</html>
define('a', () => {let b = require('b');
    let c = require('c');
    
    exports.run = () => {//   run}
})

工程化实现

step1: 扫描依赖关系表:

{a: ['b', 'c']
    b: ['d']
}

step2: 从新生成依赖数据模板

<!doctype html>
    <script src="main.js"></script>
    <script>
        //  构建工具生成数据
        require.config({
            "deps": {a: ['b', 'c'],
                b: ['d'],
                e: []}
        })
    </script>
    <script>
    require(['a', 'e'], () => {// 业务逻辑})
    </script>
</html>

step3: 执行工具,采纳模块化计划解决模块化解决依赖

define('a', ['b', 'c'], () => {
    // 执行代码
    exports.run = () => {//   run}
})

长处:

  1. 构建时生成配置,运行时执行
  2. 最终转化执行解决依赖
  3. 能够扩大

齐全体:webpack 为外围的工程化 + MVVM 框架组件化 + 设计模式

总结:

  1. 无模块化 -> IIFE => 从无到有的过程
  2. IIFE -> CommonJS => 从代码层到框架层
  3. CommonJS -> AMD => 解决了异步问题
  4. AMD -> CMD => 解决了按需加载的问题

正文完
 0