前端模块化(CMJ和ESM)

本文探讨了前端模块化的必要性,并详细介绍了CommonJS和ES6的模块化规范,包括export和import命令的使用,以及两者的区别和应用场景。重点讲解了export default的特性以及模块导入导出的规则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 为什么要模块化

  1. 把具有不同功能的版块封装为不同的模块,比如把涉及到网络请求的函数全都写在一个network.js文件里,把一些工具类的函数写在tool.js文件里,框架里的每一个组件都占据一个.vue文件,避免所有代码都写到一起,难以管理,同时还可以提高代码的复用率
  2. 通常一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数和类。

2. 模块化规范

目前流行的js模块化规范有CommonJS、AMD、CMD以及ES6的模块系统

1. CommonJs

  1. NodeJS采用CommonJS规范实现了模块系统
  2. CommonJS规范
  • CommonJS规范规定了如何定义一个模块, 如何暴露(导出)模块中的变量函数, 以及如何使用定义好的模块
    - 在CommonJS规范中一个文件就是一个模块
    - 在CommonJS规范中每个文件中的变量函数都是私有的,对其他文件不可见的
    - 在CommonJS规范中每个文件中的变量函数必须通过exports暴露(导出)之后其它文件才可以使用
    - 在CommonJS规范中想要使用其它文件暴露的变量函数必须通过require()导入模块才可以使用

2. Node模块中暴露数据的几种方法

  • exports xxx =(暴露出去一个对象)
    a.js

每一个node执行一个文件时,会给这个文件内生成一个exports和module对象

//在模块里面编写私有的变量和函数,通过exports暴露出去,记住,暴露出去的是一个对象
let name = 'mike'
function sum() {
    console.log(3);
}
exports.name = name
exports.sum = sum

b.js

let moduleA = require('./s')
console.log(moduleA);

在这里插入图片描述

  • module.exports.xxx
    a.js
let name = 'mike'
function sum() {
    console.log(3);
}
module.exports.name = name
module.exports.sum = sum

b.js

let moduleA = require('./a')
console.log(moduleA);

在这里插入图片描述

所以第一种和第二种方法到底有什么区别????
区别就是第二种方法可以不通过对象的属性赋值,可以直接赋值

let name = 'mike'
function sum() {
    console.log(3);
}

module.exports = name  //mike,可以直接赋值
exports = name //{}:不可以直接赋值
//此时如果再打印moduleA的话
//输出的就是上述注释的结果
  • 全局暴露(不推荐,不符合commonJs规范,私有+暴露+导入)
global.str = name;
global.sum = sum;

3. require的引入规则

  1. require导入模块时可以不添加导入模块的类型(模块的后缀名可以不写)
    如果没有指定导入模块的类型, 那么会按照查找顺序依次查找.js .json .node文件
    无论是三种类型中的哪一种, 导入之后都会转换成JS对象返回给我们
  2. 导入自定义模块时必须指定路径
    require除了可以导入"自定义模块(文件模块)“、还可以导入"系统模块(核心模块–nodejs自带的)”、“第三方模块(别人写的,可以直接使用)”
    导入"自定义模块"模块时前面必须加上路径(就是前面的./之类的)
    导入"系统模块"和"第三方模块"是不用添加路径
  3. 查找
    如果是"系统模块"直接到环境变量配置的路径中查找
    如果是"第三方模块"会按照module.paths数组中的路径依次查找

require(X)查找顺序:

  1. 判断X是不是内置模块,比如http,是内置模块,返回该模块即可
  2. 判断有没有路径开头
    • 把X当做一个文件,根据X所在的父模块确定绝对路径,加入文件没有后缀的话,依次查找js,json,node文件,找到就返回
    • 把X当做一个目录,一次查找下面的文件,只要其中有一个存在,就返回该文件
      在这里插入图片描述
  • 没有
    • 根据X所在的父模块,确定X可能的安装目录,一次在每一个目录里,把X当成文件名或者目录名加载
      比如,在/home/ry/projects/foo.js 执行了 require(‘bar’),那么可能的路径有在这里插入图片描述
      在这些路径里先把bar当成文件,一次找bar,bar.js/json/node,找不到再把bar当成目录,依次找pakage.json,index.js,index.json,index.node…
  1. 都没找到,就返回not found

