React项目的基本流程&redux的用法原理

Posted yg123-lb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React项目的基本流程&redux的用法原理相关的知识,希望对你有一定的参考价值。

(青竹)

1.初始化项目

src文件夹中只需要留下App.js和index.js两个文件 其他的没有用 实际开发项目也是这样,注意删除这两个文件夹中的一些已经删除的文件之间的引用关系 否则会报错

 

2.样式引入

新建一个style.js文件 管理全局样式 并在index.js中引入

npm install styled-components@3.3.2 --save 全局样式引入 import {injectGlobal} from ‘styled-components‘; injectGlobal样式 然后在style.js文件初始化全局样式(网上任意一个即可)

 html, body, div, span, applet, object, iframe,
 h1,h2, h3, h4, h5, h6, p, blockquote, pre,
 a, abbr, acronym, address, big, cite, code,
 del, dfn, em, img, ins, kbd, q, s, samp,
 small, strike, strong, sub, sup, tt, var,
 b, u, i, center,
 dl, dt, dd, ol, ul, li,
 fieldset, form, label, legend,
 table, caption, tbody, tfoot, thead, tr, th, td,
 article, aside, canvas, details, embed,
 figure, figcaption, footer, header, hgroup,
 menu, nav, output, ruby, section, summary,
 time, mark, audio, video {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
 }
 /* HTML5 display-role reset for older browsers */
 article, aside, details, figcaption, figure,
 footer, header, hgroup, menu, nav, section {
  display: block;
 }
 body {
  line-height: 1;
 }
 ol, ul {
  list-style: none;
 }
 blockquote, q {
  quotes: none;
 }
 blockquote:before, blockquote:after,
 q:before, q:after {
  content: ‘‘;
  content: none;
 }
 table {
  border-collapse: collapse;
  border-spacing: 0;
  transition:1s;
 }
 /* a{text-decoration:none} */

各部分组件样式(headStyle.js)局部引入

 import styled from ‘styled-components‘;
 export const HomeWrap = styled.div``

 

3.组件

拆分大组件 可将页面基本拆分为head home foot 并分别在各文件夹下创建组件文件(注意组件名首字母大写)和组件样式文件(headStyle.js) 创建组件过后不要忘了在App.js引入

这里需要注意一点 创建组件文件时 可以先判断组件有无状态 如果不好判断 可以先写成有状态组件 后期根据需要再进行修改

技术图片

以上基本工作完成后,注意React的样式也要写成组件的形式 在局部样式文件中建一个样式组件(HeadWrap)然后在组件文件中引入

注意:每个组件在写内容之前最好都有有一个像HeadWrap这样的包裹层

 Head.js
 import {HeadWrap,Logo,Nav,NavItem,SearchWrap,Search,SearchInfo} from ‘./headStyle‘
 ?
 headStyle.js
 export const HeadWrap = styled.div`
 ?
 `

之后的组件样式内容皆是这种形式 (antd就是这个原理 样式封装成组件)

注意

1.样式中如果有需要引入图片的地方 一般放在static文件夹(src文件夹下)中 如果是背景图引入 不能用一般css方法 会出现没有图片的情况 正确方法时

 import logo from ‘../../static/logo.jpg‘
 ?
   background-image:url(${logo}) ;

2.也没必要每个标签都封装成组件 也可在样式组件下直接写html标签,出现嵌套时也是跟css一样的语法,不过添加类名的时候是className 不是class

 

技术图片

3.标签input在写属性时需要attr

   < Search className=flag>
 ?
 export const Search = styled.input.attrs({placeholder:‘请输入内容‘})`
 ?
         &.flag{    
             /* 同级属性写法 加&*/
             width:250px;
           
         }
 `

4.小图标icon的用法(bootstrap为例)

先在public文件夹下的index.html中引入bootstrap ,这里是引入的网址 接着可直接在组件下引用小图标 如果是引入本地css文件在index.js中 一般不引用本地文件

 

4.事件方法

以搜索框为例 失焦变短 聚焦变长 这里事件的写法不是最终的代码 后期还需要分离优化

Head.js

 import React, { Component } from ‘react‘;
 import {HeadWrap,Search} from ‘./headStyle‘
 ?
 class Head extends Component {
     constructor(props){ // 这里在使用redux后 共有数据提到了reducer.js中 就不在需要
         super(props);
         this.state={
             flag:false
        }
     
    }
     inputFocus(){
         this.setState({
             flag:true
        })
    }
     inputBlur(){
         this.setState({
             flag:false
        })
    }
     render() {
         return (
             <div>
                <HeadWrap>
                < Search className={this.state.flag?‘flag‘:‘‘}
            onFocus={this.inputFocus.bind(this)}
            onBlur={this.inputBlur.bind(this)}>
             
            </Search>
                </HeadWrap>
             </div>
        );
    }
 }
 ?
 export default Head

