# 笔记

# 2022 年 3 月 1 日

# ES6 Module 动态加载

ES6 模块是编译时加载,CommonJSAMD 则是运行时加载。

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() 类似于 Noderequire 方法,区别主要是前者是异步加载,后者是同步加载

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.exportsES6 模块使用 importexport

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 模块

CommonJSrequire() 命令不能加载 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.jscompilerOptions.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 中的:typestypeRoots 都是用于指定加载包的配置项

  • 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 日

# 常用方法类型

  • setTimeoutsetInterval 的类型为 number (如果仍然异常,可以考虑使用 window.setTimeout
  • element-ui 中的各种类型组件都有其对应的类型,如:el-form 对应 Formel-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 运算符

  • 非空断言运算符!::表达式之后写 !: 实际上是一个类型断言,该值不是 nullundefined

# 2022 年 3 月 13 日

# pnpm 一探

pnpm 类似于 npm,可以将多个项目相同的依赖存储在一个统一的位置,当安装软件包时, 其包含的所有文件都会硬链接自此位置,而不会占用 额外的硬盘空间,可以极大程度的减轻存储压力以及增加安装速度

文档地址:https://www.pnpm.cn/motivation (opens new window)

实际体验发现:

  • 安装单个依赖时,应该是扫了一遍 node_modules,然后再下载依赖,话费的时间比直接 npm install 要长
  • 旧项目改用 pnpm 时,发现 webpackurl-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’);”>