新手級配置 react react-router4.0 redux fetch sass



前言

最近公司可能要用react,然而有幾個同事從來沒用過react,然后領導讓我帶他們學習react, 為下一個react項目做基礎。
然后隨手寫了幾個demo,幫助他們了解一下如何去構建配置項目。
現在分享出來,希望可以幫助到需要的人。 本demo 中有些目錄雖然沒有用,但是我還是列了出來,目的是為了展示一個正規項目的目錄骨架結構。
create-react-app 模板文件我也沒有歸類,等了解之后,可以自己歸類,加一個樣式的文件夾。

正文

就目前的大環境而言,在開發react或vue項目的時候,應該沒有幾個人還直接在html直接飲用react的庫文件吧,都是用的前端構建工具webpack + es6 來寫項目。所以直接就推薦facebook官方出品的腳手架create-react-app來做項目。 create-react-app是最適合新手來學習使用的腳手架,全部是簡單的命令。不需要像別的腳手架一樣,先去clone整個項目,然后再安裝依賴。

第一步 (安裝工具,生成項目, 啟動項目)

安裝create-react-app工具

npm install -g create-react-app    

生成項目

create-react-app react-demo

生成項目之后自動會下載依賴全部,
進入項目之后,啟動項目

cd react-demo
npm start 或者 yarn start

npm 和 yarn 都是包管理器, 都可以下載依賴,具體這里不做解釋,詳情可以自己搜索。一個項目最好用一種包管理工具,不要混用,下面都用yarn來下載依賴。不做另外說明

項目啟動之后打開3000端口。
項目啟動成功如下圖:


第二步 (配置react-router4.0,包含路由傳參等)

1、安裝依賴

yarn add react-router-dom --save

2、新建文件、配置路由
src 目錄下 新建 containers 目錄 里面放置主頁面文件
src 目錄下 新建 components 目錄 里面放置高復用模板文件
containers 中新建 Home.js

Home.js 代碼

import React, { Component } from 'react';
class Home extends Component {
    render() {
        return (
            <div>
                Home
            </div>
        );
    }
}

export default Home;

containers 中新建 List.js

List.js 代碼

import React, { Component } from 'react';
class List extends Component {
    render() {
        return (
            <div>
                List
            </div>
        );
    }
}

export default List; 

containers 中新建 Mine.js

Mine.js 代碼 (二級路由,路由傳參示例)

import React, {Component} from 'react';
import {BrowserRouter as Router, Route, Link} from 'react-router-dom';
import Mine1 from './Mine1'
import Mine2 from './Mine2'

class Mine extends Component {
    constructor(props) {
        super(props);
        this.state = {
            param: ''
        };
    }
    changeParam(event) {
        this.setState({
            param: event.target.value
        })
    }
    render() {
        return (
            <Router>
                <div>
                    Mine
                    傳參示例:<input type="text" placeholder='請輸入要傳遞到mine2的參數' value={this.state.param} onChange={this.changeParam.bind(this)}/>
                    <div className="mine-nav">
                        {/*編寫導航*/}
                        <ul>
                            <li><Link to={`${this.props.match.url}/mine1`}>Mine1</Link></li>
                            <li><Link to={`${this.props.match.url}/mine2/${this.state.param? this.state.param : "default-param"}`}>Mine2</Link></li>
                            {/*傳參示例,如果沒有參數,傳默認參數*/}
                        </ul>
                        {/*路由匹配*/}
                        <div>
                            <Route exact path={`${this.props.match.url}/mine1`} component={Mine1}/>
                            <Route path={`${this.props.match.url}/mine2/:param`} component={Mine2}/>
                        </div>
                    </div>
                </div>
            </Router>
        );
    }
}

export default Mine;

containers 中新建 Mine1.js

Mine1.js 代碼

import React, { Component } from 'react';
class Mine1 extends Component {
    render() {
        return (
            <div>
                Mine1
            </div>
        );
    }
}

export default Mine1; 

containers 中新建 Mine2.js

Mine2.js 代碼 (路由傳參示例)

import React, { Component } from 'react';
class Mine2 extends Component {
    render() {
        return (
            <div>
                2222222222222 傳遞的參數:{this.props.match.params.param}
            </div>
        );
    }
}

export default Mine2;

修改入口App.js 文件,修改為如下所示:

import React, { Component } from 'react';
import {BrowserRouter as Router, Route, Link} from 'react-router-dom';
import List from './containers/List.js'
import Home from './containers/Home.js'
import Mine from './containers/Mine.js'
import './App.css'
import logo from './logo.svg'

class App extends Component {
    render() {
        return (
            <div className="App">
                <div className="App-header">
                    <img src={logo} className="App-logo" alt="logo" />
                    <h2 className='App-title'>Welcome to React Plan</h2>
                </div>
                <div className="App-content">
                    {/*路由配置*/}
                    <Router>
                        <div className="content-box">
                            {/*編寫導航*/}
                            <ul className="nav">
                                <li><Link to="/">首頁</Link></li>
                                <li><Link to="/list">列表頁</Link></li>
                                <li><Link to="/mine/mine1">我的頁面二級路由</Link></li>
                                {/*link指向二級路由的默認頁面*/}
                            </ul>
                            {/*路由匹配*/}
                            <div className="content">
                                <Route exact path="/" component={Home}/>
                                <Route path="/list" component={List}/>
                                <Route path="/mine" component={Mine}/>
                            </div>
                        </div>
                    </Router>
                </div>
            </div>
        );
    }
}

export default App

修改App.css 文件,讓稍微有一點樣式:

.App {
    text-align: center;
}
.App-logo {
    animation: App-logo-spin infinite 20s linear;
    height: 80px;
}
.App-header {
    background-color: #222;
    height: 150px;
    padding: 20px;
    color: white;
}
.App-title {
    font-size: 1.5em;
}
.App-intro {
    font-size: large;
}
@keyframes App-logo-spin {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}

/*下面是新添加的樣式*/
* {
    margin: 0;
    padding: 0;
    list-style: none;
}
.content-box{
    overflow: hidden;
}
.nav {
    padding: 20px;
    float: left;
    width: 160px;
    background-color: #fcc;
}
.nav li{
    line-height: 30px;
}
.content{
    padding: 20px 0 0 200px;
    background-color: #ccf;
    height: 110px;
}
.mine-nav{
    background-color: #cfc;
    margin-top: 20px;
    height: 70px;
}
.mine-nav ul{
    overflow: hidden;
}
.mine-nav ul li{
    float: left;
    width: 50%;
}
.mine-nav>div{
    margin-top: 20px;
}

大功告成,點一點試試吧。配置完畢,頁面如下圖:

第三步 (配置redux)

redux具體是什么,這里就不多提,想詳細了解最好看一下阮一峰老師的博客 Redux 入門教程 來了解什么是redux, 或者你的項目到底需不需要用redux。這里只講如何去配置redux.
1、安裝依賴

yarn add redux react-redux --save

2、新建文件, 配置redux
src 目錄下 新建 store 目錄里面放置 store配置文件
src 目錄下 新建 reducers 目錄里面放置 規則性文件
src 目錄下 新建 constants 目錄里面放置 數據
src 目錄下 新建 actions 目錄里面放置 觸發更新文件
store 中新建 configStore.js

store > configStore.js 文件

import { createStore } from 'redux'
//引入規則文件
import rootReducer from '../reducers/index.js'
export default function configStore(initState){
        // 創建store
        const store = createStore(
            rootReducer,
            initState,
            // 如果安裝了redux插件可按照版本打開下面的注釋
            // window.devToolsExtension ? window.devToolsExtension() : undefined
            // window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : undefined
        )
        return store
}

reducers 中新建 index.js
reducers 中新建 number.js

reducers > index.js 文件

// 規則性文件
import { combineReducers } from 'redux'
import number from './number'
// 如果有多個規則,則引入多個文件
//import XXXX from './XXXX'

// 創建規則
export default combineReducers({
    // XXXX,
    number
})

reducers > number.js 文件

import * as actionTypes from '../constants/number.js'
import { combineReducers } from 'redux'

const text =  function (state = actionTypes.TEXT, action){
    switch(action.type) {
        case actionTypes.TEXT :
            return action.data.text
        default:
            return state
    }
}

const count =  function (state = actionTypes.COUNT, action){
    switch(action.type) {
        case actionTypes.COUNT :
            return action.data.count
        default:
            return state
    }
}

export default combineReducers({
    text,
    count
})

constants 中新建 number.js

constants > number.js 文件

export const COUNT = 100;
export const TEXT = 'number大類下的數據';

修改 containers 中的 Home.js (稍微把home組件改成使用redux的形式)

containers > Home.js 文件

