2.React 初深
React 开发环境准备
-
npm install -g create-react-app
-
create-react-app my-app
-
cd my-app
-
npm run start
工程目录文件简介
- App.js
import React,{Component} from "react";
// import {Component} from 'react'
// // 等价于
// import React from "react";
// const Component=React.Component
class App extends Component{
render(){
return(
<div>
hell world
</div>
)
}
}
export default App;
- index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />,document.getElementById('root'));
React 中最基础的 JSX 语法
- JSX 语法中,如果我们要使用自己创建的组件,组件开头必须是大写开头
class App extends Component{
render(){
//JSX 语法:不需要加'<div>xx</div>' 引号
return <div>hello world</div>;
}
}
3. React 基础精讲
使用 React 编写 TodoList 功能
- src-TodoList.js
import React,{Component,Fragment} from "react";
class TodoList extends Component{
render(){
return(
//在 JSX 语法中要求一个组件 render 函数返回的内容,外层必须有个包裹元素
//最外层必须整体包含在一个大的元素中 Fragment:占位符
<Fragment>
<div><input/><button>提交</button> </div>
<ul>
<li>学英语</li>
<li>Learn English</li>
</ul>
</Fragment>
)
}
}
export default TodoList;
React 中的响应式设计思想和事件绑定
- src-TodoList.js
import React,{Component,Fragment} from "react";
class TodoList extends Component{
constructor(props){
super(props);
this.state={
inputValue:'',
list:[]
}
}
render(){
return(
<Fragment>
<div>
<input
value={this.state.inputValue}
onChange={this.handleInputChange.bind(this)}
/>
<button>提交</button>
</div>
<ul>
<li>学英语</li>
<li>Learn English</li>
</ul>
</Fragment>
)
}
handleInputChange(e){
//this 指向有问题 undefined ↑↑↑ .bind(this)
this.setState({
inputValue:e.target.value
})
}
}
export default TodoList;
实现 TodoList 新增删除功能
<ul>
{
this.state.list.map((item,index)=>{
return <li
key={index}
onClick={this.handleItemDelete.bind(this,index)}
>{item}
</li>
})
}
</ul>
=============================
//新增
handleBthClick(){
this.setState({
list:[...this.state.list,this.state.inputValue],
inputValue:''
})
}
//删除
handleItemDelete(index){
//immutable: state 不允许我们做任何的改变××[this.state.list.splice(index,1)]××
const list=[...this.state.list];
list.splice(index,1);
this.setState({
list:list
})
}
JSX 语法细节补充
- 1.注释写法:
- {/ 注释 /}
- 单行注释
{
//注释
}
- 2.编译 html 标签
<li
key={index}
onClick={this.handleItemDelete.bind(this,index)}
dangerouslySetInnerHTML={{__html:item}}
></li>
- 点输入内容,,光标会聚焦
<label htmlFor="insertArea">输入内容</label>
<input
id="insertArea"
/>
拆分组件与组件之间的传值
- 父组件向子组件传递内容,通过属性来传递
TodoList.js
<ul>
{
this.state.list.map((item,index)=>{
return (
<div>
<TodoItem content={item}/>
</div>
)
})
}
</ul>
TodoItem.js
import React,{Component} from "react";
class TodoItem extends Component{
render(){
return <div>{this.props.content}</div>
}
}
export default TodoItem;
- 子组件如何向父组件传递数据 [删除]
TodoList.js
<TodoItem
content={item}
index={index}
deleteItem={this.handleItemDelete.bind(this)}
/>
TodoItem.js
import React,{Component} from "react";
class TodoItem extends Component{
//组件创建的时候,第一个执行的构造函数
constructor(props){
super(props);
this.handleClick=this.handleClick.bind(this);
}
render(){
return (
<div onClick={this.handleClick}>
{this.props.content}
</div>
)
}
handleClick(){
this.props.deleteItem(this.props.index)
}
}
export default TodoItem;
TodoList 代码优化
TodoItem.js
handleClick(){
const {deleteItem,index} =this.props;
deleteItem(index)
// this.props.deleteItem(this.props.index)
}
- 引入样式在组件后面
- this.handleBthClick=this.handleBthClick.bind(this)
- 异步
handleInputChange(e){
const value=e.target.value
this.setState(()=>({
inputValue:value
}))
}
//新增
handleBthClick(){
this.setState((prevState)=>({
list:[...prevState.list,prevState.inputValue],
inputValue:''
}))
}
//删除
handleItemDelete(index){
this.setState((prevState)=>{
const list=[...prevState.list]
list.splice(index,1);
return {list}
});
}
围绕 React 衍生出的思考
- 声名式开发
- 可以与其他框架并存
- 组件化开发
- 单向数据流
- 视图层框架
4.React 高级内容
Reactdevelopertools 安装及使用
- git clone -b v3 https://github.com/facebook/react-devtools
- npm install
- npm run build:extension:chrome
- 步骤:更多工作-> 扩展程序-> 开启开发者模式-> 点击加载已解压的扩展程序
- 然后选择存放插件的文件:依次选择 react-devtools->shells->chrome->build->unpacked
- 注:安装插件后刷新页面图标就会正常亮起
PropTypes 与 DefaultProps 的应用
import PropTypes from 'prop-types';
render(){
const {content,test} =this.props;
return (
<div onClick={this.handleClick}>
{test}-{content}
</div>
)
}
TodoItem.propTypes={
test:PropTypes.string.isRequired,
//arrayOf : 可以提供一个或者的语法
content:PropTypes.arrayOf(PropTypes.number,PropTypes.string),
deleteItem:PropTypes.func,
//number->string : 限制要求父组件给我这个子组件传值的时候传值类型是什么类型的
index:PropTypes.number
}
TodoItem.defaultProps={
test:'hello world'
}
props,state 与 render 函数的关系
Test.js
import React,{Component} from "react";
class Test extends Component{
//当父组件的 render 函数被运行时,它的子组件的 render 都将被重新运行一次
render(){
console.log('Test render');
return <div>{this.props.content}</div>
}
}
export default Test;
TodoList.js:
constructor(props){
super(props);
//当组件的 state 或者 props 发生改变的时候,render 函数就会重新执行
React 中的虚拟 DOM
-
什么是虚拟 DOM?
-
(1)
1.state 数据
2.JSX 模板
3.数据+模板 结合,生成真实的 DOM,来显示
4.state 发生改变
5.数据+模板结合,生成真实的 DOM,替换原始的 DOM缺陷:
第一次生成了一个完整的 DOM 片段
第二次生成了一个完整的 DOM 片段
第二次的 DOM 替换第一次的 DOM,非常耗性能 -
(2)
1.state 数据
2.JSX 模板
3.数据+模板 结合,生成真实的 DOM,来显示
4.state 发生改变
5.数据+模板结合,生成真实的 DOM,并不直接替换原始的 DOM
6.新的 DOM (DocumentFragment) 和原始的 DOM 做比对,找差异
7.找出 input 框发生了变化
8.只用新的 DOM 中的 input 元素,替换掉老的 DOM 中的 input 元素
缺陷:
性能的提示并不明显 -
(3)
1.state 数据
2.JSX 模板
3.数据+模板 生成虚拟 DOM (虚拟 DOM 就是一个 JS 对象,用它来描述真实 DOM) (损耗了性能)
['div' , {id: 'abc' }, ['span ' , { }, 'hello world']]
4. 用虚拟 DOM 的结构生成真实的 DOM,来显示
<div id='abc'><span>hello world</span>/div>
5.state 发生变化
6.数据 + 模板 生成新的虚拟 DOM (极大的提升了性能)
['div' , {id: 'abc' }, ['span' , { }, 'bye bye']]
7. 比较原始虚拟 DOM 和新的虚拟 DOM 的区别,找到区别是 span 中内容 (极大的提升了性能)
8.直接操作 DOM, 改变 span 中的内容
- 面试,虚拟 DOM 是什么
本质上就是 JS 对象,之所以能提高性能本质上是业务什么呢? js 对象不怎么耗性能,比较真实的 DOM 会很耗性能
深入了解虚拟 DOM
优点:
1.性能提升了
2.它使得跨端应用得以实现 React Native
虚拟 DOM 中的 Diff 算法
- setState 是异步的, 本质上是 提升 react 底层的性能
- Diff 算法 :
- 算法简单所带来的好处 : 比对速度非常快,可能会造成 dom 重新渲染的浪费,大大的减少两个虚拟 dom 之间比对的算法上的性能消耗,所以react采用了一个同层的虚拟 dom 的比对算法
React 中 ref 的使用
- 尽量不要操作 DOM
render(){
return(
<Fragment>
<div>
<label htmlFor="insertArea">输入内容</label>
<input
id="insertArea"
className="input"
value={this.state.inputValue}
onChange={this.handleInputChange}
ref={(input)=>{this.input=input}}
/>
<button onClick={this.handleBthClick}>提交</button>
</div>
<ul ref={(ul)=>{this.ul=ul}}>
{this.getTodoItem()}
</ul>
</Fragment>
)
}
// 获取 inputvalue 值
handleInputChange(){
const value=this.input.value
this.setState(()=>({
inputValue:value
}));
}
//新增
handleBthClick(){
//setState : 异步函数,不会立即被执行
this.setState((prevState)=>({
list:[...prevState.list,prevState.inputValue],
inputValue:''
}));
//console.log 比 setState 底层运行之前先执行
console.log(this.ul.querySelectorAll('div').length)
}
- 有时候如果 setState 是一个异步函数,就没办法正确输出数据变更后页面 DOM 的情况,,[就不太合理]
- 一旦你希望 setState 执行成功后,页面更新之后去获取页面的 DOM,,正确的做法是 :
- 当函数执行的时候,页面已经被完整的更新完了,,获取页面的 DOM,是没错的
//新增
handleBthClick(){
//setState : 异步函数,不会立即被执行
this.setState((prevState)=>({
list:[...prevState.list,prevState.inputValue],
inputValue:''
}),()=>{
console.log(this.ul.querySelectorAll('div').length)
});
}
- ref : 帮助我们在 react 直接获取 DOM 元素的时候来使用的,,一般情况下,尽量不要用 ref, 有的时候在做机器复杂的业务,比如说一些动画的时候,不可避免还是要用到 ref 中的 DOM 标签,,用 ref 来获取
React 的生命周期函数
- 生命周期函数指在某一个时刻组件会自动调用执行的函数
//在组件即将被挂载到页面的时候自动执行
componentWillMount(){
console.log('componentWillMount');
}
render(){}
//组件被挂载到页面之后,自动被执行
componentDidMount(){
console.log('componentDidMount');
}
//组件被更新之前,他会自动被执行
shouldComponentUpdate(){
console.log('shouldComponentUpdate');
return true
}
//组件被更新之前,他会自动被执行,但是他在 shouldComponentUpdate 之后被执行
//如果 shouldComponentUpdate 返回 true,它才执行;返回 false,这个函数就不会被执行了
componentWillUpdate(){
console.log('componentWillUpdate');
}
//组件更新完成之后,它会被执行
componentDidUpdate(){
console.log('componentDidUpdate');
}
- TodoItem.js
// 一个组件要从父组件接受参数
// 如果这个组件第一次存在于父组件中,不会执行
// 如果这个组件之前已经存在于父组件中,才会执行
componentWillReceiveProps(){
console.log('child componentWillReceiveProps');
}
// 当这个组件即将被从页面中剔除的时候,会被执行
componentWillUnmount(){
console.log('child componentWillUnmount');
}
React 生命周期函数的使用场景
- 降低性能:
- this.handleClick=this.handleClick.bind(this);
- react 的底层 setState 是一个异步函数, 可以把多次数据改变,结合成一次来做,,降低虚拟 DOM 的比对频率
- 可以避免无谓的组件的 render 函数的运行
TodoItem.js shouldComponentUpdate(nextProps,nextState){
if(nextProps.content!==this.props.content){
return true;
}else{
return false;
}
}
- npm i axios
TodoList.js
import axios from "axios";
render(){}
//只执行一次: 在组件被挂载到页面的时候,会被执行一次,之后就不会被重新重复执行了
// 将 ajax 请求 就放到这里,永远不会有问题
componentDidMount(){
axios.get('/api/todolist')
.then(()=>{
alert('succ')
}).catch(()=>{
alert('error')
})
}
使用 Charles 进行接口数据模拟
- 下载 Charles : https://www.charlesproxy.com/download
- localhost - > localhost.charlesproxy.com
- Tools -> Map Local Settings -> add 地址
TodoList.js
componentDidMount(){
axios.get('/api/todolist')
.then((res)=>{
this.setState(()=>({
list:[...res.data]
}))
}).catch(()=>{
alert('error')
})
}
React 的 CSS 过渡动画
App.js
import React, { Component, Fragment } from "react";
import './style.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
show: true
}
this.handleToggole = this.handleToggole.bind(this)
}
render () {
return (
<Fragment>
<div className={this.state.show ? 'show' : 'hide'}>hello</div>
<button onClick={this.handleToggole}>toggole</button>
</Fragment>
)
}
handleToggole () {
this.setState({
show: this.state.show ? false : true
})
}
}
export default App;
style.css
.show {
opacity: 1;
transition: all 1s ease-in;
}
.hide {
opacity: 0;
transition: all 1s ease-in;
}
React 中使用 CSS 动画效果
.hide {
/* forwards : 动画结束之后,保存动画最后一帧的 CSS 样式 */
animation: hide-item 2s ease-in forwards;
}
@keyframes hide-item {
0% {
opacity: 1;
color: red;
}
50% {
opacity: 0.5;
color: green;
}
100% {
opacity: 0;
color: blue;
}
}
- 渐变动画
使用 react-transition-group 实现动画(1)
import React, { Component, Fragment } from "react";
import { CSSTransition } from 'react-transition-group';
import './style.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
show: true
}
this.handleToggole = this.handleToggole.bind(this)
}
render () {
return (
<Fragment>
<CSSTransition
in={this.state.show}
timeout={1000}
classNames='fade'
unmountOnExit
onEntered={(el)=>{el.style.color='blue'}}
appear={true}
>
<div>hello</div>
</CSSTransition>
<button onClick={this.handleToggole}>toggole</button>
</Fragment>
)
}
handleToggole () {
this.setState({
show: this.state.show ? false : true
})
}
}
export default App;
- style.css
.fade-enter , .fade-appear{
opacity: 0;
}
.fade-enter-active, .fade-appear-active {
opacity: 1;
transition: opacity 1s ease-in;
}
.fade-enter-done {
opacity: 1;
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: opacity 1s ease-in;
}
.fade-exit-done {
opacity: 0;
}
使用 react-transition-group 的使用(2)
- 多个 dom 元素的动画
import React, { Component, Fragment } from "react";
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import './style.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
list: []
}
this.handleAddItem = this.handleAddItem.bind(this)
}
render () {
return (
<Fragment>
<TransitionGroup>
{
this.state.list.map((item, index) => {
return (
<CSSTransition
timeout={1000}
classNames='fade'
unmountOnExit
onEntered={(el) => { el.style.color = 'blue' }}
appear={true}
key={index}
>
<div>{item}</div>
</CSSTransition>
)
})
}
</TransitionGroup>
<button onClick={this.handleAddItem}>toggole</button>
</Fragment>
)
}
handleAddItem () {
this.setState((prevState) => {
return {
list: [...prevState.list, 'item']
}
})
}
}
export default App;
5.Redux 入门
Redux 概念简述
- Redux = Reducer + Flux
Redux 的工作流程
使用 Antd 实现 TodoList 页面布局
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button,List } from 'antd';
const data = [
'Racing car sprays burning fuel into crowd.',
'Japanese princess to wed commoner.',
'Australian walks 100km after outback crash.',
'Man charged over missing wedding girl.',
'Los Angeles battles huge wildfires.',
];
class TodoList extends Component {
render () {
return (
<div style={{marginTop:'10px',marginLeft:'10px'}}>
<div>
<Input placeholder='todo info' style={{width:'300px',marginRight:'10px'}}/>
<Button type="primary">提交</Button>
</div>
<List
style={{marginTop:'10px',width:'300px'}}
bordered
dataSource={data}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
)
}
}
export default TodoList;
5-4 创建 redux 中的 store_x264
-
npm i redux
-
src -> store -> index.js
import { createStore } from "redux";
// 笔记本 传给 store 管理员
import reducer from "./reducer";
const store=createStore(reducer);
export default store;
- src -> store -> reducer.js
const defaultState={
inputValue:'',
list:[]
}
// reducer 是笔记本
// state:存放的是整个图书馆所有书籍的信息
// eslint-disable-next-line import/no-anonymous-default-export
export default (state = defaultState,action)=>{
return state;
}
- TodoList.js
报错
Failed to parse source map: 'webpack://antd/./components/time-picker/style/i
import 'antd/dist/antd.css'; -> import 'antd/dist/antd.min.css';
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button,List } from 'antd';
// 组件的数据从 store 中取
import store from './store/index';
class TodoList extends Component {
constructor(props){
super(props);
this.state=store.getState();
}
render () {
return (
<div style={{marginTop:'10px',marginLeft:'10px'}}>
<div>
<Input value={this.state.inputValue} placeholder='todo info' style={{width:'300px',marginRight:'10px'}}/>
<Button type="primary">提交</Button>
</div>
<List
style={{marginTop:'10px',width:'300px'}}
bordered
dataSource={this.state.list}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
)
}
}
export default TodoList;
5-5Action 和 Reducer 的编写
-
redux devtools 无法进行调试,加载出数据
解决: https://blog.csdn.net/qq_42419603/article/details/120907216 -
报错 :
react-dom.development.js:86 Warning: ReactDOM.render is no longer supported- 解决方法:
npm install react@17.0.2 react-dom@17.0.2 --save
- 解决方法:
- src -> store -> index.js
import { createStore } from "redux";
// 笔记本 传给 store 管理员
import reducer from "./reducer";
// 引入 redux-devtools 需要用到的依赖包
import { composeWithDevTools } from 'redux-devtools-extension'
const store=createStore(reducer,composeWithDevTools());
export default store;
- src -> store -> reducer.js
const defaultState = {
inputValue: '123',
list: [1, 2]
}
// reducer 是笔记本
// state:存放的是整个图书馆所有书籍的信息
//reducer 可以接收 state,但是绝不能修改 state
// eslint-disable-next-line import/no-anonymous-default-export
export default (state = defaultState, action) => {
//结合之前的数据和当前用户的操作告诉 store,现在新的数据应该变成什么样
if (action.type === 'change_input_value') {
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
if (action.type==='add_todo_item'){
const newState = JSON.parse(JSON.stringify(state));
newState.list.push(newState.inputValue)
newState.inputValue=''
return newState;
}
console.log(state, action);
return state;
}
- TodoList.js
import React, {Component} from 'react';
import 'antd/dist/antd.min.css';
import {Button, Input, List} from 'antd';
// 组件的数据从 store 中取
import store from './store/index.js';
class TodoList extends Component {
constructor(props) {
super(props);
this.state = store.getState();
this.handleInputChange = this.handleInputChange.bind(this)
this.handleStoreChange = this.handleStoreChange.bind(this)
this.handleBtnClick = this.handleBtnClick.bind(this)
store.subscribe(this.handleStoreChange);
}
render() {
return (
<div style={{marginTop: '10px', marginLeft: '10px'}}>
<div>
<Input value={this.state.inputValue} placeholder='todo info'
style={{width: '300px', marginRight: '10px'}}
onChange={this.handleInputChange}
/>
<Button type="primary" onClick={this.handleBtnClick}>提交</Button>
</div>
<List
style={{marginTop: '10px', width: '300px'}}
bordered
dataSource={this.state.list}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
)
}
handleInputChange(e) {
const action = {
type: 'change_input_value',
value: e.target.value
}
store.dispatch(action)
}
handleStoreChange() {
this.setState(store.getState());
}
handleBtnClick() {
const aciton = {
type: 'add_todo_item'
};
store.dispatch(aciton);
}
}
export default TodoList;
5-6 使用 Redux 完成 TodoList 删除功能
handleItemDelete(index){
const action={
type:'delete_todo_item',
index
}
store.dispatch(action);
}
5-7ActionTypes 的拆分
- 防止一些拼写问题,,常量和变量系统会报错,,有利于找错
- store -> actionTypes.js
export const CHANGE_INPUT_VALUE = 'change_input_value'
export const ADD_TODO_ITEM = 'add_todo_item'
export const DELETE_TODO_ITEM = 'delete_todo_item'
import {CHANGE_INPUT_VALUE,ADD_TODO_ITEM,DELETE_TODO_ITEM } from './actionTypes'
5-8 使用 actionCreator 统一创建 action
-
利于管理和测试,,前端有自动化可测试工具, ,利于代码的可维护性
-
actionCreator.js
import {ADD_TODO_ITEM, CHANGE_INPUT_VALUE, DELETE_TODO_ITEM} from "./actionTypes";
export const getInputChangeAction=(value)=>({
type:CHANGE_INPUT_VALUE,
value
})
export const getAddItemAction=()=>({
type:ADD_TODO_ITEM,
})
export const getDeleteItemAction=(index)=>({
type:DELETE_TODO_ITEM,
index
})
- Todolist.js
handleInputChange(e) {
const action=getInputChangeAction(e.target.value)
store.dispatch(action)
}
5-9Redux 知识点复习补充
-
利于管理和测试,,前端有自动化可测试工具, ,利于代码的可维护性
-
actionCreator.js
import {ADD_TODO_ITEM, CHANGE_INPUT_VALUE, DELETE_TODO_ITEM} from "./actionTypes";
export const getInputChangeAction=(value)=>({
type:CHANGE_INPUT_VALUE,
value
})
export const getAddItemAction=()=>({
type:ADD_TODO_ITEM,
})
export const getDeleteItemAction=(index)=>({
type:DELETE_TODO_ITEM,
index
})
- Todolist.js
handleInputChange(e) {
const action=getInputChangeAction(e.target.value)
store.dispatch(action)
}
5-9Redux 知识点复习补充
- store 是唯一的
- 只有 store 能够改变自己的内容
-
Reducer 必须是纯函数
- 纯函数 : 给定固定的输入,就一定会有固定的输出,而且不会有任何副作用
- 注:
①一旦有 setTimeout,日期相关内容,ajax 请求存在的,不再是纯函数,不能有异步操作,不能有跟时间相关的操作
②这就是有副作用的代码,,对接收的参数做了修改,不是纯函数了
Redux 的核心 API
- createStore : 帮助我们创建 store
- store.dispatch : 帮助我们派发 action,这个 action 会传递给 store
- store.getState : 帮助我们获取到 store 中所有的数据内容
- store.subscribe : 帮助我们订阅 store 的改变,一旦改变,这接收的回调函数就会被执行
6.Redux 进阶
6-01UI 组件(傻瓜组件)和容器组件
- TodoListUI.js 页面渲染
- 容器组件 TodoList.js 业务逻辑代码,负责功能实现
Redux 中发送异步请求获取数据
- 在 charles->tools->Map Local...->Add->
-
桌面新建 list.json
["hello", "dell", "lee"] -
actionCreators.js
export const initListAction=(data)=>({
type:INIT_LIST_ACTION,
data
})
- actionTypes.js
export const INIT_LIST_ACTION = 'init_list_action'
- Todolist.js
import axios from 'axios'
componentDidMount() {
axios.get('/list.json').then((res) => {
const data=res.data;
const action=initListAction(data);
store.dispatch(action)
})
}
使用 Redux-thunk 中间件进行 ajax 请求发送
-
报错: npm install 报 ENOENT: no such file or directory, open
- npm init -y
- npm install
-
src/store/index.js [安装使用 redux-thunk 和↓] https://github.com/zalmoxisus/redux-devtools-extension
-
src/store/actionCreators.js
export const getTodolist = () => {
return (dispatch) => {//return 一个函数,可以做异步操作
axios.get('/list.json').then((res) => {
const data = res.data;
const action = initListAction(data);
dispatch(action)
})
}
}
- src/TodoList.js
//建议将异步函数和复杂逻辑拆分到,借助 redux-thunk
componentDidMount() {
const action=getTodolist()//函数
//当你调用 store.dispatch,把 action 发给 store 的时候,action 就会被自动执行
store.dispatch(action);
}
什么是 Redux 的中间件
Redux-saga 中间件使用入门
如何使用 React-redux
7.项目实战: Header 组件开发
使用 styled-components 完成 Header 组件布局
- background-size: contain 与 cover 的区别:
- 在 no-repeat 情况下,如果容器宽高比与图片宽高比不同,
- cover:图片宽高比不变、铺满整个容器的宽高,而图片多出的部分则会被截掉;
- contain:图片自身的宽高比不变,缩放至图片自身能完全显示出来,所以容器会有留白区域;
- PS:其实,从英文的意思来说:cover 意味着“遮罩、遮盖”---此处理解为“塞满”较恰当,contain 意为“包含”--也就是:我图片虽然缩放了,但是整个图是被“包含”在你里面的,你必须把我显示完整、不能裁剪我一丝一毫~
- 在 repeat 情况下:cover:与上述相同;contain:容器内至少有一张完整的图,容器留白区域则平铺背景图,铺不下的再裁掉。
使用 React-Redux 进行应用数据的管理????????[数据绕]
ajax 获取推荐数据
- store/actionCreators.js
import {fromJS} from "immutable";
import axios from "axios";
const changeList=(data)=>({
type:constants.CHANGE_LIST,
data:fromJS(data)
})
export const getList=()=>{
return (dispatch)=>{
axios.get('/api/headerList.json').then((res)=>{
const data=res.data
dispatch(changeList(data.data))
}).catch(()=>{
console.log('error')
})
}
}
- store/reducer.js
import {fromJS} from "immutable";
const defaultState=fromJS({
list:[]
});
case constants.CHANGE_LIST:
return state.set('list', action.data)
- header/index.js
<SearchInfoList>
{
this.props.list.map((item) => {
return <SearchInfoItem key={item}>{item}</SearchInfoItem>
})
}
</SearchInfoList>
热门搜索换页功能实现
- reducer.js
const defaultState = fromJS({
page:1,
totalPage:1
});
case constants.CHANGE_LIST:
return state.merge({
list: action.data,
totalPage: action.totalPage
});
- actionCreators.js
const changeList=(data)=>({
totalPage:Math.ceil(data.length / 10)
})
避免无意义的请求发送,提升组件性能
- header/index.js
const {list} = this.props;
return(
<NavSearch className={focused ? 'focused' : ''}
onFocus={() => handleInputFocus(list)}
/>
)
handleInputFocus(list) {
(list.size ===0) && dispatch(actionCreators.getList())
dispatch(actionCreators.searchFocus());
},
8.项目实战: 首页开发
如何在 React 中使用路由功能
- App.js
import {BrowserRouter, Route} from "react-router-dom";
<Provider store={store}>
<div>
<Header/>
<BrowserRouter>
<div>
{/*exact 完全跟 path 相等的*/}
<Route path='/' exact render={() => <div>home</div>}/>
<Route path='/detail' exact render={() => <div>detail</div>}/>
</div>
</BrowserRouter>
</div>
</Provider>
异步操作代码拆分优化
- src/pages/home/store/constants.js
export const CHANGE_HOME_DATA='/home/CHANGE_HOME_DATA'
- src/pages/home/store/actionCreators.js
import axios from "axios";
import * as constants from './constants'
const changeHomeData=(result)=>({
type: constants.CHANGE_HOME_DATA,
topicList: result.topicList,
articleList: result.articleList,
recommendList: result.recommendList
})
export const getHomeInfo=()=>{
return (dispatch)=>{
axios.get("/api/home.json").then(res => {
const result = res.data.data;
dispatch(changeHomeData(result));
});
}
}
- src/pages/home/store/index.js
import reducer from "./reducer";
import * as actionCreators from './actionCreators'
export {reducer,actionCreators}
- src/pages/home/index.js
import {connect} from "react-redux";
import {actionCreators} from "./store";
class Home extends Component {
componentDidMount() {
this.props.changeHomeData();
}
}
const mapDispatch = (dispatch) => ({
changeHomeData() {
const action = actionCreators.getHomeInfo();
dispatch(action);
}
});
export default connect(null, mapDispatch)(Home);
实现加载更多功能
- src/pages/home/components/List.js
<LoadMore onClick={getMoreList}>更多文字</LoadMore>
const mapDispatch = (dispatch) => ({
getMoreList() {
dispatch(actionCreators.getMoreList());
}
});
export default connect(mapState, mapDispatch)(List);
- src/pages/home/store/actionCreators.js
import {fromJS} from "immutable";
const addHomeList=(list)=>({
type: constants.ADD_ARTICLE_LIST,
list:fromJS(list)//fromJS,会把外层的数组和数组内部对象都转换为 immutable 类型
})
export const getMoreList = () => {
return (dispatch) => {
axios.get("/api/homeList.json").then(res => {
const result = res.data.data;
console.log(result);
dispatch(addHomeList(result));
});
};
};
- src/pages/home/store/reducer.js
case constants.ADD_ARTICLE_LIST:
return state.set('articleList',state.get('articleList').concat(action.list))
加载更多,根据后端传值页码不同内容不同
- src/pages/home/components/List.js
<LoadMore onClick={()=>getMoreList(page)}>更多文字</LoadMore>
const mapState = (state) => ({
list: state.getIn(["home", "articleList"]),
page:state.getIn(["home","articlePage"])
});
const mapDispatch = (dispatch) => ({
getMoreList(page) {
dispatch(actionCreators.getMoreList(page));
}
});
- src/pages/home/store/actionCreators.js
const addHomeList=(list,nextPage)=>({
type: constants.ADD_ARTICLE_LIST,
list:fromJS(list),
nextPage
})
export const getMoreList = (page) => {
return (dispatch) => {
axios.get("/api/homeList.json?page="+page).then(res => {
const result = res.data.data;
dispatch(addHomeList(result,page+1));
});
};
};
- src/pages/home/store/reducer.js
const defaultState = fromJS({
articlePage:1
});
case constants.ADD_ARTICLE_LIST:
return state.merge({
'articleList':state.get('articleList').concat(action.list),
'articlePage':action.nextPage
})
首页性能优化
- 首页的每一个组件基本上每个都调用的 connect 方法和 store 作了连接,只要 store 发生了改变,那么每个组件都会被重现渲染,每个组件的 render 函数都会被重新执行
//判断只有和这组件相关的数据发生改变的时候,我才让组件的 render 函数执行,
// 否则就 return false,来避免虚拟 dom 的比对,来提高性能,在每个组件都写这个,太麻烦了
//PureComponent 纯组件,内在底层实现了一个 shouldComponentUpdate,所以不需要手写来做性能优化了
shouldComponentUpdate() { }
















