position:sticky 使用条件分析

哪些浏览器支持,可以查看:http://caniuse.com/#search=sticky

用这个属性,主要是ios下的safari已经支持了,在ios下可以免去js模拟,效果更平滑。

在移动端模拟sticky效果,还是非常蛋疼的,需要同时监听touchmove和scroll事件,页面惯性滚动时会有一定的延迟(scorll事件在滚动停止才触发),同时移动端对fixed定位支持也不大好。JS实现,效果还是大打则扣。

关于这个position:sticky的介绍,可以google一下,网上有很多介绍了,比如神飞的《position:sticky介绍》

今天主要讲的是,position:sticky的使用条件,很多同学经常遇到position:sticky失效的情况,今天对sticky这个属性做了一番调试,总结了一些规律。现分享下。

俗话说,弄不清楚原理,就熟记结论也是一样的,那就先抛结论,sticky满足以下条件才能生效:

1、具有sticky属性的元素,其父级高度必须大于sticky元素的高度。

2、sticky元素的底部,不能和父级底部重叠。(这条不好表述,文后详细说明)

3、sticky元素的父级不能含有overflow:hidden 和 overflow:auto 属性

4、必须具有top,或 bottom 属性。

同时要注意,sticky元素仅在他父级容器内有效,超出容器范围则不再生效了。

先看个正常生效的DEMO:

See the Pen fqdkF by hugo (@baofen14787) on CodePen.

然后在sticky元素外套个div,再看看效果,发现sticky效果失效了。原因是结论的第一点,sticky元素的父级高度刚好等于sticky的高度,所以失效

See the Pen cgHdq by hugo (@baofen14787) on CodePen.

继续看第3个DEMO,也是失效的。虽然父元素的高度大于sticky元素的高度,但是sticky元素的底部刚好也是父元素的最后一个元素。(和父元素的底部重叠了)
实际上,sticky元素仅在他父级容器内有效,而sticky刚好已经到了生效的最边缘了,所以看起来就失效了。

See the Pen lKgtC by hugo (@baofen14787) on CodePen.

继续第4个DEMO,给父级容器加上overflow:hidden,sticky效果又失效了。

See the Pen okgGn by hugo (@baofen14787) on CodePen.

最后再说一下,sticky仅在父元素内生效,看DEMO

See the Pen zqyog by hugo (@baofen14787) on CodePen.

好了,DEMO放完,最后给个作业,请将这个页面sticky效果修复.

See the Pen vamnz by hugo (@baofen14787) on CodePen.

CSS命名之姓氏命名法

背景:

由历史原因及个人习惯引起的DOM结构、命名不统一,导致不同成员在维护同一页面时,无从下手,效率低下,迭代、维护成本极高。

命名原则:

1.概述

1.1基于姓氏命名法(继承+外来),如下图:

1.2说明:

(1).简单模块:在子孙模块数量可预测的情况下,继承祖先模块的命名前缀

如上图的焦点图轮播模块,模块整体结构比较简单,子元素的内容相对较少,这种情况下,假设将模块命名为 : slider,那么其后续的所有子元素,均需要继承这个类名。

slider
├── slider-list
|    |
|    └── ....
└── slider-nav
|    |
|    └── ....

(2).复杂模块:当子孙模块数量较多,且无法预估时,可以选择采用继承“祖先+父”模块的命名前缀,以保证模块之间的独立性

在上图的模块中,一个大楼层里面包含一个子模块:tab切换。在这种结构复杂的模块中,为了保证模块类名使用简单(css起名也是一个体力活)和结构看起来更清晰,可以使用继承“祖先+父”模块的命名前缀的方式。

在这个楼层模块中,楼层的名字叫:floor ,里面的tab切换单个内容块名字叫 item,那么 tab切换内部的子元素命名规则为 : floor-item-xxx

如:

floor
├── floor-hd
|    |
|    ├── floor-tit
|
├── floor-item
|    |
|    ├── floor-item-tit
|    └── floor-item-desc

上面代码中使用了2次 tit 来表示标题,也就是说如果你自己有一组命名包,你就可以很轻松的进行命名了,比如描述相关:desc、info、extra等等。

(3).复合模块:子模块中,可以嵌套其他模块,可理解为“娶媳妇”

模块间本身会有嵌套情况,如上面的结构,在今日特惠这个模块中嵌套了一个公共组件。可以理解为娶媳妇。(媳妇一般是外姓的)。

如:

hotsales
├── hotsales_li
| |
| └── mod_goods
| |
| ├── mod_goods_price
| └── mod_goods_tit
| └── mod_goods_promo

当然,其实第二点也可以用第三点的方式来实现,那么这里就涉及到一个问题?什么时候用第三点提到的方法。

比较推荐的建议是,如果嵌套的模块是一个比较通用的组件,那么可以用第三点,将子模块抽出成一个独立的组件,而其他情况下建议使用第二点的方式。

基于以上的方式,可以完成基本的命名规则,但是无法区分模块的类型。(模块是否可复用?复用范围?)

于是我们在模块的命名规则上遵循以下原则:

  1. 全站公共模块以”mod-”开头
  2. 页面公共模块以”xx-mod-”开头(xx为页面名称缩写)
  3. 独立模块,命名为一个简短的单词,如”hot、floor、banner” 等

基于以下2点来区分全站公共模块及页面模块。

  1. 同一个页面出现2次及以上同一个类名
  2. 2个以上页面出现同一个类名

在这种规则下,可能会出现这个模块原先是独立模块的,然后发现可以复用了,然后就修改类名变成公共模块。这种变更成本比较低,可接受。(因为独立模块在页面上只有一处,修改类名不会影响到其他样式)

基于这个css姓氏命名法,我们的类名是长命名的方式了,只要保证了最外层模块的唯一性,那么里面的子模块类名是不可能会出现重复的。
于是,我们要求CSS以单层命名为主,嵌套一般不能操过3层。

这样在修改样式与html结构时,相互之间是耦合的,css不依赖于html的结构。(我知道很多同学写less或scss喜欢像html那样一层一层嵌套)

最后附上以前feeling写的代码给大家参考:

webkit浏览器渲染影响因素分析

前言:浏览器的渲染对性能影响非常大,特别是在移动端页面,在宏观上,我们可以参考雅虎那20几条军规来操作,但在微观渲染层面,实际还没有一套相对成型的理论做为依据。

本文只是抛砖引玉,带大家进入微观的优化领域,实际在渲染优化这块上,还有很多技巧及方法需要大家去挖掘。本文写的也比较凌乱,望包涵!!

先来看个chrome timeline 工具上的一个图:

rendering1

在timeline上,我们看到有6种颜色的柱子,这6个类型的柱子构建了整个webkit浏览器的渲染过程。

简单的分类一下:蓝色表示加载,黄色表示脚本执行,紫色表示计算样式及布局,绿色表示绘制合成,白色表示空闲时间,灰色表示其他时间。这里主要看的是前面三个。

如下图:

rendering2

蓝色loading包含各种资源加载,在页面初始化加载阶段可以看到蓝色部分是耗时最长的,因为正在加载资源(加载html、css、js、img、flash、mp3等。)

Chrome上各个渲染部分的实际含义:

Parse Html:

发送一个http请求,获取请求的内容,然后解析html的过程。

Recalculate Style:

重新计算样式,它计算的是Style,和Layout做的事情完全不同。Layout计算的一个元素绝对的位置和尺寸,或者说是“Compute Layout”。

Recalculate被触发的时候做的事情就是处理JavaScript给元素设置的样式而已。Recalculate Style会计算出Render 树(渲染树),然后从根节点开始进行页面渲染,将CSS附加到DOM上的过程。

任何企图改变元素样式的操作都会触发Recalculate 。同Layout一样,它也是在JavaScript执行完成后才触发的。

Layout:

计算页面上的布局,即元素在文档中的位置及大小。如上面所说,Layout计算的是布局位置信息。任何有可能改变元素位置或大小的样式都会触发这个Layout事件,如width、height

Rasterizer:

光栅化,一般的安卓手机都会进行光栅化,光栅主要是针对图形的一个栅格化过程。低端手机在这部分耗时还蛮多的。

Paint:

页面上显示东西有任何变动都会触发Paint 。包括拖动滚动条、鼠标选中文字,等这些完全不改变样式,只改变显示结果的动作都会触发Paint。

Paint的工作就是把文档中用户可见的那一部分展现给用户。Paint是把Layout和Recalculate的计算的结果直接在浏览器窗体上绘制出来,它并不实现具体的元素计算。

Image Decode:

图片解码,将图片解析到浏览器上显示的过程。

Image Resize:

图片的大小设置,图片加载解析后,若发现图片大小并不是实际的大小(CSS改变了宽高),则需要Resize。Resize越大,耗时越久,所以尽量以图片的原始大小输出。

Composite Layers:

最后合并图层,输出页面到屏幕。浏览器在渲染过程中会将一些含义特殊样式的DOM结构绘制于其他图层,有点类似于Photoshop的图层概念。一张图片在Photoshop是由多个图层组合而成,而浏览器最终显示的页面实际也是有多个图层构成的。

有哪些因素会导致新建图层:

1、进行3D或者透视变换的CSS属性

2、使用硬件加速视频解码的<video>元素

3、具有3D(WebGL)上下文或者硬件加速的2D上下文的<canvas>元素

4、组合型插件(即Flash)

5、具有有CSS透明度动画或者使用动画式Webkit变换的元素

6、具有硬件加速的CSS滤镜的元素

在CSS里面,不同的属性会触发不同的layout或者paint,所以通过JS改变css的属性时,应该考虑到这些方面。如下图:

2

再引用另外一张图来看看CSS不同属性所触发的情况:

3

关于CSS属性的一个渲染问题,可以看下表,需翻墙查看: https://docs.google.com/spreadsheet/pub?key=0ArK1Uipy0SbDdHVLc1ozTFlja1dhb25QNGhJMXN5MXc&single=true&gid=0&output=html

如何优化渲染时间

1、为了确保页面的流程,必须保证60fps内不发生2次渲染树更新。 如下图,16ms内只发生如下几个操作则是正常及正确的 :

QQ截图20140817214845

2、页面滚动时,需要避免不必要的渲染及长时间渲染。

不必要的渲染包括:

1)position:fixed

fixed定位在滚动时会不停的进行渲染,特别是如果是页面顶部有个fiexd,页面底部有个类似返回顶部的fixed,则在滚动时会整个页面进行渲染,效率非常低。可以加 transform :  translateZ(0) ; 解决。

7

2) overflow:scroll

3) hover effects

有些:hover伪类在页面滚动时会不小心就触发到,如hover效果有阴影、圆角等比较耗时的属性时,建议页面滚动时,先取消hover效果,滚动停止后再加上hover效果。这个可以通过在外层加类名进行控制。

4) touch listeners

6

长时间渲染包括:

1)复杂的CSS

2)Image Decodes

这里特别是图片的Image Decodes及 Images Resize 这2个过程在移动端是非常耗时的,如下图:

4

3)Large empty layers(大的空图层,DIV)

5

参考: https://speakerdeck.com/addyosmani/velocityconf-rendering-performance-case-studies

【译】chrome – GPU加速的图层组合操作

原文:http://www.chromium.org/developers/design-documents/gpu-accelerated-compositing-in-chrome

译注:(专业词汇对应翻译)

Node(节点)
DOM Tree(DOM树)
RenderObject(渲染对象)
RenderTree(渲染树)
GraphicsContext(图形环境对象、图形上下文对象)
一个Graphics Context默示一个绘制目标。它包含绘制体系用于完成绘制指令的绘制参数和设备相干信息。Graphics Context定义了根蒂根基的绘制属性,如色彩、裁减区域、线条宽度和样式信息、字体信息、混淆模式等。来自 <http://www.byywee.com/page/M0/S772/772925.html>
Bitmap位图
RenderLayers渲染图层
Alpha mask透明遮罩
Css filtercss滤镜
Software Rendering path软件渲染模式。译成“软件渲染路径”、“软件渲染路线”都可以,它是webkit渲染页面时的传统模式,不依赖硬件加速。
HWND返回窗体或控件的句柄(注意 OLE容器控件不支持该属性。句柄:是由操作环境定义的一个唯一的整数值,它被程序用来标识或者切换到对象,如窗体或控件等。)来自 <http://baike.baidu.com/link?url=knrqRLDl2voMqg6N6pq97dWrZGaxwR7G7gwW4HQyHzn6WGTflGg0nnms9afq7oARZ1hDaduE-81jnuxXQaGziq>
IPCIPC(Inter-Process Communication,进程间通信) 来自 <http://baike.baidu.com/view/373.htm>

概要

本文档阐述chrome浏览器“硬件加速图层组合”的背景和实现细节。

介绍

一般情况下,web浏览器完全依赖CPU进行网页内容的渲染。随着GPU在硬件设备(即便是很小的硬设)中的集成应用,以及富媒体例如视频、3D效果等在网页体验中扮演着越来越重要的角色,人们开始注意想方设法高效利用底层硬件获取更好的性能、电源节能。诚然,利用GPU直接对网页内容进行图层组合可以带来显著的性能提升(译注:这里的性能应该是指渲染速度方面)。最明显的例子莫过于video元素,它利用硬件进行视频解码,利用WebGL为H5 canvas提供硬件3D加速图形绘制渲染,期间占用的内存区域是CPU无法快速存取的。(译注:WebGL参考百科文档)

将网页图层组合的操作扔给GPU还有别的诸多好处,在大多数情况下,GPU在涉及大量像素的图形绘制、组合操作方面比CPU快的多,因为GPU是专门设计善干这类事情的硬件。此外,充分利用GPU做图形绘制、组合这类工作的同时,CPU就有更多的可以并行地做其他方面的运算,这种并行工作的方式创建了一个高效的图形管道。

Part 1  Webkit渲染基础及软件渲染模式

Webkit渲染引擎的源码巨大且复杂,晦涩难懂,几乎没啥米注释!为了弄清楚GPU在chrome里面如何工作,我们得先了解下webkit渲染网页的基本构件流程。下面我们从webkit如何工作作为基本出发点,回头再慢慢引入GPU影响渲染的过程,最终了解GPU如何工作。

节点和DOM树

在Webkit里面,网页内容内部存储为一个节点对象的树,我们称之为DOM树。网页上的每个标签包括标签之间的文本,各自对应DOM树上的一个节点。DOM树最上面的节点即Document节点。

从节点到渲染对象

DOM树里面的每个视觉可见的节点,均有一个相对应的渲染对象。在webkit内,渲染对象被存储成树形数据结构,和DOM树并行,我们称之为渲染树。渲染对象知道如何在显示介质(译注:例如显示器)上呈现(绘制)对应DOM树节点的内容,具体的方式是调用图形环境对象的绘制指令。而图形环境对象负责将像素写入到位图中去,最终被显示器显示出来。在chrome里面,图形环境对象封装了一个名为Skia的2D绘图库,于是大部分绘制指令被委托给SkCanvas或者SkPlatformCanvas执行。(关于chrome如何使用Skia库,可以参考这篇文档

在软件渲染模式里,整个页面只有一个图形环境对象,所有的渲染对象被绘制到这个共享的图形环境对象中。

从渲染对象到渲染图层

每个渲染对象通过其父级渲染对象直接或间接地关联一个渲染图层。

共享相同坐标空间的渲染对象(如被相同css变形影响的哪些对象)一般情况下属于同一个渲染图层。渲染图层的存在,使得页面的元素可以按正确的顺序进行组合,使得重叠的元素、半透明的元素等等得以恰当的渲染显示。触发webkit为渲染对象创建新的渲染图层的条件有很多,具体可见源码,在方法RenderBoxModelObject::requiresLayer()或者该方法在其他类的重载方法里可以窥其一二。总的来说,对于渲染对象,只要满足下面情形之一,均会致使新的渲染图层的创建。

  1. 它是页面的根对象。(译注:应该就是document节点对应的渲染对象)
  2. 它有显式的css 位置属性。(relative,absolute,或transform)
  3. 它是透明的
  4. 它有overflow,透明遮罩或阴影(反光)
  5. 它有css滤镜
  6. 它对应的节点是3D环境或2D加速环境的canvas元素
  7. 它对应的节点是video元素

值得注意的是,渲染对象和渲染图层并不是1对1的关系。一个渲染对象所关联的渲染图层,要么是为它自身创建的渲染图层,要么是它父级对象所关联的渲染图层(如果有多个有渲染图层的父对象,取最近的那个)。(译注:同一个树路径上的多个渲染对象可能会关联同一个渲染图层)。

渲染对象是树形结构,显然渲染图层也会是树形的。渲染图层树的根节点对应页面的根元素,每个图层节点的下行图层节点(子孙节点)对应该图层视觉包含的图层(译注:视觉包含不一定是dom树上的父子、祖孙节点)。每个图层节点的子图层节点(译注:是子节点不是子孙节点)被分类存储于两个升序排序的队列(negZOrderList和posZOrderList)中,队列negZOrderList包含z轴次序比当前图层低的图层(因而这类图层在渲染图层树的位置在当前图层下面),posZOrderList反之亦然。

凑到一起:我们有很多的树

总结下前面的内容,从概念上来看我们一共有3棵并行的树结构,各自在渲染过程中的作用不尽相同:

  1. DOM树(Dom Tree),是我们最基本的模型
  2.  渲染对象树 (RenderObject Tree),其节点和DOM树的可见节点是一对一的关系。渲染过程中,渲染对象知道如果绘制其对应的Dom节点。
  3.  渲染图层树(RenderLayer Tree),由渲染图层构成。其节点和渲染对象树的节点是1对多的关系,因为一个渲染对象关联的渲染图层要么是它自身的,要么是它父级对象的。渲染图层树体现了图层之间的z轴顺序。

软件渲染模式

从根本上来说,webkit渲染页面的时候,是从根图层节点开始,进而逐级递归遍历渲染图层树。对于渲染页面的过程,Webkit的代码库里有两种迥异的编码实现过程,一个是软件渲染过程,另一个是硬件加速的渲染过程,前者是传统的渲染模式。

在软件渲染模式中,网页渲染的时候被按照渲染图层树从上而下进行,(译注:图层树体现了图层之间的z轴顺序,所以从上而下实则为从里到外,z轴越小越先被渲染。另外这里的z轴顺序和css里面的z-index有关系但并没有对应关系的,css里面的z-index是元素的,这里指的是渲染图层的。)图层被渲染的复杂过程可以参考下源码里面的RenderLayer::paintLayer()方法,为简明起见下面从这个方法里面简化提取了几个基本的步骤:

  1. 如果图层有效性校验失败,直接退出该方法。【译注:原文是图层与已坏矩形区域(the damage rect,没上下文不知道怎么翻译这玩意)发生交错,则及早退出该方法】
  2. 递归调用该图层的negZOrderList队列中的子图层的paintLayer()方法
  3. 请求该图层关联的渲染对象去绘制自身。【译注:别忘了前面提到的,渲染对象知道如何在显示介质上绘制其对应的dom节点的内容】
  4. 这个请求渲染对象绘制自身的过程,是从创建了该渲染图层的那个渲染对象开始,依据渲染对象树,对其子节点进行递归遍历绘制处理。一旦发现某个渲染对象所关联的渲染图层不是当前被渲染的图层,则停止递归遍历绘制处理。
  5. 递归调用该图层的posZOrderList队列中的子图层的paintLayer()方法

在这个模式中,渲染对象调用一个共享的图形环境对象的绘制方法,将自身绘制到目标位图中,最终被显示器显示出来。【译注:可回头看看前面的“从节点到渲染对象”小节】

要注意的是,图形环境对象本身并没有图层的概念,为了正确绘制半透明效果的图层,需要做些特别的处理:半透明效果的渲染图层在请求其关联的渲染对象绘制自身之前,会先调用图形环境对象的beginTransparencyLayer()方法。在Skia图形库实现的图形环境对象中,调用beginTransparencyLayer()方法,到导致接下来相关的渲染对象绘制自身至新的位图中,当这些渲染对象绘制完毕后,会调用一个对应的endTransparencyLayer()方法。最后,beginTransparencyLayer方法内创建的位图对象,会和paintLayer()方法创建的位图对象组合在一起,最终被显示器显示出来。

【译注:半透明效果的渲染图层,其渲染绘制的过程大概是这样的:paintLayer() => beginTransparencyLayer() => 调用关联的渲染对象的绘制方法 => endTransparenceLayer(),paintLayer和beginTransparencyLayer都会导致在内存中创建位图对象,最后两个位图对象组合在一起最终显示出来。】

从Webkit到显示器

当所有的渲染图层被绘制到一个共享位图之后,该位图需要在显示器上被显示出来。在chrome里面,位图存储于共享内存中,经由IPC(进程间通讯)传输至浏览器进程。浏览器进程负责调用操作系统的视窗API(例如windows操作系统中的HWND),将位图绘制到网页对应的标签页或窗口中去。

[转]如何组织大型JavaScript应用中的代码

本文转自: http://www.csdn.net/article/2013-04-27/2815077-code-organization-angularjs-javascript

在实际项目中,如果单纯的按model、view、controllers、template 等文件夹进行分类,等项目做大或者文件复杂后,就会出现混乱。因为不清楚引用关系。如本文所说的,找不到袜子,虽然知道在哪个抽屉里,但抽屉里内容太多,太类似。

在项目中,我们经常会修改或者维护单一的页面或者某个功能模块。如本文所说按功能模块或者页面进行文件组织确实是个不错的方法。

同时,也可以两者结合起来:先按项目功能分文件夹,里面再按MVC的架构细分文件夹。

每个项目都有一些common之类的功能模块,我建议是,如果一个功能或者函数,有超过2个以上的页面使用,就将它放于common文件夹。

===================================

本文作者Cliff Meyers是一个前端工程师,熟悉HTML5、JavaScript、J2EE开发,他在开发过程中总结了自己在应对JavaScript应用越来越庞大情况下的文件结构,深得其他开发者认可。以下为CSDN编译:

地板上堆放的衣服

首先,我们来看看angular-seed,它是AngularJS应用开发的官方入门项目,其文件结构是这样的:

  • css/
  • img/
  • js/
    • app.js
    • controllers.js
    • directives.js
    • filters.js
    • services.js
  • lib/
  • partials/

看起来就像是把衣服按类型堆在地板上,一堆袜子、一堆内衣、一堆衬衫等等。你知道拐角的那堆袜子里有今天要穿的黑色羊毛袜,但你仍需要花上一段时间来寻找。

这种组织方式很凌乱。一旦你的代码中存在6、7个甚至更多的控制器或者服务,文件管理就会变得难以处理:很难找到想要寻找的对象,源代码控制中的文件也变更集变得难懂。

袜子抽屉

常见的JavaScript文件结构还有另一种形式,即按原型将文件分类。我们继续用整理衣服来比喻:现在我们买了有很多抽屉的衣柜,打算将袜子放在其中一个抽屉里,内衣放在另一个抽屉,再把衬衫整齐地叠在第三个抽屉……

想象一下,我们正在开发一个简单的电子商务网站,包括登陆流程、产品目录以及购物车UI。同样,我们将文件分为以下几个原型:models(业务逻辑和状态)、controllers以及services(HTTP/JSON端点加密),而按照Angular默认那样非笼统地归到“service”架构。因此我们的JavaScript目录变成了这样:

  • controllers/
    • LoginController.js
    • RegistrationController.js
    • ProductDetailController.js
    • SearchResultsController.js
  • directives.js
  • filters.js
  • models/
    • CartModel.js
    • ProductModel.js
    • SearchResultsModel.js
    • UserModel.js
  • services/
    • CartService.js
    • UserService.js
    • ProductService.js

不错,现在已经可以通过树形文件目录或者IDE快捷键更方便地查找文件了,源代码控制中的变更集(changeset)也能够清楚地描述文件修改记录。虽然已经获得了极大的改进,但是仍有一定的局限性。

想象一下,你现在正在办公室,突然发现明天有个商务出差,需要几套干洗的衣服,因此给家里打电话告诉另一半把黑色和蓝色的西装交给清洁工,还有黑纹领带配灰色衬衫、白衬衫配纯黄领带。如果你的另一半并不熟悉衣柜,又该如何从三条黄色的领带中挑出你的正确需求?

模块化

希望衣服的比喻没有让你觉得过于陈旧,下面举一个实例:

  • 你的搭档是新来的开发者,他被要求去修补这个复杂应用中的一处bug。
  • 他扫过这些文件夹,看到了controllers、models、services等文件夹整齐地排列着,但是他仍然不清楚对象间的依赖关系。
  • 处于某些原因,他希望能够重用部分代码,这需要从各个文件夹中搜集相关文件,而且常常会遗漏某些文件夹中的对象。

信或不信,你确实很少会在新项目中重用很多代码,但你很可能需要重用登陆系统这样的整个模块。所以,是不是按功能划分文件会更好?下面的文件结构是以功能划分后的应用结构:

  • cart/
    • CartModel.js
    • CartService.js
  • common/
    • directives.js
    • filters.js
  • product/
    • search/
      • SearchResultsController.js
      • SearchResultsModel.js
    • ProductDetailController.js
    • ProductModel.js
    • ProductService.js
  • user/
    • LoginController.js
    • RegistrationController.js
    • UserModel.js
    • UserService.js

虽然现实世界中有空间限制,难以随意整理服装,但是编程中类似的处理却是零成本的。

现在即使是新来的开发者也能通过顶级文件夹的命名理解应用的功能,相同文件夹下的文件会存在互相依赖等关系,而且仅仅通过浏览文件组织结构就能轻易理解登录、注册等功能的原理。新的项目也可以通过复制粘贴来重用其中的代码了。

使用AngularJS我们可以进一步将相关代码组织为模块:

var userModule = angular.module('userModule',[]);
 
userModule.factory('userService', ['$http', function($http) {
  return new UserService($http);
}]);
 
userModule.factory('userModel', ['userService', function(userService) {
  return new UserModel(userService);
}]);
 
userModule.controller('loginController', ['$scope', 'userModel', LoginController]);
 
userModule.controller('registrationController', ['$scope', 'userModel', RegistrationController]);

如果我们将UserModule.js文件放到user文件夹,它就成了这个模块中使用到的对象的“manifest”,这也是适合RequireJS或者Browserify中放置某些加载指令的地方

如何处理通用代码

每个应用都会有某些代码广泛使用在多个模块中,我们常常使用名为“commom”或者“shared”的文件夹来存放这些功能代码。又该如何处理这些通用代码呢?

  1. 如果模块中的对象需要直接访问几个“通用”对象,为这些对象提供几个Facade(外观模式)。这有助于减少每个对象的依赖者,而过多的关联对象通常意味着糟糕的代码结构。
  2. 如果“通用”模块变得过于庞大,你需要将它按功能领域细分为多个子模块。确保每个应用模块只使用它需要的“通用”模块,这即是SOLID中“接口隔离原则”的变种。
  3. 在根范围($rootScope)添加实体,这样子范围也可以使用,适合多个控制器都依赖同一个对象(比如“PermissionsModel”)的情况。
  4. 在解耦两个不明确互相引用的组件时,请使用事件。Angular中Scope对象的$emit、$broadcast以及$on方法使得这种方式变得现实。控制器能够触发一个事件来执行某些动作,然后再动作结束后收到相应地通知。

原文链接: CLIFF MEYERS

backbone.marionette 资料整理

部分API说明:

上面的中文API转自 http://www.ituring.com.cn/article/31580

官方文档资料: https://github.com/marionettejs/backbone.marionette/tree/master/docs

网上的一些例子,有些文章了,可能和现有版本多少有些差异,但影响不大,对于了解marionette的用法有一定帮助:

1、http://www.smashingmagazine.com/2013/02/11/introduction-backbone-marionette/

2、这里有很多实用的例子可以参考,重点看看那个TODOMVC的那个例子:https://github.com/marionettejs/backbone.marionette/wiki/Projects-and-websites-using-marionette

3、与requireJs相结合:https://github.com/jsoverson/todomvc/tree/master/labs/dependency-examples/backbone_marionette_require

如果不熟requireJs的话 可以先看看中文API文档: http://makingmobile.org/docs/tools/requirejs-api-zh/

4、整理成PDF的文档: marionette-gentle-introduction-sample

php获取文件夹下所有文件,包含子文件夹

貌似php5.3新增了一些新的方法来实现,如 RecursiveIteratorIterator 、 scandir

不过这里还是用最土的方法实现。

/**
 * 获取文件夹下所有文件
 * @param $directory 需要获取的文件夹
 * @param bool $recursive 是否递归获取子文件夹
 * @return array
 */
function directoryToArray($directory, $recursive = false) {
	$array_items = array();
	if(!is_dir($directory)) return "$directory folder does not exist";
	if ($handle = opendir($directory)) {
		while (false !== ($file = readdir($handle))) {
			if ($file != "." && $file != "..") {
				if (is_dir($directory. "/" . $file)) {
					if($recursive) {
						$array_items = array_merge($array_items, directoryToArray($directory. "/" . $file, $recursive));
					}
					$file = $directory . "/" . $file;
					$array_items[] = preg_replace("/\/\//si", "/", $file);
				} else {
					$file = $directory . "/" . $file;
					$array_items[] = preg_replace("/\/\//si", "/", $file);
				}
			}
		}
		closedir($handle);
	}
	return $array_items;
}

调用方法:

$files = directoryToArray('../project/353438199e',true);
var_dump($files);

wampp的alias使用mod_rewrite

使用wampp新建alias的时候,默认是无法使用mod_rewrite模块。

需要做一些配置:

1、wampp开启mod_rewrite

1

2、在alias配置文件里加入如下代码,请自行根据情况修改。

Alias /labs/ "E:/ecc_ecd_rep/labs_proj/trunk/" 

<Directory "E:/ecc_ecd_rep/labs_proj/trunk/">
    Options Indexes FollowSymLinks
    AllowOverride All
    Order deny,allow
    allow from all
</Directory>

其中关键的是这句 AllowOverride All 允许使用mod_rewrite模块

3、在.htaccess文件中,需要使用 RewriteBase ,不然rewrite无法生效。

举个例子:

RewriteEngine On

# Some hosts may require you to use the `RewriteBase` directive.
# If you need to use the `RewriteBase` directive, it should be the
# absolute physical path to the directory that contains this htaccess file.
#
RewriteBase /labs/jdpat/import/api/

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

加上这句 RewriteBase /labs/jdpat/import/api/ 即可。

jQuery使用 Animate + scrollTop 实现页面滑动效果

使用jquery的Animate 方法可以实现页面上下滑动,以往常用的写法是:

$('html, body').animate({
    scrollTop: '0px','fast', function(){
       
    }
});

前几天在写页面滑动插件的时候,需要在animate后执行回调。如下:

$('html, body').animate({
    scrollTop: '0px','fast', function(){
       //这里的代码执行了两次
       $('body').trigger('scrollDone');
    }
});

于是发现,回调内执行了两次。之前一直都没注意到这个问题。

其原因主要是使用了 $('html, body') 作为animate的dom,这样做的目的是为了兼容各浏览器。

webkit 内核的浏览器使用 body 进行滑动,而其他浏览器则使用 html 进行滑动。

这里偷懒的使用了 $('html, body') ,虽然解决了兼容性问题,但是却导致 animate 回调两次的问题。因此该方案并不完美。

于是,可以做下判断,解决兼容性及回调问题:

$($.browser.webkit ? "body": "html").animate({
    scrollTop: '0px','fast', function(){
        $('body').trigger('scrollDone');
    }
});

在jquery 1.9版本后,已经不支持 $.browser.webkit 的方法进行浏览器类型检测了,需要的话,自己通过ua判断下即可

[转]Javascript中的自执行函数表达式

在写插件或我们日常代码中,经常都会用到自执行函数表达式,最简单,最常用的也就是匿名函数自运行了。 
如下代码结构:

(function(){
    //code
})()

网上看到一篇关于这方面的文章,讲得蛮详细,特转载到博客记录一下。

转载地址: http://suqing.iteye.com/blog/1981591

============================================ 
在Bootstrap源码(具体请看《Bootstrap源码解析》)和其他jQuery插件经常看到如下的写法:

+function ($) {   
  
}(window.jQuery);

这种写法称为: 
IIFE (Imdiately Invoked Function Expression 立即执行的函数表达式)。

一步步来分析这段代码。

先弄清 函数表达式(function expression)和 函数声明(function declaration)的区别: 
函数表达式 Function Expression – var test = function() {}; 
函数申明 Function Declaration – function test() {};

函数表达式中的函数可以为匿名函数,也可以有函数名,但是该函数实际上不能直接使用,只能通过表达式左边的变量 a 来调用。

var a = function(){  
  alert('Function expression');  
}  
var b = new a();

函数声明时必须有函数名。

function a(){  
  alert('Function declaration');  
}  
a();

这是一个匿名函数。

function () {  
  
}

你也许注意到匿名函数在console下会报错。console的执行和报错如下:

function(){}

 
SyntaxError: Unexpected token (

通过一元操作符+变成了函数表达式。也可以使用 – ~ !等其他一元运算符或者括号,目的是为了引导解析器,指明运算符附近是一个表达式。以下是三种经典方式 :

+function () {   
  
};  
  
(function () {  
  
});  
  
void function() {  
  
};

函数表达式通过 末尾的() 来调用并运行。就是一个IIFE。

+function () {   
  
}();  
  
(funtion () {  
  
})();

代码性能 
运算符:+加-减!逻辑非~位取反,返回NaN(Not A Number)。

“()”组运算符:返回表达式的执行结果undefined。

void:按运算符结合语句执行,返回 undefined。 
这几种的性能对比结果:

可见+性能最差(在Firefox下差距更明显),其他几种都差不多。不过IIFE只执行一遍,对js执行效率的影响特别小,使用哪种还是看个人爱好。

传参,为了避免$与其他库或者模板申明冲突,window.jQuery 作为参数传递。

+function (x) {  
    console.log(x);  
}(3);  
  
+function ($) {  
  
}(window.jQuery);

使用IIFE的好处

总结有4点: 提升性能、有利于压缩、避免冲突、依赖加载

1、减少作用域查找。 使用IIFE的一个微小的性能优势是通过匿名函数的参数传递常用全局对象window、document、jQuery,在作用域内引用这些全局对象。JavaScript解释器首先在作用域内查找属性,然后一直沿着链向上查找,直到全局范围。将全局对象放在IIFE作用域内提升js解释器的查找速度和性能。

传递全局对象到IIFE的一段代码示例:

// Anonymous function that has three arguments  
function(window, document, $) {  
  
  // You can now reference the window, document, and jQuery objects in a local scope  
  
}(window, document, window.jQuery); // The global window, document, and jQuery objects are passed into the anonymous function

2、有利于压缩。 另一个微小的优势是有利于代码压缩。既然通过参数传递了这些全局对象,压缩的时候可以将这些全局对象匿名为一个字符的变量名(只要这个字符没有被其他变量使用过)。如果上面的代码压缩后会变成这样:

// Anonymous function that has three arguments  
function(w, d, $) {  
  
  // You can now reference the window, document, and jQuery objects in a local scope  
  
}(window, document, window.jQuery); // The global window, document, and jQuery objects are passed into the anonymous function

3、避免全局命名冲突 。当使用jQuery的时候,全局的window.jQuery对象 作为一个参数传递给$,在匿名函数内部你再也不需要担心$和其他库或者模板申明冲突。 正如James padolsey所说: 
An IIFE protects a module’s scope from the environment in which it is placed.

4、通过传参的方式,可以灵活的加载第三方插件。 (当然使用模块化加载更好,这里不考虑。)举个例子,如果a页面需要使用KindEditor,a.html引入kindeditor.js 和 a.js 
你可能会这么写 a.js:

$(function() {  
  
   var editor  
    KindEditor.ready(function(K) {  
  
        editor = K.create('textarea[data-name="kindeditor"]', {  
            resizeType : 1  
        })  
    })  
  
})

b页面不需要使用Kindeditor,没有引入kindeditor.js。但是在合并JS代码后,b页面也会执行a.js中的代码,页面报错Uncaught ReferenceError: KindEditor is not defined。也就是b页面执行了KindEditor,难道所有页面都要加载Kindeditor源文件? 
可以这么修改a.js,将KindEditor变量参数化,通过给立即执行的函数表示式的参数赋值,那么其他页面都不需要加载Kindeditor源文件

+function( KindEditor){  
  
    var editor  
    if(KindEditor){  
        KindEditor.ready(function(K) {  
  
	  editor = K.create('textarea[data-name="kindeditor"]', {  
	  resizeType : 1  
	  })  
        })  
    }  
  
}(KindEditor || undefined)

IIFE最佳实践 
反对使用IIFE的其中一个理由是可读性差,如果你有大量的JavaScript代码都在一段IIFE里,要是想查找IIFE传递的实际参数值,必须要滚动到代码最后。幸运的是,你可以使用一个更可读的模式:

(function (library) {  
  
    // Calls the second IIFE and locally passes in the global jQuery, window, and document objects  
    library(window, document, window.jQuery);  
  
}  
  
// Locally scoped parameters   
(function (window, document, $) {  
  
// Library code goes here  
  
}));

这种IIFE模式清晰的展示了传递了哪些全局对象到你的IIFE中,不需要滚动到长文档的最后。

jQuery优化 
一段看上去写法有点像的代码。大部分项目用这段代码做作用域,这段代码会在DOM加载完成时初始化jQuery代码。

$(function(){   
  
});

这种写法等同于

$(document).ready(function(){  
// 在DOM加载完成时初始化jQuery代码。  
});

区别于

$(window).load(function(){  
// 在图片等媒体文件加载完成时,初始化jQuery代码。  
});

结合IIFE的最佳实践,更好的写法是,立即执行document ready

+function ($) {  
  
  $(function(){  
  
  })  
  
}(window.jQuery)

最佳实践

// IIFE - Immediately Invoked Function Expression  
  +function(yourcode) {  
  
    // The global jQuery object is passed as a parameter  
    yourcode(window.jQuery, window, document);  
  
  }(function($, window, document) {  
  
    // The $ is now locally scoped   
  
   // Listen for the jQuery ready event on the document  
   $(function() {  
  
     // The DOM is ready!  
  
   }));

具体请看工程师,请优化你的代码

其他 
在Bootstrap和其他插件中经常看到如下写法:

+function ($) { "use strict";    
    
}(window.jQuery);

关于字符串”use strict”;请看严格模式

参考资料: 
《Javascript高级程序设计(第3版)》 7.3 模仿块级作用域 
Immediately-Invoked Function Expression (IIFE) – Ben Alman 
ECMA-262-3 in detail. Chapter 5. Functions. – Dmitry A. Soshnikov 
Functions and function scope – Mozilla Developer Network 
Named function expressions – Juriy “kangax” Zaytsev 
JavaScript Module Pattern: In-Depth – Ben Cherry 
Closures explained with JavaScript – Nick Morga 
what does function function window jquery do – Stackoverflow

http://gregfranko.com/blog/i-love-my-iife/