经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » HTML/CSS » HTML5 » 查看文章
从零开始react实战:云书签-1 react环境搭建
来源:cnblogs  作者:烦嚣的人  时间:2019/8/2 8:48:39  对本文有异议

总览篇:react 实战之云书签

源码见最下面

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

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

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

创建 create-react-app

  1. 安装
  1. npm install -g create-react-app
  1. 创建 react 应用
  1. 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 支持

代码如下:

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

修改 package.json

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

  1. /* package.json */
  2. "scripts": {
  3. - "start": "react-scripts start",
  4. + "start": "react-app-rewired start",
  5. - "build": "react-scripts build",
  6. + "build": "react-app-rewired build",
  7. - "test": "react-scripts test",
  8. + "test": "react-app-rewired test",
  9. }

创建 config-overrides.js

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

  1. const { override, fixBabelImports, addLessLoader } = require("customize-cra");
  2. module.exports = override(
  3. fixBabelImports("import", {
  4. libraryName: "antd",
  5. libraryDirectory: "es",
  6. style: true
  7. }),
  8. addLessLoader({
  9. localIdentName: "[local]--[hash:base64:5]",
  10. javascriptEnabled: true,
  11. modifyVars: { "@primary-color": "#1DA57A" }
  12. })
  13. );

使用 css-module

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

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

  1. import React, { Component } from "react";
  2. import { Button } from "antd";
  3. import styles1 from "./index.module.less";
  4. class Hello extends Component {
  5. render() {
  6. return (
  7. <div className={styles1.main}>
  8. hello
  9. <div className={styles1.text}>world</div>
  10. <Button type="primary">你好</Button>
  11. <div className="text1">heihei</div>
  12. </div>
  13. );
  14. }
  15. }
  16. export default Hello;

配置路由

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

目录结构

目录解释:

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

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

然后安装react-router依赖:

  1. cnpm install --save react-router-dom

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

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

  1. // index.js
  2. import React from "react";
  3. import ReactDOM from "react-dom";
  4. import "./index.css";
  5. import App from "./App";
  6. import { BrowserRouter } from "react-router-dom";
  7. const s = (
  8. <BrowserRouter>
  9. <App />
  10. </BrowserRouter>
  11. );
  12. ReactDOM.render(s, document.getElementById("root"));

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

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

  1. render() {
  2. const mainStyle = {
  3. fontSize: "0.16rem"
  4. };
  5. return (
  6. <Provider store={store}>
  7. <div className="fullScreen" style={mainStyle}>
  8. <Switch>
  9. <Route exact path="/" component={Main} />
  10. <Route exact path="/public/login" component={Login} />
  11. <Route exact path="/404" component={NotFound} />
  12. {/* 当前面的路由都匹配不到时就会重定向到/404 */}
  13. <Redirect path="/" to="/404" />
  14. </Switch>
  15. </div>
  16. </Provider>
  17. );
  18. }

名词解释:

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

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

配置 http 请求工具

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

首先安装依赖:

  1. cnpm install --save axios

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

  1. // httpUtil.js
  2. import { notification } from "antd";
  3. import axios from "axios";
  4. //定义http实例
  5. const instance = axios.create({
  6. // baseURL: "http://ali.tapme.top:8081/mock/16/chat/api/",
  7. headers: {
  8. token: window.token
  9. }
  10. });
  11. //实例添加拦截器
  12. instance.interceptors.response.use(
  13. function(res) {
  14. return res.data;
  15. },
  16. function(error) {
  17. console.log(error);
  18. let message, description;
  19. if (error.response === undefined) {
  20. message = "出问题啦";
  21. description = "你的网络有问题";
  22. } else {
  23. message = "出问题啦:" + error.response.status;
  24. description = JSON.stringify(error.response.data);
  25. //401跳转到登录页面
  26. }
  27. notification.open({
  28. message,
  29. description,
  30. duration: 2
  31. });
  32. setTimeout(() => {
  33. if (error.response && error.response.status === 401) {
  34. let redirect = encodeURIComponent(window.location.pathname + window.location.search);
  35. window.location.replace("/public/login?redirect=" + redirect);
  36. }
  37. }, 1000);
  38. return Promise.reject(error);
  39. }
  40. );
  41. export default instance;

主要实现了如下功能:

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

配置 redux

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

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

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

