首屏加载速度慢怎么解决?
- 首屏时间?
- 首屏时间是指。浏览器从响应用户输入地址,到浏览器渲染完成的时间,此时整个页面不一定要全部渲染完成。但是需要展示当前视窗需要完成的内容。
- 首屏加载慢的原因?
- 网络延时问题
- 资源文件积是否过大
- 资源是否直接发送请求去更改了
- 加载脚本的时候,渲染内容堵塞了。
- 首屏加载速度慢
- 首屏加载慢的优化方式?
- 减小入口文件积(懒加载,把不同的路由对应的组件分割为不同的代码)
- 静态资源本地缓存
- ul框架按需加载
- 图片资源的压缩
大文件如何做断点续传?
- 做断点续传的原因?
- 不管怎么样的需求,在量级达到一定标准之后,就会变得非常复杂,大文件上传就会变得异常困难。上传时间会过长,高频词上传失败,失败后又重新上传。为了解决上述问题,我们对大文件进行单独处理。
- 分片上传?
- 分片上传就是将一个大文件,按照一定的大小,将整个文件分割成多个数据块来进行分片react事件机制
- React基于浏览器的事件机制自身实现了一套事件机制,包括事件注册、事件的合成、事件冒泡、事件派发等,在React中这套事件机制被称之为合成事件。
React的时间机制:
React 上注册的事件最终会绑定在document这个 DOM 上,而不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)
React 自身实现了一套事件冒泡机制,所以这也就是为什么我们 event.stopPropagation()无效的原因。
React 通过队列的形式,从触发的组件向父组件回溯,然后调用他们 JSX 中定义的 callback
React 有一套自己的合成事件 SyntheticEvent - React组件之间如何通信?
组件是vue中最强大的功能之一,同样组件化是React的核心思想
相比vue,React的组件更加灵活和多样,按照不同的方式可以分成很多类型的组件,而通信指的是发送者通过某种媒体以某种格式来传递信息到收信者以达到某个目的,广义上,任何信息的交通都是通信,组件间通信即指组件通过某种方式来传递信息以达到某个目的. - 父组件向子组件传递:
- (父组件在调用子组件的时候,只需要在子组件标签内传递参数,子组件通过props属性就能接收父组件传递过来的参数)
- 子组件向父组件传递
- (父组件向子组件传一个函数,然后通过这个函数的回调,拿到子组件传过来的值)
- 兄弟组件之间的通信
- (父组件作为中间层来实现数据的互通,通过使用父组件传递)
- 父组件向后代组件传递
- (使用context提供了组件之间通讯的一种方式,可以共享数据,其他数据都能读取对应的数据)
- 非关系组件传递
- (如果组件之间关系类型比较复杂的情况,建议将数据进行一个全局资源管理,从而实现通信,例如redux。关于redux的使用后续再详细介绍)
- 说说你对fiber架构的理解?解决了什么问题
JavaScript引擎和页面渲染引擎两个线程是互斥的,当其中一个线程执行时,另一个线程只能挂起等待
如果 JavaScript 线程长时间地占用了主线程,那么渲染层面的更新就不得不长时间地等待,界面长时间不更新,会导致页面响应度变差,用户可能会感觉到卡顿
而这也正是 React 15 的 Stack Reconciler所面临的问题,当 React在渲染组件时,从开始到渲染完成整个过程是一气呵成的,无法中断
如果组件较大,那么js线程会一直执行,然后等到整棵VDOM树计算完成后,才会交给渲染的线程
这就会导致一些用户交互、动画等任务无法立即得到处理,导致卡顿的情况
Fiber: React Fiber 是 Facebook 花费两年余时间对 React 做出的一个重大改变与优化,是对 React 核心算法的一次重新实现。
在react中,主要做了以下的操作:
为每个增加了优先级,优先级高的任务可以中断低优先级的任务。然后再重新,注意是重新执行优先级低的任务
增加了异步任务,调用requestIdleCallback api,浏览器空闲的时候执行
dom diff树变成了链表,一个dom对应两个fiber(一个链表),对应两个队列,这都是为找到被中断的任务,重新执行。 - 从架构角度来看,Fiber 是对 React核心算法(即调和过程)的重写。
- 从编码角度来看,Fiber是 React内部所定义的一种数据结构,它是 Fiber树结构的节点单位,也就是 React 16 新架构下的虚拟DOM。
- 解决过程: Fiber把渲染更新过程拆分成多个子任务,每次只做一小部分,做完看是否还有剩余时间,如果有继续下一个任务;如果没有,挂起当前任务,将时间控制权交给主线程,等主线程不忙的时候在继续执行。说说如何借助webpack来优化前端性能?
- 随着前端的项目逐渐扩大,必然会带来的一个问题就是性能,一般项目在完成后,会通过webpack进行打包,利用webpack对前端项目性能优化是一个十分重要的环节。
通过webpack优化前端的手段有: - JS代码压缩
- CSS代码压缩
- Html文件代码压缩
- 文件大小压缩
- 图片压缩
- Tree Shaking
- 代码分离
- 内联 chunk
- JS代码压缩
- 上传。上传完成之后,再由服务端多所有上传的文件进行汇总整合成原始的文件。
- 跟
Vue
一致,React
通过引入Virtual DOM
的概念,极大地避免无效的Dom
操作,使我们的页面的构建效率提到了极大的提升.而diff
算法就是更高效地通过对比新旧Virtual DOM
来找出真正的Dom
变化之处。diff作用:渲染真实DOM的开销很大,有时候我们修改了某个数据,直接渲染到真实dom上会引起整个dom树的重绘和重排。我们希望只更新我们修改的那一小块dom,而不是整个dom,diff算法就帮我们实现了这点。 - diff算法的本质是找出两个对象之间的差异
- diff算法的核心是子节点数组对比,思路是通过首尾两端对比
- key的作用主要是
- 决定节点是否可以复用
- 建立key-index的索引,主要是替代遍历,提升性能。
react
中diff
算法主要遵循三个层级的策略:
- tree层级
- 1)React通过updateDepth对Virtual DOM树进行层级控制。
- (2)对树分层比较,两棵树只对同一层次节点进行比较。如果该节点不存在时,则该节点及其子节点会被完全删除,不会再进一步比较。
(3)只需遍历一次,就能完成整棵DOM树的比较。
*DOM
节点跨层级的操作不做优化,只会对相同层级的节点进行优化,只有删除、创建操作,没有移动操作进行比较
- conponent 层级:
- 如果是同一个类的组件,则会继续往下
diff
运算,如果不是一个类的组件,那么直接删除这个组件下的所有子节点,创建新的
- element 层级
- 在element 层级中,每个节点在对应的层级用唯一的
key
作为标识,当我们比较同一层级的节点的时候,key就起到了关键的作用。通过key
可以准确地发现新旧集合中的节点都是相同的节点,因此无需进行对节点删除和创建,只需要将旧集合中节点的位置进行移动,更新为新集合中节点的位置。 - 刚刚提到了对节点进行删除、创建和移动,这是element提供的三种操作。
- 基于Diff的开发建议
基于tree diff:
开发组件时,尽可能少地动态操作DOM结构,尤其是移动操作。
当节点数过大或者页面更新次数过多时,页面卡顿的现象会比较明显。
这时可以通过 CSS 隐藏或显示节点,而不是真的移除或添加 DOM 节点。
基于component diff:
注意使用 shouldComponentUpdate() 来减少组件不必要的更新。
对于类似的结构应该尽量封装成组件,既减少代码量,又能减少component diff的性能消耗。
基于element diff:
当操作列表的时候,尽量减少这种大操作,比如将最后一个节点移动到列表首部的操作,当节点数量过大或更新操作过于频繁时,在一定程度上会影响 React 的渲染性能。 - 说说react 中jsx语法糖的本质?
eact通过babel把JSX转成createElement
函数,生成ReactElement
对象,然后通过ReactDOM.render函
数把ReactElement
渲染成真实的DOM
元素。 - Jsx通过babel最终转化成react.createElement这种形式,在转化的过程中,babel在编译时候,会判断jsx中组件的首字母,情况分为以下几种:
当首字母小写的时候,其备认定为原生的dom 标签,createElement的第一个变量被编译为字符串
当首字母为大写的时候,其被认定为自定义组件,createElement的第一个变量被编译为对象。
这些都会通过renderDom.render()方法进行挂载。
在react中,节点大致可以分成四个类别:
- 原生标签节点,
- 文本节点
- 函数组件
- 类组件。
- 这些类别最终都会被转化成React.createElement这种形式,createElement会根据传入的节点信息进行一个判断:
- 如果是原生标签节点, type 是字符串,如div、span
- 如果是文本节点, type就没有,这里是 TEXT
- 如果是函数组件,type 是函数名
- 如果是类组件,type 是类名
- 虚拟DOM会通过ReactDOM.render进行渲染成真实DOM,当首次调用时,容器节点里的所有 DOM 元素都会被替换,后续的调用则会使用 React 的 diff算法进行高效的更新
如果提供了可选的回调函数callback,该回调将在组件被渲染或更新之后被执行
Render
执行流程 - 使用React.createElement或JSX编写React组件,实际上所有的 JSX 代码最后都会转换成React.createElement(...) ,Babel帮助我们完成了这个转换的过程。
- createElement函数对key和ref等特殊的props进行处理,并获取defaultProps对默认props进行赋值,并且对传入的孩子节点进行处理,最终构造成一个虚拟DOM对象.
- ReactDOM.render将生成好的虚拟DOM渲染到指定容器上,其中采用了批处理、事务等机制并且对特定浏览器进行了性能优化,最终转换为真实DOM
- 说说AMD、CMD、commonJS模块化规范的区别?
- 这三个规范都是为Js模块化加载而生的,使模块能够按需加载,使系统同庞杂的代码得到组织和管理。模块化的管理代码使多人开发得到了更好的合作
- CommonJS:nodejs中的模块化机制,模块通过require()引入,exports或modules.exports导出
- 规范的特点:
- 当使用require命令加载某个模块时,就会运行整个模块的代码。
- 对于基本数据类型,属于复制。即会被模块缓存。同时,在另一个模块可以对该模块输出的变量重新赋值。
- 对于复杂数据类型,属于浅拷贝。由于两个模块引用的对象指向同一个内存空间,因此对该模块的值做修改时会影响另一个模块
- 循环加载时,属于加载时执行
- 执行流程:
- 根据CommonJS规范,一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为global对象的属性2、模块输出:模块只有一个出口,module.exports对象,我们需要把模块希望输出的内容放入该对象3、加载模块:加载模块使用require方法,该方法读取一个文件并执行,返回文件内部的module.exports对象
- 第二个AMD:上面的commonjs主要适用于node.js这种后端模块化,其中的require引用是同步的,前端环境中有很多异步的情况,因此commonjs是不够用的,AMD就出现了。
- 多个js文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器
- 2、js加载的时候浏览器会停止页面渲染,加载文件越多,页面失去响应时间越长
- CMD规范是阿里的玉伯提出来的,实现js库为sea.js。和requirejs非常相似,也是前端异步加载模块化。不同的是它的模块是需要的时候去请求,而不是先加载再使用。RequireJS用的就是AMD,AMD用define定义方法,用require引用模块。
- 最明显的区别就是在模块定义时对依赖的处理不同
1、AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块
2、CMD推崇就近依赖,只有在用到某个模块的时候再去require
- 说说你对webSocket的理解?
- WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。 - 为什么需要WebSocket
1、传统上的HTTP协议它是无状态的,服务器不能够识别是哪个客户端发送的请求,不能够保存状态。
2、WebSocket弥补了这一问题,在客户端向服务端发送请求之后,服务器处理请求并返回到客户端,使用WebSocket可以使得服务器主动向浏览器推送消息 - WebSocket与HTTP的区别
1、HTTP是客户端直接向服务端发送请求,WebSocket是在完成一次握手之后建立连接
2、发送HTTP请求的时候,服务器不能记住是谁发给他的
3、发送WebSocket请求,服务器可以记住是谁发给他的
- websoket协议流程
服务器可以主动向客户端推送消息,客户端也可以主动向服务器发送消息。是真正的双向平等对话,属于服务器推送技术的一种。websocket协议的协议标识符是ws(如果加密,则为wss),服务器网址就是URL。 - WebSocket的优缺点
优点:建立WebSocket连接之后,客户端与服务端交流更方便
客户端只需要向服务端发送一次请求,服务端主动向客户端发送消息
缺点:在服务端的状态不会频繁变化的时候,就不需要使用WebSocket连接了,浪费性能。