回馈牛客,整理了一些前端偏僻的知识点,大家可以查漏补缺一下
部分内容偏个人向,可能答案不够准确,答案仅供参考
- React 中的 Ref
何时使用 Ref
管理焦点
触发强制动画
继承第三方 DOM 库
其他
useRef()可以容纳任意值,可以用来存储当不想用 setState 存储的值,避免不需要的渲染
- useMemo 可以用来缓存值,做复杂运算,优化 DIFF
- 错误边界
主要用来将错误限定在一定区域内,自身抛出的错误无法解决、异步代码、服务端渲染、事件处理。
class 组件中定义了
static getDerivedStateFromError()
或componentDidCatch()
这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界。抛出错误后,请使用
static getDerivedStateFromError()
渲染备用 UI ,使用componentDidCatch()
打印错误信息
- CSS 两个元素高度保持一致
用 flex 即可实现,grow 为 1;
或者 height 为 100%
- 如何创建 BFC
float 的值不为 none
display 的值为 table、table-cell、inline-block 或者 flow-root
position 的值为 absolute 或者 fixed
overflow 的值不为 none
-
绝对定位的元素如果未指定宽高,会根据 top、bottom 等填充空间
-
relative 会放置在未添加定位的位置,在不影响页面布局的情况下调整元素位置,所以会留下空白
-
Absolute.fixed、sticky 布局虽然会脱离文档流,但是并不会生成新的复合层。通过触发 GPU 加速会生成新的复合层。但是当 fixed 的祖先元素的 transform 不为 none 时,容器为该祖先
如何生成复合层呢:
最常用 translate3d、translateZ
要触发硬件加速一定要注意层级的问题,避免隐式转换
只是避免了重绘
- 半像素问题
加一个伪元素,设置 height 为 1px,缩放 scaleY
- 权重计算规则
第零等:!important, 大过了其它任何设置。
第一等:代表内联样式,如: style=””,权值为 1000。
第二等:代表 ID 选择器,如:#content,权值为 0100。
第三等:代表类,伪类和属性选择器,如.content,权值为 0010。
第四等:代表类型选择器和伪元素选择器,如 div p,权值为 0001。
第五等:通配符、子选择器、相邻选择器等的。如*、>、+,权值为 0000。
第六等:继承的样式没有权值。
- Router 的 hash 与 history
hash 自带不需要后端配合,实现单页应用
history 通过 history API 的 pushState 实现更改页面但不跳转,但是如果手动触发前进后退的话需要服务端配合。
- 事件流小坑
在目标元素上定义的冒泡与捕获无效,执行顺序按照他们挂载的顺序来
- Node.childNodes != Node.children
前者会包含所有的,包括你打的空格,也会当作
text
节点来处理,后者不会。
-
对 DIV 的非标准属性的修改,不会同步到页面中。所以如果要同步到页面中的话可以考虑使用
div.dataset
标准值 -
为了避免多次回流,可以先创建
new DocumentFragment
,把 dom 先加到它里面去然后一次性将所有节点插入。插入节点可以考虑用Parent.appendChild
但是这样的话不能随心控制插入位置。可以考虑用Parent.insertBefore(新节点,参考点)
,配合Node.nextSibling
获取兄弟节点,精准控制节点位置。 -
设计模式
工厂模式
顾名思义,工厂模式就是有一个工厂,传入参数,生成对象。
优点是批量化生产,缺点是对象的识别不好
function objFactory(name,age){ const obj = {} obj.name = name obj.age = age return obj }单例模式
只有一个实例,通常用来将一系列数据封装到一起,供外部使用,防止污染外部空间。
function getInstance(fn){ let instance = null return function(){ return instance ? instance : fn.apply(this, arguments) } }策略模式
根据参数的不同选择不同的处理方式
- 浏览器内核是多线程的。
- GUI 渲染线程
负责处理页面绘制,回流与重绘等
- JS 引擎线程
与 GUI 线程是互斥的,只能有一个在运行。是为了防止 JS 代码中有对 DOM 的操作,导致出错。
无论什么时候都只有一个主线程在运行,在 Web Worker 的加持之下可以将复杂处理交给 Worker 线程来做,但是 Worker 线程不能访问 DOM 元素,WebWorker 运行在另一个上下文中,通过 PostMessage 进行消息通信,但是 Shared Worker 是单独开一个进程进行维护,所有标签页都可以访问.
当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件, 然后再去宏任务队列中取出一个事件。同一次事件循环中, 微任务永远在宏任务之前执行
一般是执行完一次事件循环,GUI 渲染一次
- 定时器触发线程
setTimeout 与 setInterval 所在的线程。因为 JS 引擎是单线程的,如果由主线程来负责维护定时器的话,如果发生阻塞,将导致计时器不准确,所以交给该线程来计时
- 事件触发线程
控制事件轮询。当 JS 引擎执行事件回调、AJAX 请求等的时候,会将对应的任务添加到事件队列中,排队等待 JS 引擎线程的处理。
- 异步 http 线程
在 XHR 连接后,浏览器新开一个线程,当检测到状态变更的时候,该线程就将回调放倒事件队列中等待处理。
像 setInterval 有一个最大的缺点就是如果当前的线程阻塞,但是它一直在定时器线程中执行,导致事件累加,一次性执行多个回调函数,不合理;而且当浏览器最小化的时候,浏览器为了节省资源,可能会将他的回调放在任务队列里边不执行,恢复之后一次性执行,很不合理。可以用 setTimeout 模拟实现 setInterval。
// 模拟setInterval let interval = 1000 function mn(interval) { setTimeout(function () { console.log(interval) setTimeout(arguments.callee, interval) }, interval) } mn(interval)
- JS 中的作用域
全局作用域/函数作用域/块级作用域(if/while 等包含的作用域)
-
margin、padding 无论是垂直或水平方向,均是相对于父元素的宽度
-
父元素相对(基本上是不为 static 就可以),子元素绝对,此时设置子元素的宽高为百分比,是相对于父元素(content+padding)来计算的。如果不是绝对定位的话就相对于父元素盒子宽高
-
Z-index 必须用在定位元素上,依赖于 Position 属性(relative/absolute/fixed/sticky)。当设置元素的父元素为(relative/absolute)或者自身为 float 时不生效
-
requestAnimationframe 原理
-
强缓存更新===〉对文件名进行 hash,如果要修改,全局修改文件名的 hash,实现更新
-
邮箱的正则表达式
-
XHR 的状态
01234
初始化完成(未 open)
open,未 send
开始 send
已响应但未解析
解析完毕
使用 xhr.withCredentials = true 配置携带 cookie
- Animation 与 Transition 并不是合成层
Animation 配合@keyframe 使用,定义关键帧,可控性比较好,可以通过参数自定义播放方向、暂停、结束之后的动作。
Transition,可以配合 transform、backgroundcolor 等实现动画,一般是需要人手动触发的
-
Service Worker 是在 webworker 基础上推出的,增加了持久离线缓存的能力,必须配合 HTTPS 使用。当使用缓存的时候,可以使用
cache.open('va')
打开一个缓存,使用add
方法添加缓存文件,再为它添加fetch
的监听,当fetch
对应文件的时候直接返回缓存。 -
React 内部大体分为三层:虚拟 DOM 层、调解(Reconciler)层、Render 层。
虚拟 DOM 层用来描述 DOM 节点;调解层用来负责调用生命周期方法,运行 DIFF 运算;Render 层根据不同的架构渲染出对应的界面。
Fiber 最关键的改动就是在调解层,以前调解层是一次性执行完的,经过 Fiber 改动后,整个调解层被分解为许多小片,拥有不同的优先级,通过定义的一个 Scheduler 来进行任务分配。优先级高的(如键盘输入)可以打断优先级低的(如 DIFF)。
Fiber 整体分为两个阶段:① 生成 Fiber 树,得出需要更新的节点信息,是渐进的,可以被打断。(这棵树每生成一个节点,都会将控制权交给主线程,检查有无优先级更高的任务,如果被高优先的任务打断,则丢弃正在生成的树,下次重新生成)
② 将需要更新的节点一次性更新,不可打断
-
e.target 指向触发事件的元素,e.currenttarget 指向当前事件流所在的元素
-
生命周期
-
Storage 默认是 5MB,如果存入的数据超出了
欧鹏浏览器会提示用户是否扩容
Chrome 系列的浏览器会存不进去,报错
-
纯组件 当传入的 props 没有变化时不重新渲染(只进行一层浅比较,地址不变就不重渲染),可以用于局部数据发生改变,带有输入框一类的;无状态组件 只有单纯的 render 函数,没有 shouldupdate,箭头函数的形式
-
受控组件:绑定了 state,onchange 改变 state,双向绑定了
-
非受控组件:不受 state 的影响,只是有一个 defaultvalue 或者只接受 props 的改变,自身不会去改变,input 输入值就显示
-
@import 引用的 CSS 文件只有在引用它的那个 CSS 文件被下载、解析之后,浏览器才会知道还有另外一个 CSS 需要下载,这时才去下载,然后下载后开始解析、构建 render tree 等一系列操作。这就导致浏览器无法并行下载所需的样式文件。
-
WebRTC 视频流,点对点即时通信。可以用来获取内网 IP
-
可以利用像新浪提供的 API 来获取公网 IP
-
margin 的定义不是让元素移动 xxpx,而是这个元素的旁边必须有 xxpx 的的空白
-
-
模块化
CommonJS
用于 Node
一个模块就是一个脚本文件,内部
module
代表自身,module.exports
导出接口。用require
进行导入,第一次执行时生成对象,在内存中进行缓存,多次require
都是用的同一对象,模块加载的顺序按照它在代码中出现的顺序。AMD
异步模块定义
使用 define 定义模块,用 require 导入模块,同时传入 callback 作为回调
CMD
类似于 AMD,但是强调依赖就近导入
ESM
官方提供的解决方案,使用
import
和export
导入导出接口CommonJS 输出的是一个指的拷贝,==运行时加载==,生成对象;ES6 模块输出的是值的引用,对外接口只是一个==静态定义==
-
绝对定位的基准点
position: absolute;
相对于 relative 容器的 contentposition: absolute; top: 0; left: 0;
相对于 border 以内,padding 的外侧-
<link rel="preload">
显式预加载。。max-age=0
在语义上普遍被解读为“不要在下次浏览时使用”,还是会在内存中取。如果什么缓存都不想让用,就用no-store
。。强制缓存直接减少请求数,是提升最大的缓存策略 -
Cache-control 字段常用的值:
max-age:即最大有效时间,在上面的例子中我们可以看到
must-revalidate:如果超过了 max-age 的时间,浏览器必须向服务器发送请求,验证资源是否还有效。
no-cache:虽然字面意思是“不要缓存”,但实际上还是要求客户端缓存内容的,只是是否使用这个内容由后续的对比来决定。
no-store: 真正意义上的“不要缓存”。所有内容都不走缓存,包括强制和对比。
public:所有的内容都可以被缓存 (包括客户端和代理服务器, 如 CDN)
private:所有的内容只有客户端才可以缓存,代理服务器不能缓存。默认值。 -
如果需要永久缓存可以给
max-age
设置一个巨大的时间,比如一年。这里就涉及到资源的更新问题,比较成熟的方案就是在编译的时候为文件名加 hash,这样的话请求连接一更新,资源就会自动更新了,如果是老版的网站,仍然会去访问对应的老资源,不会造成样式错乱等问题。 -
Last-Modified
是由一个unix timestamp
表示,则意味着它只能作用于秒级的改变 -
一次性添加大量元素
- 创建 createDocumentFragment,将所有节点加入到这里面然后一次性插入。
- JS 操作字符串效率比直接操作 DOM 高,所以使用字符串将所有 DOM 拼接,将字符串通过 element.inner HTML 插入到 element 中,效率更高。
-
Object.prototype.toString.call()
只能检测内置类,不能检测自定义类 - valueOf:是获取原始值 [[PrimitiveValue]];;toString:是将原始值转换为字符串
- for...in 会遍历对象的自身加原型链上的所有(不包括不可枚举)属性,配合
obj.hasOwnProperty(key)
判断是否是自身的属性。 - Object.keys()是获取对象所有的可遍历属性,定义的不可遍历的属性不能获取到
- Object.getOwnPropertyNames(obj 遍历自身所有属性,包括不可枚举的属性
- AJAX 是浏览器早期提供的 API,自带 abort 方法。fetch 的话是浏览器提供的另一个底层 API,ServiceWorker 一般就用 fetch。但是不支持 abort 和超时控制,浪费流量,且对所有有返回的请求都是 resolve,只有网络故障或请求被阻止的时候才会 Rejected
-
fetch
不会从服务端发送或接收任何 cookies , 如果站点依赖于用户cookie
,则会导致未经认证的请求(要发送cookies
,必须设置credentials
选项
fetch('http://example.com/movies.json') .then(function(response) { return response.json(); }) .then(function(myJson) { console.log(myJson); });
-
根据 base64 的编码原理,八比特表示六比特,大小比原文件大小大 1/3 尽管图片请求少了,但是 HTML 文件本身尺寸会变大,会影响首屏加载,所以要权衡代码看起来会有点丑,大量编码字符(当然也可以通过构建工具动态插入)base64 无法缓存,要缓存只能缓存包含 base64 的文件,比如 HTML 或者 CSS,这相比直接缓存图片要弱很多,一般 HTM 会改动频繁,所以等同于得不到缓存效益
-
for 循环可以实现 await 的并行执行
for (let i = 0; i < 5; i++) { (async () => {let res = await sleep({ i:i, }); console.log(res);})() }
- 前端工程化
广义上来说,前端工程化指的是将软件工程的方法和原理运用到前端开发过程中。狭义上来说,工程化就是指的将代码由源码转变成生产环境代码的一系列步骤,包括构建、分支管理、自动化测试等,DevOps 就是做的这个工作。
我觉得前端工程化的特点就是自动化、规范化。自动化就是用想 vue-cli、create-react-app 这种脚手架自动初始化目录,并且提供了设置好的脚本来实现测试、发布等功能。规范化顾名思义就是大家通过这么多年的发展,潜移默化或者约定协同将日常开发中的工作规范化,配合 code-review 保证最终代码的规范。
- React 性能优化
- 慎用 setState,如果是跟渲染无关的属性最好 ref 之类代替,减少不必要的渲染
- 对于不受父组件影响的子组件可以考虑用 PureComponent 纯组件,React 对它的 shouldComponentUpdate 进行了自动处理,只执行浅比较。对于不太方便修改源码的组件可以考虑用高阶组件进行二次封装,手动添加 shouldComponentUpdate 生命周期
- 对于函数式组件或者要传给子组件的 props 也可以考虑使用 useMemo 进行缓存,减少子组件的不必要渲染
- memo 与 useMemo 有区别,memo 需要两个参数,第一个是需要处理的组件,第二个是需要的 compare 函数(用作 shouldcomponentupdate 判断),如果不传第二个参数则类似于 PureComponent,只进行浅比较。useMemo 用来缓存任何计算,包括组件。useMemo 可以更细致的管理组件
- 合理拆分组件,对每个组件进行控制,尽可能地减少非必要渲染
- props 传递的数据要尽量保证用什么传什么,不要传不必要的数据
- 考虑到 SEO 的问题,因为 React 是支持同构的,所以考虑将重点页面进行单独处理,在判断到 refer 或者 UA 是搜索引擎的爬虫时,返回服务端渲染的页面。
- 避免使用内联的匿名函数,因为每次重新渲染得是时候都会生成一次,可以用 useCallback 处理一下
- 实现代码的懒加载,
const Home = React.lazy(() => import('./routes/Home'));
- AJAX 常用回调时期
- 触发
xhr.onreadystatechange
(之后每次 readyState 变化时,都会触发一次)- 触发
xhr.onloadstart
//上传阶段开始:- 触发
xhr.upload.onloadstart
- 触发
xhr.upload.onprogress
// 监听上传进度 state== 2- 触发
xhr.upload.onload
- 触发
xhr.upload.onloadend
//上传结束,下载阶段开始:- 触发
xhr.onprogress
// 监听下载进度 state == 3- 触发
xhr.onload
- 触发
xhr.onloadend
progress 时
lengthComputable
布尔值,代表有无指定长度
loaded
已接受或上传的长度,total
文件总字节数
- React 组件设计
- 高内聚低耦合
这是比较经典的一个理论,我认为要实现这个设计,最重要的就是把握好组件的边界。一个组件的所有资源都可以在一个 jsx 文件或者存在于一个文件夹中,通过 index 导出最终构造的组件,这就是高内聚,低耦合的话就是封装的不同组件尽量不要产生依赖,当然复杂组件由简单组件构成这种依赖那是不可避免地。但是同一层次的组件要遵循这个规则,让不同的组件实现不同的功能。比如现在一般都分为容器组件和展示组件,展示组件就要实现高内聚低耦合,容器组件则负责请求外来数据,将对应的数据传给各个展示组件,共同协调实现数据的展示和布局的规划。
- 针对不同的组件做对应的处理,比如一个展示组件,他没有自己的状态,那么在父组件渲染的时候为了避免重复渲染,要在封装组建的时候定义一下 shouldComponentUpdate 生命周期钩子,或者用 memo 处理一下
- 单一职责原则
- requestAnimationFrame callback 的执行时机与浏览器的 render 策略有关,一般是在要重绘的时候通知执行。