headStyle.js

 import styled from ‘styled-components‘;
 export const HeadWrap = styled.div`
 ?
 `
 export const Search = styled.input.attrs({placeholder:‘请输入内容‘})`
        width:180px;
         height:40px;
         
         border:none;
         outline:none;
         box-sizing:border-box;
         border-radius:30px;
         margin-top:10px;
         margin-right:120px;
         padding-left:20px;
         transition:1s;
       
     
         &.flag{
             /* 同级属性写法 */
             width:250px;
           
         }
 `

**头部区还有一个内容 搜索框的聚焦失焦可以控制旁边几个热门搜索词的显示与隐藏,这里可以使用一个方法 在组件外部封装一个函数来控制有无返回热门搜索词,然后将flag参数传入函数,以此来达到用flag来控制聚焦失焦 和热门搜索词的显示隐藏

 function showInfo(flag){
     if(flag){
         return (
             <span>1111</span>
        )
    }else{
         return null
    }
 }
 ?
 {showInfo(this.state.flag)} //在组件内调用**

 

5、redux

这是React开发一个页面的基本流程 页面的其他地方也都可以像这样把数据映射和添加事件方法 接着可以使用redux进行优化,将共有数据和方法集中

下载好react-redux 在index.js文件里引入

import {Provider} from ‘react-redux‘
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById(‘root‘));

 

在src文件夹下创建一个store文件夹 store下接着创建store.js和reducer.js l两个文件的作用详情官网可查

store.js

import {createStore} from ‘redux‘; //创建store 引入创建的方法  服务员
import reducer from ‘./reducer‘;
const store =createStore(reducer);

export default store  //公开store

reducer.js

const defaultState={
}

export default (state=defaultState,action)=>{
  return state
}
 

这里不要忘记在index.js中引入store.js

以Head.js为例 redux规定需要引用共有数据的组件 引入connect

import { connect } from ‘react-redux‘;
export default connect(mapState,mapDispatch)(Head);

首先是数据的映射

j将共有数据分离到reducer.js文件中

const defaultState={
    flag:false
}

export default (state=defaultState,action)=>{
  return state
}

然后回到Head.js渲染到组件中

const mapState = (state)=>{ //这种形式可以看到数据有没有获取到  可有可无
   console.log(state)
    return{
        
    }
}

const mapState = (state)=>({
    //这里值已经变成props  原先映射在组件里的this.state.flag要改成this.props.flag
    flag:state.flag
})

派发方法,也就是派发action 然后由reducer.js接受