定义

  1. 首先定义 action,创建文件redux/action/loginInfoAction.js,代码如下:
  1. // 定义登录信息在store中的名字
  2. export const DATA_NAME = "loginInfo";
  3. //定义修改loginInfo type
  4. export const CHANGE_LOGIN_INFO = "changeLoginStatus";
  5. export const changeLoginInfo = (token, userInfo) => {
  6. return {
  7. type: CHANGE_LOGIN_INFO,
  8. data: {
  9. token,
  10. userInfo
  11. }
  12. };
  13. };
  • CHANGE_LOGIN_INFO :定义操作类别
  • changeLoginInfo: 定义一个 action,在组件中调用,传入要修改的数据,在这里加上 type 上传递到 reducer 中处理.
  1. 定义 reducer,创建文件redux/reducer/loginInfo.js,代码如下:
  1. import * as loginAction from "../action/loginInfoAction";
  2. function getInitData() {
  3. let token, userInfo;
  4. try {
  5. token = localStorage.getItem("token");
  6. userInfo = JSON.parse(localStorage.getItem("userInfo"));
  7. } catch (e) {
  8. console.error(e);
  9. token = null;
  10. userInfo = null;
  11. }
  12. window.token = token;
  13. window.userInfo = userInfo;
  14. return {
  15. token,
  16. userInfo
  17. };
  18. }
  19. const LoginStatusReducer = (state = getInitData(), action) => {
  20. switch (action.type) {
  21. case loginAction.CHANGE_LOGIN_INFO:
  22. return { ...action.data };
  23. default:
  24. return state;
  25. }
  26. };
  27. export default LoginStatusReducer;
  • getInitData 方法用于初始化 userInfo 数据,这里写的比较复杂,会先从 localeStore 中取数据,然后挂载到 window 中,方便httpUtil中获取 token。
  • LoginStatusReducer 方法用于处理 action 中的数据,输出处理后的 loginInfo 数据。
  1. 编写 reducer 汇总类(redux/reducer/index.js),所有 reducer 都要汇总到一个方法中,这样就能生成整个系统的 store 对象。代码如下:
  1. import { combineReducers } from "redux";
  2. import { DATA_NAME } from "../action/loginInfoAction";
  3. import loginInfo from "./loginInfo";
  4. const data = {};
  5. data[DATA_NAME] = loginInfo;
  6. const reducer = combineReducers(data);
  7. export default reducer;
  1. 编写redux/index.js,这里生成真正的数据对象,代码如下:
  1. import { createStore } from "redux";
  2. import reducer from "./reducer";
  3. const store = createStore(reducer);
  4. export default store;
  1. 最后将 store 绑定到根节点(App.js)中即可,修改部分如下:

使用

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

  1. 创建登录页组件,pages/public/Login/index.js
    登录页代码如下:
  1. import React, { Component } from "react";
  2. import queryString from "query-string";
  3. import { Button, Input, message } from "antd";
  4. import IconFont from "../../../components/IconFont";
  5. import styles from "./index.module.less";
  6. import { connect } from "react-redux";
  7. import { changeLoginInfo, DATA_NAME } from "../../../redux/action/loginInfoAction";
  8. import axios from "../../../util/httpUtil";
  9. function mapStateToProps(state) {
  10. return state[DATA_NAME];
  11. }
  12. function mapDispatchToProps(dispatch) {
  13. return {
  14. updateLoginInfo: (token, userInfo) => dispatch(changeLoginInfo(token, userInfo))
  15. };
  16. }
  17. class Login extends Component {
  18. constructor(props) {
  19. super(props);
  20. this.state = {
  21. username: "",
  22. password: ""
  23. };
  24. this.query = queryString.parse(window.location.search);
  25. }
  26. usernameInput = e => {
  27. this.setState({ username: e.target.value });
  28. };
  29. passwordInput = e => {
  30. this.setState({ password: e.target.value });
  31. };
  32. submit = () => {
  33. axios.post("/public/login", this.state).then(res => {
  34. localStorage.setItem("token", res.token);
  35. localStorage.setItem("userInfo", JSON.stringify(res.userInfo));
  36. window.token = res.token;
  37. window.userInfo = res.userInfo;
  38. message.success("登录成功");
  39. this.props.updateLoginInfo(res.token, res.userInfo);
  40. if (this.query.redirect) {
  41. this.props.history.replace(decodeURIComponent(this.query.redirect));
  42. } else {
  43. this.props.history.replace("/");
  44. }
  45. });
  46. };
  47. render() {
  48. return (
  49. <div className="fullScreen flex main-center across-center">
  50. // 省略其他部分
  51. <Button type="primary" onClick={this.submit}>
  52. 登录
  53. </Button>
  54. ...
  55. </div>
  56. );
  57. }
  58. }
  59. export default connect(
  60. mapStateToProps,
  61. mapDispatchToProps
  62. )(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

原文链接:http://www.cnblogs.com/wuyoucao/p/11273965.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号