exposir

exposir

Member Since 3 years ago

Beijing

Experience Points
4
follower
Lessons Completed
92
follow
Lessons Completed
200
stars
Best Reply Awards
55
repos

428 contributions in the last year

Pinned
⚡ Flutter、Dart
⚡ Record everything
⚡ Good for the brain
Activity
Oct
15
20 hours ago
push

exposir push exposir/just-react

exposir
exposir
exposir
exposir

Line36-mistake

当前状态为:- mountCallback会保存回调函数果作为value保存。

错误描述:行文词不达意,略有模糊。作者本人原意为:“会将回调函数作为value保存”,但却多了“果”字。

建议更改为:- mountCallback会将回调函数作为value保存。

exposir
exposir

Merge pull request #101 from SillyBoyXia/patch-2

感谢反馈~

commit sha: 37f2c95a399c9403566f7652ce982e3ed7c7e2b8

push time in 12 hours ago
push

exposir push exposir/vue

exposir
exposir

build(deps): bump tar from 4.4.8 to 4.4.15 (#12204)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

exposir
exposir

fix(v-on): add removing all dom event listeners when vnode destroyed (#10085)

exposir
exposir

chore: update backers [ci skip] (#12208)

exposir
exposir

chore: update sponsors [ci skip] (#12211)

exposir
exposir

chore: update sponsors [ci skip] (#12217)

exposir
exposir
exposir
exposir

build(deps): bump tar from 4.4.15 to 4.4.19 (#12250)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

exposir
exposir

chore: update backers [ci skip] (#12255)

Co-authored-by: Evan You [email protected]

exposir
exposir

chore: update sponsors [ci skip] (#12256)

Co-authored-by: Evan You [email protected]

exposir
exposir

feat(compiler): condenses staticClass whitespace (fix #12113) (#12195)

  • feat(compiler): template staticClass no longer preserves whitespace

Template static classes used to preserve whitespace after compilation, resulting in builds that had bigger file outputs with whitespace in component's staticClass attributes

fix #12113

  • refactor(refactor(web-compiler): removed ignore in regex): Removed ignore flag in regex

fix #12113

  • test(ssr-string.spec.js): Removed newline character, as whitespace is purged in static classes

There's no need to escape newlines in static classes, as they're now replaced with a single whitespace character

fix #12113

  • test(directives/class.spec.js): Added whitespace to test

fix #12113

  • Apply suggestions from code review

Co-authored-by: Eduardo San Martin Morote [email protected]

exposir
exposir

chore: update backers [ci skip] (#12257)

exposir
exposir

chore: update sponsors [ci skip] (#12278)

exposir
exposir
exposir
exposir

chore: update backers [ci skip] (#12308)

exposir
exposir

chore: update sponsors [ci skip] (#12310)

commit sha: 0368ba4f37ef1ef28eb785261ab76a68f7fa03dd

push time in 12 hours ago
Oct
11
4 days ago
Activity icon
created branch

exposir in exposir/bulk-emailing create branch master

createdAt 4 days ago
Activity icon
created repository
createdAt 4 days ago
started
started time in 4 days ago
Oct
6
1 week ago
started
started time in 1 week ago
Sep
23
3 weeks ago
Activity icon
issue

exposir issue exposir/Personal-Blog

exposir
exposir

稍后阅读

文章

React 框架运行时优化方案的演进

Separation of concerns with React hooks

Next.js 应用开发实践

自顶向下学 React 源码 - 思否编程 - 学编程,来思否,升职加薪快人一步

8 Projects to Build to Master Your Front-End Skills 🥇🏆 - DEV Community

五年 RN 前端经验虾皮面试成功经验

react-devtools - npm

GitHub - ykfe/fe-dev-playbook: 教你如何打造舒适、高效、时尚的前端开发环境

技术周报 · 前端如何赋能业务

When to pick Gatsby, Next.Js or Create React App - DEV Community

Typescript 边学边练

React Native 原理与实践

2020 年了,再不会 webpack 敲得代码就不香了(近万字实战)

如何优化 React 项目的性能

https://codepen.io/collection/MggMed?grid_type=list

JavaScript 参考 - JavaScript | MDN

全自动 jQuery 与渣男的故事

React Status 中文周刊 #47 - React 架构之道

https://hyf.js.org/react-naive-book/lesson11

Vue 项目打包部署总结

跟着官方文档能学懂 Hooks 就怪了

React 要重写文档了

一名 Vue 程序员总结的 React 基础

精读《React 18》

Cookie 的 SameSite 属性 - 阮一峰的网络日志

Sep
18
3 weeks ago
Activity icon
fork

exposir forked wangdoc/clang-tutorial

⚡ C 语言教程
exposir Updated
fork time in 3 weeks ago
started
started time in 3 weeks ago
Sep
17
4 weeks ago
Activity icon
issue

exposir issue exposir/Personal-Blog

exposir
exposir

牛肉干营养成分表

一般来说每100g牛肉,蛋白质含量越高越好,而钠的含量越低越好。优质的100g牛肉干包含蛋白质50g左右。100g鲜牛肉蛋白质含量在20g左右,所以牛肉干蛋白质含量越高代表着使用的生牛肉越多。又因为牛肉干一般都会加盐,所以钠的含量在2g左右,碳水化合物则是越低越好,因为这部分加的都是糖。

良品铺子 - 手撕风干牛肉

描述:

  • 高端零食 草原风味系列
  • 精选牛肉,12小时风干,劲道手撕风味!
  • 精选牛肉肉食材,挑紧实肉质,精修、切条,约12小时腌制入味,严控三个阶段不同温度,慢烘风干12小时,带来滋味丰富的五项口感,放肆撕扯,丝丝香浓,越嚼越带劲

营养成分表

| 项目 | 每100g | NRV | | ---- | ---- | | | 能量 | 1079kj | 13% | | 蛋白质 | 41.9g | 70% | | 脂肪 | 9.9g | 17% | | 碳水化合物 | 0g | 0% | | 纳 | 1458mg | 73% |

Sep
16
4 weeks ago
Activity icon
fork

exposir forked basarat/typescript-book

⚡ :books: The definitive guide to TypeScript and possibly the best TypeScript book :book:. Free and Open Source 🌹
exposir Updated
fork time in 4 weeks ago
started
started time in 4 weeks ago
Sep
15
1 month ago
Activity icon
issue

exposir issue exposir/Personal-Blog

exposir
exposir

闭包

定义

红宝书

内部函数泄漏到外部,形成闭包

MDN

闭包是指那些能够访问自由变量的函数。

那什么是自由变量呢?

自由变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量。

由此,我们可以看出闭包共有两部分组成:

闭包 = 函数 + 函数能够访问的自由变量

var a = 1;

function foo() {
  console.log(a);
}

foo();
  • 《JavaScript 权威指南》中就讲到:从技术的角度讲,所有的 JavaScript 函数都是闭包。

ECMAScript 中,闭包指的是:

  • 从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。
  • 从实践角度:以下函数才算是闭包:
  1. 即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)
  2. 在代码中引用了自由变量

分析

var scope = "global scope";
function checkscope() {
  var scope = "local scope";
  function f() {
    return scope;
  }
  return f;
}

var foo = checkscope();
foo();
  1. 进入全局代码,创建全局执行上下文,全局执行上下文压入执行上下文栈
  2. 全局执行上下文初始化
  3. 执行 checkscope 函数,创建 checkscope 函数执行上下文,checkscope 执行上下文被压入执行上下文栈
  4. checkscope 执行上下文初始化,创建变量对象、作用域链、this 等
  5. checkscope 函数执行完毕,checkscope 执行上下文从执行上下文栈中弹出
  6. 执行 f 函数,创建 f 函数执行上下文,f 执行上下文被压入执行上下文栈
  7. f 执行上下文初始化,创建变量对象、作用域链、this 等
  8. f 函数执行完毕,f 函数上下文从执行上下文栈中弹出

闭包的作用

  • 能够访问函数定义时所在的词法作用域(阻止其被回收)
  • 私有化变量
  • 模拟块级作用域
Activity icon
issue

exposir issue exposir/Personal-Blog

exposir
exposir

重排(reflow)和重绘(repaint)

重排(reflow)和重绘(repaint)

重排(reflow)

当 DOM 的变化影响了元素的几何信息(元素的的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。

重排也叫回流,简单的说就是重新生成布局,重新排列元素。

下面情况会发生重排:

  • 页面初始渲染,这是开销最大的一次重排
  • 添加/删除可见的 DOM 元素
  • 改变元素位置
  • 改变元素尺寸,比如边距、填充、边框、宽度和高度等
  • 改变元素内容,比如文字数量,图片大小等
  • 改变元素字体大小
  • 改变浏览器窗口尺寸,比如 resize 事件发生时
  • 激活 CSS 伪类(例如::hover)
  • 设置 style 属性的值,因为通过设置 style 属性改变结点样式的话,每一次设置都会触发一次 reflow
  • 查询某些属性或调用某些计算方法:offsetWidth、offsetHeight 等,除此之外,当我们调用 getComputedStyle 方法,或者 IE 里的 currentStyle 时,也会触发重排,原理是一样的,都为求一个“即时性”和“准确性”。

重排优化建议

减少重排范围

  • 尽可能在低层级的 DOM 节点上,而不是像上述全局范围的示例代码一样,如果你要改变 p 的样式,class 就不要加在 div 上,通过父元素去影响子元素不好。
  • 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局。

减少重排次数

  • 样式集中改变
  • 分离读写操作
  • 将 DOM 离线(display:none;然后进行处理)
  • 使用 absolute 或 fixed 脱离文档流
  • 优化动画(position 属性为 absolute 或 fixed ;启用 GPU 加速:Canvas2D、transitions、transforms3D、WebGL、video )

重绘(Repaints)

  • 当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。
Activity icon
issue

exposir issue exposir/Personal-Blog

exposir
exposir

输入一个 URL 到显示出一个页面,都经历了哪些过程

输入一个 URL 到显示出一个页面,都经历了哪些过程

概述

  • DNS 解析:将域名解析成 IP 地址
  • TCP 连接:TCP 三次握手
  • 发送 HTTP 请求
  • 服务器处理请求并返回 HTTP 报文
  • 浏览器解析渲染页面
  • 断开连接:TCP 四次挥手

URL

URL(Uniform Resource Locator),统一资源定位符,用于定位互联网上资源,俗称网址。比如 http://www.w3school.com.cn/ht...,遵守以下的语法规则:

scheme://host.domain:port/path/filename
  • scheme - 定义因特网服务的类型。常见的协议有 http、https、ftp、file,其中最常见的类型是 http,而 https 则是进行加密的网络传输。
  • host - 定义域主机(http 的默认主机是 www)
  • domain - 定义因特网域名,比如 http://w3school.com.cn
  • port - 定义主机上的端口号(http 的默认端口号是 80)
  • path - 定义服务器上的路径(如果省略,则文档必须位于网站的根目录中)。
  • filename - 定义文档/资源的名称

域名解析(DNS)

什么是域名解析

DNS 协议提供通过域名查找 IP 地址,或逆向从 IP 地址反查域名的服务。DNS 是一个网络服务器,我们的域名解析简单来说就是在 DNS 上记录一条信息记录。

例如 baidu.com  220.114.23.56(服务器外网IP地址)80(服务器端口号)

浏览器如何通过域名去查询 URL 对应的 IP 呢

  • 浏览器缓存:浏览器会按照一定的频率缓存 DNS 记录。
  • 操作系统缓存:如果浏览器缓存中找不到需要的 DNS 记录,那就去操作系统中找。
  • 路由缓存:路由器也有 DNS 缓存。
  • ISP 的 DNS 服务器:ISP 是互联网服务提供商(Internet Service Provider)的简称,ISP 有专门的 DNS 服务器应对 DNS 查询请求。
  • 根服务器:ISP 的 DNS 服务器还找不到的话,它就会向根服务器发出请求,进行递归查询(DNS 服务器先问根域名服务器.com 域名服务器的 IP 地址,然后再问.baidu 域名服务器,依次类推)

HTTP 请求

TCP 三次握手

  • 客户端发送一个带 SYN=1,Seq=X 的数据包到服务器端口(第一次握手,由浏览器发起,告诉服务器我要发送请求了)
  • 服务器发回一个带 SYN=1, ACK=X+1, Seq=Y 的响应包以示传达确认信息(第二次握手,由服务器发起,告诉浏览器我准备接受了,你赶紧发送吧)
  • 客户端再回传一个带 ACK=Y+1, Seq=Z 的数据包,代表“握手结束”(第三次握手,由浏览器发送,告诉服务器,我马上就发了,准备接受吧)

为什么需要三次握手

  • 谢希仁著《计算机网络》中讲“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。

发送 HTTP 请求

TCP 三次握手结束后,开始发送 HTTP 请求报文。

请求报文由请求行(request line)、请求头(header)、请求体三个部分组成

1. 请求行包含请求方法、URL、协议版本

  • 请求方法包含 8 种:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE。
  • URL 即请求地址,由 <协议>://<主机>:<端口>/<路径>?<参数> 组成
  • 协议版本即 http 版本号
POST /chapter17/user.html HTTP/1.1

以上代码中“POST”代表请求方法,“/chapter17/user.html”表示 URL,“HTTP/1.1”代表协议和协议的版本。现在比较流行的是 Http1.1 版本

2. 请求头包含请求的附加信息,由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。

请求头部通知服务器有关于客户端请求的信息。它包含许多有关的客户端环境和请求正文的有用信息。其中比如:Host,表示主机名,虚拟主机;Connection,HTTP/1.1 增加的,使用 keepalive,即持久连接,一个连接可以发多个请求;User-Agent,请求发出者,兼容性以及定制化需求。

3. 请求体,可以承载多个请求参数的数据,包含回车符、换行符和请求数据,并不是所有请求都具有请求数据。

name=tom&password=1234&realName=tomson

上面代码,承载着 name、password、realName 三个请求参数。

服务器处理请求并返回 HTTP 报文

MVC

后台开发现在有很多框架,但大部分都还是按照 MVC 设计模式进行搭建的。

MVC 是一个设计模式,将应用程序分成三个核心部件:模型(model)-- 视图(view)--控制器(controller),它们各自处理自己的任务,实现输入、处理和输出的分离。

首先浏览器发送过来的请求先经过控制器,控制器进行逻辑处理和请求分发,接着会调用模型,这一阶段模型会获取 redis db 以及 MySQL 的数据,获取数据后将渲染好的页面,响应信息会以响应报文的形式返回给客户端,最后浏览器通过渲染引擎将网页呈现在用户面前。

http 响应报文

响应报文由响应行(request line)、响应头部(header)、响应主体三个部分组成。

1. 响应行包含:协议版本,状态码,状态码描述

状态码规则如下:

  • 1xx:指示信息--表示请求已接收,继续处理。
  • 2xx:成功--表示请求已被成功接收、理解、接受。
  • 3xx:重定向--要完成请求必须进行更进一步的操作。
  • 4xx:客户端错误--请求有语法错误或请求无法实现。
  • 5xx:服务器端错误--服务器未能实现合法的请求。

2. 响应头部包含响应报文的附加信息,由 名/值 对组成

3. 响应主体包含回车符、换行符和响应返回数据,并不是所有响应报文都有响应数据

浏览器解析渲染页面

浏览器解析渲染页面分为一下五个步骤:

  1. 根据 HTML 解析出 DOM 树
  2. 根据 CSS 解析生成 CSS 规则树
  3. 结合 DOM 树和 CSS 规则树,生成渲染树
  4. 根据渲染树计算每一个节点的信息
  5. 根据计算好的信息绘制页面

1. 根据 HTML 解析 DOM 树

  • 根据 HTML 的内容,将标签按照结构解析成为 DOM 树,DOM 树解析的过程是一个深度优先遍历。即先构建当前节点的所有子节点,再构建下一个兄弟节点。
  • 在读取 HTML 文档,构建 DOM 树的过程中,若遇到 script 标签,则 DOM 树的构建会暂停,直至脚本执行完毕。

2. 根据 CSS 解析生成 CSS 规则树

  • 解析 CSS 规则树时 js 执行将暂停,直至 CSS 规则树就绪。
  • 浏览器在 CSS 规则树生成之前不会进行渲染。

3. 结合 DOM 树和 CSS 规则树,生成渲染树

  • DOM 树和 CSS 规则树全部准备好了以后,浏览器才会开始构建渲染树。
  • 精简 CSS 并可以加快 CSS 规则树的构建,从而加快页面相应速度。

4. 根据渲染树计算每一个节点的信息(布局)

  • 布局:通过渲染树中渲染对象的信息,计算出每一个渲染对象的位置和尺寸
  • 重排:在布局完成后,发现了某个部分发生了变化影响了布局,那就需要倒回去重新渲染。

5. 根据计算好的信息绘制页面

  • 绘制阶段,系统会遍历呈现树,并调用呈现器的“paint”方法,将呈现器的内容显示在屏幕上。
  • 重绘:某个元素的背景颜色,文字颜色等,不影响元素周围或内部布局的属性,将只会引起浏览器的重绘。
  • 回流:某个元素的尺寸发生了变化,则需重新计算渲染树,重新渲染。

断开连接

当数据传送完毕,需要断开 tcp 连接,此时发起 tcp 四次挥手。

  • 发起方向被动方发送报文,Fin、Ack、Seq,表示已经没有数据传输了。并进入 FIN_WAIT_1 状态。(第一次挥手:由浏览器发起的,发送给服务器,我请求报文发送完了,你准备关闭吧)
  • 被动方发送报文,Ack、Seq,表示同意关闭请求。此时主机发起方进入 FIN_WAIT_2 状态。(第二次挥手:由服务器发起的,告诉浏览器,我请求报文接受完了,我准备关闭了,你也准备吧)
  • 被动方向发起方发送报文段,Fin、Ack、Seq,请求关闭连接。并进入 LAST_ACK 状态。(第三次挥手:由服务器发起,告诉浏览器,我响应报文发送完了,你准备关闭吧)
  • 发起方向被动方发送报文段,Ack、Seq。然后进入等待 TIME_WAIT 状态。被动方收到发起方的报文段以后关闭连接。发起方等待一定时间未收到回复,则正常关闭。(第四次挥手:由浏览器发起,告诉服务器,我响应报文接受完了,我准备关闭了,你也准备吧)

HTTP 请求

TCP 三次握手

  • 客户端发送一个带 SYN=1,Seq=X 的数据包到服务器端口(第一次握手,由浏览器发起,告诉服务器我要发送请求了)
  • 服务器发回一个带 SYN=1, ACK=X+1, Seq=Y 的响应包以示传达确认信息(第二次握手,由服务器发起,告诉浏览器我准备接受了,你赶紧发送吧)
  • 客户端再回传一个带 ACK=Y+1, Seq=Z 的数据包,代表“握手结束”(第三次握手,由浏览器发送,告诉服务器,我马上就发了,准备接受吧)

为什么需要三次握手

  • 谢希仁著《计算机网络》中讲“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。

发送 HTTP 请求

TCP 三次握手结束后,开始发送 HTTP 请求报文。

请求报文由请求行(request line)、请求头(header)、请求体三个部分组成

1. 请求行包含请求方法、URL、协议版本

  • 请求方法包含 8 种:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE。
  • URL 即请求地址,由 <协议>://<主机>:<端口>/<路径>?<参数> 组成
  • 协议版本即 http 版本号
POST /chapter17/user.html HTTP/1.1

以上代码中“POST”代表请求方法,“/chapter17/user.html”表示 URL,“HTTP/1.1”代表协议和协议的版本。现在比较流行的是 Http1.1 版本

2. 请求头包含请求的附加信息,由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。

请求头部通知服务器有关于客户端请求的信息。它包含许多有关的客户端环境和请求正文的有用信息。其中比如:Host,表示主机名,虚拟主机;Connection,HTTP/1.1 增加的,使用 keepalive,即持久连接,一个连接可以发多个请求;User-Agent,请求发出者,兼容性以及定制化需求。

3. 请求体,可以承载多个请求参数的数据,包含回车符、换行符和请求数据,并不是所有请求都具有请求数据。

name=tom&password=1234&realName=tomson

上面代码,承载着 name、password、realName 三个请求参数。

Activity icon
issue

exposir issue exposir/Personal-Blog

exposir
exposir

缓存策略

image

  • Cache-Control > expires > Etag > Last-Modified

强缓存

Expires

  • Expires 是 HTTP/1.0 控制网页缓存的字段,其值为服务器返回该请求结果缓存的到期时间,即再次发起该请求时,如果客户端的时间小于 Expires 的值时,直接使用缓存结果。

Cache-Control

在 HTTP/1.1 中,Cache-Control 是最重要的规则,主要用于控制网页缓存,主要取值为:

  • public:所有内容都将被缓存(客户端和代理服务器都可缓存)
  • private:所有内容只有客户端可以缓存,Cache-Control 的默认取值
  • no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
  • no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
  • max-age=xxx (xxx is numeric):缓存内容将在 xxx 秒后失效

区别

  • HTTP 响应报文中 expires 的时间值,是一个绝对值
  • HTTP 响应报文中 Cache-Control 为 max-age=600,是相对值
  • 在无法确定客户端的时间是否与服务端的时间同步的情况下,Cache-Control 相比于 expires 是更好的选择,所以同时存在时,只有 Cache-Control 生效。

协商缓存

协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:

  1. 协商缓存生效,返回 304
  2. 协商缓存失效,返回 200 和请求结果结果

同样,协商缓存的标识也是在响应报文的 HTTP 头中和请求结果一起返回给浏览器的,控制协商缓存的字段分别有:

  • Last-Modified
  • If-Modified-Since
  • Etag
  • If-None-Match
  • Etag / If-None-Match 优先级高于 Last-Modified / If-Modified-Since,同时存在则只有 Etag / If-None-Match 生效。

Last-Modified / If-Modified-Since

  • Last-Modified是服务器响应请求时,返回该资源文件在服务器最后被修改的时间
  • If-Modified-Since则是客户端再次发起该请求时,携带上次请求返回的 Last-Modified 值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求,发现请求头含有 If-Modified-Since 字段,则会根据 If-Modified-Since 的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于 If-Modified-Since 的字段值,则重新返回资源,状态码为 200;否则则返回 304,代表资源无更新,可继续使用缓存文件,

Etag / If-None-Match

  • Etag 是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成)
  • f-None-Match是客户端再次发起该请求时,携带上次请求返回的唯一标识 Etag 值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。服务器收到该请求后,发现该请求头中含有 If-None-Match,则会根据 If-None-Match 的字段值与该资源在服务器的 Etag 值做对比,一致则返回 304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为 200

总结

强制缓存优先于协商缓存进行,若强制缓存(Expires 和 Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since 和 Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,重新获取请求结果,再存入浏览器缓存中;生效则返回 304,继续使用缓存

Activity icon
issue

exposir issue exposir/Personal-Blog

exposir
exposir

模块化

模块化

很长一段时间,JavaScript 都没有语言级(language-level)的模块语法。这不是一个问题,因为最初的脚本又小又简单,所以没必要将其模块化。

但是最终脚本变得越来越复杂,因此社区发明了许多种方法来将代码组织到模块中,使用特殊的库按需加载模块。

  • AMD —— 最古老的模块系统之一,最初由 require.js 库实现。
  • CommonJS —— 为 Node.js 服务器创建的模块系统。
  • UMD —— 另外一个模块系统,建议作为通用的模块系统,它与 AMD 和 CommonJS 都兼容。

什么是模块?

一个模块(module)就是一个文件。一个脚本就是一个模块。就这么简单

  • export 关键字标记了可以从当前模块外部访问的变量和函数。
  • import 关键字允许从其他模块导入功能。
//say.js
function sayHi(user) {
  alert(`Hello, ${user}!`);
}

function sayBye(user) {
  alert(`Bye, ${user}!`);
}

export { sayHi, sayBye }; // 导出变量列表
//main.js
import { sayHi, sayBye } from "./say.js";

sayHi("John"); // Hello, John!
sayBye("John"); // Bye, John!
//main.js
import * as say from "./say.js";

say.sayHi("John");
say.sayBye("John");

Export default

在实际中,主要有两种模块。

  • 包含库或函数包的模块,像上面的 say.js。
  • 声明单个实体的模块,例如模块 user.js 仅导出 class User。

大部分情况下,开发者倾向于使用第二种方式,以便每个“东西”都存在于它自己的模块中。

当然,这需要大量文件,因为每个东西都需要自己的模块,但这根本不是问题。实际上,如果文件具有良好的命名,并且文件夹结构得当,那么代码导航(navigation)会变得更容易。

模块提供了一个特殊的默认导出 export default 语法,以使“一个模块只做一件事”的方式看起来更好。

将 export default 放在要导出的实体前:

// 📁 user.js
export default class User {
  // 只需要添加 "default" 即可
  constructor(name) {
    this.name = name;
  }
}

每个文件可能只有一个 export default:

……然后将其导入而不需要花括号:

//mai.js
// 📁 main.js
import User from "./user.js"; // 不需要花括号 {User},只需要写成 User 即可

new User("John");
  • 命名导出: export class User {...} import {User} from ...
  • 默认导出:export default class User {...} import User from ...

默认导出和命名导出混合

// 📁 user.js
export default class User {
  constructor(name) {
    this.name = name;
  }
}

export function sayHi(user) {
  alert(`Hello, ${user}!`);
}
// 📁 main.js
import { default as User, sayHi } from "./user.js";

new User("John");
// 📁 main.js
import * as user from "./user.js";

let User = user.default; // 默认的导出
new User("John");

动态导入

命名倒入

// 📁 say.js
export function hi() {
  alert(`Hello`);
}

export function bye() {
  alert(`Bye`);
}
let { hi, bye } = await import("./say.js");

hi();
bye();

动态导入

// 📁 say.js
export default function () {
  alert("Module loaded (export default)!");
}

……那么,为了访问它,我们可以使用模块对象的 default 属性:

let obj = await import("./say.js");
let say = obj.default;
// or, in one line: let {default: say} = await import('./say.js');

say();
Activity icon
issue

exposir issue exposir/Personal-Blog

exposir
exposir

数组及其方法

7 个改变原数组的方法

  • pop
  • push
  • shift
  • unshift
  • sort
  • reverse
  • splice

遍历数组的方法

  • forEach
  • filter
  • some
  • reduce
  • map
  • some
  • any

其他方法

  • slice
  • join
  • fill
  • concat
  • includes
  • findIndex
  • find
  • indexof
  • Array.isArray()

filter

  • filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
let arr = [1, 2, 3, 4];
result = arr.filter((value) => {
  return value > 2;
});
console.log(result); // [3,4]

some

  • some() 方法测试数组中是不是至少有 1 个元素通过了被提供的函数测试。它返回的是一个 Boolean 类型的值。
let arr = [1, 2, 3, 4];
result = arr.some((value) => {
  return value > 2;
});
console.log(result); // true

reduce

  • reduce() 方法对数组中的每个元素执行一个由您提供的 reducer 函数(升序执行),将其结果汇总为单个返回值。
  • reducer 函数接收 4 个参数和一个初始值
  • accumulator 累积值
  • currentValue 数组中正在处理的元素
  • index 索引
  • array 原数组
  • initialValue 作为第一次调用 callback 函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4 = 10
console.log(array1.reduce(reducer));

// 5 + 1 + 2 + 3 + 4 = 15
console.log(array1.reduce(reducer, 5));

map

  • map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
  • 参数:
  • currentValue 数组中正在处理的当前元素。
  • index(可选) 当前元素索引
  • array (可选)原数组
const array1 = [1, 4, 9, 16];

// pass a function to map
const map1 = array1.map((x) => x * 2);

console.log(map1);
// expected output: Array [2, 8, 18, 32]
Activity icon
issue

exposir issue exposir/Personal-Blog

exposir
exposir

微前端

即,一种由独立交付的多个前端应用组成整体的架构风格。具体的,将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的小块,而在用户看来仍然是内聚的单个产品

将庞大的整体拆成可控的小块,并明确它们之间的依赖关系。关键优势在于:

  • 代码库更小,更内聚、可维护性更高
  • 松耦合、自治的团队可扩展性更好
  • 渐进地升级、更新甚至重写部分前端功能成为了可能

特点

简单、松耦合的代码库

增量升级

独立部署

团队自治

Activity icon
issue

exposir issue exposir/Personal-Blog

exposir
exposir

对象及其方法

  • Object.assign() 用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。
  • Object.create() 创建一个新对象,使用现有的对象来提供新创建的对象的proto
  • Object.keys() 会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
  • Object.values() 返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用 for...in 循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。
  • Object.entries() 返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。
  • Object.defineProperty() 会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
  • Object.is() 判断两个值是否为同一个值。
  • Object.getOwnPropertyNames() 返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组。
  • Object.setPrototypeOf() 设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性)到另一个对象或 null。
  • Object.getPrototypeOf() 返回指定对象的原型(内部[[Prototype]]属性的值)。
  • Object.prototype.hasOwnProperty() 会返回一个布尔值,指示对象自身属性中是否具有指定的属性

Object.assign()

  • Object.assign(target, ...sources)
  • 一层对象拷贝
const a = { a: 1, b: 2 };
const b = { b: 4, c: 5 };
const c = Object.assign(a, b);
console.log(c); //  {a: 1, b: 4, c: 5}
Activity icon
issue

exposir issue exposir/Personal-Blog

exposir
exposir

bind、call、apply

bind、call、apply

  • https://zhuanlan.zhihu.com/p/71553017
  • bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
  • call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
  • apply() 方法调用一个具有给定 this 值的函数,以及以一个数组(或类数组对象)的形式提供的参数。

手写 call

Function.prototype.myCall = function (thisArg, ...args) {
  thisArg.temp = this;
  const result = thisArg.temp(...args);
  delete thisArg.temp;
  return result;
};

function sayHi() {
  console.log(this.name);
}

let user = { name: "John" };
let admin = { name: "Admin" };

// 使用 call 将不同的对象传递为 "this"
sayHi.call(user); // John
sayHi.call(admin); // Admin

手写 apply

Function.prototype.myApply = function (thisArg, args) {
  thisArg.temp = this;
  const result = thisArg.temp(args);
  delete thisArg.temp;
  return result;
};

function sayHi() {
  console.log(this.name);
}

let user = { name: "John" };
let admin = { name: "Admin" };

// 使用 call 将不同的对象传递为 "this"
sayHi.myApply(user); // John
sayHi.myApply(admin); // Admin

手写 bind

Function.prototype.myBind = function (thisArg, ...args) {
  const func = this;
  return function (...otherArgs) {
    return func.call(thisArg, ...args, ...otherArgs);
  };
};

function getName() {
  console.log(this, ...arguments);
}

const changeBind = getName.myBind({ name: "Lily" }, 1, 2, 3);
changeBind(4, 5, 6);
Activity icon
issue

exposir issue exposir/Personal-Blog

exposir
exposir

XSS

XSS

概念

跨站脚本(英语:Cross-site scripting,通常简称为:XSS)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了 HTML 以及用户端脚本语言。

XSS 攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是 JavaScript,但实际上也可以包括 Java,VBScript,ActiveX,Flash 或者甚至是普通的 HTML。攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、私密网页内容、会话和 cookie 等各种内容。

如何防止 XSS 攻击?

转义

在存储型 XSS 和反射型 XSS 攻击中,这个是一个关键的防范方式,通过对不同类型的文本和数据做对应的转义和很好的防止大部分的 XSS 攻击。

<script>哈尔滨</script> -> &lt;script&gt;哈尔滨&lt;/script&gt

CSP

CSP 是 Content Security Policy,在实际应用中,我们是通过在 HTTP 头部添加 Content-Security-Policy 的字段来实现的。

常见的 CSP 头字段如下:

Content-Security-Policy: default-src 'self' *.trusted.com

在较为严格的 CSP 条件下,对 XSS 攻击的防范有以下的作用:

  • 禁止加载外域代码,防止复杂的攻击逻辑;
  • 禁止外域提交,网站被攻击后,用户数据不会泄漏;
  • 禁止内联脚本执行;
  • 禁止未授权的脚本执行;
  • 通过 CSP 的上报功能,便于修复问题。

HttpOnly

HttpOnly 是 Cookie 的一个属性,在设置 Cookie 的时候可以对 Cookie 设置该属性,该属性主要是防止 Cookie 被 JS 脚本获取,只能通过 Http 传输和访问。

我们需要明确的是 HttpOnly 本质上并不是防止 XSS 攻击的,主要是起到缓解的作用,在恶意脚本执行之后,是无法获取到对应的 Cookie,防止来下一步攻击的进行和用户数据的进一步泄漏。

Previous