[转载]如何高效利用GitHub ← 阳志平的个人网站::技术

mikel阅读(1161)

[转载]如何高效利用GitHub ← 阳志平的个人网站::技术.

如何高效利用GitHub

正是Github,让社会化编程成为现实。本文尝试谈谈GitHub的文化、技巧与影响。

Q1:GitHub是什么

A1:一家公司

github

位于旧金山,由Chris Wanstrath, PJ HyettTom Preston-Werner三位开发者在2008年4月创办。迄今拥有59名全职员工,主要提供基于git的版本托管服务。

在此之前,它是由TomChris 在本地程序员聚会中,开始的一个用于托管git的项目。正如每个伟大的传奇都开始于一场冒险,Tom在这篇文章我如何辞掉微软30万年薪邀约,创办GitHub中谈到:

当我老去,回顾一生,我想说,“哇,那是一场冒险“;而不是,“哇,我真的很安稳。“

另一位创始人Chris也详细描述了GitHub初创的前因后果,他说道:

Do whatever you want.

于是,在2008年4月10号这一天,GitHub正式成立。

目前看来,GitHub这场冒险已经胜出。根据来自维基百科关于GitHub的描述,我们可以形象地看出GitHub的增长速度:

github

今天,GitHub已是:

  • 一个拥有143万开发者的社区。其中不乏Linux发明者Torvalds这样的顶级黑客,以及Rails创始人DHH这样的年轻极客。
  • 这个星球上最流行的开源托管服务。目前已托管431万git项目,不仅越来越多知名开源项目迁入GitHub,比如Ruby on Rails、JQuery、Ruby、Erlang/OTP;近三年流行的开源库往往在GitHub首发,例如:BootStrapNode.jsCoffeScript等。
  • alexa全球排名414的网站

Q2:GitHub风格

A2: GitHub只是GitHub

强调敏捷开发与快速原型,而又的确成功的创业团队,常具备一个重要气质:有自己的文化风格。如GitHub,又如37signals。通过他们的快速开发,向用户证明了团队在技术上的能力,并且时常有惊喜。同时,通过强调特立独行的文化,将对半衰期过短的产品族群的信任转为对GitHub团队的信任。

Gravatars的创始人(对,就是互联网最流行的头像托管系统)、Jekyll(对,它就是我近几年用的博客系统)作者、GitHub创始人,现任CTO Tom在GitHub第一年学到的10大教训创业学校演讲中谈到GitHub文化的方方面面。我尝试将这种风格总结为以下要点:

  • 专注创作,高创意
  • 运营良好与较高的内外满意度
  • 高利润,较低的融资额或零融资

创业公司多半死在钱上,就让我们先从钱谈起:

高利润,较低的融资额或者零融资

类似于GitHub这样的公司,拿到风险投资很难吗?恰恰相反,创始人PJ Hyett 在Hacker News的一篇评论中提到,自从GitHub创办以来,已与几十个VC沟通过。但是,直到今天,GitHub的融资额还是为零,并引以为豪。让我们看看GitHub官网的自我介绍:

image

运营良好与较高的内外满意度

在Quora上有人问道,GitHub是否寻找被收购?,还是PJ Hyett ,他的回答是:No。

GitHub从一开始就运营良好,员工拥有较高满意度,看看这些不太一样的做法:

  • 每一位GitHub公司的新员工,官方博客将发表文章欢迎。
  • 在GitHub内部,没有经理,需求内容与优先级由项目组自行决策。
  • 选择自己的工作时间、工作地点。
  • 员工来自开源社区。
  • 能开源的尽可能开源。

富有激情、创意的员工使得GitHub得到了社区的广泛认同,从而拥有极高的客户满意度,并从创业一开始就盈利。一份早期的调查表明,GitHub很快成为Git托管首选。

专注创作,高创意

GitHub59名全职员工仅有29名员工在本地工作!不仅仅是工作地点的安排富有创意,GitHub员工Holman, 详细介绍了GitHub的工作方式:

Q3:在GitHub,如何跟牛人学习

A3:在学习区刻意练习

追随牛人,与他们一起修行

修行之道: 关注大师的言行, 跟随大师的举动, 和大师一并修行, 领会大师的意境, 成为真正的大师。 

正如这首禅诗所言,与其在墙内仰望牛人,不如直接在GitHub:

  • watch、fork牛人们
  • 对他们的项目提交pull request
  • 主动给牛人们的项目写wiki或提交测试用例,或者问题
  • 还可以帮他们翻译中文

GitHub本身建构在git之上,git成为勾搭大师们的必要工具,以下读物成为首选:

如果希望进一步深入,可以阅读已有中文翻译版的材料:

同样,如果希望了解更多GitHub自身的知识,GitHub官方文档值得推荐:

牛人在哪里?

关于学习的心理学研究,常常会谈到一个术语:元认知、元学习、元知识。是的,关于认知的认知、关于学习的学习、关于知识的知识,你对这些信息的偏好与熟练掌握,会让你在学习一门新东西时更加轻车熟路。对一手信息进行回溯,比如作者、创始人、最初文献出处,总是会让你更容易理解知识。

在学习区刻意练习:借助GitStats进行项目统计

如何学习一门新的编程语言?——在学习区刻意练习中,我已谈过:

学习编程最好的方式是在学习区刻意练习。

如何进行自我监督?

借助于GitStats,我们能很好地统计自己的每个项目的工作量,从而看到工作进展。

用法如下,

#复制GitStats项目到本地 cd ~/dev git clone git://github.com/trybeee/GitStats.git python ~/dev/gitstats/git-stats /youproject public 

以下为生成结果示范:

每周代码提交次数:

github

每天代码提交行数:

github

如果Fork别人的项目或者多人合作项目,最好每人都拥有一个独立分支,然后由项目维护人合并。如何建立自己的分支?

# 分支的创建和合并 # git branch yourbranch # git checkout yourbranch 切换到yourbranch # 开发yourbranch分支,然后开发之后与master分支合并 # git checkout master # git merge yourbranch # git branch -d yourbranch 合并完后删除本地分支 

如何将牛人的远程分支更新到自己的本地分支?

# 查看当前项目下远程 # git remote # 增加新的分支链接,例如 git remote add niuren giturl… # 获取牛人的远程更新 git fetch niuren # 将牛人的远程更新合并到本地分支 git merge niuren/master 

生产力小技巧

codeshelver:给git库做标签

观察的项目如果多了,怎么管理?用codeshelver,安装扩展之后,可以对GitHub项目做标签。

gollum:利用git与github做wiki

gollum是一个基于git的轻型wiki系统。

GitHubwatcher: 监测重点项目

GitHubwatcher适用于通知不频繁的情景。

GitHub官方资源

GitHub官方列出了一些有用的脚本与书签

社区驱动的安装与配置文件

GitHub中各类配置文件层出不穷,一些常用的:

  • osh-my-zsh:将终端从bash改为zsh之后,可考虑安装社区驱动的zsh配置文件,含有多个插件。可参考旧文zsh与oh-my-zsh
  • gitignore:GitHub官方出品
  • yourchili:服务器各类安装shell,比如安装nginx等。

Q4: 享受纯粹的写作与演讲

A4:回归创作的初始

写作

早在2008年,就有技术图书作者通过Git来写作,以下是示范:

你能想到的技术前沿话题,大多能在GitHub找到相应的培训材料或者开源图书。

个人写作照样适用。在前文理想的写作环境:Git+GitHub+Markdown+Jekyll,我已经格外赞美过这些美好事物了。

暖色调的灯光,足够宽度的工作台,听着清脆的键盘声音,基于Git、GitHub、Markdown与Jekyll来写作,不担心写废与排版,只关 注最纯粹的写作,是一种享受。我有时候会想,如果Git、Github、Markdown、Jekyll,再加上Yaml、Json的作者,让这些作者们 重新来设计今天互联网基础架构偏文本的部分,会诞生一些什么?

个人博客

借助于Jekyllbootstrap,可以在Github上快速搭建一个基于jekyll的博客系统。

除了这个简单易行的办法之外,还存在一些其他方法,例如:

演讲

借助于GitHub,可以享受更纯粹、更酷的演讲。GitHub 2011年收购Ordered List之后,从此可以通过speakerdeck更好的分享ppt文档。

我们还可以:

  • 使用GitHub著名传教士、Progit作者Scott Chacon开发的showoff
  • 来自开源社区的其他演讲库impress.js

Q5: 代码帮你找工作

A5:GitHub简历很诚实

NumEricR(非GitHub工作人员)基于GitHub Pages功能做了一个简历生成器,使用极其简单,登陆网站GitHub简历生成器,填入你的GitHub网站用户名即可。

fredwu是Ruby中文社区活跃份子,他的开源项目angel_nest,一个天使投资与创业者对接的网站,适合Ruby初学者升级为Ruby中级开发者时学习,也在Hacker News上被热烈讨论过,让我们来看看他的简历:

http://resume.GitHub.com/?fredwu

正是因为GitHub上的代码无法造假,也容易通过你关注的项目来了解知识面的宽度与深度。现在越来越多知名公司活跃在GitHub,发布开源库并招募各类人才,例如:FacebookTwitterYahoo

开始有了第三方网站提供基于GitHub的人才招聘服务,例如:

  • GitHire:通过它,可以找出你所在地区的程序员。
  • Gitalytics.com:通过它,能评估某位程序员在GitHub、LinkedIn、StackOverflow、hackernews等多个网站的影响力。

Q6: GitHub还在影响一些什么

A6:让计算机增强人类智慧

很多年前,在某个名声显赫的学府中,两位先后拿过图灵奖的牛人有一段对话:

  • 牛人A:我们要给机器赋予智慧,让他们有自我意识!
  • 牛人B:你要给机器做那么多好事?那你打算给人类做点什么呢?

这段对话来自《失控》。牛人A是明斯基,他最喜欢将人类看做有血肉的机器,他的框架理论成为认知心理学、人工智能入门基础。牛人B则是恩格尔巴特。当明斯基1961年发表他著名的文章人工智能走向时,恩格尔巴特还籍籍无名。直到次年,恩格尔巴特发表宏文:人类智力的增强:一种概念框架。提出不同于明斯基的另一条增强人类智力的道路:不要尝试发明自动打字的机器,而是尝试发明鼠标,并且他真的发明鼠标成功了!

从近些年的发展来看,仍然是明斯基占上风,但是,三十年河东,三十年河西,明斯基的人工智能方向又有多少年没有大突破了?相反,来自恩格尔巴特的群 件、集体智慧等思想,逐步成为步入Web2.0时代之后的共识。无关对错,可以说,恩格尔巴特为增强人类智力,提供了可行的框架。与其去发明聪明的、昂贵 的、功能一体化的智能机器人,还不如发明类似于鼠标这样笨笨的、廉价的、功能单一的人类智慧服务单件。明斯基的机器人很容易陷入死胡同,没有上升到哲学的 高度。现在慢慢又回到恩格尔巴特这个方向来了。比如现在IBM开始宣传认知计算

从git与GitHub设计与解决的问题本质来看,明显加速了代码生产流程,促进了卓越智力产品的诞生。这就是一种典型的web2.0对智力生产流 程的改良与人类智慧的增强。同样,某种意义上,小说写作网站也起到类似作用。但是,学术界尤其是社会科学类的智力产品生产似乎还停留在一个古老阶段。在开 源领域,好想法层出不穷,极客影响极客,最终产生的是酷玩意。这些酷玩意抛弃浮华,直奔问题本质。那么,有没有科学界的GitHub?

类似问题层出不穷,以下为其他领域产品不完全名单。

学术研究

数据

科学计算

  • opani:雏形中,支持R、Python等多种。

教育

  • OpenStudy:一个社会性学习网络,通过互助来更好地学习,主题涉及到计算机、数学、写作等。
  • openhatch: 通过练习、任务等帮助新手更好地进入开源社区

Q7:除了GitHub,还可以选择什么?

A7:nil

因为进化的需要,多数裸猿存在选择强迫症:哪种程序语言更好?哪个web开发框架更好?当然,最令宅男技术男们羡慕的问题是,高白瘦御姐还是青春小萝莉好?:D

除了GitHub之外,

  • 中国山寨品是不是更好?(为什么不写他们名字,你懂的,山寨品总是善于争论谁是第一个山寨的,各自的排名先后:D)
  • 免费的BitBucket是不是更适合Python程序员?
  • 作为一名折腾族,我不自己搭建一个gitlabhq,是不是对不起自己?

我们可以理解,正是因为无数条分岔路口,让人类不再受制于某种基因、特定疾病、独裁家族,从而拥有无限的可能。但是,这种选择强迫症与远古时代可怜的信息量相比较,

  • 今天这个大数据时代,它还会有助于人类作为族群的整体进化与作为个体的幸福吗?
  • 今天一位一线城市30岁大学毕业生经历的选择与孔子整个一生经历的选择,纯论数量,谁多谁少?

生命如此短暂,为什么总要将青春浪费在不断的选择之中呢?罚你,回头阅读心理学家施瓦茨(Barry Schwartz)的TED演讲:选择之困惑——为何多即是少,1百遍啊1百遍。请记住施瓦茨的演讲要点:

  • 更多的选择不代表更多的自由;
  • 更多的选择导致决策的延迟和降低的满意感;
  • 快乐之秘诀,在于降低自己的期望值。

最后,让我再抒情一把吧,

美好的事物总是离不开被墙的命运,让我们静静地期待那一天的来临… 也让我们在各自行业的努力,让下一代、下一代、下一代…(希望N<=1,如果N>=4,我做鬼也放不过你们!)不再拥有这一天。

[转载]ASP.NET MVC开源项目--SusuCMS - HackerVirus - 博客园

mikel阅读(1264)

[转载]SusuCMS – HackerVirus – 博客园.

SusuCMS是一款免费开源的基于ASP.NET MVC开发的的CMS。我的目标是使他尽量对普通用户友好,对开发者友好。

1. 安装完SusuCMS后,使用网址:http://您的域名/admin 进入系统管理后台(默认用户名为admin,默认密码为admin)。 

 

2. 进入后台直接点击左边菜单的Sites,即可管理站点列表。SusuCMS是支持多站点的。这边我已经创建了4个不同的站点。

3. 点击Create按钮,你可以创建网站了。这边有个Template选项是提供站点模版的选择的,如果你想创建一个Blog类型的站点就可以选择Blog – Maximus4T选项。一旦创建网站点就不可更改。Url是网站的主地址,也支持子目录和ip,端口号之类的。点击保存后,系统会自动初始化数据,创建 一些基本的页面。

 

4. 创建页面。为了可以单独对单个站点进行编辑,你可以点击站点列表的扳手图标进入。或者点击左边菜单进入。

 

 

 5.页面管理。列表是树状的,在这里你可以修改页面状态。页面之间的父子关系。点击鼠标形状按钮即可进入页面可视化管理。

 

 

 

6. 页面Widget管理。Widget在使用之前需创建。

 

更多请访问: http://www.susucms.com

源码在http://sourceforge.net/projects/susucms 上,

[转载]ASP.NET MVC3关于生成纯静态后如何不再走路由直接访问静态页面_实用技巧_脚本之家

mikel阅读(1194)

[转载]ASP.NET MVC3关于生成纯静态后如何不再走路由直接访问静态页面_实用技巧_脚本之家.

高访问量类型的电子商务网站,需要将一些不是经常变化的页面生成静态页面,然后普通用户就可以直接访问这些静态页面而不用再访问需要连接数据库的动态页面。那么ASP.NET MVC3中如何做到这一点呢
要解决这个问题,我们需要先了解ASP.NET应用程序的生命周期,先看下面作者整理的一张图片:

从图中我们可以清楚的看到:通用IIS访问应用程序时,每次的单个页面URL访问时,都会先经过HttpApplication 管线处理请求,走过BeginRequest 事件之后才会去走路由访问具体的Controller和Action,最后结束的时候会请求EndRequest事件。下面用一张图来表示这个顺序:

注意图中标示的红色部分就是我们要实现的部分,实现如下:
1 新建MyHandler.cs

复制代码 代码如下:
public class MyHandler:IHttpModule
{
public void Init(HttpApplication application)
{
application.BeginRequest +=
(new EventHandler(this.Application_BeginRequest));
application.EndRequest +=
(new EventHandler(this.Application_EndRequest));
}
private void Application_BeginRequest(Object source,
EventArgs e)
{
// Create HttpApplication and HttpContext objects to access
// request and response properties.
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
string filePath = context.Request.FilePath;
string fileExtension =
VirtualPathUtility.GetExtension(filePath);
if (fileExtension.Equals(“.html”))
{
context.Response.WriteFile(context.Server.MapPath(filePath));//直接走静态页面
//此处可以加入缓存,条件也可以根据需要来自己定义
context.Response.End();
}
}
private void Application_EndRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
string filePath = context.Request.FilePath;
string fileExtension =
VirtualPathUtility.GetExtension(filePath);
if (fileExtension.Equals(“.html”))
{
context.Response.Write(“<hr><h1><font color=red>” +
“HelloWorldModule: End of Request</font></h1>”);
}
}
public void Dispose() { }
}

