【Webpack】模块打包 CommonJS和ES6 Module语法介绍

2023-12-28 16:53:34

模块之于程序,就如同细胞之于生物体,是具有特定功能的组成单元。不同的模块负责不同的工作,它们以某种方式联系在一起,共同保证程序的正常运转。

随着 JavaScript语言的发展,社区中产生了很多模块标准。在认识这些标准的同时,也要了解其背后的思想。例如,它为什么会有这个特性,或者为什么要这样去实现。这对我们自己编写模块也会有所帮助。

CommonJS

CommonJS是由JavaScript社区于2009年提出的包含模块文件IO控制台在内的一系列标准。在Nodejs的实现中采用了CommonJS标准的一部分,并在其基础上进行了一些调整。我们所说的CommonJS块和Nodejs中的实现并不完全一样,现在一般谈到CommonJS其实是Nodejs中的版本而非它的原始定义。

模块
CommonJS中规定每个文件是一个模块。CommonJS会形成一个属于模块自身的作用域,所有的变量及函数只有自己能访问,对外是不可见的。

// calculator.js
var name ='calculator.js';

// index.js
var name ='index.js';
require('./calculator.js');
Console.1og(name); // index.js

这里有两个文件,在index;js中我们通过CommonJS的require函数加载calculatorjs。运行之后控制台结果是“indexjs”,这说明calculator;js中的变量声明并不会影响indexjs可见每个模块是拥有各自的作用域的。

导出
导出是一个模块向外暴露自身的唯一方式。在CommonJS中,通过module exports可以导出模块中的内容,如:

module.exports = {
	name: 'calculator',
	add: function(a,b){
		return a+b
	}
}

CommonJS模块内部会有一个module对象用于存放当前模块的信息,可以理解成在每个模块的最开始定义了以下对象:

var module =(...);
//棋块自身逻辑
module.exports =(...);

module.exports用来指定该模块要对外暴露哪些内容,在上面的代码中我们导出了一个对象,包含name和add两个属性。为了书写方便,CommonJS 也支持另一种简化的导出方式一直接使用exports

exports.name = 'calculator';
exports.add = function(a,b){
		return a+b
	}

在实现效果上,这段代码和上面的 module.exports 没有任何不同。其内在机制是将exports指向了module.exports

在使用exports时要注意一个问题,即不要直接给exports赋值,否则会导致其失效。

exports= {
	name:'calculator'
};

上而代码中,由于对exports进行了赋值操作,使其指向了新的对象,module.exports却仍然是原来的空对象,因此name属性并不会被导出。

另一个在导出时容易犯的错误是不恰当地把module.exports与exports混用。要用只能用一个

上面的代码先通过exports导出了add属性,然后将moduleexports重新赋值为另外一个对象。这会导致原本拥有add属性的对象丢失了,最后导出的只有name

另外,要注意导出语句不代表模块的末尾,在module.exports或exports后面的代码依旧会照常执行。

导入
在CommonJS中使用require进行模块导人

//calculator.js
module.exports = {
	add: functiona,b) (return a + b;)
};

//index.js
const calculator = require(',/calculator.js');
const sum = calculator.add(23);
console.log(sum);// 5

我们在indexjs中导人了calculator模块,并调用了它的add函数。
当我们require一个模块时会有两种情况:

  1. require 的模块是第一次被加载。这时会首先执行该模块,然后导出内容。
  2. require的模块曾被加载过。这时该模块的代码不会再次执行,而是直接导出上次执行后得到的结果。

模块会有一个module对象用来存放其信息,这个对象中有一个属性loaded 用于记录该模块是否被加载过。它的值默认为false,当模块第一次被执行过后会置为true,后面再次加载时检查到module.loaded为true,则不会再次执行模块代码

有时我们加载一个模块,不需要获取其导出的内容,只是想要通过执行它而产生某种作用,比如把它的接口挂在全局对象上,此时直接使用require 即可。

