Giter VIP home page Giter VIP logo

mujiang.info's Introduction

mujiang.info

这段时间陷入了迷茫,感觉前端技术积累太少、太浅。在GDG Devfest上遇到米粽,聊了很久。发现很可能是总结太少的问题,所以我觉得遵从他的建议,开始写博客了。

临时记录一条导出数据的sql http://dev.mysql.com/doc/refman/5.1/en/load-data.html

select * from table where dateline > 1346516000 into outfile 'path_to_file' fields terminated by ',' enclosed by '"' lines terminated by '\r\n'

mujiang.info's People

Contributors

ericdum avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mujiang.info's Issues

看AngularJS文档需要注意的一些关键点

看AngularJS文档需要注意的一些关键点

我们这些英语不好的人,看英文文档最容易忽略的就是大段大段的文字。转而去看示例代码和代码解释,更有甚者是直接去看API文档、看源码。但是这样要把一个框架的结构和使用方式看懂是很有难度的,就像盲人摸像,如果不把整个大象全身摸完,是无法正确理解这头象的。

同时“没有万能的东西”这句话也能用在框架上,任何一个东西都有他特定的应用场景和思路。所以选择并使用一个东西的时候一定要关注作者的设计思路是不是和你预期的一致,也就是,你是不是作者的典型用户。如果是,这个优秀的框架一定能把你服务得很好;如果不是,换一个,或者试着改变自己的思路。

Angular尤其是一个颠覆传统web应用开发模式的框架,所以尤其需要关注,甚至是直接学习他的思路。

这篇文章就是把Developer Guide中的部分我觉得重要的信息摘选出来翻译。大多都不包含代码和具体用法,因为那些实例我们都很熟。

概念

Conceptual Overview

这是在创建第一个app之前就应该理解的Angular的概念。

概念 定义
Template 使用了额外标记的HTML
Directives 扩展的HTML属性和元素
Model 显示给用户交互的数据
Scope 储存Model的上下文(Context)供控制器、指令、表达式访问
Expressions 从Scope中访问变量和函数
Compiler 解析模板,并实例化指令和表达式
Filter 格式化表达式的值展示给用户
View 用户看到的东西(DOM)
Dat]a Bindin 在model和view之间同步数据
Controller View背后的业务逻辑
Dependency Injection] 创建并连接对象
Injector 依赖注入容器(dependency injection container)
Module 配置Injector
Service 不依赖view的可重用的业务逻辑

理解控制器

Understanding Controller

当DOM动过ng-controller指令指定一个控制器时,Angular会用指定的控制器构造函数实例化一个新控制器对象。一个新的child scope会被注入成为这个构造函数的参数——$scope。

正确地使用控制器(Controller)

Using Controllers Correctly

控制器一般不要做太多的事情,只应该包含一个view需要的业务逻辑。

保持控制器干净的一个通用的方法是把不属于这个控制器的工作封装成一个服务,然后通过依赖注入去用它。这些在Dependency Injection Services的章节讨论。

不要把控制器用来做:

  • 任何DOM操作 —— 控制器只应该包含业务逻辑。DOM操作是公认的难以测试的东西,把展示逻辑放在控制器会显著地影响业务逻辑的可测试性。Angular提供数据绑定来自动完成DOM操作。如果必须要进行你自己的一些DOM操作,就用指令(directive)把这些逻辑封装起来。
  • 格式化输入 —— 用angular form controls代替
  • 过滤输出 —— 用angular filters
  • 跨控制器共享stateless和stateful代码 —— 用angular services代替
  • 管理其他组件的生命周期

Filters

filters的API是filterProvider

本章没啥东西,不翻译了,摘选点我觉得重要的东西:

  • DOM中使用:{{ expression | filter1:arguments1:args2... | filter2 }}
  • 控制器中使用:只需要将控制器对filter的依赖写入控制器的参数,然后直接调用就行了。关键是依赖的名字:这个名字是filter的名字+"Filter"后缀,如reversefilter的名字就是reverseFilter,示例看这里
  • 自定义:angular.module(...).filter('reverse', function(){ return function(){ return result;}}),就是说,将一个返回值是过滤函数的函数传给module的filter方法的第二个参数。第一个参数是要自定的filter的名字(不含"Filter"后缀)。

Templates

Templates

在模板中能用的Angular元素和元素的属性:

  • Directive —— 作为已有DOM的属性或表现为可重用的DOM组件(译者注:一大堆拗口的废话就是说“元素属性或自定义元素”)
  • Markup —— {{}}用来输出一个表达式
  • Filter —— 格式化输出
  • Form Controls —— 用户输入验证

什么是Angular Service

What are Angular Services?

Angular Service是执行web app通用任务的函数或者对象。Angular有一系列内置的服务如:提供浏览器ajax请求的$http service。像其他Angular变量、标示符一样,内置的服务总是用$开头(如前面的$http)。你也可以定义自己的服务。

使用服务

Using Service

要使用服务的时候,需要把服务标示为依赖传给组件(controller、service、filter、directive)。Angular的依赖注入子系统会处理剩下的事情。injector子系统负责服务的实例化、解析依赖以及给组件提供依赖。

Angular的依赖注入使用“constructor” injection。它将依赖传递给组件的工厂或构造函数。由于js是一个动态类型的语言,Angular的依赖注入子系统不能用静态类型来标识服务依赖。正因如此,组件必须明确地用injection annotation来定义它的依赖。比如提供一个$inject属性:

    var MyController = function($location) { ... };
    MyController.$inject = ['$location'];
    myModule.controller('MyController', MyController);

或者提供一个"inline" injection annotion

    var myService = function($http) { ... };
    myModule.factory('myService', ['$http', myService]);

定义服务

Defining a Service