2. web.config中加入以下代码,才会运行自定义的管道处理类
复制代码 代码如下:
<httpModules>
<add name=”MvcTest.MyHandler” type=”MvcTest.MyHandler”/>
</httpModules>

运行一下自己的代码,看看效果你就全明白了!
补充:根据@小尾鱼的提示,如果直接在自己的项目文件下生产了和URL中一样的目录文件,比如访问:yourdomin.com/product/1.html,你的项目文件夹下真的存在product/1.html这个路径,那么IIS会直接去请求这个静态页面,如果项目中使用了自定义的管道处理程序,那么这个静态页仍然会走我们的自定义管道处理程序,我们可以在这里通过缓存来实现要不要重新成长静态页或删除过期产品的静态页,如果不使用此方法,只能去写执行计划,定时跑这些静态文件了,修改Application_BeginRequest
复制代码 代码如下:
private void Application_BeginRequest(Object source,
EventArgs e)
{
// Create HttpApplication and HttpContext objects to access
// request and response properties.
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
string filePath = context.Request.FilePath;
string fileExtension =
VirtualPathUtility.GetExtension(filePath);
if (fileExtension.Equals(“.html”))
{
//判断缓存是否存在,不存在加入缓存,调用生成静态的类和方法
//产品过期,移除静态文件,302重定向
if (System.IO.File.Exists(context.Server.MapPath(filePath)))
{
context.Response.WriteFile(context.Server.MapPath(filePath));
context.Response.End();
}
}

[转载]asp.net mvc3的静态化实现 - TerryLiang - 博客园

mikel阅读(1387)

[转载]asp.net mvc3的静态化实现 – TerryLiang – 博客园.

静态化处理,可以大大提高客户的访问浏览速度,提高用户体验,同时也降低了服务器本身的压力。在ASP.NET mvc3中,可以相对容易地处理静态化问题,不用过多考虑静态网页的同步,生成等等问题。我提供这个方法很简单,就需要在需要静态化处理的 Controller或Action上加一个Attribute就可以。下面是我写的一个生成静态文件的ActionFilterAttribute。

using System;
using System.IO;
     using System.Text;
     using System.Web;
     using System.Web.Mvc;
     using NLog;

     /// <summary>
     /// 生成静态文件
     /// </summary>
     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
     public class GenerateStaticFileAttribute : ActionFilterAttribute
     {
         #region 私有属性

         private static readonly Logger logger = LogManager.GetCurrentClassLogger();

         #endregion

         #region 公共属性

         /// <summary>
         /// 过期时间,以小时为单位
         /// </summary>
         public int Expiration { get; set; }

         /// <summary>
         /// 文件后缀名
         /// </summary>
         public string Suffix { get; set; }

         /// <summary>
         /// 缓存目录
         /// </summary>
         public string CacheDirectory { get; set; }

         /// <summary>
         /// 指定生成的文件名
         /// </summary>
         public string FileName { get; set; }

         #endregion

         #region 构造函数

         /// <summary>
         /// 默认构造函数
         /// </summary>
         public GenerateStaticFileAttribute()
         {
             Expiration = 1;
             CacheDirectory = AppDomain.CurrentDomain.BaseDirectory;
         }

         #endregion

         #region 方法

         public override void OnResultExecuted(ResultExecutedContext filterContext)
         {
             var fileInfo = GetCacheFileInfo(filterContext);

             if ((fileInfo.Exists && fileInfo.CreationTime.AddHours(Expiration) < DateTime.Now) || !fileInfo.Exists)
             {
                 var deleted = false;

                 try
                 {
                     if (fileInfo.Exists)
                     {
                         fileInfo.Delete();
                     }

                     deleted = true;
                 }
                 catch (Exception ex)
                 {
                     logger.Error("删除文件:{0}发生异常:{1}", fileInfo.FullName, ex.StackTrace);
                 }

                 var created = false;

                 try
                 {
                     if (!fileInfo.Directory.Exists)
                     {
                         fileInfo.Directory.Create();
                     }

                     created = true;
                 }
                 catch (IOException ex)
                 {
                     logger.Error("创建目录:{0}发生异常:{1}", fileInfo.DirectoryName, ex.StackTrace);
                 }

                 if (deleted && created)
                 {
                     FileStream fileStream = null;
                     StreamWriter streamWriter = null;

                     try
                     {
                         var viewResult = filterContext.Result as ViewResult;
                         fileStream = new FileStream(fileInfo.FullName, FileMode.CreateNew, FileAccess.Write, FileShare.None);
                         streamWriter = new StreamWriter(fileStream);
                         var viewContext = new ViewContext(filterContext.Controller.ControllerContext, viewResult.View, viewResult.ViewData, viewResult.TempData, streamWriter);
                         viewResult.View.Render(viewContext, streamWriter);
                     }
                     catch (Exception ex)
                     {
                         logger.Error("生成缓存文件:{0}发生异常:{1}", fileInfo.FullName, ex.StackTrace);
                     }
                     finally
                     {
                         if (streamWriter != null)
                         {
                             streamWriter.Close();
                         }

                         if (fileStream != null)
                         {
                             fileStream.Close();
                         }
                     }
                 }
             }
         }

         /// <summary>
         /// 生成文件Key
         /// </summary>
         /// <param name="controllerContext">ControllerContext</param>
         /// <returns>文件Key</returns>
         protected virtual string GenerateKey(ControllerContext controllerContext)
         {
             var url = controllerContext.HttpContext.Request.Url.ToString();

             if (string.IsNullOrWhiteSpace(url))
             {
                 return null;
             }

             var th = new TigerHash();
             var data = th.ComputeHash(Encoding.Unicode.GetBytes(url));
             var key = Convert.ToBase64String(data, Base64FormattingOptions.None);
             key = HttpUtility.UrlEncode(key);

             return key;
         }

         /// <summary>
         /// 获取静态的文件信息
         /// </summary>
         /// <param name="controllerContext">ControllerContext</param>
         /// <returns>缓存文件信息</returns>
         protected virtual FileInfo GetCacheFileInfo(ControllerContext controllerContext)
         {
             var fileName = string.Empty;

             if (string.IsNullOrWhiteSpace(FileName))
             {
                 var key = GenerateKey(controllerContext);

                 if (!string.IsNullOrWhiteSpace(key))
                 {
                     fileName = Path.Combine(CacheDirectory, string.IsNullOrWhiteSpace(Suffix) ? key : string.Format("{0}.{1}", key, Suffix));
                 }
             }
             else
             {
                 fileName = Path.Combine(CacheDirectory, FileName);
             }

             return new FileInfo(fileName);
         }

         #endregion
     }

如果大家对于生成的文件和目录有特殊的要求,那可以重写GetCacheFileInfo方法,比如按照日期生成目录等等更复杂的目录和文件结构。当然以上代码只是提供了生成静态页的方法,但是访问如何解决呢? 访问静态文件和规则就需要在HttpApplication的Application_BeginRequest实现了。首先可以设置需要静态化访问的路由地址以html结尾。下面的是一个用于首页的静态化访问的实现,很简单,当然你可以实现比较复杂的逻辑,比如根据文件时间来判断是否应该访问静态文件等等。

protected void Application_BeginRequest(object sender, EventArgs e)
         {
             StaticContentRewrite();
         }

         /// <summary>
         /// 处理静态发布内容
         /// </summary>
         private void StaticContentRewrite()
         {
             if (Context.Request.FilePath == "/" || Context.Request.FilePath.StartsWith("/index.html", StringComparison.OrdinalIgnoreCase))
             {
                 if (File.Exists(Server.MapPath("index.html")))
                 {
                     Context.RewritePath("index.html");
                 }
             }

[转载]CSS z-index 属性的使用方法和层级树的概念 - NeoEase

mikel阅读(948)

[转载]CSS z-index 属性的使用方法和层级树的概念 – NeoEase.

CSS 中的 z-index 属性用 于设置节点的堆叠顺序, 拥有更高堆叠顺序的节点将显示在堆叠顺序较低的节点前面, 这是我们对 z-index 属性普遍的认识. 与此同时, 我们总是对堆叠顺序捉摸不透, 将 z-index 的值设得很大也未必能将节点显示在最前面. 本文将通过一些例子对 z-index 的使用方法进行分析, 并且为各位带入 z-index 层级树的概念.

 

这个星期我们团队做了一次内部的技术分享, 南瓜小米粥为我们分享了他对 CSS z-index 的理解和引入层级树这个概念. 这个分享的现场效果很好, 所以我也将 z-index 和层级树话题搬到博客来谈一谈.

本文谈及多个影响节点显示层级的规则, 其中用到的所有例子全部罗列在《position 属性和 z-index 属性对页面节点层级影响的例子》中.

目录

顺序规则

如果不对节点设定 position 属性, 位于文档流后面的节点会遮盖前面的节点.

<div id="a">A</div>
<div id="b">B</div>

CSS z-index 属性顺序规则的例子

定位规则

如果将 position 设为 static, 位于文档流后面的节点依然会遮盖前面的节点浮动, 所以 position:static 不会影响节点的遮盖关系.

<div id="a" style="position:static;">A</div>
<div id="b">B</div>

CSS z-index 属性定位规则的例子, static

如果将 position 设为 relative (相对定位), absolute (绝对定位) 或者 fixed (固定定位), 这样的节点会覆盖没有设置 position 属性或者属性值为 static 的节点, 说明前者比后者的默认层级高.

<div id="a" style="position:relative;">A</div>
<div id="b">B</div>

CSS z-index 属性定位规则的例子, relative | absolute | fixed

在没有 z-index 属性干扰的情况下, 根据这顺序规则和定位规则, 我们可以做出更加复杂的结构. 这里我们对 A 和 B 都不设定 position, 但对 A 的子节点 A-1 设定 position:relative. 根据顺序规则, B 会覆盖 A, 又根据定位规则 A’ 会覆盖 B.

<div id="a">
	<div id="a-1" style="position:relative;">A-1</div>
</div>
<div id="b">B</div>

CSS z-index 属性互相覆盖的例子

上面互相覆盖在什么时候用到这样的实现? 看起来偏门, 其实很常用, 比如说, 电子商务网站侧栏的类目展示列表就可以用这个技巧来实现.

下图是某网站的类目展示区域, 二级类目的悬浮层覆盖一级类目列表外框, 而一级类目的节点覆盖二级类目的悬浮层. 如果使用 CSS 实现展示效果, 一级类目的外框相当于上面例子中的 A, 一级类目的节点相当于 A-1, 二级类目的悬浮层相当于 B.

电子商务网站侧栏的类目展示列表

参与规则

我们尝试不用 position 属性, 但为节点加上 z-index 属性. 发现 z-index 对节点没起作用.

<div id="a" style="z-index:2;">A</div>
<div id="b" style="z-index:1;">B</div>
<div id="c" style="z-index:0;">C</div>

CSS z-index 属性参与规则的例子, 没有明确定位的时候

W3C 对 z-index 属性的描述中提到 在 z-index 属性仅在节点的 position 属性为 relative, absolute 或者 fixed 时生效.

The z-index property specifies the stack order of an element. Only works on positioned elements(position: absolute;, position: relative; or position: fixed;).

<div id="a" style="z-index:2;">A</div>
<div id="b" style="position:relative;z-index:1;">B</div>
<div id="c" style="position:relative;z-index:0;">C</div>

CSS z-index 属性参与规则的例子, 明确定位的节点才能使用 z-index 属性

默认值规则

如果所有节点都定义了 position:relative. z-index 为 0 的节点与没有定义 z-index 在同一层级内没有高低之分; 但 z-index 大于等于 1 的节点会遮盖没有定义 z-index 的节点; z-index 的值为负数的节点将被没有定义 z-index 的节点覆盖.

<div id="a" style="position:relative;z-index:1;">A</div>
<div id="b" style="position:relative;z-index:0;">B</div>
<div id="c" style="position:relative;">C</div>
<div id="d" style="position:relative;z-index:0;">D</div>

CSS z-index 属性默认值规则的例子

通过检查我们还发现, 当 position 设为 relative, absolute 或者 fixed, 而没有设置 z-index 时, IE8 以上和 W3C 浏览器 (下文我们统称为 W3C 浏览器) 的 z-index 默认值是 auto, 但 IE6 和 IE7 是 0.

从父规则

如果 A, B 节点都定义了 position:relative, A 节点的 z-index 比 B 节点大, 那么 A 的子节点必定覆盖在 B 的子节点前面.

<div id="a" style="position:relative;z-index:1;">
	<div id="a-1">A-1</div>
</div>

<div id="b" style="position:relative;z-index:0;">
	<div id="b-1">B-1</div>
</div>

CSS z-index 属性从父规则的例子, 子节点不设定层级

如果所有节点都定义了 position:relative, A 节点的 z-index 和 B 节点一样大, 但因为顺序规则, B 节点覆盖在 A 节点前面. 就算 A 的子节点 z-index 值比 B 的子节点大, B 的子节点还是会覆盖在 A 的子节点前面.

<div id="a" style="position:relative;z-index:0;">
	<div id="a-1" style="position:relative;z-index:2;">A-1</div>
</div>

<div id="b" style="position:relative;z-index:0;">
	<div id="b-1" style="position:relative;z-index:1;">B-1</div>
</div>

CSS z-index 属性从父规则的例子, 不可逾越的层级

很多人将 z-index 设得很大, 9999 什么的都出来了, 如果不考虑父节点的影响, 设得再大也没用, 那是无法逾越的层级.

层级树规则

可能你会觉得在 DOM 结构中的兄弟节点会拎出来进行比较并确定层级, 其实不然.

<div id="a" style="position:relative;z-index:2;">
	<div id="a-1" style="position:relative;z-index:0;">A-1</div>
</div>

<div id="b">
	<div id="b-1" style="position:relative;z-index:1;">B-1</div>
</div>

CSS z-index 属性层级树规则的例子

我们认为同时将 position 设为 relative, absolute 或者 fixed, 并且 z-index 经过整数赋值的节点, 会被放置到一个与 DOM 不一样的层级树里面, 并且在层级树中通过对比 z-index 决定显示的层级. 上面的例子如果用层级树来表示的话, 应该如下图所示.

CSS z-index 的层级树

图中虽然 A-1 (z-index:0) 的值比 B-1 (z-index:1) 小, 但因为在层级树里 A (z-index:2) 和 B-1 在一个层级, 而 A 的值比 B-1 大, 根据从父规则, A-1 显示在 B-1 前面.

参与规则 2

前面提到的参与规则认为只要节点的 position 属性为 relative, absolute 或者 fixed, 即可参与层级比较, 其实不准确. 如果所有节点都定义了 position:relative, 并且将 z-index 设为整数值, 根据从父规则, 父节点的层级决定了子节点所在层级.

<div id="a" style="position:relative;z-index:0;">
	<div id="a-1" style="position:relative;z-index:100;">A-1</div>
</div>

<div id="b">
	<div id="b-1" style="position:relative;z-index:0;">
		<div id="b-1-1" style="position:relative;z-index:10;">B-1-1</div>
	</div>
</div>

<div id="c" style="position:relative;z-index:0;">
	<div id="c-1">
		<div id="c-1-1">
			<div id="c-1-1-1" style="position:relative;z-index:1;">C-1-1-1</div>
		</div>
	</div>
</div>

例子中 A, B-1, C-1-1 作为父节点, z-index 的值相同, 根据顺序规则, C-1-1 在 B-1 之前, B-1 在 A 之前; 又根据从父规则, 无论子节点的 z-index 值是什么, C-1-1-1 在 B-1-1 之前, B-1-1 在 A-1 之前.

CSS z-index 属性参与规则 2 的例子, 所有节点参与层级比较

如果我们将所有父节点的 z-index 属性去除, 诡异的事情发生了. IE6 和 IE7 浏览器显示效果不变, 而 W3C 浏览器的子节点不再从父, 而是根据自身的 z-index 确定层级.

<div id="a" style="position:relative;">
	<div id="a-1" style="position:relative;z-index:100;">A-1</div>
</div>

<div id="b">
	<div id="b-1" style="position:relative;">
		<div id="b-1-1" style="position:relative;z-index:10;">B-1-1</div>
	</div>
</div>

<div id="c" style="position:relative;">
	<div id="c-1">
		<div id="c-1-1">
			<div id="c-1-1-1" style="position:relative;z-index:1;">C-1-1-1</div>
		</div>
	</div>
</div>

根据默认值规则, IE6 / IE7 和 W3C 浏览器上的元素存在 z-index 默认值的区别. 我们相信, 仅当 position 设为 relative, absolute 或者 fixed, 并且 z-index 赋整数值时, 节点被放置到层级树; 而 z-index 为默认值时, 只在 document 兄弟节点间比较层级. 在 W3C 浏览器中, A, B-1 和 C-1-1 的 z-index 均为 auto, 不参与层级比较.

CSS z-index 属性参与规则 2 的例子, z-index 为 auto 的节点不参与层级比较

而在 IE6 和 IE7 中, 因为 z-index 的默认值是 0, 所以也参与了层级比较.

CSS z-index 属性参与规则 2 的例子, IE6 和 IE7 中 z-index 默认为 0

设置了 position 而没有 z-index 的节点虽然不参与层级树的比较, 但还会在 DOM 中与兄弟节点进行层级比较.

<div id="a" style="position:relative;">
	<div id="a-1" style="position:relative;z-index:100;">A-1</div>
</div>

<div id="b">
	<div id="b-1">
		<div id="b-1-1" style="position:relative;z-index:10;">B-1-1</div>
	</div>
</div>

<div id="c" style="position:relative;">
	<div id="c-1">
		<div id="c-1-1">
			<div id="c-1-1-1" style="position:relative;z-index:1;">C-1-1-1</div>
		</div>
	</div>
</div>

我们对上个例子改造一下, 将 B-1 的 position 属性删除后, W3C 浏览器显示如下图. 根据定位规则, A 和 C-1-1 会显示在 B-1 的前面; 而根据顺序规则, C-1-1 又显示在 A 前面.

CSS z-index 属性参与规则 2 的例子, position 为 auto 的节点不参与层级树比较, 但仍参与 DOM 兄弟节点间的层级比较, W3C 浏览器

在 IE6 和 IE7 中, 因为 A 和 C-1-1 设置了 position:relative, 而且 z-index 的默认值为 0, 所以也参与层级树比较, 所以有如下效果.

CSS z-index 属性参与规则 2 的例子, position 为 auto 的节点不参与层级树比较, 但仍参与 DOM 兄弟节点间的层级比较, IE6 和 IE7

总结

浏览器节点显示层级是一个费力的活, 今天你觉得 A 区块会永远置顶, 但明天因为需求变动, 突然出现 B 元素需要置顶. 一层一层往上堆砌, 某天回头一看, 发现很多区块交错在一起, 而且他们的值一个比一个大, 根本搞不清头绪. 我觉得在操刀干活之前, 最好先将 position, z-index 和层级的关系搞搞清楚, 以免后患无穷.

另外, 非情非得已, 切勿用 JavaScript 计算 z-index, 并将某个节点的 z-index 设置成所有节点中层级最高.

因为篇幅太长, 本文仅从节点属性角度进行讨论, 没有涉及 select 和 iframe 等特殊页面节点考虑, 如果有机会下次再为大家分享.

[转载]jQuery之CSS 控制 - 逆心 - 博客园

mikel阅读(975)

[转载]jQuery之CSS – 逆心 – 博客园.

1、offset()获取匹配元素在相对浏览器窗口的偏移量  返回一个对象,包括两个属性。left:相对浏览器窗口左边的距离。top:相对浏览器顶部的距离。

$(“#div1”).offset().left;  //返回id为div1相对于浏览器窗口最左边的距离

$(“#div1”).offset().top;  //返回id为div1相对于浏览器窗口最顶部的距离

以下例子展示了,当点击文本框时,在下方显示一个日期div。紧紧贴住上面的文本框。并且不需要调div的css位置,无论上面的文本框位置如 果变化,都能够紧紧且住上面的文本框,记得在前几天搞爱的车轮战报名系统的js模拟下拉列表的时候是调CSS贴住上面的文本框的,这样一旦改变了文本框的 位置,模拟的下拉列表的div的css也要跟住调,其实这是很SB的。下面这个方法好。

复制代码
function showDiv(obj) {
  jQuery("#divShow").css("left", jQuery(obj).offset().left);  //设置#divShow与浏览器的左距离为文本框距离浏览器左边的距离。
  jQuery("#divShow").css("top", jQuery(obj).offset().top + jQuery(obj).outerHeight());  //设置#divShow距离顶部的距离为文本框距离顶部的距离加上自                                                 身高度。
  jQuery("#divShow").show();
}

<input type="text" value="ok" onclick="showDiv(this);" style="margin-left:100px;" />
<div id="divShow" style="display:none;position:absolute;">2010-03-22</div>
复制代码

2、position()获取匹配元素在相对父元素的偏移量  返回一个对象,包括两个属性。left:相对父元素最左边的距离。top:相对父元素最右边的距离。只对可见元素有效。

复制代码
<head>
    <title></title>
    <script src="jquery-1.7.1.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            $("#btn1").click(function () {
                var left = $("#div2").position().left;  //21.11111
                var top = $("#div2").position().top;    //33.33333
                alert("距离父元素顶部的距离是:" + left + "; 距离父元素左边的距离是:" + top);
            })
        })
    </script>
