首页

Angular.js源码分析之ngif

发表于2015年10月27日 分类: js 标签: angular angular.js 源码分析 angular源码分析 js

在angular中有两个内置的指令ng-ifng-repeat是比较特殊的,特殊之处在于他们创建指令时候多了一个内置参数配置:$$tlbng-if相对ng-repeat逻辑则更加简单,分析起来也就更容易。所以这里就来分析分析ng-if指令,目的是不仅要知道了ng-if指令的逻辑,也要知道这个参数为啥会存在。

先来看下源码:

var ngIfDirective = ['$animate', function($animate) {
  return {
    multiElement: true,
    transclude: 'element',
    priority: 600,
    terminal: true,
    restrict: 'A',
    $$tlb: true, // 这个特殊的参数
    link: function($scope, $element, $attr, ctrl, $transclude) {
        var block, childScope, previousElements;
        // 检测值变化
        $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {

          if (value) {
          	// true就是要显示
            if (!childScope) {
            	// 依旧是调用$transclude
              $transclude(function(clone, newScope) {
                childScope = newScope;
                // 插入注释元素
                clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
                // 保留引用
                block = {
                  clone: clone
                };
                $animate.enter(clone, $element.parent(), $element);
              });
            }
          } else {
          	// 隐藏
          	// 有的话就销毁 且动画移除
            if (previousElements) {
              previousElements.remove();
              previousElements = null;
            }
            if (childScope) {
              childScope.$destroy();
              childScope = null;
            }
            if (block) {
              previousElements = getBlockNodes(block.clone);
              $animate.leave(previousElements).then(function() {
                previousElements = null;
              });
              block = null;
            }
          }
        });
    }
  };
}];

从上边代码可以看出其基本逻辑还是很简单的,但是不明白的是这个私有的设置参数$$tlb到底是用来干嘛的,有啥用处吗?是为了解决什么样的问题的(当然不建议开发者使用)?

我觉得不如首先来看没有这个配置会出现什么问题呢?做一个实验,把ngIfDirective的源码中的$$tlb去掉,然后来一个简单的demo,一探究竟。

更多

Angular.js源码分析之ngcontroller

发表于2015年10月26日 分类: js 标签: angular angular.js 源码分析 angular源码分析 js

在angular中用的最多的指令可能就是ng-controller,下边就一起来分析下这个指令。

先来看下源码:

var ngControllerDirective = [function() {
  return {
    restrict: 'A',
    scope: true,
    controller: '@',
    priority: 500
  };
}];

可以看到代码是很简单的,这里可能唯一需要注意的细节就是controller: '@'这个是神马意思?其实这个逻辑在之前分析link中分析nodeLinkFn的时候其中有setupControllers这个函数,他有这样的逻辑:

// 在controller中可以注入的东西
var locals = {
  $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
  $element: $element,
  $attrs: attrs,
  $transclude: transcludeFn
};
// 省略
var controller = directive.controller;
if (controller == '@') {
  // ngController设置的controller的值是@
  // 需要找回属性中的该值
  controller = attrs[directive.name];
}

// 调用$controller服务 但是 是延迟的
var controllerInstance = $controller(controller, locals, true, directive.controllerAs);

所以上边的指令中controller的意思就很明显了,就是取得其属性(ng-controller)值。但是后边的一行逻辑值得我们注意,那就是$controller服务。

更多

Angular.js源码分析之nginclude

发表于2015年10月25日 分类: js 标签: angular angular.js 源码分析 angular源码分析 js

还记得在源码分析之compile中在分析$CompileProvider的实例directive(也就是module实例可以用来自定义指令的directive)方法的时候,里边说到对于每自定义一个指令其实都会有对应的Provider存在,这里再次看下那部分代码:

/**
 * 注册新的指令
 */
 this.directive = function registerDirective(name, directiveFactory) {
  assertNotHasOwnProperty(name, 'directive');
  if (isString(name)) {
    // key value 形式
    assertValidDirectiveName(name);
    assertArg(directiveFactory, 'directiveFactory');
    if (!hasDirectives.hasOwnProperty(name)) {
      // 还没有name的Directive工厂
      hasDirectives[name] = [];
      // 加后缀Directive
      $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
        function($injector, $exceptionHandler) {
          // 此时使用的时候
          // 取得所有的directiveFactory然后执行
          // 得到的就是要如何构建指令的对象(指令对象)
          var directives = [];
          forEach(hasDirectives[name], function(directiveFactory, index) {
            try {
              var directive = $injector.invoke(directiveFactory);
              if (isFunction(directive)) {
                directive = { compile: valueFn(directive) };
              } else if (!directive.compile && directive.link) {
                directive.compile = valueFn(directive.link);
              }
              // 指令对象的配置属性们
              directive.priority = directive.priority || 0;
              directive.index = index;
              directive.name = directive.name || name;
              directive.require = directive.require || (directive.controller && directive.name);
              directive.restrict = directive.restrict || 'EA';
              // 解析scope(bindToController)绑定
              var bindings = directive.$$bindings =
                  parseDirectiveBindings(directive, directive.name);
              if (isObject(bindings.isolateScope)) {
                // 独立scope对象
                directive.$$isolateBindings = bindings.isolateScope;
              }
              // 指定directive的$$moduleName
              // 也就是在moduleInstance对象上暴露directive的时候使用的是
              // invokeLaterAndSetModuleName 给directiveFactory赋值了$$moduleName
              directive.$$moduleName = directiveFactory.$$moduleName;
              directives.push(directive);
            } catch (e) {
              $exceptionHandler(e);
            }
          });
          return directives;
        }]);
    }
    // 添加
    hasDirectives[name].push(directiveFactory);
  } else {
    // 批量注册 指令
    forEach(name, reverseParams(registerDirective));
  }
  return this;
};

在那篇文章中有这样说:

从上边可以看出在调用directive的时候其实是会创建一个以name+Suffix为名的service的(通过$provide.factory的方法),也就意味着同一个名字的指令可以有多个directiveFactory的,也就说我们可以一直增强同一个指令。

而在angular中一个指令被定义多次的就有这样的一个指令ngInclude,也就是本篇要分析的重点。

更多

Angular.js源码分析之link

发表于2015年10月24日 分类: js 标签: angular angular.js 源码分析 angular源码分析 js

在angular初始化的最后阶段有这样的一段代码:

compile(element)(scope);

上一篇大概分析了下compile,那么这次继续分析下一步也就是调用传入scope,其实这一步也就是所谓的link。

回顾上篇中说了compile的返回的结果是一个publicLinkFn函数,然后link阶段其实也就是调用这个函数了:

return function publicLinkFn(scope, cloneConnectFn, options) {
  assertArg(scope, 'scope');

  options = options || {};
  var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
    transcludeControllers = options.transcludeControllers,
    futureParentElement = options.futureParentElement;

  // When `parentBoundTranscludeFn` is passed, it is a
  // `controllersBoundTransclude` function (it was previously passed
  // as `transclude` to directive.link) so we must unwrap it to get
  // its `boundTranscludeFn`
  if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
    parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
  }

  if (!namespace) {
    namespace = detectNamespaceForChildElements(futureParentElement);
  }
  var $linkNode;
  if (namespace !== 'html') {
    // When using a directive with replace:true and templateUrl the $compileNodes
    // (or a child element inside of them)
    // might change, so we need to recreate the namespace adapted compileNodes
    // for call to the link function.
    // Note: This will already clone the nodes...
    $linkNode = jqLite(
      wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
    );
  } else if (cloneConnectFn) {
    // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
    // and sometimes changes the structure of the DOM.
    // 因为在处理transclude的时候也会compile
    // 而返回的那个函数transcludeFn被调用的时候 会传入第二个参数
    // 因为transcludeFn可能会被调用多次 所以这里需要clone一份
    $linkNode = JQLitePrototype.clone.call($compileNodes);
  } else {
    $linkNode = $compileNodes;
  }

  if (transcludeControllers) {
    for (var controllerName in transcludeControllers) {
      $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
    }
  }

  compile.$$addScopeInfo($linkNode, scope);
	
	// 其实就是transclude传入的函数
  if (cloneConnectFn) cloneConnectFn($linkNode, scope);
  // 执行compositeLinkFn函数 里边会执行所有的link函数
  // link 到scope
  if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
  return $linkNode;
};

可以看到调用了在上一篇compile中compileNodes返回的处理所有节点的link函数,来看下具体逻辑:

更多

Angular.js源码分析之compile编译

发表于2015年10月23日 分类: js 标签: angular angular.js 源码分析 angular源码分析 js

上一篇提到了angular中编译compile部分,下边就来一起看看其详细的实现过程。

首先来找找$compile服务这个是在什么时候定义的,找到核心模块ng中一段代码:

angularModule('ng', ['ngLocale'], ['$provide',
  function ngModule($provide) {
    // 省略
    // 创建$compile的$CompileProvider
    $provide.provider('$compile', $CompileProvider)
      .directive({
            a: htmlAnchorDirective,
            input: inputDirective,
            //省略
      })
    // 省略
  }
])

所以说这里要分析的是$CompileProvider以及为啥能直接链式调用directive的问题(注意到了吗?$provide是没有directive方法的,所以注定是$provide.provider()的返回值有directive方法)。

首先来看下$CompileProvider的代码:

/**
 * 核心 compile
 */
$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
function $CompileProvider($provide, $$sanitizeUriProvider) {
  var hasDirectives = {}, // 保存所有的指令
      Suffix = 'Directive',
      COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
      CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
      ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
      REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;

  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
  // The assumption is that future DOM event attribute names will begin with
  // 'on' and be composed of only English letters.
  var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
  // 省略
  this.directive = function registerDirective(name, directiveFactory) {
    // 省略
  };
  // 省略
  var debugInfoEnabled = true;
  this.debugInfoEnabled = function(enabled) {
    if (isDefined(enabled)) {
      debugInfoEnabled = enabled;
      return this;
    }
    return debugInfoEnabled;
  };

  this.$get = [
            '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
            '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
    function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
             $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {

    // 省略
  }];
}

更多

Angular.js源码分析之注入器

发表于2015年10月22日 分类: js 标签: angular angular.js 源码分析 angular源码分析 js

上一篇主要分析了整体的执行流程,最后说到了要执行注入器部分了,下边就一起来看看angular强大的依赖注入机制是怎样一回事。

注入器

执行过程的那部分代码是这样子的:

// 创建注入器
// 隐藏了注入机制
// 详见createInjector函数
var injector = createInjector(modules, config.strictDi);
// 注入这几个服务执行bootstrapApply
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
   function bootstrapApply(scope, element, compile, injector) {
    scope.$apply(function() {
      // 进入ng环境(上下文)执行
      element.data('$injector', injector);
      // compile 核心
      // compile返回一个publicLink函数
      // 然后传入scope直接执行
      // 这个scope也就是rootScope
      compile(element)(scope);
    });
  }]
);

首先就要看看createInjector是个什么鬼?

// 创建注入器实例
function createInjector(modulesToLoad, strictDi) {
  strictDi = (strictDi === true);
  var INSTANTIATING = {},
      providerSuffix = 'Provider',
      path = [],
      loadedModules = new HashMap([], true),
      
      providerCache = {
        $provide: {// $provide服务
            provider: supportObject(provider),
            factory: supportObject(factory),
            service: supportObject(service),
            value: supportObject(value),
            constant: supportObject(constant),
            decorator: decorator
          }
      },
      // $injector服务
      providerInjector = (providerCache.$injector =
          createInternalInjector(providerCache, function(serviceName, caller) {
            if (angular.isString(caller)) {
              path.push(caller);
            }
            throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
          })),
      // provider实例(调用$get)缓存
      instanceCache = {},
      instanceInjector = (instanceCache.$injector =
          createInternalInjector(instanceCache, function(serviceName, caller) {
            var provider = providerInjector.get(serviceName + providerSuffix, caller);
            return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
          }));


  // 加载执行module
  // loadModules得到的是所有的runBlocks
  // 然后执行runBlocks中内容
  forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });

  return instanceInjector;

  // 省略一些代码 看后续分析
}

createInjector.$$annotate = annotate;

从上往下看,首先看providerCache对象有$provide属性,值为对象,提供了provider,factory,servicevalue,constant以及decorator这些方法,很明显猜到了这里就是angular的$provide服务的实现部分。

supportObject

这里使用了supportObject方法,先看看如何实现的:

// supportObject(function(key, val) {})
// 支持对象式传参调用
// 也就是在外边可以类似这样使用
// module.provider({
//   aDirective: function() {}
//   bDirective: function() {}
// })
function supportObject(delegate) {
  return function(key, value) {
    if (isObject(key)) {
      forEach(key, reverseParams(delegate));
    } else {
      return delegate(key, value);
    }
  };
}

其实就是提供了对象式便捷的调用方式。

更多

Angular.js源码分析之scope作用域

发表于2015年10月22日 分类: js 标签: angular angular.js 源码分析 angular源码分析 js

上一篇在最后提到了依赖项中的$rootScope,那部分代码是这样的:

// 创建注入器
// 隐藏了注入机制
// 详见createInjector函数
var injector = createInjector(modules, config.strictDi);
// 注入这几个服务执行bootstrapApply
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
   function bootstrapApply(scope, element, compile, injector) {
    scope.$apply(function() {
      // 进入ng环境(上下文)执行
      element.data('$injector', injector);
      // compile 核心
      // compile返回一个publicLink函数
      // 然后传入scope直接执行
      // 这个scope也就是rootScope
      compile(element)(scope);
    });
  }]
);

本篇就来分析下这个$rootScope。首先他可以被注入,那他是在什么地方加入到注入器缓存中的呢?细心的可能以及发现,在核心模块ng中,有一段这样的代码:

// 继续一堆的provider
$provide.provider({
  $anchorScroll: $AnchorScrollProvider,
  // 省略
  $controller: $ControllerProvider,
  // 省略
  $rootScope: $RootScopeProvider,
  // 省略
});

这里就看到了原来核心是$RootScopeProvider这个玩意搞得。

更多

Angular.js源码分析之开篇 执行流程

发表于2015年10月21日 分类: js 标签: angular angular.js 源码分析 angular源码分析 js

从本篇开始主要来分析下angular.js(v1.4.7)的源码,这是第一篇,先看最基础的,整个angular的初始化过程是什么样的。

结构

首先看代码最后有这样的代码:

// 真正的开始执行部分
//try to bind to jquery now so that one can write jqLite(document).ready()
//but we will rebind on bootstrap again.
bindJQuery();

publishExternalAPI(angular);

angular.module("ngLocale", [], ["$provide", function($provide) {
// 代码
}]);

jqLite(document).ready(function() {
  angularInit(document, bootstrap);
});

这个过程很明显,也就是首先执行bindJQuery,然后再调用publishExternalAPI,在页面load之后初始化angular,也就是angularInit(document, bootstrap)。所以说源码分析也会按照这个顺序进行。

bindJQuery

从名字就可以看出,主要是绑定jQuery的,这是因为在angular中还有一个实现了部分jQuery功能的JQLite,如果判断了jQuery是存在的,那么angular.element就直接赋值为jQuery了,否则就为angular自身实现的JQLite,下边看一下带分析的代码:

更多

常用的nginx配置

