天天看點

react全家桶實戰

整個目錄結構如下:

react全家桶實戰

package.json代碼如下:

{
  "name": "active",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.4.1",
    "react-dom": "^16.4.1",
    "react-redux": "^5.0.7",
    "react-router-dom": "^4.3.1",
    "react-scripts": "1.1.4",
    "react-swipe": "^5.1.1",
    "react-transition-group": "^2.3.1",
    "redux": "^4.0.0",
    "redux-logger": "^3.0.6",
    "redux-promise": "^0.6.0",
    "redux-thunk": "^2.3.0",
    "swipe-js-iso": "^2.0.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "proxy": {
    "/api": {
      "target": "http://localhost:1000/",
      "changeOrigin": true,
      "pathRewrite": {
        "/api": "/"
      }
    }
  },
  "devDependencies": {
    "babel-plugin-transform-decorators-legacy": "^1.3.5"
  }
}
           

api/home.js(在redux中的action中被引入store/actions/home.js)用來擷取首頁資料

//擷取輪播圖資料
function getSilders() {
    //最原始的fetch
    // fetch('api/loop', {method: "post"}).then(function (response) {
    //     // 轉換為 JSON
    //     console.log("response")
    //     console.log(response)
    //     return response.json();
    // }).then(function (j) {
    //     console.log('擷取到的資料是====');
    //     console.log(j);
    //     return j
    // });
    redux-promise的用法:可以将payload中的promise執行,執行後将内容放到action.payload中進行派發:{ type:SET_SLIDERS, patload:[{},{},{},{}] }
    return fetch('api/loop', {method: "post"})
}
export { getSilders }
           

common/Tab/Tab.js;在app.js中被引入;作為底部導航;切換首頁面

import React,{Component} from 'react'
import {Link,NavLink} from 'react-router-dom'

export default class Tab extends Component{
    constructor(){
        super()

    }
    render(){
        return (
            <ul>
                <li><Link to={'/'}> 首頁</Link></li>
                <li><Link to={'/lesson'} >課程 </Link></li>
                <li><Link to={'/profile'} > 使用者中心</Link></li>

                {/*第二種寫法如下:*/}
                {/*<li> <NavLink to={'/'}>首頁</NavLink> </li>*/}
                {/*<li> <NavLink to={'/lesson'}>課程</NavLink> </li>*/}
                {/*<li> <NavLink to={'/profile'}>使用者中心</NavLink> </li>*/}
            </ul>
        )
    }
}
           

總結:

1:路由的2種寫法NavLink , Link:

2:在目前路由下,再次點選路由切換報警告如下:

react全家桶實戰

containers/Home/Homeheader/Homeheader.js;被Home.js被引入

import React, {Component} from 'react'
import logo from '../../../common/img/logo.png'
import './index.css'
import Transition from 'react-transition-group/Transition';

const duration = ;
//預設樣式
const defaultStyle = {
    transition: `opacity ${duration}ms ease-in-out`,
    opacity: ,
    display:'none'
}

const transitionStyles = {
    entering: {opacity: },
    entered: {opacity: },
};

export default class Homeheader extends Component {
    constructor() {
        super()
        this.state = {
            isShow: false
        }
    }

    changeShow = () => {
        this.setState({
            isShow: !this.state.isShow
        })
    }
    selectLesson=(e)=>{
        //li元素,目前點選的元素是誰就是誰
        // console.log(e.target)
        // //整個ul元素。事件在誰身上綁着就是誰
        // console.log(e.currentTarget)
        // //擷取h5 data屬性
        // console.log(e.target.dataset.type)
        //隐藏元素
        this.changeShow()
        this.props.selectCurrentLesson(e.target.dataset.type)
    }

    render() {
        return (
            <div className={'home-header'}>
                <p>
                    <i onClick={this.changeShow} className={'iconfont icon-menu menu'}></i>
                    <img className={'logo'} src={logo} alt=""/>
                </p>
                <Transition in={this.state.isShow} timeout={duration} onEnter={(node)=>{
                    node.style.display='block'
                }}  onExited={(node)=>{
                    node.style.display='none'
                }}>
                    {(state) => (
                        <ul className={'menu-ul'} style={{
                            ...defaultStyle,
                            ...transitionStyles[state]
                        }}
                            onClick={(e)=>{
                                this.selectLesson(e)
                            }}
                        >
                            <li data-type="all">全部課程</li>
                            <li data-type="react">react課程</li>
                            <li data-type="vue">vue課程</li>
                        </ul>
                    )}
                </Transition>

            </div>
        )
    }
}

           

總結:

1:顯示隐藏采用‘react-transition-group’元件

2:官方元件位址:https://github.com/reactjs/react-transition-group

3:html5中的data屬性擷取:e.target.dataset.type

4:效果圖如下:

react全家桶實戰

containers/Home/Home.js;路由‘/’的元件

import React, {Component} from 'react'
import './index.css'
import Homeheader from './Homeheader/Homeheader'
import {connect} from 'react-redux'
import HomeSlider from './HomeSlider'
import Loading from '../../components/Loading'
import actions from '../../store/actions/home'
//修飾符的用法待研究
@connect(state => ({...state.home}), actions)
export default class Home extends Component {
    // constructor(){
    //     super()
    // }
    componentWillMount() {
        // 第一種方法:直接ajax
        // fetch('api/loop', {method: "post"}).then(function (response) {
        //     // 轉換為 JSON
        //     return response.json();
        // }).then(function (j) {
        //     console.log(j);
        // });
        // 第2種方法:ajax從外部引入
        // getSilders()
        // 第3種方法:采用redux;隻有connect裝飾器以後才能使用redux中的store;如果redux中存在資料;則不進行請求
        if (this.props.slider.length === ) {
            this.props.getSliderApi()
        }

    }
    selectCurrentLesson = (val) => {
        // console.log('傳給父元件的值是'+val)
        console.log(this.props)
        this.props.updateCurrentLesson(val)
    }
    render() {
        return (
            <div>
                <Homeheader selectCurrentLesson={this.selectCurrentLesson}></Homeheader>
                //解決輪播圖不出現的BUG
                {
                    this.props.slider.length> ? <HomeSlider lists={this.props.slider}></HomeSlider> : <Loading></Loading>
                }
            </div>
        )
    }
}
           

