# 笔记

# 2021 年 10 月 1 日

# ref、reactive(10.28 补)

composition API 下定义基本数据类型和对象的方式...

const userName = ref("张三")
console.log(username.value)

const listData = reactive([
  { id: 1, content: "123" },
  { id: 2, content: "234" }
])
console.log(listData)

em...用着是真的有点麻烦:

  • ref 定义变量要通过 .value 来访问值(但通过 return 返回的会自动浅解包)
  • 打印 reactive 对象到控制台的样子挺怪的,不直观

reactive.png

# 2021 年 10 月 2 日

# vue2 vs vue3 周期函数(11.4 补)

新增:

  • beforeUnmount:在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。
  • unmounted:卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。
  • renderTracked:跟踪虚拟 DOM 重新渲染时调用。钩子接收 debugger event 作为参数。此事件告诉你哪个操作跟踪了组件以及该操作的目标对象和键。
  • renderTriggered:当虚拟 DOM 重新渲染被触发时调用。和 renderTracked 类似,接收 debugger event 作为参数。此事件告诉你是什么操作触发了重新渲染,以及该操作的目标对象和键。

删除:

  • beforeDestory
  • destoryed

准确的说,应该是 beforeDestorydestoryed 改成了 beforeUnmountunmounted,并且新增了两个钩子函数用于追踪数据的变化

注意在 vue3composition API 中,是没有 beforeCreatecreate 这两个钩子函数,只有 setup,并且 composition API 中是用 onX 来注册对应的钩子函数

# 2021 年 10 月 3 日

# 自定义指令(11.5 补)

vue3 相较于 vue2,在钩子函数上做了一些改动,为了更好的跟生命周期钩子函数保持一致;除此之外,参数中 expression(字符串形式的指令表达式如:'1+1') 将不在使用

(幸好常用的几个钩子变化不大 🤣)

vue3 指令钩子函数:

  • created - 新增!在元素的 attribute 或事件监听器被应用之前调用。
  • bindbeforeMount
  • insertedmounted
  • beforeUpdate:新增!在元素本身被更新之前调用,与组件的生命周期钩子十分相似。
  • update → 移除!该钩子与 updated 有太多相似之处,因此它是多余的。请改用 updated。
  • componentUpdatedupdated
  • beforeUnmount:新增!与组件的生命周期钩子类似,它将在元素被卸载之前调用。
  • unbind -> unmounted

# 2021 年 10 月 4 日

# Devtools Beta(11.15 补)

如果发现自己的 vue3 项目不能使用 Devtools 了,试试安装一下 beta 版的开发者工具吧

下载地址:点这里 🚀 (opens new window)

# 2021 年 10 月 5 日

# HTTP 缓存

# HTTP1.0

Expires:即过期时间,存在 RH 中,在这个时间之前可以直接从缓存中获取数据

Last-Modified:资源的最后修改时间

If-Modified-Since:协商缓存过程中,请求头会增加 If-Modified-Since 字段用于携带 Last-Modified 的值

# HTTP1.1

Cache-Control

  • max-age(s):代表资源在**秒内有效
  • public:表示客户端和服务端都可以缓存
  • private:只有浏览器可以缓存
  • no-catch:跳过强缓存,直接进入协商缓存阶段
  • no-store:不进行任何方式的缓存
  • s-maxage:针对代理服务器的缓存时间

ETag:资源文件的唯一标识

If-None-Match:协商缓存过程中,请求头会增加 If-None-Match 字段用于携带 ETag 的值

# 缓存位置

  • 比较大的 JSCSS 文件会被丢进磁盘,反之丢进内存
  • 内存使用率比较高的时候,文件优先进入磁盘

# 2021 年 10 月 6 日

# 输入框自动填充样式优化(11.23 补)

有时写表单时会发现谷歌会自动填充,并且样式还贼丑。可以试试下面的 css 代码,保证恢复如初