import React, { Component } from 'react';
import {connect} from 'react-redux';
class Home extends Component {
    render() {
        return (
            <div>
                Home <br/>
                {this.props.text} ==> {this.props.count}
            </div>
        );
    }
}

// 如果只是讀取數據,那么只需要這一個方法即可。(這里兩個寫出來只是為了方便理解)
function mapStateToProps(state){
    return {
        count: state.number.count,
        text: state.number.text
    }
}
// 同理,如果只是設置數據,那么只需要這一個方法即可。
function mapDispatchToProps(dispatch) {
    return {

    }
}
export default connect(
    mapStateToProps,
    mapDispatchToProps
)(Home)

修改 src 中的 index.js (給以前看的本博客的小伙伴們說聲道歉,漏了這一步,現在補上。這一步是給store增加入口)

src > index.js 文件

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import {Provider} from 'react-redux'
import configStore from './store/configStore.js'

const store = configStore();

ReactDOM.render(
    <Provider store={store}>
        <App/>
    </Provider>,
    document.getElementById('root'));
registerServiceWorker();

做到這里,應該已經實現了第一步,可以讀取數據了。 如圖:

讀取數據實現了,接下來肯定要實現設置數據。

containers 中的新建 TestRedux.js

containers > TestRedux.js 文件

import React, { Component } from 'react';
import {bindActionCreators} from 'redux'
import {connect} from 'react-redux'
import * as handlerNumberActions from '../actions/number.js'

class TestRedux extends Component {
    textChange(event){
        this.props.handlerNumberActions.changeText({
            text: event.target.value
        })
    }
    numberAdd(){
        this.props.handlerNumberActions.addNumber({
            count: this.props.count
        })
    }
    numberSubtract(){
        this.props.handlerNumberActions.subtractNumber({
            count: this.props.count
        })
    }
    
    
    render() {
        return (
            <div style={{height:'100px',background: '#ffc',padding: '10px'}}>
                <ul>
                    <li>修改redux數據</li>
                    <li>修改文本: <input type="text" style={{padding: '5px 10px'}} value={this.props.text} onChange={this.textChange.bind(this)} /></li>
                    <li>操作數值:
                        <button style={{padding: '5px 10px'}} onClick={this.numberAdd.bind(this)}>+</button>
                        -------------------
                        <button style={{padding: '5px 10px'}} onClick={this.numberSubtract.bind(this)}>-</button>
                    </li>
                </ul>
            </div>
        );
    }
}

function mapStateToProps(state){
    return {
        count: state.number.count,
        text: state.number.text
    }
}
function mapDispatchToProps(dispatch) {
    return {
        handlerNumberActions: bindActionCreators(handlerNumberActions, dispatch)
    }
}
export default connect(
    mapStateToProps,
    mapDispatchToProps
)(TestRedux)

actions 中的新建 number.js

actions > number.js 文件

import * as actionTypes from '../constants/number.js'

export function addNumber(data){
    data.count ++;
    return {
        type: actionTypes.COUNT,
        data
    }
}
export function subtractNumber(data){
    data.count --;
    return {
        type: actionTypes.COUNT,
        data
    }
}

export function changeText(data){
    return {
        type: actionTypes.TEXT,
        data
    }
}

修改 src 中的 App.js (把剛寫好的TestRedux組件添加進去)

src > App.js 文件

import React, { Component } from 'react';
import {BrowserRouter as Router, Route, Link} from 'react-router-dom';
import List from './containers/List.js'
import Home from './containers/Home.js'
import Mine from './containers/Mine.js'
import TestRedux from "./containers/TestRedux"
import './App.css'
import logo from './logo.svg'

class App extends Component {
    render() {
        return (
            <div className="App">
                <div className="App-header">
                    <img src={logo} className="App-logo" alt="logo" />
                    <h2 className='App-title'>Welcome to React Plan</h2>
                </div>
                <div className="App-content">
                    {/*路由配置*/}
                    <Router>
                        <div className="content-box">
                            {/*編寫導航*/}
                            <ul className="nav">
                                <li><Link to="/">首頁</Link></li>
                                <li><Link to="/list">列表頁</Link></li>
                                <li><Link to="/mine/mine1">我的頁面二級路由</Link></li>
                                {/*link指向二級路由的默認頁面*/}
                            </ul>
                            {/*路由匹配*/}
                            <div className="content">
                                <Route exact path="/" component={Home}/>
                                <Route path="/list" component={List}/>
                                <Route path="/mine" component={Mine}/>
                            </div>
                        </div>
                    </Router>
                </div>
                <TestRedux />
            </div>
        );
    }
}

