js模块化
JavaScript模块化规范
1. 模块化概述
- 什么是模块化?
- 将程序文件依据一定规则拆分成多个文件,这种编码方式就是模块化的编码方式
- 拆分出来的每个文件就是一个模块,模块中的数据都是私有的,模块之间相互隔离
- 同时也能通过一些手段,可以把模块内的指定数据“交出去”,供其他模块使用
- 为什么需要模块化?
随着应用的复杂度越来越高,其代码量和文件数量都会急剧增加,会逐渐引发以下问题:- 全局污染问题
- 依赖混乱问题
- 数据安全问题
2. 导入与导出的概念
模块化的核心思想是:模块之间是隔离的,通过导入和导出进行数据和功能的共享。
- 导出(暴露):模块公开其内部的一部分,如变量,函数等,是这些内容可以被其他模块使用;
- 导入(引入):模块引入和使用其他模块导出的功能,以重用代码和功能
3. 引用问题
- ES模块化:文件导出数据的时候定义的常量/变量和导出的时候同名的常量使用的是同一块内存空间;ES模块化中,导入的部分是常量,不允许修改的
- CommonJs:导入的时候定义的变量和导出的时候的变量是复制的关系,不是同一个内存空间
4. 模块化规范
- CommonJS:服务端应用广泛
- 导出:export.js
- exports.name = name;
- module.exports =
- 注意:
- 每个模块内部的:this,exports,module.exports在初始的时候,都指向同一个空对象,直接导出的话,该空对象就是当前模块导出的数据
- 无论如何修改导出对象,只要写module.exports,最后导出的都是module.exports的值
- exports是对module.exports的初始引用,仅为了方便给导出的对象添加属性,所以不能使用exports = value的形式导出数据,但是可以使用module.exports === xxxx 导出数据
- 在使用exports,this,module.exports的方式修改数据的时候,其指向的对象已经不再是同一个了。(具体原理待研究)
- 浏览器端运行
- 浏览器端需要编译,步骤如下
- 全局安装browserify: npm i browserify -g
- 编译 browserify index.js -o build.js (index.js是源文件,build.js是输出的目标文件)
- 页面中引入使用
- 扩展
- 一个js模块在执行时,是被包裹在一个内置函数中执行的,所以每个模块都有自己的作用域,我们可以通过如下方式验证这一说法:
console.log(arguments.callee.toString())- 内置函数的大概形式如下
function (exports, require, module, __filename, __dirname){/*****/}
- 导入
- const obj = require('export.js的文件路径')
- const { name } = require('export.js的文件路径') // 对象解构赋值
- const { name: reName } = require('export.js的文件路径') // 变量重命名
- 导出:export.js
- AMD
- 环境配置:
- 文件目录
- js - main.js - file1.js - file2.js - libs require.js -index.html - 文件说明
- js文件夹中存放业务逻辑代码,main.js用于汇总各模块
- libs中存放的是第三方库,例如必须要用的require.js
- 下载require.js,在index.html中配置main.js与require.js
// index.html <script data-main="./js/main.js" src="./js/libs/require.js"></script>- 在main.js中编写模块配置对象,注册所有模块,内容为
requirejs.config({ // 基本路径 baseUrl: './js', // 模块标识名与模块路径映射 paths: { file1: "file1", file2: "file2", } }) - 文件目录
- 导出:调用require.js中的内置函数define(),参数为函数,函数内部为正常代码,需要在最后位置将需要导出的变量进行return导出。
define(function() { /**正常代码部分*/ return xxx; // 返回值部分,类型不限 }) - 导入:调用define函数,第一个参数传入一个数组,数组里边写上对应的文件模块映射的字符串
define(["file1"], function(f){ f.doSomething(); /**正常代码部分*/ return xxx; })
- 环境配置:
- CMD
- 目录结构
- sea-modules // 存放seajs,jquery等文件,这也是模块的部署目录 - static // 存放各个项目的js,css文件 - todo - app // 存放html文件 - todo.html- 环境准备
- 使用sea.js
<!-- html文件 在页尾引入sea.js --> <script src="path/to/sea.js"></script> <!-- 在下面写模块的配置和入口 --> <script> // sea.js简单配置 seajs.config({ base: '../sea-modules/', alias: { 'jquery', '../xx/xx/jquery.js', } }) // 加载入口模块 内容为main的入口文件的路径 seajs.use("../xxx/xxx/main.js") </script>- 导出数据
// math.js文件 define(function(require, exports, module) { // exports的方式导出接口 exports.add = function(a, b) { return a + b; }; // 还可以使用module.exports的方式导出整个接口 // module.exports = {...} });- 导入数据
// math.js文件 define(function(require, exports, module) { const xxx = require('math.js') // 通过require的方式引入依赖 require.async('./b', function(b) { // 通过require.async异步加载一个模块,在加载完成后执行回调 b.doSomething(); }) require.async(['./c', './d'], function(c, d) { // 通过require.async异步加载多个模块,在加载完成后执行回调 c.doSomething(); d.doSomething(); }) });- 使用模块文件
// main.js文件 加载一个模块 seajs.use('math.js', function(math) { const sum = math.add(1, 2); console.log(sum); // 输出3 }); // 加载多个模块 seajs.use(['./a', './b'], function(a,b){ a.doSometing(); b.doSometing(); }) - ES模块化:客户端应用广泛
- ES6模块化规范是一个官方标准的规范,它是在语言标准的层面上实现了模块化规范,是目前最流行的模块化规范,且浏览器与服务端均支持该规范
- 在客户端中运行
- 用服务器跑html文件,html文件引入模块化部分文件是使用
- 在node中运行
- 将js文件后缀设置为mjs
- 配置package.json文件,里边必须将type设置为module
- 导出
- 分别导出:使用多个export进行导出,export 后边跟语句
- 统一导出:使用export {} 导出, {}不是对象,是包含统一导出内容的块
- 默认导出:使用 export default,写法为: export default 值
- 备注:上述导出方式可以同时使用
- 导入
- 全部导入 import * as esModule from '
${filepath}' // 适用于分别导出和统一导出 esModule为自定义常量名 - 命名导入 import { name } from '
${filepath}' - 默认导入 import defaultName from '
${filepath}' - 动态导入:在代码中使用 import('
${filepath}') 进行导入,结果为一个promise,可以使用.then链式调用或者asyns,await获取结果 - import 不接受任何数据 import '
${filepath}' // 例如: import 'index.css'; import 'myJs.js' - 注意:命名导入和默认导入可以混合使用
- 全部导入 import * as esModule from '
浙公网安备 33010602011771号