</head>
<body>
    <div id="div1" style="width:200px;height:400px;border:10px solid #000; padding:20px 20px; margin:30px 30px; position:relative;">
        <div id="div2" style="width:100px;height:200px; background-color:Green; position:absolute; left:22px; top:34px;">我是一个div元素</div>
        <input id="btn1" type="button" value="查看" />
    </div>
</body>
复制代码

以上代码相当于JavaScript中的:

        function fun1() { var left = document.getElementById("div2").offsetLeft;  //21 var top = document.getElementById("div2").offsetTop;   //33 alert("距离父元素顶部的距离是:" + left + "; 距离父元素左边的距离是:" + top); }

JavaScriptJQuery不同一点的地方就是它输出的是整数21,33,但是跟CSS设置的却差了一个像素,JQuery还能够用Math里的方法来还原,但是JavaScript就不知道怎么搞了。

 

3、scrollTop()    获取匹配元素距离滚动条顶部的距离,说白了就是边框的最顶部与当前显示出来的最顶部的距离。

scrollTop(val)  设置匹配元素距离滚动条顶部的距离

该属性常常配合scroll()事件一起使用,能够实现元素随滚动条的滚动而滚动,类似于漂浮广告效果。

$(this).scroll(function(){

$(“#div1”).css(“top”, $(document).scrollTop());  //注意id为div1的div要设置为绝对定位。如果是底部漂浮,只需要$(document).scrollTop()加上相应的垂直距离就可以了。

  })

4、scrollLeft()     获取匹配元素距离滚动条顶部的距离,说白了就是边框的最左边与当前显示出来的最左边的距离。

scrollLeft(val)  设置匹配元素距离滚动条顶部的距离

复制代码
<head>
    <title></title>
    <script src="jquery-1.7.1.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            $("#btn1").click(function () {
                var scrollTop = $("#div1").scrollTop();
                var scrollLeft = $("#div1").scrollLeft();
                alert("显示的最顶部距离真正的顶部的距离是:" + scrollTop + "; 显示的最左边距离真正的左边的距离是:" + scrollLeft);
            })
        })
    </script>
