# 笔记
# 2021 年 12 月 1 日
# 解构赋值
收录了一些结构赋值常用的场景~
文档地址:ES6 官方文档 🚀 (opens new window)
# 交换变量的值
let x = 1
let y = 2
;[x, y] = [y, x]
# 从函数返回多个值
function example() {
return {
foo: 1,
bar: 2
}
}
let { foo, bar } = example()
// 同返回数组
# 函数参数的定义
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
# 提取 JSON 数据
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
}
let { id, status, data: number } = jsonData
# 函数参数的默认值
jQuery.ajax = function(
url,
{
async = true,
beforeSend = function() {},
cache = true,
complete = function() {},
crossDomain = false,
global = true
// ... more config
} = {}
) {
// ... do stuff
}
# 遍历 MAP 结构
const map = new Map()
map.set("first", "hello")
map.set("second", "world")
for (let [key, value] of map) {
console.log(key + " is " + value)
}
# 输入模块的指定方法
const { SourceMapConsumer, SourceNode } = require("source-map")
# 2021 年 12 月 2 日
# BigInt
号称 ECMAScript 的第八种数据类型。有以下特点
- 必须加上后缀
n
- 只用于表示整数
- 不限于各种进制
- 没有位数限制
- 可以使用负号,但不能使用正号(
+
) - 与普通整数不相等:
42n === 42
的结果为false
typeof
返回的值为BigInt
可以通过 BigInt()
函数将其他类型转换成 BigInt
BigInt
还包含 toString()
、valueOf()
、toLocaleString()
等一些方法 🚀 (opens new window)
# 2021 年 12 月 3 日
缺席...
# 2021 年 12 月 4 日
缺席...
# 2021 年 12 月 5 日
缺席...
# 2021 年 12 月 6 日
# referer === referrer ?
HTTP
中的 referer
是请求头的一个字段,用于表示这个请求是从哪个来源网页发出的,即可以检查访客从哪里来,也可以用于对付伪造的跨网站请求(csrf)
referer
的正确英文是 referrer
,这是早期 HTTP
规范当中存在的拼写错误,后来为了保持向下兼容就将错就错。一般说 referrer
指的就是 document.referrer
,
因引用地址信息 referer
可能会带来隐私权问题,不少网页浏览器允许用户设置不要提交这个信息,有些代理服务器和防火墙也会将引用地址信息过滤掉,以避免外部获知非公开的网络地址。因此如果想要保证 referer 信息的上传,可以手动设置一下 meta 头
<meta content="always" name="referrer" />
referer
不被允许修改,浏览器会提示:Refused to set unsage header "Referer"
其他不能修改的请求头如下:
Accept-Charset
Accept-Encoding
Access-Control-Request-Headers
Access-Control-Request-Method
Connection
Content-Length
Cookie
Cookie2
Date
DNT
Expect
Feature-Policy
Host
Keep-Alive
Origin
Proxy-
Sec-
Referer
TE
Trailer
Transfer-Encoding
Upgrade
Via
# 2021 年 12 月 7 日
# Content-Security-Policy
作为前端开发,应该都知道浏览器的同源策略:浏览器仅加载同协议、域名及端口的脚本文档。Content-Security-Policy
也就是浏览器内容安全策略 CSP
,其安全模式也是来源于“同源策略”,通过控制脚本/资源的来源来达到保证安全的目的。CSP
是一个可以显著降低 XSS
攻击的风险和影响的一种防护功能
CSP
可通过 http
标头(首选且优先级较高,一般在 nginx
配置)或 meta
标签进行配置,相关可配置资源有如下:
<!-- meta标签使用方式 -->
<meta
http-equiv="Content-Security-Policy"
content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'"
/>
name | policy |
---|---|
base-uri | 限制 base 标签内容来源 |
child-src | 限制 iframe 内容来源 |
connect-src | 限制 http 请求地址(XHR 、WS 、EventSource ) |
font-src | 限制字体文件来源地址 |
form-action | 限制 form 表单提交地址 |
frame-ancestors | 限制被 iframe 嵌套的来源地址(不能在 HTML 上使用) |
frame-src | 已弃用。请改用 child-src |
img-src | 限制图片资源地址 |
media-src | 限制音视频资源地址 |
object-src | 限制 flash 来源地址(可执行外部 js 代码) |
plugin-types | 限制插件来源地址 |
report-uri | 限制发送报告的地址(不能用于 meta ) |
script-src | 限制 js 来源地址 |
style-src | 限制 css 来源地址 |
upgrade-insecure-requests | 指使 UserAgent 将 HTTP 更改为 HTTPS |
需要针对某一项指令配置多个值时,可以直接添加多个值。注意中间只需要空格隔开。多项指令用 ;
隔开
script-src https://host1.com https://host2.com
如果不针对某一项指令设置值时,默认以 * 作为有效来源(即相当于无限制)。可以使用 default-src
指令替换默认行为,但需要注意,default-src
主要针对 -src
类指令的来源进行限制
指令的值可以指定域名地址,也可以有以下(不限,部分项没列举出来,只是常用项)几种关键字:
name | desc |
---|---|
none | 不执行任何匹配 |
self | 与当前来源(非子域)匹配 |
unsafe-inline | 允许内联 js 和 css |
unsafe-eval | 允许执行 eval 等命令 |
如果设置了 unsafe-inline
或 unsafe-eval
,需要做进一步的处理。由于 CSP 可以明确规定哪些为浏览器可接受的资源集,如果是采用 XSS 攻击(内联脚本),这是无法处理的。如果要使用的话,需要使用一个加密随机数(仅使用一次)或一个哈希值将指定脚本列入白名单
<!-- 生成加密随机数 -->
<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa"></script>
Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
<!-- 计算脚本自身的SHA哈希值并插入到script-src指令中 -->
Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='
<!-- 或者 -->
相关文档:网络基础 🚀 (opens new window)
# 2021 年 12 月 8 日
# Mixed Contents
混合内容(mixed contents
):当 https
页面包含有 http
的内容时,即为 mixed-contents
。这种页面部分加密,部分未加密,容易受到攻击
从 Chrome 79
开始,Chrome
将逐渐转向默认阻止所有混合内容,并且会自动将混合资源升级到 https://
可以通过 CSP
配置相关指令(取值目前就找到一个...):
<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content" />
谷歌日志文档:https://blog.chromium.org/2019/10/no-more-mixed-messages-about-https.html (opens new window)
# 2021 年 12 月 9 日
# Referrer-Policy
这里的 Referrer-Policy
是指 http
响应头的参数,其用来监管哪些访问来源信息会在 referrer
中发送(可以理解为 A
页面跳转到 B
页面时所包含的 A
的来源信息,可以通过 document.referrer
获取)。主要有以下几种情况:
值 | 描述 |
---|---|
no-referrer | 不会发送 |
no-referrer-when-downgrade(default) | 同安全级别下会发送(https=>https) |
origin | 仅发送域名 |
origin-when-cross-origin | 同源下发送完整的 URL,否则仅发送域名 |
same-origin | 同源下发送 |
strict-origin | 同安全级别下发送域名 |
strict-origin-when-cross-origin | 同源下发送完整 url,同安全级别发送域名 |
unsafe-url | 发送完整 url |
可通过 meta
标签进行设置。
<meta name="referrer" content="no-referrer" />
测试发现:修改了 Referrer-Policy
后,js
、css
等资源都更改成对应值,但对 index.html
(localhost
)这个文件却不一定,其他如:部分图片、字体文件等也不一定会跟随改变。具体原因暂未查明
TIPS:这部分内容跟 referrer、CSP 等内容挺相关的,可以结合一起看看会更好一些(可直接搜索)
参考文档:
- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Referrer-Policy (opens new window)
- https://segmentfault.com/a/1190000022101053?utm_source=sf-similar-article (opens new window)
- https://segmentfault.com/a/1190000017896469 (opens new window)
# 2021 年 12 月 10 日
# Cannot read property 'tapPromise' of undefined
这里是指使用 compression-webpack-plugin
时遇到 Cannot read property 'tapPromise' of undefined
异常的问题及解决方案
出现这个异常的问题是由于 webpack
版本导致。最新版的插件仅适用于 webpack5
,而对于 webpack4
已经不支持。因此需要降低 compression-webpack-plugin
插件的版本。
经测试 6.1.1
版本可满足 webpack4 的需求,同时也是下载次数较多的一个版本
# 2021 年 12 月 11 日
# MaxInitialRequests(12.13 补)
MaxInitialRequests
是利用 SplitChunksPlugin
做 webpack
代码分割优化的其中一个选项,是指入口代码块最多允许的并行请求数。
// 测试使用版本
"webpack": 4.46.0
"vue": 2.6.14
# 2021 年 12 月 12 日
# Preload 和 Prefetch
Preload
提前加载资源但并未执行,等真正被用到的时候才会立即执行。Prefetch
是告诉浏览器未来可能会用到的某个资源,浏览器会在空闲时间去加载对应的资源
注意:一旦页面关闭了,preload
会立即停止获取资源,而对于 prefetch
资源,即使页面关闭,prefetch
发起的请求仍会进行不会中断
注意:使用时可以考虑一下资源使用情况:vue
项目中默认会给首页需要加载的 js/css
文件加上 preload
,而非首页的文件加上 prefetch
。但实际上并非所有页面都会被用户访问,即并非需要加载所有的资源文件。所以需要衡量一下。大致可以考虑以下几点吧:
- 项目是属于对外的还是对内的(门户网站/管理后台)
- 项目是运行在移动端还是 PC 端(移动端可能会浪费用户流量)
- 项目本身的优化程度(使用预加载对项目性能的提升情况)
- 服务器带宽(带宽较小的情况下还是有必要考虑一下)
使用方式如下:
<!-- vue项目中已默认开启prefetch和preload -->
<head>
<link href=/path/some.js rel=preload/prefetch as=script>
<!-- 注意文件类型,as=script/style -->
<link href=/path/some.css rel=preload/prefetch as=style>
</head>
相关文章:https://zhuanlan.zhihu.com/p/48521680 (opens new window)
兼容性:https://caniuse.com/?search=prefetch (opens new window)
# 2021 年 12 月 13 日
# JS 异步加载之 async、defer
async
和 defer
平时用的比较少,一般 vue
项目下的话都是直接使用楼上的 preload
和 prefetch
。async
和 defer
都是指异步加载脚本。为了更深入理解一些,所以手动去测试了一下。相关环境及说明如下:
- 运行环境为
live-server
- 脚本都为同步脚本
- 浏览器版本:谷歌(
96.0.4664.93
)
测试代码:https://github.com/Real102/testAsyncDefer.git (opens new window)
测试结果:
- 只有
async
脚本下:脚本的执行在DCL(DOMContentLoaded)
之后 - 只有
defer
脚本下:脚本执行在DCL
之前 async
脚本和defer
脚本:defer
脚本始终在DCL
之前执行。如果async
脚本在前,其脚本的执行时间跟脚本的执行时长有关系,在DCL
前后都有;而大部分async
脚本在后,其脚本执行时间都在DCL
后async
脚本和普通脚本下:普通脚本会在DCL
之前执行,但async
脚本执行时间在DCL
前后不定defer
脚本和普通脚本下:defer
脚本在普通脚本后,且都在DCL
之前- 混合脚本下:
async
脚本不稳定,其他都在DCL
之前
大致结论:
- 对于脚本执行顺序,
async
的脚本是加载完就执行;defer
脚本是并行加载,且刚好是在DCL
事件前普通脚本执行后再执行(这一点没什么疑问,都一样) async
脚本执行顺序按加载完成顺序依次进行,defer
脚本执行顺序根据在页面中引入顺序依次进行async
脚本的执行时间点不定,如果只有async
脚本,那么会在DCL
事件后,如果有defer
或普通脚本,那么在DCL
前后都可能出现defer
脚本与放置在body
结束标签前的脚本相似,只不过defer
脚本会提前先加载好
使用选择:
- 如果脚本没有任何依赖,可以使用
async
- 如果脚本依赖于另一个脚本,那么使用
defer
- 如果脚本比较少并且作为另外一个脚本的依赖,那么可以使用内联
script
并且加上async
属性
文字内容有点多,阅读起来可能有点乱。建议自己手动操作一遍,配合浏览器开发者工具的 performance
分析,理解会更深入一些
相关文档:
- 页面生命周期 (opens new window)
- 使用 Preload/Prefetch 优化你的应用 (opens new window)
- async vs defer attributes (opens new window)
# 2021 年 12 月 14 日
缺席...
# 2021 年 12 月 15 日
# 运算符扩展
指数运算符:
ES2016
新增的指数运算符 **
,在此之前使用 Math
的方法Math.pow
链判断运算符:
业务逻辑中经常需要用到的一种情况:判断对象内部的某个属性,比如读取 userInfo.userName
这个属性,需要写成这样:
const userName = (userInfo && userInfo.userName) || "default"
如果对象层次再深一些的话,需要判断的次数就越多。因此,在 ES2020
引入了链判断运算符?.
const userName = userInfo?.userName || "default"
除此之外,还可以判断对象方法是否存在,如果存在就立即执行
iterator.return?.()
Null 判断运算符:
ES2020
引入一个新的 Null
判断运算符??
,它的行为类似于||
,当运算符左侧的值为 null
或 undefined
时,返回右侧的值
逻辑赋值运算符:
ES2021
引入了三个新的逻辑赋值运算符,将逻辑运算符与赋值运算符进行结合:||=
、&&=
、??=
。相当于先进行逻辑运算,然后根据运算结果,再进行赋值运算
// 老的写法
user.id = user.id || 1
// 新的写法
user.id ||= 1
# 2021 年 12 月 16 日
# splitChunks 小 tips
背景前提:vue 项目下的 splitchunks 配置
- 如果在首页(
home/app.vue
)页引入的组件,打包时不论minChunks
和minSize
条件达到与否,都会打包到app.js
里面 - 未在首页引入的组件,且
chunks != initial
时,只要符合minSize
且被引用次数超过一次的代码块,会被提取到一个新的chunk
,命名为:fileNameA~fileNameB.hash.js
chunks = initial
时,不论满足与否,都不会抽取出来
# 2021 年 12 月 17 日
缺席...
# 2021 年 12 月 18 日
缺席...
# 2021 年 12 月 19 日
缺席...
# 2021 年 12 月 20 日
# JSX in vue
render
函数内同样只能有一个根元素,包括赋值到变量中的HTML
结构slot
不能直接<slot></slot>
,需要使用this.$slot.default
,同函数式组件- 行内的三元运算符需要放在双大括号内
- 不能使用 v-if,可以使用 v-show
- v-model 需要自己实现(似 vue3 的方式)
- 事件监听跟原生 js 类似
on-click
,如果方法需要携带参数,需要通过一个function
返回on-click={() => this.handleClick(payload)}
# 2021 年 12 月 21 日
# animation
animation-delay
:延迟时间animation-direction
:每次运行完后是反向运动还是重新回到开始的位置重复运行(normal
、alternate
、reverse
、alternate-reverse
)animation-duration
:动画周期(second
)animation-iteration-count
:重复次数(number
、infinite
)animation-name
:关键帧名称animation-play-state
:允许暂停和恢复动画(running
、paused
)animation-timing-function
:动画速度(ease
、linear
、step-start
、cubic-bezier
等)animation-fill-mode
:运动完后的状态是初始还是结束时的状态(none
、forwords
、backwords
、both
)
# 2021 年 12 月 22 日
缺席...
# 2021 年 12 月 23 日
缺席...
# 2021 年 12 月 24 日
# 观察者模式与发布-订阅模式
观察者模式:
观察者模式为对象间的一种一对多的依赖关系,多个观察者对象同时监听一个目标对象,当目标对象发生变化时,主动通知所有观察者对象,使他们可以自动更新
观察者模式的优势在于形成了一个响应系统,在目前变化时就会通知监听者;而缺点是目标与监听者是耦合在一起的
订阅-发布模式:
订阅-发布模式并非是 24
种基本设计模式中的一个,而是观察者模式的一个别称,或者说是一个更优解。订阅-发布模式下的订阅者与发布者没有直接的联系,是完全解耦的,他们之间都是通过事件中心(调度中心)来进行联系。发布者不需要关心谁是订阅者,而订阅者同样不用关系谁是发布者。
订阅-发布模式的优势在于发布者和订阅者是完全解耦的,两者是由事件中心来进行联系;而缺点一方面会导致数据交互变得复杂,另一方面是会导致性能的消耗会增加,创建订阅者及维护事件队列都需要耗费一定内存,订阅者创建了就会一直存在,如果没有发布,那么就永远不会被响应。
不过 vue
中提供了 object.freeze
方法,就是为了避免以上情况出现,主动将不必要的数据移除监听
参考文档:https://www.cnblogs.com/onepixel/p/10806891.html (opens new window)
# 2021 年 12 月 25 日
# HTML 语义化属性
aria-*:
aria
的全程为 Accessible Rich Internet Application
,意思是无障碍富互联网应用
这个属性的主要用途是:增强网页在残障辅助阅读设备上的识别读取。如当页面聚焦到某一添加了 aria-label
属性的标签时,屏幕阅读器会读出 aria-label
的值,但在页面上不会有任何显示效果的区别。
role:
本质上也是用于增强语义性,当 HTML
标签不能充分表达语义性的时候,可以借助 role
来说明(role
的作用是描述一个非标准的 tag
的实际作用),可以增强其可访问性、可用性和可交互性
比如用 div
做 button
,那么可以设置 role='button'
,辅助工具就可以认出这实际上是 button
# 2021 年 12 月 26 日
缺席...
# 2021 年 12 月 27 日
缺席...
# 2021 年 12 月 28 日
缺席...
# 2021 年 12 月 29 日
缺席...
# 2021 年 12 月 30 日
缺席...
# 2021 年 12 月 31 日
# RAF 动画
window.requestAnimationFrame(callback)
意思是:通知浏览器执行一个动画,并且要求在下一次重绘前调用指定的回调函数更新动画
一般电脑屏幕的刷新频率为 60Hz
,1s
的时间刷新 60
次,即 16~17ms/次
。RAF
回调函数执行次数通常与浏览器刷新次数相匹配,当屏幕上发生视觉变化时,RAF
将可以保证 JavaScript
在帧开始时运行。而如果是使用 settimeout
或 setinterval
时,有可能会使动画在帧的结尾时才调用,因此可能会错过上一帧,导致卡顿
以下是 element-ui
回到顶部的组件的 RAF
案例,兼容了不存在 RAF
的浏览器
请确保总是使用第一个参数(或其它获得当前时间的方法)计算每次调用之间的时间间隔,否则动画在高刷新率的屏幕中会运行得更快
const cubic = value => Math.pow(value, 3);
const easeInOutCubic = value => value < 0.5 ? cubic(value * 2) / 2 : 1 - cubic((1 - value) * 2) / 2;
scrollToTop() {
const el = this.el;
const beginTime = Date.now();
const beginValue = el.scrollTop;
const rAF = window.requestAnimationFrame || (func => setTimeout(func, 16));
const frameFunc = () => {
const progress = (Date.now() - beginTime) / 500;
if (progress < 1) {
el.scrollTop = beginValue * (1 - easeInOutCubic(progress));
rAF(frameFunc);
} else {
el.scrollTop = 0;
}
};
rAF(frameFunc);
}