学习AngularJS

源代码结构

  • /AngularPublic.js,外部接口的实现逻辑,比如$http, $injector等。源代码 对照这个,就知道所有Angular API的来龙去脉了。
  • /src/auto/injector.js,实现Dependency Injection源代码
  • 测试使用

Angular 模块

angular.module 这一API同时用于创建和获取Angular模块,当调用改API时传入第二个参数表示创建模块,如angular.module('test', [])不带第二个参数为获取模块,如angular.module(t'est')

Tips

  • $injectPhoneListCtrl.$inject = ['$scope', '$http']; 避免JS代码压缩过程中造成的$scope, $http标识符被替换导致Angular失效的问题。或者用如下的方法var PhoneListCtrl = ['$scope', '$http', function($scope, $http) { /* constructor body */ }];
  • ngSrc。直接在图片的src属性中,设置Angular变量,当Angular没有执行的时候,浏览器会请求带的地址,造成错误

路由和视图

什么是Injector

Dependency Injector根据控制函数申明的参数(还原参数字符串值),向Angular内置的服务提供者请求相应服务的实例,并作为参数传入控制函数。

注入器本身不知道$http, $route服务的作用,甚至不知道这些服务的存在,只有在定义模块的时候进行配置,Injector的核心功能是加载模块的定义声明,注册在这些定义声明中提供的所有服务,当被请求的时候,将这些服务注入对应的函数,通过服务的提供者实现延迟实例化。

实现原理

根据实例函数的参数名,来加载该名字对应的函数,并作为参数传入。参考:

  1. Angular DI实现原理
  2. 深入理解Angulay DI(官方wiki)

PS

Angular's dependency injector provides services to your controller when the controller is being constructed. The dependency injector also takes care of creating any transitive dependencies the service may have (services often depend upon other services).

Note that the names of arguments are significant, because the injector uses these to look up the dependencies.

服务提供者

服务提供者是一个对象,它提供服务,并且通过暴露配置这些服务的接口,控制一个服务的创建和运行行为,举$route服务的例子来说,$routeProvider接口用于开发人员定义应用的路由规则

定义路由

通过在定义模块的时候,申请$routeProvider,并提供相应配置来实现,如下的例子

angular.module('phonecat', [])
  //request API $routerProvider to be injected into config function
  .config(['$routeProvider', function($routeProvider) {
    $routeProvider
      .when('/phones', {templateUrl: 'partials/phone-list.html',   controller: PhoneListCtrl})
      .when('/phones/:phoneId', {templateUrl: 'partials/phone-detail.html', controller: PhoneDetailCtrl})
      .otherwise({redirectTo: '/phones'});
  }]);

为了配置应用的路由规则,首先为应用创建一个模块(名为phonecat,在ng-app="xx"中使用),然后通过请求的API $routerProvider注入我们的配置函数,来实现路由配置路由的配置。 注意:这其实也是异步加载再初始化控制函数,这里可以是因为控制函数的初始化是在内容插入之后,而“用户运营项目”中,先初始化了控制函数,再插入被控制的内容,导致无法被控制。

使用路由

  1. "ng-app",设定使用的模块,<html lang="en" ng-app="phonecat">
  2. "ng-view",设定视图容器,<body><div ng-view></div></body>

实现控制函数

function PhoneDetailCtrl($scope, $routeParams) {
  $scope.phoneId = $routeParams.phoneId;
}

Tips

  • ':xxx',定义在:后面的变量会被提取到$routeParams对象中

自动初始化

当DOMContentLoaded/document.readyState=='complete'时,Angular会查找ng-app指令,如果找到

  1. 加载ng-app指令关联的模块
  2. 创建应用程序的injector
  3. 编译ng-app下的DOM内容

手动初始化

<script>
   angular.element(document).ready(function() {
     angular.module('myApp', []);
     angular.bootstrap(document, ['myApp']);
   });
</script>

在应用根元素上添加ng-app属性会进行自动初始化,但是想在自动初始化之前做一些配置或者计算的话,需要用到手动初始化:如上,angular.element类似于jQ中的$,angular.bootstrap为初始化函数,将选择的元素compile成应用程序,他不会自动创建应用模块,因此需要手动创建myApp模块之后,再将其作为参数传入,以便Injector进行加载

directives

通过Angular编译DOM,可以根据用户行为来改变HTML元素、元素的属性、HTML内容等,提供这些行为支持的扩展被称为directives。directive只是一个函数,编译DOM时会被自动执行。Angular 编译原理,请参考可拖拽指令的实现例子