开发正可以在Angular的Module中通过注册一个名字和**服务工厂函数(service factory function)**来定义自己的服务。

服务工厂函数是为了创建一个服务的对象或函数,以便使用。这个对象或函数会被注入到指定了这个依赖的组件中。

Angular的工厂函数是懒执行的。就是说,他们只会在需要满足依赖的时候执行,并且每个服务都只会被执行一次。任何依赖服务的东西获取到服务的引用都是工厂函数生成的。

使用$location

Using $location

$location服务基于window.location来解析地址栏的URL。改变地址栏的URL会被反射到$location服务,改变$location也会反射到地址栏。

它不是为了在改变URL后整页面刷新设计的,如果要这样做,用低级的API——$window.location.href。

$location服务:

  • 暴露当前的URL,让你能:
    • watch、observe它
    • 改变它
  • 保持自身与浏览器同步,当用户:
    • 改变地址栏的URL
    • 点击浏览器的前进、后退按钮(或者历史链接)
    • 点击页面中的链接

对比一下:

window.location $location service
用途 读写浏览器的地址 一样
API 暴露原始对象并能被直接更改 暴露jquery风格的getter和setter
集成到angular的生命周期 没有 了解内部所有生命周期的阶段,并与$watch等整合
无缝集成HTML5 API 没有 是(使用fallback到老浏览器)
aware of docroot/context from which the application is loaded no - window.location.path returns "/docroot/actual/path" yes - $location.path() returns "/actual/path"

$location的Hashbang和HTML5模式

Hashbang and HTML5 Modes

$location服务可以配置两种模式来控制URL的格式:Hashbang模式(默认)和使用HTML5 History API的HTML5模式。应用在两种模式下都使用相同的API。

Hashbang模式 HTML5模式
配置 默认 {html5Mode:true}
URL格式 所有浏览器都使用hashbang URLs 在现代浏览器中使用正规的URLs,在老浏览器中使用hashbang
链接重写
需要服务器端配置

hashbang_vs_regular_url

要使用HTML5模式,必须在服务器端设置重写规则,简单来说就是你必须把所有链接指向入口文件。如index.html。

$location的HTML链接重写

HTML link rewrite

当用HTML5 history API模式的时候,你可能需要在不同的浏览器上使用不同的链接。但你真正需要做得只是指定一个标准的URL链接,比如: <a href="/some?foo=bar">link</a>

当用户点击链接的时候,在老浏览器中URL会自动变为/index.html#!/some?foo=bar

在下面这些情况中,链接不会被重写,而浏览器会进行整页的刷新

  • 链接含有target属性:<a href="/ext/link?a=b" target="_self">link</a>
  • 到其他域名的绝对链接:<a href="http://angularjs.org/">link</a>
  • 当base有定义时,通过/开头的链接指向其他base

当在根域名运行angular的时候,可能有平行的其他应用在同一目录,otherwise路由会尝试处理所有URL,包括静态文件。

要预防这个问题,你可以给应用设置base:<base href=".">然后给要被处理的URL架上.前缀。现在,不使用angular路由的链接不会有.前缀,也就不会被$routeProvider中的otherwise规则所拦截了。

$location警告

页面重载跳转

$location服务只允许改变URL,而不允许重载页面。当你需要改变URL、重载页面或者要跳转到其他页面,请用低等级的API:$window.location.href

在scope的生命周期以外使用$location

$location了解Angular scope的生命周期。当浏览器的URL改变的时候,它会更新$location并调用$apply,所有$wathers/$observers都会被通知到。在$digest期间改变$location是完全没有问题的,$location会将这个改变传递到浏览器并会通知所有的$wathers/$observers。当你要从angular外部改变$location的时候(如触发DOM事件或者测试),一定要调用$apply来传递。

$location.path() and ! or / prefixes

一个路径应该总是由/开头;如果缺失的话,$location.path()设置器会自动加入反斜线。

注意hashbang中的!前缀不是$location.path()的一部分,它实际上是hash的前缀。

未完

Mac升级了10.10 Yosemite的同学打不开iPhoto, iMove, FinalCut的解决方案来了

升级了10.10的同学上述的3个应用前都会打上一个X。
image
强行打开会弹出提示说不支持。

解决方案来了:

  1. 到百度云盘下载http://pan.baidu.com/s/1hqh4O9u 对应的包解压。
  2. 首先在应用文件夹找到它们的图标,然后点右键,选择“Show Package Content”,自行翻译。
    image
  3. 打开Contents文件夹
  4. 备份info.plist和version.plist文件,如打包:
    image
  5. 复制解压出来的plist文件,覆盖刚才的文件
    image
  6. 弹出的提示中选replace
    image
  7. 然后就可以完美地得到解开的应用了
    image
  8. 双击,enjoy

png8支持透明通道(一)

每一次有新同学加入,除了讲代码,还有一个必须要说得就是图片的格式选择和压缩。

看之前请回答几个问题:

  1. 你知道jpg、gif、png的区别吗?
  2. 你知道png有8位、24位、32位吗?
  3. ie6透明问题只能用滤镜这类的方法来解决吗?
  4. 你知道png8可以有半透明(alpha通道)吗?

如果你对上面的问题有否定的回答,那么我确定这篇文章值得你一看。

图片格式的选择

根据需求选择合适的图片格式是每一个前端工程师的义务。一般会要求美术同学给到原始的图片或psd,然后前端再进行格式转化和压缩。

为什么不让美术同学直接给到压缩好的图片呢?主要有两个考虑。

  1. 降低沟通成本
  2. 技术问题让技术解决

下面先简单地介绍一下各种格式的图片,随手写来,不准确的地方欢迎拍砖,我会一一回复、改正。

jpg

jpg(jpeg)是一种有损压缩的图片具体的解释见:http://zh.wikipedia.org/wiki/JPEG

