// 用法import dva from "dva";const app = dva();// 加载插件app.use({});// 注入 modelapp.model(require("./models/example"));// 添加路由app.router(require("./routes/indexAnother"));// 启动app.start("#root");// 使用 React 解决 view 层、redux 管理 model、saga 解决异步的主要功// 目前 dva 的源码核心部分包含两部分,dva 和 dva-core。前者用高阶组件 React-redux 实现了 view 层,后者是用 redux-saga 解决了 model 层// *********************************// dva:// view层绑定;传递了一些初始化数据到 dva-core 所实现的 model 层;提供dva 中常用的方法函数const history = opts.history || createHashHistory();const createOpts = { // 初始化 react-router-redux 的 router initialReducer: { routing, }, // 初始化 react-router-redux 添加中间件的方法,放在所有中间件最前面 setupMiddlewares(middlewares) { return [routerMiddleware(history), ...middlewares]; }, // 使用代理模式为 history 对象增加新功能,并赋给 app setupApp(app) { app._history = patchHistory(history); },};const app = dvaCore.create(opts, createOpts);const oldAppStart = app.start;app.router = router;app.start = start;// start内部实现function render(container, store, app, router) { const ReactDOM = require("react-dom"); ReactDOM.render( // React.createElement(()=>Element) === const Ele=()=>Element; <Ele/> React.createElement(getProvider(store, app, router)), container );}// 使用高阶组件包裹组件function getProvider(store, app, router) { return (extraProps) => ( <Provider store={store}> {router({ app, history: app._history, ...extraProps })} </Provider> );}if (container) { render(container, store, app, app._router); // 热加载在这里 app._plugin.apply("onHmr")(render.bind(null, container, store, app));} else { // 否则就生成一个 react ,供外界调用 return getProvider(store, this, this._router);}// dva-core// 解决了 model 的问题,包括 state 管理、数据的异步加载、订阅-发布模式的实现export function create(hooksAndOpts = {}, createOpts = {}) { const { initialReducer, setupApp = noop } = createOpts; // Plugin 是作者设置的一堆钩子性监听函数,使用者按照作者设定过的关键词传递回调函数 const plugin = new Plugin(); plugin.use(filterHooks(hooksAndOpts)); const app = { //prefixNamespace为 model 的 reducers 和 effects 中的方法添加了 ${namespace}/ 的前缀 _models: [prefixNamespace({ ...dvaModel })], _store: null, _plugin: plugin, use: plugin.use.bind(plugin), model, start, }; return app; // .... 方法的实现 function model() { // model 方法 } function start() { // Start 方法 }}// https://dvajs.com/guide/source-code-explore.html#src-index-js-3// pluginconst hooks = [ "onError", "onStateChange", "onAction", "onHmr", "onReducer", "onEffect", "extraReducers", "extraEnhancers",];export function filterHooks(obj) { return Object.keys(obj).reduce((memo, key) => { // 如果对象的 key 在 hooks 数组中 // 为 memo 对象添加新的 key,值为 obj 对应 key 的值 if (hooks.indexOf(key) > -1) { memo[key] = obj[key]; } return memo; }, {});}export default class Plugin { constructor() { this.hooks = hooks.reduce((memo, key) => { memo[key] = []; return memo; }, {}); /* 等同于 this.hooks = { onError: [], onStateChange:[], .... extraEnhancers: [] } */ } // 存储注册的hook use(plugin) { invariant( isPlainObject(plugin), "plugin.use: plugin should be plain object" ); const hooks = this.hooks; for (const key in plugin) { // 检测plugin自身有没有key值,防止使用者故意捣乱在 plugin 自己写一个 hasOwnProperty = () => false // 这样无论如何调用 plugin.hasOwnProperty() 返回值都是 false if (Object.prototype.hasOwnProperty.call(plugin, key)) { invariant(hooks[key], `plugin.use: unknown plugin property: ${key}`); if (key === "extraEnhancers") { hooks[key] = plugin[key]; } else { hooks[key].push(plugin[key]); } } } } apply(key, defaultHandler) { const hooks = this.hooks; /* 通过 validApplyHooks 进行过滤, apply 方法只能应用在全局报错或者热更替上 */ const validApplyHooks = ["onError", "onHmr"]; invariant( validApplyHooks.indexOf(key) > -1, `plugin.apply: hook ${key} cannot be applied` ); /* 从钩子中拿出挂载的回调函数 ,挂载动作见 use 部分*/ const fns = hooks[key]; return (...args) => { // 如果有回调执行回调 if (fns.length) { for (const fn of fns) { fn(...args); } // 没有回调直接抛出错误 } else if (defaultHandler) { defaultHandler(...args); /* 这里 defaultHandler 为 (err) => { throw new Error(err.stack || err); } */ } }; }}const sagaMiddleware = createSagaMiddleware();// ...const sagas = [];const reducers = { ...initialReducer };for (const m of app._models) { reducers[m.namespace] = getReducer(m.reducers, m.state); if (m.effects) sagas.push(app._getSaga(m.effects, m, onError, plugin.get("onEffect")));}// ....store.runSaga = sagaMiddleware.run;// Run sagassagas.forEach(sagaMiddleware.run);const middleware = () => (next) => (action) => { const { type } = action; if (isEffect(type)) { return new Promise((resolve, reject) => { // .... resolve ,reject }); } else { return next(action); }};function isEffect(type) { // dva 里 action 的 type 有固定格式: model.namespace/model.effects // const [namespace] = type.split(NAMESPACE_SEP); 是 es6 解构的写法 // 等同于 const namespace = type.split(NAMESPACE_SEP)[0]; // NAMESPACE_SEP 的值是 `/` const [namespace] = type.split(NAMESPACE_SEP); // 根据 namespace 过滤出对应的 model const model = app._models.filter((m) => m.namespace === namespace)[0]; // 如果 model 存在并且 model.effects[type] 也存在,那必然是 effects if (model) { if (model.effects && model.effects[type]) { return true; } } return false;}