input:focus:-webkit-autofill,
input:-webkit-autofill {
  box-shadow: 0 0 0px 0 #fff inset;
  -webkit-box-shadow: 0 0 0px 0 #fff inset !important;
  transition: background-color 500000s ease-in-out 50000s;
}

# 2021 年 10 月 7 日

# Unicode 与字符的相互转换(12.2 补)

String.fromCharCode()ES5 语法,只能返回码点小于 0xFFFF 的字符 String.fromCodePoint()ES6 语法,支持识别大于 0xFFFF 的字符

charCodeAt()ES5 语法,返回指定下标字符的十进制码点,同样只能小于 65535 的字符 codePointAt()ES6 语法,返回指定下标的十进制码点,支持识别大于 65535 的字符(需要使用 tostring 方法转换成真正的十六进制码点)

为什么是 FFFF?因为 JavaScript 内部,字符是以 utf-16 的格式存储,每个字符站 2 个字节,一个字节等于 8 位,即共 16 位(二进制),转换成十六进制就变成了 FFFF

# 2021 年 10 月 8 日

# ES6 新增的字符串方法(12.2 补)

  • includes:判断是否包含字符串
  • startsWith:判断是否以某字符串开头
  • endsWith:判断是否以某字符串结尾
  • repeat:重复字符串,参数为重复次数
  • padStart:参数为(len, str),指定某字符串以 str 在头部补足到 len 长度
  • padEnd:参数为(len, str),指定某字符串以 str 在尾部补足到 len 长度
  • trimStart:消除头部的空格
  • trimEnd:消除尾部的空格
  • replaceAll:参数为(searchValue, replacement),全局替换 searchValuereplacement,返回一个新的字符串,不改变原先的字符串

# 2021 年 10 月 9 日

# ES6 数值相关方法(12.2 补)

  • Math.pow 是计算某个数的 n 平方,可以用 ** 代替
  • Number.isFinite:判断是否有限
  • Number.isNaN:判断是否为 NaN
  • Number.parseInt():ES6 将该全局方法移植到 number 对象,使逐步模块化
  • Number.parseFloat():ES6 将该全局方法移植到 number 对象,使逐步模块化
  • Number.isInteger():判断是否为整数

Tips: Number.isNaN() 跟传统的 isNaN() 方法仍有点区别,后者会先调用 Number() 方法转换成数值后,再执行 isNaN() 方法(isFinite() 方法同)

isNaN(NaN) // true
isNaN("NaN") // true
Number.isNaN(NaN) // true
Number.isNaN("NaN") // false
Number.isNaN(1) // false

# 2021 年 10 月 10 日

# 0.1 + 0.2 问题(12.3 补)

JavaScript 采用 IEEE754 标准,数值存储为 64 位双精度格式,数值精度最多可以达到 53 个二进制位(1 个隐藏位与 52 个有效位),超过了就会别丢弃。

0.1 用二进制表示为:0.1.toString(2) -> 0.00011001100... 0.2 用二进制表示为:0.2.toString(2) -> 0.00110011001...

相加得出 0.010011001100...,最终得出的值为 0.30000000000000004 ≠ 0.3

参考文档:为什么 0.1 + 0.2 = 0.300000004 🚀 (opens new window)

如何解决?

# 2021 年 10 月 11 日

# changeOrigin

proxy 代理的选项之一,默认值为 false,会保留主机头的来源。即后端获取到接口请求头中的 host 值即为 devServerhost 的值

changeOrigin-false

若设置为 true,则 host 值为 proxytarget 的值

changeOrigin-true

因此(猜测)可以设置 changeOrigintrue,绕过后端的 host 检查

# 2021 年 10 月 12 日

# Git Hook

在提交代码到 gitlab 时,增加一层 hook 限制

vue-cli 创建的项目会自动安装 yorkieforkhusky,但与后者不兼容)搭配 lint-staged 即可实现在 git add 时先 run lint妈妈再也不用担心代码给人搞乱了 😎)