</head>
<body>
    <div id="div1" style="width:200px;height:400px;border:10px solid #000; padding:20px 20px; margin:30px 30px; overflow:scroll;">
        <div style="width:400px;height:800px;/*撑出滚动条*/">我是一个div元素</div>
        <input id="btn1" type="button" value="查看" />
    </div>
</body>
复制代码

5、height()    获取匹配元素的高度值  //不包括padding,不包括边框 val可以是字符串”300px”、也可以是数字300,还可以是一个函数,返回值作为参数

height(val)    设置匹配元素的高度值

6、 width()     获取匹配元素的宽度值  //不包括padding,不包括边框

width(val)     设置匹配元素的宽度值

复制代码
<head>
    <title></title>
    <script src="jquery-1.7.1.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            $("#btn1").click(function () {
                var Width = $("#div1").width();   //200 css的width属性,不包括内边距、边框和外边距
                var Height = $("#div1").height(); //400 css的height属性,不包括内边距、边框和外边距
                alert("div1的宽度是:" + Width + "; div1的高度是:" + Height);
            })
        })
    </script>
</head>
<body>
    <div id="div1" style="width:200px;height:400px;border:10px solid #000; padding:20px 20px; margin:30px 30px;">
        我是一个div元素
        <input id="btn1" type="button" value="查看" />
    </div>
</body>
复制代码

7、innerHeight()    获取匹配元素的高度值   //包括padding但不包括border

innerHenght(val)  设置匹配元素的高度值

8、innerWidth()     获取匹配元素的宽度值  //包括padding但不包括border

innerWidth(val)     设置匹配元素的宽度值

复制代码
<head>
    <title></title>
    <script src="jquery-1.7.1.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            $("#btn1").click(function () {
                var innerWidth = $("#div1").innerWidth();   //240 包括边框和内边距
                var innerHeight = $("#div1").innerHeight(); //440 包括边框和内边距
                alert("div1的宽度是:" + innerWidth + "; div1的高度是:" + innerHeight);
            })
        })
    </script>