编译的过程

  1. 编译:便历DOM,搜集所有的directives,返回一个映射函数
  2. 隐射:将directives和一个作用域结合起来,并创建一个动态视图,作用域内发生的任何改变都会映射到视图中去,用户和视图的交互会映射到作用域中去,确保作用域的稳定。

开发Angular指令

方法一:返回Link函数

angular
.module('directives', [])    
.directive('xx', function($document){
  // 返回一个该指令的初始化函数
  return function(scope, element, attr){

    // $document: 定义ng-app的元素

    // element: 使用了该指令的元素
        比如:<span dragable></span>,Angular在编译的时候就会将该span元素作为element参数传进来

    // 实际上是通过ng指令来进行事件的注册,因为是将指令加入到HTML中,因此不需要选择DOM的这一操作!
  }
});

方法二:返回包含链接函数的对象,支持更多的配置

angular
.module('directives', [])
.directive('contenteditable', function($document){
  // 返回的是包含了各种配置参数的对象
  return {
    require: "ngModel",
    link: function(scope, element, attrs, ctrl){
      // *ctrl* 为require模块的控制器,此处为NgModelController
            参考:http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController#$setViewValue

      // View -> Model
      elm.on('bulr', function(){
        scope.$apply(function(){  
          ctrl.$setViewValue(elm.html());
        });
      });

      // model -> view
      ctrl.$render = function(value) {
        elm.html(value);
      };

      // load init value from DOM
      ctrl.$setViewValue(elm.html());
    }
  }
})
[2013-09-27]

学习BootStrap3 & Foundation4

栅格系统

启用Grid

添加viewport属性: <meta name="viewport" content="width=device-width, initial-scale=1.0">

四种Grid

  1. col-xs-*: 全分辨率支持

  2. col-sm-*: 分辨率 >= 768

  3. col-md-*: 分辨率 >= 992

  4. col-lg-*: 分辨率 >= 1200

原理

  1. 当分辨率满足使用的栅格的条件时,进行栅格布局,否则退化为正常DIV布局(即指定的col-xx-x无效)。由于col-xs支持全分辨率,因此永远会采用栅格布局。
  2. 可以设定多个Grid类,当某一条件不满足时,会check另外的条件,*优先级: lg > md > sm > xs *
[2013-09-24]

ECMASCript 规范详解

ECMAScript-Language-Specification 工欲善其事,必先利其器,winter大神,我来了~

语言概述

  • primitive values :Undefined, Null, Boolean, Number, String
  • Every object created by a constructor has an implicit reference (called the object‘s prototype) to the value of its constructor‘s "prototype" property.通过构造函数创建的对象的__proto__属性会指向构造函数的prototype,prototype之间可以传递组成prototype链。
  • new Constructor() == Object.create(Constructor.prototype) Object.create方法接受第一个参数为返回的新对象__proto__属性的引用对象,第二个参数为新建对象默认的属性,相当于Object.defineProperties接受的参数。参考链接Object.create
  • 构造函数自身不会共享prototype内的属性(例如只有Array.prototype.push而无法通过Array.push访问到prototype中的方法和属性4.2.1)

一些定义

  • Boolean Object, String Object, Number Object, 由对应的构造函数通过new操作符,并传入相应的参数返回来的对象,这些对象具有内部属性,该属性的值的类型是对应的变量类型,这些对象可以通过执行不带new的构造函数返回其内部属性的值,如String(new String('a')) === 'a',注意这里是全等,因为==会自动将两边的表达式进行值转换,'a' == new String('a')也是成立的,但事实上前者是String value后者是String Object。
  • Number type包括所有的Number values,NaN,和正负infinity

Chapter 6 Source Text

在ES中,注释中出现的转义字符不会进行解析,不会导致注释被截断(JAVA中注释会被转义序列截断)

Chapter 7 Lexical Conventions