{
  "gitHooks": {
    "pre-commit": "lint-staged"
  },
  "lint-staged": {
    "*.{js,vue}": ["vue-cli-service lint", "git add"]
  }
}

注意:lint-staged 需要 node12.13.0+ 的版本才能正常使用

问题:

  • eslint 校验结果为 warning 的也可以提交到 gitlab

# 2021 年 10 月 13 日

# Number.EPSILON(12.6 补)

ES6Number 对象上新增了一个极小的常量 Number.EPSILON,它表示 1 与大于 1 的最小浮点数之间的差,相当于 2-52 次方

Number.EPSILON 一般用于设置“能够接受的误差范围”。比如,误差范围设为 2-50 次方(即 Number.EPSILON * Math.pow(2, 2)),即如果两个浮点数的差小于这个值,我们就认为这两个浮点数相等。

因此,0.1 + 0.2 的问题可以这样来解决:

function withinErrorMargin(left, right) {
  return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2)
}

0.1 + 0.2 === 0.3 // false
withinErrorMargin(0.1 + 0.2, 0.3) // true

# 2021 年 10 月 14 日

# Math 对象的扩展(12.6 补)

  • Math.trunc():去除小数部分,返回整数部分
  • Math.sign():判断一个数是正数、负数还是零
  • Math.cbrt():计算立方根

查看更多 🚀 (opens new window)

# 2021 年 10 月 15 日

# .env 环境变量文件

主要有.env.env.[env].env.[env].local 三种类型文件([env] 指各种环境),优先级从左到右递增,其中 .env.[env].local 只在本地生效并且一般不提交到 gitlab,常用于多人合作开发时部署自己的联调环境

环境变量文件中,只有 NODE_ENV, BASE_URL 和以 VUE_APP_ 开头的变量能通过 webpack.DefinePlugin 静态地嵌入到代码中

# 2021 年 10 月 16 日

# 箭头函数(12.10 补)

对于普通函数来说,内部的 this 指向函数运行时所在的对象,但是这一点对箭头函数不成立。它没有自己的 this 对象,内部的 this 就是定义时上层作用域中的 this。也就是说,箭头函数内部的 this 指向是固定的,相比之下,普通函数的 this 指向是可变的。

function foo() {
  setTimeout(() => {
    console.log("id:", this.id)
  }, 100)
}

var id = 21

foo.call({ id: 42 })
// id: 42

# 2021 年 10 月 17 日

# ES6 尾调用优化

函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数 A 的内部调用函数 B,那么在 A 的调用帧上方,还会形成一个 B 的调用帧。等到 B 运行结束,将结果返回到 AB 的调用帧才会消失。如果函数 B 内部还调用函数 C,那就还有一个 C 的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。

尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。这将大大节省内存。这就是“尾调用优化”的意义。

但需要注意:尾调用优化仅在严格模式下开启

参考文档:尾调用优化的实现 🚀 (opens new window)

# 2021 年 10 月 18 日

------------------------------------------------------

大致过完一遍 vue-cli4 官方文档...
稍微熟悉了其中的配置...
但手写 chainWebpack 还是有点困难...
常用的插件也暂时没看...
留着过 webpack 的时候再看吧...
这个月好像断档的有点多...
得努力了...
hope... 祈っている... 🙏🙏🙏

# 2021 年 10 月 19 日

# 从 0 开始搞 webpack

需要选定一个入口文件作为内部依赖关系图的“开始”,默认输入路径为 /dist;所有需要使用到的文件都需要在入口文件中引用

各种常见类型文件对应处理 loader:

文件类型 对应 loader
js default
css style-loader、css-loader
scss node-sass、sass-loader
less less-loader
png | jpg | gif | svg file-loader | url-loader
ttf | woff(2) | otf | eot file-loader
  • 处理 cssloader 顺序为:css-loader -> style-loader
  • 处理 scss 文件需要使用 sass-loader,前提是需要安装 node-sass 依赖
  • 若要将小 icon 转换成 base64,需要用到 url-loader
  • url-loaderfile-loader 只需要使用其中一个即可处理图片文件

