react項(xiàng)目實(shí)戰(zhàn)筆記

項(xiàng)目實(shí)戰(zhàn)

1.腳手架生成項(xiàng)目

create-react-app jianshu

2.styled-components的使用

  • 1.安裝styled-components包
yarn add styled-components
  • 2.創(chuàng)建style.js文件,代碼如下
import { createGlobalStyle  } from "styled-components";
//導(dǎo)出全局樣式
export const GlobalStyle = createGlobalStyle `
//此處是全局樣式表,可以把reset.css放在這里
html, body, div, span{...}
`
  • 3.在index.js中引入,代碼如下
import React from 'react';
import ReactDOM from 'react-dom';
//引入全局樣式
import {GlobalStyle} from "./css/style.js";
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    /*全局樣式*/
    <GlobalStyle />
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

3.在組建中使用styled-components

  • header組件的編寫
    • header組件目錄結(jié)構(gòu)如下


      image.png
      • header (文件夾)
        • index.js (header組件的代碼)
        • style.js (header組件的樣式)
    • index.js代碼如下
import react,{Component} from "react";
import {HeaderWrapper,NavLogo,Nav,NavLeft,NavRight,HeaderLink,LinkItem} from "./style"

class header extends Component{
    render(){
        return (
            <HeaderWrapper>
                <NavLogo href="/"></NavLogo>
                <Nav>
                    <NavLeft>
                        <p className="p1">首頁(yè)</p>
                        <p className="p2">下載APP</p>
                        <input type="text" placeholder="搜索"/>
                    </NavLeft>
                    <NavRight>
                        <p>Aa</p>
                        <a href="">登錄</a>
                    </NavRight>
                </Nav>
                <HeaderLink>
                    <LinkItem>注冊(cè)</LinkItem>    
                    <LinkItem className="link2">寫文章</LinkItem>    
                </HeaderLink>  
            </HeaderWrapper>
        )
    }
}
export default header;

style.js代碼如下

import styled from "styled-components";
//引入圖片
import logoImg from "../../asstes/img/nav-logo.png";
//HeaderWrapper會(huì)被解析成div元素 模板字符串中寫樣式,把HeaderWrapper暴露出去,在組件中使用就不會(huì)產(chǎn)生樣式?jīng)_突問(wèn)題了宙刘。
export const HeaderWrapper = styled.div`
  display: flex;
  height: 58px;
  padding: 0 39px;
  border-bottom:1px solid #eee;
`;
//NavLogo會(huì)被解析成a標(biāo)簽 注意圖片要用import的方式
export const NavLogo = styled.a`
  background: url("${logoImg}") no-repeat;
`;
//NavLeft有子元素,子元素樣式的寫法
export const NavLeft = styled.div`
  NavLeft的樣式
  .p1{
      p1的樣式
  }
  .p2{
      p2的樣式
  }
  input{
      input的樣式
  }
`;
//其他代碼省略....

4.使用redux改造代碼

import  { Component } from "react";
import {connect} from "react-redux";

import {changeFocusStatus} from "../../store/actionCreator";
class header extends Component {
  constructor(props){
    super(props);
    this.state = {
      focus:false
    }
  }
  render() {
    const {props} = this;
    return (
      <HeaderWrapper>
        /*重點(diǎn)代碼 其他代碼省略*/
        <div className="search" className={props.foucsed==true?"search focus":"search"} >
            <input type="text" placeholder="搜索" onFocus={()=>{props.changeFoucs(true)}} onBlur={()=>{props.changeFoucs(false)}} />
            <span className="iconfont icon-fangdajing"></span>
         </div> 
      </HeaderWrapper>
    );
  }
}
const mapStateToProps = (state)=>{
  return {
    foucsed:state.foucsed
  }
}
const mapActionToProps = (dispatch)=>{
  return {
    changeFoucs(value){
      const action = changeFocusStatus(value);
      dispatch(action)
    }
  }
}
export default connect(mapStateToProps,mapActionToProps)(header);
5.拆分reducer.js,然后使用combineReducers進(jìn)行組合,類似于vue的store拆分
  • reducer.js代碼
import { combineReducers } from "redux";
import { reducer as HeaderReducer } from "../common/header/store";

