React,用于构建用户界面的 JavaScript 库,只提供了 UI 层面的解决方案
遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效
使用虚拟 DOM 来有效地操作 DOM,遵循从高阶组件到低阶组件的单向数据流
帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面
react 类组件使用一个名为 render() 的方法或者函数组件return,接收输入的数据并返回需要展示的内容
react的特性:
JSX 语法
单向数据绑定
虚拟 DOM
声明式编程
Component
区别:虚拟 DOM 不会进行排版与重绘操作,而真实 DOM 会频繁重排与重绘
虚拟 DOM 的总损耗是“虚拟 DOM 增删改+真实 DOM 差异增删改+排版与重绘”,真实 DOM 的总损耗是“真实 DOM 完全增删改+排版与重绘”
优缺点
真实 DOM 的优势:易用
缺点:效率低,解析速度慢,内存占用量过高
性能差:频繁操作真实 DOM,易于导致重绘与回流
使用虚拟 DOM 的优势如下:
简单方便:如果使用手动操作真实 DOM 来完成页面,繁琐又容易出错,在大规模应用下维护起来也很困难
性能方面:使用 Virtual DOM,能够有效避免真实 DOM 数频繁更新,减少多次引起重绘与回流,提高性能
跨平台:React 借助虚拟 DOM,带来了跨平台的能力,一套代码多端运行
缺点:
在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化
首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,速度比正常稍慢
React生命周期主要分为三个阶段:创建阶段、更新阶段和卸载阶段
componentWillMount:组件将要被构建
Render:渲染组件
componentDidMount:组件构建完成
componentWillReceiveProps:组件将要被接收新的属性数据
shouldComponentUpdate:判断组件是否应该更新
compoinentWillUpdate:组件将要更新
Render:渲染组件
componentDidUpdate:组件更新完成
componentWillUnmount:组件将要被销毁
在react生命周期中,即将有三个钩子函数被淘汰,他们分别时componentWillMount和componentWillReceiveProps和componentnWillUpdate
其实这三个方法仍然存在,只要在前者加上UNSAFE前缀,同时增加两个新的生命周期钩子
getDerivedStateFromProps getSnapshotBeforeUpdate
一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state, 当需要修改里面的值的状态需要通过调用setState来改变,从而达到更新组件内部数据的作用
setState第一个参数可以是一个对象,或者是一个函数,而第二个参数是一个回调函数,用于可以实时的获取到更新之后的数据
在使用setState更新数据的时候,setState的更新类型分成:同步更新,异步更新
在组件生命周期或React合成事件中,setState是异步
在setTimeout或者原生dom事件中比如addEventListener绑定事件,setState是同步,
对同一个值进行多次 setState, setState 的批量更新策略会对其进行覆盖,取最后一次的执行结果。
React自身实现了一套事件机制,包括事件的注册、事件的存储、事件的合成及执行等,
React 上注册的事## 标题件最终会绑定在document这个 DOM 上,而不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)
React 自身实现了一套事件冒泡机制,所以这也就是为什么我们 event.stopPropagation()无效的原因。
React 通过队列的形式,从触发的组件向父组件回溯,然后调用他们 JSX 中定义的 callback
React 有一套自己的合成事件 SyntheticEvent
BFC的理解
受控组件
由React控制的输入表单元素而改变其值的方式,称为受控组件。
比如,给表单元素input绑定一个onChange事件,当input状态发生变化 非受控组件
非受控组件
表单数据由DOM本身处理。即不受setState()的控制,与传统的HTML表单输入相似,input输入值 即显示最新值。
在非受控组件中,可以使用一个ref来从DOM获得表单值。
fiber构架的理解
通俗的话说就是:使用createElement函数返回一个reactElement的函数,reactElement返回一个虚拟的额节点,虚拟节点中嵌套虚拟节点,然后通过reactDom.render返回,并转化为一个真实dom
使用多个定时器造成的冲突 dom的引用 闭包 全局变量
style-loader 将css添加到DOM的内联样式标签style里
css-loader 允许将css文件通过require的方式引入,并返回css代码
less-loader 处理less
sass-loader 处理sass
postcss-loader 用postcss来处理CSS
autoprefixer-loader 处理CSS3属性前缀,已被弃用,建议直接使用postcss
file-loader 分发文件到output目录并返回相对路径
url-loader 和file-loader类似,但是当文件小于设定的limit时可以返回一个Data Url
html-minify-loader 压缩HTML
babel-loader 用babel来转换ES6文件到ES5
loader是webpack核心,用于对模块的源代码进行转换
loader支持链式调用,从右至左执行,支持同步或异步函数
强缓存:不像服务器发送请求,直接从缓存中获取读取资源,在控制台的网络中,可以看到该请求返回200的状态码
协商缓存:会向服务器发送请求,服务器根据这个请求的请求头携带的一些参数来判断是否命中协商缓存,如果命中,那么就返回一个304的状态码,并携带一个新的请求头通知浏览器从缓存中读取资源
通过webpack优化前端的手段有:
JS代码压缩
CSS代码压缩
Html文件代码压缩
文件大小压缩
图片压缩
Tree Shaking
代码分离
内联 chunk
常见内存泄露情况
意外的全局变量
function foo(arg) {
bar = "this is a hidden global variable";
}
另一种意外的全局变量可能由 this 创建:
function foo() {
this.variable = "potential accidental global";
}
// foo 调用自己,this 指向了全局对象(window)
foo();
上述使用严格模式,可以避免意外的全局变量
定时器也常会造成内存泄露
var someResource = getData();
setInterval(function() {
var node = document.getElementById('Node');
if(node) {
// 处理 node 和 someResource
node.innerHTML = JSON.stringify(someResource));
}
}, 1000);
如果id为Node的元素从DOM中移除,该定时器仍会存在,同时,因为回调函数中包含对someResource的引用,定时器外面的someResource也不会被释放
包括我们之前所说的闭包,维持函数内局部变量,使其得不到释放
function bindEvent() {
var obj = document.createElement('XXX');
var unused = function () {
console.log(obj, '闭包内引用obj obj不会被释放');
};
obj = null; // 解决方法
}
没有清理对DOM元素的引用同样造成内存泄露
const refA = document.getElementById('refA');
document.body.removeChild(refA); // dom删除了
console.log(refA, 'refA'); // 但是还存在引用能console出整个div 没有被回收
refA = null;
console.log(refA, 'refA'); // 解除引用
包括使用事件监听addEventListener监听的时候,在不监听的情况下使用removeEventListener取消对事件监听