React 是什么?
React是一个声明式,高效且灵活的用于构建用户界面的JavaScript库。
React.createElement():知道就行
react脚手架
先安装:npm install -g create-react-app
在开箱急用:npx create-react-app my-app
启动项目:npm start
Npx :npm v5.2.0引入的一条命令
ReactDOM.render()
1、ReactDOM.render函数是整个 React 应用程序首次渲染的入口函数
2、ReactDOM.render是React的最基本方法用于将模板转为HTML语言,并插入指定的DOM节点。ReactDOM.render(template,targetDOM)。
3、该方法接收两个参数:第一个是创建的模板,多个dom元素外层需使用一个标签进行包裹,第二个参数是插入该模板的目标位置。
4、ReactDOM.render( JSX写的html模板,dom容器对象)
5、总结:一个react的程序,就是把JSX通过ReactDOM.render()函数渲染到网页上。程序员完成的是JSX的编写。
let arr = ["铅笔","油笔","钢笔","毛笔"]
var arr2 =[]
for(let i in arr){
arr2.push(<li>{arr[i]}</li>)
}
const show = ()=> (
<ul>{arr2}</ul>
)
ReactDOM.render(show(),document.getElementById("box"))
JSX
JSX是Javascript的一种语法拓展
JSX是JavaScript XML简写,表示在JavaScript中编写XML格式代码(也就是HTML格式)
优势:
- 声明式语法更加直观
- 与HTML结构相同
- 降低了学习成本、提升开发效率
使用JSX语法创建react元素:const title = <h1 >HELLO</h1>
JSX不是标准的ECMAScript语法,它是ECMAScript的语法扩展
需要使用babel编译处理后,才能在浏览器环境中使用
Create-react-app脚手架已经默认有该配置,无需手动配置:@babel/preset-react
注意点
1、属性名使用驼峰命名法
2、特殊属性名:class=>className,for=>htmlFor, tabindex=>tabindex
3、没有子节点的react元素可用/>结束
4、推荐使用小括号包裹JSX,从而避免JS中的自动插入分号陷阱
JSX中使用JS表达式
嵌入JS表达式
1、数据存储在JS中
语法{JS表达式}
注意点:
1、单大括号中可以使用任意JS表达式
2、JSX自身也是JIS表达式
3、注意:JS中的对象是个例外,一般只出现在style属性中
4、在{}中不能出现if/for语句的
JSX的列表渲染
- 渲染一组数据,使用数组的map()方法
- 渲染列表时应该添加key属性,属性值要保证唯一
- map()遍历谁,就给谁+key属性
- 尽量避免使用索引号作为key值
Jsx样式处理
行内样式---style
类名--className
React完全利用JS语言自身的能力来编写UI,而不是造轮子增强HTML功能
react组件
组件是react的一等公民,使用react就算在用组件
组件表示页面中的部分功能
组合多个组件实现完整的页面功能
特点:可复用、独立、可组合
两种创建方式
1、使用函数创建组件
-使用JS的函数(箭头函数)创建的组件
-名称大写字母开头
-必须有返回值,表示该组件的结构
-返回值为Null,不渲染任何内容
-使用函数名作为组件标签名
例:
function Hello(){
return (
<div>函数组件</div>
)
}
ReactDOM.render(<Hello />, root)
2、使用类创建组件
-使用ES6的class创建的组件
-大写字母开头
-类组件应该继承React.Component父类,从而可以使用父类中提供的方法或属性
-必须提供render()方法
-Render方法必须有返回值,表示该组件的结构
例:
class Hello extends React.Component{
render() {
return <div>类组件</div>
}
}
ReactDOM.render(<Hello />, root)
3、React事件处理
3.1事件绑定
React事件绑定语法与DOM事件语法相似
语法:on+事件名称={事件处理程序},比如: onClick={() =>{}}
注意:React事件采用驼峰命名法,比如:onMouseEnter、onFocus
3.2事件对象
可以通过事件处理程序的参数获取到事件对象
React中的事件对象叫做︰合成事件(对象)
合成事件:兼容所有浏览器,无需担心跨浏览器兼容性问题
4、有状态组件和无状态组件
函数组件又叫做无状态组件,类组件又叫做有状态组件
状态( state )即数据
函数组件没有自己的状态,只负责数据展示(静)
类组件有自己的状态,负责更新Ul,让页面“动”起来
比如计数器案例中,点击按钮让数值加1。0和1就是不同时刻的状态,而由0变为1就表示状态发生了变化。
状态变化后,UI也要相应的更新。React中想要实现该功能,就要使用有状态组件来完成。
5、组件中的state和setState
5.1 state的基本使用
状态( state )即数据,是组件内部的私有数据,只能在组件内部使用
state的值是对象,表示一个组件中可以有多个数据
获取状态:this.state
总结:
状态即数据
状态是私有的,只能在组件内部使用
通过this.state来获取状态
5.2 setState()修改状态
状态是可变的
语法:this.setState(要修改的数据})
注意∶不要直接修改state 中的值,这是错误的!!!
setState()作用∶
1.修改state 2.更新UI
思想:数据驱动视图
5.3 从JSX中抽离事件处理程序
JSX中掺杂过多JS逻辑代码,会显得非常混乱
推荐︰将逻辑抽离到单独的方法中,保证JSX结构清晰
原因:事件处理程序中this的值为undefined
希望:this指向组件实例( render方法中的this即为组件实例)
6、事件绑定this指向
6.1 箭头函数
利用箭头函数自身不绑定this的特点
render()方法中的this为组件实例,可以获取到setState()
6.2 Function.prototype.bind()
利用ES5中的bind方法,将事件处理程序中的this与组件实例绑定到一起
6.3 class实例方法(推荐)
利用箭头函数形式的class实例方法
注意:该语法是实验性语法,但是,由于babel的存在可以直接使用
7、表单处理
7.1 受控组件
HTML中的表单元素是可输入的,也就是有自己的可变状态
而,React中可变状态通常保存在state中,并且只能通过setState()方法来修改
React将 state 与表单元素值value绑定到一起,由state的值来控制表单元素的值
受控组件:其值受到React 控制的表单元素
步骤
1.在state中添加一个状态,作为表单元素的value值(控制表单元素值的来源)
2.给表单元素绑定change事件,将表单元素的值设置为state的值(控制表单元素值的变化)
示例总结:
1.文本框、富文本框、下拉框操作value属性
2.复选框操作checked属性
多表单元素优化步骤:
1.给表单元素添加name属性,名称与state相同
2.根据表单元素类型获取对应值
3.在change事件处理程序中通过[name]来修改对应的state
7.2 非受控组件
说明:借助于ref,使用原生 DOM方式来获取表单元素值
ref 的作用∶获取DOM或组件
使用步骤:
1.调用React.createRef()方法创建一个ref 对象
2.将创建好的ref 对象添加到文本框中
3.通过ref 对象获取到文本框的值
react组件通讯
组件是独立且封闭的单元,默认情况下,只能使用组件自己的数据,在组件优化过程中,我们将一个完整的功能拆分成多个组件,
以更好的完成整个应用的功能,而在这个过程中,多个组件之间不可避免的要共享某些数据,为了实现这个功能,就需要打破
组件的独立封闭性,让其与外界沟通,这个过程就是组件通讯
组件的props
组件是封闭的,要接收外部数据应该通过props来实现
props的作用:接收传递给组件的数据
传递数据:给组件标签添加属性
接收数据:哈数组件通过参数props接收数据,类组件通过this.props接收数据
特点:
1、可以给组件传递任意类型的数据
2、Props是只读的对象,只能读取属性的值,无法修改对象
3、注意:使用类组件时,如果写了构造函数,应该将props传递给super(),否则,无法在构造函数中获取到props
组件通讯的三种方式
1、父组件传递数据给子组件
A、父组件提供要传递的state数据
B、给子组件标签添加属性,值为state中的数据
C、子组件中通过props接收父组件中传递的数据
2、子组件传递数据给父组件
A、父组件提供一个回调函数(用于接收数据)
B、将该函数作为属性的值,传递给子组件
C、子组件通过Props调用回调函数
D、将子组件的数据作为参数传递给回调函数
注意:回调函数中this的指向问
3、兄弟组件
将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态(状态提升)
公共父组件职责:1、提供共享状态 2、提供操作共享状态的方法
要通讯的子组件只需通过props接收状态或操作状态的方法
Context
作用:实现跨组件传递数据(主题、语言等)
1、调用React.createContext()创建Procider(提供数据)和Consumer(消费数据)两个组件
2、使用Procider组件作为父节点
3、设置value属性,表示要传递的数据
4、调用Consumer组件接收数据
总结:
1、如果两个组件是远方亲戚可以使用Context实现组件通讯
2、context提供了两个组件:Provider 和 Consumer
3、Provide组件:用来提供数据
4、Consumer组件:用来消费数据
props深入
1、children属性
表示组件标签的子节点,当组件标签有子节点时,props就会有该属性
children属性与普通的props一样,值可以时任意值
2、props校验
允许在创建组件的时候,就指定Props的类型、格式等
作用:捕获使用组件时因为props导致的错误,给出明确的错误提示,增加组件的健壮性
使用步骤:
1、安装包prop-types
2、导入Prop-types包
3、使用组件名.propTypes = {} 来给组件的Props添加校验规则
prop-types约束规则
1、常见类型:array,bool, func,number,object,string
2、React元素类型:elemrnt
3、必填项:isRequired
4、特定的结构对象:shape({})
默认值
1、场景:分页组件-》每页显示条数
2、App.defaultProps
3、作用:给Props设置默认值,在未传入props时生效
组件的生命周期
只有类组件才有声明周期
1、创建时
Constructor:最先执行,作用:初始化state,未事件处理程序绑定this
Render:每次组件渲染都会触发;作用:渲染UI(注意:不能调用setState())
componentDidMount:组件挂载(完成dom渲染)后;作用:发送网络请求,DOM操作
2、更新时
setState(),组件接收到新的props、forceUpdate()
componentDidpdate:组件更新后;作用:发送网络请求;DOM操作
注意:在componentDidpdate()中直接调用setState()更新状态会导致递归更新;必须放在一个if条件中;
上一次:prevProps;当前:this.props
3、卸载时
componentWillUnmount:组件卸载时,作用:执行清理工作(比如:清理定时器等)
Render-props模式
把prop是一个函数并且告诉组件要渲染什么内容的技术
高阶组件
是一个函数,接收要包装的组件,返回增强后的组件
高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑代码,通过prop将复用的状态传递给被包装组件WrappedComponent
使用步骤
1、创建一个函数,名称约定以with开头
2、指定函数参数,参数应该以大写字母开头(作为要渲染的组件)
3、在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
4、在该组件中,渲染参数组件,同时将状态通过Prop传递给参数组件
5、调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面。
设置displayName
便于调试时区分不同的组件
传递props
将state和this.props一起传递给组件就行
setState()的说明
1、更新数据
setState()是异步更新数据的
注意:使用该语法时,后面的setState()不要依赖前面的setState()
可以多次调用setState(),只会触发一次重新渲染
2、推荐语法
使用setState((state, props) => {})语法
3、第二个参数
在状态更新(页面完成重新渲染)后立即执行某个操作
语法:setState(updater, callback)
JSX语法的转化过程
JSX仅仅时createElement()方法的语法糖
JSX语法被@bable/preset-react插件编译为createElement()方法
createElement()会转成React元素(对象)
组件更新机制与性能优化
组件更新机制
setState()的两个作用:1、修改state 2、更新组件(UI)
过程:父组件重新渲染时,也会重新渲染子组件,但只会渲染当前组件子树(当前组件及其所有子组件)
组件性能优化
1、减轻state
只存储跟组件渲染相关的数据(比如:count/列表数据/loding等)
注意:不用做渲染的数据不要放在state中,比如定时器、id等
对于这种需要在多个方法中用到的数据,应该放在this中
2、避免不必要的重新渲染
父组件更新会引起子组件也被更新,如果子组件没有任何变化时也会重新渲染
解决方案:使用钩子函数:shouldComponentUpdata()
作用:通过返回值决定组件是否重新渲染,返回true表示重新渲染,false表示不重新渲染
触发时机:更新阶段的钩子函数,组件重新渲染前执行(shouldComponentUpdate => render)
3、纯组件
PureComponent 与React.Component功能相似
区别:PureComponent内部自动实现了shouldComponentUpdate钩子,不需要手动区比较
原理:纯组件内部通过分别对比前后两次props和state的值,来决定是否重新渲染组件
纯组件内部的对比是shallow compare(浅层对比)
值类型:比较两个值是否相同
引用类型:只比较对象的引用(地址)是否相同
注意:state或者props中属性值为引用类型时,应该创建新数据,不要直接修改原数据
对象:{...list, num:2}
数组:用concat或者slice等返回新数组的方法或者解构
虚拟DOM和diff算法
虚拟DOM(React元素):本质上就是一个JS对象,用来描述你希望在屏幕上看到的内容(UI)
执行过程
1、初次渲染时,React会根据初始state(Model),创建一个虚拟DOM对象(树)
2、根据虚拟DOM生成真正的DOM,渲染到页面中
3、当数据变化后,重新根据新的数据,创建新的虚拟DOM对象
4、与上一次得到的虚拟DOM对象,使用Diff算法对比,得到需要更新的内容
5、最终,React只将变化的内容更新到DOM中,重新渲染到页面
组件render()调用后,根据状态和JSX结构生成虚拟DOM对象
路由的基本使用
使用步骤:
1、安装
Yarn add react-router-dom
cnpm install react-router-dom -D
2、导入路由的三个核心组件:Router/Route/Link
Import {BrowserRouter as Router, Route, Link} from ‘react-router-dom’
3、使用Router组件包裹整个应用
1、使用Link组件作为导航菜单(路由入口)
2、使用Route组件配置路由规则和要展示的组件(路由出口)
常用的组件说明
Router组件:包裹整个应用,一个React应用只需要使用一次
两种常用Route:HasshRouter和BrowserRouter
HashRouter:使用URL的哈希值实现
BrowserRouter:使用H5的historyAPI实现
Link组件:用于指定导航链接(a标签) to属性:location.pathname
Route组件:指定路由展示组件相关信息 path属性:路由规则 component:展示的组件
路由的执行过程
1、点击Link组件,修改了浏览器地址栏的Url
2、React路由监听到地址栏url的变化
3、React路由内部遍历所有Route组件,使用路由规则(path)与pathname进行匹配
4、当路由规则(path)能够匹配地址栏的pathname时,就展示该Route组件的内容
编程式导航
通过JS代码来实现页面跳转
This.props.history.push(‘/home’)
Push(path):跳转到某个页面,参数Path表示要跳转的路径
Go(n):前进或后退到某个页面,参数n表示前进或者后退页面数量
默认路由path设置为’/’
匹配模式
1、模糊匹配模式
只要Pathname以path开头都能匹配成功
2、精确匹配
给Route组件添加exact属性,让其变为精确匹配模式
精确匹配:只有当Path和pathname完全匹配才会展示该路由
HOOKS
React hooks:钩子
1、useState()
用于为函数组件引入状态(state).
纯函数不能有状态,所以把状态放在钩子里面
2、useContext()
组件之间的共享状态
使用React Context API 在组件外部建立一个Context
Const AppContext = React.createContext()
<AppContext.Provider value ={{username: ‘supersome’}}>
在子组件中就可以获取状态了
Const {username} = useContext(AppContext)
3、useReducer()
action钩子,用来引入reducer功能
基本用法: const [state, dispatch] = useReducer(reducer, initalState)
接受reducer函数和状态的初始值作为参数,返回一个数组
数组的第一个成员是状态的当前值,第二个成员是发送sction的dispatch函数
由于hooks可以提供共享状态和reducer函数,所以它在这些方面可以取代redux。但是,它没法提供中间件(middleware)和时间旅行(time travel),如果你需要这两个功能,还是要用redux
4、useEffect()
副作用钩子,用来引入具有副作用的操作,最常用的就是向服务器请求数据
用法:useEffect(()=>{
接受两个参数。第一个参数是一个函数,异步操作的代码放里面
第二个参数是一个数组,用于给出effect的依赖项,只要这个数组发生变化,useEffect()就会执行。
第二个参数可以省略,这时每次组件渲染时,就会执行useEffect()
5、useLocation()
redux
是一个用于javascript状态容器,提供可与预测化的状态管理
构建一致化的应用,运行于不同的环境(客户端、服务端、原生应用),益于测试大小只有2kb左右
三大核心
1、单一数据源
整个应用的state被存储在一颗object tree中,并且这个bject tree只存在于唯一一个store中
2、state是只读的
唯一改变state的方法就是触发action, action 是一个用于描述已发生事件的普通对象
这样确保了视图和网络请求都不能直接去修改state,相反,它们只能表达要修改的意图,因为所有的修改都被集中处理,并且严格按照一个接一个的顺序执行
3、使用纯函数来执行修改
为了描述action如何修改state tree,你需要去编写reducers
Reducers只是一些纯函数,它接收先前的state和action,并且返回新的state.
可以复用、可以控制顺序,传入附加参数
Redux的组成
1、state-状态
就是我们传递的数据,在开发项目时,大致可以分为三类
A、DomainDate:可以理解成为服务器端的数据,比如:获取用户的信息,商品的列表等
B、UI State:决定当前UI决定展示的状态,比如:弹窗的显示隐藏,受控组件等
C、App State:App级别的状态。比如:当前是否请求loading,当前路由信息等可能被多个组件去使用的到的状态
2、action-事件
action是把数据从应用传到store的载体,它是store数据的唯一来源,一般来说,我们可以通过store.dispatch()将action传递给store
action特点
action本质就是一个javascript的普通对象
Action对象内部必须要又以type属性来表示要执行的动作
多数情况下,这个type会被定义成字符串常量
除了type字段之外,action的结构随意进行定义
而我们在项目中,更多的喜欢用action创建函数(就是创建action的地方)
只是描述了有事件要发生,并没有描述如何去更新state
3、Reducer
reducer本质就是一个函数,它用来响应发送过来的actions,然后经过处理,把state发送给store的
注意:在reducer函数中,需要return返回值,这样store才能接收到数据
函数会接收两个参数,第一个参数是初始化state,第二个参数是action
4、Store
Store就是把action和reducer联系到一起的对象
主要职责:
维持应用的state
提供getState()方法获取state
提供dispatch()方法发送action
通过subscribe()来注册监听
通过subscribe()返回值来注销监听
安装redux
Npm i redux
理解redux:如果我们想要去发送一个action,需要导入store
Store.dispatch() --> reducer -- return ---> store
如果说哪个组件想到接受state
导入store
Store.subscribe() --> 注册监听才能拿到数据store.getState()
组件注销的时候 去掉我们的监听
React-redux
1、概述
React-redux是redux官方出的,用于配合react的绑定库
React-redux能够使你的react组件从redux store中很方便的读取数据,并且向store中分发actions以此来更新数据
两个重要成员
Provider(组件):这个组件能够使你整个app都能获取到store中的数据
Connect(方法):这个方法能够使组件跟store来进行关联
A、Provider
provider包裹在根组件最外层,使所有的子组件都可用拿到state
provider接收store作为props,然后通过context往下传递,这样react中任何组件都可以通过context获取到store
B、Connect
provider内部组件如果想要使用到state中的数据,就必须要connerct进行一层包裹封装,换一句话来说就是必须要被connect进行加强
Connect就是方便我们组件能够获取到store中的state