回馈牛客,整理了一些前端偏僻的知识点,大家可以查漏补缺一下

部分内容偏个人向,可能答案不够准确,答案仅供参考

  • 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 指向当前事件流所在的元素

  • 生命周期

image.png

  • 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 的的空白

  • 常用排序算法总结(一) - SteveWang - 博客园

  • 模块化

  1. CommonJS

    用于 Node

    一个模块就是一个脚本文件,内部 module 代表自身,module.exports 导出接口。用 require 进行导入,第一次执行时生成对象,在内存中进行缓存,多次 require 都是用的同一对象,模块加载的顺序按照它在代码中出现的顺序。

  2. AMD

    异步模块定义

    使用 define 定义模块,用 require 导入模块,同时传入 callback 作为回调

  3. CMD

    类似于 AMD,但是强调依赖就近导入

  4. ESM

    官方提供的解决方案,使用 importexport 导入导出接口

    CommonJS 输出的是一个指的拷贝,==运行时加载==,生成对象;ES6 模块输出的是值的引用,对外接口只是一个==静态定义==

  • 绝对定位的基准点

  • position: absolute; 相对于 relative 容器的 content

  • position: 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 表示,则意味着它只能作用于秒级的改变

  • 一次性添加大量元素

  1. 创建 createDocumentFragment,将所有节点加入到这里面然后一次性插入。
  2. 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 性能优化
  1. 慎用 setState,如果是跟渲染无关的属性最好 ref 之类代替,减少不必要的渲染
  2. 对于不受父组件影响的子组件可以考虑用 PureComponent 纯组件,React 对它的 shouldComponentUpdate 进行了自动处理,只执行浅比较。对于不太方便修改源码的组件可以考虑用高阶组件进行二次封装,手动添加 shouldComponentUpdate 生命周期
  3. 对于函数式组件或者要传给子组件的 props 也可以考虑使用 useMemo 进行缓存,减少子组件的不必要渲染
  4. memo 与 useMemo 有区别,memo 需要两个参数,第一个是需要处理的组件,第二个是需要的 compare 函数(用作 shouldcomponentupdate 判断),如果不传第二个参数则类似于 PureComponent,只进行浅比较。useMemo 用来缓存任何计算,包括组件。useMemo 可以更细致的管理组件
  5. 合理拆分组件,对每个组件进行控制,尽可能地减少非必要渲染
  6. props 传递的数据要尽量保证用什么传什么,不要传不必要的数据
  7. 考虑到 SEO 的问题,因为 React 是支持同构的,所以考虑将重点页面进行单独处理,在判断到 refer 或者 UA 是搜索引擎的爬虫时,返回服务端渲染的页面。
  8. 避免使用内联的匿名函数,因为每次重新渲染得是时候都会生成一次,可以用 useCallback 处理一下
  9. 实现代码的懒加载,const Home = React.lazy(() => import('./routes/Home'));
  • AJAX 常用回调时期
  1. 触发 xhr.onreadystatechange (之后每次 readyState 变化时,都会触发一次)
  2. 触发 xhr.onloadstart //上传阶段开始:
  3. 触发 xhr.upload.onloadstart
  4. 触发 xhr.upload.onprogress // 监听上传进度 state== 2
  5. 触发 xhr.upload.onload
  6. 触发 xhr.upload.onloadend //上传结束,下载阶段开始:
  7. 触发 xhr.onprogress // 监听下载进度 state == 3
  8. 触发 xhr.onload
  9. 触发 xhr.onloadend

progress 时

lengthComputable 布尔值,代表有无指定长度

loaded 已接受或上传的长度,total 文件总字节数

  • React 组件设计
  1. 高内聚低耦合
  2. 这是比较经典的一个理论,我认为要实现这个设计,最重要的就是把握好组件的边界。一个组件的所有资源都可以在一个 jsx 文件或者存在于一个文件夹中,通过 index 导出最终构造的组件,这就是高内聚,低耦合的话就是封装的不同组件尽量不要产生依赖,当然复杂组件由简单组件构成这种依赖那是不可避免地。但是同一层次的组件要遵循这个规则,让不同的组件实现不同的功能。比如现在一般都分为容器组件和展示组件,展示组件就要实现高内聚低耦合,容器组件则负责请求外来数据,将对应的数据传给各个展示组件,共同协调实现数据的展示和布局的规划。

  3. 针对不同的组件做对应的处理,比如一个展示组件,他没有自己的状态,那么在父组件渲染的时候为了避免重复渲染,要在封装组建的时候定义一下 shouldComponentUpdate 生命周期钩子,或者用 memo 处理一下
  4. 单一职责原则
  • requestAnimationFrame callback 的执行时机与浏览器的 render 策略有关,一般是在要重绘的时候通知执行。
#Java##学习路径#
全部评论

相关推荐

点赞 评论 收藏
分享
11-24 00:11
已编辑
广东工业大学 算法工程师
避雷深圳&nbsp;&nbsp;yidao,试用期&nbsp;6&nbsp;个月。好嘛,试用期还没结束,就直接告诉你尽快找下一家吧,我谢谢您嘞
牛客75408465号:笑死,直属领导和 hr 口径都没统一,各自说了一些离谱的被裁理由,你们能不能认真一点呀,哈哈哈哈哈😅😅😅
点赞 评论 收藏
分享
2 23 评论
分享
牛客网
牛客企业服务