总览篇:react 实战之云书签

源码见最下面

本篇是实战系列的第一篇,主要是搭建 react 开发环境,在create-react-app的基础上加上如下功能:

  • antd 组件库按需引入 ,支持主题定制
  • 支持 less 语法,并使用 css-module
  • 配置路由
  • 支持 http 请求
  • 配置 redux

注意:需要 node 版本大于 8.0.

创建 create-react-app

  1. 安装
npm install -g create-react-app
  1. 创建 react 应用
create-react-app bookmark-world

生成的目录结构如下图所示:

配置 antd,less

有两种方法能够对其配置进行修改:

  • 通过npm run eject暴露出配置文件,然后 修改这些配置文件,相比于下面的方法不太优雅,因此不考虑.
  • 通过react-app-rewired覆盖配置.

后续需要修改配置的都用第二种--覆盖配置。

首先安装依赖

在 2.1.x 版本的 react-app-rewired 需要配合customize-cra来进行配置覆盖。所以需要安装如下依赖:

  • react-app-rewired ,配置覆盖
  • customize-cra ,配置覆盖
  • antd ,ui 库
  • babel-plugin-import ,按需引入 antd
  • less ,less 支持
  • less-loader ,less 支持

代码如下:

npm install --save react-app-rewired customize-cra antd babel-plugin-import less less-loader

修改 package.json

react-app-rewired替换掉原来的react-scripts

/* package.json */
"scripts": {
- "start": "react-scripts start",
+ "start": "react-app-rewired start",
- "build": "react-scripts build",
+ "build": "react-app-rewired build",
- "test": "react-scripts test",
+ "test": "react-app-rewired test",
}

创建 config-overrides.js

在项目根目录,也就是package.json的同级目录创建config-overrides.js文件.内容如下:

const { override, fixBabelImports, addLessLoader } = require("customize-cra");

module.exports = override(
fixBabelImports("import", {
libraryName: "antd",
libraryDirectory: "es",
style: true
}),
addLessLoader({
localIdentName: "[local]--[hash:base64:5]",
javascriptEnabled: true,
modifyVars: { "@primary-color": "#1DA57A" }
})
);

使用 css-module

要使用 css-module 需要将 css 文件命名为fileName.module.less,然后就能在组件中引入并正常使用了,如下:

注意默认情况下后缀必须是.module.less 才能用 css-module 的写法

import React, { Component } from "react";
import { Button } from "antd";
import styles1 from "./index.module.less"; class Hello extends Component {
render() {
return (
<div className={styles1.main}>
hello
<div className={styles1.text}>world</div>
<Button type="primary">你好</Button>
<div className="text1">heihei</div>
</div>
);
}
} export default Hello;

配置路由

首先修改 src 目录结构。改成如下所示:

目录解释:

  • assets: 存放图标,小图片等资源文件
  • components:存放公共组件
  • layout: 存放样式组件,用于嵌套路由和子路由中复用代码
  • pages: 存放页面组件
  • redux:存放 redux 相关
    • action: 存放 action
    • reducer: 存放 reducer 操作
  • util: 工具类

删除serviceWorker.js文件,并在index.js中删除和它相关的代码。这个是和离线使用相关的。

然后安装react-router依赖:

cnpm install --save react-router-dom

从路由开始就能体会到 react 一切都是 js 的精髓,react-router-dom 提供了一些路由组件来进行路由操作。本程序使用history路由。

首先修改index.js根组件放到<BrowserRouter>下,以开启 history 路由。代码如下:

// index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { BrowserRouter } from "react-router-dom"; const s = (
<BrowserRouter>
<App />
</BrowserRouter>
); ReactDOM.render(s, document.getElementById("root"));

然后路由的配置方式有很多种,这里采用代码的方式组织路由,并将将 App.jsx 作为路由配置中心。(也可以基于配置文件,然后写一个解析配置文件的代码)

先加入登录和主页的路由,主要代码如下:


