新手级配置 react react-router4.0 redux fetch sass
文章出处:HoChine---原文地址
前言
最近公司来了几个实习生,刚好我手头没什么要紧事,然后领导让我带他们学习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、新建文件, 配置reduxsrc
目录下 新建 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)
做到这里,应该已经实现了第一步,可以读取数据了。 如图:
读取数据实现了,接下来肯定要实现设置数据。
在 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 } }
至此,完整的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
做同样配置的修改。
到这里,所有的配置就全部完成了。项目亲测可以运行。