React企业级项目实战1

Generator:生成器函数

Generator函数是ES6提供的一种异步编程解决方案,语法行为和传统函数完全不同。

  • function关键字与函数名直接只有一个*

  • yield定义了内部不同的状态

  • yield表达式只能在Generator函数⾥使用,在其他地方

    会报错

    function* helloWorldGenerator() { 
      yield 'hello';
      yield 'world';
      return 'ending';
    }
    var hw = helloWorldGenerator();//实例化
    
    //执⾏ 
    console.log(hw.next()); 
    console.log(hw.next()); 
    console.log(hw.next());
    console.log(hw.next()); 
    
  • yield表达式后面的表达式,只有当调用next⽅方法、内部指针指向该语句时才会执⾏,因此等于为 JavaScript 提供了⼿手动的“惰性求值”(Lazy Evaluation)的语法功能。

    var a = 0; 
    function* fun() { 
      let aa = yield (a = 1 + 1);
      return aa; 
    }
    console.log("fun0", a); 
    let b = fun();
    console.log("fun", b.next());//注释下这句试试,⽐ 较下前后a的值
    console.log("fun1", a);
    

    由于 Generator 函数返回的遍历器对象,只有调⽤next⽅法 才会遍历下一个内部状态,所以其实提供了一种可以暂停执 ⾏的函数。即yield 表达式就是暂停标志。

⼿动搭建移动端项⽬

Routes
import React from "react";
import {BrowserRouter as Router, Route, Switch, Link} from "react-router-dom";
import PrivateRoute from "./PrivateRoute";
import HomePage from "../pages/HomePage/";
import UserPage from "../pages/UserPage/";
import LoginPage from "../pages/LoginPage/";
import _404 from "../pages/_404/";
export default function Routes(props) {
  return ( 
    <Router>
        <Switch>
            <Route path="/" exact component= {HomePage} />
            {/* <Route path="/user" component= {UserPage} /> */}
            <Route path="/login" component= {LoginPage} />
            <PrivateRoute path="/user"> 
                <UserPage />
            </PrivateRoute>
            <Route component={_404} /> 
        </Switch>
    </Router>
    ); 
}
BasicLayout
import React, {Component} from "react";
import BottomNav from "../../components/BottomNav";
import TopBar from "../../components/TopBar";
import classnames from "classnames";
import "./index.scss";

export default class BasicLayout extends
Component {
  componentDidMount() {
    const {
      title = "商城",  //默认值
      shortIcon = "https://store-images.s- microsoft.com/image/apps.64108.9007199266248398 .f50070aa-ca14-4881-9e29-fb874435dc3d.a620dd2f- 083d-4523-bdd5-d50a527956d4"
    } = this.props;
    document.title = title;
    shortIcon && (document.getElementById("shortIcon").href = shortIcon);
  }
  render() {
    const {children, showTopBar = true, title = "商城", _className} = this.props;
    return (
    <div className={classnames("basicLayout", _className)}>
        {showTopBar && <TopBar title={title}/>}
        <article>{children}</article>
        <BottomNav />
    </div>
); }
}
PrivateRoute
import React, {useState, useEffect} from "react";
import {Redirect, Route} from "react-router- dom";
import {connect} from "react-redux";
export default connect(
  //mapStateToProps
  ({user}) => ({
    isLogin: user.isLogin })
    )(function PrivateRoute({children, isLogin, ...rest}) {
  return (
    <Route
        {...rest} render={({location}) =>
          isLogin ? (
            children
      ):(
      <Redirect to={{pathname: "/login", state: {redirect: location.pathname}}}/>
        )}
    />
);
});
LoginPage
import React, {Component} from "react";
import {Redirect} from "react-router-dom";
import {connect} from "react-redux";
import BasicLayout from "../../layout/BasicLayout/";
import "./index.scss";
export default connect(({user}) => ({user}), {
  login: userInfo => ({type: "loginSaga",payload: userInfo})})(
    class LoginPage extends Component {
      constructor(props) {
        super(props);
        this.state = {name: ""};
      }
      render() {
        const {login, user, location} =
        this.props;
        const {isLogin, loading, err, tip} =user;
        if (isLogin) {
            const {redirect = "/"} = location.state || {};
            return <Redirect to={redirect} />;
        }
        const {name} = this.state;
        return (
            <BasicLayout title="登录" _className="loginPage">
              <h3>LoginPage</h3>
              <input type="text" value={name} 
                  onChange={event =>this.setState({name: event.target.value})} 
              />
              <p className="red">{err.msg}</p>
              <button onClick={() => login({name})}>
                  {loading ? "登录中..." : "登录"} </button>
              <p className="green">{tip.msg}</p>
            </BasicLayout>
        );
      }
})
action/login.js

async 函数是什么?一句话,它就是 Generator 函数的语法糖。

async 函数的实现原理,就是将 Generator 函数和⾃动执行器器,包装在⼀个函数⾥。

