umi框架实战项目

29.9React课程

第10节:umi框架实战项目

(第10节:umi框架实战项目&)

第10节:umi框架实战项目&

Generator是异步解决方案,next执行下一个步骤

*可以放在后面也可以放在前面

Yield表示暂停

import React, { Component } from "react";

function* helloGenerator() {
  yield "hello";
  yield "world";
  yield "ending";
}

const hg = helloGenerator();
console.log(hg.next());
console.log(hg.next());
console.log(hg.next());
console.log(hg.next());

class GeneratorPage extends Component {
  render() {
    return <h1>GeneratorPage</h1>;
  }
}

export default GeneratorPage;
import React from "react";
import logo from "./logo.svg";
// import GeneratorPage from "./pages/GeneratorPage";
import ThunkPage from "./pages/ThunkPage";

function App() {
  return (
    <div className="App">
      {/* <GeneratorPage /> */}
      <ThunkPage />
    </div>
  );
}

export default App;

React-redux使用redux更加react,Redux-thunk支持dispatch传递函数,支持异步解决方案

import React, { Component } from "react";
import { BrowserRouter, Route, Link, Switch } from "react-router-dom";

import LoginPage from "./LoginPage";
import UserPage from "./UserPage";
import PrivatePage from "./PrivatePage";

class ThunkPage extends Component {
  render() {
    return (
      <div>
        <h1>Rudex - react-redux</h1>
        <BrowserRouter>
          <Link to="/login">登录</Link>
          <Link to="/user">用户中心</Link>
          <Switch>
            <Route path="/login" component={LoginPage} />
            {/* <Route path="/user" component={UserPage} /> */}
            <PrivatePage path="/user" component={UserPage} />
          </Switch>
        </BrowserRouter>
      </div>
    );
  }
}

export default ThunkPage;

LoginPage.js,通过thunk中间件发送异步请求

import React, { Component } from "react";

import { Redirect } from "react-router-dom";

import { connect } from "react-redux";

export default connect(
  state => ({
    isLogin: state.isLogin,
    loading: state.loading,
    error: state.error
  }),
  {
    //thunk
    // login: () => dispatch => {
    //   dispatch({ type: "requestLogin" });
    //   setTimeout(() => {
    //     dispatch({
    //       type: "requestSuccess"
    //     });
    //   }, 2000);
    // }
    //saga
    login: name => ({ type: "login", name })
  }
)(
  class LoginPage extends Component {
    constructor(props) {
      super(props);
      this.state = {
        name: ""
      };
    }
    setName = event => {
      this.setState({
        name: event.target.value
      });
    };
    render() {
      const { isLogin, loading, login, error } = this.props;

      if (isLogin) {
        return <Redirect to="/user" />;
      }

      const { name } = this.state;
      return (
        <div>
          <h1>登录</h1>
          {error && <p>{error}</p>}
          <input value={name} onChange={this.setName} />
          <button
            onClick={() => {
              login(name);
            }}
          >
            {loading ? "登录中..." : "登录"}
          </button>
        </div>
      );
    }
  }
);

UserPage.js

import React, { Component } from "react";

export default class UserPage extends Component {
  render() {
    return (
      <div>
        <h1>用户中心</h1>
      </div>
    );
  }
}

PrivatePage.js 路由守卫权限判断,可以传递path和component以及isLogin

传入动态值

import React, { Component } from "react";
import { Route, Redirect } from "react-router-dom";

import { connect } from "react-redux";

export default connect(state => ({
  isLogin: state.isLogin
}))(
  class PrivatePage extends Component {
    render() {
      const { path, component, isLogin } = this.props;

      if (isLogin) {
        return <Route path={path} component={component} />;
      }
      return (
        <Redirect to={{ pathname: "/login", state: { redirect: path } }} />
      );
    }
  }
);

store/index.js

import { createStore, applyMiddleware } from "redux";

import thunk from "redux-thunk";

//引入saga
import createSagaMiddleware from "redux-saga";

import mySaga from "./mySaga";

//创建中间件
const sagaMiddleware = createSagaMiddleware();

const initialLogin = {
  isLogin: false,
  loading: false,
  username: "",
  error: ""
};

const loginReducer = (state = { ...initialLogin }, action) => {
  switch (action.type) {
    case "requestLogin":
      return {
        ...state,
        loading: true
      };
    case "requestSuccess":
      return {
        ...state,
        isLogin: true,
        loading: false
      };
    case "requestFailure":
      return {
        ...state,
        error: action.err
      };
    default:
      return state;
  }
};

// const store = createStore(loginReducer, applyMiddleware(thunk));
const store = createStore(loginReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(mySaga);

export default store;

index.js

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";

import { Provider } from "react-redux";
import store from "./store";

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

Redux-saga解决异步请求问题

This.props获取组件属性渲染哪一个组件,重定向动态路径的路由守卫,没有登录跳转到登录页。

对Route的包装

mySaga.js,takeEvery定制任务单,任务单有login任务菜户监控执行loginHandle

action是用户传入的,login(name),返回的res与err action都可以拿到

import { call, put, takeEvery } from "redux-saga/effects";

const Userservice = {
  login(name) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (name === "kaikeba") {
          resolve({ name: "kaikeba" });
        } else {
          reject("用户名或密码错误");
        }
      }, 1500);
    });
  }
};