export default App

至此,完整的redux就徹底的配置完畢了。點一點,或者修改一下文本試試吧。
目錄結構和效果如下圖:


第四步 (配置fetch)

fetch是什么呢?fetch其實是 XMLHttpRequest 的替代品,比起 XMLHttpRequest 這種粗糙的東西,fetch顯然看起來精致多了,最棒的是fetch 是基於 Promise 的,讓我們擺脫了地獄回調的噩夢。下面我們就把fetch 簡單封裝一下。方便在react中使用。
1、安裝依賴

yarn add whatwg-fetch es6-promise --save

2、新建文件 封裝 fetch 方法

src 目錄下的新建 fetch 目錄, 在 fetch 文件下新建 index.js 封裝 fetch

fetch > index.js 文件

import 'whatwg-fetch'
import 'es6-promise'

export function get(url) {
    let result = fetch(url, {
        credentials: 'include',
        headers: {
            'Access-Control-Allow-Origin': '*',
            'Accept': 'application/json, text/plain, */*'
        },
        // 設置允許cors跨域
        mode: 'cors'
    });
    return result;
}

// 將對象拼接成 key1=val1&key2=val2&key3=val3 的字符串形式
function obj2params(obj) {
    let result = '';
    let item;
    for (item in obj) {
        result += '&' + item + '=' + encodeURIComponent(obj[item]);
    }
    
    if (result) {
        result = result.slice(1);
    }
    
    return result;
}

// 發送 post 請求
export function post(url, paramsObj) {
    let result = fetch(url, {
        method: 'POST',
        credentials: 'include',
        headers: {
            'Accept': 'application/json, text/plain, */*',
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: obj2params(paramsObj)
    });
    
    return result;
}

public 目錄下 新建 mock 目錄 放置 模擬數據。
mock 目錄下 新建 list.json 文件。

public > mock > list.json

{
    "errorNo": 0,
    "message": "success",
    "data": [
        {
            "name": "Kelli",
            "age": 12
        },
        {
            "name": "Vivien",
            "age": 17
        },
        {
            "name": "Jacklyn",
            "age": 19
        },
        {
            "name": "Bobbie",
            "age": 32
        }
    ]
}

修改 src > containers 下的 List.js 文件。

src > containers > List.js

import React, {Component} from 'react';
import {get} from '../fetch/index';

class List extends Component {
    constructor(props) {
        super(props);
        this.state = {}
    }
    componentWillMount() {
        this.getListData()
    }
    getListData() {
        get("./mock/list.json").then((res) => {
            return res.json();
        }).then((json)=>{
            this.setState({
                dataList: json.data
            })
        }).catch(function (err) {
            console.log(err);
        })
    }
    
    render() {
        let _this = this;
        function createListDom() {
            return {
                __html: _this.state.dataList && _this.state.dataList.map( item => {
                    return '<li>name: '+ item.name + ',age: '+ item.age +'</li>'
                }).join('')
            };
        }
        return (
            <div>
                List <br/>
                <ul dangerouslySetInnerHTML={createListDom()} />
            </div>
        );
    }
}

export default List;

這是只是簡單的測試了get 方法,至於post 請求,有興趣的大家可以私下去測試。
配置完成如下圖:

第五步 (配置sass)

在高版本的create-react-app中去掉了支持sass的功能,如果我們還想用只能自己配置,需要改動配置文件。
1、安裝依賴

yarn add node-sass sass-loader --save-dev

2、修改配置

node_modules/react-scripts/config 下找到 webpack.config.dev.js 文件,
先在 exclude 中按照規律在后面添加 /.scss$/ ,
然后再在loaders中配置 sass文件規則。

{
    test: /\.scss$/,
    loaders: ['style-loader', 'css-loader', 'sass-loader']
}

改兩個地方的配置之后,sass就配置好了,如圖:

webpack.config.dev.js 是開發環境的配置文件,如果想在生產環境也生效,那么在 webpack.config.prod.js 做同樣配置的修改。

到這里,所有的配置就全部完成了。項目親測可以運行。

作者 HoChine
2018 年 04月 27日
項目演示: http://hochine.cn/demo/react-demo
GitHub地址: https://github.com/HoChine/react-demo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM