前言

最近公司可能要用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. C# 基于Bootstrap的三级联动

    实现效果如图: 一.声明市.县.乡对应的下拉控件select <div class="form-group"> <label class="col-sm ...

  2. spring boot 中active的profile会和标准配置合并吗

    如下图,两个profile配置文件,一个默认的(application.properties),一个是test的. 活跃配置为test. spring.profiles.active=test ste ...

  3. SendMessage 遇到的神坑

    场景 两个进程A和B,需要从A中设置B中的文本框的内容 过程 x.x.x.x. 成功获取了B中的内容,惊喜,离成功更近异步 xxxx ***** ....... x.x.x.x. 大约查找了几百个网页 ...

  4. python--socket/Socketerver并发/udp

    Socketerve并发 基于tcp套接字,关键就是两个循环,一个链接循环,一个通讯循环 Socketserver模块中分两个大类:server类(解决链接问题)和request类(解决通信问题) s ...

  5. TreeMap就这么简单【源码剖析】

    前言 声明,本文用得是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单[源码剖析] Map集合.散列表.红黑树介绍 HashMap就是这么简单[源码剖析] LinkedH ...

  6. SpringMVC(七):@RequestMapping下使用POJO对象绑定请求参数值

    Spring MVC会按照请求参数名和POJO属性名进行自动匹配,自动为该对象填充属性值,支持级联属性. 如:address.city.dept.address.province等. 步骤一:定义Ac ...

  7. 集合之ArrayList的源码分析

    转载请注明出处 一.介绍 对于ArrayList,可以说诸位绝不陌生,可以说是在诸多集合中运用的最多一个类之一,那么它是怎样构成,怎样实现的呢,相信很多人都知道数组构成的,没毛病,如果遇到面试的时候, ...

  8. 用 k8s 管理机密信息 - 每天5分钟玩转 Docker 容器技术(155)

    应用启动过程中可能需要一些敏感信息,比如访问数据库的用户名密码或者秘钥.将这些信息直接保存在容器镜像中显然不妥,Kubernetes 提供的解决方案是 Secret. Secret 会以密文的方式存储 ...

  9. 使用Remix编译和部署以太坊智能合约

      Remix 是一個开源的 Solidity 智能合约开发环境,提供基本的编译.部署至本地或测试网络.执行合约等功能.Solidity 是 以太坊Ethereum 官方设计和支持的开发语言,专门用于 ...

  10. .NET CORE 2.0之 依赖注入在类中获取IHostingEnvironment,HttpContext

    在.NET CORE 中,依赖注入非常常见, 在原先的 HttpContext中常用的server.Mappath已经么有了如下: HttpContext.Current.Server.MapPath ...