require('./task.js');

另外,require函数可以接收表达式,借助这个特性我们可以动态地指定模块加载路径

const moduleName = ['foo.js','bar.js']
moduleNames.forEach(name=>{
	require('./' + name);
})

ES6 Module

模块
请看下面的例子,我们将前面的 calculator.js 和index.js 使用ES6的方式进行了改写。

// calculator.js
export default{
	name:'calculator',
	add: function(a,b){
		return a+b
	}
}

// index.js
import calculator from './calculator.js'
const sum = calculator.add(2,3)
console.log(sum); // 5

ES6Module也是将每个文件作为一个模块,每个模块拥有自身的作用域,不同的是导人、导出语句。import和export也作为保留关键字在ES6版本中加入了进来

ES6 Module会自动采用严格模式,如果将原本是CommonJS的模块或任何未开启严格模式的代码改写为ES6Module要注意这点。

导出
在ES6Module中使用export命令来导出模块。export有两种形式:

  1. 命名导出
  2. 默认导出

一个模块可以有多个命名导出。它有两种不同的写法:

// 写法1
export const name = 'calculator'
export const add = function(a,b){return a+b}

// 写法2
const name = 'calculator';
const add = function(a,b){return a+b}
export {name, add}

上面两种写法的效果是一样的

在使用命名导出时,可以通过as 关键字对变量重命名。如:

const name = 'calculator';
const add = function(a,b){return a+b}
export {name, add as getSum }; //在导入时即为 name 和getsum类

与命名导出不同,模块的默认导出只能有一个。如:

export default {
	name: 'calculator',
	add: function(a,b){
		return a+b
	}
}

我们可以将export default理解为对外输出了一个名为deault的变量,因此不需像命名导出一样进行变量声明,直接导出值即可。

导入
ES6 Module中使用import语法导人模块, 先看下面例子

//calculator.js
const name= 'calculator';
const add = function(a, b) ( return a + b;);
export ( name.add );

// index.js
import {name, add} from './calculator.js'
add(2,3);

加载带有命名导出的模块时,import 后面要跟一对大括号来将导人的变量名包裹来,并且这些变量名需要与导出的变量名完全一致。导人变量的效果相当于在当前作用域下声明了这些变量(name和add),并且不可对其进行更改,也就是所有导人的变都是只读的

与命名导出类似,我们可以通过as 关键字可以对导人的变量重命名

import {name, add as calculateSum } from './calculator.js'
calculateSum(2,3)

在导人多个变量时,我们还可以采用整体导人的方式。如:都

import * as calculator from './calculator.js'
console.log(calculator.add(23));
console.log(calculator.name);

接下来处理默认导出,请看下面这个例子:

// calculator.js
export default{
	name: 'calculator',
	add: function(a,b){return a+b}
}

// index.js
import myCalculator from './calculator.js'
calculator.add(2,3)

对于默认导出来说,import后面直接跟变量名,并且这个名字可以自由指定(比如这里是myCalculator),它指代了calculator.js中默认导出的值。从原理上可以这样去理解:

import ( default as myCalculator ) from './calculator;js';

最后看一个两种导人方式混合起来的例子:

// index.js
import React, {Component} from 'react'

这里的React对应的是该模块的默认导出,而Component 则是其命名导出中的一个变量
这里的 React 必须写在大括号前面,而不能顺序颠倒,否则会提示语法错误。

复合写法
在工程中,有时需要把某上个模块导人之后立即导出,比如专门用来集合所有页面或组件的人口文件。此时可以采用复合形式的写法:

export ( name,add ) from  ./calculator.js';

复合写法目前只支持当被导入模块 (这里的 calculator.js)通过命名导出的方式暴露出来的变量

默认导出则没有对应的复合形式,只能将导人和导出拆开写。

import calculator from ",/calculator.js ";
export default calculator;

文章来源:https://blog.csdn.net/loyd3/article/details/135242948
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。