# 笔记
# 2022 年 3 月 1 日
# ES6 Module 动态加载
ES6
模块是编译时加载,CommonJS
和 AMD
则是运行时加载。
ES2020
提案引入了 import()
函数,支持动态加载模块,并且 import()
返回的是一个 Promise
对象
const main = document.querySelector("main")
import(`./section-modules/${someVariable}.js`)
.then(module => {
module.loadPageInto(main)
})
.catch(err => {
main.textContent = err.message
})
import()
函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行import()
函数与所加载的模块没有静态连接关系,这点也是与import
语句不相同import()
类似于Node
的require
方法,区别主要是前者是异步加载,后者是同步加载
import()
加载模块成功以后,这个模块会作为一个对象,当作 then
方法的参数。因此,可以使用对象解构赋值的语法,获取输出接口。
import("./myModule.js").then(({ export1, export2 }) => {
// ...·
})
// 如果模块有 default 输出接口,可以用参数直接获得
import("./myModule.js").then(myModule => {
console.log(myModule.default)
})
// 在 async 函数之中
async function main() {
const myModule = await import("./myModule.js")
const { export1, export2 } = await import("./myModule.js")
const [module1, module2, module3] = await Promise.all([
import("./module1.js"),
import("./module2.js"),
import("./module3.js")
])
}
main()
# 2022 年 3 月 2 日
# ES6 模块与 CommonJS 模块的差异
CommonJS
模块输出的是一个值的拷贝,ES6
模块输出的是值的引用CommonJS
模块是运行时加载,ES6
模块是编译时输出接口CommonJS
模块的require()
是同步加载模块,ES6
模块的import
命令是异步加载,有一个独立的模块依赖的解析阶段
JavaScript
现在有两种模块。一种是 ES6
模块,简称 ESM
;另一种是 CommonJS
模块,简称 CJS
CommonJS
模块是 Node.js
专用的,与 ES6
模块不兼容。语法上面,两者最明显的差异是:CommonJS
模块使用 require()
和 module.exports
,ES6
模块使用 import
和 export
从 Node.js v13.2
版本开始,Node.js
已经默认打开了 ES6
模块支持。Node.js
要求 ES6
模块采用 .mjs
后缀文件名。也就是说,只要脚本文件里面使用 import
或者 export
命令,那么就必须采用 .mjs
后缀名。Node.js
遇到 .mjs
文件,就认为它是 ES6
模块。如果不希望将后缀名改成 .mjs
,可以在项目的 package.json
文件中,指定 type
字段为 module
。一旦设置了以后,该项目的 JS
脚本,就被解释成 ES6
模块。
{
"type": "module"
}
如果这时还要使用 CommonJS
模块,那么需要将 CommonJS
脚本的后缀名都改成 .cjs
。如果没有 type
字段,或者 type
字段为 commonjs
,则 · 脚本会被解释成 CommonJS
模块。
总结为一句话:.mjs
文件总是以 ES6
模块加载,.cjs
文件总是以 CommonJS
模块加载,.js
文件的加载取决于 · 里面 type
字段的设置。
CommonJS
模块加载ES6
模块
CommonJS
的 require()
命令不能加载 ES6
模块,会报错,只能使用 import()
这个方法加载。
;(async () => {
await import("./my-app.mjs")
})()
ES6
模块加载CommonJS
模块
ES6
模块的 import
命令可以加载 CommonJS
模块,但是只能整体加载,不能只加载单一的输出项。
// 正确
import packageMain from "commonjs-package"
// 报错
import { method } from "commonjs-package"
# 2022 年 3 月 3 日
# 类型断言
TypeScript
允许你覆盖它的推断,并且能以你任何你想要的方式分析它,这种机制被称为「类型断言」
。
TypeScript
类型断言用来告诉编译器你比它更了解这个类型,并且它不应该再发出错误。
// 初版类型断言语法如下,
let foo: any;
let bar = <string>foo; // 现在 bar 的类型是 'string'
// 因上述语法与JSX语法存在歧义
// 因此建议使用以下语法来为类型断言
let bar as foo;
双重断言?
# 2022 年 3 月 4 日
# 类型别名
TypeScript
提供了为类型注解设置别名的便捷语法,你可以使用 type SomeName = someValidTypeAnnotation
来创建别名
// StrOrNum 就是类型别名,后面的这一串 string | number 是类型注解
type StrOrNum = string | number
// 使用
let sample: StrOrNum
sample = 123
sample = "123"
// 会检查类型
sample = true // Error
# 2022 年 3 月 5 日
# TS 命名空间
命名空间可以作为组织代码的一种手段,帮助我们记录类型并且不用担心与其他对象产生命名冲突
可以将命名空间 export
出去,然后再通过命名空间访问暴露出来的变量。
namespace Utility {
// 这里注意,命名空间里面的变量也需要export,否则在外面访问不了
export function log(msg) {
console.log(msg);
}
export function error(msg) {
console.log(msg);
}
}
// usage
Utility.log('Call me');
Utility.error('maybe');
# 2022 年 3 月 6 日
# @types
可以通过 npm
来安装使用 @types
,如下为 jQuery
npm install @types/jquery --save-dev
默认情况下,TypeScript
会自动包含支持全局使用的任何声明定义。例如 jquery
,应该能够在项目中开始全局使用 $
此外,还可以通过 tsconfig.js
的 compilerOptions.types
选项,引入指定的类型。如下所示,通过配置 compilerOptions.types: [ "jquery" ]
后,只允许使用 jquery
的 @types
包,即使安装了另一个声明文件,比如 npm install @types/node
,它的全局变量(例如 process
)也不会泄漏到你的代码中,直到你将它们添加到 tsconfig.json
类型选项。
{
"compilerOptions": {
"types": ["jquery"]
}
}
TS
类型声明包搜索地址:https://www.typescriptlang.org/dt/search?search= (opens new window)
# 2022 年 3 月 7 日
# @types、types、typeRoots
tsconfig.js
中的:types
、typeRoots
都是用于指定加载包的配置项
types
是引入指定的包,并且只有列出来的才会被引入typeRoots
是指定一个路径,并且该路径下的所有包都会被引入@types
默认所有可见的@types
包会在编译过程中被包含进来
(可以考虑将项目的 .d.ts
等类型声明文件统一放置在 src/@types/
下,然后在 typeRoots
中指定)
参考地址:https://www.tslang.cn/docs/handbook/tsconfig-json.html (opens new window)
# 2022 年 3 月 8 日
# shims-vue.d.ts 文件作用
.vue
类型文件本身不被 ts
识别,ts
只能识别 .ts
、.d.ts
、.tsx
后缀文件
加上模块声明是为了能够被 webpack
处理,并且能够识别 export
出来的对象
declare module '*.vue' { // Ambient module declarations 环境模块说明
import Vue from 'vue'
export default Vue
}
参考地址:https://fettblog.eu/typescript-modules-for-webpack/ (opens new window)
# 2022 年 3 月 9 日
# .d.ts 文件
- 会全局自动生效,需要注意命名冲突问题,结合
namespace
使用 - 如果依赖某个全局库,可以使用
/// <reference types="..." />
命令 (em...没有实际使用过)
参考文档:preventing-name-conflicts (opens new window)
# 2022 年 3 月 10 日
# 常用方法类型
setTimeout
、setInterval
的类型为number
(如果仍然异常,可以考虑使用window.setTimeout
)element-ui
中的各种类型组件都有其对应的类型,如:el-form
对应Form
;el-table
对应Table
# 2022 年 3 月 11 日
# 类型守卫
类型守卫主要指一些在特定作用域内 收缩
变量类型的类型判断行为(也就是类型保护?)
typeof
类型判断instanceof
原型判断in
属性判断==
、===
等判断运算符
function test(arg: string | number) {
if (typeof arg == "string") {
// 这里 arg 的类型 收紧 为 string
} else {
// 这里 arg 的类型 收紧 为 number
}
}
# 2022 年 3 月 12 日
# TS 运算符
- 非空断言运算符
!:
:表达式之后写!:
实际上是一个类型断言,该值不是null
和undefined
# 2022 年 3 月 13 日
# pnpm 一探
pnpm
类似于 npm
,可以将多个项目相同的依赖存储在一个统一的位置,当安装软件包时, 其包含的所有文件都会硬链接自此位置,而不会占用 额外的硬盘空间,可以极大程度的减轻存储压力以及增加安装速度
文档地址:https://www.pnpm.cn/motivation (opens new window)
实际体验发现:
- 安装单个依赖时,应该是扫了一遍
node_modules
,然后再下载依赖,话费的时间比直接npm install
要长 - 旧项目改用
pnpm
时,发现webpack
、url-loader
等依赖都会出现问题,如10KB
以下的图片压缩异常 - 尝试两个项目用
pnpm
安装,但发现整理的包体积并没有缩小
# 2022 年 3 月 14 日
# NodeJS is not undef
在 tsconfig.json
中的 types
选项中,添加了 node
后,使用 NodeJS
命名空间提示 NodeJS is not undef
,可以在 eslintrc.js
中增加
{
"globals": {
"NodeJS": true
}
}
# 2022 年 3 月 15 日
# XSS
XSS 攻击有两大要素: 1. 攻击者提交恶意代码。 2. 浏览器执行恶意代码。
在使用 .innerHTML、.outerHTML、document.write() 时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 .textContent、.setAttribute() 等。
- script 标签
- img 标签
- 字符转义
- meta 标签中的 url:
<META HTTP-EQUIV=”refresh” CONTENT=”0; URL=http://;URL=javascript:alert(‘XSS’);”>