什么叫有损压缩?有损压缩就是为了将图片压缩到更小,利用人眼观察图片的特点改变某些丢弃掉一些人眼不易察觉的原始颜色信息,而这个是不可逆的。

这种压缩方式优点很明显

  • 压缩比高(可调)
  • 颜色丰富
  • 边缘平滑

缺点也很明显

  • 降低画质(可调)
  • 不能重复压缩(越压画质越差,每次压缩都会丢失信息)

gif

gif是一种无损压缩8位位图可以实现动画,具体见:http://zh.wikipedia.org/wiki/Gif

什么叫8位位图?分两段来看,“位图”指详细记录了每个像素点的颜色信息的图片,“8位”指每一个像素点有8个2二进制位,有计算机基础知识的就知道8位就能表达256个颜色。

优点:

  • 动画
  • 无损

缺点:

  • 256色
  • 压缩比不如png8(不一定,稍后讲)
  • 不支持透明

png

png通常说的是png24/png32,是一种无损压缩的带alpha通道(透明度)的位图。http://zh.wikipedia.org/wiki/PNG

什么是png24/png32?png24就是24位png——8位_3通道(RGB红绿蓝)。png32就是32位png——8位_4通道(RGBA红绿蓝透明)

优点:

  • 颜色丰富
  • 支持alpha通道
  • 无损

缺点:

  • 图片太大。
  • ie6不支持alpha通道

png8

还有一种png是png8,是一种无损压缩的带alpha通道的8位位图,使用索引。把他单独拿来说是因为很多人都不熟悉他,他也是本文的重点。

什么是使用索引?具体实现我也不太清楚,可以理解为在图片前建立256个颜色的索引,如定义:0表示#ff1201,1表示#ff0000......然后再分别在每个像素点上直接使用0、1这样的标号。也可以直接简单地理解为:没有动画,但有alpha通道的gif。

优点:

  • 现有工具压缩算法好
  • 支持alpha通道
  • ie6也支持alpha通道
  • 无损

缺点:

  • 256色

实际应用

虽然每种格式都有相应地缺点,但是根据不同的需求,总是能找到最适合的。

  • jpg通常用在颜色复杂、但不要求特别精细的图片上,如照片、宣传图、背景等。
  • png通常用在颜色复杂并且要求特别精细或者有透明需求的图片上,如复杂的logo、图标等。由于无损还可以当做“原图”来存档使用。
  • png8在png的基础上“减去”颜色复杂。
  • gif由于静态部分可以被png8代替,所以通常只用于简单的动画。

png的压缩

其他压缩方式先不说了,用firework是可以满足需求的。而之所以很多人不知道png8支持alpha通道我认为很大程度上就是因为photoshop和firework不支持。所以需要其他工具来压缩。

待续……

这两周由于团队有大变动,我又接手了一些运维方面的事情,有大量的东西需要学。所以篇文章的后半部分需要延期了,这周补上。

内容预告,急性子同学可自行补:

浏览器阻塞探究

上周发现虾米音乐的js都放在head里面,心想不是会卡进程么?和虾米音乐的同学聊了之后发现他们认为这个网站功能和js密切相关,所以不希望让用户看到不可用的页面。

根据产品选择技术可能无可厚非,其他优化方案这里不谈,我就想看看卡渲染进程到底是怎么卡的。

所以有了以下思路,

  1. 在head中,用while去无限循环跑3秒。
  2. 进入body,输出
  3. 再用同样的方式跑3秒,输出
  4. 外调一个3秒网络延时的js,同时执行其他js
  5. 同时外调一个5秒、10秒网络延时的js,同时执行其他js

期望:

  1. 无限循环js卡住进程导致白屏
  2. 上一步结束后输出
  3. body中的无限循环再次卡住进程,证明head、body无异
  4. 网络进程依次加载js、执行js
  5. 总执行时间3+3+3+5+10 = 24秒

循环代码:

    var time1 = (new Date()).getTime();
    while(((new Date()).getTime()-time1) < 3000);

加载外部js,并即时的代码:

    <script>
        var time = (new Date()).getTime();
        var intv = setInterval(function(){ timer3.innerText = ((new Date()).getTime()-time)/1000 }, 50);
    </script>
    <script onload="clearInterval(intv);" src="http://mujiang.info/delayload/3s.php"></script>

开始测试

查看测试、代码: http://cdpn.io/ABnmI

结果:

qq20131024-1

safari和chrome都类似。但是呈现的顺序不一样。chrome等到两段卡3秒的循环都结束了才呈现。但safari第一段js结束就开始呈现了。

可以看到外调js种,5秒、10秒的测试之用了7秒左右的时间。再去查看timeline,原来第一个三秒的js在请求的同时就开始请求5秒、10秒的js了。

所以来看看完成的期望:

  1. 无限循环js卡住进程导致白屏
  2. body中的无限循环再次卡住进程,证明head、body无异

没完成的是:

  1. 上一步结束后输出 —— 浏览器结果不一致
  2. 网络进程依次加载js、执行js —— 同时加载
  3. 总执行时间3+3+3+5+10 = 24秒 —— 总时间位3+3+3+7 = 16秒,不过这是好事。

至此,又有了几个新的思考:

  1. 既然所有js同时加载,为什么不在一开头就加载呢?(前面有6秒等候时间)。
  2. HTML5 的 async属性能不能防止网络阻塞,执行顺序和加载顺序一样吗。
  3. HTML4 的 defer属性能不能防止执行阻塞。

回答思考

这是刚开在chrome中的Network视图,看着3个js加载之前长长的空白,不觉得蛋微微有点疼么?浪费可耻啊!那6秒都干了些什么!

qq20131024-2