发表于2015年09月06日 分类: server 标签: nginx server

前言

虽然说作为一个前端攻城师一般情况下是不会担心nginx的相关配置的,如果有运维同学的话,一般是找他们帮忙搞定这些事情的。但是小一点的公司的话可能就根本没有运维了,这时候我们就需要“自己动手,丰衣足食”了!

nginx简介

Nginx(发音同engine x)是一款由俄罗斯程序员Igor Sysoev所开发轻量级的网页服务器、反向代理服务器以及电子邮件(IMAP/POP3)代理服务器。由于其各种优势各种好(自行了解),所以很热很火🔥,基本上大大小小的公司都在使用了。

以前的前端可能不会涉及到这方面,但是由于node.js火爆之后,前端以及向所谓的“全栈”方向转移了,然后基于node.js的各种工程、工具也是越来越多。这时候前端需要的越来越多了,而nginx似乎成为了“刚需”(对自己而言),虽然不需要深入的了解nginx什么原理啊什么机制啊,但是最基础的还是需要的,最起码最起码会做一些基本的配置,满足自己的日常需要。

基础配置

首先,最基础的莫过于配置一个server,然后指向自己项目的根目录,以便能通过域名或者ip访问。下边看一个最基础版本的开发环境配置:

# nginx.conf
http {
	include       mime.types;
	default_type  application/octet-stream;

	log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
					  '$status $body_bytes_sent "$http_referer" '
					  '"$http_user_agent" "$http_x_forwarded_for"';

	access_log  logs/access.log  main;

	sendfile        on;
	#tcp_nopush     on;

	keepalive_timeout  65;
		
	gzip  on;
	autoindex on; #文件列表
	autoindex_exact_size off;
	autoindex_localtime on;
		
	# 开启ssi支持,默认是off
	ssi on;
	# 默认值是off,开启后在处理SSI文件出错时不输出错误提示:”[an error occurred while processing the directive] ”
	ssi_silent_errors on;
	# 默认是text/html,如果需要shtml支持,则需要设置:ssi_types text/shtml
	ssi_types text/shtml;

	expires -1;
	if_modified_since off;
	add_header Last-Modified "";
	add_header Etag "";

	add_header Access-Control-Allow-Origin *;# 允许任何域
	add_header Access-Control-Allow-Headers X-Requested-With;
	add_header Access-Control-Allow-Methods GET,POST,OPTIONS;

	server {
		listen       80;
		server_name  localhost;

		#access_log  logs/host.access.log  main;

		location / {
			root   /git/;
			index  index.html index.htm;
		}

		#error_page  404              /404.html;

		# redirect server error pages to the static page /50x.html
		#
		error_page   500 502 503 504  /50x.html;
		location = /50x.html {
			root   html;
		}
	}

	server {
		listen 80;
		server_name fed.com;
		location / {
			root   /git/fed/;
		}
	}
}

更多

记一次ssh服务重启失败

发表于2015年09月05日 分类: server 标签: ssh sshd centos server

由于阿里云最近的一次云盾的大功劳引起的悲剧,自己也就不幸成为了其中一个“幸运儿”。可能是因为当时我正在通过ssh连接自己的服务器,然后再通过git拉取代码,但是这一下失败了。悲剧就此发生,再次使用git,md,发现没有git命令了?当时自己就坐在那一个劲的问自己为啥??

然后我想自己再次重连下自己的服务器看看啥子情况,退出后连接自己的服务器,更大的悲剧发生了,发现自己连接不上去了,这该如何是好?由于当时自己还没有意识到是阿里云的故障问题,以为自己捣鼓坏了呢,然后就去网站上通过终端连接,查看自己服务器状态,进去之后还是发现自己的git、vi啊什么乱七八杂的直接没有了,运行啥就告诉自己没有此命令了!这。。。

然后自己就“机智”的去了论坛,一看,立马知道罪魁祸首了!没有办法,只能干等了。等啊等。。。

更多