Redux基础使用

//redux引入applyMiddleware

import {

createStore,

applyMiddleware,

} from ‘redux’;

// 通过applyMiddleware来结合多个Middleware, 返回一个enhancer

const enhancer = applyMiddleware(thunkMiddleware);

// 将enhancer作为第二个参数传入到createStore中

const store = createStore(reducer, enhancer);

定义返回一个函数的action:

  • 注意:这里不是返回一个对象了,而是一个函数;

  • 该函数在dispatch之后会被执行;

const getHomeMultidataAction = () => {

return (dispatch) => {

axios.get(“http://123.207.32.32:8000/home/multidata”).then(res => {

const data = res.data.data;

dispatch(changeBannersAction(data.banner.list));

dispatch(changeRecommendsAction(data.recommend.list));

})

}

}

修改home.js中的代码:

import React, { PureComponent } from ‘react’;

import { connect } from “react-redux”;

import {

addAction,

getHomeMultidataAction

} from ‘…/store/actionCreators’;

class Home extends PureComponent {

componentDidMount() {

this.props.getHomeMultidata();

}

…其他逻辑代码

}

…mapStatetoProps

const mapDispatchToProps = dispatch => {

return {

addNumber: function(number) {

dispatch(addAction(number));

},

getHomeMultidata() {

dispatch(getHomeMultidataAction());

}

}

}

export default connect(mapStateToProps, mapDispatchToProps)(Home);

3.redux-devtools

import { createStore, applyMiddleware, compose } from ‘redux’;

import thunkMiddleware from ‘redux-thunk’;

import reducer from ‘./reducer.js’;

const composeEnhancers = window.REDUX_DEVTOOLS_EXTENSION_COMPOSE || compose;

// 通过applyMiddleware来结合多个Middleware, 返回一个enhancer

const enhancer = composeEnhancers(applyMiddleware(thunkMiddleware));

// 将enhancer作为第二个参数传入到createStore中

const store = createStore(reducer, enhancer);

export default store;

trace打开:

const composeEnhancers = window.REDUX_DEVTOOLS_EXTENSION_COMPOSE({trace: true}) || compose;

4.redux-saga

saga中间件使用了ES6的generator语法

数编一个生成器函数:

function *foo() {//生成器generator函数

yield “Hello”;

yield “World”;

}

const iterator = foo();//迭代器iterator

console.log(iterator, typeof iterator); // 一个object类型的iterator对象

调用iterator的next函数,会销毁一次迭代器,并且返回一个yield的结果:

// 调用一次next()是消耗一次迭代器

iterator.next(); // {value: “Hello”, done: false}

iterator.next(); // {value: “World”, done: false}

iterator.next(); // {value: undefined, done: true}

研究一下foo生成器函数代码的执行顺序:

function *foo() {

console.log(“111111”);

yield “Hello”;

console.log(“222222”);

yield “World”;

console.log(“333333”);

}

// 调用一次next()是消耗一次迭代器

iterator.next(); // {value: “Hello”, done: false}

// 打印111111

iterator.next(); // {value: “World”, done: false}

// 打印222222

iterator.next(); // {value: undefined, done: true}

// 打印333333

generatorpromise一起使用:

function *bar() {

const result = yield new Promise((resolve, reject) => {

setTimeout(() => {

resolve(“Hello Generator”);

return “Hello”;

}, 2000);

});

console.log(result);

}

const bIterator = bar();

bIterator.next().value.then(res => {

bIterator.next(res);

});

集成redux-saga中间件

import { createStore, applyMiddleware, compose } from ‘redux’;

import thunkMiddleware from ‘redux-thunk’;

import createSagaMiddleware from ‘redux-saga’;

import reducer from ‘./reducer.js’;

import mySaga from ‘./saga’;

// 通过createSagaMiddleware函数来创建saga中间件

const sagaMiddleware = createSagaMiddleware();

const composeEnhancers = window.REDUX_DEVTOOLS_EXTENSION_COMPOSE({trace: true}) || compose;

// 通过applyMiddleware来结合多个Middleware, 返回一个enhancer

const enhancer = composeEnhancers(applyMiddleware(thunkMiddleware, sagaMiddleware));

// 将enhancer作为第二个参数传入到createStore中

const store = createStore(reducer, enhancer);

