浅谈前端工程化之模块化开发

挽枫 878 2022-04-23

前言

本篇文章只是浅聊一下commonjs和esm的使用、不涉及原理讲解相关,高手请划过。

那些年,没有模块化开发的日子

早期在做多人协同开发的时候,各自写各自的脚本文件,脚本文件里的变量名也是各自维护编写,这就带来一个问题,有可能你的脚本里可能声明了别人使用的变量名,这就是臭名昭著的变量污染(全局污染)。当然,使用匿名函数自执行的方式形成独立的块级作用域能解决这类问题,但是随着业务的复杂,我们在实际开发中遇到的情况可能会更加复杂,所以模块化开发标准诞生了。当然催生模块化开发标准诞生的条件远不止全局污染这一个问题,还有依赖的管理问题。

当前的模块化标准

由于javascript本身的一些问题,造成了今日它模块化标准的百花齐放,百家争鸣。关于javascript的模块化标准,主要有如下这几个:commonjs、amd、umd、system、esm(es6/es2015、es2020、es2022、esnext)、node12、nodenext。

CommonJs标准

CommonJs是最早提出的模块化标准解决方案,nodejs借鉴CommonJs的规范,实现了很好的模块化管理。目前CommonJs主要用在以下几个场景:

  • Node是CommonJs在服务端的具有代表性的实现。
  • Browserify是CommonJs在浏览器的实现。
  • webpack使得前端应用可以在编译前使用CommonJs标准的库进行开发。

commonjs模块的导出和导入

// add.js
const add = (a,b)=>{
    return a+b;
}
module.exports = {
    add,
    newAdd:add  // 导出时重命名
}

// index.js
const { add, newAdd } = require("./add");
console.log("add", add(1, 2)); // 3
console.log("newAdd", newAdd(1, 2)); // 3

从外部导入一个模块并重新导出

// add.js
const add = (a,b)=>{
    return a+b;
}
module.exports = {
    add,
    newAdd:add  // 导出时重命名
}

// util.js
const addModule = require("./add");
module.exports={
    ...addModule
}

// index.js
const { add, newAdd } = require("./util");
console.log("add", add(1, 2));
console.log("newAdd", newAdd(1, 2));

使用exports导出一个commonjs模块

const printf = (text)=>{console.log(text)};
const text = 'Hello CommonJs';
// 错误导出,why?请等待下期文章出来
exports = {
printf,
text
}

//正确导出
exports.printf = printf;
exports.text = text;

注意事项,如果在commonjs模块中使用了exports导出属性就无法再使用module.exports默认导出了,如果使用了将会覆盖exports导出的属性
let a = 'a';
let b = 'b';
exports.a = a;
exports.b = b;

module.exports = { }; // 那么最终被导出去的是{ }

ESM标准

ESM是ECMAScript Module的缩写。ES6就是ESM的实现,从ES6开始javascript才真正意义上有了自己的模块化标准。

使用export导出一个ES模块

// hello.js
export const text = "Hello ESM!";

使用import导入一个ES模块

// 如果使用的是export的导出,则导入时需要被解构
import {text} from "./hello.js";
console.log(text);//"Hello ESM!"

使用export default导出一个ES模块

// hello.js
const text = "Hello ESM!";
export default text;

导入一个由export default导出的ES模块

imoprt text from './hello.js';
console.log(text);

导出时重命名

// util.js
const add = (a, b) => {
  return a + b;
};
export { add as Add };

// index.js
import { Add } from "./util.js";
console.log("add", Add(1, 2));

引入一个外部模块重新导出

// add.js
const add = (a, b) => {
  return a + b;
};
export { add as Add };
export default 'add';

// sub.js
const sub = (a, b) => {
    return a - b;
  };
export { sub as Sub };

// util.js
export * from "./add.js";
export * from "./sub.js";
export { Add as newAdd } from "./add.js";
export { default as str } from "./add.js";

// index.js
import { Add, newAdd, str } from "./util.js";
console.log("add", Add(1, 2)); // 3
console.log("newAdd",newAdd(1,2)); //3
console.log("str",str); // add



CommonJs与ESM导入导出的关键字表

关键字 CommonJs ESM
导出 exports、module.exports export、export default
导入 require(xx) import xx from xx

CommonJs里的require和ESM的import的区别(可选读)

require与import的区别