</head>
<body>
    <div id="div1" style="width:200px;height:400px;border:10px solid #000; padding:20px 20px; margin:30px 30px;">
        我是一个div元素
        <input id="btn1" type="button" value="查看" />
    </div>
</body>
复制代码

以上的主JQuery相当于代码(javascript版):

function fun1() {
  var innerWidth = document.getElementById("div1").clientWidth;
  var innerHeight = document.getElementById("div1").clientHeight;
  alert("div1的宽度是:" + innerWidth + "div1的高度是:" + innerHeight);
}

9、 outerHeight()    获取元素的高度值  //包括padding和border

outerHeight(val)    设置元素的高度值

还可以接受一个参数,该参数代表是否计算外边距,如果为true则表示计算外边距。

10、outerWidth()    获取匹配元素的宽度值  //(包括padding和border)

outerWidth()     设置匹配元素的宽度值

还可以接受一个参数,该参数代表是否计算外边距,如果为true则表示计算外边距。

复制代码
<head>
    <title></title>
    <script src="jquery-1.7.1.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            $("#btn1").click(function () {
                var outerWidth = $("#div1").outerWidth();   //260包括边框和内边距
                var outerHeight = $("#div1").outerHeight(); //460 包括边框和内边距
                alert("div1的宽度是:" + outerWidth + "; div1的高度是:" + outerHeight);

                var outerWidth1 = $("#div1").outerWidth(true);   //320 包括边框、外边距和内边距(也就是元素实际占用的大小)
                var outerHeight1 = $("#div1").outerHeight(true); //520 包括边框、外边距和内边距(也就是元素实际占用的大小)
                alert("div1的宽度是:" + outerWidth1 + "; div1的高度是:" + outerHeight1);
            })
        })
    </script>
</head>
<body>
    <div id="div1" style="width:200px;height:400px;border:10px solid #000; padding:20px 20px; margin:30px 30px;">
        我是一个div元素
        <input id="btn1" type="button" value="查看" />
    </div>
</body>
复制代码

jQuery的参数不为真的情况下,以上jQuery的主代码相当于:

        function fun1() {
            var outerWidth = document.getElementById("div1").offsetWidth;
            var outerHeight = document.getElementById("div1").offsetHeight;
            alert("div1的宽度是:" + outerWidth + "; div1的高度是:" + outerHeight);
        }

如果参数为真的情况下,就相当于javascript:

复制代码
        function fun1() {
            var div1 = document.getElementById("div1");
            var outerWidth1 = div1.offsetWidth + parseInt(div1.style.marginLeft) + parseInt(div1.style.marginRight);
            var outerHeight1 = div1.offsetHeight + parseInt(div1.style.marginTop) + parseInt(div1.style.marginBottom);
            alert("div1的宽度是:" + outerWidth1 + "; div1的高度是:" + outerHeight1);
        }
复制代码

此处可能写的不是很好,应该有更好的办法,本人javascript还在初学当中。

[转载]仿 Google Reader 随滚动条滚动加载页面效果 - wupingzsf - 博客园

mikel阅读(1009)

转载仿 Google Reader 随滚动条滚动加载页面效果 – wupingzsf – 博客园.

Google阅读器上 有一个AJAXscollLoad效 果很不错,就是阅读项目时不需要翻页,浏览器滚动条往下拉到一定位置时自动加载新的一批项目进来,一直到所有项目加载完为止。对于我来说 再好不过了,因为我很不喜欢翻页,尤其是输入页码再定位到页。要知道,数据量增加很频繁时,要通过定位页码来找到目标数据似乎并没有什么意义。我觉得用户 体验成熟的WEB应用程序应当引导用户使用TAG或搜索等功能来找到目标数据。至于浏览数据,尤其是浏览最新的数据,利用浏览器滚动条来加载,是很好的尝 试……

我试着用JQuery来实现这个功能。先要得到滚动条的总长属性值:scrollHeight,还有滚动条当前位置属性 值:scrollTop。然后通过计算,若当前值位于总长值三分之二时加载新数据。假设DOM上有一个元素为<div id=”mypage”></div>,该元素overflow样式为scroll,也就是元素中的内容溢出元素指定高度时启用滚动 条。利用JQuery的load方法为元素加载一个已经存在的文件,我假设它是table.html。这个文件的内容可以是足以使浏览器滚屏的一张数据 表。

<script type=”text/JavaScript” src=”jquery.js“>//加载jQuery库</script>
<script type=”text/JavaScript”>

var hght=0;//初始化滚动条总长
var top=0;//初始化滚动条的当前位置
$(document).ready(function(){//DOM的onload事件
$(”#mypage”).load(”table.html”);//table.html的内容被加载到mypage元素

$(”#mypage”).scroll( function() {//定义滚动条位置改变时触发的事件。
hght=this.scrollHeight;//得到滚动条总长,赋给hght变量
top=this.scrollTop;//得到滚动条当前值,赋给top变量
});
});

setInterval(”cando();”,2000);//每隔2秒钟调用一次cando函数来判断当前滚动条位置。

function cando(){
if(top>parseInt(hght/3)*2)//判断滚动条当前位置是否超过总长的2/3,parseInt为取整函数
show();//如果是,调用show函数加载内容。
}