// 必须启动saga中间件,并且传入其要监听的generator

sagaMiddleware.run(mySaga);

export default store;

saga.js文件的编写:

  • takeEvery:可以传入多个监听的actionType,每一个都可以被执行(对应有一个takeLastest,会取消前面的)

  • put:在saga中派发action不再是通过dispatch,而是通过put;

  • all:可以在yield的时候put多个action;

import { takeEvery, put, all } from ‘redux-saga/effects’;

import axios from ‘axios’;

import {

FETCH_HOME_MULTIDATA

} from “./constants”;

import {

changeBannersAction,

changeRecommendsAction,

} from ‘./actionCreators’;

function* fetchHomeMultidata(action) {

const res = yield axios.get(“http://123.207.32.32:8000/home/multidata”);

console.log(res);

const data = res.data.data;

yield all([

put(changeBannersAction(data.banner.list)),

put(changeRecommendsAction(data.recommend.list))

])

}

function* mySaga() {

yield takeEvery(FETCH_HOME_MULTIDATA, fetchHomeMultidata)

}

export default mySaga;

5.中间件的原理

打印日志需求

事实上,我们可以利用一个hack一点的技术:Monkey Patching,利用它可以修改原有的程序逻辑;

  • 已经直接修改了dispatch的调用过程;

  • 在调用dispatch的过程中,真正调用的函数其实是dispatchAndLog

let next = store.dispatch;

function dispatchAndLog(action) {

console.log(“dispatching:”, addAction(10));

next(addAction(5));

console.log(“新的state:”, store.getState());

}

store.dispatch = dispatchAndLog;

可以将它封装到一个模块中,只要调用这个模块中的函数,就可以对store进行这样的处理:

function patchLogging(store) {

let next = store.dispatch;

function dispatchAndLog(action) {

console.log(“dispatching:”, action);

next(addAction(5));

console.log(“新的state:”, store.getState());

}

store.dispatch = dispatchAndLog;

}

thunk需求

redux中利用一个中间件redux-thunk可以让我们的dispatch不再只是处理对象,并且可以处理函数

dispatch进行转换,这个dispatch会判断传入的

function patchThunk(store) {

let next = store.dispatch;

function dispatchAndThunk(action) {

if (typeof action === “function”) {

action(store.dispatch, store.getState);

} else {

next(action);

}

}

store.dispatch = dispatchAndThunk;

}

将两个patch应用起来,进行测试:

patchLogging(store);

patchThunk(store);

store.dispatch(addAction(10));

function getData(dispatch) {

setTimeout(() => {

dispatch(subAction(10));

}, 1000)

}

// 传入函数

store.dispatch(getData);

合并中间件

单个调用某个函数来合并中间件并不是特别的方便,我们可以封装一个函数来实现所有的中间件合并:

function applyMiddleware(store, middlewares) {

middlewares = middlewares.slice();

middlewares.forEach(middleware => {

store.dispatch = middleware(store);

})

}

applyMiddleware(store, [patchLogging, patchThunk]);

理解一下上面操作之后,代码的流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zcgX941B-1634980687242)(./images/16.webp)]

三.reducer拆分


1.拆分reducer

先抽取一个对counter处理的reducer:

// counter相关的状态

const initialCounter = {

counter: 0

}

function counterReducer(state = initialCounter, action) {

switch (action.type) {

case ADD_NUMBER:

return { …state, counter: state.counter + action.num };

case SUB_NUMBER:

return { …state, counter: state.counter - action.num };

default:

return state;

}

}

再抽取一个对home处理的reducer:

// home相关的状态

const initialHome = {

banners: [],

recommends: []

}

function homeReducer(state = initialHome, action) {

switch (action.type) {

case CHANGE_BANNER:

return { …state, banners: action.banners };

case CHANGE_RECOMMEND:

return { …state, recommends: action.recommends };

default:

return state;

}

}

如果将它们合并起来呢?

const initialState = {

}

function reducer(state = initialState, action) {

return {

counterInfo: counterReducer(state.counterInfo, action),

homeInfo: homeReducer(state.homeInfo, action),

}

}

reducer文件拆分

已经将不同的状态处理拆分到不同的reducer

对文件结构再次进行拆分:

./store

├── counter

│ ├── actioncreators.js

│ ├── constants.js

