React笔记
- 创建项目
npx create-react-app demo
cd demo
npm start
- 入口文件
/index.js
import React from 'react'; // 这里要引入react之后,下面的jsx语法才能被识别
import ReactDOM from 'react-dom';
import TodoList from './TodoList';
ReactDOM.render(<TodoList />, document.getElementById('root'));
/public/index.html
挂载dom
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<title>todolist</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
- 组件构成
<!-- 引入 React, Component -->
import React, { PureComponent, Fragment } from 'react'
<!-- 引入组件 -->
import TodoItem from './TodoItem';
<!-- 引入样式 -->
import './style.css';
<!-- 组件 -->
class A extends PureComponent { // 等价于 class A extends React.Component
<!-- 组件初始化会首先加载constructor函数 -->
constructor (props) {
super(props);
this.state = {
inputVal: '',
list: []
}
this.handleInputChange = this.handleInputChange.bind(this)
this.handleBtnClick = this.handleBtnClick.bind(this)
this.handleItemDelete = this.handleItemDelete.bind(this)
}
<!-- 每次state更新都会触发render函数更新dom -->
render () {
return (
<!-- 这里书写jsx -->
// 同vue 最外层也只能是一个标签包裹,但是如果不想使用标签可以使用react自带的一个占位组件
<Fragment>
{/* 这是在jsx中的注释书写格式 */}
{/* jsx中对于label的for会识别为循环的for,所以要改成htmlFor */}
<label htmlFor="insert">输入内容</label>
<input
id="insert"
// 因为class和类的class冲突,所以属性使用className
className="input"
value={ this.state.inputVal }
// 所有的事件都要大些写首字母
onChange={ this.handleInputChange }
ref={(input) => {this.input = input}}
/>
<button onClick={ this.handleBtnClick }>提交</button>
<ul>
{ this.getTodoItem() }
</ul>
</Fragment>
)
}
<!-- 方法 -->
getTodoItem () {
return this.state.list.map((item, index) => {
return (
<TodoItem
key ={ index }
content={item} // 向子组件传参
index = {index}
handleDel = { this.handleItemDelete }></TodoItem> <!-- 向子组件传递方法,子组件调用该方法-->
)
})
}
<!-- 修改state中的数据 -->
// 点击添加
handleBtnClick (e) {
// prevState 等价于this.state
this.setState((prevState) => ({
list: [...prevState.list, prevState.inputVal],
inputVal: ''
}))
}
// 实现数据的双向绑定
handleInputChange (e) {
let value = e.target.value
<!-- 使用ref的时候 -->
let value = this.input.value
this.setState(() => ({ // 异步函数更改数据
inputVal: value
}))
}
}
<!-- 暴露 -->
export default A
- 子组件对父组件的校验等
import React, { Component } from 'react'
// 参数校验要单独引入
import PropTypes from 'prop-types'
class TodoItem extends Component {
constructor (props) {
super(props);
this.handleClick = this.handleClick.bind(this)
}
render(h) {
const { content, test } = this.props;
return (
<!-- 这样可以正确编译语句中的html标签 -->
<div onClick={this.handleClick} dangerouslySetInnerHTML={{__html:`${test} - ${content}`}}></div>
)
}
handleClick () {
const { handleDel, index } = this.props;
// 父组件传递函数的时候要将this绑定在父组件上,不然在子组件会找不到该方法
handleDel(index)
}
}
// 对父组件的传值进行校验, 和vue中的props中的类型啥都一样,注意这里小写
TodoItem.propTypes = {
test: PropTypes.string.isRequired, // 必传校验
content: PropTypes.oneOfType(PropTypes.string, PropTypes.number), // 多种类型
handleDel: PropTypes.func,
index: PropTypes.number
}
// 给父组件的传值设置默认值
TodoItem.defaultProps = {
test: 'hello'
}
export default TodoItem
- React的虚拟Dom实现原理
- state数据
- JSX模板
- 生成虚拟Dom(虚拟Dom就是一个JS对象,用它来描述真实的Dom)(损耗了性能)['div',{id: 'abc'}, ['span', {}, 'hello world']]
- 用虚拟DOM的结构生成真实的Dom,用来显示<div id="abc"><span>hello world</span></div>
- state发生变化
- 数据+模板生成新的虚拟Dom,(极大地提升了性能)['div',{id: 'abc'}, ['span', {}, 'niubi']]
- 比较原始虚拟DOM和新的虚拟DOM的区别,找到区别中的内容(极大地提升了性能)
- 直接操作DOM,改变span中的内容
虚拟Dom为JS对象,在js中比较非常方便,性能需求较小
JSX->createElement->虚拟Dom(JS对象)->真实的DOM
render () {
return (
<div><span>hello</span></div>
)
}
等同于
render () {
return React.createElement('div', {}, React.createElement('span', {}, 'hello))
}
- diff算法
- 同层比对,只要同层有不同,下面的全部重新渲染
- key值比对(为什么不要用index做key值的原因)
- 生命周期
生命周期函数: 在某一时刻组件会自动调用执行的函数
react生命周期.png
- initialization
setup props and state
- mounting
componentWillMount()
render()
componentDidMount()
- updation
<!-- 1. -->
/*
当一个组件要从父组件接受参数
只要父组件的render函数重新执行了,子组件的这个生命周期函数就会被执行
*/
componentWillReciveProps()
<!-- 2.组件被更新之前自动执行 -->
shouldComponentUpdate () // 需要返回一个波尔值,以确认组件是否需要更新,true的话接下来调用componentWillUpdate,在一些父组件更新子组件不需要更新的情况下,返回false,以提升性能,不会老师触发子组件的render
<!-- 3. 组件确认更新后,组件更新之前调用-->
componentWillUpdate ()
<!-- 4.render -->
render()
<!-- 5. 组件更新之后调用-->
componentDidUpdate ()
- unmounting
<!-- 组件即将被从页面中移除 的时候执行 -->
conponentWillUnmount()
因为组件继承自Component,所以其他的声明函数都内置了,都可以不写,render生命周期函数除外,所以render函数必须有
- 性能提升
- 在constructor中绑定this作用域
- setState改变数据使用了异步函数,可以将多次虚拟dom的改变转为1次,减少虚拟dom的比对
- 虚拟dom的比对 同层比对和key值比对
- 借助 shouldComponentUpdate 提升组件性能
- 动画
- 简单的过渡动画和动画都可以通过css实现
- 复杂的可以引入react-transition-group来实现,基本上和vue差不多
-
Redux
Redux工作流程.png
Redux = reducer + Flux
/**
- state是存储的原始的数据,action是接受到的方法
- 这个在组件中通过dispatch提交给store,store将自身数据和action再提交到reducer中
- reducer接受到store传来的数据和action,复制一份数据,然后根据action改变后返回给store,store改变
*/
yarn add redux
store.js
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() // redux谷歌插件显示
);
export default store;
reducer.js
reducer必须是纯函数:给定固定的输入就有固定的输出,并且没有副作用,例如里面有date,ajax请求或者异步就不是了
const defaultState = {
inputVal: 'no',
list: ['1','asd']
}
export default (state=defaultState, action) => {
let newState = JSON.parse(JSON.stringify(state)); // 对state进行深拷贝
switch (action.type) {
case 'input_change_val':
newState.inputVal = action.inputVal;
return newState;
case 'add':
newState.list.unshift(action.value)
newState.inputVal = ''
return newState;
default:
return state;
}
}
todilist.js
constructor(props) {
super(props)
this.handleStoreChange = this.handleStoreChange.bind(this)
this.hanldeAdd = this.hanldeAdd.bind(this)
/**
* 订阅store,store改变的时候执行该方法
*/
store.subscribe(this.handleStoreChange)
}
// 实现动态改变
handleInputChange(e) {
store.dispatch({
type: 'input_change_val',
inputVal: e.target.value
})
}
handleStoreChange () {
// 将store中改变的数据同步到state
this.setState(store.getState())
}
- Redux-thunk Redux-saga
可以使action的返回值为函数而不仅仅是对象的中间件
yarn add redux-thunk
- redux-thunk
react-redux 可以让组件在reducer中使用函数异步获取数据
npm i redeux-thunk -S
store/index.js
import { createStore, compose, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import reducer from './reducer'
// 调用redux-devtools
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const store = createStore(reducer, composeEnhancers(
applyMiddleware(thunk)
));
export default store;
.router.js
import React, { PureComponent } from 'react'
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'
import { Provider } from 'react-redux'
import store from './store'
import App from './App'
import Home from './pages/home/Home'
class IRouter extends PureComponent {
render () {
return (
<Provider store={store}>
<BrowserRouter>
<Switch>
<Route exact path="/" render={() => (
<Redirect to="/home"/>
)}/>
<Route path="/home" component={Home} />
<Route path="/app" component={App} />
</Switch>
</BrowserRouter>
</Provider>
)
}
}
export default IRouter
网友评论