总览篇:react 实战之云书签javascript
源码见最下面css
本篇是实战系列的第一篇,主要是搭建 react 开发环境,在create-react-app
的基础上加上以下功能:java
注意:须要 node 版本大于 8.0.node
npm install -g create-react-app
复制代码
create-react-app bookmark-world
复制代码
生成的目录结构以下图所示:react
有两种方法可以对其配置进行修改:ios
npm run eject
暴露出配置文件,而后 修改这些配置文件,相比于下面的方法不太优雅,所以不考虑.react-app-rewired
覆盖配置.后续须要修改配置的都用第二种--覆盖配置。git
在 2.1.x 版本的 react-app-rewired 须要配合customize-cra
来进行配置覆盖。因此须要安装以下依赖:github
代码以下:数据库
npm install --save react-app-rewired customize-cra antd babel-plugin-import less less-loader
复制代码
用react-app-rewired
替换掉原来的react-scripts
npm
/* 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",
}
复制代码
在项目根目录,也就是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 文件命名为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 目录结构。改为以下所示:
目录解释:
删除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>
);
}
复制代码
名词解释:
/
只匹配/
,不然匹配全部的路径exact
且 path 为/
),而后重定向到/404
后续用到嵌套路由时会更加深刻的讲解路由相关。
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;
复制代码
主要实现了以下功能:
redux 算是 react 的一大难点。这里咱们能够把 redux 理解成一个内存数据库,用一个对象来存储全部的数据.
对这个数据的修改有着严格的限制,必须经过 reducer 来修改数据,经过 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
}
};
};
复制代码
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;
复制代码
httpUtil
中获取 token。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;
复制代码
redux/index.js
,这里生成真正的数据对象,代码以下:import { createStore } from "redux";
import reducer from "./reducer";
const store = createStore(reducer);
export default store;
复制代码
这里以登陆页为例,学习如何获取到 loginInfo 和修改 loginInfo.
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);
复制代码
其中最关键的是下面三个部分:
目前登陆访问的接口为 yapi 的 mock 数据,真正的后台代码将会在后面编写。
做为一个刚开始学习 react 的菜鸟,欢迎各位大牛批评指正。
源码:github,切换到 tag:第一篇:环境搭建
,即可以看到截止到本篇的源码。
本文原创发布于:www.tapme.top/blog/detail…