0.背景
本文的主角是 Modules 。你会问:“必须模块化吗?可是经理让我一分钟后上线!”
1.总览
其实写Vue
项目的时候,很多时候都用到了模块化编程,常常写export
import
,好,我们先简要浏览一下:
// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
console.log("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
console.log("2π = " + sum(pi, pi));
// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
// app.js
import exp, {pi, e} from "lib/mathplusplus";
console.log("e^π = " + exp(pi));
2.分开谈谈
正如MDN
所说的,Javascript
程序本来很小——在早期,它们大多被用来执行独立的脚本任务,在你的 web
页面需要的地方提供一定交互,所以一般不需要多大的脚本。
过了几年,我们现在有了运行大量 Javascript
脚本的复杂程序,还有一些被用在其他环境(例如 Node.js
)。使用JavaScript
模块依赖于import
和 export
。
2.1.export
export
可以用来导出函数、对象、值等等,
比如我们导出一些变量,比如环境配置的URL、一个Web请求函数、一个写好的工具类等等。
// Exporting individual features
export let name1, name2, …, nameN; // also var, const
export let name1 = …, name2 = …, …, nameN; // also var, const
export function functionName(){...}
export class ClassName {...}
同样的,我们还可以导出列表、重命名变量、解构已有的对象进行导出:
// Export list
export { name1, name2, …, nameN };
// Renaming exports
export { variable1 as name1, variable2 as name2, …, nameN };
// Exporting destructured assignments with renaming
export const { name1, name2: bar } = o;
一个要学会的重点是default
的使用,它帮助我们在js
文件末尾输出唯一一个导出的东西。与多个export
相比,这么确实要友好一点。
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };
还记得function*
是什么吗?之前说过哦,是生成器,讲过的哦。
举一个例子,在开源项目newbee-mall-vue3-app
中,对axios
进行封装,添加一些诸如默认的头部信息后,最后导出封装好的axios
对象供其它模块使用。
...
axios.interceptors.response.use(res => {
if (typeof res.data !== 'object') {
ElMessage.error('服务端异常!')
return Promise.reject(res)
}
if (res.data.resultCode != 200) {
if (res.data.message) ElMessage.error(res.data.message)
if (res.data.resultCode == 419) {
router.push({ path: '/login' })
}
return Promise.reject(res.data)
}
return res.data.data
})
export default axios
当然,在一些情况下,你也可以聚合模块,视具体需要使用即可。
// Aggregating modules
export * from …; // does not set the default export
export * as name1 from …; // Draft ECMAScript® 2O21
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
export { default, … } from …;
2.2.import
开局先来一个 Call Back! 还记得之前说过的默认导出的写法吧,导入的时候就可以使用这种写法对应:
import defaultExport from "module-name";
然后需要注意的是,如果导入一个模块中所有的内容,记得对这个模块来个命名,因为导入N个模块,模块之间肯定有重名的变量。这相当于给它们加了一个命名空间的感觉。
import * as name from "module-name";
最常见的用法是用上解构,从模块导出的多个属性中抽出来一个,像这样:
import {myExport} from '/modules/my-module.js';
聊点番外,这一段参考的MDN上的英文对应的文字是这样的:
Given an object or value named myExport which has been exported from the module my-module either implicitly (because the entire module is exported, for example using export * from ‘another.js’) or explicitly (using the export statement), this inserts myExport into the current scope.
里面提到了两个单词explicitly(明确地)
、implicitly(暗含的)
,一个向外ex
,一个向内im
,这一对单词频繁出现。如果你在思考代码的复用和设计,要常常思考什么应该隐含其中而什么应该明确指明,这种分寸感,只能在特定的不同的情景下反复尝试才能找到。
另外,多个直接加逗号就可以,如果在导出的时候发现原有导出的内容十分简易、容易重复,也可以重命名。
import {
reallyReallyLongModuleExportName as shortName,
anotherLongModuleName as short
} from '/modules/my-module.js';
有时候,你会直接import一个js文件,就是为了让这个js文件做一些初始化的工作,比如帮你创建个文件目录之类的。
import '/modules/my-module.js';
这里稍微聊一下动态导入,这部分我还真没看到谁经常使用,其实就是,如果嫌弃静态导入拖慢了载入速度,或者是遇到了导入什么js之后必须执行什么(我是没遇到过),这类的特殊场景会使用。
3.结语
好了,模块化是大势所趋,各个语言的未来,希望大家多多思考如何封装代码,让代码简洁、高效且安全稳定。祝愿读到这里的你没有bug。