Angular启动
本来有一篇很好的文章讲解启动流程,后来设为秘密的了,虽然有复制,但也不太好拿人家不愿共享的东西出来。在这就贴一下我的总结啦。
AngularJS加载完成后工作:
1 | bindJQuery(); // 如果有JQuery |
publishExternalAPI
1 | publishExternalAPI: |
假设我们自己定义了一个 app 模块。
1 | app.run(['$scope', '$http', function ($scope, $http) { |
angularInit 做的事情是,查找页面上的 ng-app,启动整个应用。
doBootstrap 首先创建 injector,然后执行下面代码。
1 | injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate', |
创建Injector的过程:
1 | doBootstrap-->createInjector(modules): |
每个应用只创建一个Injector,当然一个页面可以手动启动几个独立的app,好像还没人这么干。
从下面的代码中可以看到,一个app模块依赖的provider实例全在providerCache中,没错我说的就是xxProvider实例。
当我们定义一个provider时,我们知道它是包装过的一个函数
1 | function XXXProvider() {this._get = function () {}} |
这个 providerCache 中保存的就是该函数的实例 instance 它有一个 _get
属性。而 instanceCache 中保存的是调用_get
返回的实例。
一个 Injector 的实现并不复杂,cache 存放 provider 和实例,invoke 方法将函数提供的 $inject 或者参数名称 name 映射为 cache[name],然后调用函数。
将 Provider 类和 Provider 实例分开单独的 cache 和 injector是基于这样的考虑:Provider 类不应该注入一个实例,同样实例不应该注入一个 Provider 类。
内部实现一个 createInternalInjector 方法提取共同部分创建这两个 injector。如果在 instanceCache 中查找不到,providerInjector 又有对应的 Provider 类,则会从 providerInjector 获取到该 Provider 类实例化一个。
至于模块的 config 方法,当然是与 Provider 类有关,而 run 方法当然是与 Provider 实例有关。就是
1 | //config(['xxProvider', function (xxProvider) {}]) 在Provider类注入完之后才应该调用,所以保存在_configBlocks 而非 _invokeQueue中 |
注册 directive 提供的的 directiveFactory 一般也不会被用到。注册的 Directive 有单独的 cache。$compile 的功能实现也不复杂,查找节点,以节点为参数调用对应 Directve 的 compile 方法。对每个节点都进行递归处理。