美文网首页前端综合专辑
前端:React+React-router+Redux+ant

前端:React+React-router+Redux+ant

作者: 谦君子丶温如玉 | 来源:发表于2019-01-29 11:31 被阅读3次

    创建项目完成图示.png

    然后我们进入项目并启动:

    cd antd-demo-ts //进入项目
    
    npm start //启动应用
    

    此时浏览器会访问 http://localhost:3000/ ,看到 Welcome to React 的界面就算成功了。

    二、测试ant Desgin引用是否成功

    cnpm install antd --save-dev  //使用cnpm安装ant design依赖并写入package.json文件中
    

    安装成功后,我们先修改 src/App.tsx 文件

    import * as React from 'react';
    import Button from 'antd/lib/button';
    import './App.css';
     
    import logo from './logo.svg';
     
    class App extends React.Component {
      public render() {
        return (
          <div className="App">
            <header className="App-header">
              <img src={logo} className="App-logo" alt="logo" />
              <h1 className="App-title">Welcome to React</h1>
            </header>
            <p className="App-intro">
              To get started, edit <code>src/App.tsx</code> and save to reload.
            </p>
            <Button type="primary">AntDesgin按钮</Button>
          </div>
        );
      }
    }
     
    export default App;
    

    然后修改 src/App.css 引入 antd 的样式:

    @import '~antd/dist/antd.css'; //在App.css文件顶部引入
     
    .App {
      text-align: center;
    }
     
    ...
    

    修改完成后 npm start 启动项目查看效果。。

    \color{red}{但是~~,此时你会发现编译报错了,错误信息如下图:}
    error.png

    本菜当时看到这个问题的时候也是一脸懵逼,什么情况呢?上网找答案之后发现原来是ts校验规则的问题,这点我实在非常想要吐槽,import引入居然要按照字母排序,十分不适应,后续还会有更多的ts校验规则的问题,在此就先解决再继续往下进行。

    \color{red}{解决方法:}

    修改项目根目录下 tslint.json 文件

    {
      "extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"],
      "linterOptions": {
        "exclude": [
          "config/**/*.js",
          "node_modules/**/*.ts"
        ]
      },
      "rules": {
          "ordered-imports": false,
          "prefer-const": false,
          "no-console":false,
          "no-debugger":false,
          "await-promise":false,
          "curly":false,
          "no-empty":false,
          "no-for-in-array":false,
          "no-invalid-this":false,
          "no-var-keyword":false,
          "member-access": false,
          "max-classes-per-file":false,
          "prefer-for-of":false,
          "triple-equals":false,
          "no-unused-expression": false,
          "prefer-readonly": false,
          "comment-format":false,
          "no-unnecessary-initializer": false,
          "no-unused-variable": false,
          "object-literal-sort-keys": false,
          "no-string-literal": false,
          "no-default-export":false
      }
    }
    

    将 tslint.json文件内容替换为上述代码即可解决问题,规则解释如下(官方文档 https://palantir.github.io/tslint/rules/ ,参考文档 https://www.cnblogs.com/wyy5552/p/8796695.html):
    以上解决了ts校验规则的问题后,重新运行项目发现编译成功,运行效果图如下:

    run.png
    此时我们成功引用进来了ant Desgin
    三、实际项目中的ant design按需引入的性能优化 run.png
    至此我们对ant Design的引入就告一段落了。
    注:以上参考ant design官方文档 https://ant.design/docs/react/use-in-typescript-cn

    四、修改项目目录

    按照个人习惯,我们新建assets文件夹以便于管理,修改后项目目录如下图所示:
    注:修改完成后,别忘记修改 App.tsx、index.tsx 文件中的import路径哦

    list.png

    五、引入scss\color{red}{(不想使用scss可跳过本步骤)}

    由于本菜喜欢scss的书写方便,由此引入,习惯css的同学可以不使用
    1、安装 sass-loader node-sass 依赖:

    cnpm install add sass-loader node-sass --save-dev //安装 sass-loader node-sass 依赖
    

    \color{red}{但是~~(又来了),此时我们会发现,项目中并没有 webpack.config.js 这个文件,那么这个文件到底在哪呢?稍等,我去截个图。。。}

    source.png
    path.png
    好了,通过上述步骤我们找到了webpack的配置文件,其实我们通过package.json中的start命令可以发现点端倪,其实webpack只是被封装起来了,
    接下来我们就修改 webpack.config.dev.js 文件:
    {
       test: /\.css$/,
       use: [
          ...
       ],
    },
    {
        test: /\.scss$/,
        loaders: ['style-loader', 'css-loader', 'sass-loader'],
    }, //增加scss的规则
    

    修改完成之后我们在 assets 文件夹下创建新的 scss 文件夹,并手动创建 App.scss 文件及 index.scss 文件:

    创建完成后将 App.cssindex.scss 文件中的代码直接复制进对应的scss文件,然后删除css文件夹,操作完成后的项目目录如下:

    \color{red}{注:别忘记修改App.tsx及index.tsx中的引用哦}

    image.png
    上述修改完成后,重启项目发现scss成功引入!

    六、创建Spring-Boot项目并整合Mybatis

    本菜在此就不做详细赘述了,想必用java的同学都会,不会的话参考下面链接:
    1、spring-boot 1.5.*版本项目搭建及整合mybatis (https://blog.csdn.net/winter_chen001/article/details/77249029
    2、spring-boot 2.0+版本项目搭建及整合mybatis (https://blog.csdn.net/Winter_chen001/article/details/80010967

    七、编写登录所需接口

    代码如下(仅贴出controller层~):

    package com.ycgame.controller;
     
    import com.ycgame.model.Result;
    import com.ycgame.model.User;
    import com.ycgame.service.user.UserService;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
     
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;
    import java.util.ArrayList;
    import java.util.List;
     
    import static com.ycgame.utils.SetResultInfo.setResultInfo;
     
    /**
     * Created by Administrator on 2018/7/16.
     */
    @Controller
    @RequestMapping(value = "/user")
    public class UserController {
     
        @Autowired
        private UserService userService;
     
        @ResponseBody
        @PostMapping("/login")
        public Result login(@RequestBody User user, HttpServletRequest request){
            System.out.println(user.getUsername());
            Result result=userService.findByUsername(user);
            if(result.getCode()==0){
                HttpSession session=request.getSession();
                session.setAttribute("user",user);
            }
            return result;
        }
     
        @ResponseBody
        @GetMapping("/loginState")
        public Result loginState(HttpServletRequest request){
            HttpSession session=request.getSession();
            User user=(User)session.getAttribute("user");
            Result result;
            if(user!=null){
                List<Object> dataList=new ArrayList<Object>();
                dataList.add(user);
                result=setResultInfo(0,dataList,"已登录");
            }else{
                result=setResultInfo(103,null,"未登录");
            }
            return result;
        }
     
        @ResponseBody
        @GetMapping("/loginOut")
        public Result loginOut(HttpServletRequest request){
            HttpSession session=request.getSession();
            session.removeAttribute("user");
            Result result=setResultInfo(0,null,null);
            return result;
        }
    }
    

    上述接口功能分别为:登录获取登录状态退出登录

    八、引入React-router及axios

    安装 react-router 依赖,本菜使用的是V4版本,各位可自行选择

    cnpm install react-router-dom --save-dev //cnpm安装react-router-dom(react-router V4版本)
     
    cnpm install @types/react-router-dom --save-dev 
    

    为了实现类似 vue-router 中编程式导航的功能,本菜参考(https://segmentfault.com/a/1190000011137828)的第三种方案(主要刚接触,第一种方法不太会。。而且貌似挺麻烦。。勿喷。。)

    1、安装history依赖
    cnpm install @types/history --save-dev //安装histroy依赖
    
    2、自己创建一个 history.ts/.js 文件,文件类型和位置看个人喜好,代码如下:
    import createHistory from 'history/createBrowserHistory';
     
    export default createHistory();
    

    经过以上步骤,我们的 react-router 就算成功引入了。下面开始引入axios

    cnpm install axios --save-dev //安装axios
    

    \color{red}{(可跳过)}安装完成后,本菜对axios发送请求做了简单的封装(utils.ts),代码如下:

    //utils.ts
     
    import axios from 'axios'
     
    const utils ={
        axiosMethod: (config:any) => {
            axios({
                method: config.method,
                url: config.url,
                params: config.params ? config.params : null,
                data: config.data ? config.data : null,
            }).then(config.callback).catch(config.catch ? config.catch : () => {})
        }
    }
     
    export default utils
    

    \color{red}{注:axios发送请求时,post请求与get请求参数对象名是不一样的,post请求的参数对象为data,而get为params~}

    \color{red}{(可跳过)}本菜为了请求统一管理新建了一个 axiosRequestConfig.ts 文件,代码如下:

    //axiosRequestConfig.ts
     
    const config={
        //登录校验
        doLoginConfig:{
            method:'post',
            url:'/user/login'
        },
        //登录状态验证
        loginStateConfig:{
            method:'get',
            url:'/user/loginState'
        },
        //退出登录
        loginOutConfig:{
            method:'get',
            url:'/user/loginOut'
        }
    }
     
    export default config
    

    经过上述步骤,我们成功引入了react-routeraxios,本菜当前的项目目录如下:

    image.png

    九、配置webpack代理

    由于spring-boot运行在8080端口,而我们的react项目则是3000端口,因此存在跨域问题,那么我们如何配置webpack的代理呢,下面上代码:

    首先按照 第五步 的方法找到 webpackDevServer.config.js 文件(node_modules =>react-scripts-ts =>config=>webpackDevServer.config.js ),修改为以下内容:

    ...
     
    https: protocol === 'https',
        host: host,
        overlay: true,
        historyApiFallback: {
          // Paths with dots should still use the history fallback.
          // See https://github.com/facebookincubator/create-react-app/issues/387.
          disableDotRule: true,
        },
        public: allowedHost,
        proxy: {
          '/user/*': {
            target: 'http://localhost:8080/', //本地后台的地址
            secure: false,
            changeOrigin: true
        },
    },
     
    before(app){
     
    ...
    

    经过上述步骤,我们就能放心的撸代码了,再也不用担心跨域请求的问题啦~~

    十、开始撸码

    1、新建 Login.tsx 文件,文件位置看个人习惯,本菜直接放在src下面,使用ant design表单组件,代码如下:

    //Login.tsx
     
    import * as React from 'react';
    import './assets/scss/Login.scss';
    import utils from './utils/utils'
    import requestConfig from './utils/axiosRequestConfig'
    import { Form, Icon, Input, Button, Checkbox } from 'antd';
    import history from './utils/history';
     
     
    const FormItem = Form.Item;
     
    class LoginForm extends React.Component {
      constructor(props: any) {
        super(props);
        this.state = {
          loginForm: {
            username: '',
            password: ''
          }
        }
        this.handleInputChange = this.handleInputChange.bind(this);
        this.submitForm = this.submitForm.bind(this);
      }
     
      handleInputChange(event: any): void {
        const target = event.target;
        const value = target.value;
        const name = target.name;
        const tempObj = { ...this.state['loginForm'] };
        tempObj[name] = value;
        this.setState({
          loginForm: tempObj
        })
      }
     
      submitForm(e: any) {
        e.preventDefault();
        this.props['form'].validateFields((err: any, values: any) => {
          if (!err) {
            let doLoginConfig = requestConfig.doLoginConfig;
            let config = {
              data: { t: new Date().getTime(), ...this.state['loginForm'] },
              callback: (response: any) => {
                if (response.data.code == 0) {
                  history.push('/home');
                }
              }
            }
            let finalConfig = { ...doLoginConfig, ...config };
            utils.axiosMethod(finalConfig);
          }
        });
      }
     
      render(): any {
        const { getFieldDecorator } = this.props['form'];
        return (
          <Form onSubmit={this.submitForm} className="login-form">
            <FormItem>
              {getFieldDecorator('userName', {
                rules: [{ required: true, message: '请输入用户名', whitespace: true }],
              })(
                <Input name='username' onChange={this.handleInputChange} prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="请输入用户名" />
              )}
            </FormItem>
            <FormItem>
              {getFieldDecorator('password', {
                rules: [{ required: true, message: '请输入密码', whitespace: true }],
              })(
                <Input name='password' onChange={this.handleInputChange} prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="请输入密码" />
              )}
            </FormItem>
            <FormItem>
              {getFieldDecorator('remember', {
                valuePropName: 'checked',
                initialValue: true,
              })(
                <Checkbox style={{ color: 'white' }}>记住我</Checkbox>
              )}
              <a className="login-form-forgot" href="">忘记密码</a>
              <Button type="primary" htmlType="submit" className="login-form-button">
                登录
              </Button>
              <a href="">前往注册</a>
            </FormItem>
          </Form>
        );
      }
    }
     
    const LoginFormComponent = Form.create()(LoginForm);
     
    class Login extends React.Component {
      render() {
        return (
          <div className="loginMain">
            <div className="login">
              <h2 style={{ color: 'white', textAlign: 'center' }}>用户登录</h2>
              <LoginFormComponent />
            </div>
          </div>
        );
      }
    }
     
    export default Login;
    

    2、在assets/scss文件夹下新建 Login.scss 的样式文件,代码如下:

    //Login.scss
     
    h2 {
        font-size: 20px;
    }
     
    .loginMain{
        height: 100%;
        background: url('../images/bg.jpg') no-repeat center center fixed;
        background-size: cover;
    }
     
    .login{
        width:350px;
        height:330px;
        background: rgba($color: #000, $alpha: .4);
        border-radius: 20px;
        margin: auto;
        padding: 25px;
        color: white;
        position: absolute;
        left: 0 ;
        right: 0;
        top:25%;
    }
     
    .login-form {
        max-width: 300px;
    }
     
    .login-form-forgot {
        float: right;
    }
     
    .login-form-button {
        width: 100%;
    }
    

    3、修改 index.tsx 文件,代码如下:

    import * as React from 'react';
    import * as ReactDOM from 'react-dom';
    import App from './App';
    import Login from './Login';
    import registerServiceWorker from './registerServiceWorker';
    import { Router, Route } from 'react-router-dom';
    import history from './utils/history';
    import requestConfig from './utils/axiosRequestConfig'
    import utils from './utils/utils'
     
    import './assets/scss/index.scss';
     
     
    class Model extends React.Component {
      constructor(props: any) {
        super(props);
      }
     
      componentDidMount() {
        const loginStateConfig = requestConfig.loginStateConfig;
        const config = {
          param: { t: new Date().getTime() },
          callback: (response: any) => {
            if (response.data.code == 0) {
              history.push('/home');
            } else {
              history.push('/');
            }
          }
        }
        const finalConfig = { ...loginStateConfig, ...config };
        utils.axiosMethod(finalConfig);
      }
     
      render() {
        return (
          <div style={{ height: '100%' }}>
            <Route exact={true} path="/" component={Login} />
            <Route exact={true} path="/home" component={App} />
          </div>
        )
      }
    }
     
    ReactDOM.render(
      <Router history={history}>
        <Model />
      </Router>
      ,document.getElementById('root') as HTMLElement
    );
    registerServiceWorker();
    

    4、给 App.tsx 增加退出登录按钮,修改后代码如下:

    import './assets/scss/App.scss';
     
    import * as React from 'react';
    import history from './utils/history';
    import requestConfig from './utils/axiosRequestConfig'
    import utils from './utils/utils'
    import { Button, Icon } from 'antd';
    import logo from './assets/images/logo.svg';
     
    class App extends React.Component {
      constructor(props: any) {
        super(props);
        this.loginOutMethod = this.loginOutMethod.bind(this);
      }
     
      loginOutMethod() {
        const loginOutConfig = requestConfig.loginOutConfig;
        const config = {
          param: { t: new Date().getTime() },
          callback: (response: any) => {
            if (response.data.code == 0) {
              history.push('/');
            }
          }
        }
        const finalConfig = { ...loginOutConfig, ...config };
        utils.axiosMethod(finalConfig);
      }
     
      render() {
        return (
          <div className="App">
            <header className="App-header">
              <img src={logo} className="App-logo" alt="logo" />
              <h1 className="App-title">Welcome to React</h1>
            </header>
            <p className="App-intro">
              To get started, edit <code>src/App.tsx</code> and save to reload.
            </p>
            <Button type="primary">AntDesgin按钮</Button>
            <Icon style={{ color: 'green', fontSize: '24px', marginLeft: '50px' }} type="check-circle-o" />
            <br/>
            <Button style={{marginTop:'30px'}} onClick={this.loginOutMethod}>退出登录</Button>
          </div>
        );
      }
    }
     
    export default App;
    

    以上我们的代码部分就暂时撸完了现在我们启动spring-boot项目及npm start运行react项目,效果如下(不会做gif理解一下。。。):

    image.png
    image.png
    输入正确的用户名密码后点击登录会跳转到localhost:3000/home页面(主页),点击退出登录会返回登录页测试效果与我们希望的结果一致。

    结束语

    看到这里就结束了!!?这时候就会有小伙伴说了,Redux被你吃了么?全程没看到啊。。说来惭愧,Redux整合本菜倒是弄得差不多了,然而由于刚接触,后续有空再更新吧。。

    以上就是本菜最近几天的摸索成果,实现起来可能不够优雅,希望各位React大佬勿喷,毕竟我只是一个React的萌新。。。最后希望本文对各位同学入坑React能有所帮助,如果能帮到您,是我的荣幸~~

    \color{red}{最后附上github地址:}https://github.com/xueyecheng/react-antd-ts-demo

    相关文章

      网友评论

        本文标题:前端:React+React-router+Redux+ant

        本文链接:https://www.haomeiwen.com/subject/gusufqtx.html