const reducer = combineReducers({
    header:HeaderReducer
});

export default reducer;
  • 在header組件中創(chuàng)建store文件夾,下面存放actionCreators矿酵、reducer.js瓷患、actionTypes(更改文件名字為constant.js),之后在index.js中把三個(gè)文件引入統(tǒng)一對(duì)外暴露描沟。目錄結(jié)構(gòu)如下圖所示:


    image.png
  • index.js代碼
import reducer from "./reduce";
import * as constant from "./constant";
import * as actionCreators from "./actionCreators";

export { reducer , constant , actionCreators }
  • reducer.js代碼
import {CHANGE_FOUCS_STATUS} from "./constant";
const defaultStore = {
    foucsed:false
};
export default (store = defaultStore, action) => {
    if(action.type==CHANGE_FOUCS_STATUS){
        const newStore = JSON.parse(JSON.stringify(store));
        newStore.foucsed = action.value;
        return newStore;
    }
    return store;
};

constant.js(也就是actionTypes.js)代碼 constant是常量的意思

export const CHANGE_FOUCS_STATUS = "change_foucs_status";

actionCreators.js代碼

import { CHANGE_FOUCS_STATUS } from "./constant";

export const changeFocusStatus = (value) => {
  return {
    type: CHANGE_FOUCS_STATUS,
    value,
  };
};
  • 最后修改header組件index.js代碼引入constant.js(也就是actionTypes.js)的方式
//其他代碼省略
import { actionCreators } from "./store";
const mapActionToProps = (dispatch)=>{
  return {
    changeFoucs(value){
      const action = actionCreators.changeFocusStatus(value);
      dispatch(action)
    }
  }
}

5.使用immutable包保證state中的數(shù)據(jù)不被修改(防止寫代碼誤操作)

  • 默認(rèn)的數(shù)據(jù)在reducer.js中,改造reducer.js代碼
import { CHANGE_FOUCS_STATUS } from "./constant";
//引入immutable庫(kù),mutable"可變的"的意思,immutable"不可變的"意思 
import { fromJS } from "immutable";
//通過(guò)fromJS方法傳入一個(gè)對(duì)象,得到一個(gè)immutable對(duì)象
const defaultStore = fromJS({
  focused: false,
});
export default (store = defaultStore, action) => {
  if (action.type == CHANGE_FOUCS_STATUS) {
    // immutable對(duì)象的set方法,會(huì)結(jié)合之前immutable對(duì)象(也就是defaultStore)的值和要設(shè)置的值,返回一個(gè)全新的immutable對(duì)象,并不會(huì)對(duì)之前的對(duì)象做修改。
    return store.set("focused",action.value);
  }
  return store;
}; 
  • 由于store變成了一個(gè)immutable對(duì)象,所以獲取值的方式也需要改變。
//header組件代碼改動(dòng)
const mapStateToProps = (state)=>{
  return {
    //由于
    focused:state.header.get("focused")
  }
}

6.在總的reducer.js中結(jié)合dedux-immutable庫(kù)把最外層的reducer對(duì)象也變成immutable對(duì)象,改造代碼如下:

// import { combineReducers } from "redux";
//引入包的時(shí)候combineReducers從redux-immutable引入就可以了。
import { combineReducers } from "redux-immutable";
import { reducer as HeaderReducer } from "../common/header/store";
const reducer = combineReducers({
    header:HeaderReducer
});
export default reducer;
  • 由于整個(gè)reducer返回的都是一個(gè)immutable對(duì)象,所以在頁(yè)面中使用的時(shí)候都需要用get方法獲取數(shù)據(jù),改造header組件代碼如下:
const mapStateToProps = (state)=>{
  return {
    // focused:state.get("header").get("focused")
    //下面的寫法等價(jià)于上面的寫法
    focused:state.getIn(["header","focused"])
  }
}

7.搜索框聚焦,請(qǐng)求搜索歷史數(shù)據(jù),使用redux-thunk中間件,把異步請(qǐng)求數(shù)據(jù)操作寫在actionCreator中。效果如下:

image.png
  • store\index.js文件 加入中間件redux-thunk
