0%

JS模块化

什么是模块/模块化?

模块

  • 具有特定功能的 JS 文件

模块化

  • 将一个复杂的程序依据一定的规范封装成几个模块 (JS文件), 最后再组合在一起
  • 模块的内部数据/实现是私有的, 只是向外暴露一些接口(方法), 与外部其它模块进行通信

为什么要模块化?

  1. 降低程序复杂度
  2. 提高解耦性
  3. 部署方便 (按需引入)

模块化的好处

  1. 避免命名冲突
  2. 更好的分离, 按需加载
  3. 高复用性
  4. 高可维护性

页面加载引入 script

  • 请求过多
  • 依赖模糊
  • 难以维护

模块化规范

CommonJS

  • 说明

    • 每个文件都可以当作一个模块
    • 在服务端 —— 模块的加载是 运行时同步加载
    • 在浏览器端 —— 模块 需要提前编译打包 处理
  • 基本语法

    • 暴露模块
      • module.exports = value
      • exports.xxx = value
      • 暴露的是 module.exports 对象
    • 引入模块
      • require(xxx)
  • 实现

    • 服务端实现

      • Node.js
    • 浏览器端实现

      • Browserify

      • 也称为 CommonJS 的浏览器端的打包工具

        1
        2
        # 先处理 js 文件中不被浏览器识别的 require 语法
        browserify js/src/app.js -o js/dist/bundle.js
        1
        2
        // 在 html 中引入处理后的 js 文件
        <script src="./js/dist/bundle.js"></script>

AMD(requirejs)

  • Asynchronous Module Definition

    • 异步模块定义
    • 专门用于浏览器端, 模块的加载是 异步
  • 基本语法

    • 定义暴露模块 (通过 return 向外暴露)

      • 定义没有依赖的模块

        1
        2
        3
        define(function () {
        return 模块
        })
      • 定义有依赖的模块

        1
        2
        3
        define(['module1', 'module2'], function (m1, m2) {
        return 模块
        })
    • 引入使用模块

      1
      2
      3
      require(['module1', 'module2'], function (m1, m2) {
      使用 m1 / m2
      })
  • 实现

  • 用法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    // dataService.js
    define(function () {
    let msg = 'dataService'
    function getMsg() {
    return msg
    }
    // 向外暴露
    return { getMsg }
    })

    // alter.js
    define(['dataService', 'jquery'], function (dataService, $) {
    let msg = 'alert'
    function showMsg() {
    console.log(msg, dataService.getMsg())
    }
    $('body').css('background', 'pink')
    // 向外暴露
    return { showMsg }
    })

    // main.js
    (function () {
    // 配置
    requirejs.config = {
    baseUrl: 'js/', // 基于根路径去找下面引入的模块, 不写是基于当前文件去找
    paths: {
    dataService: './modules/dataService', // 不要加 .js
    alter: './modules/alter',
    jquery: './lib/jquery-1.10.0' // 只能小写, jQuery 自定义的模块名就是 jquery
    angular: './lib/angular'
    },
    shim: { // 针对 angular 的单独配置
    angular: {
    exports: 'angular'
    }
    }
    }

    requirejs(['alter', 'angular'], function (alter, angular) {
    alter.showMsg()
    console.log(angular)
    })
    })()
    1
    2
    <!-- 引入 require.js 并指定主文件的入口 -->
    <script data-main="js/main.js" src="js/require.js"></script>

CMD(seajs)

  • 说明
    • Common Module Definition
    • 通用模块规范
    • 专门用于 浏览器端, 模块的加载时异步的
    • 模块使用时才会加载执行
  • 实现(浏览器端)

ES6 模块化

  • 说明

  • 语法

    • 导出模块 - export
    • 引入模块 - import
  • 如何实现(浏览器端)?

    • 先将使用 Babel 将 ES6 编译为 ES5 代码
    • 再使用 Browerify 编译打包 js

ES6 模块化的使用

  1. 定义 package.json 文件

    1
    2
    3
    4
    {
    "name": "es6_babel-browerify",
    "version": "1.0.0"
    }
  2. 安装 @babel/cli、@babel/preset-env、@babel/core

    1
    2
    npm install --save-dev @babel/core @babel/cli @babel/preset-env
    npm install --save @babel/polyfill
  3. 在根目录下定义 babel.config.json 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    {
    "presets": [
    [
    "@babel/env",
    {
    "targets": {
    "edge": "17",
    "firefox": "60",
    "chrome": "67",
    "safari": "11.1",
    },
    "useBuiltIns": "usage",
    }
    ]
    ]
    }
  4. 引入模块

  5. 编译

    1
    2
    npx babel js/src -d js/lib // ES6 编译为 ES5 (但包含 CommonJS 语法)
    browserify js/lib/app.js -o js/lib/bundle.js // 编译 js 文件

默认暴露和常规暴露

  • 常规暴露 export

    • export 可以写多次, import 的时候必须有 {}

    • 方式一

      1
      2
      3
      4
      5
      6
      7
      8
      export function foo () {
      ...
      }

      export var arr = [1, 2, 3]

      // 引入
      import {foo, arr} from ''
    • 方式二

      1
      2
      3
      4
      5
      6
      7
      8
      9
      function foo () {
      ...
      }
      var arr = [1, 2, 3]

      export { foo, arr }

      // 引入
      import {foo, arr} from ''
  • 默认暴露 export default

    • 可以暴露任意数据类型, 暴露的是什么数据, 接收到的就是什么数据

    • 只能写一次

      1
      2
      3
      4
      export default 任意类型

      // 引入
      import 变量名 from ''