解决方案很简单,很容易观察出来第一段js加载的时候,其他的js就都跟着加载了。

那么我在head最开始就加一段js会怎么样呢?

效果:http://mujiang.info/test/browserjam/index2.php

可以看到执行时间被压到了10秒,快乐整整6秒,就是刚才前面卡的6秒。(口水都流出来了)

Network: (多出来的js就是刚加的js)

qq20131024-3

至于浏览器同学一次能同时加载多少个文件呢?safari、chrome都是6个。其他浏览器没试。

qq20131024-6

话说加载图片会不会占线程呢?答案是会。总数就是6个。就不给图了

async的问题就更简单了,async加载的js不仅不会阻塞解析,还不会阻塞其他js的执行。

效果:http://mujiang.info/test/browserjam/async.php

asd1

至于defer属性,看了文档说被标记的js会在页面解析(parsing)完成之后执行。按照我的理解,浏览器parsing完之后还有构建DOM、构建render tree、layout和parinting的过程。所以如果真的只是不影响parsing的话,那就能解释结果为什么观察不出结果:http://mujiang.info/test/browserjam/defer.php

总结一下

  1. 大量的代码放在前面会导致“白屏”
  2. 当浏览器遇到第一个script的时候会把所有script都拿来下载。
  3. 同时下载的进程为6个。图片、js共享。
  4. async可以避免阻塞。但是js执行顺序为改变。

本文用到的代码都在这里:
https://github.com/ericdum/mujiang.info/tree/master/test/browserjam

好吧,我偏题了,没测出safari有啥好的,除了渲染更新更快(也蛮好的)。不过这些东西我觉得比safari得自身情况更有意思。safari到底怎么提升了速度过几天在看了。

踩到一个MySQL的坑,它选择了错误的索引

刚才收到服务器报警,MySQL连接数量剧增。查了一下日志,发现了一条极为简单地查询,但却耗费了14秒左右的查询时间。

SELECT ... 
FROM ... 
WHERE `user_id` = 000 AND `app` = "xxx" 
ORDER BY `time` 
LIMIT 1;

看了一下这个表的索引,这里user_id、app、time分别有一个索引。由于这个表的可搜索字段非常多,所以这些索引都是只有一层的。

用EXPLAIN解释一下发现这条查询没有使用我们期望的user_id来作为索引,而使用了time。rows为443万。

+----+-------------+--------+-------+------------------------+------------+---------+------+---------+-------------+
| id | select_type | table  | type  | possible_keys    | key  | key_len | ref  | rows    | Extra       |
+----+-------------+--------+-------+------------------------+------------+---------+------+---------+-------------+
|  1 | SIMPLE      | xxxxxx | range | app,time,user_id | time | 4       | NULL | 4432792 | Using where |
+----+-------------+--------+-------+------------------------+------------+---------+------+---------+-------------+

这就是问题所在了,根据这个解释,这条查询要遍历、比较443万行数据才能返回结果。

做了几个小时的实验,搜了很多关于innoDB/MySQL索引选择的资料都没有很好地解释这个问题。

只是找到了造成选择time做索引的几个必要条件就是:

  1. user_id做索引时rows足够大,本例中达到5000
  2. 使用time作为排序条件
  3. LIMIT足够小,本例中小于等于2

而造成查询过慢的原因还有:

  1. 结果为空(本例中需要遍历443万行数据)

我猜测在MySQL的索引优化逻辑中先检测了user_id发现rows为5000,因为要time排序所以必然要遍历这5000行。

然后检查到time虽然有几百万行,但它是有序的,只要找到1、2(limit)个符合规则的结果就可以停止遍历。

所以MySQL认为使用time可能更有效。

对于本例,可以使用一个非常暴力的FORCE INDEX来固定选择user_id做索引。不过这样代码和数据库耦合度太高。

根据业务逻辑,可以换为由id来排序,效果立杆见影,再EXPLAIN一下。

+----+-------------+--------+------+------------------------+---------+---------+-------+------+-------------+
| id | select_type | table  | type | possible_keys    | key     | key_len | ref   | rows | Extra       |
+----+-------------+--------+------+------------------------+---------+---------+-------+------+-------------+
|  1 | SIMPLE      | xxxxxx | ref  | app,time,user_id | user_id | 5       | const | 5010 | Using where |
+----+-------------+--------+------+------------------------+---------+---------+-------+------+-------------+

能力有限,先就这样了,有什么发现再来补充。

npm的package.json中文文档

以前看nodejs只是看看API没有具体看这些细节,把别人的package.json拿来抄一抄,看字段也知道留下name、version、private、dependencies就好了,能方便地npm install部署就可以了,也没去深究。现在想在用起来在架构上面就遇到了很多问题。包括文件结构、管理、部署、重启……
昨天想先了解一下YO、GRUNT、Bower这一套东西正好看到了package.json的devDependencies字段。找到这个文档看,看着看着就看成了中文,也给大家送送福利。这样大家看起来就快多了。
有什么问题希望大家快快拍砖。

package.json(5)网页版

简介

本文档有所有package.json中必要的配置。它必须是真正的json,而不是js对象。

本文档中描述的很多行为都受npm-config(7)的影响。

默认值

npm会根据包内容设置一些默认值。

  • "scripts": {"start": "node server.js"}

    如果包的根目录有server.js文件,npm会默认将start命令设置为node server.js

  • "scripts":{"preinstall": "node-waf clean || true; node-waf configure build"}

    如果包的根目录有wscript文件,npm会默认将preinstall命令用node-waf进行编译。

  • "scripts":{"preinstall": "node-gyp rebuild"}

    如果包的根目录有binding.gyp文件,npm会默认将preinstall命令用node-gyp进行编译。

  • "contributors": [...]

    如果包的根目录有AUTHORS文件,npm会默认逐行按Name <email> (url)格式处理,邮箱和url是可选的。#号和空格开头的行会被忽略。

name

在package.json中_最_重要的就是name和version字段。他们都是必须的,如果没有就无法install。name和version一起组成的标识在假设中是唯一的。改变包应该同时改变version。

name是这个东西的名字。注意:

  • 不要把node或者js放在名字中。因为你写了package.json它就被假定成为了js,不过你可以用"engine"字段指定一个引擎(见后文)。
  • 这个名字会作为在URL的一部分、命令行的参数或者文件夹的名字。任何non-url-safe的字符都是不能用的。
  • 这个名字可能会作为参数被传入require(),所以它应该比较短,但也要意义清晰。
  • 在你爱上你的名字之前,你可能要去npm registry查看一下这个名字是否已经被使用了。http://registry.npmjs.org/

version

在package.json中_最_重要的就是name和version字段。他们都是必须的,如果没有就无法install。name和version一起组成的标识在假设中是唯一的。改变包应该同时改变version。

version必须能被node-semver解析,它被包在npm的依赖中。(要自己用可以执行npm install semver

可用的“数字”或者“范围”见semver(7).

description

放简介,字符串。方便屌丝们在npm search中搜索。

keywords

关键字,数组、字符串。还是方便屌丝们在npm search中搜索。

homepage

项目官网的url。

注意:这和“url”_不_一样。如果你放一个“url”字段,registry会以为是一个跳转到你发布在其他地方的地址,然后喊你滚粗。

嗯,滚粗,没开玩笑。

bugs

你项目的提交问题的url和(或)邮件地址。这对遇到问题的屌丝很有帮助。

差不多长这样:

{ "url" : "http://github.com/owner/project/issues"
, "email" : "[email protected]"
}

你可以指定一个或者指定两个。如果你只想提供一个url,那就不用对象了,字符串就行。

如果提供了url,它会被npm bugs命令使用。

license

你应该要指定一个许可证,让人知道使用的权利和限制的。

最简单的方法是,假如你用一个像BSD或者MIT这样通用的许可证,就只需要指定一个许可证的名字,像这样:

{ "license" : "BSD" }

如果你又更复杂的许可条件,或者想要提供给更多地细节,可以这样:

"licenses" : [
  { "type" : "MyLicense"
  , "url" : "http://github.com/owner/project/path/to/license"
  }
]

在根目录中提供一个许可证文件也蛮好的。

people fields: author, contributors

author是一个人。contributors是一堆人的数组。person是一个有name字段,可选的有url、email字段的对象,像这样:

{ "name" : "Barney Rubble"
, "email" : "[email protected]"
, "url" : "http://barnyrubble.tumblr.com/"
}

或者可以把所有的东西都放到一个字符串里,npm会给你解析:

"Barney Rubble <[email protected]> (http://barnyrubble.tumblr.com/)

email和url在两种形式中都是可选的。

也可以在你的npm用户信息中设置一个顶级的maintainers字段。

files

files是一个包含项目中的文件的数组。如果命名了一个文件夹,那也会包含文件夹中的文件。(除非被其他条件忽略了)

你也可以提供一个.npmignore文件,让即使被包含在files字段中得文件被留下。其实就像.gitignore一样。

main

main字段配置一个文件名指向模块的入口程序。如果你包的名字叫foo,然后用户require("foo"),main配置的模块的exports对象会被返回。

这应该是一个相对于根目录的文件路径。

对于大多数模块,它是非常有意义的,其他的都没啥。

bin

很多包都有一个或多个可执行的文件希望被放到PATH中。npm让妈妈再也不用担心了(实际上,就是这个功能让npm可执行的)。

要用这个功能,给package.json中的bin字段一个命令名到文件位置的map。初始化的时候npm会将他链接到prefix/bin(全局初始化)或者./node_modules/.bin/(本地初始化)。

比如,npm有:

{ "bin" : { "npm" : "./cli.js" } }

所以,当你初始化npm,它会创建一个符号链接到cli.js脚本到/usr/local/bin/npm

如果你只有一个可执行文件,并且名字和包名一样。那么你可以只用一个字符串,比如:

{ "name": "my-program"
, "version": "1.2.5"
, "bin": "./path/to/program" }

结果和这个一样:

{ "name": "my-program"
, "version": "1.2.5"
, "bin" : { "my-program" : "./path/to/program" } }

man

指定一个单一的文件或者一个文件数组供man程序使用。

如果只提供一个单一的文件,那么它初始化后就是man <pkgname>的结果,而不管实际的文件名是神马,比如:

{ "name" : "foo"
, "version" : "1.2.3"
, "description" : "A packaged foo fooer for fooing foos"
, "main" : "foo.js"
, "man" : "./man/doc.1"
}

这样man foo就可以用到./man/doc.1文件了。

如果文件名不是以包名开头,那么它会被冠以前缀,下面的:

{ "name" : "foo"
, "version" : "1.2.3"
, "description" : "A packaged foo fooer for fooing foos"
, "main" : "foo.js"
, "man" : [ "./man/foo.1", "./man/bar.1" ]
}

会为man fooman foo-bar创建文件。

man文件需要以数字结束,然后可选地压缩后以.gz为后缀。The number dictates which man section the file is installed into.

{ "name" : "foo"
, "version" : "1.2.3"
, "description" : "A packaged foo fooer for fooing foos"
, "main" : "foo.js"
, "man" : [ "./man/foo.1", "./man/foo.2" ]
}

会为man fooman 2 foo创建。

directories

CommonJS Packages规范说明了几种方式让你可以用directorieshash标示出包得结构。如果看一下npm's package.json,你会看到有directories标示出doc, lib, and man。

在未来,这个信息可能会被用到。

directories.lib

告诉屌丝们你的库文件夹在哪里。目前没有什么特别的东西需要用到lib文件夹,但确实是重要的元信息。

directories.bin

如果你指定一个“bin”目录,然后在那个文件夹中得所有文件都会被当做"bin"字段使用。

如果你已经指定了“bin”字段,那这个就无效。

directories.man

一个放满man页面的文件夹。贴心地创建一个“man”字段。
A folder that is full of man pages. Sugar to generate a "man" array by
walking the folder.

directories.doc

将markdown文件放在这里。最后,这些会被很好地展示出来,也许,某一天。
Put markdown files in here. Eventually, these will be displayed nicely,
maybe, someday.

directories.example

将事例脚本放在这里。某一天,它可能会以聪明的方式展示出来。

repository

指定你的代码存放的地方。这个对希望贡献的人有帮助。如果git仓库在github上,那么npm docs命令能找到你。

这样做:

"repository" :
  { "type" : "git"
  , "url" : "http://github.com/isaacs/npm.git"
  }

"repository" :
  { "type" : "svn"
  , "url" : "http://v8.googlecode.com/svn/trunk/"
  }

URL应该是公开的(即便是只读的)能直接被未经过修改的版本控制程序处理的url。不应该是一个html的项目页面。因为它是给计算机看的。

scripts

“scripts”是一个由脚本命令组成的hash对象,他们在包不同的生命周期中被执行。key是生命周期事件,value是要运行的命令。

参见 npm-scripts(7)

config

"config" hash可以用来配置用于包脚本中的跨版本参数。在实例中,如果一个包有下面的配置:

{ "name" : "foo"
, "config" : { "port" : "8080" } }

然后有一个“start”命令引用了npm_package_config_port环境变量,用户可以通过npm config set foo:port 8001来重写他。

参见 npm-config(7)npm-scripts(7)

dependencies

依赖是给一组包名指定版本范围的一个hash。这个版本范围是一个由一个或多个空格分隔的字符串。依赖还可以用tarball或者git URL。

**请不要将测试或过渡性的依赖放在dependencieshash中。**见下文的devDependencies

详见semver(7).

  • version 必须完全和version一致
  • >version 必须比version
  • >=version 同上
  • <version 同上
  • <=version 同上
  • ~version 大约一样,见semver(7)
  • 1.2.x 1.2.0, 1.2.1, 等,但不包括1.3.0
  • http://... 见下文'依赖URL'
  • * 所有
  • "" 空,同*
  • version1 - version2>=version1 <=version2.
  • range1 || range2 二选一。
  • git... 见下文'依赖Git URL'
  • user/repo 见下文'GitHub URLs'

比如下面都是合法的:

{ "dependencies" :
  { "foo" : "1.0.0 - 2.9999.9999"
  , "bar" : ">=1.0.2 <2.1.2"
  , "baz" : ">1.0.2 <=2.3.4"
  , "boo" : "2.0.1"
  , "qux" : "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0"
  , "asd" : "http://asdf.com/asdf.tar.gz"
  , "til" : "~1.2"
  , "elf" : "~1.2.3"
  , "two" : "2.x"
  , "thr" : "3.3.x"
  }
}

依赖URL

可以指定一个tarball URL,这个tarball将在包被初始化的时候下载并初始化。

依赖Git URL

Git urls 可以是下面几种形式:

git://github.com/user/project.git#commit-ish
git+ssh://user@hostname:project.git#commit-ish
git+ssh://user@hostname/project.git#commit-ish
git+http://user@hostname/project/blah.git#commit-ish
git+https://user@hostname/project/blah.git#commit-ish

commit-ish是可以被git checkout的任何tag、sha或者branch。默认为master

GitHub URLs

1.1.65版后,你可以仅仅用“user/foo-project”引用GitHub urls,比如:

{
  "name": "foo",
  "version": "0.0.0",
  "dependencies": {
    "express": "visionmedia/express"
  }
}

devDependencies

如果有人要使用你的模块,那么他们可能不需要你开发使用的外部测试或者文档框架。

在这种情况下,最好将这些附属的项目列在devDependencies中。

这些东西会在执行npm link或者npm install的时候初始化,并可以像其他npm配置参数一样管理。详见npm-config(7)

对于非特定平台的构建步骤,比如需要编译CoffeeScript,可以用prepublish脚本去实现,并把它依赖的包放在devDependency中。(译者注:prepublish定义了在执行npm publish的时候先行执行的脚本)

比如:

{ "name": "ethopia-waza",
  "description": "a delightfully fruity coffee varietal",
  "version": "1.2.3",
  "devDependencies": {
    "coffee-script": "~1.6.3"
  },
  "scripts": {
    "prepublish": "coffee -o lib/ -c src/waza.coffee"
  },
  "main": "lib/waza.js"
}

prepublish脚本会在publishing前运行,这样用户就不用自己去require来编译就能使用。并且在开发模式中(比如本地运行npm install)会运行这个脚本以便更好地测试。

peerDependencies

在一些场景中,如在一个host中不必须进行require时候,你想表现你的package与一个host工具或者库的兼容关键。这一般用来引用_插件_。尤其是你的模块可能要暴露一个特定的接口,并由host文档来预期和指定。

比如:

{
  "name": "tea-latte",
  "version": "1.3.5"
  "peerDependencies": {
    "tea": "2.x"
  }
}

这能保证你的package可以只和tea的2.x版本一起初始化。npm install tea-latte可能会产生下面的依赖关系

├── [email protected]
└── [email protected]

试图初始化另一个有会冲突的依赖的插件将导致一个错误。因此,确保你的插件的需求约束越弱越好,而不要去把它锁定到一个特定的版本。

假设这个host遵守semver规范,只改变这个package的主版本会打破你的插件。因此,如果你在package中用过每个1.x版本,就用"^1.0"或者"1.x"来表示。如果你依赖于功能介绍1.5.2,用">= 1.5.2 < 2"。

bundledDependencies

一组包名,他们会在发布的时候被打包进去。

拼成"bundleDependencies"(缺d)也可以。

optionalDependencies

如果一个依赖可用,但你希望在它安装错误的时候npm也能继续初始化,那么你可以把它放在optionalDependencies hash中。这是一个包名到版本或者url的map,就像dependencies hash一样。只是它运行错误。

处理缺乏依赖也是你的程序的责任。比如像这样:

try {
  var foo = require('foo')
  var fooVersion = require('foo/package.json').version
} catch (er) {
  foo = null
}
if ( notGoodFooVersion(fooVersion) ) {
  foo = null
}

// .. then later in your program ..

if (foo) {
  foo.doFooThings()
}

optionalDependencies会覆盖dependencies中同名的项,所以通常比只放在一个地方好。

engines

你可以指定工作的node的版本:

{ "engines" : { "node" : ">=0.10.3 <0.12" } }

并且,像dependensies一样,如果你不指定版本或者指定“*”作为版本,那么所有版本的node都可以。

如果指定一个“engines”字段,那么npm会需要node在里面,如果“engines”被省略,npm会假定它在node上工作。

你也可以用“engines”字段来指定哪一个npm版本能更好地初始化你的程序,如:

{ "engines" : { "npm" : "~1.0.20" } }

记住,除非用户设置engine-strict标记,这个字段只是建议值。

engineStrict

如果你确定你的模块_一定不_会运行在你指定版本之外的node或者npm上,你可以在package.json文件中设置"engineStrict":true。它会重写用户的engine-strict设置。

除非你非常非常确定,否则不要这样做。如果你的engines hash过度地限制,很可能轻易让自己陷入窘境。慎重地考虑这个选择。如果大家滥用它,它会再以后的npm版本中被删除。

os

你可以指定你的模块要运行在哪些操作系统中:

"os" : [ "darwin", "linux" ]

你也可以用黑名单代替白名单,在名字前面加上“!”就可以了:

"os" : [ "!win32" ]

操作系统用process.platform来探测。

虽然没有很好地理由,但它是同时支持黑名单和白名单的。

cpu

如果你的代码只能运行在特定的cpu架构下,你可以指定一个:

"cpu" : [ "x64", "ia32" ]

就像os选项,你也可以黑一个架构:

"cpu" : [ "!arm", "!mips" ]

cpu架构用process.arch探测。

preferGlobal

如果包主要是需要全局安装的命令行程序,就设置它为true来提供一个warning给只在局部安装的人。

它不会真正的防止用户在局部安装,但如果它没有按预期工作它会帮助防止产生误会。

private

如果你设置"private": true,npm就不会发布它。

这是一个防止意外发布私有库的方式。如果你要确定给定的包是只发布在特定registry(如内部registry)的,用publishConfighash的描述来重写registry的publish-time配置参数。

publishConfig

这是一个在publish-time使用的配置集合。当你想设置tag或者registry的时候它非常有用,所以你可以确定一个给定的包没有打上“lastest”的tag或者被默认发布到全局的公开registry。

任何配置都可以被重写,但当然可能只有“tag”和“registry”与发布的意图有关。

参见npm-config(7)有可以被重写的列表。

SEE ALSO

  • semver(7)
  • npm-init(1)
  • npm-version(1)
  • npm-config(1)
  • npm-config(7)
  • npm-help(1)
  • npm-faq(7)
  • npm-install(1)
  • npm-publish(1)
  • npm-rm(1)

你好啊

HI,Eric Dum:你好啊!
(我其实不是来提issues的,看完就删了吧。)
我们同事曾经发邮件给你,可能你没看到,于是我在这里给你留言。
我们有个项目是电子商务和云计算交叉的领域,在招募技术人才。看到你的个人信息,看能否交流一下有没有共事或技术合作的机会。我们全职,兼职,远程都有JOB提供。联系QQ: 2355679565 或通过我们的网站WWW.SHOP123.COM您了解一下。最好有机会电话沟通一下,你看方便吗?even手机13750813289 ,谢谢

简单的图片轮播,多多的优化

今天分享一个很常见的东西——图片轮播。

虽然说这是一个很常见的东西,但真要论起来,其实有很多地方可以优化。

比如淘宝首页的图片轮播,如果一个顾客的鼠标像我这样**得晃过去晃过来,被他经过的轮播就“停止”了。

注意鼠标移动的轨迹和轮播的大图片,如果用户在轮播周期内反复_经过_轮播元素,图片轮播就会“永远”停止。而下方的图片已经经过了几次轮播了。

xd_marque41

而同样的事情如果发生在心动游戏的官网上会,这不会有任何不符合预期的行为发生,鼠标悬停轮播停止,鼠标离开不影响轮播进程。

注意鼠标移动的轨迹和轮播的大图片

xd_marque31

引题结束,希望能引起你的兴趣,如若不然你应该不会看到这行字。。。或者下行字。。。

这个是我12年刚进入心动时写的,需求很简单——图片轮播、右下角链接。

为了实现这个需求,代码也可以很简单:

  1. 定义$.fn.xdMarquee,写成jquery扩展
  2. 函数中下载图片,并插入DOM
  3. 定义start、next、clear来控制轮播
  4. 定义右下角指示器click事件
  5. 开始轮播

源代码:

https://github.com/ericdum/mujiang.info/blob/master/share/marquee.js

捡重要的代码看:

    function marquee(data, options){
        //设置、初始化....
        //第二、三步
        var the = this;
        //第四步
        this.controller.find('span').live('click', function(){ 
            the.start(0, $(this).index()); 
        });
        //第五步
        this.start();
    }

    function start( timeout, index ) {
        this.clear(); //清除timeout
        this.promise(timeout, index);
    }

    function next(index) {
        // 执行动画 ....
        this.current = index;
        this.promise();//设置下次轮播
    }

    function promise( timeout, index ){
        timeout = timeout>=0 ? timeout : this.timeout;
        var the = this;
        this.tm = window.setTimeout(function(){
            the.next(index);
        }, timeout);
    }

    function clear() {
        window.clearTimeout(this.tm);
    }

悬停问题

因为我们认为用户鼠标悬停代表用户对图片产生了关注,所以悬停要暂停图片播放,就需要在上面初始化的方法中时候加入:

    this.hovering  = false;
    $(this).hover(function(){ 
        the.hovering = true; 
    }, function(){ 
        the.hovering = false; 
        the.start(); 
    });

然后再next方法中加入:

    if( this.hovering ) return;

这就可以了(另外要做好点击切换时的处理)。完成以后便发现了文章一开头提到的问题。

为了解决这个问题,首先想到的是在鼠标移走之后让图片立即改变。代码很简单,把上面第二段代码hover中对start的调用改成the.start(500)就好了——鼠标离开后500毫秒变换图片。

    //hover(function(){...},function(){...
    the.start(500)

但这就随之而来了另一个问题,如果用户老是经过这个地方,图片岂不是会不停地换——频率太快。

解决方案是如果图片播放的时间不足,则鼠标悬停对轮播不造成影响。实现的方法就是新增一个stopping状态。在next函数中判断hovering==true时,把它设置成true。而后在hover的第二个函数中判断:

    $(this).hover(function(){ 
        the.hovering = true; 
    }, function(){ 
        the.hovering = false; 
        if( the.stopping ){
            the.stopping = false;
            the.start(500); 
        }
    });

    //in next function
    if( this.hovering ) {
        this.stopping = true;
        return;
    }

性能问题

除了上面的工作以外还有个显而易见的问题:这种广告图片都是比较大的jpg图片(形状、色彩丰富),压缩的空间不大(jpg有损)。并且除了第一张,其他图片再3秒内是不需要显示的。所以决定分批下载,先下载第一张图,等第一张图下载完成之后再下载后续的图(这个时候这些图已经会排在下载队列的最后了)。

方案是扩展先前定义的initImages方法:

    function initImages(){
        var the = this;
        this.marquee.empty();//保持卫生
        this.appendImage(0, function(){
            for(var i=1; i<the.data.length; i++){
                the.appendImage(i);
            }
        });
    }

    function appendImage(index, callback){
        //检查参数,准备DOM对象... 
        img.load(callback);
        img.attr('src', data[0]);
        //插入DOM...
    }

菊花问题

图片延迟加载和以及本身就是靠js加载的第一张图片,必然会有概率发生图片还未加载完就要被用户观看的问题。所以必然要有野菊花。

解决方案更简单了,直接css加到了root元素的背景上,图片载入后自然盖住他,完美无缝,切换每张图没载入都可以显示到菊花。

js方面修改appendImage方法,防止未加载的图片被用户看到(那个飘渺的白框)。

    function appendImage(index, callback){
        //...
        img.hide();
        img.load(function(){
            $(this).show();
            callback();
        });
        img.attr('src', data[0]);
        //...
    }

其他问题

  1. 调整角标的显示逻辑,一次全部渲染,避免“跳”或乱序
  2. 调整轮播开始的逻辑,第一张图片加载完成后开始,保证广告的显示时间基本符合预期。
  3. 由于js放在页面底部,其实5张图片一起加载的性能差不多,兼容将第一张图片直接放在html中的情况。

技术需要整理

这段时间陷入了迷茫,感觉前端技术积累太少、太浅。

偶然在GDG Devfest上遇到米宗神,聊了很久,发现很可能是总结太少的问题。因为我们发现很多问题我都处理过,但我没有意识到这些地方的重要性,只是当成一般的坑填掉就走了,别人问起也不拿这些问题来说。所以,我决定遵从他的建议,整理代码开始写博客了。

送别粽神后,我思考了很久,要总结些什么?怎么总结?

我一直是一个不太善于言辞的人,提笔忘字。去年一时蛋疼建了一个 http://niuble.com/ 没怎么发文,也没怎么维护,更没怎么开发。导致一切都一塌糊涂,虽然说聊胜于无,但是对于我来说无更胜于**。所以现在在某些地方写blog地址的时候就直接写 http://ericathy.us/ 了——一个结婚纪念网站。

现在我重新申请了一个域名 http://mujiang.info/ - 木匠手记。我曾解释过:

我叫欢乐木匠艾瑞克,是因为我觉得现在做的前端就像原先的木匠一样 —— 手艺人、有艺术气息、有工程能力、能制造出直接与人交互的产品。他们能用木头制造各种实用的工具、用具、玩具;他们不断改良自己的工具、提高自己的工艺、培养自己的修养以更低的成本制造出更丰富、更实用、更美丽的产品……

然而同一时期,有其他几位木匠。或许不是那么正派,有一些跨界的木匠。他们提炼了工序、技艺,发明了机器……

我不要做会被机器取代的木匠!这不一定要发明机器,但一定要比机器更高明。

所以我要开始写木匠手记了,我决定从以下几个方面走:

  • 整理github上面发布的项目,把漏洞、bug、不严谨的地方找出来,好好改。
  • 把以前的项目review,整理出有意思的部分,抽离出来。
  • 每周至少整理一次工作、生活心得。应该有好几个部分,web技术、团队管理/开发流程、产品理解、其他。

立帖为证!

PS: 木匠手记页面构思中,在此之前都会写在这里。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.