import { createStore, compose, applyMiddleware } from "redux";
import reducer from "./reducer";
import thunk from "redux-thunk";
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, composeEnhancers(
    applyMiddleware(thunk)
));
export default store;
  • actionCreators.js 導(dǎo)出函數(shù),在該函數(shù)中請(qǐng)求異步數(shù)據(jù)并通過(guò)dispatch方法傳遞action給store
//注意一個(gè)小細(xì)節(jié),由于header中的store數(shù)據(jù)是調(diào)用fromJS得到的immutable數(shù)據(jù),所以此處把拿到的數(shù)據(jù)也轉(zhuǎn)換成immutable數(shù)據(jù)
import { fromJS } from "immutable";
const setKeywordList = (list) => ({
  type: KEYWORD_LIST,
  list:fromJS(list)
});
//暴露出去一個(gè)函數(shù)
export const getKeywordList = (data)=>{
  return async (dispatch)=>{
    const {data} = await axios.get("http://127.0.0.1:5500/data/headerList.json");
    const action = setKeywordList(data.list)
    dispatch(action);
  }
}
  • header\store\reduce.js 代碼改造,改變if為switch
import { CHANGE_FOUCS_STATUS,KEYWORD_LIST } from "./constant";
//引入immutable庫(kù),mutable"可變的"的意思,immutable"不可變的"意思 
import { fromJS } from "immutable";
//通過(guò)fromJS方法傳入一個(gè)對(duì)象,得到一個(gè)immutable對(duì)象
const defaultStore = fromJS({
  focused: false,
  list:["羅小黑","刺客伍六七"]
});
export default (store = defaultStore, action) => {
  switch(action.type){
    case CHANGE_FOUCS_STATUS:
      // immutable對(duì)象的set方法,會(huì)結(jié)合之前immutable對(duì)象的值和要設(shè)置的值,返回一個(gè)全新的對(duì)象礼预。
      return store.set("focused",action.value); 
    case KEYWORD_LIST:
      return store.set("list",action.list);
    default:
      return store
  }
};
  • header\index.js 頭部組件
import { Component } from "react";
import { connect } from "react-redux";
import { actionCreators } from "./store";
class header extends Component {
  //渲染歷史記錄
  getHisTory(state, list) {
    if (state) {
      return (
        <History>
          {list.map((item) => {
            return (
              <div className="item" key={item}>
                <div className="item__left">
                  <i className="iconfont icon-clock"></i>
                  <span>{item}</span>
                </div>
                <i className="iconfont icon-chahao"></i>
              </div>
            );
          })}
        </History>
      );
    }
  }
  render() {
    const { focused,changeFoucs,list } = this.props;
    return (
      <NavLeft>
        <p className="p1">首頁(yè)</p>
        <p className="p2">下載APP</p>
        <div className={focused == true ? "search focus" : "search"}>
          <input
            type="text"
            placeholder="搜索"
            onFocus={() => {
              changeFoucs(true);
            }}
            onBlur={() => {
              changeFoucs(false);
            }}
          />
          <span className="iconfont icon-fangdajing"></span>
          {this.getHisTory(focused, list)}
        </div>
      </NavLeft>
    );
  }
}
const mapStateToProps = (state) => {
  return {
    focused: state.getIn(["header", "focused"]),
    list: state.getIn(["header", "list"]),
  };
};
const mapActionToProps = (dispatch) => {
  return {
    changeFoucs(value) {
      const action = actionCreators.changeFocusStatus(value);
      dispatch(action);
      //如果聚焦,就去請(qǐng)求后端數(shù)據(jù)
      if(value)dispatch(actionCreators.getKeywordList())
    }
  };
};
export default connect(mapStateToProps, mapActionToProps)(header);

首頁(yè)其他部分

  • 一共分為四個(gè)部分,效果圖如下


    image.png
  • 每個(gè)部分可以將其拆分成組件去維護(hù),首頁(yè)只負(fù)責(zé)把其他組件添加進(jìn)來(lái)。

  • 目錄結(jié)構(gòu)如下


    image.png
  • Recommond.js 組件代碼如下

