react与传统mvc的关系
react是一个轻量级的视图层库!!!
react不是一个完整的mvc库,最多认为是mvc中的view,甚至react并不非常认可mvc开发模式;react构建页面UI库。可以简单地理解为,react将界面分成各个独立的小块,每一块都是组件,组件之间可以组合、嵌套,就成了我们的界面
react高性能的体现: 虚拟dom
react高性能的原理:在web开发中我们总需要将变化的数据实时反映到ui上,这时就需要对dom进行操作。而复杂或频繁的dom操作通常是性能瓶颈产生的原因(提高性能操作通常是衡量一个前端人员技能的重要指标)
react fiber方法
其实简单——分片操作。把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,虽热总时间很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不好被独占,其他任务依然有运行的机会
react的特点和优势
- 虚拟dom ==> 高性能
react定义了一套变量形式的dom模型,一切操作和换算直接在变量中,这样减少了操作真实dom,性能真实相当的高,和主流MVC框架有本质的区别,不去和dom打交道
- 组件系统 ==> 高效率
react最核心的思想是将页面中任何一个区域或者元素都可以看做一个组件component
那什么是组件呢?
组件指定的是包含HTML、css、js、image元素的聚合体
- 单向数据流
其实react的核心内容就是数据绑定,所谓数据绑定指的是只要将一些服务端的数据和前端页面绑定好,开发者只关注实现业务就行了
- jsx 语法
在vue中,我们使用render函数来构建组件的dom结构性能较高,因为省去了查找和编译模板的过程,但是在render中利用createElement创建结构的时候代码可读性较低,较为复杂,此时可以利用jsx语法来在render中创建dom,解决这个问题,但是前提是需要使用工具来编译jsxl
在react变量数组的方法
map:采用arr.map方法
但是在vue中数组的遍历需要有一个关键的key值,key值的好处,复用帮助react来识别哪些元素改变了,但是当我们不能确定关键的下标时key值如何定义
(我们可以自己定义数组结构,jsonArray套json对象,给每个json对象赋予一个唯一的id字段)
jsx的解析流程
jsx代码===》 通过babel进行编译(React.createElement方法)=》 虚拟dom(js内存对象)=》ReactDOM.render()===》真实的dom
jsx代码通过babel进行编译成了虚拟dom(创建了虚拟的dom内部走的是React.createElement()方法)然后生成了虚拟DOM,然后通过ReactDOM.render()这个方法生成了真实的dom
所谓的 JSX 其实就是 JavaScript 对象,所以使用 React 和 JSX 的时候一定要经过编译的过程:
JSX代码 — > 使用react构造组件,bable进行编译—> JavaScript对象 — `ReactDOM.render()函数进行渲染`—>真实DOM元素 —>插入页面
给jsx代码添加样式
style={ {backgroundColor:“green”,transform:“rotate(30deg)”} }
className=“abc”
map遍历数组
let arr = [1,2,3]
arr.map(item=>{
return <p key={item}>{item}</P>
})
封装
let arr = [1,2,3,4]
let arr2 = ["a","b","c","d"]
function showList(arr){
return arr.map((item,index)=>{
return <p key={index}>{item}</P>
})
}
ReactDOM.render(<div>{showList(arr)}</div>,document,getElementById("app"))
数组的应用
let currentIndex = 0
function showList(arr){
return (
<ul>
{
arr.map((item,index)=>{
return <li
key={index}
className={currentIndex===index?'red':''}
onClick={()=>currentIndex=index;render()}
></li>
})
}
</Ul>
)
}
function render(){
ReactDOM.render(<div>{showList(arr)}</div>,document.getElementById("app"))
}
函数式组件
函数式组件
const 组件名(首字母大写)= props =>{
return jsx表达式
}
isShow = true
const App = props =>{
return (
<div>
<h2 onClick={()=>{isShow=!isShow;render()}}>点击实现切换</h2>
{/*<div className={isShow?"show":"hide"}>这个是div组件</div>
</div>*/}
{isShow && <div>显示与隐藏</div>}
)
}
function render(){
ReactDOM.render(<App/>,document.getElementById("app"))
}
class组件
类组件的方法
class App extends React.Component{
render(){
return jsx表达式
}
}
isShow = true
calss App extends React.Component{
render(){
return (
<div>
<h2 onClick={()=>{isShow=!isShow;render()}}>点击实现切换</h2>
{/*<div className={isShow?"show":"hide"}>这个是div组件</div>
</div>*/}
{isShow && <div>显示与隐藏</div>}
)
}
}
function render(){
ReactDOM.render(<App/>,document.getElementById("app"))
}
函数式组件接受参数
函数接受参数
{props.xxxx}
const App = props =>{
return (
<div>
我是app组件---{props.obj.name}---{props.info.phoneNo}
</div>
)
}
let obj = {
name = "zhangsan",
age = 18
}
let info = {
phoneNo: 112111,
sex: "man"
}
ReactDOM.render(<App/>,document.getElementById("app"))
类组件库接受参数
类组件接受参数
{this.props.xxxx}
class App extends React.Component{
render(){
return (
<div>这个是app组件---{this.props.obj.name}---{this.info.sex}</div>
)
}
}
let obj = {
name = "zhangsan",
age = 18
}
let info = {
phoneNo: 112111,
sex: "man"
}
ReactDOM.render(<App/>,document.getElementById("app"))
对象的处理
如何在jsx中显示一个对象
Object.key(json).map(xxxx)
let obj = {a:1,b:2,c:3}
class App extends React.Component{
render(){
return (
<div>
{
Object.keys(obj).map(item=>{
return <p key={item}>{item}:{obj[item]}</p>
})
}
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById("app"))
ref回调函数
在render()函数中ref={(el)=>{this.textInput=el}}
在componentDidMount()函数中调用this.textInput.focus()
<div id="app"></div>
<script type="text/babel">
//如果你目前还在使用 this.refs.textInput 这种方式访问 refs ,我们建议用回调函数形式绑定ref
//https://react.docschina.org/docs/refs-and-the-dom.html
class App extends React.Component{
componentDidMount(){ //相当于Vue中的mounted钩子函数
this.textInput.focus()
}
render(){
return (
<div>
<input ref={ (el)=>{ this.textInput = el} } />
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById("app"))
ref在类组件中使用
第一步是先在constructor里面通过React.createRef()来创建(在继承中第一行要写super()这个属性)
第二步在需要的组件或者dom元素上进行绑定
第三步通过current属性来获取标记的元素
<div id="app"></div>
<script type="text/babel">
//如果你目前还在使用 this.refs.textInput 这种方式访问 refs ,使用creteRef这种形式绑定ref
//https://react.docschina.org/docs/refs-and-the-dom.html
class App extends React.Component{
constructor() {
super() //继承的时候,子类的constructor里面第一行代码必须写super关键字,目的调用父类的空构造函数来去实例化子类本身。
this.myRef = React.createRef() //1.创建了一个ref的引用,并且可以通过this.myRef属性去进行访问
}
componentDidMount(){ //相当于Vue中的mounted钩子函数
//3.注意:使用current属性才可以获取到标记的元素本身
this.myRef.current.focus()
}
render(){
return (
<div>
{/*2.需要在组件或者dom元素上面通过ref进行标记*/}
<input ref={this.myRef}/>
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById("app"))
ref函数组件使用
第一步创建ref的引用对象
第二步绑定
第三步通过current来指向
//如果你目前还在使用 this.refs.textInput 这种方式访问 refs ,使用creteRef这种形式绑定ref
//https://react.docschina.org/docs/refs-and-the-dom.html
//注意:函数式组件中内部不能访问this!
//其实会发现在函数式组件中压根没有生命周期钩子,性能高!
//如何在函数式组件中定义ref? react16.8之后推出了一系列的Hooks!(useRef)
const App = props=>{
//1.创建一个ref的引用对象
const inputEl = React.useRef(null);
const onButtonClick = ()=>{
//3. `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
}
return (
<div>
{/*2.通过ref属性绑定dom元素*/}
<input ref={inputEl}/>
<button onClick={onButtonClick}>点击获取焦点</button>
</div>
)
}
ReactDOM.render(<App/>,document.getElementById("app"))
state
状态通常放在constructor进行定义
this.setState就是为了解决让视图进行更新
setState方法是异步操作的不会阻塞后续的代码
setState方法的第二个参数的回调函数内部就可以获取最新的状态
<div id="app"></div>
<script type="text/babel">
let a = 10;
class Counter extends React.Component{
//状态通常放在constructor中进行初始化定义
constructor(){
super()
this.state = {
count:1 //这就是定义组件的初始化的状态count:1
}
}
//注意:在事件函数中this需要写成箭头函数
handleClick = ()=>{
//react中是一个v层框架,数据改变,视图不变!!! (vue-mvvm Object.definePropery 数组 obj $set $forceUpdate vue3.0 proxy)
// this.state.count = ++this.state.count
// this.forceUpdate()
// 如果后续想要更改状态,并且让视图进行重新render的话,需要调用setState方法,而不是直接更改数据。
// setState方法是异步的,不会阻塞后续的代码。如何获取最新的状态呢?
// 所以说在setState方法的第二个参数的回调函数内部就可以获取到最新的状态了!
// this.setState({
// count:this.state.count+1
// },()=>{
// console.log(this.state.count)
// })
this.setState({
count:this.state.count+1
})
this.setState({
count:this.state.count+1
})
this.setState({
count:this.state.count+1
})
}
render(){
return (
<div>
我是counter... {this.state.count} -- {a}
<p><button onClick={this.handleClick}>点击更改</button></p>
</div>
)
}
}
class App extends React.Component{
render(){
return (
<div>
<Counter/>
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById("app"))
useEffect的使用方法
- useEffect的第一个参数表示回调函数,内部返回一个新的函数,在这个新的函数里面模拟了componentWillUnmount这个钩子函数
- useEffect可以配合useRef进行componentDidUpdate的钩子函数模拟
- 第二个参数代表过滤条件,如果是空数组,那就相当于模拟了componentDidMount
useEffect(()=>{
return ()=>{
// 这个参数是回调函数,内部返回一个新的函数,模拟了componentWillUnmount
}
},[])//这个是第一个参数,空数组,表示过滤,模拟的是componentDidMount
useEffect主要在函数式组件中使用,它的使用方法模拟了类方法的(componentWillUnmount,componentDidUpdate,componentDidMount)的方法
1.模拟componentDidMount方法
useEffec(()=>{
console.log("模拟componentDidMount") // 进行异步操作,模拟的是componentDidMount
return ()=>{ // return返回的是一个函数在这个函数返回值里面,模拟的是componentWillUnmount
// 在这里模拟componentWillUnmount
}
})
2.模拟componentDidUpdate
如果使用useEffect来模拟类组件的componentDidUpdate方法,需要与useRef来结合使用
useEffect(()=>{
if(!isRun.current){// current是useRef的属性,
isRun.current = true
}else{
console.log(“模拟的是componentDidUpdate组件)
}
})