React,用于构建用户界面的 JavaScript 库,提供了 UI 层面的解决方案,遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效,使用虚拟DOM来有效地操作DOM,遵循从高阶组件到低阶组件的单向数据流,帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面.
特性
-
JSX语法
-
单向数据绑定
-
虚拟DOM
-
声明式编程
-
Component(组件化)
优势
-
高效灵活
-
声明式的设计,简单使用
-
组件式开发,提高代码复用率
-
单向响应的数据流会比双向绑定的更安全,速度更快
=====================================================================================
Real DOM
-
Real DOM,真实DOM, 意思为文档对象模型,是一个结构化文本的抽象,在页面渲染出的每一个结点都是一个真实DOM结构
-
更新缓慢
-
可以直接更新 HTML
-
如果元素更新,则创建新DOM
-
DOM操作代价很高
-
消耗的内存较多
Virtual DOM
-
Virtual Dom,本质上是以 JavaScript 对象形式存在的对 DOM 的描述。创建虚拟DOM目的就是为了更好将虚拟的节点渲染到页面视图中,虚拟DOM对象的节点与真实DOM的属性一一照应
-
更新更快
-
无法直接更新 HTML
-
如果元素更新,则更新 JSX
-
DOM 操作非常简单
-
很少的内存消耗
===========================================================================
JSX 是JavaScript XML的缩写,不是html或xml,基于ECMAScript的一种新特性,一种定义带属性树结构的语法;
特性:
-
自定义组件名首字母大写
-
嵌套;在render函数中return返回的只能包含一个顶层标签,否则也会报错。
-
求值表达式;JSX基本语法规则,遇到HTML标签(以<开头),就用HTML规则解析;遇到代码块(以{开头),就用JS规则解析
-
驼峰命名
-
class属性需要写成className
-
JSX允许直接在模板插入JS变量。如果这个变量是一个数组,则会展开这个数组的所有成员
-
在JSX中插入用户输入是安全的,默认情况下ReactDOM会在渲染前,转义JSX中的任意值,渲染前,所有的值都被转化为字符串形式,这能预防XSS攻击。
===============================================================================
类组件:
-
无论是使用函数或是类来声明一个组件,它决不能修改它自己的 props。
-
所有 React 组件都必须是纯函数,并禁止修改其自身 props。
-
React是单项数据流,父组件改变了属性,那么子组件视图会更新。
-
属性 props是外界传递过来的,状态 state是组件本身的,状态可以在组件中任意修改
-
组件的属性和状态改变都会更新视图。
函数组件:
-
函数组件接收一个单一的 props 对象并返回了一个React元素
-
函数组件的性能比类组件的性能要高,因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件。
===========================================================================================
Virtual DOM 是一个轻量级的 JavaScript 对象,它最初只是 real DOM 的副本。它是一个节点树,它将元素、它们的属性和内容作为对象及其属性。 React 的渲染函数从 React 组件中创建一个节点树。然后它响应数据模型中的变化来更新该树,该变化是由用户或系统完成的各种动作引起的。
Virtual DOM 工作过程有三个简单的步骤:
-
每当底层数据发生改变时,整个 UI 都将在 Virtual DOM 描述中重新渲染。
-
然后计算之前 DOM 表示与新表示的之间的差异。
-
完成计算后,将只用实际更改的内容更新 real DOM。
==========================================================================================
State
-
一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state,一般在 constructor 中初始化
-
当需要修改里面的值的状态需要通过调用setState来改变,从而达到更新组件内部数据的作用,并且重新调用组件render方法
-
setState还可以接受第二个参数,它是一个函数,会在setState调用完成并且组件开始重新渲染时被调用,可以用来监听渲染是否完成
Props
-
React的核心思想就是组件化思想,页面会被切分成一些独立的、可复用的组件,组件从概念上看就是一个函数,可以接受一个参数作为输入值,这个参数就是props,所以可以把props理解为从外部传入组件内部的数据
-
react具有单向数据流的特性,所以他的主要作用是从父组件向子组件中传递数据
-
props除了可以传字符串,数字,还可以传递对象,数组甚至是回调函数
-
在子组件中,props在内部不可变的,如果想要改变它看,只能通过外部组件传入新的props来重新渲染子组件,否则子组件的props和展示形式不会改变
相同点
-
两者都是 JavaScript 对象
-
两者都是用于保存信息
-
props 和 state 都能触发渲染更新
区别
-
props 是外部传递给组件的,而 state 是在组件内被组件自己管理的,一般在 constructor 中初始化
-
props 在组件内部是不可修改的,但 state 在组件内部可以进行修改
-
state 是多变的、可以修改
======================================================================================
是什么
React 中的 Refs提供了一种方式,允许我们访问 DOM节点或在 render方法中创建的 React元素。
本质为ReactDOM.render()返回的组件实例,如果是渲染组件则返回的是组件实例,如果渲染dom则返回的是具体的dom节点。
如何使用
-
传入字符串,使用时通过 this.refs.传入的字符串的格式获取对应的元素
-
传入对象,对象是通过 React.createRef() 方式创建出来,使用时获取到创建的对象中存在 current 属性就是对应的元素
-
传入函数,该函数会在 DOM 被挂载时进行回调,这个函数会传入一个 元素对象,可以自己保存,使用时,直接拿到之前保存的元素对象即可
-
传入hook,hook是通过 useRef() 方式创建,使用时通过生成hook对象的 current 属性就是对应的元素
应用场景
在某些情况下,我们会通过使用refs来更新组件,但这种方式并不推荐,过多使用refs,会使组件的实例或者是DOM结构暴露,违反组件封装的原则;
但下面的场景使用refs非常有用:
-
对Dom元素的焦点控制、内容选择、控制
-
对Dom元素的内容设置及媒体播放
-
对Dom元素的操作和对组件实例的操作
-
集成第三方 DOM 库
==============================================================================
setState本身并不是异步,之所以会有一种异步方法的表现形式,归根结底还是因为react框架本身的性能机制所导致的。因为每次调用setState都会触发更新,异步操作是为了提高性能,将多个状态合并一起更新,减少re-render调用。
实现同步:
- setState提供了一个回调函数供开发者使用,在回调函数中,我们可以实时的获取到更新之后的数据。
state = {
number:1
};
componentDidMount(){
this.setState({number:3},()=>{
console.log(this.state.number) // 3
})
}
- 利用setTimeout
state = {
number:1
};
componentDidMount(){
setTimeout(()=>{
this.setState({number:3})
console.log(this.state.number) //3
},0)
}
- 还有在原生事件环境下
state = {
number:1
};
componentDidMount() {
document.body.addEventListener(‘click’, this.changeVal, false);
}
changeVal = () => {
this.setState({
number: 3
})
console.log(this.state.number) //3
}
=========================================================================================
在ES6中,通过extends关键字实现类的继承,super关键字实现调用父类,super代替的是父类的构建函数,使用super(xx)相当于调用sup.prototype.constructor.call(this.xx),如果在子类中不使用super关键字,则会引发报错
super()就是将父类中的this对象继承给子类的,没有super()子类就得不到this对象
在React中,类组件是基于es6的规范实现的,继承React.Component,因此如果用到constructor就必须写super()才初始化this,在调用super()的时候,我们一般都需要传入props作为参数,如果不传进去,React内部也会将其定义在组件实例中,所以无论有没有constructor,在render中this.props都是可以使用的,这是React自动附带的,但是也不建议使用super()代替super(props),因为在React会在类组件构造函数生成实例后再给this.props赋值,所以在不传递props在super的情况下,调用this.props为undefined,而传入props的则都能正常访问,确保了 this.props 在构造函数执行完毕之前已被赋值,更符合逻辑
总结
-
在React中,类组件基于ES6,所以在constructor中必须使用super
-
在调用super过程,无论是否传入props,React内部都会将porps赋值给组件实例porps属性中
-
如果只调用了super(),那么this.props在super()和构造函数结束之间仍是undefined
===============================================================================
是什么
React基于浏览器的事件机制自身实现了一套事件机制,包括事件注册、事件的合成、事件冒泡、事件派发等,在React中这套事件机制被称之为合成事件;
合成事件是 React模拟原生 DOM事件所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器
执行顺序
-
React 所有事件都挂载在 document 对象上
-
当真实 DOM 元素触发事件,会冒泡到 document 对象后,再处理 React 事件
-
所以会先执行原生事件,然后处理 React 事件
-
最后真正执行 document 上挂载的事件
总结
-
React 上注册的事件最终会绑定在document这个 DOM 上,而不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)
-
React 自身实现了一套事件冒泡机制,所以这也就是为什么我们 event.stopPropagation()无效的原因。
-
React 通过队列的形式,从触发的组件向父组件回溯,然后调用他们 JSX 中定义的 callback
-
React 有一套自己的合成事件 SyntheticEvent
了解更多详情请点击React事件机制的理解
==================================================================================
-
render方法中使用bind
-
render方法中使用箭头函数
-
constructor中bind
-
定义阶段使用箭头函数绑定
class App extends React.Component {
handleClick() {
console.log('this > ', this);
}
render() {
return (
{/1.render方法中使用bind/}
{/*2.render方法中使用箭头函数 */}
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
//3.constructor中bind
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('this > ', this);
}
render() {
return (
)
}
}
class App extends React.Component {
//4.定义阶段使用箭头函数绑定
handleClick = () => {
console.log('this > ', this);
}
render() {
return (
)
}
}
区别
-
编写方面:方式一、方式二写法简单,方式三的编写过于冗杂
-
性能方面:方式一和方式二在每次组件render的时候都会生成新的方法实例,性能问题欠缺。若该函数作为属性值传给子组件的时候,都会导致额外的渲染。而方式三、方式四只会生成一个方法实例
-
综合上述,方式四(箭头函数绑定)是最优的事件绑定方式
===============================================================================
- 初始渲染阶段:这是组件即将开始其生命之旅并进入 DOM 的阶段。
getDefaultProps:获取实例的默认属性
getInitialState:获取每个实例的初始化状态
componentWillMount:组件即将被装载、渲染到页面上
render:组件在这里生成虚拟的 DOM 节点
componentDidMount:组件真正在被装载之后
- 更新阶段:一旦组件被添加到 DOM,它只有在 prop 或状态发生变化时才可能更新和重新渲染。这些只发生在这个阶段。
componentWillReceiveProps:组件将要接收到属性的时候调用
shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回 false,接收数据后不更新,阻止 render 调用,后面的函数不会被继续执行了)
componentWillUpdate:组件即将更新不能修改属性和状态
render:组件重新描绘
componentDidUpdate:组件已经更新
- 卸载阶段:这是组件生命周期的最后阶段,组件被销毁并从 DOM 中删除。
componentWillUnmount:组件即将销毁
===================================================================================
-
componentWillMount() – 在渲染之前执行,在客户端和服务器端都会执行。
-
componentDidMount() – 仅在第一次渲染后在客户端执行。
-
componentWillReceiveProps() – 当从父类接收到 props 并且在调用另一个渲染器之前调用。
-
shouldComponentUpdate() – 根据特定条件返回 true 或 false。如果你希望更新组件,请返回true ,不想更新组件则返回 false就会阻止render渲染。默认情况下,它返回 true。
-
componentWillUpdate() – 在 DOM 中进行渲染之前调用。
-
componentDidUpdate() – 在渲染发生后立即调用。
-
componentWillUnmount() – 从 DOM 卸载组件后调用。用于清理内存空间。
==============================================================================
shouldComponentUpdate,这个方法用来判断是否需要调用 render 方法重绘 dom。
因为 dom 的描绘非常消耗性能,如果我们能在这个方法中能够写出更优化的 dom diff 算法,可以极大的提高性能。
点击React学习笔记四——受控组件和非受控组件查看详解
============================================================================
受控组件是React控制的组件,input等表单输入框值不存在于 DOM 中,而是以我们的组件状态存在。每当我们想要更新值时,我们就像以前一样调用setState。
不受控制组件是您的表单数据由 DOM 处理,而不是React 组件,Refs 用于获取其当前值;
=============================================================================
和原生HTML定义事件的唯一区别就是JSX采用驼峰写法来描述事件名称,大括号中仍然是标准的JavaScript表达式,返回一个事件处理函数。在JSX中你不需要关心什么时机去移除事件绑定,因为React会在对应的真实DOM节点移除时就自动解除了事件绑定。
React并不会真正的绑定事件到每一个具体的元素上,而是采用事件代理的模式:在根节点document上为每种事件添加唯一的Listener,然后通过事件的target找到真实的触发元素。这样从触发元素到顶层节点之间的所有节点如果有绑定这个事件,React都会触发对应的事件处理函数。这就是所谓的React模拟事件系统。
==============================================================================
虚拟dom(virtual dom) 是 JS对象,是一个真实dom的JS对象;虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了。
===============================================================================
跟Vue一样,React 也存在diff算法,而元素key属性的作用是用于判断元素是新创建的还是被移动的元素,从而减少不必要的Diff,因此key的值需要为每一个元素赋予一个确定的标识。
-
如果列表数据渲染中,在数据后面插入一条数据,key作用并不大;前面的元素在diff算法中,前面的元素由于是完全相同的,并不会产生删除创建操作,在最后一个比较的时候,则需要插入到新的DOM树中。因此,在这种情况下,元素有无key属性意义并不大。
-
如果列表数据渲染中,在前面插入数据时,当拥有key的时候,react根据key属性匹配原有树上的子元素以及最新树上的子元素,只需要将元素插入到最前面位置,当没有key的时候,所有的li标签都需要进行修改
-
并不是拥有key值代表性能越高,如果说只是文本内容改变了,不写key反而性能和效率更高,主要是因为不写key是将所有的文本内容替换一下,节点不会发生变化,而写key则涉及到了节点的增和删,发现旧key不存在了,则将其删除,新key在之前没有,则插入,这就增加性能的开销
总结
良好使用key属性是性能优化的非常关键的一步,注意事项为:
-
key 应该是唯一的
-
key不要使用随机值(随机数在下一次 render 时,会重新生成一个数字)
-
避免使用 index 作为 key
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
良好使用key属性是性能优化的非常关键的一步,注意事项为:
-
key 应该是唯一的
-
key不要使用随机值(随机数在下一次 render 时,会重新生成一个数字)
-
避免使用 index 作为 key
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-lFHfKikn-1714811966128)]
[外链图片转存中…(img-LYaD3c5O-1714811966129)]
[外链图片转存中…(img-Ir3p9J1P-1714811966129)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!