# html-webpack-plugin

默认情况下 htmlwebpackplugin 会自动生成一个 HTML 文件,也可以自定义选定 template 文件

plugins: {
  new htmlwebpackplugin({
    title: "测试title",
    filename: "index.html",
    template: path.resolve(__dianame, "realpath/template.html")
  })
}

指定 template 文件后,htmlwebpackplugin 会自动注入相关的 css、js 文件等

如上,指定 template 且设置了 title 参数后,如果 template 文件中已经设置了标题,那么 title 将不生效,需要通过 <%= htmlWebpackPlugin.options.title %> 获取到配置中的 title 参数

如果需要指定多入口文件:

需要 new 多几个 htmlwebpackplugin 对象,同时修改 entryoutput 参数

# clean-webpack-plugin

每次构建项目之前清理 /dist 文件夹

使用时需要注意:

// webpack.config.js
// const CleanWebpackPlugin  = require('clean-webpack-plugin') 错误
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

plugins: [
  new CleanWebpackPlugin()
],

Otherwise:可以借助 node.jsfile 模块进行文件夹删除

思考:new CleanWebpackPlugin() 放置的位置会不会影响到最终的效果?

# 2021 年 10 月 20 日

# source-map

将编译后的代码映射回源代码,用于开发环境

相关 source-map 类型及对应打包速度如下:

+++ 非常快速, ++ 快速, + 比较快, o 中等, - 比较慢, --

devtool 构建速度 重构速度 生产环境 品质(quality)
none +++ +++ yes 打包后的代码
eval +++ +++ no 生成后的代码
cheap-eval-source-map + ++ no 转换过的代码(仅限行)
cheap-module-eval-source-map o ++ no 原始源代码(仅限行)
eval-source-map -- + no 原始源代码
cheap-source-map + o yes 转换过的代码(仅限行)
cheap-module-source-map o - yes 原始源代码(仅限行)
inline-cheap-source-map + o no 转换过的代码(仅限行)
inline-cheap-module-source-map o - no 原始源代码(仅限行)
source-map -- -- yes 原始源代码
inline-source-map -- -- no 原始源代码
hidden-source-map -- -- yes 原始源代码
nosources-source-map -- -- yes 无源代码内容

# 2021 年 10 月 21 日

# webpack 自动化编译

webpack 提供了以下三种自动化编译方案:

  • webpack watch mode
  • webpack-dev-server
  • webpack-dev-middleware

# webpack watch mode

// package.json
{
  "script": {
    "watch": "webpack --watch"
  }
}

实现原理大致可以理解为:webpack 监控着项目(依赖图)中的所有文件,当文件发生改变时,自动重新编译代码,无需手动运行编译。

这种方法存在一个缺点,那就是每次重新编译,都需要手动刷新浏览器才会更新(--watch 会耗费较高的电脑性能吧?)

# webpack-dev-server

安装 webpack-dev-server 依赖后即可跑项目(webpack 官网指南下的配置 contentBase 不可用~)

webpack-dev-server 在编译之后不会写入到任何输出文件,而是将 bundle 文件保留在内存中,然后将它们 serveserver 中,相当于部署在 server 的根路径下

相关原理内容较多,可参考以下链接:

# webpack-dev-middleware

webpack-dev-middleware 是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 serverwebpack-dev-server 在内部使用了它,它也可以作为一个单独的 package 来使用,以便根据需求进行更多自定义设置。

官方文档地址:

# 2021 年 10 月 22 日

# split-chunk-plugin

比较通用的性能优化方案之一,可提升重复构建打包速度及页面访问速度。常用属性及其配置如下:

module.exports = {
  optimization: {
    splitChunk: {
      chunks: "all", // 打包的模块是按需引入、直接引入、还是全部都用于优化,对应的值为async initial all
      minSize: 30000, // 抽离公共包的最小size
      maxSize: 0, // 最大size
      minChunks: 1, // 某个模块至少被多少代码块引用,才会被提取成新的chunk
      maxAsyncRequests: 5, // 分割后,按需加载的代码块最多允许的并行请求数,在webpack5里默认值变为6
      maxInitialRequests: 3, // 分割后,入口代码块最多允许的并行请求数,在webpack5里默认值变为4
      automaticNameDelimiter: "~", // 用于生成的名称的分隔符
      automaticNameMaxLength: 30, // 生成的块的名称字符的最大值
      name: true, // 生成块的名称,为true时,将根据块和缓存组密钥自动生成名称
      cacheGroups: {
        // 将体积较大的包单独分离出来,减少vendor的体积,加快首屏速度
        vendors: {
          name: "chunk-vendors", // enforce默认为false下不能直接设置name值,否则打包会失败
          test: /[\\/]node_modules[\\/]/,
          priority: -10, // 表示缓存的优先级;
          reuseExistingChunk: true // 表示是否使用缓存
        }
      }
    }
  }
}

简单介绍其中的几个重要参数:

  • minSize:当公共组件/包的大小超过 minSize 值时,才会抽离成单独的包
  • minChunks:当某个组件/包的引用次数超过 minChunks 时,会抽离成单独的包
  • reuseExistingChunk:当包未更改时是否直接使用之前的包

Warning:

  • minChunks 好像不生效?
  • minSizeminChunks 同时触发时才会拆包还是触发其中一个条件即拆包?

# 2021 年 10 月 23 日

# 数组新增的方法(12.15 补)

  • includes:表示某个数组是否包含给定的值,第二个参数为开始搜索的起始位置
  • flat:表示将嵌套的数组“拉平”,但默认只会“拉平”一层
  • at:接受一个整数作为参数,返回数组对应位置的成员

# 对象新增的方法(12.15 补)

  • Object.is :用于判断两个值是否相等,与 === 相似,但 +0 不等于 -0 以及 NaN 等于自身
  • Object.assign:用于对象的合并,将源对象的所有可枚举属性复制到目标对象
  • Object.fromEntries:用于将键值对数组转换为对象
Object.fromEntries([
  ["foo", "bar"],
  ["baz", 42]
])
// { foo: "bar", baz: 42 }

# 2021 年 10 月 24 日

# super 关键字(12.15 补)

ES6 新增了一个关键字 super,指向当前对象的原型对象。与之相类似的是 this 关键字,指向函数所在的当前对象

const proto = {
  foo: "hello"
}

const obj = {
  foo: "world",
  find() {
    return super.foo
  }
}

Object.setPrototypeOf(obj, proto)
obj.find() // "hello"

注意super 关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。

# Object.setPrototypeOf(12.15 补)

Object.setPrototypeOf 方法可将一个指定对象的原型到另外一个对象或 null,是修改对象原型更合适的方法

注意:由于现代 JavaScript 引擎优化属性访问所带来的特性的关系,更改对象的 [[Prototype]] 在各个浏览器和 JavaScript 引擎上都是一个很慢的操作。

# 2021 年 10 月 25 日

# 浏览器渲染

收录一些关键字段

  • Chrome 浏览器,渲染引擎产生多个实例,对于每一个 tab 都是一个独立的过程(其他浏览器不同)
  • 页面的渲染是一个渐进的过程,为了更好的用户体验,渲染引擎将尽可能在浏览器中显示内容,因此会提前解析部分内容和显示,剩下的内容从网络返回回来再继续解析(通过浏览器 network 面板下打开 Capture screenshots可看出)
  • 词法分析是将输入信息切成小令牌(token)的过程。小令牌是语言词汇,即有效建筑模块的集合。
  • 非可视化元素将不会插入到渲染树中。如 head 元素、display:none 的元素;但如 opacity:0 的元素还会插入

# 2021 年 10 月 26 日

