JavaScript模块化

104 阅读4分钟

模块的理解

1.将一个复杂的程序,依据一定的规则,拆分成单个文件,并最终组合在一起。
2.这些拆分的文件就是模块,模块内部的数据是私有的,只是向外部暴露一些方法与外部其他模块通信。

为什么要模块化?

  • 降低复杂度,提高解耦性
  • 避免命名冲突
  • 更好的分离,按需加载
  • 更高的复用性,更高的可维护性

现在流行的模块化规范有2种:CommonJS、ES6

CommonJS

CommonJS模块化的代码,既可以在服务器端(node)运行,也可以在浏览器端运行

  • 在服务器端的模块化代码可以直接运行
  • 在浏览器端的模块化代码要经过Browserify编译
  1. 暴露语法
  • 第一种方式:module.exports = value
  • 第二种方式:exports.xxx = value
  1. 引入语法
  • 引入第三方模块:require(xxx),xxx为模块名
  • 引入自定义模块:require(xxx),xxx为模块文件路径

exports和module.exports指向一个对象

image.png

module.exports暴露

module1暴露:暴露出去的是一个对象

const data = '123'
const msg = 'abc'

module.exports = {
    showData(){
        console.log(data);
    },
    showMsg(){
        console.log(msg);
    }
}

使用:因为暴露的是一个对象,所以a就是一个对象,使用对象里面的数据:对象名.属性就可以使用

//const module1 = require('./module1')

//引用的同时,解构赋值
const {showData,showMsg} = require('./module1')

module1.showData();
module1.showMsg();

exports暴露 module2使用exports.xxx = value去暴露,value是暴露的内容,xxx是它的名字

exports.sum = function(a,b){}

exports.sub = function(a,b){}

使用

const module2 = require('./module2')

//引入的同时,解构赋值
const {sum,sub} = require('./module2')

引入的内容是什么,取决于暴露的是什么

如果module.exports和exports.xxx产生引入冲突,那么优先执行module.exports的。也就是说module.exports和exports.xxx不能混用!

引入第三方模块 先npm下载第三方模块,然后直接导入模块名,比如:npm install uniq

const uniq = require('uniq')

上方所有的代码都是在node环境下运行的,都是在集成终端中:node app 命令来实现的

Commonjs浏览器端

CommonJS在浏览器端的模块化代码要经过Browserify编译。

比如说require()命令,就是node专有的命令,所以浏览器是执行不了这个命令的。

因此需要全局安装:npm i browserify -g

对引入后的最终模块进行Browserify编译:browserify _最终模块路径_ -o _指定路径_

-o:将编译后的最终模块,输出到指定模块,一般叫build.js

browserify ./app.js -o ./build.js

ES6模块化(用的最多)

ES6模块化规范是浏览器与服务器端通用的模块化开发规范。他的出现,极大地降低了前端开发者的模块化学习成本,开发者不再需要额外的学AMD、CMD、CommonJS等模块化规范。

  • 每个js文件都是一个独立的模块
  • 导入其他模块成员,使用import关键字
  • 对外共享模块成员使用export关键字

通过npm init -y快速初始化一个包管理初始化配置文件:package.json

在package.json中:"type":module,使用ES6模块化语法。

默认导出与默认导入

默认导出:export default 默认导出的成员

//定义模块私有成员
let n1 = 10
function show(){}

//默认导出,使用export default语法,向外共享n1和show
export default {
    n1,
    show
}

默认导入:import 接收名称 from '模块标识符'

import m1 from './01.js'

console.log(m1)//{n1:10,show:[Fcuntion:show]}

默认导出与默认导入的注意事项

  • 默认导出: 在每个模块中,【只允许使用唯一的一次】export default,否则会报错!!!

image.png

  • 默认导入:

默认导入时的【接收名称】必须是合法的成员名称。不能以数字开头!!!

//不合法!因为123接收名称以数字开头了
import 123 from './01.js'

按需导出与按需导入

按需导出的语法:export 按需导出的成员

//当前模块为02.js

export let s1 = 'aaa'

export function say(){}

按需导入的语法:import {解构赋值} from '地址'

import {s1,say} from './02.js'

console.log(s1)//aaa
console.log(say)//一个方法

按需导出与按需导入的注意事项

  • 每个模块中可以使用多次按需导出
  • 按需导入的成员名称必须和按需导出的名称一致
  • 按需导入时,可以用as关键字进行重命名
  • 按需导入可以和默认导入一起使用

按需导入的成员名称必须和按需导出的名称一致

export let s1 = 'aaa'

import {s1} from './02.js'

按需导入时,可以用as关键字进行重命名

export let s1 = 'aaa'

import {s1 as str1} from './02.js'

console.log(str1)//s1已经没了,必须使用它的别名

按需导入与默认导入配合

export let s1 = 'aaa'
export function say(){}
let info = 'test'

export default{
    info
}

import info,{s1,say} from '02.js'

直接导入并执行

如果只是单纯的执行某个模块中的代码,并不需要得到模块中向外共享的成员,此时可以直接导入(import)并执行模块代码

//当前文件模块./03.js

for(let i = 0;i<3;i++){
    console.log(i)
}

test.js

直接导入并执行模块代码,不需要得到模块向外共享的成员
import './03.js'