CJS模块化 和 ES6模块化 (详细教程)

114 阅读6分钟

1. 模块化概述

1.1. 什么是模块化?

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

1.2. 为什么需要模块化

随着应用的复杂度越高, 我们的其代码和文件数量都会急剧增加, 会逐渐引发以下问题:

  1. 全局污染问题
  2. 依赖混乱问题
  3. 数据安全问题

2. 有哪些模块化规范?

历史背景(了解即可):2009 年,随着 Node.js 的出现,JavaScript 在服务器端的应⽤逐渐增

多,为了让 Node.js 的代码更好维护,就必须要制定⼀种 Node.js 环境下的模块化规范,来⾃

Mozilla 的⼯程师 Kevin Dangoor 提出了 CommonJS 规范(CommonJS 初期的名字叫

ServerJS),随后 Node.js 社区采纳了这⼀规范。

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

分别为:

1.png

3. 导⼊与导出的概念

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

  • 导出(暴露):模块公开其内部的⼀部分(如变量、函数等),使这些内容可以被其他模块使⽤。

  • 导⼊(引⼊):模块引⼊和使⽤其他模块导出的内容,以重⽤代码和功能

2.png

4. CommonJS 规范

4.1 初步体验

  • 1.「创建 school.js」

    const name = 'NFIT'
    
    const slogan = '珠海欢迎您'
    
    function getTel() {
        return '101-56133123'
    }
    
    function getCities() {
        return ['北京','上海','深圳','成都','武汉','⻄安']
    }
    
    // 通过给exports对象添加属性的⽅式,来导出数据(注意:此处没有导出getCities)
    exports.name = name
    exports.slogan = slogan
    exports.getTel = getTel
    
  • 2.「创建 student.js」

    const name = '张三'
    const motto = '相信明天会更好!'
    function getTel (){
     return '13877889900'
    }
    function getHobby(){
     return ['抽烟','喝酒','烫头']
    }
    // 通过给exports对象添加属性的⽅式,来导出数据(注意:此处没有导出getHobby)
    exports.name = name
    exports.slogan = slogan
    exports.getTel = getTel
    
  • 3.「创建 index.js」

    // 引⼊school模块暴露的所有内容
    const school = require('./school')
    // 引⼊student模块暴露的所有内容
    const student = require('./student')
    

4.2 导出数据

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

  • 第⼀种⽅式: module.exports = value

  • 第⼆种⽅式: exports.name = value

注意点如下:

  1. 每个模块内部的: this 、 exports 、 modules.exports 在初始时,都指向同⼀ 个空对象,该空对象就是当前模块导出的数据,如下图:

3.png

  1. ⽆论如何修改导出对象,最终导出的都是 module.exports 的值。

  2. exports 是对 module.exports 的初始引⽤,仅为了⽅便给导出象添加属性,所以

不能使⽤ exports = value 的形式导出数据,但是可以使⽤ module.exports

= xxxx 导出数据。

4.3. 导⼊数据

CJS模块化标准中,使⽤内置的 require 函数进⾏导⼊数据

// 直接引⼊模块
const school = require('./school')
// 引⼊同时解构出要⽤的数据
const { name, slogan, getTel } = require('./school')
// 引⼊同时解构+重命名
const {name:stuName,motto,getTel:stuTel} = require('./student')

4.4 扩展理解

⼀个 JS 模块在执⾏时,是被包裹在⼀个内置函数中执⾏的,所以每个模块都有⾃⼰的作⽤域,我

们可以通过如下⽅式验证这⼀说法:

console.log(arguments)
console.log(arguments.callee.toString())

内置函数的⼤致形式如下:

function (exports, require, module, __filename, __dirname){
 /*********************/
}

