前言

最近公司可能要用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. Extensions in UWP Community Toolkit - Mouse Cursor

    概述 UWP Community Toolkit Extensions 中有一个为 Mouse 提供的扩展 - Mouse Cursor Extensions,本篇我们结合代码详细讲解 Mouse C ...

  2. django中图片的上传和显示

    上传图片实际上是 把图片存在服务器的硬盘中,将图片存储的路径存在数据库中. 1 首先要配置文件上传的路径: 1.1 建立静态文件目录 在项目根目录下 新建一个 static文件夹,下面再建立一个med ...

  3. Python之日志 logging模块

    关于logging模块的日志功能 典型的日志记录的步骤是这样的: 创建logger 创建handler 定义formatter 给handler添加formatter 给logger添加handler ...

  4. LinkedHashMap就这么简单【源码剖析】

    前言 声明,本文用得是jdk1.8 前面已经讲了Collection的总览和剖析List集合以及散列表.Map集合.红黑树还有HashMap基础了: Collection总览 List集合就这么简单[ ...

  5. 电脑上的安卓系统——PhoenixOS浅度体验

    前言 其实这篇关于PhoenixOS的浅度评测在几个月前就准备发了,当时是刚看到新闻说Android 7.0 x86的正式版刚刚发布,于是就下载来安装一番,结果.....体验极差= =,只能用这4个字 ...

  6. Java面试题——中级(上)

    List和Set比较,各自的子类比较 对比一:Arraylist与LinkedList的比较 1.ArrayList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高 ...

  7. C++的三大特性:封装、继承和多态性的详解

    封装 所谓封装就是将某些东西包装盒隐藏起来,让外界无法直接使用,只能通过某些特定的方式才能访问.封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是通过外部接口以及特定的访问权限来使 ...

  8. python3全栈开发-异常处理

    一. 什么是异常 异常就是程序运行时发生错误的信号(在程序出现错误时,则会产生一个异常,若程序没有处理它,则会抛出该异常,程序的运行也随之终止),在python中,错误触发的异常如下 而错误分成两种 ...

  9. java中的数组概念

    数组的定义形式: 动态初始化方式: 1.声明并开辟数组 String str[]=new String[3];//3表示数组的长度 2.分布完成 String str[]=null; str=new ...

  10. [LeetCode] Super Washing Machines 超级洗衣机

    You have n super washing machines on a line. Initially, each washing machine has some dresses or is ...