│ ├── index.js

│ └── reducer.js

├── home

│ ├── actioncreators.js

│ ├── constants.js

│ ├── index.js

│ └── reducer.js

├── index.js

├── reducer.js

└── saga.js

  • home/actioncreators.js:存放home相关的action;

  • home/constants.js:存放home相关的常量;

  • home/reducer.js:存放分离的reducer代码;

  • index.js:统一对外暴露的内容;

2.combineReducers合并

目前合并的方式是通过每次调用reducer函数自己来返回一个新的对象:

import { reducer as counterReducer } from ‘./counter’;

import { reducer as homeReducer } from ‘./home’;

const initialState = {

}

function reducer(state = initialState, action) {

return {

counterInfo: counterReducer(state.counterInfo, action),

homeInfo: homeReducer(state.homeInfo, action),

}

}

redux提供了一个combineReducers函数可以方便的对多个reducer进行合并:

import { combineReducers } from ‘redux’;

import { reducer as counterReducer } from ‘./counter’;

import { reducer as homeReducer } from ‘./home’;

const reducer = combineReducers({

counterInfo: counterReducer,

homeInfo: homeReducer

})

export default reducer;

combineReducers是如何实现的?

  • 事实上,它也是讲我们传入的reducers合并到一个对象中,最终返回一个combination的函数(相当于我们之前的reducer函数了);

  • 在执行combination函数的过程中,它会通过判断前后返回的数据是否相同来决定返回之前的state还是新的state;

  • 新的state会触发订阅者发生对应的刷新,而旧的state可以有效的组织订阅者发生刷新;

export default function combineReducers(reducers) {

const reducerKeys = Object.keys(reducers)

const finalReducers = {}

for (let i = 0; i < reducerKeys.length; i++) {

const key = reducerKeys[i]

if (process.env.NODE_ENV !== ‘production’) {

if (typeof reducers[key] === ‘undefined’) {

warning(No reducer provided for key "${key}")

}

}

if (typeof reducers[key] === ‘function’) {

finalReducers[key] = reducers[key]

}

}

const finalReducerKeys = Object.keys(finalReducers)

// This is used to make sure we don’t warn about the same

// keys multiple times.

let unexpectedKeyCache

if (process.env.NODE_ENV !== ‘production’) {

unexpectedKeyCache = {}

}

let shapeAssertionError

try {

assertReducerShape(finalReducers)

} catch (e) {

shapeAssertionError = e

}

return function combination(state = {}, action) {

if (shapeAssertionError) {

throw shapeAssertionError

}

if (process.env.NODE_ENV !== ‘production’) {

const warningMessage = getUnexpectedStateShapeWarningMessage(

state,

finalReducers,

action,

unexpectedKeyCache

)

if (warningMessage) {

warning(warningMessage)

}

}

let hasChanged = false

const nextState = {}

for (let i = 0; i < finalReducerKeys.length; i++) {

const key = finalReducerKeys[i]

const reducer = finalReducers[key]

const previousStateForKey = state[key]

const nextStateForKey = reducer(previousStateForKey, action)

if (typeof nextStateForKey === ‘undefined’) {

const errorMessage = getUndefinedStateErrorMessage(key, action)

throw new Error(errorMessage)

}

nextState[key] = nextStateForKey

hasChanged = hasChanged || nextStateForKey !== previousStateForKey

}

hasChanged =

hasChanged || finalReducerKeys.length !== Object.keys(state).length

return hasChanged ? nextState : state

}

}

四.🧡ImmutableJS


数据可变性的问题

const obj = {

name: “why”,

age: 18

}

console.log(obj); // {name: “why”, age: 18}

const obj2 = obj;

obj2.name = “kobe”;

console.log(obj); // {name: “kobe”, age: 18}

有没有办法解决上面的问题呢?

进行对象的拷贝即可:Object.assign或扩展运算符

console.log(obj); // {name: “why”, age: 18}

const obj2 = {…obj};

obj2.name = “kobe”;

console.log(obj); // {name: “why”, age: 18}

1.认识ImmutableJS

为了解决上面的问题,出现了Immutable对象的概念:

  • Immutable对象的特点是只要修改了对象,就会返回一个新的对象,旧的对象不会发生改变;