4.5 浏览器端运行

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

  • 第⼀步:全局安装 browserify : npm 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 初步体验

  • 1.「创建 school.js」

    // 导出name
    export let name = {str:'NFIT'}
    // 导出slogan
    export const slogan = '欢迎来到广东!'
    // 导出name
    export function getTel (){
     return '010-56253825'
    }
    function getCities(){
     return ['北京','上海','深圳','成都','武汉','⻄安']
    
  • 2.「创建 student.js」

    // 导出name
    export const name = '张三'
    // 导出motto
    export const motto = '相信明天会更好!'
    // 导出getTel
    export function getTel (){
     	return '13877889900'
    }
    function getHobby(){
    	return ['抽烟','喝酒','烫头']
    }
    
  • 3.「创建 index.js」

    // 引⼊school模块暴露的所有内容
    import * as school from './school.js'
    // 引⼊student模块暴露的所有内容
    import * as student from './student.js'
    
  • 4.「⻚⾯中引⼊ index.js」

    <script type="module" src="./index.js"></script>
    

5.2. Node 中运⾏ ES6 模块

Node.js 中运⾏ ES6 模块代码有两种⽅式:

  • ⽅式⼀:将 JavaScript ⽂件后缀从 .js 改为.mjsNode 则会⾃动识别ES6模块。

  • ⽅式⼆:在 package.json 中设置 type 属性值为 module

5.3. 导出数据

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

  • 1.「分别导出」

    备注:在上⽅【初步体验中】环节,我们使⽤的导出⽅式就是【分别导出】

    // 导出name
    export let name = {str:'NFIT'}
    // 导出slogan
    export const slogan = '珠海欢迎你!'
    // 导出getTel
    export function getTel (){
     return '010-56253825'
    }
    
  • 2.「统⼀导出」

    // 导出name
    export let name = {str:'NFIT'}
    // 导出slogan
    export const slogan = '珠海欢迎你!'
    function getTel (){
     return '010-56253825'
    }
    function getCities(){
     return ['北京','上海','深圳','成都','武汉','⻄安']
    }
    // 统⼀导出了:name,slogan,getTel
    export {name,slogan,getTel}
    
  • 3.「默认导出」

    const name = '张三'
    const motto = '⾛⾃⼰的路,让别⼈五路可⾛!'
    function getTel (){
     return '13877889900'
    }
    function getHobby(){
     return ['抽烟','喝酒','烫头']
    }
    //默认导出:name,motto,getTel
    export default {name,motto,getTel}
    

    备注 :「上述多种导出⽅式,可以同时使⽤」

    // 导出name ———— 分别导出
    export let name = {str:'NFIT'}
    // 导出slogan
    export const slogan = '珠海欢迎你!
    function getTel (){
     return '010-56253825'
    }
    function getCities(){
     return ['北京','上海','深圳','成都','武汉','⻄安']
    }
    // 导出slogan ———— 统⼀导出
    export {slogan}
    // 导出getTel ———— 默认导出
    export default getTel
    

5.4. 导⼊数据

对于 ES6 模块化来说,使⽤何种导⼊⽅式,要根据导出⽅式决定。

  • 1.「导⼊全部」(通⽤)

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

    import * as school from './school.js'
    
  • 2.「命名导⼊」(对应导出⽅式:分别导出、统⼀导出)

    导出数据的模块

    // 导出name ———— 分别导出
    export let name = {str:'NFIT'}
    // 导出slogan
    export const slogan = '珠海欢迎你!
    function getTel (){
     return '010-56253825'
    }
    function getCities(){
     return ['北京','上海','深圳','成都','武汉','⻄安']
    }
    //统⼀导出
    export { getTel }
    

    命名导⼊:

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

    通过 as 重命名:

    import { name as myName,slogan,getTel } from './school.js'
    
  • 3.「默认导⼊」(对应导出⽅式:默认导出)

    导出数据的模块

    const name = '张三'
    const motto = '⾛⾃⼰的路,让别⼈五路可⾛!'
    function getTel (){
     return '13877889900'
    }
    function getHobby(){
     return ['抽烟','喝酒','烫头']
    }
    //使⽤默认导出的⽅式,导出⼀个对象,对象中包含着数据
    export default { name,motto,getTel }
    

    默认导⼊:

    import student from './student.js' //默认导出的名字可以修改,不是必须为student
    
  • 4.「命名导⼊默认导⼊可以混合使⽤」

    导出数据的模块

    // 导出name ———— 分别导出
    export let name = {str:'NFIT'}
    // 导出slogan
    export const slogan = '珠海欢迎你!
    function getTel (){
     return '010-56253825'
    }
    function getCities(){
     return ['北京','上海','深圳','成都','武汉','⻄安']
    }
    //统⼀导出
    export default getTel
    

    「命名导⼊」与「默认导⼊」混合使⽤,且默认导⼊的内容必须放在前⽅:

    import getTel,{name,slogan} from './school.js
    
  • 5.「动态导⼊」(通⽤)

    允许在运⾏时按需加载模块,返回值是⼀个 Promise。

    const school = await import('./school.js');
    console.log(school)
    
  • import 可以不接收任何数据

    例如只是让 mock.js 参与运⾏

    import './mock.js
    

此时,我们感受到模块化确实解决了:①全局污染问题、②依赖混乱问题、③数据安全问题。

使⽤原则:导出的常量,务必⽤ const 定义