uploadhub的技术博客 uploadhub的技术博客
首页
  • 学习笔记

    • 《HTML5和CSS3篇》
    • 《JavaScript基础篇》
    • 《JavaScript高级篇》
    • 《Ajax篇》
    • 《JavaScript模块化篇》
    • 《Node.js篇》
    • 《MongoDB篇》
    • 《Promise篇》
    • 《Git篇》
  • 《Vue2+Vue3篇》
  • 《React篇》
  • 一面-基础
  • 二三面-进阶
关于我
  • 分类
  • 标签
  • 归档

uploadhub

首页
  • 学习笔记

    • 《HTML5和CSS3篇》
    • 《JavaScript基础篇》
    • 《JavaScript高级篇》
    • 《Ajax篇》
    • 《JavaScript模块化篇》
    • 《Node.js篇》
    • 《MongoDB篇》
    • 《Promise篇》
    • 《Git篇》
  • 《Vue2+Vue3篇》
  • 《React篇》
  • 一面-基础
  • 二三面-进阶
关于我
  • 分类
  • 标签
  • 归档
  • 学习笔记

    • HTML5和CSS3篇
    • JavaScript基础篇
    • JavaScript高级篇
    • Ajax篇
    • JavaScript模块化篇
      • 1. 什么是模块
      • 2.各种方式的模块化
      • 3. 为什么要模块化
        • 3.1 模块化的好处
        • 3.2 潜在的问题(页面引入script)
      • 4. 模块化规范
        • 4.1 CommonJS
        • 基于服务器端(Node.js实现)
        • 基于浏览器端(Browserify实现)
        • 相关问题
        • 4.2 AMD
        • 基本语法
        • 使用
        • 4.3 CMD
        • 4.4 ES6模块化规范
        • 文档结构
        • 编译过程
    • Node.js篇
    • MongoDB篇
    • Promise篇
    • Git篇
  • 前端
  • 学习笔记
uploadhub
2022-03-23
目录

JavaScript模块化篇

# 1. 什么是模块

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

编码时是按照模块一个一个编码的, 整个项目就是一个模块化的项目

# 2.各种方式的模块化

1.全局function模式(原始写法)

  • 直接将需要声明的变量写在全局中
  • 缺点:污染全局,容易命名冲突/数据不安全
function foo1(){}
function foo2(){}
foo1()
foo2()
1
2
3
4

2.namespace方式

  • 将需要声明的变量封装到一个函数中
  • 解决:命名冲突,减少Global的变量数目
  • 缺点:本质是对象,外部可以直接修改模块内部的数据,不安全
let module = {
    foo1(){},
    foo2(){}
}
module.foo1()
module.foo2()
1
2
3
4
5
6

3. IIFE方式

  • 通过闭包的方式,只暴露想要暴露的数据或方法
  • 函数是唯一的local scope
let Module = (function module(){
    var __private = 'save now'
    function foo(){}
    return {foo: foo}
})()
Module.foo()
Module.__private    //undefined
1
2
3
4
5
6
7

4. 引入依赖

  • 通过给闭包函数引入参数的形式来传递依赖
  • 这就是模块模式,现代模块化的基石
let Module = (function module($){
    var __private = 'save now'
    $('#test').hide()
    function foo(){}
    return {foo: foo}
})($)
1
2
3
4
5
6

# 3. 为什么要模块化

  1. 现代网页更像一个web app
  2. 代码复杂度逐渐上升
  3. 需要高解耦性的js文件
  4. 需要部署一种最大优化http数量的网站

image-20220207122502870

# 3.1 模块化的好处

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

# 3.2 潜在的问题(页面引入script)

  1. 提高了http数量,降低网站性能
  2. 引入顺序改变会导致报错
  3. 依赖关系模糊
  4. 难以维护

image-20220207122415535

# 4. 模块化规范

# 4.1 CommonJS

  1. 每个文件都可以当做一个模块
  2. 在服务器端: 模块的加载是运行时同步加载的
  3. 在浏览器端: 模块需要提前编译打包处理

# 基于服务器端(Node.js实现)

Node.js官网 (opens new window)

文件目录:

project
  |---modules
  |---node_modules
  |---app.js
  |---0package.json
1
2
3
4
5

基本语法

1.定义暴露模块 : exports

exports.xxx = value
module.exports = value
1
2

2.引入模块 : require

var module = require('模块名/模块相对路径')
1

# 基于浏览器端(Browserify实现)

  1. Browserify也称为CommonJS的浏览器端的打包工具
  2. 官网地址为Browserify (opens new window)

文件目录:

project
  |---js
      |---dist         打包后的文件目录
      |---src          源码所在目录
          |---modules
          |---app.js
  |---node_modules
  |---index.html
  |---package.json
1
2
3
4
5
6
7
8
9

使用Browserify打包,使用的命令顺序如下:

  1. npm install -g browserify
  2. npm install --save-dev browserify
  3. browerify src.js -o destination.js

# 相关问题

问:引入模块发生在什么时候?

  1. Node : 运行时, 动态同步引入
  2. Browserify : 在运行前对模块进行编译/转译/打包的处理(已经将依赖的模块包含进来了), 运行的是打包生成的js, 运行时不存在需要再从远程引入依赖模块

问:Node.js与Browserify的区别?

  1. Node.js运行时动态加载模块(同步)
  2. Browserify是在转译(编译)时就会加载打包(合并)require的模块

# 4.2 AMD

AMD规范官网 (opens new window)

  1. AMD全称Asynchronous Module Definition,译为“异步模块定义”
  2. 专门用于浏览器端,模块的加载是异步的
  3. 没有文件作用域,在define与require函数外部共用全局作用域

# 基本语法

定义没有依赖的模块