# shim 预置依赖

  • 当需要全局引入依赖的时候(虽然不推荐,但是真的香)如 lodash、jQuery
  • 当需要对某些依赖进行 polyfill 时

以上能力都是基于 ProvidePlugin 实现

贴个文档地址吧:

# webpack 构建性能

webpack 官方提供的构建优化最佳实践:构建性能 🚀 (opens new window)

# 2021 年 10 月 27 日

# Symbol(12.16 补)

ES6 引入的一种新的原始数据类型(JavaScript 第七种数据类型),表示独一无二的值。

Symbol 可以接收一个字符串作为参数,如果是对象,那么会调用其 tostring 方法

let s = Symbol("foo")
typeof s // "symbol"

const obj = {
  toString() {
    return "abc"
  }
}
const sym = Symbol(obj)
sym // Symbol(abc)

Symbol 作为属性名,遍历对象的时候,只能通过 Object.getOwnPropertySymbols() 方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。

Symbol 的其他方法:

  • Symbol.for():替换或新建一个新的 Symbol
  • Symbol.keyFor():查找并返回一个已登记的 Symbol 类型值的 key

其他相关方法可查阅文档 🚀 (opens new window)

# 2021 年 10 月 28 日

# Reflect.ownKeys(12.16 补)

可以返回所有类型的键名,包括常规键名和 Symbol 键名

let obj = {
  [Symbol("my_key")]: 1,
  enum: 2,
  nonEnum: 3
}
Reflect.ownKeys(obj)
//  ["enum", "nonEnum", Symbol(my_key)]

# 2021 年 10 月 29 日

# Set(12.21 补)

ES6 提供的新数据结构 Set,类似于数组,但值都是唯一的。(在 Set 中认为 NaN 等于其本身,与 === 不一样)

相关实例属性和方法

  • size:返回总数
  • add:添加某个值
  • delete:删除某个值
  • has:判断是否存在
  • clear:清除所有值
  • forEachkeysvaluesentries:遍历方法

# WeakSet(12.21 补)

WeakSetSet 相似,但有以下两个区别:

  • WeakSet 的成员只能是对象(任何具有 Iterable 接口的对象,都可以作为 WeakSet 的参数)
  • WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用
  • WeakSet 不能遍历

相关方法:

  • has:返回是否存在
  • add:添加某个成员
  • delete:删除某个成员

WeakSet 的一个用处,是储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏

# 2021 年 10 月 30 日

# Map(12.22 补)

ES6 提供的新数据结构 Map,类似于对象,是键值对的集合,但“键”的范围不限于字符串。相关增删查的方法同 Set(注意一点:Map 的遍历顺序就是插入的顺序)

Map 转对象

function strMapToObj(strMap) {
  let obj = Object.create(null)
  for (let [k, v] of strMap) {
    obj[k] = v
  }
  return obj
}

# WeakMap(12.22 补)

WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合,但是有以下两点不同:

  • WeakMap 只接受对象为键名
  • WeakMap键名所指向的对象,不计入垃圾回收机制
const e1 = document.getElementById("foo")
const e2 = document.getElementById("bar")
const arr = [
  [e1, "foo 元素"],
  [e2, "bar 元素"]
]

上面代码中,e1e2 是两个对象,我们通过 arr 数组对这两个对象添加一些文字说明。这就形成了 arre1e2 的引用。一旦不再需要这两个对象,我们就必须手动删除这个引用,否则垃圾回收机制就不会释放 e1e2 占用的内存。

# 2021 年 10 月 31 日

# 理解构造函数

可以理解为一个工厂,用于批量生产/初始化具有某些“特定功能”的对象,即拥有特定的属性和方法

  • 构造函数的首字母一定要大写
  • 需要配合 new 使用才有意义

new 的用处:

  • 在内存中创建一个新的对象
  • this 指向这个对象
  • 执行构造函数的代码(通常是赋予属性、方法)
  • 返回这个新对象
Last Updated: 12/28/2021, 4:41:47 PM