总览篇:
源码见最下面
本篇是实战系列的第一篇,主要是搭建 react 开发环境,在create-react-app
的基础上加上如下功能:
注意:需要 node 版本大于 8.0.
npm install -g create-react-app
create-react-app bookmark-world
生成的目录结构如下图所示:
有两种方法能够对其配置进行修改:
npm run eject
暴露出配置文件,然后 修改这些配置文件,相比于下面的方法不太优雅,因此不考虑.react-app-rewired
覆盖配置.后续需要修改配置的都用第二种--覆盖配置。
在 2.1.x 版本的 react-app-rewired 需要配合customize-cra
来进行配置覆盖。所以需要安装如下依赖:
代码如下:
npm install --save react-app-rewired customize-cra antd babel-plugin-import less less-loader
用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", }
在项目根目录,也就是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:第一篇:环境搭建
,便可以看到截止到本篇的源码。
本文原创发布于:
如对本文有疑问, 点击进行留言回复!!
网友评论