define(function(){})
1

定义有依赖的模块

define([module1, module2],function(module1, module2){    //显式声明,依赖注入
    return 模块对象       //将想要暴露的模块对象直接返回
})  
1
2
3

引入使用模块

require([module1, module2],function(module1, module2){
    //使用m1、m2等传过来的模块对象
})
1
2
3

# 使用

  1. 引入requirejs (opens new window)
<script data-main="./js/main.js" src="./js/libs/require.js"></script>
1
  1. 项目目录:
.js
  |---libs
      |---require.js
      |---angular.js
  |---modules
      |---module1.js
      |---module2.js
  |---main.js
|---index.html
1
2
3
4
5
6
7
8
9
  1. 使用requirejs
(function(){
    requirejs.config({
        baseUrl: 'js/',     //设置根目录
        paths:{
            module1: 'modules/module1', //后缀js会自动添加,所以不能手动加.js后缀
            module2: 'modules/module2',
            angular: 'libs/angular'
        },
        shim:{
            angular:{
                exports: 'angular'  //对于原生不支持AMD的将第三方对象暴露给window对象的第三方模块需要手动配置暴露的对象名
            }
        }
    })
    requirejs(['module1', 'module2', 'angular'], (m1, m2, angular)=>{})
})()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 4.3 CMD

  1. CMD全称Common Module Definition,译为“通用模块定义”
  2. CMD专门用于浏览器端,模块的加载是异步的
  3. 模块使用时才会加载执行

基本语法

  1. 下载sea.js, 并引入
  • 官网: http://seajs.org/
  • github : https://github.com/seajs/seajs
  • 将sea.js导入项目: js/libs/sea.js
  1. 创建项目结构
|-js
|-libs
  |-sea.js
|-modules
  |-module1.js
  |-module2.js
  |-module3.js
  |-module4.js
  |-main.js
|-index.html
1
2
3
4
5
6
7
8
9
10
  1. 定义sea.js的模块代码
  • module1.js

    define(function (require, exports, module) {
      //内部变量数据
      var data = 'atguigu.com'
      //内部函数
      function show() {
        console.log('module1 show() ' + data)
      }
    
      //向外暴露
      exports.show = show
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • module2.js

    define(function (require, exports, module) {
      module.exports = {
        msg: 'I Will Back'
      }
    })
    
    1
    2
    3
    4
    5
  • module3.js

    define(function (require, exports, module) {
      const API_KEY = 'abc123'
      exports.API_KEY = API_KEY
    })
    
    1
    2
    3
    4
  • module4.js

    define(function (require, exports, module) {
      //引入依赖模块(同步)
      var module2 = require('./module2')
    
      function show() {
        console.log('module4 show() ' + module2.msg)
      }
    
      exports.show = show
      //引入依赖模块(异步)
      require.async('./module3', function (m3) {
        console.log('异步引入依赖模块3  ' + m3.API_KEY)
      })
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  • main.js : 主(入口)模块

    define(function (require) {
      var m1 = require('./module1')
      var m4 = require('./module4')
      m1.show()
      m4.show()
    })
    
    1
    2
    3
    4
    5
    6
  1. index.html:
<!--
使用seajs:
1. 引入sea.js库
2. 如何定义导出模块 :
  define()
  exports
  module.exports
3. 如何依赖模块:
  require()
4. 如何使用模块:
  seajs.use()
-->
<script type="text/javascript" src="js/libs/sea.js"></script>
<script type="text/javascript">
seajs.use('./js/modules/main')
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

​

# 4.4 ES6模块化规范

  1. 依赖模块需要编译打包处理
  2. 浏览器端实现,需要先使用Babel将ES6编译为ES5代码,再使用Browserify编译打包js

# 文档结构

project
  |---js
      |---src
      |---build
      |---dist
  |---index.html
  |---.babelrc
1
2
3
4
5
6
7

# 编译过程

  1. 安装babel-cli babel-reset-es2015 browerify
npm install -g babel-cli browerify
npm install --save-dev babel-preset-es2015
1
2
  1. 在项目根目录自定义.babelrc文件
{
    "presets": ["es2015"]
}
1
2
3
  1. 编写es6模块化语法

常规暴露

  • 在src文件夹新建module1.js
  • export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系,因此单独暴露时不能只传递变量名
export function foo(){      //单独暴露
    console.log('我是module1')
}
/*
*export {foo}    //统一暴露
*
*/
1
2
3
4
5
6
7
  • 在src文件夹新建main.js
/*
*import foo from './module1.js'  //单独暴露时
*import {foo} from './module1.js'   //统一暴露时
*
*/
foo()   //'我是module1'
1
2
3
4
5
6

默认暴露

  • 在src文件夹新建module1.js
function foo(){
    console.log('我是module1')
}
/*
*export default foo         //引入时可以是任意声明变量来引入暴露出去的数据类型
*/
1
2
3
4
5
6
  • 在src文件夹新建main.js
/*
*import a from './module1.js'
*
*/
a()   //'我是module1'
1
2
3
4
5
  1. 在项目根目录使用babel将ES6语法转换成ES5语法
babel ./src -d ./build
1
  1. 在项目根目录使用browerify将ES6语法中的commonjs转换成普通ES5语法
browerify ./build/main.js -o ./dist/bundle.js
1
  1. 在index.html中引入最终编译好的js文件
<script src="./js/dist/bundle.js"></script>
1
#JavaScript#模块化
Ajax篇
Node.js篇

← Ajax篇 Node.js篇→

最近更新
01
HTTP协议及缓存机制
05-28
02
开发环境
05-28
03
JS基础知识(一)-变量类型和计算
05-28
更多文章>
Theme by Vdoing | Copyright © 2021-2023 uploadhub | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式