3 es6模块化

在ES6出现之前,JS不像其他语言拥有“模块化”这一概念,比如python的import,就连css也有@import,于是为了支持JS模块化,我们使用类、立即执行函数或者第三方插件(RequireJS、seaJS)来实现模块化,在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。es6成为浏览器和服务器通用的模块解决方案。

一个文件就是一个模块, 模块中的数据都是私有的,ES6模块化模块和NodeJS中一样, 可以通过对应的关键字暴露模块中的数据, 可以通过对应的关键字导入模块, 使用模块中暴露的数据

  1. ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。

  2. 特点:静态加载,传统的commenJS属于是运行时加载(无法在编译时做静态优化),而es6模块化成为编译时加载,所以效率要比commonJS模块加载的效率高,这也就导致了没法引用es6模块本省,因为它不是对象;es5模块自动采取严格模式

    eg:

    let { stat, exists, readfile } = require('fs');
    //只有在运行时才会生成一个对象,然后加载这个对象中的三个方法,
    import { stat, exists, readFile } from 'fs';
    

1.export命令

模块功能主要由两个命令构成:exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

  1. 一个模块就是一个独立的文件,该文件内部的所有变量外部无法获取,如果你希望外部能够读取模块内的一个变量,必须使用export关键字输出该变量,函数和类也是同理

    eg:

    //profile.js
    export var firstName='Michael'
    export var lastName = 'Jackson';
    export var year = 1958;
    

    或者:

    var firstName='Michael'
    var lastName = 'Jackson';
    var year = 1958;
    export {firstName,lastName,year as YEARS} //可以使用as重命名
    

    只有这两种写法,其他的都是错误的,比如什么var m=1 export m也错了,没有加{}是错误的

2.import命令

  1. 使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。

    // main.js
    import { firstName, lastName, year } from './profile.js';
    function setName(element) {
      element.textContent = firstName + ' ' + lastName;
    }
    

    export命令接受一对大括号,里面指定要从其他模块导入的变量名,大括号的变量名,必须与被导入模块对外接口的名称相同

    如果想为输入的变量重新取一个名字,可以使用as

    eg:import { firstName as name } from ‘./profile.js’;

​ 建议对import导入的变量都采用只读形式,不要修改,如果是对象是可以修改的,因为避免难以排查的错误

from后的路径

可以是相对路径也可以是绝对路径,如果不带路径,就必须有配置文件,告诉 JavaScript 引擎该模块的位置。

如果多次重复执行同一句import语句,那么只会执行一次

3.整体加载

import * as circle form ‘./circle’

eg:

export function area(radius) {
  return Math.PI * radius * radius;
}

export function circumference(radius) {
  return 2 * Math.PI * radius;
}

import * as circle from './circle';  //cricle对象不允许修改
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));

4.export default

从上面的例子可以看出,使用import命令的时候,用户需要知道所有加载的变量名称或者函数名称才能去使用,否则无法加载,但是,希望快速加载的用户未必愿意去阅读api文档知道有哪些属性和方法

export default 用于输出一个叫做default的变量或者方法。所以它后面不能跟变量声明语句。并且只能export输出一个,所以import的时候可以随意指定名称,也可以不加{}

5.复合写法

混合使用

eg:在a.js文件里面
在这里插入图片描述
在b.js里面

在这里插入图片描述

4. es6和cmj的总结

  1. 导入导出:
    require: node 和 es6 都支持的引入
    export / import : 只有es6 支持的导出引入
    module.exports / exports: 只有 node 支持的导出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值