Summary

  • tokens:除了空白,注释,行终止符之外的传递给语法分析程序的输入元素。由reserved words(保留字), identifiers(标识符/变量名), literals(null, true/false, number, string, regEx), punctuators of the ECMAScript language(操作符) 组成
  • ES程序的源代码首先被转换成输入元素(tokens,行终止符,注释,空白)。从左往右扫描源代码并使下一个输入的元素具有尽量长的字符长度。
  • 除法和正则表达式都是/符号开头,当除法操作符或者除法赋值(/=)被允许的时候,/被判定为除法符号(InputElementDiv symbol),其他情况下,被判定为正则表达式符号(InputElementRegExp symbol)。举个例子如下:
    
    a = b 
    /hi/g.exec(c).map(d); 
    
    换行符后的第一个非空白/非注释元素是/,因此允许除法和除法赋值,所以行末不会自动添加;,上面的例子被当成a = b / hi / g.exec(c).map(d);处理。会报错,而不是出现无法判断除号还是正则表达式的两难情况
  • 行截止符号:<LF> <CR> <LS> <PS> 都属于正则表达式的\s

分号(semicolon)的自动插入

  • 解析到一个无法继续构成当前语法的token(offending token)时,如果这个oToken和上一个token隔了换行符,或者这个oToken是},那么会自动在这个oToken之前加上分号。也就是说:{}作为代码段包裹时,其中的最后一条语句,或者有换行符时(且该token不被需要时,return就属于特例)
  • 解析到可以继续构成当前语法,但是不满足使用条件的token(restricted token)。例如a ++之间不能有换行符,虽然a和++这两个token可以完成语法解析,但是之间的换行符限制了该语法的构建,此时会自动添加分号变成a;\n++++\na则不属于restricted token, 可以正常运行)。类似的有return, break, continue, throw。这些语句后面跟的表达式必须跟在同一行,不然会报错。
  • 到达文档流末尾了还无法解析得到正常的程序,自动添加;

Chapter 8 Type

对象是各种属性的集合

  • 数据属性。关联各种类型的ES value、布尔值等
  • 访问属性。可以是访问函数、布尔值的集合
  • 内部属性。没有key,无法直接通过ES操作符访问,只是为了规范而存在

数据属性的特性:

  • Value: ES中的数据类型
  • Writable: Value是否可变
  • Enumberable: 在for-in循环中是否可列出
  • Configurable: 该属性是否可删除、转换成其他类型(accessor property)、改变上述Attribuate

访问器的特性:

  • Get: 取值方法
  • Set: 赋值方法
  • Enumberable
  • Configurable

一些测试

[2013-08-16]

FIS 学习指南

下午参加了公司前端集成解决方案——F.I.S的2.0发布会议,感觉一些feature还是很酷的,用nodejs实现,号称能整合coffee,markdown等多种语言,记录一下学习该工具的笔记。FIS的官方网站

简介

fis根据根目录下面的fis-conf.js配置文件来进行资源定位:将资源文件的相对路径替换成线上路径,内容嵌入:将一些其他语言编写的文件内容嵌入(如MarkDown),图片文件替换成base64编码等,依赖声明:编译中识别文件内标记的对其他资源的依赖声明。

资源定位

  1. HTML文件。识别资源文件的src href属性并进行相应替换
  2. JS文件。在JS文件中通过__uri(path)来引用资源。(注意不支持字符串嵌入,只支持函数调用的方式)
  3. CSS文件。自动识别css文件、html文件中的style标签内容中url(path)及src=path字段,并将其替换成对应资源的编译后url路径

资源嵌入

  1. HTML文件。在资源定位的基础上,对资源路径追加?_inline参数,会将图片变成base64,css/js文件内容插入到文件内,<!--inline[demo.html]-->插入其他文本文件内容。
  2. JS文件。使用__inline()函数嵌入其他文件内容(图片被自动base64)
  3. CSS文件

依赖声明

  1. JS文件。识别js文件中的 require函数,或者注释中的 @require字段标记的依赖关系 ,并在生成的map.json文件中的文件记录重的deps属性中,列出所有的依赖关系
  2. CSS文件。识别css文件 注释中的@require字段 标记的依赖关系
  3. HTML文件
[2013-08-14]

来自google的网站性能优化教程

教程视频链接Critical rendering path

一些笔记

  • HTML文件会一边下载,一边解析并构建DOM树,CSS则不同,只能加载完整之后再进行解析。将CSS文件拆分成几个较小的部分会带来好处,因为可以逐渐的计算和渲染css(尤其是在Mobile上面)
  • 加载的css没有被下载并解析好之前,页面会阻塞,不会呈现任何内容
  • DOM tree和CSSOM tree一起组成Render tree,具有display none属性的元素不会出现在Render tree中。
[2013-08-12]

Secrets of the javascript ninja 读书笔记