containers/Lesson/Lesson.js;路由‘/lesson’的元件

import React,{Component} from 'react'
import './index.less'

export default class Lesson extends Component{
    // constructor(){
    //     super()
    // }
    render(){
        return (
            <div>
            Lesson
            </div>
        )
    }
}
           

containers/Profile/Profile.js;路由‘/Profile’的元件

import React,{Component} from 'react'
import './index.less'

export default class Profile extends Component{
    // constructor(){
    //     super()
    // }
    render(){
        return (
            <div>
            Profile
            </div>
        )
    }
}
           

store/actions/home.js;在Home.js中被引入;

import * as Types from '../action-types'
import { getSilders } from '../../api/home'


let actions={
    //更新目前選擇的課程
    updateCurrentLesson(lesson){
        return {
            type:Types.SET_CURRENT_LESSON,
            lesson
        }
    },
    getSliderApi(){
        // redux-thunk
        return function (dispatch,getState) {
            //redux-promise的用法:可以将payload中的promise執行,執行後将内容放到action.payload中進行派發:{ type:SET_SLIDERS, patload:[{},{},{},{}] }
            dispatch({type:Types.SET_SLIDERS ,payload:getSilders().then(function (response) {
                    // 轉換為 JSON
                    console.log("response")
                    console.log(response)
                    if(response.status==){
                        return response.json();
                    }else{
                        alert('伺服器挂掉了')
                    }
                }).then(function (j) {
                    // console.log('擷取到的資料是====');
                    // console.log(j);
                    return j
                })})
        }

    }
}
export default  actions
           

store/reducers/home.js;在下面的index.js中被引入;

//一個頁面一個reducer
import * as Types from '../action-types'
let initState={
    currentLesson:'all',
    slider:[]
}
function home(state=initState, action) {
    switch (action.type){
        case Types.SET_CURRENT_LESSON:
            return {
                ...state,
                currentLesson:action.lesson
            }
        case Types.SET_SLIDERS:
            return {
                ...state,
                slider:action.payload
            }
        default:{
            return state
        }


    }
}
export default home
           

store/reducers/index.js;在store中index.js中被引入;

import { combineReducers } from 'redux'
import home from './home'
export default  combineReducers({
    home
})
           

store/action-types.js;在store中的各種JS檔案中引入;

//設定目前課程
export const SET_CURRENT_LESSON='set_current_lesson'
//設定輪播圖資料
export const SET_SLIDERS='set_sliders'
           

store/index.js;被最外層的index.js引入

import { createStore , applyMiddleware } from 'redux'
import reduxLogger from 'redux-logger'
import reduxThunk from 'redux-thunk'
import reduxPromise from 'redux-promise'
import reducer from './reducers/index'


let store= createStore(reducer,applyMiddleware(reduxLogger,reduxThunk,reduxPromise))
window._store=store; //為了測試用
export default store
           

總結:

寫redux的邏輯

1:action-types

2:reducer

3:action

4:元件中調用action; dispatch

react全家桶實戰

架構最外層的index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
//配置路由元件
import {HashRouter as Router,Route, Switch} from 'react-router-dom'
import Home from './containers/Home/Home'
import Profile from './containers/Profile/Profile'
import Lesson from './containers/Lesson/Lesson'

import {Provider} from 'react-redux'
import store from './store/index'

import './common/reset.css'

ReactDOM.render(
    <Provider store={store}>
        <Router>
            <App>
                <Switch>
                    <Route path='/' exact={true} component={Home}></Route>
                    <Route path='/lesson' component={Lesson}></Route>
                    <Route path={'/profile'} component={Profile}></Route>
                </Switch>
            </App>
        </Router>
    </Provider>
    , document.getElementById('root'));
registerServiceWorker();
           

架構最外層的app.js

import React, { Component } from 'react';
import Tab from './common/Tab/Tab'


class App extends Component {
  render() {
    return (
        <div>
            {this.props.children}
            <Tab></Tab>
        </div>

      // <div className="App">
      //     {this.props.children}
      //
      // </div>
    );
  }
}

export default App;
           

HomeSlider.js

import React,{Component} from 'react'
//輪播圖元件
import ReactSwipe from 'react-swipe';
export default class HomeSlider extends Component{
    constructor(){
        super()
        this.state={
            index:
        }
    }

    render(){
        let _this=this
        let opts={continuous: true,
            speed: ,
            auto: ,
            callback: function(index, elem) {
                _this.setState({
                    index:index
                })
            },
        }
        return (
            <div className={'loop'}>
                <ReactSwipe className="carousel" swipeOptions={opts}>
                    {
                        this.props.lists.map((item, index) => {
                            return (
                                <div key={index}><img src={item.url} alt=""/></div>
                            )

                        })
                    }
                </ReactSwipe>
                <div className={'slider-dots'}>
                    {
                        this.props.lists.map((item, index) => {
                            return (
                                <span key={index} className={this.state.index===index ? 'active': ''}></span>
                            )

                        })
                    }

                </div>
            </div>

        )
    }
}
           

1:輪播圖插件

輪播圖插件swipe-js-iso拖動一次後不再輪播的BUG修複

react全家桶實戰

繼續閱讀