前言

最近公司可能要用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

新手级配置 react react-router4.0 redux fetch sass的更多相关文章

  1. react+webpack+babel+eslint+redux+react-router+sass 环境快速搭建

    本文中的例子支持webpack-dev-server自动刷新及react热替换,使用了redux管理state,用react-router切换路由,用babel实现ES6语法书写,同时支持async/ ...

  2. STL——空间的配置和释放std::alloc(第一级配置器和第二级配置器)

    1 空间的配置和释放,std::alloc 对象构造前的空间配置和对象析构后的空间释放,由<stl_alloc.h>负责,SGI对此的设计哲学如下: 向system heap要求空间 考虑 ...

  3. react、react-router、redux 也许是最佳小实践1

    小前言 这是一个小小的有关react的小例子,希望通过一个小例子,可以让新手更好的了解到react.react-router4.0.redux的集中使用方法. 这是基于create-react-app ...

  4. (转)2019年 React 新手学习指南 – 从 React 学习线路图说开去

    原文:https://www.html.cn/archives/10111 注:本文根据 React 开发者学习线路图(2018) 结构编写了很多新手如何学习 React 的建议.2019 年有标题党 ...

  5. webpack 配置 (支持 React SCSS ES6 编译打包 和 模块热更新 / 生成SourceMap)

    1.首先是目录结构 |-node_modules/ #包文件 |-build/ #静态资源生成目录 |-src/ #开发目录 |-js/ |-index.js #入口文件 |-app.js #Reac ...

  6. [React] react+redux+router+webpack+antd环境搭建一版

    好久之前搭建的一个react执行环境,受历史影响是webpack3.10.0和webpack-dev-server2.7.1的环境,新项目准备用webpack4重新弄弄了,旧的记录就合并发布了(在没有 ...

  7. React Router 4.0 体验

    React Router 4.0 (以下简称 RR4) 已经正式发布,它遵循React的设计理念,即万物皆组件.所以 RR4 只是一堆 提供了导航功能的组件(还有若干对象和方法),具有声明式(声明式编 ...

  8. 【前端】react学习阶段总结,学习react、react-router与redux的这些事儿

    前言 借用阮一峰的一句话:真正学会 React 是一个漫长的过程. 这句话在我接触react深入以后,更有感触了.整个react体系都是全新的,最初做简单的应用,仅仅使用react-tools打包js ...

  9. 如何优雅地在React项目中使用Redux

    前言 或许你当前的项目还没有到应用Redux的程度,但提前了解一下也没有坏处,本文不会安利大家使用Redux 概念 首先我们会用到哪些框架和工具呢? React UI框架 Redux 状态管理工具,与 ...

随机推荐

  1. SSO的全方位解决方案 - Kerberos协议(RFC 1510)

    一.桌面SSO和WEB-SSO的局限性 前面我们的解决方案(桌面SSO和WEB-SSO)都有一个共性:要想将一个应用集成到我们的SSO解决方案中,或多或少的需要修改应用程序. Web应用需要配置一个我 ...

  2. ubuntu下创建python的虚拟环境

    当我们在同一个机器上进行开发多个项目,每个项目于用到包的不同版本的时候,就很尴尬. 安装python包的命令是: sudo pip install 包名 这样的话,会将包安装到/usr/local/l ...

  3. pandas.read_csv参数详解

    读取CSV(逗号分割)文件到DataFrame 也支持文件的部分导入和选择迭代 更多帮助参见:http://pandas.pydata.org/pandas-docs/stable/io.html 参 ...

  4. .Net中Web增加加密狗管理

    由于业务中最近需要使用到加密狗,增加对Web代码的管控,所以需要进行加密狗使用的研究 首先,对于没有接触使用过加密狗的人需要有个大致的认识,加密狗分为 MasterDog, 1.下载加密狗的开发套件, ...

  5. 复习HTML+CSS(6)

    n  表格和表单的嵌套顺序 n  单行文本域 语法格式:<input type="text" 属性="值"> 常用属性 l  Name:文本框的名字 ...

  6. 关于div包裹img,底下多出3px间隙的问题

    背景:昨天写过一个div包裹图片的html,已经reset了所有的div,但还是发现img与div底部会有3px的间距,我检查了所有的css,发现并未发现什么问题,结果度娘了一下,发现好多朋友都遇到了 ...

  7. [LeetCode] Zuma Game 祖玛游戏

    Think about Zuma Game. You have a row of balls on the table, colored red(R), yellow(Y), blue(B), gre ...

  8. [HAOI 2009]逆序对数列

    Description 对于一个数列{ai},如果有i<j且ai>aj,那么我们称ai与aj为一对逆序对数.若对于任意一个由1~n自然数组成的 数列,可以很容易求出有多少个逆序对数.那么逆 ...

  9. [IOI2007]训练路径

    Description 马克(Mirko)和斯拉夫克(Slavko)正在为克罗地亚举办的每年一次的双人骑车马拉松赛而紧张训练.他们需要选择一条训练路径. 他们国家有N个城市和M条道路.每条道路连接两个 ...

  10. 计蒜客NOIP模拟赛(2) D2T3 银河战舰

    [问题描述]    瑞奥和玛德利德是非常好的朋友.瑞奥平时的爱好是吹牛,玛德利德的爱好是戳穿瑞奥吹的牛.    这天瑞奥和玛德利德来到了宇宙空间站,瑞奥向玛德利德炫耀这个空间站里所有的银河战舰都是自己 ...