齐伟老师的JS分享干货多多,使我顿时感觉到了和大神之间的差距,花点时间好好都下这本书,网上评价不错

一些基础

  • assert -- JS可视化测试函数

chapter 4 Function

实现对象数组

var a = {length: 0}
Array.prototype.push.call(a, 'hello');  //return 1 the length of a 
Array.prototype.push.call(a, 'world!'); //return 2 the length of a

简单的类数组对象管理器

var elems = {
  length: 0,
  add: function(elem){
    Array.prototype.push.call(this, elem);
  },
  gather: function(id){
    this.add(document.getElementById(id));
  }
};

基于length属性的函数重载

function addMethod(object, name, fn) {
  var old = object[name];
  object[name] = function(){
    if (fn.length == arguments.length)
      return fn.apply(this, arguments)
    else if (typeof old == 'function')
    return old.apply(this, arguments);
  };
}

上述函数利用闭包创建了对重载前函数的引用,新函数根据传入的参数判断该调用哪个函数(重载之前的还是重载之后的),利用下述代码,就可以对ninja中的whatever方法进行重载,根据传入的参数数量调用不同的函数进行处理

var ninja = {};
addMethod(ninja,'whatever',function(){ /* do something */ });
addMethod(ninja,'whatever',function(a){ /* do something else */ });
addMethod(ninja,'whatever',function(a,b){ /* yet something else */ });

chapter 8 Taming threads and timers

setInterval会丢失

当setInterval触发的时候,如过还有等待执行的之前触发的setInterVal,它们不会叠加,之后只会触发一次。

[2013-08-12]

jQuery使用笔记

offset的坑

对一个隐藏元素使用offset()方法,返回的对象的top值是页面的滚动值,没有参考价值。

[2013-08-07]

学习CoffeeScript

一些常识

  • You don't need to use parentheses to invoke a function if you're passing arguments. The implicit call wraps forward to the end of the line or block expression.
[2013-08-05]

网页性能优化(记music主页优化)

近期接手music.baidu.com主页的优化任务,目前首页呈现时间1.7s左右,加载完成时间7.5s左右,目标是将加载完成时间下降到5s一下,本文记录了网上关于性能优化方面的资料和实际操作,看看能学到哪些有意思的事情!

不要过度合并图片

优化之后发现网站加载事件反而变慢,看了下加载最慢的是一张合并后的127kb的图片,原来我把一些二维码也合并到这张图片中去了。去掉之后,图片的体积减少为79kb,由此可见,images spirit针对小图进行合并会具有显著效果,而一些较大的图片,延迟加载才是解决问题的根本!

[2013-07-31]

NodeJS学习笔记

部署

console最大输出3层

今天打算实现一个nodejs的脚本,用于将项目中不再使用的图片清理出去,在用console.log查看输出结果的时候,发现深层的目录直接输出为[object],开始还以为是递归程序出问题了,排查了下发现程序并没有出现问题。观察输出的格式终于得道了:当一个对象过深时,不再自动展开该对象内容,而是直接输出对象的类型替代

fs.watch模块的坑

  • 如果是监视一个文件目录的话,默认并不递归,也就是只能监视一个目录下面的文件变动,子目录的变动不会有反映
  • 被监视的文件目录本身发生变化(比如重命名),也会触发rename事件,且返回的文件名始终是该目录名。目录内假如也有该名字的文件夹,重命名时也会触发rename事件,区别在于父目录的rename只触发一次,即原始文件名,子目录的rename触发两次,原始文件名和新文件名

捕获ctrl-c退出事件

程序正常运行的退出事件是exit, ctrl-c导致的退出事件是SIGINT, 用process.on 'SIGINT'捕获, 并在处理函数中加上do process.exit继续退出事件

http.request的坑

  • 请求文件时,请带上绝对路径"/",写好的程序一直报400,查看了apache的错误日志(/etc/httpd/logs/error_log)的最后几行,发现Invalid URI in request POST index.php HTTP/1.1,再对比前面的其他错误Undefined index: to in /var/www/html/index.php,猜测是不是index.php前面少了一个绝对路径符号/?这才想起来HTTP协议里面报头请求的路径是绝对路径,导致一直400报错。。。是浏览器自动将相对路径转换成绝对路径。
  • 当请求的服务器异常时(比如关闭),会抛出Error: connect ECONNREFUSED错误,并且提示Unhandled 'error' event,但是明明将http.request放在我的try catch代码段里面的,估计和js的try catch机制有关:如果子层没有抛出孙子层的异常,那么无法直接在外层捕获孙子层的异常,,这个问题其实是我的书写错误,http.request的异常处理应该注册在返回的对象的error事件中,参考stackoverflow此文
  • 官方的文档没有介绍如何获取http响应内容,原来是在http.request @option (res)->回调的函数中的response(res)对象的on data事件中进行返回数据的读取,参考此文:http.request docs contains example how to receive body of the response through handling data event

