模块化开发

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

模块化演变过程

Stage1 文件划分方式
  • 污染全局作用域
  • 命名冲突问题
  • 无法管理模块依赖
  • 早期模块化完全依靠约定
Stage2 命名空间方式
  • 每个模块只暴露一个全局对象,所有模块都挂载到这个对象上
  • 减少了命名冲突的可能
  • 但是没有私有空间,模块成员可以在外部被访问或修改
  • 模块之间的依赖关系没有得到解决
Stage3 IIFE 立即执行函数
  • 使用立即执行函数包裹代码,要输出的遍历挂载到一个全局对象上
  • 变量拥有了私有空间,只有通过闭包修改和访问变量
  • 参数作为依赖声明去使用,使得每个模块的依赖关系变得明显

模块化规范

1. CommonJS规范
  • 一个文件就是一个模块
  • 每个模块都有单独的作用域
  • 通过module.exports导出成员
  • 通过require函数载入模块
  • CommonJS是以同步模式加载模块
2. 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文件请求频繁

3. 淘宝推出的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>')
  }
})
4. ES Module
// ./modulejs
const foo = 'es modules'
export { foo }
// ./app.js
import { foo } from './module.js'
console.log(foo) // => es modules
  • 自动采用严格模式,忽略’use strict’
  • 每个ESM模块都是单独的私有作用域
  • ESM是通过CORS去请求外部JS模块的
  • ESM的script标签会延迟脚本执行

ESModules

import用法
// 不能省略.js拓展名 CommonJS可以
// CommonJS载入目录就可以载入目录下面的index.js ESM不可以必须完整路径
import { name } from "./module.js";
console.log(name);
// 后期打包操作可以省略拓展名和默认文件是index.js这种操作
import { lowercase } from "./util/index.js";
console.log(lowercase);
// 必须以点开头 和CommonJS相同
// 字母开头 ESM人为加载第三方模块
// 斜线开头 根目录往后数
import { name } from "/04-import/module.js";
console.log(name);
// 完整的URL
import { name } from "http://localhost:52330/04-import/module.js";
// 可以直接引用CDN上的模块文件
console.log(name);

// ---------------------------------------------------------
import {} from "http://localhost:52330/04-import/module.js";
// 简写
import "http://localhost:52330/04-import/module.js";
// 不会重复加载只能加载一次
// ---------------------------------------------------------
// 导出的非常的多
import * as mod from "./module.js";
console.log(mod);
// 于是实现了导入成员
// mod.age = 19;
// 依旧是不可以更改 nice
// console.log(mod);
// ---------------------------------------------------------
var modulePath = "./module.js";
import { age } from modulePath
// Uncaught SyntaxError: Unexpected identifier
console.log(age);

if (true) {
  import { age } from "./module.js";
}
Uncaught SyntaxError: Unexpected token '{'

// 如何动态导入? import()函数 返回一个promise

import("./module.js").then((module) => {
  console.log(module.name);
});

import { age, default as chris } from "./module.js";
console.log(age, chris);
// 简写
import chris, { age } from "./module.js";
console.log(age, chris);

导出的同时引入(中间商)

// import { button } from "./button.js";
// import { avatar } from "./avatar.js";

// export { button, avatar };
// 注意 如果是default需要重命名 否则会当成index自己的默认导出
export { button } from "./button.js";
export { avatar } from "./avatar.js";
Polyfill

browser-es-module-loader

  • 将浏览器中不识别的ESM去交给babel转换

  • 需要import的文件通过ajax请求

    • 请求回来的代码再次通过babel转换
  • 支持的话polyfill会重复执行两次

  • script nomodule

  • 开发可以 生产慎用 动态解析脚本 效率低

  • 应该编译出来直接可以再浏览器中工作

<script src="https://unpkg.com/browse/browser-es-module-loader@0.4.1/dist/babel-browser-build.js"></script>
<script src="https://unpkg.com/browse/browser-es-module-loader@0.4.1/dist/browser-es-module-loader.js"></script>

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>polyfill</title>
  </head>
  <body>
    <!-- IE不兼容 -->
    <!-- 编译工具 编译成ES5就行了 -->
    <!-- polyfill直接支持浏览器中的大部分ESM特性 -->
    <script
      nomodule
      src="https://unpkg.com/browse/browser-es-module-loader@0.4.1/dist/babel-browser-build.js"
    ></script>
    <script
      nomodule
      src="https://unpkg.com/browse/browser-es-module-loader@0.4.1/dist/browser-es-module-loader.js"
    ></script>
    <!-- Promise未定义 -->
    <!-- 引入promise ployfill -->
    <script
      nomodule
      src="https://unpkg.com/promise-polyfill@8.1.3/dist/polyfill.min.js"
    ></script>
    <script type="module">
      import { name } from "./module.js";
      console.log(name);
    </script>
    <script></script>
  </body>
</html>
导入导出

导出的形式:

  • 变量
  • 函数
  • default
  • 集中导出才需要花括号

导入导出的注意事项

  • 导入的是一个常量 => 无法修改变量

    • 导出的是个对象就可以修改
  • 花括号是规定,不是一个对象,所以不能直接解构

    • 但是导出的是对象的情况也可以使用,还可以解构
  • 导出的模块的变量更改,导入的模块也会更改

    • 但是如果导出的是一个对象就更改不了了

ESM in Nodejs

nodejs 原生支持ESM,直接用。历史上有段时间不支持,需要加 –experimental-modules

于CJS交互
  • ESM中可以载入CJS成员
  • exports.是module.exports的别名
  • esm载入cjs模块提取需要一个第三方的变量做个过渡+
  • node中不允许commonjs载入es module

和CJS区别

  • ESM中没有CJS中的全局模块成员了
// 导入导出的方式改变
require/module/exports => import/export 
// 获取文件名的方式改变
__filename  => import.meta.url + fileURLToPath = __filename

import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url);        
  • dirname => dirname(__filename )
import { fileURLToPath } from "url"; import { dirname } from "path"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename);               
  • 把当前模块包装成一个函数的形参(实现私有模块作用域)

其他

拓展名设置cjs就不需要设置mjs,设置的是mjs,就不需要cjs了。

.babelrc => ''presets":["@babel/preset-env"]

通过babel来支持ESM!

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值