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


前言

最近公司可能要用react,然而有几个同事历来没用过react,而后领导让我带他们学习react, 为下一个react项目作基础。
而后随手写了几个demo,帮助他们了解一下如何去构建配置项目。
如今分享出来,但愿能够帮助到须要的人。 本demo 中有些目录虽然没有用,可是我仍是列了出来,目的是为了展现一个正规项目的目录骨架结构。
create-react-app 模板文件我也没有归类,等了解以后,能够本身归类,加一个样式的文件夹。css

正文

就目前的大环境而言,在开发react或vue项目的时候,应该没有几我的还直接在html直接饮用react的库文件吧,都是用的前端构建工具webpack + es6 来写项目。因此直接就推荐facebook官方出品的脚手架create-react-app来作项目。 create-react-app是最适合新手来学习使用的脚手架,所有是简单的命令。不须要像别的脚手架同样,先去clone整个项目,而后再安装依赖。html

第一步 (安装工具,生成项目, 启动项目)

安装create-react-app工具前端

npm install -g create-react-app

生成项目vue

create-react-app react-demo

生成项目以后自动会下载依赖所有,
进入项目以后,启动项目node

cd react-demo
npm start 或者 yarn start

npm 和 yarn 都是包管理器, 均可如下载依赖,具体这里不作解释,详情能够本身搜索。一个项目最好用一种包管理工具,不要混用,下面都用yarn来下载依赖。不作另外说明react

项目启动以后打开3000端口。
项目启动成功以下图:webpack


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

一、安装依赖git

yarn add react-router-dom --save

二、新建文件、配置路由
src 目录下 新建 containers 目录 里面放置主页面文件
src 目录下 新建 components 目录 里面放置高复用模板文件
containers 中新建 Home.jses6

Home.js 代码github

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.
一、安装依赖

yarn add redux react-redux --save

二、新建文件, 配置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中使用。
一、安装依赖

yarn add whatwg-fetch es6-promise --save

二、新建文件 封装 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的功能,若是咱们还想用只能本身配置,须要改动配置文件。
一、安装依赖

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

二、修改配置

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

相关文章
相关标签/搜索