FE 每周问题总结

22 篇文章 0 订阅
18 篇文章 0 订阅

FE 每周问题总结


这篇文章,旨在不在分散的记录每周遇到的问题


2020/04/13 add typeof判断类型


2020/04/13 add js内存溢出bug


2020/04/21 add 区分 prettier eslint


2020/05/15 add webhookh


2020/06/01 add yarn vs npm


2020/06/21 add 单元测试


2020/06/28 add 时间标准化


2020/07/02 add 环境变量


2020/07/04 add 前端构建


2020/07/07 add 浏览器缓存策略


2020/09/01 add 文件上传后乱码问题


2020/09/04 add axios baseURL


2020/09/08 add npx


2020/09/24 add 前端优化


2020/10/13 add axios res node 使用 import


2020/10/28 add commonjs amd/cmd


2020/11/02 add 浏览器请求 favicon


2020/11/03 add koa 与 express


2020/11/13 add 前端缓存


2020/12/15 add a 标签安全问题


2020/12/18 add callback


2020/12/18 add promise & bluebird


2020/12/21 add cors


2020/12/21 add request.context


2020/12/24 add 前端位置


2020/12/24 add difference between ajax fetch axios


2021/01/08 add javascript 转义符


2021/01/31 add 错误栈


2021/02/02 add 七层网络、四层网络


2021/02/19 add JavaScript Number


2021/02/19 add chrome 第三方 cookie


2021/02/23 add 由接口 pending 引发的思考


2021/03/28 add 浏览器 options 请求, node buffer.alloc


2021/04/12 add 排错指南


2021/04/17 add lock file lockfileVersion


2021/04/24 add URL 携带参数


2021/05/23 add node 并发请求问题


2021/05/31 add 前端环境变量配置


2021/06/25 add http cookie


2021/08/13 add loading 加载时机、vuex


2021/08/22 add content-type vs responseType


2021/08/23 add code review


2021/11/01 add 补充浏览器缓存策略


2021/11/07 add content-disposition and download


2021/11/10 add commonjs 规范与浏览器


2021/11/14 add 前端多区域时区问题、转正则表达式问题


2021/12/05 add 修正 loading 加载时机


2021/12/05 add 前端性能优化之 webp


2022/01/03 add router.push 与 location.href 的区别


2022/01/07 add 前端性能优化之懒加载


2022/01/15 add 使用 router.replace 和 router.push 的时机


2023/05/21 add bff


文章目录

express 与 koa

express和koa的区别

  • 洋葱模型复习

排错指南

  1. postman 能够接收到,但前端接收不到,很大的原因是跨域,此时应该联系服务端修正相关内容

前端 code review 注意点

我们在开发过程中也许最初会按自己的代码风格来编写,但随着开发周期的拉长可能因为需求的更改而出现冗余代码或者说不符合「最简」「可复用」原则的代码,虽然 code review 可以发现一些问题,但不建议将其作为一道可依赖的关卡。基于此,我们应该怎么做?

references: Code Review 总结

node 并发请求问题

Handling multiple parallel HTTP requests in Node.js

正如上面 Stack Overflow 中提出的问题一样,很多人对于如果我同时发五个请求,一个请求需要 20s 返回,那是不是最后一个请求要 100s 才能返回呢?,答案当然不是的

关于nodejs能同时接受多少个请求的问题

A simple guide to JavaScript concurrency in Node.js and a few traps that come with it

  • Node took a slightly different approach to handling multiple concurrent requests at the same time if you compare it to some other popular servers like Apache. Spawning a new thread for each request is expensive. Also, threads are doing nothing when awaiting other operations’ result (i.e.: database read). That’s why Node is using one thread instead.Such an approach has numerous advantages. No overhead comes with creating new threads. Also, your code is much easier to reason about, as you don’t have to worry about what will happen if two threads access the same variable. It’s because that simply cannot happen. There are some drawbacks as well. Node isn’t the best choice for applications that mostly deal with CPU intensive computing. On the other hand, it excels at handling multiple I/O requests. So, let’s focus on this part for a bit.

CPU intensive computing: Processes or tasks which run on a computer require various resources like CPU cycles, memory, disk or network which are managed by the operating system, so that each task executes efficiently (without waiting for a resource if possible).Sorting, search, graph traversal, matrix multiply are all CPU operations.

  1. node 的异步 I/O 机制对处理高并发问题很友好,每接收一个请求,使用异步调用处理请求,不用等待结果,可以继续运行其他操作,也就是说可以继续接受请求。
    what is I/O operations?

These are operations that communicate with stuff from the outside of your application. It means HTTP requests, disk reads and writes or database operations, just to name a few. I/O in Node comes in two “flavors”: blocking and non-blocking. It’s quite important to distinguish these two. “Blocking” in “blocking I/O operations” is quite self-descriptive. It means that next operations are blocked and that they have to wait as long as the currently running operation is taking.

// blocking-io
fs.readFileSync(filePath1)
fs.readFileSync(filePath2)
console.log(‘Logging after both reads are finished’)
// non-blocking-io

fs.readFile(filePath1, (err, content) => {
   if (err) // handle error 
   // otherwise do something with the content
})

fs.readFile(filePath2, (err, content) => { /* same story */ })

console.log(‘Will happen before any of the reads is finished’)
  1. When such I/O operations are completed, how does Node know that it’s time to handle them?因此需要一个“管理器”来管理所有的异步操作,那就是事件循环。
asyncOperation(param1, param2, function(error, result) { /* … */ })

The interesting part here is a function that you pass as the last argument. It’s a callback function that will be called once your operation is finished, but not immediately. We agreed that such things shouldn’t happen anytime and Event Loop will make sure of that. Once your operation is finished, instead of calling the callback right away, it will place it into a special queue. Once it’s possible to handle it safely, your callbacks will be pushed to the stack and then executed one by one. You should be careful here, even if your I/O operation was asynchronous, the callback will be handled on the main thread, assuming it’s a JS operation. So, if you’re doing something time consuming, you’ll be blocking the Event Loop.

  1. 然而当 node 在处理 CPU intensive operations 时处理 I/O 操作会导致后者直接 no response,这也是单线程的缺点。但也有应对方法那就是「Cluster Mode」 What Cluster Mode gives you is a very basic load balancer. It will distribute the load in the round-robin approach between the nodes.(具体内容不再赘述)

当然以上内容可以结合更深层次的关于宏任务(http requests 就是宏任务)和微任务的解读,可以看 https://www.bilibili.com/video/BV1K4411D7Jb

http

http content-type vs responseType

HTTP消息头(HTTP headers)-常用的HTTP请求头与响应头

XMLHttpRequest.responseType

XMLHttpRequest.responseType

axios 请求配置

前段时间遇到一个问题 restAPI 中提示返回的是二进制格式,但由于我请求时未设置 responseType 导致返回格式非预期的二进制。

http request response 报文中均含有 content-type header 标识

request: content-type: 请求体的MIME类型 (用于POST和PUT请求中)

response: content-type:当前内容的MIME类型

而 responseType 则是请求方设定的希望返回的数据类型,不写默认就是 text,这样就会导致(服务端返回了正确的类型,我却没收到正确的类型)这种事。

content-type changed automated??

最近在处理一些问题的时候,发现 http 请求中当添加一些特殊的 header 时或某些插件就会导致 content-type 变成 application/octet-stream

axios 会根据 data 的格式自动设置 content-type 为 application/json or application/x-www-form-urlencoded, 而对于 form-data 类型的数据则会删掉用户设置的 content-type 交由浏览器进行设置 axios

姑且不去追究是添加特殊 header 导致的 content-type 改变还是直接修改了 content-type,而application/octet-stream则意味着当前传输的内容的格式是「任意二进制数据」(in RFC 2046),而这个配置往往和content-disposition 联用于 response 中表示要求保存这个返回结果。我是否需要从Content-Type:application / octet-stream处下载文件?
Do I need Content-Type: application/octet-stream for file download?

content-disposition attachment and dowanload

content-disposition中所述,content-disposition 是用来设置浏览器返回内容的展现形式,而通常我们将其设置为 attachment 来实现让浏览器下载某个文件。

前段时间遇到一个用户浏览器弹不出下载框,这里总结一下并备日后出现具体 case 具体分析。

  • 起初我考虑用户浏览器加了特殊插件阻止了这类弹出下载框的行为(当然不排除这种现象)
  • 虽然我们进行了设置但是每个人的浏览器设置不同,有的则不会弹出下载框直接下载到默认位置,比如在 chrome://settings/downloads 中设置「是否下载前询问每个文件的保存位置」
  • 其他行为:axios 发 get 请求会导致这种现象,You Can’t Prompt a File Download With the Content Disposition Header Using Axios (XHR). Sorry.因此通常,我们都是打开一个新页面(在前端 window.open)直接在浏览器发起请求此时就会得到下载文件的提示。(但我遇到的场景显然不适用于此,这里记录一下)

loading 加载时机与 vuex dispatch action

为什么将 loading 加载时机这种与业务强相关的内容和 vuex 联系起来呢,且看下面的分解

Composing Actions

Promise.prototype.finally()

使用 loading 我们可以解决在 vuex 中设置了初始值 state,但通过 dispatch action 更新 state 这期间造成的“页面闪烁”问题。那么我们到底具体什么位置写 loading 呢?

  • 最好是 store 中,将 loading 作为 state 的一个变量,在 action 中通过 mutation 修改

通常的一种做法是我们在 get 请求中加入 loading,每次修改即进行 put 请求后再 get 所有数据。由此带来一个问题:每次 put 请求后页面都会闪烁一下。为了解决这个问题:

- 仅在 post 请求后进行重新 get 所有数据,同时结合(vue transition-group)[https://cn.vuejs.org/v2/api/#transition-group]来让体检更好
- 仅在首次渲染页面即使用 get 请求时进行 loading 加载(即使用方式 2),put 请求后,直接拿 put 请求的结果 mutate 数据,直接渲染而不是再去 get 数据。(参考 https://www.apple.com.cn/ 的体验)
  • 其次是在组件内部(适用于除了处理 loading 还要处理组件内部一些其他非公用 store 逻辑的场景),使用 mapActions 本质还是使用 store.dispatch,而 dispatch 本质就是 promise,我们可以在 promise.finally() 中处理 loading 逻辑

URL 携带业务参数

业务上经常遇到这种场景:我们希望在 URL 中携带一些参数以便于我们刷新页面或将此 URL 分享给其他人的时候能显示搜索后的结果。这里汇集了几个使用 Vue 的过程中一些解决方案以及自己最终选择的认为最优的方案:方案 3 或 方案 4

  1. 场景1:

特点:

  • 信息放在 query
  • 涉及「初次进入页面」和「用户交互」改动
  • localStorage + handleChange 放在 actions 里面
    技术:
  • 其中利用 vuex-router-sync 将 route 同步到了 store rootState 中
  • 其中利用 localStorage 持久化参数
    缺点:
  • 多了一步 getters,没有复用到「用户交互」流程
    在这里插入图片描述
  1. 场景2:

特点:

  • 信息放在 param
  • 涉及「初次进入页面」,由于可以直接根据 params 匹配路由所以较简单,
  • localStorage + handleChange 放在 actions 里面
    技术:
  • 其中利用 vuex-router-sync 将 route 同步到了 store rootState 中
  • 其中利用 localStorage 持久化参数
    在这里插入图片描述
  1. 场景 3
    特点:
  • 信息放在 query 中
  • 涉及「初次进入页面」和「用户交互」,主要通过组件中监听 query 变化实现视图更新,
    技术:
  • 其中利用 vuex-router-sync 将 route 同步到了 store rootState 中
    看法:
  • 这种方式以 watch Query 为关键点,将「初次进入页面」和「用户交互」流程整体复用,是一种不错的使用方式。但需要注意的是,这种方式需要在切换 params 的时候注意清空 query
    在这里插入图片描述
  1. 场景 4
    特点:
  • 信息放在 query 中
  • 涉及「初次进入页面」和「用户交互」,主要通过组件中监听 query 变化实现视图更新,
    看法:
  • 无需将 query 注入到 vuex 中。算是场景 3 的另一种方式。
    在这里插入图片描述

tips:

  • 当我们将信息放在 query 中向服务端进行传递时,默认此时全部都是字符串
  • 如果想保留数据类型则可以通过向服务端 post json 结构数据的方式,这样就可以保留基本类型。(我们可以用 axios 等第三方 http 请求库)

lock file

最近做业务的时候由于本地 npm 版本较低导致 install 之后出现 package-lock.json 和线上版本出现了大量的 diff,这里记录一下相关概念和理解

什么时候不能在 Node.js 中使用 Lock Files

是什么

lock file 描述了整个依赖关系树,它在创建时被解析,包括具有特定版本的嵌套依赖关系。在 npm 名为 package-lock.json ,在 yarn 中名为 yarn.lock。在这两个npm和yarn它们被放置你的package.json 的旁边

为什么要有 lock file

package.json 中 dependencies 字段显示你的项目应该安装的依赖项,但不显示这些依赖项的依赖项。依赖项可以指定精确版本或 semver 范围。对于 semver 范围,npm 或 yarn 将会选择最适合的版本。

什么时候需要 lock file 什么时候不需要 lock file

需要 lock file

当在构建 Web 程序或服务器之类的应用时,这非常有用,因为我们希望在 CI 环境中模拟用户的行为。因此,如果在源代码控制(如 git)中跟踪我们的 lock file,就可以确保每个开发人员以及服务器或构建系统还有 CI 系统都能够使用相同版本的依赖项

tips:about npm install <7 for lockFileVersion=1 and >7 for lockFileVersion=2.

不需要 lock file

npm 发包的时候实际并不会将 lock file 进行捆绑,因此在写 npm 包的时候应该做的是把 lock file 放在 .gitignore 里面

其他

npm doc

官方文档中指出 npm-shrinkwrap.json 是和 package-lock.json 一样的文件。
摆脱了 package-lock.json 并不意味着无法固定我们所拥有的依赖关系和子依赖关系。我们可以用另一个名为 npm-shrinkwrap.json 的文件。

它与 package-lock.json 基本相同,并由 npm shrinkwrap 生成并实际的打包并发布到 npm 注册表中。

因此,通过将 npm shrinkwrap 添加到 npm 脚本作为 prepack 脚本甚至是 git commit hook,可以确保在你的开发环境中,与你的用户和 CI 中使用相同版本的依赖项。通过使用 shrinkwrap 文件,你可以确定精确的版本,但它也会阻止人们获得自动安装的关键补丁程序

node buffer

一篇帮你彻底弄懂NodeJS中的Buffer

Node.js Buffer(缓冲区)

JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。

但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。

事情的起因是我试图在 node 中构造一个 200k 的数据,比较不方便的使用了 for 循环,实际使用 buffer.alloc 非常方便

由接口 pending 引发的思考

TTFB: Time To First Byte

https://www.cnblogs.com/both-eyes/p/10573713.html

网站加载 Waiting (TTFB) 时间过长的原因和解决办法

浏览器 options 请求

为什么会有OPTIONS请求

为什么每次请求之前要发送一个OPTIONS请求

跨域资源共享 CORS 详解

Access-Control-Max-Age

场景: 由于同源策略的影响,在其应对方案 CORS 中对于非简单请求必须使用 options 请求。

OPTIONS请求方法的主要用途有两个:

1、获取服务器支持的HTTP请求方法;

2、用来检查服务器的性能。例如:AJAX进行跨域请求时的预检,需要向另外一个域名的资源发送一个HTTP OPTIONS请求头,用以判断实际发送的请求是否安全。

通常实际业务中发出的请求都是非简单请求,因为 Content-Type 字段的类型是 application/json 时就被认定是非简单请求,不过为了避免调试受影响,可以打开浏览器 devtool 的时候选择 network 的 XHR 请求即可过滤掉 options 请求。

当我们在写服务端内容时,很多场景会遇到前端或其他域名来请求本接口从而导致出现跨域问题,而每次复杂请求都发 options 请求,不过可以通过设置 Access-Control-Max-Age 来降低频率

浏览器 stalled

stalled 是什么

stalled阶段是浏览器得到要发出这个请求的指令,到请求可以发出的等待时间,一般是代理协商、以及等待可复用的TCP连接释放的时间,不包括DNS查询、建立TCP连接等时间等。
image

导致 stalled 的原因
  1. 单一服务发送时候stalled过长,往往是丢包所致,这也意味着网络或服务端有问题。
  2. 多个服务并发导致stalled过长,是浏览器对同一个主机域名的并发连接数有限制,过长的请求是被阻塞了,处在队列中等待tcp连接

综上一般出现 stalled 的情况是由于前端问题而非服务端问题

改进措施
  1. 将资源合理分布到多台主机上,可以提高并发数,但是增加并行下载数量也会增大 开销,这取决于带宽和CPU速度,过多的并行下载会降低性能;
  2. 脚本置于页面底部;

TTFB

网站加载 Waiting (TTFB) 时间过长的原因和解决办法

web性能优化之Waiting(TTFB)简介

TTFB 是什么?

既然上文提到了前端导致的 stalled 情况,那么一般接口 pending 什么情况是服务端导致的呢,一般就去参考 TTFB

TTFB(Time to First Byte)是指从客户端开始和服务端交互到服务端开始向客户端浏览器传输数据的时间(包括DNS、socket连接和请求响应时间),是能够反映服务端响应速度的重要指标。大多数服务器的 TTFB 时间都在 50 ms 以下

什么导致的 TTFB 过长
  1. DNS查询
  2. 服务器响应,一般动态网页会造成这种现象
  3. 网络原因
  4. 重定向等
改进措施
  1. 减少 DNS 查找
    • 减少DNS查找次数,最理想的方法就是将所有的内容资源都放在同一个域(Domain)下面,这样访问整个网站就只需要进行一次DNS查找,这样可以提高性能。
    • 但理想总归是理想,上面的理想做法会带来另外一个问题,就是由于这些资源都在同一个域,而HTTP /1.1 中推荐客户端针对每个域只有一定数量的并行度,那么就会出现下载资源时的排队现象,这样就会降低性能。
    • 折衷的做法是:建议在一个网站里面使用至少2个域,但不多于4个域来提供资源。
    • 注意 http2 多路复用已经解决了 http1 对每个域并行数量的限制
  2. 使用静态网页
  3. 使用 CDN

Chrome 第三方 cookie 的问题

chrome有一个版本是可以在无痕模式下阻止第三方cookie的吗?

预测最近面试会考 Cookie 的 SameSite 属性

Cookie个数限制及大小

最近在用户反馈群中了解有些用户跳出登录态或无线在登录页跳转,最后查出来是 Chrome 无痕模式下出现了默认设置为阻止第三方 cookie

HTTP Cookie/Web Cookie 或 浏览器 Cookie

HTTP Cookie 主要用于以下三个方面:

  • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)

那么 set-cookie 和 cookie 的区别有哪些呢?

set-cookie 是响应头部向用户代理即浏览器发送 cookie 信息

Set-Cookie: <cookie名>=<cookie值>

可以在服务端响应头中设置多个 set-cookie,但其实是不能拼接的,最终表现是多个 set-cookie 响应头

而从浏览器端向服务器发请求的时候添加的则是 Cookie 请求头:

GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

此时多个 cookie 是写在一个 cookie 请求头里面的,可进行拼接

Cookie 的缺点

如果被问到话,可以从大小、安全、增加请求大小等方面回答。

  • 不同浏览器有不同的限制,但建议 cookie 一般要小于 20 个,总大小小于 4k
  • 由于 cookie 被读取导致的 csrf(sameSite 防范),xss(httpOnly 防范)
  • 由于每次请求都携带 cookie 会增加请求的大小

(其他可参考 references)

JavaScript Number

references:

JavaScript 浮点数运算的精度问题

JS-Number—了解IEEE双精度浮点数

在 JavaScript 中整数和浮点数都属于 Number 数据类型,所有数字都是以 64 位浮点数形式储存,即便整数也是如此,其为采用 IEEE 754 标准的 64 位双精度浮点数。

关于原理不再赘述,有需要看 reference 即可,主要记录一下解决方案。

  1. 最大整数为 2^53-1,我们可以通过用String类型的表示来取值或传值,否则会丧失精度,当然也可以使用 BigInt但注意做运算时不能和Number类型混淆。
  2. 0.1+0.2!==0.3 类的浮点数问题一定程度的可以通过toFixed来解决。其他方式此处不赘述。

七层网络,四层网络

七层网络协议模型指的是:开放式系统互联通信参考模型(英语:Open System Interconnection Reference Model,缩写为 OSI),简称为OSI模型(OSI model),一种概念模型,由国际标准化组织提出,一个试图使各种计算机在世界范围内互连为网络的标准框架。

四层是指TCP/IP四层模型,主要包括:应用层、运输层、网际层和网络接口层

网络7层协议,4层,5层?理清容易混淆的几个概念

错误栈

深入理解 JavaScript Errors 和 Stack Traces

在用 node 写服务端的时候避免不了处理错误信息,我们常常需要去对其进行追踪

  1. 每当有一个函数调用,就会将其压入栈顶。在调用结束的时候再将其从栈顶移出。
  2. 当 Error 发生的时候,通常会抛出一个 Error 对象。Error 对象也可以被看做一个 Error 原型,用户可以扩展其含义,以创建自己的 Error 对象。
Error.prototype 对象通常包含下面属性:

constructor - 一个错误实例原型的构造函数
message - 错误信息
name - 错误名称
这几个都是标准属性,有时不同编译的环境会有其独特的属性。
在一些环境中,例如 Node 和 Firefox,甚至还有 stack 属性,这里面包含了错误的 Stack trace。一个 Error 的堆栈追踪包含了从其构造函数开始的所有堆栈帧。
  1. 使用堆栈追踪即 stack trace

Error.captureStackTrace 函数的第一个参数是一个 object 对象,第二个参数是一个可选的 function。捕获堆栈跟踪所做的是要捕获当前堆栈的路径(这是显而易见的),并且在 object 对象上创建一个 stack 属性来存储它。

JavaScript escape character

最近有这样一个需求:想在前端显示字符串时不将转义字符进行转义显示,也就是 \n 而不是一个换行。尝试了很多种方式,最后定下来 JSON.stringify 将字符串转化下。

reference:

Escape characters in JavaScript

http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4

  1. 使用正则匹配 \ 替换为 \\
    显然无法进行匹配,但若使用下面一种匹配方式又存在遗漏某些转义字符的可能
    image

  2. es6 新增了 String 的一个原型方法 String.raw,然后明显我们将它结合与模板语法用时遇到了瓶颈,显然并不符合预期因此摒弃

const a = 'test\nok';
const b = String.raw`${a}`

image

  1. 最终方案: 使用 JSON.stringify 将字符串转化,关于其 polyfill 可以看 https://github.com/douglascrockford/JSON-js/blob/master/json2.js
    此时转化后它的行为和 mongodb 存库时行为一致,如存 test\vtest 转化为test\u000test等。(mongodb 文档数据库,存储的是文档(Bson->json的二进制化).因此并不意外)

转正则表达式

references:

How to escape regular expression special characters using javascript?

Escape string for use in Javascript regex

通常我们会使用 new RegExp 去将一个字符串转为正则表达式,而此时这些转义字符则会添乱导致报错,通常的处理方法如下:

const escapeRegExp = (text) => {
  return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}

Difference between ajax fetch axios

AJAX, fetch, and
Axios

详细介绍了三者,下面简单总结下。

ajax: Asynchronous JavaScript And XML, Making background HTTP requests using JavaScript

fetch: 浏览器提供的原生 AJAX 接口

axios: Promise based HTTP client for the browser and node.js

个人总结:fetch axios 本质实质都是 ajax 实现,关于两者区别如浏览器兼容性可以参考 What is difference between Axios and Fetch?,目前 node 开发中更常用 axios, 作为 http client 可以支持相关的 http 协议请求方法,如新引入的 patch 等。

前端位置

起因是前段时间做了个鼠标移动到相关链接上出现图片预览的功能,限于所用组件需要写一些原生的内容(表格用的 ag-grid)

tips:

  1. 处理鼠标移动或者获取位置相关的问题经常会遇到抖动问题,当然有时真的是抖动问题,此时则需要 debounce;有时注意是元素叠加,此时则需要调整元素位置
  2. 做产品要多找有价值的竞品进行参考,让产品更精致符合主流,而不是 personalize

request.context

Request.context

该字段标识请求的上下文,每个请求都有独立的 context,尤其因为 http 协议是无状态的而不是维护全局 context。

也正是这种原因会导致使用中用 A 做入口函数同时引入 B 且 B 中有使用 context 时 context 为 undefined 的情况,此时解决方式是将 context 一起传递给 B

cors

今天有个用户询问跨域失败的问题,当时我没仔细看错误直接转接给了同事,实际上后面仔细看错误是个我很清楚且能够提供建议的错误,有时候工作中不妨多思考一下,但当然不会的地方一定要及时询问别人。

跨域资源共享 CORS 详解

image

首先从报错上可以很清楚的知道如果 request 中设置了携带 cookie 如

fetch("http://localhost:3000/login", {
    method: "post",
    mode: "cors",
    credentials: "include",
    headers: {'Content-Type': "application/json"}
});
同理使用jq的ajax也一样
$.ajax({
    url: 'http://localhost:3000/login',
    type: 'post',
    xhrFields: {
    withCredentials: true
},
headers:{'Content-Type': "application/json"}
});

此时必须 response 头中 Access-control-allow-credentialstrue,若不是浏览器就报异常,当然也有一种我没遇到的现象就是照样返回 200 但是把服务器返回的内容屏蔽掉了。

同时在排查这类问题时可以使用本地写个 html 文件引入 axios 发个请求。(一种很好的排查问题的思路)

bluebird

使用 bluebird 实现更强大的 Promise
bluebird.js - 让所有浏览器都支持 ES6 Promise 对象

Bluebird 的一个实用功能是把不使用 Promise 的已有API包装成返回 Promise 的新 API。大部分 NodeJS 的标准库 API 和不少第三方库的 API 都使用了回调方法的模式,也就是在执行异步操作时,需要传入一个回调方法来接受操作的执行结果和可能出现的错误。对于这样的方法,Bluebird 可以很容易的将它们转换成使用 Promise 的形式。因此在有需要的时候可以使用 bluebird来优化代码,特此记录

callback

reference:

彻底理解 Node.js 中的回调(Callback)函数

callback 本身是 node 一大重要特点:异步 IO 的重要应用。在 Node 中,绝大多数的操作都是以异步的方式进行调用,而回调函数也是最好的接收异步调用返回数据的方式,但实际上随着如 promise async 等避免回调地狱的出现,我们可以更像写同步函数那样写异步函数。

a 标签安全问题

reference:

如何让浏览器在访问链接时不要带上referer

使用标签时,你可能会忽略的一个安全问题

  1. 需要明确从一个网页通过 a 标签跳往新页面时是会携带原网页的 referer 的,有时我们需要这个 referer,有时不需要则根据 reference 中的方法移除即可。
  2. 如果原页面和跳转页面同域,则将可以在新页面直接操作原页面,因此这是一个潜在的安全隐患

而预防这种安全隐患的方式是:在带有target="_blank"的 <a>标签中,加上rel="noopener"属性。如果使用window.open的方式打开页面,将opener对象置为空。这样的副作用是:在某些低版本浏览器中,新页面中拿不到referer信息。

浏览器请求 favicon

最近使用 faas 时候,用浏览器发起请求 url 会发起两次请求,使用 curl 则能解决这种问题

关于favicon.ico的两三事

让网站不去请求favicon.ico图标

去掉/favicon.ico的请求

favicon.ico 图标用于收藏夹图标和浏览器标签上的显示,如果不设置,浏览器会请求网站根目录的这个图标,如果网站根目录也没有这图标会产生 404【当您浏览不同的域时,浏览器会在后台向 http://<your.domain.com>/favicon.ico 发送请求】。因此通常情况会直接将这个图标放在 public 目录下,要不然直接禁止产生这个请求。

commonjs amd/cmd

AMD、CMD和CommonJS规范

JavaScript笔记(CommonJS规范,以及exports、module.exports和export、export default区别)

模块

  1. 关于 exportsmodule.exports 的区别

module.exports与exports,export与export default之间的关系和区别

  • “exports is assigned the value of module.exports before the module is evaluated.”
  • 上面这句话的意思可以理解为每个 node 模块前面都有一个 var exports = module.exports;
  • 基于以上,不要轻易使用 exports ,推荐使用 module.exports

node.js 使用 import

可以参考 StackOverflow 中的发展历程:

  • node 13 之前都是采用改文件后缀为 mjs以及运行时添加命令行指令 --experimental-modules 的方式
  • node 13 后采用 package.json 修改 "type": "module" 即可。

前端优化

  1. 要善于去使用 localStorage 去处理那些容易引起 UI 变化的变量,且这些变量并不容易发生改变。
    • 注意点:命名要规整

npx

npx 使用教程

需要明确以下几点即可:

  • Node 自带 npm 模块,所以可以直接使用 npx 命令。
  • npx 想要解决的主要问题,就是调用项目内部安装的模块
    • 理由是:运行的时候,会到node_modules/.bin路径和环境变量$PATH里面,检查命令是否存在。
  • 避免全局安装模块 eg:
$ npx create-react-app my-react-app

上面代码运行时,npxcreate-react-app下载到一个临时目录,使用以后再删除。所以,以后再次执行上面的命令,会重新下载create-react-app

axios

axios 相对路径,绝对路径

业务场景:我们想在 va 区域请求不同的域名对应与相应域名对应的服务端 api

  • 此时可以将 baseURL 设置为 /api 的形式,此为相对路径
  • 若为https://aaa/api就是绝对路径
  • 同时参考当我们实例化 axios 对象时的 baseUrl 作用: baseURL will be prepended to url unless url is absolute. It can be convenient to set baseURL for an instance of axios to pass relative URLs to methods of that instance.

axios response 循环引用

res 是 axios 封装的返回,里面的字段有循环引用(主要是 res.config),因此不能直接序列化 res,一般取出 res.data 就行

reference: axios在 node 端使用,响应response 用JSON.stringify 序列化 会 报循环引用的错误

HTML content-type

Content-Type(内容类型),一般是指网页中存在的 Content-Type,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件,这就是经常看到一些 PHP 网页点击的结果却是下载一个文件或一张图片的原因

Content-Type 标头告诉客户端实际返回的内容的内容类型。

Content-Type: text/html; charset=utf-8
Content-Type: multipart/form-data; boundary=something

浏览器缓存

有关于浏览器缓存策略上分类其实无非协商缓存和强缓存,expire 是 http1.0 的产物,而 cache-control 是 http1.1 的产物,同时存在时后者优先级高。

比如通常对于经常不需要改动的静态资源如 js css 文件往往设置较大的 cache-control,而此时通过一些比较通用的如 webpack 打包时可配置 js css 名字含有动态 hash

下文将围绕几个我所提出的问题进行解疑。

references:

深入理解浏览器的缓存机制

缓存有那么多种,分别是干什么的?

一文读懂前端缓存🌟🌟🌟🌟🌟

Cache-Control头

Cache-Control MDN

from memory cache / from disk cache 有什么区别?

这两个其实是从缓存位置上区分的不同缓存,一般我们在使用 Chrome 的 devtool 的时候会从访问资源的 size 栏中看到这两种不同来源标识。而缓存从位置上区分有如下几种:

  • Service Worker
  • Memory Cache
    • 存在内存中,浏览器 tab 关闭即失效。
  1. 几乎所有的网络请求资源都会被浏览器自动加入到 memory cache 中。但是也正因为数量很大但是浏览器占用的内存不能无限扩大这样两个因素,memory cache 注定只能是个“短期存储”。常规情况下,浏览器的 TAB 关闭后该次浏览的 memory cache 便告失效 (为了给其他 TAB 腾出位置)。而如果极端情况下 (例如一个页面的缓存就占用了超级多的内存),那可能在 TAB 没关闭之前,排在前面的缓存就已经失效了。
  2. memory cache 是浏览器为了加快读取缓存速度而进行的自身的优化行为,不受开发者控制,也不受 HTTP 协议头的约束,算是一个黑盒。但若真的不想走缓存,此时可以将 cache-control 设置为 no-store 则绝对不使用缓存,每次都重发请求。
  • Disk Cache
    • 存在硬盘中,持久化。

disk cache 会严格根据 HTTP 头信息中的各类字段来判定哪些资源可以缓存,哪些资源不可以缓存;哪些资源是仍然可用的,哪些资源是过时需要重新请求的。当命中缓存之后,浏览器会从硬盘中读取资源,虽然比起从内存中读取慢了一些,但比起网络请求还是快了不少的。绝大部分的缓存都来自 disk cache。

  • 网络请求

补充:

当浏览器要请求资源时

  • 调用 Service Worker 的 fetch 事件响应

  • 查看 memory cache

  • 查看 disk cache。这里又细分:

    • 如果有强制缓存且未失效,则使用强制缓存,不请求服务器。这时的状态码全部是 200
    • 如果有强制缓存但已失效,使用对比缓存,比较后确定 304 还是 200
  • 发送网络请求,等待网络响应

  • 把响应内容存入 disk cache (如果 HTTP 头信息配置可以存的话)

  • 把响应内容 的引用 存入 memory cache (无视 HTTP 头信息的配置)

  • 把响应内容存入 Service Worker 的 Cache Storage (如果 Service Worker 的脚本调用了 cache.put())

cache-control 只能用在响应头中吗?

不是,cache-control 可以用在请求头和响应头中,但一般用在响应头里面即由服务端来设置缓存与否。

时间标准化

有很多处理时间的库,比如 dayjs moment.js,但其实在 format 时间的时候会踩坑

不同区域时区处理问题

一般我们会在中国站点使用东八区,而国际站点往往使用 UTC 即 GMT+0 的时间,以下是开发中的一些小 tip

dayjs 是前端甚至 node 服务端常用来规范时间的包,有以下两个 tip:

  • 将时间戳规范化为 UTC 时间,可以使用 dayjs(param).utcOffset().format()
  • 将一个日期(日期带有区域的不唯一性,可能是一个东八区时间,也可能是个统一世界标准时)转化为标准的时间戳,可以使用 dayjs(param).utc(true) 将一个日期认为是 UTC 时间转出时间戳
// 若我们认为 2021/11/14 18:26 为 UTC 时间,转为时间戳后即为 1636914360000,东八区时间即为 2021-11-15 02:26:00
const utc1 = dayjs('2021/11/14 18:26').utc(true).valueOf()



// 若我们认为 2021/11/14 18:26 为 东八区 时间,转为时间戳后即为 1636885560000
const utc2 = dayjs('2021/11/14 18:26').utc().valueOf()

注: 这些当然可以通过文档确认,此处只做简单记录。

单元测试

在我的服务端学习笔记(1) 中有介绍过集成测试和单元测试的区别,最近写业务代码需要写单元测试,因此对于 jest的使用也有了一些新的认识,下面列举几个常用配置项:

更多信息可以参考开发文档:JEST

前端单元测试之Jest

聊一聊前端自动化测试

很全面的 VUE 单元测试文档:VUE Test Utils

  • moduleNameMapper
    • in jest 就是根目录的意思
  • collectCoverage
  • collectCoverageFrom

前端单元测试覆盖面:

  • 涉及逻辑更改后视图的变化
  • 涉及进入组件后 dispatch action 的触发
  • 工具函数是否正确返回

注: 前端单元测试将更注重于显示层面的测试,不是像集成测试那样去和服务端进行数据交互,因此耗时也将大幅度减少,因此可以利用 husky (git hooks make easy)将其集成到 git 工作流中

yarn vs npm

Difference between npm and yarn

npm和yarn的区别,我们该如何选择?

两者都是「包管理器」,yarn 是为了弥补 npm 安装速度慢,日志多以及旧版本的 npm 不 lock 依赖导致出现的一系列问题(但是后来 npm 更新加入了 package-lock.json)

yarn: Yet Another Resource Negotiator

dependencies vs devdependencies

What’s the difference between dependencies, devDependencies and peerDependencies in npm package.json file?

dependencies和devDependencies的区别?

How can I install all deps in NODE_ENV production?

  • dependencies

    • 生产环境需要的依赖,打包时会包含
    • 如果想将包安装在 dependencies 中执行 npm install --save 即可
  • devdependencies

    • 开发环境需要的依赖,打包时不包含,NODE_ENV=production 时默认不安装 devdependencies
    • 如果想将包安装在 devdependencies 中执行 npm install --save-dev 即可

webhooks

什么是webhook

概念

webhook是一种web回调或者http的push API,是向APP或者其他应用提供实时信息的一种方式。Webhook在数据产生时立即发送数据,也就是你能实时收到数据。这一种不同于典型的API,需要用了实时性需要足够快的轮询。这无论是对生产还是对消费者都是高效的,唯一的缺点是初始建立困难。

Webhook有时也被称为反向API,因为他提供了API规则,你需要设计要使用的API。Webhook将向你的应用发起http请求,典型的是post请求,应用程序由请求驱动。

区分 prettier eslint

搞懂 ESLint 和 Prettier

ESLint 是什么呢?

是一个开源的 JavaScript 的 linting 工具,使用 espree 将 JavaScript 代码解析成抽象语法树 (AST),然后通过AST 来分析我们代码,从而给予我们两种提示:

  • 代码质量问题:使用方式有可能有问题(problematic patterns)
  • 代码风格问题:风格不符合一定规则 (doesn’t adhere to certain style guidelines)

prettier

ESLint 主要解决了两类问题,但其实 ESLint 主要解决的是代码质量问题。另外一类代码风格问题其实 Airbnb JavaScript Style Guide 并没有完完全全做完,因为这些问题"没那么重要",代码质量出问题意味着程序有潜在 Bug,而风格问题充其量也只是看着不爽。

Prettier 声称自己是一个有主见 (偏见) 的代码格式化工具 (opinionated code formatter),Prettier 来不需要我们再思考究竟是用 single quote,还是 double quote 这些乱起八糟的格式问题,Prettier 帮你处理。最后的结果,可能不是你完全满意,但是,绝对不会丑,况且,Prettier 还给予了一部分配置项,可以通过 .prettierrc 文件修改。

所以相当于 Prettier 接管了两个问题其中的代码格式的问题,而使用 Prettier + ESLint 就完完全全解决了两个问题。但实际上使用起来配置有些小麻烦,但也不是什么大问题。因为 Prettier 和 ESLint 一起使用的时候会有冲突

相应的插件:

eslint-plugin-prettier

typeof 判断类型

最近在项目中使用lodash时提示isFunction函数已经deprecated了,无奈想自己验证是否为函数,只好====>

MDN,typeof

image

js内存溢出bug

我对于这件事情的处理还是过分的依赖国内网站,应该拓宽一下戏路

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

=======>最终问题通过更新node版本进行解决

commonjs 规范与浏览器

references:

浏览器加载 CommonJS 模块的原理与实现

前端科普系列(3):CommonJS 不是前端却革命了前端

浏览器默认不支持 commonjs 规范,因为它是同步的,浏览器端每加载一个文件(require 进来的文件),要发网络请求去取,如果网速慢,就非常耗时,浏览器就要一直等 require 返回,就会一直卡在那里,阻塞后面代码的执行,从而阻塞页面渲染,使得页面出现假死状态,更直白的讲是缺少 module、
exports、require、global 这几个环境变量,

- 同时基于此也有了 webpack 等打包工具把 commonjs 规范的文件构建成浏览器能运行的文件。

前端性能优化

前端性能优化之 webp

Web 性能优化: 图片优化让网站大小减少 62%

为什么要使用 webp 图片?

  • webp 是谷歌提出的一种新型图片格式,它的 key points 在于图片清晰度不变(肉眼效果)但文件体积明显缩小。
  • 文件减小后,渲染速度会提升。

如何将 jpg png 文件转换成 webp 图片文件?

imagemin

const imagemin = require('imagemin');
const PNGImages = 'assets/images/*.png';
const JPEGImages = 'assets/images/*.jpg';
const output = 'build/images';
const imageminWebp = require('imagemin-webp');

const convertPNGToWebp = () =>
 imagemin([PNGImages], output, {
   use: [
     imageminWebp({
       quality: 85,
     }),
   ]
 });
const convertJPGToWebp = () =>
 imagemin([JPGImages], output, {
   use: [
     imageminWebp({
       quality: 75,
     }),
   ]
 });
Promise.all([convertPNGToWebp, convertJPGToWebp])
 .catch(error => console.log(error));

如何使用 webp 文件,同时兼容那些不支持 webp 的浏览器呢

<picture>
   <source srcset="sample_image.webp" type="image/webp">
   <source srcset="sample_image.jpg" type="image/jpg">
   <img src="sample_image.jpg" alt="">
</picture>

使用此标记,理解 image/webp 媒体类型的浏览器将下载 Webp 图片并显示它,而其他浏览器将下载 JPEG 图片。

任何不支持 的浏览器都将跳过所有 source 标签,并加载底部 img 标签。因此,我们通过提供对所有浏览器类的支持,逐步增强了我们的页面。

如何在 css 中使用 webp 文件呢

可以参考 my vue project config来配置通过 modernizrr 来自动检测为能展示 webp 图像的浏览器根元素(html)上添加特殊标签

前端性能优化之懒加载

vue路由懒加载及组件懒加载

Webpack and dynamic import

路由懒加载:为了尽量避免首页白屏,提高首屏组件加载速度,一般在路由中通过 import 引入一个组件即可。

    {
      path: '/',
      name: 'HelloWorld',
      component: ()=>import("@/components/HelloWorld")
    }

组件懒加载:为了需要某个组件的时候才执行它,在自适应布局中则需要这种组件加载方式,一般使用 webpack dynamic import 结合

    {
      path: '/',
      name: 'HelloWorld',
      component: ()=>import(/* webpackChunkName: "Hello"  */ /* webpackMode: "eager" */"@/components/HelloWorld")
    }

其中: webpackChunkName: "Hello" 应用于调试时找到具体的 chunk,webpackMode: "eager" 则用于将本 chunk 打包在父 chunk 内,减少一个查找 chunk 的 http request

router & location.href & router-link

需求背景:在一个 Vue 项目中实现点击跳转到一个新 tab or 点击跳转但不切 tab

  • 针对:只跳转,但不切 tab

这是明显的 spa,实际上只需要使用 router.push 即可

  • 针对:跳转到新 tab

Vue.js, How to open a link in a new tab

可以使用 a 标签,设置 href 和 target="_blank" rel="noopener noreferrer"(rel 设置为安全防范)后即可跳转到新 tab

那么既然上述方式能够完美解决问题了,我们为什么还需要使用 router-link 呢,使用 router-link 同时搭配 target=“_blank” 即可实现跳转新 tab.并且它的优势如下:

  1. 无论是 HTML5 history 模式还是 hash 模式,它的表现行为一致,所以,当你要切换路由模式,或者在 IE9 降级使用 hash 模式,无须作任何变动。
  2. 在 HTML5 history 模式下,router-link 会守卫点击事件,让浏览器不再重新加载页面。
  3. 当你在 HTML5 history 模式下使用 base 选项之后,所有的 to 属性都不需要写 (基路径) 了

router.replace vs router.push

router.push 会向路由栈中添加一条记录,router.replace 则会替换路由栈中的当前一条记录。关于他们的区别,想必很多人都很熟悉。那么在实际的开发中,它们到底怎么使用呢?

时长使用 ios 设备的人会注意,ios 很注意 native 应用的右滑回退功能,即便是 Safari 中的网页。那么理所当然我们应该在开发 web 页面时在需要右滑回退的位置使用 router.push,而那些无效的路由(或者说不希望会被回退追溯到的路由)则使用 router.replace

bff

BFF-服务前端的后端

BFF(Backends for frontends)避坑指南

概念

BFF (Backends For Frontends) 服务于前端的后端。后端各种微服务、API之间的一层胶水代码。主要的业务场景请求转发、数据组织、接口适配、权鉴和SSR。

作用

  1. 数据处理:字段名处理、数据结构处理、冗余数据处理以适配前端不同的展示逻辑
  2. 数据缓存:对一些特殊场景的数据进行缓存,提高性能,进而提升用户体验
  3. 接口聚合:让后端更注重服务基础能力的开发,更聚焦业务模型本身,界面的交互与逻辑由 BFF 层来完成
  4. 服务于多个终端,Web 端、H5 活动页、小程序、IOS、Android… 底层基础服务不需要考虑不同设备的兼容逻辑,这个需求前端同学更清楚

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值