render() {
const mainStyle = {
fontSize: "0.16rem"
};
return (
<Provider store={store}>
<div className="fullScreen" style={mainStyle}>
<Switch>
<Route exact path="/" component={Main} />
<Route exact path="/public/login" component={Login} />
<Route exact path="/404" component={NotFound} />
{/* 当前面的路由都匹配不到时就会重定向到/404 */}
<Redirect path="/" to="/404" />
</Switch>
</div>
</Provider>
);
}

名词解释:

  • Switch: 该组件表示只匹配一个,匹配到后不再继续往下匹配
  • Route:路由组件
  • exact:表示完全匹配,如果开启这个,/只匹配/,否则匹配所有的路径
  • Redirect:重定向组件,当前面的都不匹配就会匹配这个(因为没有开启exact且 path 为/),然后重定向到/404

后续用到嵌套路由时会更加深入的讲解路由相关。

配置 http 请求工具

http 请求工具这里选择的是axios

首先安装依赖:

cnpm install --save axios

然后编写工具类util/httpUtil.js,代码如下:

// httpUtil.js

import { notification } from "antd";
import axios from "axios"; //定义http实例
const instance = axios.create({
// baseURL: "http://ali.tapme.top:8081/mock/16/chat/api/",
headers: {
token: window.token
}
}); //实例添加拦截器
instance.interceptors.response.use(
function(res) {
return res.data;
},
function(error) {
console.log(error);
let message, description;
if (error.response === undefined) {
message = "出问题啦";
description = "你的网络有问题";
} else {
message = "出问题啦:" + error.response.status;
description = JSON.stringify(error.response.data);
//401跳转到登录页面
}
notification.open({
message,
description,
duration: 2
});
setTimeout(() => {
if (error.response && error.response.status === 401) {
let redirect = encodeURIComponent(window.location.pathname + window.location.search);
window.location.replace("/public/login?redirect=" + redirect);
}
}, 1000);
return Promise.reject(error);
}
); export default instance;

主要实现了如下功能:

  • 自动添加 token,设计前后端通过 jwt 做认证,因此每个请求都要加上 token
  • 响应预处理,如果有错误,自动弹窗提示。如果响应码为 401,重定向到登录页面。

配置 redux

redux 算是 react 的一大难点。这里我们可以把 redux 理解成一个内存数据库,用一个对象来存储所有的数据.

对这个数据的修改有着严格的限制,必须通过 reducer 来修改数据,通过 action 定义修改的动作。

这里以用户登录数据为例。

定义

  1. 首先定义 action,创建文件redux/action/loginInfoAction.js,代码如下:
// 定义登录信息在store中的名字
export const DATA_NAME = "loginInfo"; //定义修改loginInfo type
export const CHANGE_LOGIN_INFO = "changeLoginStatus"; export const changeLoginInfo = (token, userInfo) => {
return {
type: CHANGE_LOGIN_INFO,
data: {
token,
userInfo
}
};
};
  • CHANGE_LOGIN_INFO :定义操作类别
  • changeLoginInfo: 定义一个 action,在组件中调用,传入要修改的数据,在这里加上 type 上传递到 reducer 中处理.
  1. 定义 reducer,创建文件redux/reducer/loginInfo.js,代码如下:
import * as loginAction from "../action/loginInfoAction";

function getInitData() {
let token, userInfo;
try {
token = localStorage.getItem("token");
userInfo = JSON.parse(localStorage.getItem("userInfo"));
} catch (e) {
console.error(e);
token = null;
userInfo = null;
}
window.token = token;
window.userInfo = userInfo;
return {
token,
userInfo
};
} const LoginStatusReducer = (state = getInitData(), action) => {
switch (action.type) {
case loginAction.CHANGE_LOGIN_INFO:
return { ...action.data };
default:
return state;
}
}; export default LoginStatusReducer;
  • getInitData 方法用于初始化 userInfo 数据,这里写的比较复杂,会先从 localeStore 中取数据,然后挂载到 window 中,方便httpUtil中获取 token。
  • LoginStatusReducer 方法用于处理 action 中的数据,输出处理后的 loginInfo 数据。
  1. 编写 reducer 汇总类(redux/reducer/index.js),所有 reducer 都要汇总到一个方法中,这样就能生成整个系统的 store 对象。代码如下:
import { combineReducers } from "redux";
import { DATA_NAME } from "../action/loginInfoAction";
import loginInfo from "./loginInfo"; const data = {};
data[DATA_NAME] = loginInfo; const reducer = combineReducers(data); export default reducer;
  1. 编写redux/index.js,这里生成真正的数据对象,代码如下:
import { createStore } from "redux";
import reducer from "./reducer"; const store = createStore(reducer); export default store;
  1. 最后将 store 绑定到根节点(App.js)中即可,修改部分如下:

使用

这里以登录页为例,学习如何获取到 loginInfo 和修改 loginInfo.

  1. 创建登录页组件,pages/public/Login/index.js

    登录页代码如下:
import React, { Component } from "react";
import queryString from "query-string";
import { Button, Input, message } from "antd";
import IconFont from "../../../components/IconFont";
import styles from "./index.module.less";
import { connect } from "react-redux";
import { changeLoginInfo, DATA_NAME } from "../../../redux/action/loginInfoAction";
import axios from "../../../util/httpUtil"; function mapStateToProps(state) {
return state[DATA_NAME];
} function mapDispatchToProps(dispatch) {
return {
updateLoginInfo: (token, userInfo) => dispatch(changeLoginInfo(token, userInfo))
};
} class Login extends Component {
constructor(props) {
super(props);
this.state = {
username: "",
password: ""
};
this.query = queryString.parse(window.location.search);
} usernameInput = e => {
this.setState({ username: e.target.value });
};
passwordInput = e => {
this.setState({ password: e.target.value });
}; submit = () => {
axios.post("/public/login", this.state).then(res => {
localStorage.setItem("token", res.token);
localStorage.setItem("userInfo", JSON.stringify(res.userInfo));
window.token = res.token;
window.userInfo = res.userInfo;
message.success("登录成功");
this.props.updateLoginInfo(res.token, res.userInfo);
if (this.query.redirect) {
this.props.history.replace(decodeURIComponent(this.query.redirect));
} else {
this.props.history.replace("/");
}
});
}; render() {
return (
<div className="fullScreen flex main-center across-center">
// 省略其他部分
<Button type="primary" onClick={this.submit}>
登录
</Button>
...
</div>
);
}
} export default connect(
mapStateToProps,
mapDispatchToProps
)(Login);

其中最关键的是下面三个部分:

  • mapStateToProps:本方法从整个 store 中获取需要的数据,传递到 Login 组件的 props 中。
  • mapDispatchToProps:本方法用于修改 store 数据,返回的函数对象也会绑定到 Login 组件的 props 中,其中的 dispath 参数,用于调用 reducer 中的处理函数,根据 changeLoginInfo 返回的 action。
  • connect 方法用于将上面两个函数和 Login 组件绑定起来,这样就能在 props 中获取到了。如果还有 withRouter,应将 withRouter 放在最外层。

目前登录访问的接口为 yapi 的 mock 数据,真正的后台代码将会在后面编写。

结尾

作为一个刚开始学习 react 的菜鸟,欢迎各位大牛批评指正。

源码:github,切换到 tag:第一篇:环境搭建,便可以看到截止到本篇的源码。

本文原创发布于:www.tapme.top/blog/detail/20190626