function show(){
$.get(”table.html”, function(data){//利用jQuery的get方法得到table.html内容
$(”#mypage”).append(data);//用append方法追加内容到mypage元素。
hght=0;//恢复滚动条总长,因为$(”#mypage”).scroll事件一触发,又会得到新值,不恢复的话可能会造成判断错误而再次加载……
top=0;//原因同上。
});
}

</script>
<div id=”mypage”></div>

为 什么要隔2秒钟调用一次判断呢?因为只要$(”#mypage”).scroll事件一被触发,就会影hght和top值,这个值可能总是满足cando 函数的判断,也就是top值总是位于hght的三分之二。因此可能会多次加载造成服务器负担加重。而每加载一次后把hght和top值赋0,也是这个原 因。

这段代码的效果就是只要元素mypage的滚动条位置位于滚动条总长的三分之二时,追加table.html的内容到元素mypage 中去。当然这样运行是无休止地加载下去。在真正的AJAX运用中,table.html可以换成asp或php脚本,接收get或post参数来进行处 理,然后返回有意义的数据。jQuery的get方法可以设置get方式的参数数据,比如:

$.get(”test.php”, { name: “boho”, id: “1″ } );

相当于http://hostlocal/test.php?name=boho&id=1这种形式的http访问。然后通过get方法的回调函数来获取test.php输出的内容:

$.get(”test.php”, {name:”boho”,id:”1″},function(data){
alert(”Data Loaded: ” + data);
});

[转载]15个非常不错的PHP 组件、工具和教程 - 好收藏 - 博客园

mikel阅读(980)

[转载]15个非常不错的PHP 组件、工具和教程 – 好收藏 – 博客园.

原文地址:http://www.goodfav.com/zh/15-useful-php-components-techniques-and-tutorials-384.html

PHP是一种用于创建动态Web应用程序最流行的开发语言。一些最有影响力和非常流行的开源CMS /出版平台,如WordPress,Joomla和Drupal的都是用PHP开发的。 PHP有很多有点,它快速,稳定,安全,易于使用并且开放源码。熟悉PHP的Web开发人员要想有一个良好的开发效率,一些工具箱是必不可少的。

在下面的这篇文章中,我整理了一份有用的PHP组件,工具和教程,有了它们,PHP开发人员的生活应该会轻松许多。

PHP 集成环境

XAMPP

PHP 框架

cakephp

CodeIgniter

pear.php.net

Symfony

PHP 绘图工具

pChart – a PHP class to build charts

Creating a Bar Graph using CSS and PHP

Libchart

JpGraph

PHP 教程、技巧和有用的组件

Query – PHP & Ajax with the Twitter API

Sanitize and Validate Data with PHP Filters

Backup Your Database into an XML File Using PHP

10 PHP code snippets for working with strings

Caching Dynamic PHP pages easily

3 ways to compress CSS files using PHP

How to Build a Login System for a Simple Website

PHP components: Autosuggest

 

详情请阅读:http://www.goodfav.com/zh/15-useful-php-components-techniques-and-tutorials-384.html

[转载]ASP.NET WebForm(MVC)下实现消息推送(提供简单Demo下载) - Good_Luck - 博客园

mikel阅读(969)

[转载]ASP.NET WebForm(MVC)下实现消息推送(提供简单Demo下载) – Good_Luck – 博客园.

由于项目需要,笔者最近需要实现Web客户端之间的消息的即时推送功能。

功能描述如下:

假设A,B,C用户登陆,内存记录下已登录的用户的信息,这时A在所在的客户端(SendInfo.aspx)页面向B发消息,则在B所在客户端页面(SendInfo.aspx)将弹出消息框。

关键点有两个:

1.保证客户端和服务端的连接

2.保证服务端能够向客户端广播消息

笔者是第一次做这样的实现,所以Google了一些资料,了解到可使用Comet,ajax轮询,WebSocket等技术实现,由于时间关系,发现有些技术不是很容易理解,这里做了一个简单Demo.希望能够达到抛砖引玉的作用,与大家分享,共同提高。

笔者做了两个框架下的实现,ASP.NET Web Form和ASP.NET MVC 下的尝试。

ASP.NET Web Form版:

项目组织结构

AsyncHandler.cs

View Code

复制代码
using System; using System.Collections.Generic; using System.Web; using System.Threading; namespace CometSample { public class WebIMAsyncHandler : IHttpAsyncHandler { #region IHttpAsyncHandler 成员

        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { string _UID = context.Request.Params["uid"]; WebIMClientAsyncResult _AsyncResult = new WebIMClientAsyncResult(context, cb, extraData); string _Content = context.Request.Params["content"]; string _Action = context.Request.Params["action"]; if (_Action == "login") { _UID = context.Request.Params["uid"]; _AsyncResult.LoginID = _UID; WebIMMessageHandler.Instance().Login(_UID, _AsyncResult); } else if (_Action == "logout") { _AsyncResult.LoginID = _UID; WebIMMessageHandler.Instance().Logout(_UID, _AsyncResult); } else if (_Action == "connect") { _AsyncResult.LoginID = _UID; WebIMMessageHandler.Instance().Connect(_AsyncResult); } else if (_Action == "getuserlist") { _AsyncResult.LoginID = _UID; WebIMMessageHandler.Instance().GetUserList(_AsyncResult); } //增加消息发送
            else if (_Action == "sendmsg") { _AsyncResult.LoginID = _UID; //WebIMMessageHandler.Instance().GetUserList(_AsyncResult); //调用
 WebIMMessageHandler.Instance().AddMessage(_Content, _AsyncResult); } //调用 //WebIMMessageHandler.Instance().AddMessage(_Content, _AsyncResult);
            return _AsyncResult; } public void EndProcessRequest(IAsyncResult result) { } #endregion

        #region IHttpHandler 成员

        public bool IsReusable { get { return false; ; } } public void ProcessRequest(HttpContext context) { throw new NotImplementedException(); } #endregion } public class WebIMClientAsyncResult : IAsyncResult { bool m_IsCompleted = false; private HttpContext m_Context; private AsyncCallback m_Callback; private object m_ExtraData; private string m_Content; private string m_LoginID = string.Empty; public WebIMClientAsyncResult(HttpContext p_Context, AsyncCallback p_Callback, object p_ExtraData) { this.m_Context = p_Context; this.m_Callback = p_Callback; this.m_ExtraData = p_ExtraData; } /// <summary>
        /// 用户编号 /// </summary>
        public string LoginID { get { return m_LoginID; } set { m_LoginID = value; } } /// <summary>
        /// 发送消息的内容,暂时未使用到 /// </summary>
        public string Content { get { return m_Content; } set { m_Content = value; } } #region IAsyncResult 成员

        public object AsyncState { get { return null; } } public WaitHandle AsyncWaitHandle { get { return null; } } public bool CompletedSynchronously { get { return false; } } public bool IsCompleted { get { return m_IsCompleted; } } #endregion

        /// <summary>
        /// 向客户端响应消息 /// </summary>
        /// <param name="data"></param>
        public void Send(object data) { try { m_Context.Response.Write(this.Content); if (m_Callback != null) { m_Callback(this); } } catch { } finally { m_IsCompleted = true; } } } }
复制代码

MessageHandler.cs

View Code

复制代码
using System; using System.Collections; using System.Collections.Generic; using System.Web; using System.Text; namespace CometSample { public class WebIMMessageHandler { private static readonly WebIMMessageHandler m_Instance = new WebIMMessageHandler(); //记录所有请求的客户端
        List<WebIMClientAsyncResult> m_Clients = new List<WebIMClientAsyncResult>(); //Dictionary<string,WebIMClientAsyncResult> m_Clients=new Dictionary<string,WebIMClientAsyncResult>();
        string m_Users = string.Empty; public WebIMMessageHandler() { } public static WebIMMessageHandler Instance() { return m_Instance; } /// <summary>
        /// 登录 /// </summary>
        /// <param name="p_LoginID"></param>
        /// <param name="p_ClientAsyncResult"></param>
        public void Login(string p_LoginID, WebIMClientAsyncResult p_ClientAsyncResult) { bool _Logined = false; foreach (WebIMClientAsyncResult _Item in m_Clients) { if (_Item.LoginID == p_LoginID) { p_ClientAsyncResult.Content = "你已登录"; _Logined = true; break; } } if (!_Logined) { //m_Clients.Add(p_ClientAsyncResult);
                p_ClientAsyncResult.Content = "OK"; } p_ClientAsyncResult.Send(null); } private string GetUsers() { /* string _Users = string.Empty; foreach (WebIMClientAsyncResult _Item in m_Clients) { _Users += _Item.LoginID + ","; } return _Users; */

            var sbUsers = new StringBuilder(); sbUsers.Append("Users:"); foreach (WebIMClientAsyncResult _Item in m_Clients) { sbUsers.Append(_Item.LoginID); sbUsers.Append(","); } return sbUsers.ToString(); } public void Logout(string p_LoginID, WebIMClientAsyncResult p_ClientAsyncResult) { foreach (WebIMClientAsyncResult _Item in m_Clients) { if (_Item.LoginID == p_LoginID) { m_Clients.Remove(_Item); break; } } p_ClientAsyncResult.Content = "退出成功"; p_ClientAsyncResult.Send(null); //UpdateUserList();

            string _Users = GetUsers(); foreach (WebIMClientAsyncResult _Item in m_Clients) { _Item.Content = _Users; _Item.Send(null); } m_Clients.Clear(); } public void GetUserList(WebIMClientAsyncResult p_ClientAsyncResult) { Connect(p_ClientAsyncResult); string _Users = GetUsers(); foreach (WebIMClientAsyncResult _Item in m_Clients) { _Item.Content = _Users; _Item.Send(null); } m_Clients.Clear(); } public void Connect(WebIMClientAsyncResult p_Client) { bool _Exists = false; foreach (WebIMClientAsyncResult _Item in m_Clients) { if (_Item.LoginID == p_Client.LoginID) { _Exists = true; break; } } if (!_Exists) { m_Clients.Add(p_Client); } } /* public void UpdateUserList() { string _Users = GetUsers(); foreach (WebIMClientAsyncResult result in m_Clients) { result.Content = m_Users; result.Send(null); } m_Clients.Clear(); }*/

        /// <summary>
        /// 广播消息 /// </summary>
        /// <param name="p_Message"></param>
        /// <param name="p_AsyncResult"></param>
        public void AddMessage(string p_Message, WebIMClientAsyncResult p_ClientAsyncResult) { //保持连接
            if (p_Message == "-1") { m_Clients.Add(p_ClientAsyncResult); } else { //将当前请求的内容输出到客户端
                p_ClientAsyncResult.Content = p_Message; p_ClientAsyncResult.Send(null); //否则将遍历所有已缓存的client,并将当前内容输出到客户端
                foreach (WebIMClientAsyncResult result in m_Clients) { //发送给所有已经登录用户
                    var strMsg = string.Format("{0}{1}{2}{3}{4}",p_ClientAsyncResult.LoginID,"发送给",result.LoginID,"的消息:",p_Message); //result.Content = p_Message;
                    result.Content = strMsg; result.Send(null); //发送给指定用户
                    /* if (string.Equals(result.LoginID, "ZhangShan") && !string.Equals(p_ClientAsyncResult.LoginID, "ZhangShan")) { var strMsg = string.Format("{0}{1}{2}{3}{4}{5}","Msgs:", p_ClientAsyncResult.LoginID, "发送给", result.LoginID, "的消息:", p_Message); //result.Content = p_Message; result.Content = strMsg; result.Send(null); } */ } //清空所有缓存
 m_Clients.Clear(); } } } }
复制代码

Login.js

View Code

复制代码
/// <reference path="jquery-1.3.2.min.js" >
$(document).ready(function () { //登录,登录成功后,获取在线用户列表,
    function login() { //增加页面跳转
        var strUrl = '/SendInfo.aspx?strUid=' + $("#txtLoginID").val(); window.open(strUrl); } $("#btnLogin").click(function () { if ($("#txtLoginID").val() == '') alert('空'); login(); }); })
复制代码

WebIM.js

View Code

复制代码
/// <reference path="jquery-1.3.2.min.js" > //$(document).ready(function () {

    //状态,代表是否登录
    //var _logined = false;

    //登录,登录成功后,获取在线用户列表,
    function login() { //$.post("comet_broadcast.asyn", { action: 'login', uid: $("#txtLoginID").val() },
        $.post("comet_broadcast.asyn", { action: 'login', uid: strUid }, function (data, status) { if (data == "OK") { _logined = true; getuserlist(); //增加页面跳转
                /*var strUrl = '\SendInfo.aspx?strUid=' + $("#txtLoginID").val(); window.open(strUrl); */ } else { alert(data); } }); } //获取在线用户列表,获取列表后,进入消息等待
    function getuserlist() { //$.post("comet_broadcast.asyn", { action: 'getuserlist', uid: $("#txtLoginID").val() },
        $.post("comet_broadcast.asyn", { action: 'getuserlist', uid: strUid }, function (data, status) { //alert('getuserlist' + data);
            var result = $("#divResult"); result.html(result.html() + "<br/>" + "用户列表:" + data); wait(); }); } //退出
    function logout() { //$.post("comet_broadcast.asyn", { action: 'logout', uid: $("#txtLoginID").val() },
        $.post("comet_broadcast.asyn", { action: 'logout', uid: strUid }, function (data, status) { _logined = false; alert(data); } ); } //消息等待,接收到消息后显示,发起下一次的消息等待
    function wait() { //$.post("comet_broadcast.asyn", { action: 'connect', uid: $("#txtLoginID").val() },
        $.post("comet_broadcast.asyn", { action: 'connect', uid: strUid }, function (data, status) { /* var result = $("#divResult"); result.html(result.html() + "<br/>" + "用户列表:" + data); */

               //2.窗口
               new Ext.ux.ToastWindow({ title: '提示窗口', html: data, iconCls: 'error' }).show(document); //服务器返回消息,再次建立连接
               if (_logined) { wait(); } }, "html"); } /*********** *********************消息发送部分*************************** ************/

    function send() { //$.post("comet_broadcast.asyn", { action: 'sendmsg', uid: $("#txtLoginID").val(), content: $("#content").val() },
        $.post("comet_broadcast.asyn", { action: 'sendmsg', uid: strUid, content: $("#content").val() }, function (data, status) { /* var result = $("#divResult"); result.html(result.html() + "<br/>" + "已发消息:" + data); */

            //发送方页面提示
            //潜规则:暂时不处理
            /* //2.窗口 new Ext.ux.ToastWindow({ title: '提示窗口', html: data, iconCls: 'error' }).show(document); */ }, "html" ); //向comet_broadcast.asyn发送请求,消息体为文本框content中的内容,请求接收类为AsnyHandler
        //$.post("comet_broadcast.asyn", { content: $("#content").val() });

        //清空内容
        $("#content").val(""); }; /** * 获取字符串中某个特殊字符首次出现的位置之前的子串 */
    function GetSubStrBySpecChar(strConnStr,strSplit){ var arrStr = strConnStr.split(strSplit); var strSubStr = arrStr[0]; return strSubStr; }
复制代码

Default.aspx

View Code

复制代码
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CometSample._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>

    <script src="Scripts/jquery-1.3.2.min.js" type="text/javascript"></script>
    <script src="Scripts/Login.js" type="text/javascript"></script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label ID="Label1" runat="server" Text="帐号"></asp:Label>
        <input id="txtLoginID" type="text" />
        <input id="btnLogin" type="button" value="Login" />
        <br />
        <div id="divUserList">
        </div>
    </div>
    </form>
</body>
</html>
复制代码

SendInfo.aspx

View Code

复制代码
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="SendInfo.aspx.cs" Inherits="CometSample.SendInfo" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>SendInfo</title>
    <script src="Scripts/jquery-1.3.2.min.js" type="text/javascript"></script>
    <script src="Scripts/WebIM.js" type="text/javascript"></script>

    <link href="Scripts/ext-3.4.0/resources/css/ext-all.css" rel="stylesheet" type="text/css" />
    <script src="Scripts/ext-3.4.0/adapter/ext/ext-base.js" type="text/javascript"></script>
    <script src="Scripts/ext-3.4.0/ext-all.js" type="text/javascript"></script>
    <script src="Scripts/ToastWindow.js" type="text/javascript"></script>

    <script language="javascript" type="text/javascript">

        var strUid = "<%=strUid %>"; $(document).ready(function () { //状态,代表是否登录
            var _logined = false; //alert(strUid);
            //getuserlist_send();
 login(); $("#btnSend").click(function () { send(); }) $("#content").keypress(function (e) { var keyCode = null; if (e.which) keyCode = e.which; else if (e.keyCode) keyCode = e.keyCode; if (keyCode == 13) { send(); return false; } return true; }); }) </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <div id="divUserList">
        </div>
        <br /> 广播内容: <input type="text" id="content" /><br /> 消息记录: <div id="divResult">
        </div>
        <input type="button" id="btnSend" value="广播" />
    </div>
    <div>
        <input id="btnLogout" type="button" value="注销" onclick="logout();"/></div>
    </form>
</body>
</html>
复制代码

最后还需要关注的是配置文件中的路径

在web.config 文件的system.web之间加上

<httpHandlers>
            <add path="comet_broadcast.asyn" type="CometSample.WebIMAsyncHandler" verb="POST,GET"/>
        </httpHandlers>

 

好了运行程序,

效果如下:

登陆之后,跳转到sendinfo页面

 

笔者打开连个浏览器,模拟两个客户端登陆,并且模拟广播消息(能够广播,那么向指定客户端发消息也就很容易了)

我们可以看到在页面右下角,有消息弹出

在.NET WebForm下笔者实现了客户端之间即时消息的推送,但是在.NET MVC 2 下遇到了一些问题,因为mvc框架下对 ,NET WebForm中某些东西不支持。

[转载]ASP.NET MVC中加载WebForms用户控件(.ascx) - dudu - 博客园

mikel阅读(989)

[转载]ASP.NET MVC中加载WebForms用户控件(.ascx) – dudu – 博客园.

问题背景

博客园博客中的日历用的是ASP.NET WebForms的日历控件(System.Web.UI.WebControls.Calendar),它会为“上一月”、“下一月”的链接生成”__doPostBack()”的js调用,如下图:

目前发现它会带来两个问题:

1. 不支持IE10;

2. 某些电脑不允许执行__doPostBack。

问题提炼

前提:

  1. 我们想以最低的成本解决这个问题,也就是对当前代码尽可能少的改动。所以要尽可能重用现有的日历控件代码。
  2. 日历改为Ajax加载,点击“上一月”、“下一月”时Ajax更新日历内容。
  3. ASP.NET MVC处理Ajax请求。

要解决的问题:

如何在ASP.NET MVC Controller中加载包含WebForms日历控件的用户控件(.ascx),并得到其输出的字符串,然后将__doPostBack的代码替换为ajax调用代码。

核心问题:

如何在ASP.NET MVC Controller中得到用户控件(.ascx)输出的字符串。

解决方法

先看代码

复制代码
public ActionResult Calendar() { var page = new Page(); var form = new HtmlForm(); var calendar = page.LoadControl("~/Controls/CNBlogsCalendar.ascx"); form.Controls.Add(calendar); page.Controls.Add(form); using (var sw = new StringWriter()) { System.Web.HttpContext.Current.Server.Execute(page, sw, true); return Content(sw.ToString()); } }
复制代码

代码很简单,但得到这个代码花了今天一上午时间。

代码说明:

  • 必须要new Page(),只有Page才能LoadControl。
  • 必须要new HtmlForm(),因为日历控件必要要放在<form runat=”server”>之间。
  • 关键功臣是HttpContext.Current.Server.Execute,动态加载控件并输出字符串全靠它。这个功臣是在这篇文章中找到的(感谢Sam Mueller)。之前我用过的方法(继续不走寻常路:ASP.NET MVC中使用Web Forms用户控件)不仅麻烦,而且在这个场景下会有问题。

代码运行结果:

完整代码下载:

http://files.cnblogs.com/dudu/CNBlogsDemoMvcAscx.rar