dva知识点总结

问题回顾:(工作中突现历史遗留问题,当路由跳转后,通过this.props.dispatch(routerRedux.push({ pathname: '/order/orderDetail', search: queryString.stringify(params) })),但是跳转的页面this.props里的location.search没有及时更新,导致页面没有重新刷新(现已解决),没有排查到具体原因,所以这边对于先对redux进行进一步的学习一下,卑微ing)

这边进行一些自己的学习总结:Redux:

redux是JavaScript状态容器,提供可预测化的状态管理。

redux的数据流过程:应用中的所有数据都存储在一个store中,改变应用的数据的方法就是派发一个action,action中描述type(描述发生了什么),payload(一些需要更改的数据),然后在reducer中进行判断来更改store。

import { createStore } from 'redux';

/**
 * 这是一个 reducer,形式为 (state, action) => state 的纯函数。
 * 描述了 action 如何把 state 转变成下一个 state。
 *
 * state 的形式取决于你,可以是基本类型、数组、对象、
 * 甚至是 Immutable.js 生成的数据结构。惟一的要点是
 * 当 state 变化时需要返回全新的对象,而不是修改传入的参数。
 *
 * 下面例子使用 `switch` 语句和字符串来做判断,但你可以写帮助类(helper)
 * 根据不同的约定(如方法映射)来判断,只要适用你的项目即可。
 */
function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1;
  case 'DECREMENT':
    return state - 1;
  default:
    return state;
  }
}

// 创建 Redux store 来存放应用的状态。
// API 是 { subscribe, dispatch, getState }。
let store = createStore(counter);

// 可以手动订阅更新,也可以事件绑定到视图层。
store.subscribe(() =>
  console.log(store.getState())
);

// 改变内部 state 惟一方法是 dispatch 一个 action。
// action 可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });
// 1

数据流向图下所示:

单项数据流可以使数据流向比较清晰,容易查看导致数据变化的行为

flux:

 

 

redux:

 这边也对dva框架进行一些分析:

dva: 基于redux, redux-saga, react-router的轻量级前端框架

ps:

在分析一个项目的时候,先去看看packjson.json,基本可以了解一个项目所用的相关依赖,

scripts:{ “start” : '........'} npm run start后就执行相关的依赖及入口文件等

 

dva所做的一些事:

1.从‘dva’中引入dva

2.生成一个app对象

const app = dva({
  ...createLoading(), // 全局的loading
  history: createBrowserHistory(), // 支持h5的history api
  onError(error) { // 错误提示
    message.error(error.message)
  }
})

3.加载插件

app.use({})

4.注入model 

app.model()

5.添加路由

app.router()

6.启动

app.start('#root')
在这些步骤完成之后,dva完成了使用react解决view层,redux管理model,其实也就是解决了分离动态的data和静态的view
页面搭建过程中 通过connect得到model中的数据,通过派发action来进行model数据的修改。
页面一些本身的数据也可通过react的this.state={}来进行初始和修改
 
 
扒一下dva源码那些事
1.dva是个函数,返回了一个app得对象
2.目前 dva 的源码核心部分包含两部分,dva 和 dva-core。前者用高阶组件 React-redux 实现了 view 层,后者是用 redux-saga 解决了 model 层。
 
看一眼目录结构:

 

 先来看一眼dva目录里得内容:

默认导出一个函数,函数里返回一个对象:

export default function(opt = {}) {
    const app = create(opts, createOpts);
    .........
    // app上添加一些方法
    app.router = router;
    app.start = start;
    return app
}

 

// 所以最后一步调用start的方法就是此时绑定的start方法,来看一下start的具体实现

app.start('#root')

  function start(container) {
    // 允许 container 是字符串,然后用 querySelector 找元素
    if (isString(container)) {
      container = document.querySelector(container);
      invariant(container, `[app.start] container ${container} not found`);
    }

    // 并且是 HTMLElement
    invariant(
      !container || isHTMLElement(container),
      `[app.start] container should be HTMLElement`,
    );

    // 路由必须提前注册
    invariant(app._router, `[app.start] router must be registered before app.start()`);

    if (!app._store) {
      oldAppStart.call(app);
    }
    const store = app._store;

    // export _getProvider for HMR
    // ref: https://github.com/dvajs/dva/issues/469
    app._getProvider = getProvider.bind(null, store, app);

    // If has container, render; else, return react component
    if (container) {
      // 如果有真实的dom对象
      render(container, store, app, app._router);
     // 此时的render方法是通过ReacrDOM.render渲染出dom元素
      app._plugin.apply('onHmr')(render.bind(null, container, store, app)); // 热加载
    } else {
     // 否则就调用getProvider
     // getProvider是通过高阶组件包裹组件

// function getProvider(store, app, router) {
//   const DvaRoot = extraProps => (
//     <Provider store={store}>{router({ app, history: //app._history, ...extraProps })}</Provider>
//   );
//   return DvaRoot;
// }
//
return getProvider(store, this, this._router); } }

 

 

在这里,dva 做了三件比较重要的事情:

1. 使用 call 给 dva-core 实例化的 app(这个时候还只有数据层) 的 start 方法增加了一些新功能(或者说,通过代理模式给 model 层增加了 view 层)。
2. 使用 react-redux 完成了 react 到 redux 的连接。
3. 添加了 redux 的中间件 react-redux-router,强化了 history 对象的功能。

 

ps: 1.connect方法是在react-redux上引入的

2.dispatch方法是在哪里引入的??为什么app._store上有dispatch方法

 

 另外,dva上封装了Router的相关内容
