CommonJS 与 ES6 模块化梳理

146 阅读4分钟

1. 模块化概述

1.1 什么是模块化?

  • 将程序文件依据一定规则拆分成多个文件,这种编码方式就是模块化的编码方式。
  • 拆分出来每个文件就是一个模块,模块中的数据都是私有的,模块之间互相隔离
  • 同时也能通过一些手段,可以把模块内的指定数据“交出去”,供其他模块使用。

2. 有哪些模块化规范?

随着时间的推移,针对 JavaScript 的不同运行环境,相继出现了多种模块化规范,按时间排序,分别为:

  1. CommonJS ----- 服务端应用广泛
  2. AMD(不常用)
  3. CMD(不常用)
  4. ES6 模块化 ------ 浏览器端应用广泛

3. 导入导出的概念

模块化的核心思想就是:模块之间是隔离的,通过导入和导出进行数据和功能的共享。

  • 导出:模块公开其内部的一部分(如变量,函数等),使这些内容可以被其他模块使用。
  • 导入:模块引入和使用其他模块导出的内容,以重用代码和功能。

4. CommonJS 规范

4.1 导出数据的两种方式

在 CommonJS 标准中,导出数据有两种方式:

  • 第一种方式:module.exports = value
  • 第二种方式:exports.name = value

注意点如下:

  1. 每个模块内部的:thisexportsmodules.exports 在初始时,都指向同一个空对象,该空对象就是当前模块导出的数据,如下图: image.png
  2. 无论如何修改导出对象,最终导出的都是 modules.exports 的值。
  3. exports 是对 modules.exports 的初始引用,仅为了方便给导出对象添加属性,所以不能使用 exports = value 的形式导出数据,但是可以使用 modules.exports = xxx 导出数据。
school.js

const name = 'zhangsan'
const motto = '明天会更好'

function getTel (){
    return '13877889900'
}

module.exports = { name, motto, getTel }

4.2 导入数据

CJS 模块化标准中,使用内置的 require 函数进行导入数据

// 直接引入模块
const school = require('./school')

// 引入同时解构出要用的数据
const { name, motto, getTel } = require('./school')

// 引入同时解构 + 重命名
const { name: schName, motto, getTel: schTel } = require('./school')

4.3 扩展理解

一个 JS 模块在执行时,是被包裹在一个内置函数中执行的,所以每个模块都有自己的作用域。

4.4 浏览器端运行

Nodejs 默认是支持 CommonJS 规范的,但浏览器端不支持,所以需要经过编译,步骤如下:

  • 第一步:全局安装 browserifynpm i browserify -g
  • 第二步:编译 browserify index.js -o build.js (index.js 是源文件,build.js 是输出的目标文件)
  • 第三步:页面中引入使用
    <script type='text/javascript' src="./build.js"></script>
    

5. ES6 模块化规范

ES6 模块化规范是一个官方标准的规范,是目前最流行的模块化规范,且浏览器与服务器端均支持该规范。

5.1 node 中运行 ES6 模块

  • 第一种:将.js文件后缀名改为.mjs
  • 第二种:创建 package.json 文件
    // package.json
    {
        "type": "module"
    }
    

5.2 导出数据

ES6 模块化提供 3 种导出方式:①分别导出、②统一导出、③默认导出

分别导出

    export const name =  'zhangsan'
    export const motto = '明天会更好'
    
    export function getTel(){
        return '010-1234567'
    }

统一导出

    const name =  'zhangsan'
    const motto = '明天会更好'
    
    function getTel(){
        return '010-1234567'
    }
    
    export { name, motto, getTel }

默认导出

// m1.js
function getTel(){
   return '010-1234567'
}
// 使用 `export default` 关键字进行默认暴露
export default getTel;

5.3 导入数据

对于 ES6 模块化来说,使用何种导入方式,要根据导出方式决定。

导入全部(通用)

可以将模块中的所有导出内容整合到一个对象中。

    import * as school from './school.js'

命名导入(对应导出方式:分别导出、统一导出)

import { name, motto, getTel } from './school.js'

默认导入 (对应导出方式:默认导出)

如果是默认的导出,导入时可以拿任意变量接收,导出的是什么,收的就是什么

import school from './school.js'

命名导入 和 默认导入 可以混合使用

// 导出 school.js
export const name =  'zhangsan'
const motto = '明天会更好'

function getTel(){
    return '010-1234567'
}

export { motto }

export default getTel
// 导入

import getTel, {name, motto} from './school.js'

动态导入

// 导出 school.js
const name =  'zhangsan'
const motto = '明天会更好'

function getTel(){
    return '010-1234567'
}

export { name, motto, getTel }
    const btn = document.getElementById('btn')
    
    btn.onclick = async() => {
        const result = await import('./school.js')
        console.log(result) // { getTel:function.., motto: '明天会更好', name =  'zhangsan' }
    }

import 可以不接收任何数据

// test.js 只是打印一个随机数
console.log(Math.random())

场景:只是想让 test.js 参与代码运行

// index.js
import './test.js