# 笔记

# 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;

双重断言?

参考文档:类型断言 (opens new window)

# 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’);”>