JS 的符号绑定
如果 export 一个变量(使用 let 声明),那么再 import 到其他模块中,这时会发现这个变量是无法在其他模块中进行更改的,因为会被转换为常量。
但是如果在这个变量的同一模块中有其他导出的函数,其作用是去更改该变量,则可以在其他模块中调用该函数来修改这个变量,例如:
export let a = 1;
export const increase = () => {
a += 1;
};
在其他模块中访问 a 会访问到该模块内部定义的这个 a(而不是复制后重新赋值的变量 a),但是无法直接进行修改(例如a += 1
),却可以通过increase
进行修改,所以为了预防这种情况,导出的值必须是常量。
而上面提到的导入的变量 a 和导出的 a 是同一个变量,而非和解构时一样,会重新创建一个同名变量并赋值,这就是 ESM 的具名导出特性。
浏览器的 ESM 解析过程
静态导入解析
在解析 HTML 文件时,遇到了 script 标签,就会开始执行以下过程:
- 静态导入解析:
- 将相对地址转换为绝对地址,进行记录,下载 js 文件;
- 得到 js 文件后,解析 import 语句,下载对应的 js 文件(注意相同地址的 js 文件不会重复下载);
- 递归执行步骤 2,得到所有从 script 标签的内容开始递归依赖的 js 文件;
- 从入口文件开始一行一行执行 js 文件,遇到 import 语句就进入到对应的 js 文件进行执行;
- 执行的过程中如果遇到 export 则会对该文件建立一个映射,记录其导出内容,以方便后续其他模块使用相同的导出内容;
- 动态导入解析:
- 如果在执行过程中遇到动态导入语句,则也会进行上面相同的依赖分析,建立相应的导出影射;
TS 类型——去除对象类型中的所有必选值
type GetOptional<T> = {
[P in keyof T as T[P] extends Required<T>[P] ? never : P]: T[P];
};
type Test = {
a: 1;
b: 2;
c?: 3;
d?: 4;
};
type TestOptional = GetOptional<Test>;
// { c?: 3; d?: 4; }
TS 类型——方法可填参数根据对象属性进行限定
type Watcher<T> = {
on<K extends keyof T & string>(
event: `${K}Changed`,
listener: (oldValue: T[K], newValue: T[K]) => void,
);
};
declare function watch<T>(obj: T): Watcher<T>;
const obj = { a: 1, b: 2 };
// on方法的参数类型会根据对象的属性动态生成
watch(obj).on('aChanged', (oldValue, newValue) => {
console.log(oldValue, newValue);
});
将任务放在微队列中执行
【模拟微队列【渡一教育】】
实现一个方法,其参数为一个函数,执行后会将这个函数放到微队列中执行。这个方法可以在 Promise.then 方法中用到。
解决的关键在于考虑多种不同的执行环境,实现如下:
function runMicroTask(func) {
// 如果有Promise,就使用Promise.resolve
if (typeof Promise !== 'undefined') {
Promise.resolve().then(func);
return;
}
// 如果有MutationObserver,就使用MutationObserver
if (typeof MutationObserver !== 'undefined') {
const observer = new MutationObserver(func);
const textNode = document.createTextNode('1');
observer.observe(textNode, {
characterData: true,
});
textNode.data = '2';
return;
}
// 如果有process.nextTick,就使用process.nextTick(一般在Node.js环境下)
if (process && process.nextTick) {
process.nextTick(func);
return;
}
// 如果以上都不支持,就使用setTimeout
setTimeout(func, 0);
}
NPM 的对等依赖问题
在进行一些插件开发时,通常会指定项目中需要某个包的对应版本,这时候就会在package.json
中加上peerDependencies
这个选项用于限制,例如:
{
"peerDependencies": {
"vue": "2.0.0"
}
}
这时,如果使用了该插件,就会限制项目中使用的 vue 为2.0.0
版本,如果这时候使用的 vue 版本对不上,则 npm 可能会出现ERESOLVE unable to resolve dependency tree
的报错。
npm 本来在 v7 之前的版本是不对这种问题进行报错的,不过后续的版本中就会进行处理了,如果要让 npm 忽略这种问题(在确认了这个包可用于当前项目后),可以在npm i
时添加--legacy-peer-deps
从而按照旧版本的方式进行处理。