前端工程化实战-模块化开发与规范标准-02-02-01模块化开发

模块化开发

模块化开发时当前最重要的前端开发范式之一 模块化只是思想

1.模块化演变过程

Stage1 文件划分方式

  • 污染全局作用域
  • 命名冲突问题
  • 无法管理模块依赖
  • 早起模块化完全依靠约定

Stage2 命名空间方式

  • 每个模块只暴露一个全局对象,所有模块都挂载到这个对象上
  • 减少了命名冲突的可能
  • 但是没有私有空间,模块成员可以在外部被访问或修改
  • 模块之间的依赖关系没有得到解决

Stage3 IIFE 立即执行函数

  • 使用立即执行函数包裹代码,要输出的遍历挂载到一个全局对象上
  • 变量拥有了私有空间,只有通过闭包修改和访问变量
  • 参数作为依赖声明去使用,使得每个模块的依赖关系变得明显

2.模块化规范

模块加载不受代码的控制,通过require的方式,但引入和移除会有问题

CommonJS规范

  • nodejs中提出的规范
  • 一个文件就是一个模块
    -每个模块都有单独的作用域
  • 通过module.exports导出成员
  • 通过require函数载入模块
  • 问题:
    若在浏览器使用此规范,会有问题, CommonJS是以同步模式加载模块,
    nodejs启动时会加载模块,但浏览器端,每次页面加载都会有大量同步请求的接口,会是前端效率低下

AMD(Asynchronous Module Definition)异步模块规范

  • 模块加载器:Require.js
// 定义一个模块
define('module1', ['jquery', './module2'], function ($, module2) {
  return {
    start: function () {
      $('body').animate({ margin: '200px' })
      module2()
    }
  }
})

// 载入一个模块
require(['./module1'], function (module1) {
  module1.start()
})

  • 目前绝大多数第三方库都支持AMD规范
  • AMD使用起来相对复杂
  • 模块JS文件请求频繁

淘宝推出的Sea.js + CMD(Common Module Definition)通用模块规范

// CMD 规范 (类似 CommonJS 规范)
define(function (require, exports, module) {
  // 通过 require 引入依赖
  var $ = require('jquery')
  // 通过 exports 或者 module.exports 对外暴露成员
  module.exports = function () {
    console.log('module 2~')
    $('body').append('<p>module2</p>')
  }
})

3.模块化标准规范

  • 在node.js中使用CommonJS
         CommonJS是node.js内置的模块化工具,只需要遵循CommonJS的标准即可,不需要引入别的依赖
  • 在浏览器中使用ES Modules
        ES Modules是ECMAScript2015

4.ES Modules

通过给 script 添加 type = module 的属性,就可以以 ES Module 的标准执行其中的 JS 代码

  • 自动采用严格模式,忽略’use strict’
  • 每个ESM模块都是单独的私有作用域
  • ESM是通过CORS去请求外部JS模块的
  • ESM的script标签会延迟脚本执行
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>ES Module - 模块的特性</title>
</head>
<body>
  <!-- 通过给 script 添加 type = module 的属性,就可以以 ES Module 的标准执行其中的 JS 代码了 -->
  <script type="module">
    console.log('this is es module')
  </script>

  <!-- 1. ESM 自动采用严格模式,忽略 'use strict' -->
  <script type="module">
    console.log(this)
  </script>

  <!-- 2. 每个 ES Module 都是运行在单独的私有作用域中 -->
  <script type="module">
    var foo = 100
    console.log(foo)
  </script>
  <script type="module">
    console.log(foo)
  </script>

  <!-- 3. ESM 是通过 CORS 的方式请求外部 JS 模块的 -->
  <!-- <script type="module" src="https://unpkg.com/jquery@3.4.1/dist/jquery.min.js"></script> -->

  <!-- 4. ESM 的 script 标签会延迟执行脚本 -->
  <script defer src="demo.js"></script>
  <p>需要显示的内容</p>
</body>
</html>

demo.js

alert('hello')

1.ES Module的导入导出

  • as 导出重命名
  • as 导出 default , 使用的时候必须使用 as 再进行重命名, 因为 default 是关键字
  • export default 默认导出
