关于react@5.x 学习顺序及重点总结
1.开发环境搭建
npx create-react-app myapp
2.react中的组件
import React,{Component} from 'react'
import ReactDom from 'react-dom'
class App extends Component{
constructor(props) {
super(props)
}
render() {
return (<div>hello world</div>)
}
}
ReactDom.render(
<App/>,document.getElementById('root')
)
3.jsx语法
React.createElement('div',className:'title','hello world')
createElement方法有三个参数,第一个是要创建的标签,第二个参数是标签的属性,第三个参数是标签的内容
只是原生的写法,使用脚手架构建项目的话,可以简化第二个参数
使用ReactDOM中的render方法将元素渲染到根节点上
ReactDOM.render(a,document.querySelector('#root'))
4.react中的占位符
import React,{Component,Fragment} from 'react'
<Fragment></Fragment>可以代替render函数最外层包裹的标签,这样结构中就不会有多余的元素
5.响应式设计思想和事件绑定
constructor(props) {
super(props)
this.state = {
inputValue:'hello',
list:[]
}
}
<input value={this.state.inputValue} onChange={this.handleInputChange.bind(this)} </input>
<button onClick={this.handleClick.bind(this)}>提交</button>
handleInputChange(e) {
this.setState({
inputValue:e.target.value
})
}
handleClick() {
this.setState({
list:[...this.state.list,this.state.inputValue]
})
}
6.循环渲染
<ul>
{
this.state.list.map((item,index)=>(<li key={index}>{item}</li>)
)
}
</ul>
7.想要渲染出html标签的时候
{
this.state.list.map((item,index)=>(<li key={index} dangersousSetInnerHtml={{__html:item}}></li>)
)
}
8.组件之间的传值
父组件向子组件传值通过属性的形式传递
父组件传递
<TodoItem content={item}/>
子组件接收
class TodoItem extends Component {
render() {
return <div>{this.props.content}</div>
}
}
9.setState优化
this.setState(()=>({
list:[...this.state.list,this.state.inputValue],
inputValue:''
}))
10.PropTypes和DefaultProps
import PropTypes from 'prop-types'
TodoItem.propTypes = {
content:PropTypes.String,//父组件传递过来的content必须是字符串类型
deleteItem:PropTypes.func,
test:PropTypes.String.isRequired
}
更多用法,查看react文档的 Typechecking with propTypes
TodoItem.defaultTypes = { //给接收的属性设置默认值
test:'hello world'
}
11.props,State与render函数之间的关系
1.当组件的props或者State发生改变的时候,组件的render函数就会重新执行
2.当父组件的render函数被运行时,他的子组件的render都将被重新执行一遍
12.react中的虚拟dom以及虚拟dom中的diff算法
13.react中ref的使用
<input ref={(input)=>this.input=input}></input> this.input指向当前的dom节点
使用的时候只要使用this.input就能获取到当前元素
14.react中的生命周期函数
分为四个阶段
1.Initialization
setup props and state
2.Mounting
componentWillMount---->render----->componentDidMount
3.Updation
这里有两种情况,一种是props更新,一种是states更新
3.1 props更新 true向下执行
componentWillReceiveProps---->shouldComponentUpdate--->componentWillUpdate--->render---->componentDidUpdate
false不会在向下执行
state更新
shouldComponentUpdate---->componentWillUpdate---->render---->componentDidUpdate
组件被更新之前,shouldComponentUpdate会自动执行,他需要返回一个布尔值
shouldComponent(){
return true
}
componentDidUpdate() { //组件更新完成之后会被执行
}
componentWillReceiveProps() {
//当一个组件要从父组件接收参数
//只要父组件的render函数被(重新)执行了,子组件的这个生命周期函数会执行
//如果这个组件第一次存在父组件中不会执行
//如果这个组件之前已经存在于父组件中才会执行
}
4.Unmounting
compoentWillUnmount () {
//当这个组件即将从页面中删除的时候,会被执行
}
15.生命周期函数的使用场景
shouldComponentUpdate(nextProps,nextState) { //两个默认参数,nextProps和nextState,假设子组件中接受一个content的prop
if(nextProps.content!==this.props.content) {
return true
}else {
return false
}
}
componentDidMount() {
//ajax请求放在这里面
}
16.react中使用css动画效果
github中react-transtion-group项目
import {CSSTranstion} from 'react-transition-group'
<CSSTranstion
in={this.state.show}
timeout={1000}
classNames='fade'
unmountOnExit //当动画执行完成之后动画被移除
onEntered={(el)=>el.style.color='blue'} //当入场动画结束之后会自动执行的钩子
appear={true} //第一次展示hello时候也要动画效果 增加的类名是 .fade-active .fade-appear-active
>
<div>hello</div>
</CSSTranstion>
更多细节查看文档...
17.多个元素之间的动画效果
import {TransitionGroup} from 'react-transition-group'
<TransitionGroup>
{
this.list.map((item,index)=>{
return (
<CSSTranstion
in={this.state.show}
timeout={1000}
classNames='fade'
unmountOnExit //当动画执行完成之后动画被移除
onEntered={(el)=>el.style.color='blue'} //当入场动画结束之后会自动执行的钩子
appear={true} //第一次展示hello时候也要动画效果 增加的类名是 .fade-active .fade-appear-active
>
<div key={index}>{item}</div>
</CSSTranstion>
)
})
}
</TransitionGroup>
18.redux工作流程
19.创建redux中的store
1.store文件夹下的store.js
import {createStore} from 'redux'
import reducer from './reducer.js'
const strore = createStore(reducer)
export store
2.reducer.js
const defaultState = {
inputValue:'',
list:[]
}
export default = (state,action)=>{
if(action.type==='change_input_value') {
const newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
return newState
}
return state
}
3.组件中取值
constructor(props) {
super(props)
this.state = store.getState()
store.subscribe(this.handleStoreChange)
}
4.重新渲染页面
handleStoreChange() {
this.setState(store.getState())
}
20.将变量统一放到constants.js文件中,这样有这方面的错误可以直接报错
21.所有的action都是用actionCreator来创建,方便测试和管理
22.UI组件又叫傻瓜组件,他只负责页面的展示,没有逻辑代码
23.无状态组件
//当我们的组件中只有一个render函数的时候,我们就可以使用无状态组件来定义这个组件
const TodoList = (props)=>{
return (
<div>hello world</div>
)
}
无状态组件的性能比较高,他就是一个函数,无状态组件要执行的函数只有render函数
24.redux中常用的两个中间件
中间件指的是redux的中间件,而不是react的
1.redux-thunk
在github中搜索redux-thunk项目
//关键代码:
import {createStore,applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
const store = createStore(reducer,applyMiddleware(thunk))
如果需要使用调试工具的话,需要查看调试工具的文档,进行配置
使用了redux-thunk之后,可以使actionCreators中创建的action是一个函数,也就是里面的函数的返回值可以是一个函数
export const getTodoList = ()=>{
return (dispatch)=>{
axios.get('/api/getlist.json').then((res)=>{
const data = res.data
const action = initListAction(data)
dispatch(action)
})
}
}
2.redux-saga中间件
在github中搜索redux-saga项目
//关键代码:
import {createStore,applyMiddleware} from 'redux'
import mySaga from './sagas.js' //自己创建的saga文件
import createSageMiddleware from 'redux-saga'
const store = createStore(reducer,applyMiddleware(createSageMiddleware))
sagaMiddleware.run(mySaga)
//配置过之后,saga.js文件中也能接受到action
//saga.js文件中的内容
import {takeEvery} from 'redux-saga/effects'
import {GET_INIT_LIST} from './actionTypes'
import {initListAction} from './actionCreators'
function* getInitList() {
const res = yield axios.get('/api/getlist.json')
const action = initListAction(res.data)
yield put(action) //使用put方法将action传递给reducer
}
function * mySage() {
yield takeEvery(GET_INIT_LIST,getInitList) //第一个参数是action的类型 ,一旦执行到这个action,就执行后面的方法
}
export default mySage
在做大型项目的时候,redux-saga是优于redux-thunk的
25.第三方模块react-redux的使用
import {Provider} from 'react-redux'
import store from './store'
const App = (
<Provider store={store}>
<TodoList/>
</Provider>
)
ReactDOM.render(App,document.getElementById('root')) //Provider会将store提供给内部的所有组件
在组件的文件中
import {connect} from 'react-redux'
const mapState = (state)=>({
inputValue:state.inputValue
})
const mapDispatch = (dispatch)=>({
changeInputValue() {
}
})
export default connect(mapState,mapDispatch)(TodoList)
26.styled-components使用
//在gitub上搜索styled-components项目
使用reset.css对项目的css样式进行初始化 官网:https://meyerweb.com/eric/tools/css/reset/
代码示范:
组件文件中:
import {HeaderWrapper} from './style.js'
class Header extends Component {
render() {
return (<HeaderWrapper>hello</HeaderWrapper>)
}
}
style.js文件中
export const HeaderWrapper = styled.div`
width:100px;
height:200px;
`
27.使用combineReducers完成对数据的拆分管理
全局的store文件夹下 import {combineReducers} from 'redux' import {reducer as headerReducer} from '../common/header/store' const reducer = ()=>({ header:headerReducer }) export default reducer
28.使用immutable统一数据格式
reducer.js文件中
import {fromJS} from 'immutable'
const defaultState = fromJS({
focused:true
})
将import {combineReducers} from 'redux'改为 import {combineReducers} from 'redux-immutable'
const mapState = (state)=>({
focused:state.get('header').get('focused')
//也可以写成下面这种
focused:state.getIn(['header','focused'])
})
29.reducer文件中的if语句可以改写成switch语句来提升性能
import * as constants from './constants.js'
export default(state=defaultState,action)=>{
switch(action.type) {
case constants.SEARCH_FOCUS :
return state.set('focused',true);
case constants.CHANGE_LIST :
return state.merge({ //merge的性能更高,连续调用set会生成多个immutable对象
list:action.data,
totalPage:action.totalPage
})
default:
return state
}
}
30.react@5.0.0下的路由的使用方法
import {BrowserHistory,Route} from 'react-router-dom'
class App extends Component {
render() {
<BrowserHistory>
<Route path='/' exact component={<Home/>}></Route>
<Route path='/detail' exact component={<detail/>}></Route>
</BrowserHistory>
}
}
31.性能优化
将import React,{Component} from 'react' 改为import React,{PureComponent} from 'react' PureComponent在底层实现了一个shouldComponentUpdate,只有当当前组件的props或者state改变时他才会重新执行render函数,当只有他的父组件改变时,不执行当前子组件的render函数 不过使用PureComponent就使用immutable来管理数据,不然可能会有坑
32.页面跳转
import {Link} from 'react-router-dom'
{
list.map((item,index)=>{
return(
<Link key={index} to={'detail' + item.get('id')}>
<ListItem>
<img src={item.get(imgUrl)}/>
<ListInfo>
<h3 className='title'>{item.get('title')}</h3>
<p>{item.get('desc')}</p>
</ListInfo>
</ListItem>
</Link>
)
})
}
<Route path='/detail/:id' exact component={<detail/>}></Route>
这个参数获取的方式是: const id = this.props.match.params.id
<Route path='/detail' exact component={<detail/>}></Route>
这个参数获取的方式是: const res = this.props.location.search // '?id=2'
33.react中的异步组件
需要使用一个react-loadable的包
yarn add react-loadable
在store文件夹下创建loadable.js
import React,{Component} from 'react'
import {Loadable} from 'react-loadable'
const LoadableComponent = Loadable({
loader:()=>import('./reducer.js'),
loading() { //在加载伟完成时页面显示的临时内容
return (
<div>正在加载...</div>
)
}
})
导出一个组件,有下面两种写法
export default class App extends Component {
render() {
return (<LoadableComponent/>)
}
}
也可以象下面这样,导出一个无状态组件
export default ()=><LoadableComponent/>
34.如果要设置元素上的属性的时候
export const NavSearch = styled.input.attrs({
placeHolder:'搜索'
})`
width:200px;
height:30px;
`
35.如果reducer的内容较多时,可以对其进行拆分,方便管理
const changeHomeData=(state,action)=> {
return state.merge({
topicList: fromJS(action.topicList),
articleList: fromJS(action.articleList),
recommandList: fromJS(action.recommandList)
});
}
const addArticleData = (state,action) => {
return state.merge({
'articleList': state.get('articleList').concat(action.articleList),
'page':action.nextPage
})
}
export default (state = defaultState, action) => {
switch (action.type) {
case constants.CAHNGE_HOME_DATA:
return changeHomeData(state,action)
case constants.ADD_ARTICLE_DATA:
return addArticleData(state,action)
case constants.CAHNGE_SCROLL_TOP:
return state.set('showScroll',action.show)
default:
return state
}
}
36.登录鉴权
import {Redirect} from 'react-router-dom'
class Login extends PureComponent {
render() {
const { toLogin,loginStatus } = this.props
{
if (!loginStatus) { //控制登录状态的变量
return (
<LoginWrapper>
<LoginBox>
<Input placeholder='账号' ref={(input)=>{this.account=input} }/>
<Input placeholder='密码' type='password' ref={(input)=>{this.password=input} }/>
<Button onClick={()=>{toLogin(this.account,this.password)}}>登录</Button>
</LoginBox>
</LoginWrapper>
)
} else {
return (
<Redirect to='/'/>
)
}
}
}
}