PHP webserver的坑

  • php处理上传文件,首先_$FILES[]变量里面根据上传文件的name值来作为文件的key,文件的内容存放在该key下的key为tmp_name的路径中,即访问路径为$_FILES["XXX"]["tmp_name"]
  • selinux真是恶心的东西,重启之后发现存放上传文件的时候服务器报permission denied错误,但是明明之前试过是可以的,唯一的不同就是之前执行过关闭selinux的指令(好吧,我一直以为设置过不自动启动了的--!)``
[2013-07-23]

CSS规范(学习笔记)

参考页面

今天组内大神祥鹏展示了一下他深厚的CSS功底:子元素设置百分比的margin-top值是以什么为参考的?这必然是父元素的高度吧?可实际上是父元素的宽度。。。囧,由此大神强烈推荐了W3C的CSS规范,因此有了本文。

一些笔记

  • 子元素继承的,是父元素的绝对值,而不是百分比值,比如一个div的font-size为10px,line-height设置为120%(12px),则子元素从父元素继承过来的行高是12px,而不是120%

Formatting model

  • 隐藏的元素不会出现在盒模型内
  • padding继承background属性
  • margin永远是透明的,因此父元素的背景会透出来
  • 从格式化模型角度看只有block层级和inline类型的两种元素

概念:可替换元素 -- 一个元素的内容由该元素的属性替换(比如IMG元素由其src属性所指向的图片替换)。可以想象可替换元素具有自己的大小,如果width属性设定为auto则该元素使用自带的大小,不然会根据设定值进行大小调整(可替换元素有block-level和inline元素)

block-level

display具有blocklist-item属性的元素、float的元素是block-level的元素

竖直方向自动格式化

  • 竖直方向的margin值会自动合并,取较大的那个值(因为大部分情况下自动合并看上去更好,更接近设计者的意图)
  • 如果父元素没有设置padding或者border,第一个子元素的margin-top会被自动合并成0(即此时margin无效!PS:此时好像会将父元素往下撑开,到时候求教一下同事。设置了父元素的padding之后,margin生效了根据规范,一个盒子如果没有上补白(padding-top)和上边框(border-top),那么这个盒子的上边距会和其内部文档流中的第一个子元素的上边距重叠。

水平方向自动格式化

  • 水平方向的margin不会发生自动合并
  • 元素默认的宽度是auto,如果不是可替换元素(没有自带大小参数),UA自动计算该元素的width属性使得其width、padding、margin值的和等于父元素的宽度。可替换元素则使用自带的大小
  • width,margin-left,margin-right这三个属性若只有一个设定为auto,那么那个属性就会尽量最大(比如设置具有固定宽度的DIV的margin-left为auto,那么该元素就会向右布局,类似float: right的效果)默认margin-right设置为auto,所以类似float: left的效果
  • 对行内元素和float元素设置值为auto的margin,会被当成0

浮动元素

  • 使用float属性会使元素脱离文档流,并具有block-level的格式(例如设置一个img的float: left属性,会使该图片向左移动直到它碰到另一个block-level元素的margin/border/padding) 自然文档流会在右侧排布。浮动元素的margin/border/padding都会保留,不会自动合并

行高

  • 非可替换inline元素(没有默认大小)上下的border和margin不会撑开行高,如果设置的行高过小,那么文字会出现在下一行上
  • 可替换inline元素(比如img)
[2013-07-19]

JavaScript的一些高级使用

读到寒冬的博客《在浏览器的背后》,文中提到的词法分析和语法分析的部分,涉及到了一个js函数声明的一个很高端的应用

闭包在函数声明中的运用

var dataState = function dataState(c){
  if(c=="<") {
    return tagOpenState;
  }
  else {
    emitToken(c);
    return dataState;
  }
};

dataState是一个函数发生器,能够返回另外一个函数或者自身。看一下整个词法分析器的Gist代码,就会发现其精妙之处

[2013-07-19]