// ./modulejs
const foo = 'es modules'
export { foo }
// ./app.js
import { foo } from './module.js'
console.log(foo) // => es modules

2.ES Module 导入导出的注意事项

  • export { } 、import 不是导出一个对象字面量, import 也不是对对象的解构。 仅仅是一个固定用法 export { name, age }
  • export default { } 这样导出的才是一个对象字面量 export default { name, age }
  • 导出的是引用地址(即使它是个基本类型),而不是拷贝一个新的, 暴露出的引用关系是只读的(不能被修改)

3.ES Module 导入用法(import)

  • from 后面必须完整路径名,不能省略 .js 扩展名(CommonJS 可以省略)
import { name } from './module'  //错误
import { name } from './module.js' //正确
  • from 也不可以用 目录名, 然后找到目录下 index.js 的文件
import { lowercase } from './utils' //错误
import { lowercase } from './utils/index.js'//正确
  • 相对路径的./不可省略()
import { name } from 'module.js' //错误
import { name } from './module.js'//正确
import { name } from '/04-import/module.js'//正确
import { name } from 'http://localhost:3000/04-import/module.js'//正确
  • import { } from ‘./module.js’ 只是执行模块, 并不导出任何内容
    • 简写 import from ‘./module.js’
  • 提取全部成员 import * as xxx from ‘./modules’
  • import 只能出现在顶层作用域
  • 动态导入( import 函数 返回一个 promise )
import('./module.js').then(function (module) {
  console.log(module)
})
  • 提取默认成员和其它成员
import { name, age, default as title } from './module.js'
//简写写法
import abc, { name, age } from './module.js'

4.ES Module 导出导入成员

export   {  name , foo }  from './module.js'
export { default: Button } from './module.js'

5.ES Module 浏览器环境Polyfill

  • IE 和老的国产浏览器不支持
  • script 标签 有个 nomodule 属性, 会让script脚本在不支持module的浏览器上执行
  <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 nomodule src="https://unpkg.com/browser-es-module-loader@0.4.1/dist/browser-es-module-loader.js"></script>

6.ES Module in Node-支持情况

node version > 8.5 就可以在 node中 使用ES Module
使用方法:

// 第一,将文件的扩展名由 .js 改为 .mjs;
// 第二,启动时需要额外添加 `--experimental-modules` 参数;

import { foo, bar } from './module.mjs'

console.log(foo, bar)

// 此时我们也可以通过 esm 加载内置模块了
import fs from 'fs'
fs.writeFileSync('./foo.txt', 'es module working')

// 也可以直接提取模块内的成员,内置模块兼容了 ESM 的提取成员方式
import { writeFileSync } from 'fs'
writeFileSync('./bar.txt', 'es module working')

// 对于第三方的 NPM 模块也可以通过 esm 加载
import _ from 'lodash'
_.camelCase('ES Module')

// 不支持,因为第三方模块都是导出默认成员
// import { camelCase } from 'lodash'
// console.log(camelCase('ES Module'))

7.ES Module in Node-与CommonJS交互

  • ES Module 中可以导入 CommonJS 模块, 但是只能以导入默认成员的形式
  • 不允许 CommonJS 导入 ES Module
  • CommonJS 始终只会导出一个默认成员
  • 注意 import 不是解构导出对象

8.ES Module in Node-与CommonJS的差异

  • CommonJS 中 全局变量
    • require 、module 、 exports 、__filename 、 __dirname
  • ES Module 中 没有常用的5 个成员
    • import.meta.url === __filename = fileURLToPath(import.meta.url)
    • __dirname

9.ES Module in Node-新版本的进一步支持

  • Node v12 之后的版本,可以通过 package.json 中添加 type 字段为 module,将默认模块系统修改为 ES Module,此时就不需要修改文件扩展名为 .mjs 了
  • 如果需要在 type=module 的情况下继续使用 CommonJS,需要将文件扩展名修改为 .cjs

10.ES Module in Node-Babel兼容方案

  • 安装 @babel/core @babel/node
    @babel/plugin-transform-modules-commonjs
  • 新建 .bablerc 文件,配置如下
    .babelrc
{
  "plugins": [
    "@babel/plugin-transform-modules-commonjs"
  ]
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值