const mapDispatch=(dispatch) =>({
    inputFocus(){
        const action ={
          type:"input_focus"
   }
        dispatch(action)

同映射数据 此时方法的条件也要更改,变为this.props.方法名

在reducer.js中 可以先console.log(action) 看action有没有接受到

const defaultState={
    flag:false
}

export default (state=defaultState,action)=>{
    console.log(action);
const newState =JSON.parse(JSON.stringify(state));
   if(action.type ===‘input_focus‘){
 
  newState.flag=true;
  return newState
   }else if(action.type ===‘input_blur‘){
  
   newState.flag=false;
   return newState
  }
  return state
  }


以上是本次项目的head区,接下来是home区的流程

home页面需要注意的是 它的内容较多 所有需要提前构造好一个拆分组件 的思路,因为一个组件的内容不适合太多 合理的拆分组件能让我们思路更加清晰 开发效率更快,数据映射的流程与上述基本一致,

以本项目为例 home页面我将其拆分成了四个子组件 在home文件夹下重新创建一个components文件夹来存放子组件 而且 子组件的样式可以统一写在homeStyle.js中 不要忘了在Home.js中引入这四个子组件

注意这里图片的加载我们一般不用本地的图片而是使用网络图片

页面中某些需要遍历的部分

class Topic extends Component {
state={
    topicList:[
        
    ]
}
    render() {
        return (
            <div>
                <TopicWrap>
                   
                  {
                        this.state.topicList.map((item,index)=>{
                            return(
                              
                                <TopicItem key={index} >
                                <img src={item.topicUrl}/>
                            <span >{item.txt}</span>
                            </TopicItem>
                        
                            )
                        })
                  }
                  
                </TopicWrap>
            </div>
        );
    }
}


以上便是所有数据映射和事件派发的基本流程写法 子组件中也可以使用redux的写法 同理

接下来可以对代码进行进一步的拆分优化

6.拆分reducer

在开发项目过程中 如果项目较大 共有数据和派发方法较多 即使使用redux还是显得代码不够简洁,所以可以对reducer.js文件再进行拆分,以本项目head组件和home组件下四个子组件为例,拆分后四个子组件变为无状态组件

1.在head文件夹下新建一个store文件夹 store文件夹 新建一个headReducer.js 专门管理head.js的共有数据

此时主reducer不再写逻辑代码 只起到把拆分后的reducer文件链入的作用 最后通过store.js将数据拆分到所有组件中

reducer.js

import {combineReducers} from ‘redux‘; //负责拆分reducer的模块
import headReducer from ‘../components/head/store/headReducer‘
import homeReducer from ‘../components/home/store/homeReducer‘
import singleReducer from ‘../components/single/store/singleReducer‘
import loginReducer from ‘../components/login/store/loginReducer‘

export default combineReducers({
   head:headReducer, //这里head是key值 是自己取的
   home:homeReducer,
   single:singleReducer,
   login:loginReducer
})
 

headReducer.js

const defaultState={
    flag:false
};

export default (state=defaultState,action)=>{ 
console.log(action);
const newState =JSON.parse(JSON.stringify(state));
if(action.type ===‘input_focus‘){
 
  newState.flag=true;
  return newState
}else if(action.type ===‘input_blur‘){
  
   newState.flag=false;
   return newState
}

2.拆分过后,要注意此时head组件中映射数据的地方,我们将headReducer赋予了一个key值head,

所以映射数据改为

const mapState=(state) =>({
   flag:state.head.flag  //head对应上述所取key值head
})

home同理,子组件中所有需要的数据全部放入homeReducer.js中 然后分别在各个子组件中映射数据 这里数据映射到子组件后 渲染方式也要改为props

 

7.请求数据(难点)

1.下载axios npm i axios

public是一个特殊的文件夹,可以将请求的json文件放入

2.在home组件中 良好的写法习惯是先看能不能请求到数据 这里要注意数据的格式,实际项目开放中最好和后台沟通好json数据的格式 方便我们请求,既然要请求数据 原先在homeReducer中写死的数据要删掉

home组件中
 componentDidMount(){
      
        axios.get(‘/data/getHomeList.json‘).
        then((res)=>{
            console.log(res.data);
        })
    }

接着要把数据发送给homeReducer,就需要派发action

import {connect} from ‘react-redux‘

class Home extends Component{
    componentDidMount(){
      
        axios.get(‘/data/getHomeList.json‘).
        then((res)=>{
            console.log(res.data);  
            const data =res.data.data; //后面的data是json文件中的data
            const action ={
                type:‘input_home_data‘,
                data
            };
            this.props.changeHomeData(action); 实参
        })
    }
    
}

const mapDispatch =(dispatch)=>({
    changeHomeData(action){ //形参
        dispatch(action);//接收
    }
})

export default connect(null,mapDispatch) (Home)

注意上述代码派发action时,action在钩子函数componentDidMount中 而派发方法却在组件外,

因此我们先在mapDispatch中自定义一个派发action的方法changeHomeData,再用props把方法渲染到componentDidMount中,通过参数action

3.homeReducer接收action

const defaultState={
    list:[],
    topicList:[], 
};

export default (state=defaultState,action)=>{ 
console.log(action);
if(action.type ===‘input_home_data‘){
    const newState =JSON.parse(JSON.stringify(state));
    newState.topicList =action.data.topicList;
    newState.list =action.data.list;
     return newState
    
 }
   return state;
}

8.最后的优化

1.以本项目中的head组件为例 如果dispatch派发次数过多 还可以进行拆分

在head下的store文件夹下新建一个headActionCreat.js

export const handleFocus =() =>({ //对象就代表创建的action对象
    type:"input_focus"
})

export const handleBlur =() =>({ //对象就代表创建的action对象
    type:"input_blur"
})

原先派发方法的地方head.js

import * as headActionCreates from ‘./store/headActionCreates‘

const mapDispatch=(dispatch) =>({
    inputFocus(){
    //     const action ={
    //         type:"input_focus"

    //     }
    //    dispatch(action)
    // 如果一个组件中的action有很多个 应该把他们放在一个单独的文件里
//    在组件中引入使用
    dispatch(headActionCreates.handleFocus())
    },
    inputBlur(){
    //     const action ={
    //         type:"input_blur"

    //     }
    //    dispatch(action)
    dispatch(headActionCreates.handleBlur())
    }
})

2.经过一系列拆分后,组件中应该已经没有state了 所以可以变成无状态组件,

react-redux会把state映射成props 所以有状态组件可以改成无状态组件

function Head(props) {  //传入props  下面渲染数据的地方也不需要this
    
        return(
            <div>
        <HeadWrap>
            <Logo/>
            <Nav>
              
                <NavLink to=‘/‘><NavItem>首页</NavItem></NavLink> 
                <NavLink to=‘/detail‘><NavItem>详情</NavItem></NavLink> 
               
              
                <NavLink to=‘/login‘><NavItem>登录</NavItem></NavLink> 
              
            </Nav>
        

            {showInfo(props.flag)}
            <SearchWrap>
           
           < Search className={props.flag?‘flag‘:‘‘}
           onFocus={props.inputFocus}
           onBlur={props.inputBlur}>
             
           </Search>
           <span className="glyphicon glyphicon-search"></span>
            </SearchWrap>
            
              </HeadWrap>
            </div>
        )
  
}

3.如果像本项目中flag、inputFocus、inputBlur前缀相同,相同属性名可以解构赋值(参考)

const {flag,inputFocus,inputBlur} =props
下面的props可以省略

 

以上是关于React项目的基本流程&redux的用法原理的主要内容,如果未能解决你的问题,请参考以下文章

redux-saga基本用法

react-redux入门教程

React入门之Redux:react-redux基本使用

React入门之Redux:react-redux基本使用

Redux-介绍&工作流程

Redux