从零开始react实战:云书签-1 react环境搭建的更多相关文章

  1. 从零开始react实战:云书签(总览)

    一个合格的全栈开发怎么能不会 react 呢?所以从现在开始系统的学习 react 开发.目标:完成完成一个云书签,包含前后台. 基于create-react-app进行开发,选择这个框架有以下两个原 ...

  2. React Native入门教程 1 -- 开发环境搭建

    有人问我为啥很久不更新博客..我只能说在学校宿舍真的没有学习的环境..基本上在宿舍里面很颓废..不过要毕业找工作了,我要渐渐把这个心态调整过来,就从react-native第一篇博客开始.话说RN也出 ...

  3. React Native在window下的环境搭建(二):创建新项目

    React Native创建一个新项目: react-native init TestAndroidApp 提示:你可以使用--version参数(注意是两个杠)创建指定版本的项目.例如react-n ...

  4. client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

    [本文源址:http://blog.csdn.net/q1056843325/article/details/54729657 转载请加入该地址] 明天就是除夕了 预祝大家新春快乐 [ ]~( ̄▽ ̄) ...

  5. react v16.12 源码阅读环境搭建

    搭建后的代码(Keep updated): https://github.com/lirongfei123/read-react 欢迎将源码阅读遇到的问题提到issue 环境搭建思路: 搭建一个web ...

  6. Guns(开源后台管理系统框架)实战(一)——开发环境搭建

    1. 开发环境搭建 1.1. 开发环境要求 1.2. 配置Maven 1.3. 配置MySQL 1.4. Git克隆项目 1.5. Eclipse导入系统 2. 小结 3. 参考引用 1. 开发环境搭 ...

  7. 阿里云服务器之Tomcat环境搭建以及域名绑定

    上一步主要主要讲解在服务器中建立自己的hexo博客环境,最后达到可以远程访问,以及远程git推送到github.这章主要讲解Tomcat环境的搭建,以及域名解析.到这里你的服务器以及可以被全世界的人民 ...

  8. 阿里云服务器之hexo环境搭建

    上一步主要主要讲解云服务器购买和连接云服务器,以及文件的操作.本文主要讲解利用hexo搭建自己的静态博客,在服务器中建立自己的hexo博客环境,最后达到可以远程访问,以及远程git推送到github. ...

  9. Java Web项目实战第1篇之环境搭建

    写在前面的话 从今天开始一个Java Web实战项目,参考自 http://blog.csdn.net/eson_15/article/details/51277324 这个博客(非常感谢博主的分享精 ...

随机推荐

  1. vi,etc目录文件,环境变量,别名功能

      1 vi命令使用技巧补充 1.1 如何快速编辑文本内容 yy ---快速复制文件内容 3yy ---复制三行内容 p ---快速粘贴文本内容 3p ---粘贴三行内容 dd ---快速删除文件内容 ...

  2. 你需要了解的HTTP协议

    了解HTTP协议 HTTP (超文本传输协议,HyperText Transfer Protocol),是一种用于分布式.协作式和超媒体信息系统的应用层协议.HTTP 是万维网的数据通信基础. 通常, ...

  3. SSM(二)MyBatis多表联查

    这篇文章写了以下几个简单的例子,用来说明MyBatis多标联查基本语法 1.sql片段的用法 2.一对多查询 3.多条sql的一对多查询 4.多对一查询 5.多条sql一对多查询 6.多对多查询 这里 ...

  4. 在CentOS 7上安装Python3

    源码包安装 Python官方没有对Linux打包, 需要下载源码包进行编译. 可以自己下载最新版本的Python, 在Python下载页面, 点击Python3.x.x版本对应的Download, 在 ...

  5. IIS下网站对options请求直接返回404

    什么是options请求 options请求为发送非简单跨域请求前的预检请求,若该请求未正常返回,浏览器会阻止后续的请求发送. 一般情况下,有三种方式会导致浏览器发起预检请求 1.请求的方法不是GET ...

  6. 常用的方法论-5why

  7. 在windowx的Hyper-v 安装CentOS系统

    博客写的很少,一方面是因为我觉得目前很多博客都是相互抄袭,或者有很多部分都是重复的内容.而我自己再去写同样的内容的画,有点浪费时间. 所以,如果我要写,我希望是写一些与众不同,或者重复率比较低的内容, ...

  8. 对于Spring中AOP,DI,IoC概念的理解

    IOC IoC(inversion of Control),控制反转.就好像敏捷开发和SCRUM一样,不是什么技术,而是一种方法论,一种工程化的思想.使用IoC的思想意味着你将设计好的对象交给容器控制 ...

  9. 跟我学SpringCloud | 第十二篇:Spring Cloud Gateway初探

    SpringCloud系列教程 | 第十二篇:Spring Cloud Gateway初探 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 如 ...

  10. scrapy基础知识之 CrawlSpiders:

    通过下面的命令可以快速创建 CrawlSpider模板 的代码: scrapy genspider -t crawl spidername xx.com LinkExtractors class sc ...