import { Component } from 'react'
import { Recommands, LoadMore } from '../style'
import { connect } from 'react-redux'
import { actionCreators } from '../store'
class Recommond extends Component {
  render() {
    let { list } = this.props
    return (
      <Recommands>
        {list.map((item, index) => {
          return (
            <div key={index} className="recommond__item">
              <div className="item__left">
                <h3>{item.get('title')}</h3>
                <p>{item.get('content')}</p>
              </div>
              <div className="item__right">
                <img src={item.get('img')} alt="" />
              </div>
            </div>
          )
        })}
        <LoadMore
          onClick={() => {
            this.props.loadMore(this.props.pageIndex)
          }}
        >
          閱讀更多
        </LoadMore>
      </Recommands>
    )
  }
}
const mapStateToProps = (state) => {
  return {
    list: state.getIn(['home', 'recommondList']),
    pageIndex: state.getIn(['home', 'pageIndex']),
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    loadMore(pageIndex) {
      // console.log(actionCreators.loadMore())
      dispatch(actionCreators.loadMore(pageIndex))
    },
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(Recommond)
  • 如果想要在頁(yè)面剛進(jìn)入的時(shí)候就去請(qǐng)求home頁(yè)面的數(shù)據(jù)抹锄,可以在componentDidMount的時(shí)候調(diào)用
componentDidMount() {
  //可以通過(guò)props找到映射到peops中的數(shù)據(jù)和方法
  this.props.getHomeData();
}
* 在使用圖片作為背景圖而圖片是動(dòng)態(tài)傳入的話可以用以下寫法
```jsx
//list.js代碼如下
const List = (props) => {
  let { list } = props
  return (
    <div>
      {list.map((item, index) => {
        return <ListsItem imgUrl={item} key={index} />
      })}
    </div>
  )
}
//style.js代碼如下 ${} 里面可以寫一個(gè)函數(shù),函數(shù)的形參就是一個(gè)對(duì)象 通過(guò)對(duì)象.屬性名可以訪問(wèn)到值
export const ListsItem = styled.div`
  width: 280px;
  height: 50px;
  background: url(${(props) => props.imgUrl});
  background-size: cover;
`
  • 使用PureComponent優(yōu)化性能
    • 父組件中的state和props中的數(shù)據(jù)發(fā)生變化的時(shí)候,render會(huì)重新執(zhí)行,并且子組件的render也會(huì)被執(zhí)行,但有時(shí)候子組件不需要重新渲染,此時(shí)可以用shouldComponentUpdate加入判斷邏輯決定子組件是否要重新渲染,PureComponent實(shí)現(xiàn)了shouldComponentUpdate,但最好結(jié)合immutable一起使用,不然可能會(huì)出錯(cuò)逆瑞。
      項(xiàng)目地址:https://gitee.com/yyagami/jianshu.git
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市伙单,隨后出現(xiàn)的幾起案子获高,更是在濱河造成了極大的恐慌,老刑警劉巖吻育,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件念秧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡布疼,警方通過(guò)查閱死者的電腦和手機(jī)摊趾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)游两,“玉大人砾层,你說(shuō)我怎么就攤上這事〖福” “怎么了肛炮?”我有些...
    開(kāi)封第一講書人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我侨糟,道長(zhǎng)碍扔,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任秕重,我火速辦了婚禮不同,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘溶耘。我一直安慰自己二拐,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布汰具。 她就那樣靜靜地躺著卓鹿,像睡著了一般菱魔。 火紅的嫁衣襯著肌膚如雪留荔。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,985評(píng)論 1 291
  • 那天澜倦,我揣著相機(jī)與錄音聚蝶,去河邊找鬼。 笑死藻治,一個(gè)胖子當(dāng)著我的面吹牛碘勉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播桩卵,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼验靡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了雏节?” 一聲冷哼從身側(cè)響起胜嗓,我...
    開(kāi)封第一講書人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钩乍,沒(méi)想到半個(gè)月后辞州,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡寥粹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年变过,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涝涤。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡媚狰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出阔拳,到底是詐尸還是另有隱情崭孤,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站裳瘪,受9級(jí)特大地震影響土浸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜彭羹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一黄伊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧派殷,春花似錦还最、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至经伙,卻和暖如春扶叉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背帕膜。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工枣氧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人垮刹。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓达吞,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親荒典。 傳聞我的和親對(duì)象是個(gè)殘疾皇子酪劫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容