Mongoose学习参考文档——基础篇

一、快速通道

1.1 名词解释

  • Schema : 一种以文件形式存储的数据库模型骨架,不具备数据库的操作能力

  • Model : 由Schema发布生成的模型,具有抽象属性和行为的数据库操作对

  • Entity : 由Model创建的实体,他的操作也会影响数据库

注意

1.本学习文档采用严格命名方式来区别不同对象,例如:

varPersonSchema;//Person的文本属性varPersonModel;//Person的数据库模型varPersonEntity;//Person实体

2.SchemaModelEntity的关系请牢记,Schema生成ModelModel创造EntityModelEntity都可对数据库操作造成影响,但ModelEntity更具操作性。

1.2 准备工作

1.首先你必须安装MongoDBNodeJS

2.在项目只能够创建一个数据库连接,如下:

var mongoose =require('mongoose');//引用mongoose模块var db = mongoose.createConnection('localhost','test');//创建一个数据库连接

3.打开本机localhosttest数据库时,我们可以监测是否有异常

    db.on('error',console.error.bind(console,'连接错误:'));
    db.once('open',function(){//一次打开记录});

注意

  成功开启数据库后,就可以执行数据库相应操作,假设以下代码都在回调中处理

4.定义一个Schema

varPersonSchema=new mongoose.Schema({
      name:String//定义一个属性name,类型为String});

5.将该Schema发布为Model

varPersonModel= db.model('Person',PersonSchema);//如果该Model已经发布,则可以直接通过名字索引到,如下://var PersonModel = db.model('Person');//如果没有发布,上一段代码将会异常

6.用Model创建Entity

var personEntity =newPersonModel({name:'Krouky'});//打印这个实体的名字看看
    console.log(personEntity.name);//Krouky

7.我们甚至可以为此Schema创建方法

//为Schema模型追加speak方法PersonSchema.methos.speak =function(){
      console.log('我的名字叫'+this.name);}varPersonModel= db.model('Person',PersonSchema);var personEntity =newPersonModel({name:'Krouky'});
    personEntity.speak();//我的名字叫Krouky

8.Entity是具有具体的数据库操作CRUD

    personEntity.save();//执行完成后,数据库就有该数据了

9.如果要执行查询,需要依赖Model,当然Entity也是可以做到的

PersonModel.find(function(err,persons){//查询到的所有person});

注意

  1. 具体的如何配置SchemaModel以及ModelEntity的相关操作,我们会在后面进行

  2. ModelEntity都有能影响数据库的操作,但仍有区别,后面我们也会做解释

二、新手指引

  如果您还不清楚Mongoose是如何工作的,请参看第一章快速通道快速浏览他的用法吧

1. Schema——纯洁的数据库原型

1.1 什么是Schema

  • 我理解Schema仅仅只是一断代码,他书写完成后程序依然无法使用,更无法通往数据库端
  • 他仅仅只是数据库模型在程序片段中的一种表现,或者是数据属性模型

1.2 如何定义Schema

varBlogSchema=newSchema({
      title:String,
      author:String//new Schema()中传入一个JSON对象,该对象形如 xxx:yyyy ,/xxx是一个字符串,定义了属性,yyy是一个Schema.Type,定义了属性类型});

1.3 什么是Schema.Type

  Schema.Type是由Mongoose内定的一些数据类型,基本数据类型都在其中,他也内置了一些Mongoose特有的Schema.Type。当然,你也可以自定义Schema.Type,只有满足Schema.Type的类型才能定义在Schema内。

1.4 Schema.Types

  NodeJS中的基本数据类型都属于Schema.Type,另外Mongoose还定义了自己的类型

//举例:varExampleSchema=newSchema({
      name:String,
      binary:Buffer,
      living:Boolean,
      updated:Date,
      age:Number,
      mixed:Schema.Types.Mixed,//该混合类型等同于nested
      _id:Schema.Types.ObjectId,//主键
      _fk:Schema.Types.ObjectId,//外键
      array:[],
      arrOfString:[String],
      arrOfNumber:[Number],
      arrOfDate:[Date],
      arrOfBuffer:[Buffer],
      arrOfBoolean:[Boolean],
      arrOfMixed:[Schema.Types.Mixed],
      arrOfObjectId:[Schema.Types.ObjectId]
      nested:{
        stuff:String,}});

1.5 关于Buffer

  BufferArrayBufferNodejs两种隐藏的对象,相关内容请查看NodeJS-API

1.6 关于Mixed

  Schema.Types.MixedMongoose定义个混合类型,该混合类型如果未定义具体形式。因此,如果定义具体内容,就直接使用{}来定义,以下两句等价

varAnySchema=newSchema({any:{}});varAnySchema=newSchema({any:Schema.Types.Mixed});

  混合类型因为没有特定约束,因此可以任意修改,一旦修改了原型,则必须调用markModified()

    person.anything ={x:[3,4,{y:'change'}]}
    person.markModified('anything');//传入anything,表示该属性类型发生变化
    person.save();

1.7 关于ObjectId

  主键,一种特殊而且非常重要的类型,每个Schema都会默认配置这个属性,属性名为_id,除非自己定义,方可覆盖

var mongoose =require('mongoose');varObjectId= mongoose.Schema.Types.ObjectId;varStudentSchema=newSchema({});//默认会有_id:ObjectIdvarTeacherSchema=newSchema({id:ObjectId});//只有id:ObjectId

  该类型的值由系统自己生成,从某种意义上几乎不会重复,生成过程比较复杂,有兴趣的朋友可以查看源码。

1.8 关于Array

  ArrayJavaScript编程语言中并不是数组,而是集合,因此里面可以存入不同的值,以下代码等价:

varExampleSchema1=newSchema({array:[]});varExampleSchema2=newSchema({array:Array});varExampleSchema3=newSchema({array:[Schema.Types.Mixed]});varExampleSchema4=newSchema({array:[{}]});

1.9 附言

  Schema不仅定义了文档结构使用性能,还可以有扩展插件实例方法静态方法复合索引文档生命周期钩子

  Schema可以定义插件,并且插件具有良好的可拔插性,请有兴趣的读者继续往后阅读或者查阅官方资料。

2. Schema的扩展

2.1 实例方法

  有的时候,我们创造的Schema不仅要为后面的ModelEntity提供公共的属性,还要提供公共的方法。

  下面例子比快速通道的例子更加高级,可以进行高级扩展:

varPersonSchema=newSchema({name:String,type:String});//查询类似数据PersonSchema.methods.findSimilarTypes =function(cb){returnthis.model('Person').find({type:this.type},cb);}

  使用如下:

varPersonModel= mongoose.model('Person',PersonSchema);var krouky =newPersonSchema({name:'krouky',type:'前端工程师'});
    krouky.findSimilarTypes(function(err,persons){//persons中就能查询到其他前端工程师});

2.2 静态方法

  静态方法在Model层就能使用,如下:

PersonSchema.statics.findByName =function(name,cb){this.find({name:newRegExp(name,'i'),cb});}varPersonModel= mongoose.model('Person',PersonSchema);PersonModel.findByName('krouky',function(err,persons){//找到所有名字叫krouky的人});

2.3 索引

  索引或者复合索引能让搜索更加高效,默认索引就是主键索引ObjectId,属性名为_id索引会作为一个专题来讲解

2.4 虚拟属性

  Schema中如果定义了虚拟属性,那么该属性将不写入数据库,例如:

varPersonSchema=newSchema({
      name:{
        first:String,last:String}});varPersonModel= mongoose.model('Person',PersonSchema);var krouky =newPersonModel({
      name:{first:'krouky',last:'han'}});

  如果每次想使用全名就得这样

    console.log(krouky.name.first +' '+ krouky.name.last);

  显然这是很麻烦的,我们可以定义虚拟属性

PersonSchema.virtual('name.full').get(function(){returnthis.name.first +' '+this.name.last;});

  那么就能用krouky.name.full来调用全名了,反之如果知道full,也可以反解firstlast属性

PersonSchema.virtual('name.full').set(function(name){var split = name.split(' ');this.name.first = split[0];this.name.last= split[1];});varPersonModel= mongoose.model('Person',PersonSchema);var krouky =newPersonModel({});
    krouky.name.full ='krouky han';//会被自动分解
    console.log(krouky.name.first);//krouky

2.5 配置项

  在使用new Schema(config)时,我们可以追加一个参数options来配置Schema的配置,形如:

varExampleSchema=newSchema(config,options);

  或者使用

varExampleSchema=newSchema(config);ExampleSchema.set(option,value);

  可供配置项有:safestrictcappedversionKeyautoIndex

2.5.1 safe——安全属性(默认安全)

  一般可做如下配置:

newSchema({...},{safe:true});

  当然我们也可以这样

newSchema({...},{safe:{j:1,w:2,wtimeout:10000}});

  j表示做1份日志,w表示做2个副本(尚不明确),超时时间10秒

2.5.2 strict——严格配置(默认启用)

  确保Entity的值存入数据库前会被自动验证,如果你没有充足的理由,请不要停用,例子:

varThingSchema=newSchema({a:String});varThingModel= db.model('Thing',SchemaSchema);var thing =newThing({iAmNotInTheThingSchema:true});
    thing.save();//iAmNotInTheThingSchema这个属性将无法被存储

  如果取消严格选项,iAmNotInTheThingSchema将会被存入数据库

  该选项也可以在构造实例时使用,例如:

varThingModel= db.model('Thing');var thing1 =newThingModel(doc,true);//启用严格var thing2 =newThingModel(doc,false);//禁用严格

注意:

  strict也可以设置为throw,表示出现问题将会抛出错误

2.5.3 shardKey

  需要mongodb做分布式,才会使用该属性

2.5.4 capped——上限设置

  如果有数据库的批量操作,该属性能限制一次操作的量,例如:

newSchema({...},{capped:1024});//一次操作上线1024条数据

  当然该参数也可是JSON对象,包含size、max、autiIndexId属性

newSchema({...},{capped:{size:1024,max:100,autoIndexId:true}});
2.5.5 versionKey——版本锁

  版本锁是Mongoose默认配置(__v属性)的,如果你想自己定制,如下:

newSchema({...},{versionKey:'__someElse'});

  此时存入数据库的版本锁就不是__v属性,而是__someElse,相当于是给版本锁取名字。

  具体怎么存入都是由MongooseMongoDB自己决定,当然,这个属性你也可以去除

newSchema({...},{versionKey:false});

  除非你知道你在做什么,并且你知道这样做的后果

2.5.6 autoIndex——自动索引

  该内容将在索引章节单独讲解

3. Documents

  Document是与MongoDB文档一一对应的模型,Document可等同于Entity,具有属性和操作性

注意:

  Document的`CRUD都必须经过严格验证的,参看2.5.2 Schema的strict严格配置

3.1 查询

  查询内容过多,专题讲解

3.2 更新

  有许多方式来更新文件,以下是常用的传统方式:

PersonModel.findById(id,function(err,person){
      person.name ='MDragon';
      person.save(function(err){});});

  这里,利用Model模型查询到了person对象,该对象属于Entity,可以有save操作,如果使用Model`操作,需注意:

PersonModel.findById(id,function(err,person){
      person.name ='MDragon';var _id = person._id;//需要取出主键_iddelete person._id;//再将其删除PersonModel.update({_id:_id},person,function(err){});//此时才能用Model操作,否则报错});

  update第一个参数是查询条件,第二个参数是更新的对象,但不能更新主键,这就是为什么要删除主键的原因。

  当然这样的更新很麻烦,可以使用$set属性来配置,这样也不用先查询,如果更新的数据比较少,可用性还是很好的:

PersonModel.update({_id:_id},{$set:{name:'MDragon'}},function(err){});

  需要注意,DocumentCRUD操作都是异步执行,callback第一个参数必须是err,而第二个参数各个方法不一样,updatecallback第二个参数是更新的数量,如果要返回更新后的对象,则要使用如下方法

Person.findByIdAndUpdate(_id,{$set:{name:'MDragon'}},function(err,person){
      console.log(person.name);//MDragon});

  类似的方法还有findByIdAndRemove,如同名字,只能根据id查询并作update/remove操作,操作的数据仅一条

3.3 新增

  如果是Entity,使用save方法,如果是Model,使用create方法

//使用Entity来增加一条数据var krouky =newPersonModel({name:'krouky'});
    krouky.save(callback);//使用Model来增加一条数据varMDragon={name:'MDragon'};PersonModel.create(MDragon,callback);

  两种新增方法区别在于,如果使用Model新增时,传入的对象只能是纯净的JSON对象,不能是由Model创建的实体,原因是:由Model创建的实体krouky虽然打印是只有{name:'krouky'},但是krouky属于Entity,包含有Schema属性和Model数据库行为模型。如果是使用Model创建的对象,传入时一定会将隐藏属性也存入数据库,虽然3.x追加了默认严格属性,但也不必要增加操作的报错

3.4 删除

  和新增一样,删除也有2种方式,但EntityModel都使用remove方法

4.Sub Docs

  如同SQL数据库中2张表有主外关系,Mongoose将2个Document的嵌套叫做Sub-Docs(子文档)

  简单的说就是一个Document嵌套另外一个Document或者Documents:

varChildSchema1=newSchema({name:String});varChildSchema2=newSchema({name:String});varParentSchema=newSchema({
      children1:ChildSchema1,//嵌套Document
      children2:[ChildSchema2]//嵌套Documents});

  Sub-Docs享受和Documents一样的操作,但是Sub-Docs的操作都由父类去执行

varParentModel= db.model('Parent',parentSchema);var parent =newParentModel({
      children2:[{name:'c1'},{name:'c2'}]});
    parent.children2[0].name ='d';
    parent.save(callback);

  parent在执行保存时,由于包含children2,他是一个数据库模型对象,因此会先保存chilren2[0]chilren2[1]

  如果子文档在更新时出现错误,将直接报在父类文档中,可以这样处理:

ChildrenSchema.pre('save',function(next){if('x'===this.name)returnnext(newError('#err:not-x'));next();});var parent =newParentModel({children1:{name:'not-x'}});
    parent.save(function(err){
      console.log(err.message);//#err:not-x});

4.1 查询子文档

  如果childrenparent的子文档,可以通过如下方法查询到children

var child = parent.children.id(id);

4.2 新增、删除、更新

  子文档是父文档的一个属性,因此按照属性的操作即可,不同的是在新增父类的时候,子文档是会被先加入进去的。

  如果ChildrenSchema是临时的一个子文档,不作为数据库映射集合,可以这样:

varParentSchema=newSchema({
      children:{
        name:String}});//其实就是匿名混合模式

5.Model

5.1 什么是Model

  Model模型,是经过Schema构造来的,除了Schema定义的数据库骨架以外,还具有数据库行为模型,他相当于管理数据库属性、行为的类

5.2 如何创建Model

  你必须通过Schema来创建,如下:

//先创建SchemavarTankSchema=newSchema({
      name:'String',
      size:'String'});//通过Schema创建ModelvarTankModel= mongoose.model('Tank',TankSchema);

5.2 操作Model

  该模型就能直接拿来操作,具体查看API,例如:

var tank ={'something',size:'small'};TankModel.create(tank);

注意:

  你可以使用Model来创建EntityEntity实体是一个特有Model具体对象,但是他并不具备Model的方法,只能用自己的方法。

//通过Model创建Entityvar tankEntity =newTankModel('someother','size:big');
  tankEntity.save();

6.Query

  查询是数据库中运用最多也是最麻烦的地方,这里对Query解读的并不完善,仅仅是自己的一点领悟而已。

6.1 查询的方式

  通常有2种查询方式,一种是直接查询,一种是链式查询(2种查询都是自己命名的)

6.1.1 直接查询

  在查询时带有回调函数的,称之为直接查询,查询的条件往往通过API来设定,例如:

PersonModel.findOne({'name.last':'dragon'},'some select',function(err,person){//如果err==null,则person就能取到数据});

  具体的查询参数,请查询API

6.1.2 链式查询

  在查询时候,不带回调,而查询条件通过API函数来制定,例如:

var query =PersonModel.findOne({'name.last':'dragon'});
    query.select('some select');
    query.exec(function(err,pserson){//如果err==null,则person就能取到数据});

  这种方式相对直接查询,分的比较明细,如果不带callback,则返回queryquery没有执行的预编译查询语句,该query对象执行的方法都将返回自己,只有在执行exec方法时才执行查询,而且必须有回调。

  因为query的操作始终返回自身,我们可以采用更形象的链式写法

Person.find({ occupation:/host/}).where('name.last').equals('Ghost').where('age').gt(17).lt(66).where('likes').in(['vaporizing','talking']).limit(10).sort('-occupation').select('name occupation').exec(callback);

7.Validation

  数据的存储是需要验证的,不是什么数据都能往数据库里丢或者显示到客户端的,数据的验证需要记住以下规则:

  • 验证始终定义在SchemaType
  • 验证是一个内部中间件
  • 验证是在一个Document被保存时默认启用的,除非你关闭验证
  • 验证是异步递归的,如果你的SubDoc验证失败,Document也将无法保存
  • 验证并不关心错误类型,而通过ValidationError这个对象可以访问

7.1 验证器

  1. required 非空验证
  2. min/max 范围验证(边值验证)
  3. enum/match 枚举验证/匹配验证
  4. validate 自定义验证规则

  以下是综合案例:

varPersonSchema=newSchema({
      name:{
        type:'String',
        required:true//姓名非空},
      age:{
        type:'Nunmer',
        min:18,//年龄最小18
        max:120//年龄最大120},
      city:{
        type:'String',enum:['北京','上海']//只能是北京、上海人},
      other:{
        type:'String',
        validate:[validator,err]//validator是一个验证函数,err是验证失败的错误信息}});

7.2 验证失败

  如果验证失败,则会返回err信息,err是一个对象该对象属性如下

    err.errors                //错误集合(对象)
    err.errors.color          //错误属性(Schema的color属性)
    err.errors.color.message  //错误属性信息
    err.errors.path             //错误属性路径
    err.errors.type             //错误类型
    err.name                //错误名称
    err.message                 //错误消息

  一旦验证失败,ModelEntity都将具有和err一样的errors属性

8.Middleware中间件

8.1 什么是中间件

  中间件是一种控制函数,类似插件,能控制流程中的init、validatesaveremove`方法

8.2 中间件的分类

  中间件分为两类

8.2.1 Serial串行

  串行使用pre方法,执行下一个方法使用next调用

var schema =newSchema(...);
    schema.pre('save',function(next){//做点什么next();});
8.2.2 Parallel并行

  并行提供更细粒度的操作

var schema =newSchema(...);
    schema.pre('save',function(next,done){//下一个要执行的中间件并行执行next();
      doAsync(done);});

8.3 中间件特点

  一旦定义了中间件,就会在全部中间件执行完后执行其他操作,使用中间件可以雾化模型,避免异步操作的层层迭代嵌套

8.4 使用范畴

  1. 复杂的验证
  2. 删除有主外关联的doc
  3. 异步默认
  4. 某个特定动作触发异步任务,例如触发自定义事件和通知

  例如,可以用来做自定义错误处理

    schema.pre('save',function(next){var err =newEerror('some err');next(err);});
    entity.save(function(err){
      console.log(err.message);//some err});

http://cnodejs.org/topic/504b4924e2b84515770103dd

社交网络分析 课程简介

课程概述

世界万事万物皆有联系——人、信息、事件和地点,新兴的在线社交媒体更是如此。要理解这些杂乱无章的联系,将其作为网络来分析是一种切实可行的方法。在本课程中,你将学到网络的结构和演化历程。本课程涉及社会学、数学、计算机科学、经济学和物理学等广泛学科的知识。课程包括在线互动演示和真实数据集的分析实践,主要包括以下任务:确定网络中的重要节点、探测社区、跟踪信息的扩散和观点的形成。

课程大纲

第1周:什么是网络,学习他们有什么用?
概念:节点、边、邻接矩阵,1模网络和2模网络、节点度
活动:上传一个社交网络(如把你的Facebook社交网络上传到Gephi软件中并显示为图形)
第2周:随机网络模型:Erdos-Renyi模型和Barabasi-Albert模型
概念:连通组件、最大连通分支、平均最短路径、直径、广度优先搜索、优先连接
活动:创建随机网络、计算组件分布、平均最短路径、评估结构对信息扩散能力的影响
第3周:网络中心性
概念:中介中心性、接近中心性、特征向量中心性(+ PR值)、网络中心势
活动:计算和解释现实网络(你的Facebook 图谱、安然公司电子邮件网络、Twitter网络等)的节点中心性
第4周:社区
概念:聚类、社区结构、模块化、重叠社区
活动:探测并解释各种网络(科研合作、政治博客、食材等)的分离与重叠
第5周:小世界网络模型、优化、战略网络形成与搜索
概念:小世界、地理网络、分散搜索
Activity: 验证几个真实网络是否具有小世界属性,在不同拓扑结构上模拟分散搜索,评估小世界拓扑上信息扩散的效果。
第6周:传播、观点形成、协调与合作
概念:简单传播、阈值模型、观点形成
活动:通过模拟评估网络结构的上述过程的影响
第7周:又酷又另类的社交网络分析应用
Hidalgo等:用产品的空间网络(哪个国家生产哪种产品)预测经济发展程度
Ahn等和Teng等:通过成分和风味网络学习烹饪
Lusseau等:海豚的社交网络
其他待定
活动:利用之前课程学到的知识动手探索这些网络
第8周:社交网络分析与在线社交网络
概念:诸如Facebook、LinkedIn、Twitter,CouchSurfing等社交平台,如何利用社交网络分析了解用户并提升服务
活动:阅读最近有关这些服务的研究以及社交网络分析概念如何应用于社交平台的研究

背景知识

本课程不要求数学和编程技巧。针对有编程能力的同学,会有一些额外的练习,主要使用R统计编程语言,结合NetLogo实现。

参考资料

如果想提前准备,建议下载 Gephi并尝试它的一些教程。要交互探索社交网络,可访问 查看NetLogo 示例。如果你渴望阅读,Easley 和Kleinberg的免费文本非常棒,可通过 网络、大众和市场获得。与本门课相关的章节包括1-5、13-14和19-21。

授课形式

课程包括讲座视频,每集时长8-12分钟,并包含1-2个小测验。除了视频,还有独立的家庭作业,可选的编程任务,以及(必须参加的)期末考试。
http://c.open.163.com/coursera/courseIntro.htm?cid=38&tabNoJmp=1#/courseIntro

MySQL索引使用方法和性能优化

关于MySQL索引的好处,如果正确合理设计并且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车。对于没有索引的表,单表查询可能几十万数据就是瓶颈,而通常大型网站单日就可能会产生几十万甚至几百万的数据,没有索引查询会变的非常缓慢。还是以WordPress来说,其多个数据表都会对经常被查询的字段添加索引,比如wp_comments表中针对5个字段设计了BTREE索引。

一个简单的对比测试

以我去年测试的数据作为一个简单示例,20多条数据源随机生成200万条数据,平均每条数据源都重复大概10万次,表结构比较简单,仅包含一个自增ID,一个char类型,一个text类型和一个int类型,单表2G大小,使用MyIASM引擎。开始测试未添加任何索引。

执行下面的SQL语句:

1 mysql> SELECT id,FROM_UNIXTIME(timeFROM article WHERE a.title='测试标题'

查询需要的时间非常恐怖的,如果加上联合查询和其他一些约束条件,数据库会疯狂的消耗内存,并且会影响前端程序的执行。这时给title字段添加一个BTREE索引:

1 mysql> ALTER TABLE article ADD INDEX index_article_title ON title(200);

再次执行上述查询语句,其对比非常明显:

MySQL索引的概念

索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度。上述SQL语句,在没有索引的情况下,数据库会遍历全部200条数据后选择符合条件的;而有了相应的索引之后,数据库会直接在索引中查找符合条件的选项。如果我们把SQL语句换成“SELECT * FROM article WHERE id=2000000”,那么你是希望数据库按照顺序读取完200万行数据以后给你结果还是直接在索引中定位呢?上面的两个图片鲜明的用时对比已经给出了答案(注:一般数据库默认都会为主键生成索引)。

索引分为聚簇索引和非聚簇索引两种,聚簇索引是按照数据存放的物理位置为顺序的,而非聚簇索引就不一样了;聚簇索引能提高多行检索的速度,而非聚簇索引对于单行的检索很快。

MySQL索引的类型

1. 普通索引

这是最基本的索引,它没有任何限制,比如上文中为title字段创建的索引就是一个普通索引,MyIASM中默认的BTREE类型的索引,也是我们大多数情况下用到的索引。

01 –直接创建索引
02 CREATE INDEX index_name ON table(column(length))
03 –修改表结构的方式添加索引
04 ALTER TABLE table_name ADD INDEX index_name ON (column(length))
05 –创建表的时候同时创建索引
06 CREATE TABLE `table` (
07 `id` int(11) NOT NULL AUTO_INCREMENT ,
08 `title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
09 `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
10 `timeint(10) NULL DEFAULT NULL ,
11 PRIMARY KEY (`id`),
12 INDEX index_name (title(length))
13 )
14 –删除索引
15 DROP INDEX index_name ON table

2. 唯一索引

与普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值(注意和主键不同)。如果是组合索引,则列值的组合必须唯一,创建方法和普通索引类似。

01 –创建唯一索引
02 CREATE UNIQUE INDEX indexName ON table(column(length))
03 –修改表结构
04 ALTER TABLE table_name ADD UNIQUE indexName ON (column(length))
05 –创建表的时候直接指定
06 CREATE TABLE `table` (
07 `id` int(11) NOT NULL AUTO_INCREMENT ,
08 `title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
09 `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
10 `timeint(10) NULL DEFAULT NULL ,
11 PRIMARY KEY (`id`),
12 UNIQUE indexName (title(length))
13 );

3. 全文索引(FULLTEXT)

MySQL从3.23.23版开始支持全文索引和全文检索,FULLTEXT索引仅可用于 MyISAM 表;他们可以从CHAR、VARCHAR或TEXT列中作为CREATE TABLE语句的一部分被创建,或是随后使用ALTER TABLE 或CREATE INDEX被添加。////对于较大的数据集,将你的资料输入一个没有FULLTEXT索引的表中,然后创建索引,其速度比把资料输入现有FULLTEXT索引的速度更为快。不过切记对于大容量的数据表,生成全文索引是一个非常消耗时间非常消耗硬盘空间的做法。

01 –创建表的适合添加全文索引
02 CREATE TABLE `table` (
03 `id` int(11) NOT NULL AUTO_INCREMENT ,
04 `title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
05 `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
06 `timeint(10) NULL DEFAULT NULL ,
07 PRIMARY KEY (`id`),
08 FULLTEXT (content)
09 );
10 –修改表结构添加全文索引
11 ALTER TABLE article ADD FULLTEXT index_content(content)
12 –直接创建索引
13 CREATE FULLTEXT INDEX index_content ON article(content)

4. 单列索引、多列索引

多个单列索引与单个多列索引的查询效果不同,因为执行查询时,MySQL只能使用一个索引,会从多个索引中选择一个限制最为严格的索引。

5. 组合索引(最左前缀)

平时用的SQL查询语句一般都有比较多的限制条件,所以为了进一步榨取MySQL的效率,就要考虑建立组合索引。例如上表中针对title和time建立一个组合索引:ALTER TABLE article ADD INDEX index_titme_time (title(50),time(10))。建立这样的组合索引,其实是相当于分别建立了下面两组组合索引:

–title,time

–title

为什么没有time这样的组合索引呢?这是因为MySQL组合索引“最左前缀”的结果。简单的理解就是只从最左面的开始组合。并不是只要包含这两列的查询都会用到该组合索引,如下面的几个SQL所示:

1 –使用到上面的索引
2 SELECT FROM article WHREE title='测试' AND time=1234567890;
3 SELECT FROM article WHREE utitle='测试';
4 –不使用上面的索引
5 SELECT FROM article WHREE time=1234567890;
MySQL索引的优化

上面都在说使用索引的好处,但过多的使用索引将会造成滥用。因此索引也会有它的缺点:虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会膨胀很快。索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询语句。下面是一些总结以及收藏的MySQL索引的注意事项和优化方法。

1. 何时使用聚集索引或非聚集索引?

动作描述 使用聚集索引 使用非聚集索引
列经常被分组排序 使用 使用
返回某范围内的数据 使用 不使用
一个或极少不同值 不使用 不使用
小数目的不同值 使用 不使用
大数目的不同值 不使用 使用
频繁更新的列 不使用 使用
外键列 使用 使用
主键列 使用 使用
频繁修改索引列 不使用 使用

事实上,我们可以通过前面聚集索引和非聚集索引的定义的例子来理解上表。如:返回某范围内的数据一项。比如您的某个表有一个时间列,恰好您把聚合索引建立在了该列,这时您查询2004年1月1日至2004年10月1日之间的全部数据时,这个速度就将是很快的,因为您的这本字典正文是按日期进行排序的,聚类索引只需要找到要检索的所有数据中的开头和结尾数据即可;而不像非聚集索引,必须先查到目录中查到每一项数据对应的页码,然后再根据页码查到具体内容。其实这个具体用法我还不是很理解,只能等待后期的项目开发中慢慢学学了。

2. 索引不会包含有NULL值的列

只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。

3. 使用短索引

对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。

4. 索引列排序

MySQL查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

5. like语句操作

一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。

6. 不要在列上进行运算

例如:select * from users where YEAR(adddate)<2007,将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成:select * from users where adddate<’2007-01-01′。关于这一点可以围观:一个单引号引发的MYSQL性能损失。

最后总结一下,MySQL只对一下操作符才使用索引:<,<=,=,>,>=,between,in,以及某些时候的like(不以通配符%或_开头的情形)。而理论上每张表里面最多可创建16个索引,不过除非是数据量真的很多,否则过多的使用索引也不是那么好玩的,比如我刚才针对text类型的字段创建索引的时候,系统差点就卡死了。

移动版专题设计的那些事

响应式设计是否适合于专题设计

tuntundeng:如今越来越多的移动设备出现在我们身边,作为设计师对网站专题的设计进入到一个新阶段,需要考虑更多的用户访问环境,输入设备,分辨率等不同因素。响应式网页是目前热门的解决方案,好比一双男生的休闲鞋,可以与所有裤子进行随意搭配。但是对于专题设计来说,响应式设计并不合适。

阅读推荐:
《app宣传专题设计》
《PC走向移动的产品策略要点》
《利用人性弱点的互联网产品》

专题网页的设计通常是短平快,在特定时间达到一些运营需求。而响应式设计通常需要考虑更多不同分辨率下的响应效果,花费的设计和开发时间成本可能是double。而且专题设计具有很强的形式感,响应式设计的流体布局,必然会对视觉造成限制,所以对于专题来说做成响应式的网站需要慎重考虑。最高效的办法就是传统的为手机版单独做一版设计稿,让手机版网页满足通用的移动设备分辨率。

先做PC版的页面还是手机版页面

看用户的访问数据来定。一般情况下都是先做PC版的页面再做手机页面,PC端网页可以呈现更丰富的内容,用户浏览网页时更专注,达到最好的体验。但有很多情况下,页面的访问更多的来自手机端,比如手游的专题,用户通过微信、手Q入口进入。如果开始就先做PC版的专题,想必手机版的内容将是一个移植的PC版网页,让移动版的体验大打折扣。世界上最远的距离不是我在你身边却不知道我爱你,而是我用手机开你网页,你却你不知道。

145656Fjx

酷跑的官网用户访问来源,超过80%来自移动端,在为酷跑的专题设计时则先从手机版开始设计。

举两个栗子,twitter与instagram的PC页面都是从手机端的延展,继承了手机端的交互,保持了一个很好的一致性体验。

145656TvX

手机版专题页面的注意事项

1. 安全宽度与扩展区域让页面适应主流分辨率

与PC上网页的做法一样,确定一个安全宽度,把重要信息控制在640PX宽度内(以iphone分辨率为安全宽度。目前手游手机版专题规范的数值如下,仅供参考:

145657wVU1

目前主要浏览器在iphone4S下的首屏高度如下,可以根据具体页面投放的渠道做响应的首屏高度

145700wGm

2. 控制图片的大小

专题的头图一般有很强的视觉,对于手机用户来说,加载一张图片的等待时间比PC上成本大很多,如果一个页面加载时间超过5秒,70%的用户会选择关闭,那么再出彩的专题也没办法呈现再用户面前了,所以需要对设计稿做折中的处理。

在做头部的延展区域(超出640px范围),推荐使用纯色,渐变,可以平铺的素材,以便于减少头图的大小,提高加载速度,并且延展区域可以无缝连接。模糊的背景可以最大限度的压缩图片质量(反正是糊的,再怎么糊都可以)

145701Ri6

3. 字体

手机中的字号一般上电脑中的两倍,一般在电脑中用的12px的字体在手机网页中就该使用24px。在设计的过程中,也要使用iOS和安卓默认渲染的字体,以便更真实的还原真实环境。

在规范手游移动版专题中,主要字号控制在3个,大中小都有一个区间。避免随意用字体字号,并且字号必须上整数。

145702DhS

4. 控件交互区域适合触控

手机专题的主按钮高度大于80px,并且根据活动的需求放在首屏内。文字链接上下的间距大于80px,手指在屏幕上热区最小感应是44px。

1457034kP

5. 移动端网页少用跳转

手机用户的网络环境不如PC用户,页面的跳转会对用户产生更大的心理效力,如果在手机页面中能吧信息合理的展示在一个页面中最佳。如果非要跳转,咱们可以用些假装不是跳转的方式,比如展开,浮出等。减少用户产生的不安全感。

145704u2W

移动专题也可以很出彩

虽然手机因为性能,网络等限制因素,不能达到PC专题那么优秀的效果。但手机网页还是有他独有的优势。

Wagerfield

145705mlN

手机网页也是可以动起来的,而且还可以通过手机特有的重力感应功能做视差滚动效果的交互,看起来很优雅。

145705AGP

Diplomatic-cover

1457061iM

通过手机控制PC页面的浏览也是一种新体验,不妨试试挖掘更多可能性,用户对新事物的接受程度没咱们想的那么低。

1457062kc

Infinityblade

145706lIB

无尽之剑的官网,手机版与PC版有很好的一致性。手机的手指滑动交互在这个页面中有很好的体验,并且动画效果很赞。

145707lZk

做手机网页的必备工具:PS PLAY

详细介绍戳这里《Ps Play——移动端预览Photoshop设计零阻力》

ISUX出品的工具,通过同一wifi可以同步电脑中的设计稿在手机中预览,快速检阅设计稿的真实环境效果,减少不必要的修改。

145708ve1

纵使手机本身的局限,使得咱们做手机网页的时候缩手缩脚,但手机的功能越来越强大,相信在不久之后,手机完全可以实现现在PC中的效果。网络环境也会越来越好,加载速度的加快也有利于设计师发挥更大的空间

原文地址:tgideas
作者: tuntundeng

PhantomJS-前端自动化测试 – 网易用户产品&前端群博客

前端自动化测试向来是一件很困难的事情,特别是涉及UI层面的测试时更是爱莫能助,目前还没有很成熟的Web UI层自动化测试方案,我们这时候就必须借助于很多第三方的工具。

PhantomJS便是这么一个为自动化而生的利器,它本质上是一个基于webkit内核的无界面浏览器,并可使用JavaScript或CoffeeScript进行编程。由于没有界面,它的使用就有点像curl, lynx之类的命令行式文本浏览器。但PhantomJS远不是文本浏览器那么简单,由于它是基于webkit内核的,因此拥有的完善的Javascript解析、页面渲染功能,你完全可用它来模拟一个现代浏览器在加载网页时所做的各种事件。

我们来看看PhantomJS能做的事件吧:

  1. 不借助于浏览器的前端测试,并支持Jasmine, Capybara, QUnit, WebDriver, YUI Test, BusterJS, FuncUnit, Robot Framework等诸多单元测试框架

  2. 页面操作,通过标准DOM API或借助jQuery库对页面进行修改

  3. 页面截图,甚至支持页面中的SVG和Canvas元素,可作为图片导出引擎,支持导出为pdf或png

  4. 网络测试,可轻松生成网络测试报告(HAR格式),用于绘制瀑布图

举例来说,比如我们希望将Canvas绘制的内容导出为png图片,这通过PhantomJS来做就再合适不过了。但首先你得编写Javascript操作脚本,下面是PhantomJS自带的一个demo( colorwheel.js):

var page =newWebPage;
page.viewportSize ={ width:400, height :400};
page.content ='<html><body><canvas id=”surface”></canvas></body></html>’;
page.evaluate(function(){
var el = document.getElementById(‘surface’),
context = el.getContext(‘2d’),
width = window.innerWidth,
height = window.innerHeight,
cx = width /2,
cy = height /2,
radius = width  /2.3,
imageData,
pixels,
hue, sat, value,
i =0, x, y, rx, ry, d,
f, g, p, u, v, w, rgb;

el.width = width;
el.height = height;
imageData = context.createImageData(width, height);
pixels = imageData.data;

for(y =0; y < height; y = y +1){
for(x =0; x < width; x = x +1, i = i +4){
rx = x – cx;
ry = y – cy;
d = rx * rx + ry * ry;
if(d < radius * radius){
hue =6*(Math.atan2(ry, rx)+Math.PI)/(2*Math.PI);
sat =Math.sqrt(d)/ radius;
g =Math.floor(hue);
f = hue – g;
u =255*(1- sat);
v =255*(1- sat * f);
w =255*(1- sat *(1- f));
pixels[i]=[255, v, u, u, w,255,255][g];
pixels[i +1]=[w,255,255, v, u, u, w][g];
pixels[i +2]=[u, u, w,255,255, v, u][g];
pixels[i +3]=255;
}
}
}

context.putImageData(imageData,0,0);
document.body.style.backgroundColor =’white’;
document.body.style.margin =’0px’;
});

page.render(‘colorwheel.png’);
phantom.exit();

我们只需执行 phantomjs colorwheel.js,便可按自定义的canvas操作得到最终的图片,如下:

 

如果我们只是想截图,那就更简单了,phantomJS同样提供了一个demo脚本:rasterize.js,我们只要执行:

phantomjs rasterize.js http://raphaeljs.com/polar-clock.html clock.png

便可轻松得到:

   

再看看怎么做简单的页面测试,比如只想知道一个页面的总加载时间(包括下载所有静态资源、页面渲染),可简单地使用自带的pagetest.js

phantomjs loadspeed.js http://auto.163.com/special/browserupgrade/

得到结果如下:

Page title is 低版本IE升级提示
Loading time 1147 msec

够简单吧,如果你想进一步做网页性能分析,得到资源加载的详细瀑布图,phantomJS还可以为你生成HAR(HTTP Archive)报表。同样借助一下自带的netsniff.js:

photomjs netsniff.js http://auto.163.com/special/browserupgrade/

将生成的json格式报告放到HAR viewer( http://www.softwareishard.com/har/viewer/  )中,便可轻松得到资源加载瀑布图:

   

事实上,phantomjs自带的用例就有近40个,好好挖掘一下,你肯定能得到意想不到的惊喜,如果你想要做单元测试,那就更要潜心研究一下了:http://code.google.com/p/phantomjs/

Backbone案例的初略理解

http://juntype.com/junlab/99.html

最近发现自己越来越不行了,做前端也有些岁月了,神马HTML5,CSS3等层出不穷,如果再不及时Update一下自己,很快也会被这个行业给淘汰了!废话不多说了,今天主要是记一下自己学习Backbone的笔记。
国内有用到这技术的有豆瓣说 阿尔法城 可能是他们比较出名。。。
前端MVC 主要讲的是在前端方面实现MVC的模板,在前端也有自己的模块,视图,控制器,这结合RESTful来做是个不错的选择,我在瘦服务端架构TSA(Thin Server Architecture)记录了一下
而Backbone只是通向前端MVC其中的一条路而已。。。
Backbone.js API中文文档
我下面的源码出自 让我们用Backbone.js来写一个HelloWorld程序 我也是通过这源码结合API进行学习 上面那篇博文有些错误,我已经修正。
这里说一下Backbone的执行顺序
路由(Backbone.Router)-> 模型(Backbone.Model)-> 视图(Backbone.View)

路由告诉你要去哪里。

App.Controllers.Routes = Backbone.Router.extend({  
    routes: {  
    "!/hello" : "hello",//使用#!/hello驱动路由 这里会去分行下面hello这个函数 
    },  
    hello : function(){  
        //新建一个模型,模型向后端请求更新内容成功后根据模型渲染新页面
        var helloModel = new App.Models.Hello; //调用模板
        helloModel.fetch({  
            success: function(model){  //成功拿到数据,调用视图渲染页面
                var helloView = new App.Views.Hello({model: model});  
                helloView.trigger('change');  //绑定change事件
            }
        });
    }
});


模型告诉该干些什么。这里是去拿数据,set是提供一个设置初始数据

App.Models.Hello = Backbone.Model.extend({  
    url: function() {  
        return '/index/test'; // 获得数据的后台地址。
    },  
    initialize: function() {  
        this.set({'message':'hello world'}); // 前端定义一个message字段,name字段由后端提供。
    }  
});


最后你就知道了有什么了

App.Views.Hello = Backbone.View.extend({  
    el: "body",  //在哪里显示
    template: $("#hello-container-template").html(),  //获取模板 模板是用Mustache
    initialize: function(options){  
        this.options = options;  
        this.bind('change', this.render);  
        this.model = this.options.model;  
    },
    render: function(){ // render方法,目标只有两个:填充this.el,返回this以便链式操作。
        $(this.el).html(Mustache.to_html(this.template,this.model.toJSON()));
        return this;
    }
});


上面就是MVC的核心了,下面就是一个管理着 视图/控制/模型 的全局类

var App = {  
    Models: {},  
    Views: {},  
    Controllers: {},  
    Collections: {},  
    initialize: function() {  
        new App.Controllers.Routes();  
        Backbone.history.start() // 要驱动所有的Backbone程序,Backbone.history.start()是必须的。  
    }  
};


调用,完事啦。。。

App.initialize();


其实我只知道了这么点皮毛,接下来就要在实践中多去学习了,期待有后续。。。
给自己加个油,提个醒!
PS 接下来,我将试着在 doulaipk.com 去实践,求PHP程序猿.

nginx根据host判断root的 with passenger配置

server {
  server_name ~^(.*\.)?(?<app>[^.]+)\.dev$;
  root /opt/apps/$app/public;
  rails_env development;
  passenger_enabled on;
}

Nginx 0.8.25+ 应该都能用,参考 server_name

需要跑哪个Rails/Rack app就在/opt/apps下面建立个symbol link (link到含config.ru的根目录)

cd /opt/apps
ln -s /path/to/myapp

当然http里需要设置下passenger_rootpassenger_ruby

www.myapps.dev 和 myapps.dev 都会访问 /opt/apps/myapp

如果你连/etc/hosts都不想改,想像pow那样,在 Mac 下可以用这个脚本来启动pow里的DNS server

Linux有Hoof可以用(我还没试过),使用nsswitch来自动把指定的根域名解析到本机

github更新自己fork的代码,从原作者分支更新代码

github上有个很方便的功能叫fork,将别人的工程一键复制到自己账号下。这个功能很方便,但有点不足的是,当源项目更新后,你fork的分支并不会一起更新,需要自己手动去更新。下面记录下网上找到的更新的方法

1、在本地装好github客户端,或者git客户端

2、clone 自己的fork分支到本地,可以直接使用github客户端,clone到本地,如果使用命令行,命令为:

git clone git@github.com:break123/three.js.git three.js

3、增加源分支地址到你项目远程分支列表中(此处是关键),命令为:

git remote add mrdoob git://github.com/mrdoob/three.js.git

此处可使用git remote -v查看远程分支列表

4、fetch源分支的新版本到本地

[master]> git fetch mrdoob

5、合并两个版本的代码

[master]> git merge mrdoob/master

6、将合并后的代码push到github上去

[master]> git push origin master

通过github更新自己fork的代码 

Zepto 使用中的一些注意点

前段时间完成了公司一个产品的 HTML5 触屏版,开发中使用了 Zepto 这个著名的 DOM 操作库。
为什么不是 jQuery 呢?因为 jQuery 的目标是兼容所有主流浏览器,这就意味着它的大量代码对移动端的浏览器是无用或者低效的。
而 Zepto 只针对移动端浏览器编写,因此体积更小、效率更高,更重要的是,它的 API 完全仿照 jQuery ,所以学习成本也很低。

但是在开发过程中,我发现 Zepto 还远未成熟,其中包含了一些或大或小的“坑”,与 jQuery 的差距还是很明显的,所以写篇文章记录下,希望对后来者有帮助
注意,本文撰写时 Zepto 版本为 1.0 正式版

从哪里下载 Zepto

这个问题看起来很蠢,从官网下载不就行了嘛!可是你有没有发现下载链接上面有行小字呢?

There are more modules; a list of all modules is available in the README.

在这个 README 里面你会惊奇地发现,Zepto 源码中有 14 个模块,而官网提供的标准版里面只有 7 个模块!而且居然不包含对移动端开发非常重要的 touch 模块(提供对触摸事件的支持)!
所以我的建议是,不要从官网下载,而是从 Github 下载了源代码之后自己 Build 一个版本,这样你可以自行挑选适合的模块。比如我挑选的模块是这么几个:

  • polyfill,zepto,detect,event,ajax,form,fx 这7个就是标准版包含的模块
  • fx_methods 有了这个模块之后,.show() .hide() 等几个方法才能支持动画了,比如 .show('fast')
  • data 提供对 .data() 方法的完整支持,像 jQuery 一样用内存对象存储
  • assets 移除 img 元素后做一些特殊处理,用来清理内存
  • selector 更多的选择器的支持,后面会提到
  • touch 对触摸事件的支持,比如 tap 事件

如果你对 Node 不了解不知道如何 Build 的话,可以下载我的版本

不要用 click 事件,用 tap 代替

这个估计已经广为人知了,因为 click 事件有 200~300 ms 的延迟,为了更快的响应,最好用 Zepto 提供的 tap 事件
不相信的话,可以用以下代码测试一下

var t1,t2;
$('#id').tap(function () {
    t1 = Date.now();
});
$('#id').click(function () {
    t2 = Date.now();
    alert(t2 - t1);
});

Zepto 对 CSS 选择器的支持

郑重提醒,:text :checkbox :first 等等在 jQuery 里面很常用的选择器,Zepto 不支持!
原因很简单,jQuery 通过自己编写的 sizzle 引擎来支持 CSS 选择器,而 Zepto 是直接通过浏览器提供的document.querySelectorAll 接口。
这个接口只支持标准的 CSS 选择器,而上面提到的那些属于 jQuery 选择器扩展,所以仔细看看这个网页,注意一下这些选择器。

当然也有好消息,就是上面提到的 selector 模块,如果有这个模块的话,能够支持 部分 的 jQuery 选择器扩展,列举如下:

  • :visible :hidden
  • :selected :checked
  • :parent
  • :first :last :eq
  • :contains :has

元素的尺寸计算

首先 Zepto 没有 .innerHeight() .outerWidth() 等四个方法,其次,它的 .height()/.width() 方法也不完善,对于display:none 的元素,计算出的高宽都是 0
而这在 jQuery 里面是没有问题的,因为 jQuery 针对这种元素,会先设置其 css 样式设置为position: "absolute", visibility: "hidden", display: "block" 
计算完高宽后再恢复,参见 https://github.com/jquery/jquery/blob/master/src/css.js#L460
如果遇到这种特殊情况,可以参考 jQuery 写一个类似的方法

.prop() 方法的陷阱

有次我要把一个文本框置为只读,写了这么一行 $('#text').prop('readonly', true) 结果死活不工作
找了半天才发现,正确的写法是这样 $('#text').prop('readOnly', true) ,如果你居然看不出两者的差别,那么悄悄提示你:注意大小写!
翻了一下相关的文档,原来只读属性的正确拼法确实是 readOnly,可是在 jQuery 里面上一段代码却能正常工作
于是到 jQuery 源码里面一找才发现,还有这么一段https://github.com/jquery/jquery/blob/master/src/attributes.js#L466

jQuery.each([
    "tabIndex",
    "readOnly",
    "maxLength",
    "cellSpacing",
    "cellPadding",
    "rowSpan",
    "colSpan",
    "useMap",
    "frameBorder",
    "contentEditable"
], function() {
    jQuery.propFix[ this.toLowerCase() ] = this;
});

从这里也能看到,jQuery 的成熟度真是难以超越,因为他把我们都惯坏了……
考虑到这段代码比较简单,我厚颜无耻地抄袭了一下然后给 Zepto 提了一个 pull request ,如果你们喜欢这种无脑的用法,可以去评论表达支持(记得用英文)

2013-11-25 这个 PR 已经被 Merge

.show() 的动画效果

如果没有 fx_mehods 模块的话,.show() 方法是不支持动画的,不过有了这模块后,动画的支持还是有点小问题,比如这么一段 HTML

<div style="background:black;opacity:0.7;display:none">
    test
</div>

如果你调用 $('div').show('fast') ,那么动画完成后你看到的不会是一个半透明的元素,而是全黑不透明的
因为 Zepto 的 .show() 动画实现的很简单,没有高宽的变化,而是将透明度从 0 逐渐变为 1,所以元素上原来设置的透明度就被替代了。
这种情况下,可以用 .fadeIn() 方法来替代 .show()

结语

看到这里相信你已经了解为什么我说” Zepto 还远未成熟“,目前它其实还仅仅处于“能用”,远未达到 jQuery “好用”的地步
最后,关于整个 HTML5 触屏版的前端开发,我有篇 Slide 做了总结,本文只是其中关于 Zepto 部分的详细阐述,感兴趣的可以去看看

http://chaoskeh.com/blog/some-experience-of-using-zepto.html

2010 Web前端技术趋势及总结 Facebook摘全明星MVP

摘要:经过这段时间国内及国外各大公司的集中自曝,我们可以从中总结出 2010 Web前端技术的一些趋势。总的来说,随着后端技术的成熟,各大公司已经把重点从后端架构调整/建设转移至前端。

导读:经过这段时间国内(百度,淘宝,新浪等)及国外(Facebook,Youtube,Yahoo等)各大公司的集中自曝,我们可以从中总结出 2010 Web前端技术的一些趋势。总的来说,随着后端技术(存储,并发,分布式)的成熟,各大公司已经把重点从后端架构调整/建设转移至前端(TTI时间,快速发布,带宽利用率)。

作为明星技术的HTML 5/CSS 3,都未正式成为各公司的考虑重心,虽有所尝试,但在关键功能上,均未成为主力。这也W3C对当前HTML 5/CSS 3标准现状的表述:“不适宜用作生产环境”一致。

Web前端技术的范围

1. 编程语言/技术(HTML,JavaScript,CSS等)

2. 跨浏览器兼容性/支持(JS Framework,CSS Library)

3. 网络传输性能(并行下载,带宽利用率)

4. 浏览器渲染时间/性能(TTI即用户可交互前等待时间,JS执行性能)

个人感觉,Facebook无疑又成为了技术上的明星,在大家还在感慨其对于PHP的重大改进HipHop(Blocked inside China mainland)的时候,今年Facebook又在前端技术方面给大家带来了惊喜。

Facebook面临的问题

500M(Million)注册用户,50%每天至少访问一次,用户平均每日在线时间为5小时25分钟。带宽及服务器压力均很大。

Facebook的解决方案

Quickling

Facebook提出了一个新名词Ajaxify,顾名思义,就是将传统的POST/GET转换为Ajax请求。优点显而易见,首先减少了不必要的 HTML传输,只请求和渲染页面需要更新的部分,这就相应减少了所需传输的内容加快了内容送达至用户的时间。并且也减少了服务端对HTML的不必要的渲染。Facebook也提到了可以减少session的重复load/unload。

使用Ajax也许不是什么新鲜的新闻,大家拒绝这项技术的原因可能很大程度基于SEO的需求。解决方案也很简单,将Ajax只是作为提高用户体验的手段,而不是浏览网站必须的方法,即可解决SEO的问题(P.S. Facebook不需要SEO)。

整套方案包括:Link Controller, HistoryManager, BootLoader, Busy Indicator, CSS Unloading, Permanent link support, Resetting timer functions。这些方案本身没有什么特殊的,大部分都可以顾名思义,需要解释一下的可能是link controller,其含义是将标准的HTML LINK请求转换为Ajax请求(通过绑定click事件)。Facebook的可贵之处是提供了这一整套完整的解决方案,最大程度上保证了网站的可用性。

效果:

提高了10%-30%的网站传输时间,并提高了20%-30%的服务端页面渲染速度。

使用范围:

45%的Facebook页面使用了此项技术。

PageCache

简单的说,就是将访问过的页面缓存在客户端。但我们知道,作为Facebook这样交互性很强的网站,需要保障用户能尽早的获得更新后的信息,而不是给用户展示一个毫无意义的过期页面。

Facebook设计了一个框架来识别一个页面是否来自于缓存(猜测:页面首次加载完毕后将所有Ajax的Callback和Result缓存在本地。Facebook页面是基于Ajax获取页面内容),若来自于缓存,通过Ajax来更新所需更新的模块(猜测:通过JavaScript预先定义本页面所需更新的div Id及对应的callback handler,并在页面下载时同时下载下来)。

其提到了三种更新类型:增量更新,用户复写(例如用户在页面上回复了一则评论)及跨页更新(例如在消息详细页面将一则消息标识为已读,需将首页的未读消息数进行更新)。核心思路还是依据Ajax进行更新。具体思路为:

增量更新:只要页面来自于缓存,即更新所有预定义的需增量更新的模块。

用户复写:通过HistoryManager记录用户操作并在cache页面读取后重放所有被标记为“replayable”的操作。

跨页更新:通过服务端Database API发送信号至客户端将过期缓存标识为invalid(不清楚如何实现。也许是DB端提供一个开放的webservice,客户端通过Ajax持续访问此API来获得此信息)。获得了缓存过期信号后,通过Ajax更新需要更新的信息。

Facebook顺带提到了一个更新Ajax内容避免页面变化/闪烁的小技巧,就是先将需更新的地方设置为blank,而非直接更新其内容。

效果:

加速了10倍的网站响应时间并节约了20%的服务端页面渲染成本。

BigPipe

此项技术通过将页面分割为各个Pagelets的方式,将整张页面的获取/渲染变成了并行的方式(感觉非常像iframe sets,但Facebook使用Ajax实现。)。此项技术是Quickling和PageCache的基石。此技术包含了服务端/客户端两方面,在前后端均打破了以往页面的渲染形式。

实现细节:

Pagelet的Response为JSON格式,包括id,css,js,content,onload等属性及相应内容,收到后会通过预定义好的JS function来进行渲染。

Pagelet提供的高级功能:Pagelet的继承,Phased Rendering(猜测:依据规则渲染,也就是依据Pagelet的Response进行渲染),跨Pagelet依赖(数据依赖,显示依赖,JS依赖)。

BigPipe的三种模式:

◆一次渲染模式:即普通模式,支持搜索引擎,用来支持那些不支持JS的客户端。

◆管线模式:即并行模式,并行请求,并即时渲染。

◆并行模式:并行请求,但在获得所有请求的结果后再渲染。

效果:

提高了2倍的页面响应时间。

扩展阅读:世界最大的PHP站点 Facebook后台技术探秘

YouTube面临的问题

每天2Billion的访问。每分钟上传35小时的内容。可YouTube需要即时播放视频!越快越好。

YouTube解决方案

1. 将JavaScript引用位置从页首移至页尾。

2. 直接嵌入Flash Player(YouTube之前使用JS来加载Flash Player)。通过页尾的JS来判断客户端的Flash版本(或不支持Flash),来替换预先嵌入的Flash Player或内容(如果需要的话),用来支持特定的客户群。

效果:页面渲染时间从~400ms降低为~200ms。Flash播放时间从~1200ms降低为~1100ms。

3. 预加载视频连接: 通过使用JavaScript创建Image引用视频内容来与解析DNS并预开启一个connection供之后使用。

效果:建立视频连接的总时间从~260ms降低为~180ms。

4. 提供简化版:这个很无聊,就是提供一个简版。

效果:页面加载时间从~1750ms降低为~1100ms。

5. UIX Widget系统:延迟加载非关键内容。其实整段没什么新意,大部分省略,无非是通过Ajax在页面渲染完后再来动态加载非关键内容。比较特别的是利用 JS的事件冒泡,在最上层用一个handler来处理各种事件(优点不详。。也许只是代码比较简洁集中吧),通过CSS来标识和识别对应的 handler。

Yahoo Mail

Yahoo如何构建下一代的Mail系统?答案就是通过YUI3。Yahoo的技术绝对是最优的,其已经将Web前端技术发展到一个非常成熟的地步,照顾到Web的方方面面(数据压缩,模块化,高效CSS,非阻碍式JavaScript加载,静态内容提供,利用浏览器Cache等等),所以也鲜有创新了。某种程度上来说,Facebook的一些所谓创新也不过是后知后觉,Yahoo早已考虑并实现了这些方案,只是也许不是那么有针对性而已。

Baidu

感觉整体倾向于组织结构介绍及一些比较过时的内容。

Taobao

还在讨论一些何时使用Ajax,何时不使用的问题。

相反的,淘宝的精益测试倒是引起了我的兴趣,出自微软的淘宝员工鹤云讲述了淘宝是如何进行CI(持续集成)的。有一些经验例如代码覆盖率测试也给人一些启发。

新浪博客

也是一些组织架构,开发方式的内容。介绍了一下新浪自己的JavaScript框架。并无太多亮点。

大概就总结了这么多吧,感觉还是国外在主导。国内也在越来越重视这个方向,一些有实力的企业也做出了一些成绩,但还是与国际潮流有差距,也许是重视程度的区别吧。欢迎大家补充讨论。

原文链接:http://developer.51cto.com/art/201012/237980.htm