但是这样的方式就不会浪费内存了吗?

  • 为了节约内存,又出现了一个新的算法:Persistent Data Structure(持久化数据结构或一致性数据结构);

当然,我们一听到持久化第一反应应该是数据被保存到本地或者数据库,但是这里并不是这个含义:

  • 用一种数据结构来保存数据;

  • 当数据被修改时,会返回一个对象,但是新的对象会尽可能的利用之前的数据结构而不会对内存造成浪费;

如何做到这一点呢?结构共享:

  • 如下面的静态图;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2KYlwYGH-1634980687245)(./images/1.gif)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eXV67Z2L-1634980687251)(./images/17.webp)]

2.ImutableJS常见API

const imjs = Immutable;

// 1.定义JavaScript的Array和转换成immutable的List

const friends = [

{ name: “why”, age: 18 },

{ name: “kobe”, age: 30 }

]

// 不会进行深层转换

const imArray1 = imjs.List(friends);

// 会进行深层转换

const imArray2 = imjs.fromJS(friends);

// console.log(imArray1);

// console.log(imArray2);

// 1.定义JavaScript的Object和转换成immutable的Map

const info = {

name: “coderwhy”,

age: 18,

friend: {

name: “kobe”,

age: 30

}

}

const imObj1 = imjs.Map(info);

const imObj2 = imjs.fromJS(info);

// console.log(imObj1);

// console.log(imObj2);

// 3.对immutable操作

// 3.1.添加数据

// 产生一个新的immutable对象

console.log(imArray1.push(“aaaa”));

console.log(imArray1.set(2, “aaaa”));

// 原来的是不变的

console.log(imArray1);

// 3.2.修改数据

console.log(imArray1.set(1, “aaaa”));

console.log(imArray2.set(2, imjs.fromJS(“bbbb”)));

// 3.3.删除数据

console.log(imArray1.delete(0).get(0)); // {name: “kobe”, age: 30}

// 3.4.查询数据

console.log(imArray1.get(1));

console.log(imArray2.get(1));

console.log(imArray1.get(1).name);

console.log(imArray2.getIn([1, “name”]));

// 4.转换成JavaScript对象

const array = imArray1.toJS();

const obj = imObj1.toJS();

console.log(array);

console.log(obj);

ImmutableJS重构redux

3.dva了解

import React from ‘react’;

import dva, { connect } from ‘dva’;

import { Route } from ‘dva/router’;

// 1. Initialize

const app = dva();

// 2. Model

app.model({

namespace: ‘count’,

state: 0,

reducers: {

‘count/add’ { return count + 1 },

‘count/minus’ { return count - 1 },

},

});

// 3. View

const App = connect(({ count }) => ({

count

}))(function(props) {

return (

{ props.count }

<button key=“add” onClick={() => { props.dispatch({type: ‘count/add’})}}>+

<button key=“minus” onClick={() => { props.dispatch({type: ‘count/minus’})}}>-

);

});

// 4. Router

app.router(

);

// 5. Start

app.start(document.getElementById(‘root’));

5 步 4 个接口完成单页应用的编码,不需要配 middleware,不需要初始化 saga runner,不需要 fork, watch saga,不需要创建 store,不需要写 createStore,然后和 Provider 绑定,等等。但却能拥有 redux + redux-saga + … 的所有功能。

五.redux代码片段


1.redux

import { connect } from 'react-redux

2.rxconst

export const constantName = ‘constantName’

3.rxaction

export const actionName = (payload) => ({

type: type,

payload

})

4.rxreducer

const initialState = {

}

export default (state = initialState, { type, payload }) => {

switch (type) {

case typeName:

return { …state, …payload }

default:

return state

}

}

5.rcredux

import React, { Component } from ‘react’

import { connect } from ‘react-redux’

Vue 编码基础

2.1.1. 组件规范

2.1.2. 模板中使用简单的表达式

2.1.3 指令都使用缩写形式

2.1.4 标签顺序保持一致

2.1.5 必须为 v-for 设置键值 key

2.1.6 v-show 与 v-if 选择

2.1.7 script 标签内部结构顺序

2.1.8 Vue Router 规范

Vue 项目目录规范

2.2.1 基础

2.2.2 使用 Vue-cli 脚手架

2.2.3 目录说明

2.2.4注释说明

2.2.5 其他

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值