Preface

从参加工作到此刻动笔已经有7个周了,前三个周在沈阳主要使用vue+element-ui做一个前端的后台管理系统页面。后一个月被派到长春,目前所在的项目组主要使用React+dva+antd(ui)做一个中台系统的前端和中台管理界面。这7周的工作是我对编程有了一定的了解。虽说是面的JAVA开发工程师进的公司,但进组的第一天就被商量改做前端。从什么页不会,到慢慢的开始完成需求任务,感觉还是不错。但仍感到自身底子太薄,需要不断的加强。
特此,根据DVA官网open in new window上的源码解析教程,做了一系列学习笔记,这是第一篇【初始篇】

package.json里的秘密

当我们从GitHub上git clone一个dva项目后,只需要npm install然后npm start就可以启动了,但我们使用完npm start命令时,到底发生了什么?在作者的文档中,我们找到了答案“去package.json中寻找”。
package.json是一个项目的配置文件,在这里可以找到项目的所有依赖。

package.json中的devDependencies可以找到的依赖,在npm中一样可以找的到。

在了解完roadhog的作用后,我们便从入口文件src/index.js下手:
src/index.js中,dva一共做了如下几件事:

  • 0.从‘dva’依赖中引入dva:import dva from 'dva' ;
  • 1.通过函数生成一个app对象:const app = dva();
  • 2.加载插件:app.use({});
  • 3.注入model:app.model(require(;./models/example'));
  • 4.添加路由:app.router(require('./routes/indexAnother'));
  • 5.启动:app.start('#root');

这六步中,dva完成了使用React解决view层redux管理modelsaga解决异步的主要功能。目前前端工程师都在做分离‘动态的data’和‘静态的view’。MVX思想并没有改变。

dva

dva源码open in new window
遵循上面的方法,先查看package.json,引用依赖很好的说明了dva的功能:统一view层。

    //dva使用的依赖如下:
    "babel-runtime": "^6.26.0", // 一个编译后文件引用的公共库,可以有效减少编译后的文件体积

    "dva-core": "^1.1.0", // dva 另一个核心,用于处理数据层

    "global": "^4.3.2", // 用于提供全局函数的引用

    "history": "^4.6.3", // browserHistory 或者 hashHistory

    "invariant": "^2.2.2", // 一个有趣的断言库

    "isomorphic-fetch": "^2.2.1", // 方便请求异步的函数,dva 中的 fetch 来源

    "react-async-component": "^1.0.0-beta.3", // 组件懒加载

    "react-redux": "^5.0.5", // 提供了一个高阶组件,方便在各处调用 store

    "react-router-dom": "^4.1.2", // router4,终于可以像写组件一样写 router 了

    "react-router-redux": "5.0.0-alpha.6",// redux 的中间件,在 provider 里可以嵌套 router

    "redux": "^3.7.2" // 提供了 store、dispatch、reducer 

src/index.js

你可以在这里找到index.jsopen in new window
在这里,dva做了三件重要的事情:

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

使用call方法实现代理模式

dva中实现代理模式的方法如下:

  • 1.新建function,函数内实例化一个app对象。
  • 2.新建变量指向该对象希望代理的方法, oldStart = app.start
  • 3.新建同名方法start,在其中使用call,指定oldStart的调用者为app。
  • 4.令app.start = start,完成对app对象的start方法的代理。
export default function(opts = {}){
    
    // ...初始化 route ,和添加 route 中间件的方法。

    /**
    * 1.新建 function ,函数内实例化一个app对象
    */
    const app = core.create(opts, createOpts);

    /**
    * 2. 新建变量指向该对象希望代理的方法
    */
    const oldAppStart = app.start;
    app.router = router;

    /**
   * 4. 令 app.start = start,完成对 app 对象的 start 方法的代理。
   * @type {[type]}
   */
  app.start = start;
  return app;

  // router 赋值

  /**
   * 3.1 新建同名方法 start,
   * 
   */
  function start(container) {
    // 合法性检测代码

    /**
     * 3.2 在其中使用 call,指定 oldStart 的调用者为 app。
     */
    oldAppStart.call(app);
	
	// 因为有 3.2 的执行才有现在的 store
    const store = app._store;

	// 使用高阶组件创建视图
  }
}

接 ▶️ 《深度理解DVA源码》(第二篇)open in new window