//worker

function* loginHandle(action) {
  try {
    yield put({ type: "requestLogin" });
    const res = yield call(Userservice.login, action.name);
    yield put({ type: "requestSuccess", res });
  } catch (err) {
    yield put({ type: "requestFailure", err });
  }
}

//watcher saga

function* mySaga() {
  yield takeEvery("login", loginHandle);
}

export default mySaga;
import { createStore, applyMiddleware } from "redux";

import thunk from "redux-thunk";

//引入saga
import createSagaMiddleware from "redux-saga";

import mySaga from "./mySaga";

//创建中间件
const sagaMiddleware = createSagaMiddleware();

const initialLogin = {
  isLogin: false,
  loading: false,
  username: "",
  error: ""
};

const loginReducer = (state = { ...initialLogin }, action) => {
  switch (action.type) {
    case "requestLogin":
      return {
        ...state,
        loading: true
      };
    case "requestSuccess":
      return {
        ...state,
        isLogin: true,
        loading: false
      };
    case "requestFailure":
      return {
        ...state,
        error: action.err
      };
    default:
      return state;
  }
};

// const store = createStore(loginReducer, applyMiddleware(thunk));
const store = createStore(loginReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(mySaga);

export default store;

Switch独占路由

入口引入

路由守卫是对route的包装,没有登录跳转到登录页,this.props可以拿到path

动态值,可以根据其他路径重定向

Redux ,createStore中接收reducers,根据action不同返回不同state

初始化状态

发送请求等待结果,改变isLogin

初始化状态

根据action的不同返回不同的state

把默认值传递给state,发送请求改变loading状态

Export default 组件中使用redux

状态与组件绑定,使用redux

Provider内部通过上下文实现,provider包裹App

属性和dispatch映射,通过connect

导出connect包裹一个组件 ,高阶组件connect就是一个装饰器两个参数mapStateToProps和mapDispatchToProps就可以在组件内部通过this.props获取状态。

绑定State和dispatch,props中有了isLogin和loading。Connect中绑定mapStateToProps和mapDispatchToProps

onClick事件,点击按钮发送dispatch

对应请求,开始requestLogin,接下来发送requestSuccess是异步的

requestLogin同步的,redux没有支持异步能力,需要通过中间件thunk支持异步

点击执行,{}是一个事件,登录进入用户中心页

Thunk支持异步

Dispatch才具有解析函数能力,dispatch有两个操作发送requestLogin和requestSuccess

路由守卫也需要绑定状态,没有dispatch的派发只依赖状态,只需要绑定状态即可。

要从this.props中获取isLogin,标签上没有绑定isLogin,根据不同isLogin去不同页面

Saga支持异步实现方式更加简便,提供effect

Call发送请求,put更新状态,takeEvery定制任务单,任务单有任务执行否则不执行

模拟用户接口,传递数据

Action.name是用户login接口接收到的参数

如果请求有错误try...catch,传递res和err,action都能拿的到

Watcher设置监听,takeEvery任务单有login任务才会去监控

Action只有dispatch发送的时候才生成,

Saga也是redux的中间件

使用中间件

Login接收一个参数,传递name,action就能获取name

使用带参数的login

带参数的login,接收用户参数

保存用户输入的name

mySaga的接口

执行requestFailure,保留报错信息

Store中实现requestFailure

Err是通过mySaga中传递来的

要在loginHandle中传入action参数,

参数的传递,点击登录传递参数name ,用户传递进来的值

通过dispatch订阅的login发送过来login和name,就可以通过action来接收name了

之后进入到saga逻辑中来

Login中会接收到一个参数,进入判断,reject中的信息可以通过err捕获

store中可以通过action接收到err

解构出error

loginHandle是具体的行为,通过任务清单来触发函数

启动中间件,监听任务单里的任务

(umi&)

umi&

快速创建目录结构

指定css类型

创建动态路由

import React from 'react';
import { Router as DefaultRouter, Route, Switch } from 'react-router-dom';
import dynamic from 'umi/dynamic';
import renderRoutes from 'umi/lib/renderRoutes';
import history from '@tmp/history';
import { routerRedux } from 'dva';

const Router = routerRedux.ConnectedRouter;

const routes = [
  {
    path: '/',
    component: require('../../layouts/index.js').default,
    routes: [
      {
        path: '/404',
        exact: true,
        component: require('../404.js').default,
      },
      {
        path: '/about',
        exact: true,
        component: require('../about.js').default,
        title: 'about page',
        Routes: [
          require('../../routes/PrivateRoute.js').default,
          require('../../routes/Test.js').default,
        ],
      },
      {
        path: '/goods',
        exact: true,
        component: require('../goods/index.js').default,
      },
      {
        path: '/goods',
        exact: true,
        component: require('../goods.js').default,
      },
      {
        path: '/',
        exact: true,
        component: require('../index.js').default,
      },
      {
        path: '/login',
        exact: true,
        component: require('../login.js').default,
      },
      {
        path: '/users',
        exact: false,
        component: require('../users/_layout.js').default,
        routes: [
          {
            path: '/users',
            exact: true,
            component: require('../users/index.js').default,
          },
          {
            path: '/users/:id',
            exact: true,
            component: require('../users/$id.js').default,
          },
          {
            component: () =>
              React.createElement(
                require('/Users/kele/.config/yarn/global/node_modules/umi-build-dev/lib/plugins/404/NotFound.js')
                  .default,
                { pagesPath: 'pages', hasRoutesInConfig: false },
              ),
          },
        ],
      },
      {
        path: '/:post/commit',
        exact: true,
        component: require('../$post/commit.js').default,
      },
      {
        path: '/:post',
        exact: true,
        component: require('../$post/index.js').default,
      },
      {
        component: () =>
          React.createElement(
            require('/Users/kele/.config/yarn/global/node_modules/umi-build-dev/lib/plugins/404/NotFound.js')
              .default,
            { pagesPath: 'pages', hasRoutesInConfig: false },
          ),
      },
    ],
  },
  {
    component: () =>
      React.createElement(
        require('/Users/kele/.config/yarn/global/node_modules/umi-build-dev/lib/plugins/404/NotFound.js')
          .default,
        { pagesPath: 'pages', hasRoutesInConfig: false },
      ),
  },
];
window.g_routes = routes;
const plugins = require('umi/_runtimePlugin');
plugins.applyForEach('patchRoutes', { initialValue: routes });

export { routes };

export default class RouterWrapper extends React.Component {
  unListen() {}

  constructor(props) {
    super(props);

    // route change handler
    function routeChangeHandler(location, action) {
      plugins.applyForEach('onRouteChange', {
        initialValue: {
          routes,
          location,
          action,
        },
      });
    }
    this.unListen = history.listen(routeChangeHandler);
    routeChangeHandler(history.location);
  }

  componentWillUnmount() {
    this.unListen();
  }

  render() {
    const props = this.props || {};
    return <Router history={history}>{renderRoutes(routes, props)}</Router>;
  }
}

$id.js,通过props获取动态路由的值

import styles from "./$id.css";

export default function(props) {
  const { match } = props;
  console.log(match);
  return (
    <div className={styles.normal}>
      <h1>Page $id:: {match.params.id}</h1>
    </div>
  );
}

嵌套路由

_layout.js


import styles from './_layout.css';

export default function(props) {
  return (
    <div className={styles.normal}>
      <h1>Layout for ./users</h1>
      {
        props.children
      }
    </div>
  );
}

页面跳转


import styles from './index.css';

export default function() {
  return (
    <div className={styles.normal}>
      <h1>Page index</h1>
    </div>
  );
}

index.js


import styles from './index.css';

export default function() {
  return (
    <div className={styles.normal}>
      <h1>Page index</h1>
    </div>
  );
}

dva仓库要有namespace,export导出一个对象,接收状态要通过namespace来区分。

Reducers管理状态返回最新的状态,接收state和action,effects异步操作不能直接修改state通过reducer来修改state。

Namespace根据模块来区分创建很多的状态。

Action是触发器,通过type属性匹配到reducer或effec,playload属性则是数据体,传递参数给reducer和effect

(创建umi项目&)

创建umi项目&

dva使用model,dva内置connect,把状态映射到组件中。

Connect第一个参数来映射属性的,mapStateToProps,返回一个对象。

取到model中的goods,通过state.goods获取属性值。State是所有的状态集。

goodList映射到props中,通过props直接解构{goodList},打印数组。

创建新的namespace

更新数据状态,解构当前state接收新的另外新的对象title,通过action.payload参数传递

组件中映射addGoods,通过绑定dispatch获得,要接收一个参数,返回action即可。

Payload传递参数

addGoods要解析出来,function({addGoods}),直接使用addGoods即可。

addGoods发送dispatch在Model的reducers接收state和action,返回对象的更新

请求接口,通过effect进行处理。

Result就是数据

getList参数结构出来动作action和call与put

Axios发送请求

Call拿到结果通过put来更新数据。Action对应一个reducers来更新state,payload接收过来的参数就是res拿到的数据。

初始状态更新返回的action.payload,就是res的payload

函数式组件,组件渲染前就进行渲染需要通过hook实现

调用getList,通过命名空间调用effect中的方法

函数式组件,接收回调,执行就可以了

Dva-loading内置加载状态,直接把loading映射到组件上即可。

获取loading来使用,如果有goods命名空间加载,加载完毕置空

开启antd

基本布局,所有的子组件通过props放到content中,共同用于导航与页尾

404页面,exception已经保证好了样式

登录校验组件

Connect包裹函数组件

登录失败

通知组件,根据不同状态码弹窗

 

 

 

 

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wespten

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值