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");
call与fork:阻塞调⽤和无阻塞调用
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 { //...
}
);