1. // 用法
    2. import dva from "dva";
    3. const app = dva();
    4. // 加载插件
    5. app.use({});
    6. // 注入 model
    7. app.model(require("./models/example"));
    8. // 添加路由
    9. app.router(require("./routes/indexAnother"));
    10. // 启动
    11. app.start("#root");
    12. // 使用 React 解决 view 层、redux 管理 model、saga 解决异步的主要功
    13. // 目前 dva 的源码核心部分包含两部分,dva 和 dva-core。前者用高阶组件 React-redux 实现了 view 层,后者是用 redux-saga 解决了 model 层
    14. // *********************************
    15. // dva:
    16. // view层绑定;传递了一些初始化数据到 dva-core 所实现的 model 层;提供dva 中常用的方法函数
    17. const history = opts.history || createHashHistory();
    18. const createOpts = {
    19. // 初始化 react-router-redux 的 router
    20. initialReducer: {
    21. routing,
    22. },
    23. // 初始化 react-router-redux 添加中间件的方法,放在所有中间件最前面
    24. setupMiddlewares(middlewares) {
    25. return [routerMiddleware(history), ...middlewares];
    26. },
    27. // 使用代理模式为 history 对象增加新功能,并赋给 app
    28. setupApp(app) {
    29. app._history = patchHistory(history);
    30. },
    31. };
    32. const app = dvaCore.create(opts, createOpts);
    33. const oldAppStart = app.start;
    34. app.router = router;
    35. app.start = start;
    36. // start内部实现
    37. function render(container, store, app, router) {
    38. const ReactDOM = require("react-dom");
    39. ReactDOM.render(
    40. // React.createElement(()=>Element) === const Ele=()=>Element; <Ele/>
    41. React.createElement(getProvider(store, app, router)),
    42. container
    43. );
    44. }
    45. // 使用高阶组件包裹组件
    46. function getProvider(store, app, router) {
    47. return (extraProps) => (
    48. <Provider store={store}>
    49. {router({ app, history: app._history, ...extraProps })}
    50. </Provider>
    51. );
    52. }
    53. if (container) {
    54. render(container, store, app, app._router);
    55. // 热加载在这里
    56. app._plugin.apply("onHmr")(render.bind(null, container, store, app));
    57. } else {
    58. // 否则就生成一个 react ,供外界调用
    59. return getProvider(store, this, this._router);
    60. }
    61. // dva-core
    62. // 解决了 model 的问题,包括 state 管理、数据的异步加载、订阅-发布模式的实现
    63. export function create(hooksAndOpts = {}, createOpts = {}) {
    64. const { initialReducer, setupApp = noop } = createOpts;
    65. // Plugin 是作者设置的一堆钩子性监听函数,使用者按照作者设定过的关键词传递回调函数
    66. const plugin = new Plugin();
    67. plugin.use(filterHooks(hooksAndOpts));
    68. const app = {
    69. //prefixNamespace为 model 的 reducers 和 effects 中的方法添加了 ${namespace}/ 的前缀
    70. _models: [prefixNamespace({ ...dvaModel })],
    71. _store: null,
    72. _plugin: plugin,
    73. use: plugin.use.bind(plugin),
    74. model,
    75. start,
    76. };
    77. return app;
    78. // .... 方法的实现
    79. function model() {
    80. // model 方法
    81. }
    82. function start() {
    83. // Start 方法
    84. }
    85. }
    86. // https://dvajs.com/guide/source-code-explore.html#src-index-js-3
    87. // plugin
    88. const hooks = [
    89. "onError",
    90. "onStateChange",
    91. "onAction",
    92. "onHmr",
    93. "onReducer",
    94. "onEffect",
    95. "extraReducers",
    96. "extraEnhancers",
    97. ];
    98. export function filterHooks(obj) {
    99. return Object.keys(obj).reduce((memo, key) => {
    100. // 如果对象的 key 在 hooks 数组中
    101. // 为 memo 对象添加新的 key,值为 obj 对应 key 的值
    102. if (hooks.indexOf(key) > -1) {
    103. memo[key] = obj[key];
    104. }
    105. return memo;
    106. }, {});
    107. }
    108. export default class Plugin {
    109. constructor() {
    110. this.hooks = hooks.reduce((memo, key) => {
    111. memo[key] = [];
    112. return memo;
    113. }, {});
    114. /*
    115. 等同于
    116. this.hooks = {
    117. onError: [],
    118. onStateChange:[],
    119. ....
    120. extraEnhancers: []
    121. }
    122. */
    123. }
    124. // 存储注册的hook
    125. use(plugin) {
    126. invariant(
    127. isPlainObject(plugin),
    128. "plugin.use: plugin should be plain object"
    129. );
    130. const hooks = this.hooks;
    131. for (const key in plugin) {
    132. // 检测plugin自身有没有key值,防止使用者故意捣乱在 plugin 自己写一个 hasOwnProperty = () => false
    133. // 这样无论如何调用 plugin.hasOwnProperty() 返回值都是 false
    134. if (Object.prototype.hasOwnProperty.call(plugin, key)) {
    135. invariant(hooks[key], `plugin.use: unknown plugin property: ${key}`);
    136. if (key === "extraEnhancers") {
    137. hooks[key] = plugin[key];
    138. } else {
    139. hooks[key].push(plugin[key]);
    140. }
    141. }
    142. }
    143. }
    144. apply(key, defaultHandler) {
    145. const hooks = this.hooks;
    146. /* 通过 validApplyHooks 进行过滤, apply 方法只能应用在全局报错或者热更替上 */
    147. const validApplyHooks = ["onError", "onHmr"];
    148. invariant(
    149. validApplyHooks.indexOf(key) > -1,
    150. `plugin.apply: hook ${key} cannot be applied`
    151. );
    152. /* 从钩子中拿出挂载的回调函数 ,挂载动作见 use 部分*/
    153. const fns = hooks[key];
    154. return (...args) => {
    155. // 如果有回调执行回调
    156. if (fns.length) {
    157. for (const fn of fns) {
    158. fn(...args);
    159. }
    160. // 没有回调直接抛出错误
    161. } else if (defaultHandler) {
    162. defaultHandler(...args);
    163. /*
    164. 这里 defaultHandler 为 (err) => {
    165. throw new Error(err.stack || err);
    166. }
    167. */
    168. }
    169. };
    170. }
    171. }
    172. const sagaMiddleware = createSagaMiddleware();
    173. // ...
    174. const sagas = [];
    175. const reducers = { ...initialReducer };
    176. for (const m of app._models) {
    177. reducers[m.namespace] = getReducer(m.reducers, m.state);
    178. if (m.effects)
    179. sagas.push(app._getSaga(m.effects, m, onError, plugin.get("onEffect")));
    180. }
    181. // ....
    182. store.runSaga = sagaMiddleware.run;
    183. // Run sagas
    184. sagas.forEach(sagaMiddleware.run);
    185. const middleware = () => (next) => (action) => {
    186. const { type } = action;
    187. if (isEffect(type)) {
    188. return new Promise((resolve, reject) => {
    189. // .... resolve ,reject
    190. });
    191. } else {
    192. return next(action);
    193. }
    194. };
    195. function isEffect(type) {
    196. // dva 里 action 的 type 有固定格式: model.namespace/model.effects
    197. // const [namespace] = type.split(NAMESPACE_SEP); 是 es6 解构的写法
    198. // 等同于 const namespace = type.split(NAMESPACE_SEP)[0];
    199. // NAMESPACE_SEP 的值是 `/`
    200. const [namespace] = type.split(NAMESPACE_SEP);
    201. // 根据 namespace 过滤出对应的 model
    202. const model = app._models.filter((m) => m.namespace === namespace)[0];
    203. // 如果 model 存在并且 model.effects[type] 也存在,那必然是 effects
    204. if (model) {
    205. if (model.effects && model.effects[type]) {
    206. return true;
    207. }
    208. }
    209. return false;
    210. }