import { Redirect, Route, routerRedux, withRouter } from 'dva/router' // 从dva/router上引入

 redux是状态管理的库,router是控制页面跳转的库,react-router-redux加强了React Router 中的history这个实例,将history中接受到的变化反应到state中去。

dva在此基础上又进行一层封装,方便其在model的subscriptions中监听router变化。

使用如下:

subscriptions: {
async setup({history, dispatch}) {
history.listen((location) => {
  // 监听路由,进行路由变化时的一些监听操作
..............
  })
 }
}

 

dva-core源码如下:

export function create(hooksAndOpts = {}, createOpts = {}) {
  const { initialReducer, setupApp = noop } = createOpts;

  const plugin = new Plugin();
  // 声明Plugin构造函数
  // constructor() {this._handleActions = null; this.hooks = {key: []}}
  // 定义了实例方法 use,apply,get
  plugin.use(filterHooks(hooksAndOpts));
  // 过滤已有键filterHooks(hooksAndOpts)
  // use方法就是对存储的键值对进行处理

  const app = {
    _models: [prefixNamespace({ ...dvaModel })], //返回的是对于reducers的封装
    // model.reducers = prefix(reducers, namespace, 'reducer');
    // prefix就是把key包装成`${namespace}${NAMESPACE_SEP}${key}`形式
    _store: null,
    _plugin: plugin,
    use: plugin.use.bind(plugin), // 使用plugin的use方法
    model, // 方法
    start,
  };
  return app;
function model() {
}
......
function start() {
...getSaga()
    app._getSaga = getSaga.bind(null);

    const sagas = [];
    const reducers = { ...initialReducer };
    for (const m of app._models) {
      reducers[m.namespace] = getReducer(m.reducers, m.state, plugin._handleActions);
      if (m.effects) {
        sagas.push(app._getSaga(m.effects, m, onError, plugin.get('onEffect'), hooksAndOpts));
      }
    }
........

}
}

对history值进行一层封装,使用 patchHistory 函数代理 history.linsten

即强化了 redux 和 router 的联系,路径变化可以引起 state 的变化,进而听过监听 state 的变化来触发回调

function patchHistory(history) {
  const oldListen = history.listen;
  history.listen = callback => {
    // TODO: refact this with modified ConnectedRouter
    // Let ConnectedRouter to sync history to store first
    // connected-react-router's version is locked since the check function may be broken
    // min version of connected-react-router
    // e.g.
    // function (e, t) {
    //   var n = arguments.length > 2 && void 0 !== arguments[2] && arguments[2];
    //   r.inTimeTravelling ? r.inTimeTravelling = !1 : a(e, t, n)
    // }
    // ref: https://github.com/umijs/umi/issues/2693
    const cbStr = callback.toString();
    const isConnectedRouterHandler =
      (callback.name === 'handleLocationChange' && cbStr.indexOf('onLocationChanged') > -1) ||
      (cbStr.indexOf('.inTimeTravelling') > -1 &&
        cbStr.indexOf('.inTimeTravelling') > -1 &&
        cbStr.indexOf('arguments[2]') > -1);
    callback(history.location, history.action);
    return oldListen.call(history, (...args) => {
      if (isConnectedRouterHandler) {
        callback(...args);
      } else {
        // Delay all listeners besides ConnectedRouter
        setTimeout(() => {
          callback(...args);
        });
      }
    });
  };
  return history;
}

 

 

redux-saga

 getSaga.js   ----本质就是返回一个generator函数
 
export default function getSaga(effects, model, onError, onEffect, opts = {}) {
  return function*() {
    for (const key in effects) { //遍历model所有effect属性
      if (Object.prototype.hasOwnProperty.call(effects, key)) {
        const watcher = getWatcher(key, effects[key], model, onError, onEffect, opts);
    // 生成一个watcher
        const task = yield sagaEffects.fork(watcher);
        yield sagaEffects.fork(function*() {
          yield sagaEffects.take(`${model.namespace}/@@CANCEL_EFFECTS`);
          yield sagaEffects.cancel(task);
        });
      }
    }
  };
}

 

 dva有自己的一套创建store的方式
app._store = createStore({
      reducers: createReducer(),
      initialState: hooksAndOpts.initialState || {},
      plugin,
      createOpts,
      sagaMiddleware,
      promiseMiddleware,
    });

 

 
// createStore
export default function({
  reducers,
  initialState,
  plugin,
  sagaMiddleware,
  promiseMiddleware,
  createOpts: { setupMiddlewares = returnSelf },
}) {
  // extra enhancers
  const extraEnhancers = plugin.get('extraEnhancers');
  invariant(
    isArray(extraEnhancers),
    `[app.start] extraEnhancers should be array, but got ${typeof extraEnhancers}`,
  );

  const extraMiddlewares = plugin.get('onAction');
  const middlewares = setupMiddlewares([
    promiseMiddleware,
    sagaMiddleware,
    ...flatten(extraMiddlewares),
  ]);

  const composeEnhancers =
    process.env.NODE_ENV !== 'production' && win.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
      ? win.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true, maxAge: 30 })
      : compose;

  const enhancers = [applyMiddleware(...middlewares), ...extraEnhancers];

  return createStore(reducers, initialState, composeEnhancers(...enhancers));
}

 

 

先写到这里吧,待后续再次学习后进行补充。
 

 

posted @ 2022-06-17 17:43  千亿昔  阅读(365)  评论(3编辑  收藏  举报