import LoginService from "../service/login";
export async function loginAction(dispatch, userInfo) 
  dispatch({type: "LOGIN_REQUEST"});
  const res1 = await login(dispatch, userInfo);
	//async、await:上一个请求之后,进行下一个异步请求
  getMoreUserInfo(dispatch, res1);
}
function login(dispatch, userInfo) {
  return LoginService.login(userInfo).then(
    res => {
    return res;
  },
    err => {
      dispatch({type: "LOGIN_FAILURE", payload: err});
    }
  );
}
function getMoreUserInfo(dispatch, userInfo) {
  return
    LoginService.getMoreUserInfo(userInfo).then( res => {
      dispatch({type: "LOGIN_SUCCESS", payload: {...userInfo, ...res}});
      return res;
    },
      err => {
          dispatch({
              type: "LOGIN_FAILURE",
              payload:err
          });
      }
); 
}

redux-saga

  • 概述:redux-saga是⼀个用于管理应⽤程序 Side Effect(副作⽤,例如异步获取数据,访问浏览器缓存等)的 library,它的⽬标是让副作用管理更容易,执⾏更高效,测试更简单,在处理故障时更容易。
  • 地址:https://github.com/redux-saga/redux-saga
  • 安装:npm install --save redux-saga
  • 使⽤:用户登录

redux-saga 的世界里,Sagas 都⽤ Generator 函数实现。我们从 Generator ⾥yield 纯 JavaScript 对象以表达 Saga 逻辑。 我们称呼那些对象为 Effect

你可以使用 redux-saga/effects 包提供的函数来创建 Effect。

effects

effect 是⼀个 javascript 对象,⾥面包含描述副作用的信息,可以通过 yield 传达给 sagaMiddleware 执行。

在 redux-saga 世界里,所有的 effect 都必须被 yield 才会执行,所以有人写了eslint-plugin-redux-saga 来检查是否每个 Effect 都被 yield。并且原则上来说,所有的 yield 后面也只能跟effect,以保证代码的易测性。

put

作⽤和 redux 中的 dispatch 相同。

 yield put({ type: "loginSuccess");
callfork:阻塞调⽤和无阻塞调用

redux-saga 可以用 fork 和 call 来调⽤子 saga ,其中 fork 是⽆无阻塞型调⽤,call 是阻塞型调⽤,即call是有阻塞地调用 saga 或者返回 promise 的函数。

take

等待 redux dispatch 匹配某个 pattern 的 action 。

function* loginSaga(props) {
  // yield takeEvery("login", loginHandle); // 等同于
  const action = yield take("loginSaga"); 
  yield fork(loginHandle, action);
}
takeEvery

takeEvery 可以让多个 saga 任务并⾏被 fork 执⾏。

import {fork, take} from "redux-saga/effects"
const takeEvery = (pattern, saga, ...args) => fork(function*() {
  while (true) {
    const action = yield take(pattern)
    yield fork(saga, ...args.concat(action))
	} 
})

不同于 redux-thunk,你不会再遇到回调地狱了,你可以很容易易地测试异步流程并保持你的 action 是⼲净的,因此我们可以说redux-saga更擅⻓解决复杂异步这样的场景,也更便于测试。

saga的⽅式实现路由守卫

1、创建⼀个./action/userSaga.js处理用户登录请求

  • call:调⽤用异步操作
  • put:状态更更新 (dispatch)
  • takeEvery:做saga监听

action/loginSaga.js

import {
  call,
  fork,
  put,
  take
  // takeEvery
} from "redux-saga/effects";
import LoginService from "../service/login";

// worker saga,接受action参数
function* loginHandle(action) {
// 调⽤异步操作 call
  yield put({type: "LOGIN_REQUEST"}); 
  try {
    const res1 = yield call(LoginService.login, action.payload);
    const res2 = yield call(LoginService.getMoreUserInfo, res1);
    yield put({
      type: "LOGIN_SUCCESS", 
      payload: {...res1, ...res2}
    });
  } catch (err) {
      yield put({
        type: "LOGIN_FAILURE",
        payload:err
      });
    }
}
// watcher saga,监听type值,这里takeEvery也可以写多个值
function* loginSaga() {
  yield takeEvery("loginSaga", loginHandle);
}

export default loginSaga;

const takeEvery = (pattern, saga, ...args) => fork(function*() {
  while (true) {
    const action = yield take(pattern);
    yield fork(saga, ...args.concat(action));
} });

store/index.js:注册redux-saga

import {createStore, combineReducers, applyMiddleware} from "redux";
import thunk from "redux-thunk";
import createSagaMiddleware from "redux-saga";
import {userReducer} from "./userReducer";
import loginSaga from "../action/loginSaga";
const sagaMiddleware = createSagaMiddleware();
const store = createStore( combineReducers({user: userReducer}),
// applyMiddleware(thunk)
  applyMiddleware(sagaMiddleware)
);
sagaMiddleware.run(loginSaga); 

export default store;

LoginPage.js

export default connect(({user}) => ({user}), {
    login: userInfo => ({
      type: "loginSaga",
      payload: userInfo
    })
})(
class LoginPage extends Component { //...
}
);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

椰卤工程师

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

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

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

打赏作者

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

抵扣说明:

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

余额充值