# 笔记
# 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
对象到控制台的样子挺怪的,不直观
# 2021 年 10 月 2 日
# vue2 vs vue3 周期函数(11.4 补)
新增:
- beforeUnmount:在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。
- unmounted:卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。
- renderTracked:跟踪虚拟
DOM
重新渲染时调用。钩子接收debugger event
作为参数。此事件告诉你哪个操作跟踪了组件以及该操作的目标对象和键。 - renderTriggered:当虚拟
DOM
重新渲染被触发时调用。和renderTracked
类似,接收debugger event
作为参数。此事件告诉你是什么操作触发了重新渲染,以及该操作的目标对象和键。
删除:
- beforeDestory
- destoryed
准确的说,应该是 beforeDestory
和 destoryed
改成了 beforeUnmount
和 unmounted
,并且新增了两个钩子函数用于追踪数据的变化
注意在 vue3
的 composition API
中,是没有 beforeCreate
和 create
这两个钩子函数,只有 setup
,并且 composition API
中是用 onX
来注册对应的钩子函数
# 2021 年 10 月 3 日
# 自定义指令(11.5 补)
vue3
相较于 vue2
,在钩子函数上做了一些改动,为了更好的跟生命周期钩子函数保持一致;除此之外,参数中 expression
(字符串形式的指令表达式如:'1+1
') 将不在使用
(幸好常用的几个钩子变化不大 🤣)
vue3
指令钩子函数:
created
- 新增!在元素的 attribute 或事件监听器被应用之前调用。bind
→beforeMount
inserted
→mounted
beforeUpdate
:新增!在元素本身被更新之前调用,与组件的生命周期钩子十分相似。update
→ 移除!该钩子与 updated 有太多相似之处,因此它是多余的。请改用 updated。componentUpdated
→updated
beforeUnmount
:新增!与组件的生命周期钩子类似,它将在元素被卸载之前调用。unbind
->unmounted
# 2021 年 10 月 4 日
# Devtools Beta(11.15 补)
如果发现自己的 vue3
项目不能使用 Devtools
了,试试安装一下 beta
版的开发者工具吧
# 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
的值
# 缓存位置
- 比较大的
JS
、CSS
文件会被丢进磁盘,反之丢进内存 - 内存使用率比较高的时候,文件优先进入磁盘
# 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
),全局替换searchValue
为replacement
,返回一个新的字符串,不改变原先的字符串
# 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)
如何解决?
- 转成整数再相加
- 使用 Number.EPSILON 🚀 (opens new window)
# 2021 年 10 月 11 日
# changeOrigin
proxy
代理的选项之一,默认值为 false
,会保留主机头的来源。即后端获取到接口请求头中的 host
值即为 devServer
下 host
的值
若设置为 true
,则 host
值为 proxy
下 target
的值
因此(猜测)可以设置 changeOrigin
为 true
,绕过后端的 host
检查
# 2021 年 10 月 12 日
# Git Hook
在提交代码到 gitlab
时,增加一层 hook
限制
vue-cli
创建的项目会自动安装 yorkie
(fork
自 husky
,但与后者不兼容)搭配 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 补)
ES6
在 Number
对象上新增了一个极小的常量 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()
:计算立方根
# 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
运行结束,将结果返回到 A
,B
的调用帧才会消失。如果函数 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 |
- 处理
css
的loader
顺序为:css-loader
->style-loader
- 处理
scss
文件需要使用sass-loader
,前提是需要安装node-sass
依赖 - 若要将小
icon
转换成base64
,需要用到url-loader
url-loader
和file-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
对象,同时修改 entry
和 output
参数
- 更多相关 api 请查阅:html-webpack-plugin 🚀 (opens new window)
# clean-webpack-plugin
每次构建项目之前清理 /dist 文件夹
使用时需要注意:
// webpack.config.js
// const CleanWebpackPlugin = require('clean-webpack-plugin') 错误
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
plugins: [
new CleanWebpackPlugin()
],
Otherwise:可以借助 node.js
的 file
模块进行文件夹删除
思考: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 | 无源代码内容 |
- 相关使用场景可参考:devtool 🚀 (opens new window)
- 相关测试效果用例:source-map 🚀 (opens new window)
# 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
文件保留在内存中,然后将它们 serve
到 server
中,相当于部署在 server
的根路径下
相关原理内容较多,可参考以下链接:
# webpack-dev-middleware
webpack-dev-middleware
是一个封装器(wrapper
),它可以把 webpack
处理过的文件发送到一个 server
。 webpack-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
好像不生效?minSize
和minChunks
同时触发时才会拆包还是触发其中一个条件即拆包?
# 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
:清除所有值forEach
、keys
、values
、entries
:遍历方法
# WeakSet(12.21 补)
WeakSet
与 Set
相似,但有以下两个区别:
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 元素"]
]
上面代码中,e1
和 e2
是两个对象,我们通过 arr
数组对这两个对象添加一些文字说明。这就形成了 arr
对 e1
和 e2
的引用。一旦不再需要这两个对象,我们就必须手动删除这个引用,否则垃圾回收机制就不会释放 e1
和 e2
占用的内存。
# 2021 年 10 月 31 日
# 理解构造函数
可以理解为一个工厂,用于批量生产/初始化具有某些“特定功能”的对象,即拥有特定的属性和方法
- 构造函数的首字母一定要大写
- 需要配合
new
使用才有意义
new
的用处:
- 在内存中创建一个新的对象
- 让
this
指向这个对象 - 执行构造函数的代码(通常是赋予属性、方法)
- 返回这个新对象