2011年5月28日
上回我们已经创建了一个简单的Asp.Net MVC3网站并成功的连接了MongoDB。但只实现了创建和显示的功能。本回实现了完整的增删改查。
创建的部分,上次的代码中存在一些错误,造成了每个属性都会被创建为单独的一条记录,这并不是我们期待的结果。更改如下:
\Controllers\HomeController.cs
1: [HttpPost]
2: public ActionResult Create(FormCollection collection)
3: { 4: try
5: { 6: var db = GetDB();
7: var doc = new BsonDocument();
8:
9: foreach (var key in collection.AllKeys)
10: { 11: doc.Add(new BsonElement(key, collection[key]));
12: }
13:
14: db["testTable"].Insert(doc);
15: return RedirectToAction("Index"); 16: }
17: catch
18: { 19: return View();
20: }
21: }
其实这样写并不安全,会把所有从页面POST过来的内容都作为属性创建并保存,但是简单啦。我们注意力主要集中在MongoDB的使用,安全性并不在考虑范围内。
显示的部分,也做了相应的修改,使其更符合Asp.Net MVC3 Razor引擎的规范:
\Controllers\HomeController.cs
1: public ActionResult Index()
2: { 3: var testTable = GetDB()["testTable"].FindAll();
4: return View(testTable);
5: }
\Views\Home\Index.cshtml
1: @{ 2: ViewBag.Title = "Index";
3: Layout = "~/Views/Shared/_Layout.cshtml";
4: }
5: <h2>
6: Index</h2>
7: <ul>
8: @foreach (var testData in Model)
9: { 10: <li>
11: @{ 12: var id = string.Empty;
13: foreach (var property in testData.Names)
14: { 15: if (property == "_id")
16: { 17: id = testData[property].ToString();
18: }
19: else
20: { 21: @string.Format("{0}:{1}", property, testData[property]);<br /> 22: }
23: }
24: <a href="/Home/Delete/@id">Delete</a> <a href="/Home/Edit/@id">Edit</a> <a href="/Home/Details/@id">Details</a>
25: }
26: </li>
27: }
28: </ul>
29: <a href="/Home/Create">Create New</a>
总体来说,Razor引擎的代码写起来还是流畅+愉快。有一个简单的校技巧,想要链接目录从网站根目录开始的话,要用“/”开头。
显示效果如下:

接下来我们就分别来实现删除,编辑和查看功能。
Details链接:
\Controllers\HomeController.cs
1: public ActionResult Details(string id)
2: { 3: try
4: { 5: var db = GetDB();
6: var doc = db["testTable"].Find(Query.EQ("_id", new BsonObjectId(id))).First(); 7: return View(doc);
8: }
9: catch
10: { 11: return View();
12: }
13: }
Edit页面:
\Controllers\HomeController.cs
1: public ActionResult Edit(string id)
2: { 3: var db = GetDB();
4: var doc = db["testTable"].Find(Query.EQ("_id", new BsonObjectId(id))).First(); 5: return View(doc);
6: }
\Home\Edit.cshtml
1: @{ 2: ViewBag.Title = "Edit";
3: Layout = "~/Views/Shared/_Layout.cshtml";
4: }
5: <h2>
6: Edit</h2>
7: <form method="post" action="/Home/Edit/@Model["_id"].ToString()">
8: Name:
9: <input name="name" type="text" value="@Model["name"]" /><br />
10: Age:
11: <input name="age" type="text" value="@Model["age"]" /><br />
12: Gender:
13: <input name="gender" type="text" value="@Model["gender"]" /><br />
14: Married:
15: <input name="married" type="text" value="@Model["married"]" /><br />
16: <input type="submit" value="Update" />
17: <a href="/Home/Index">Cancel</a>
18: </form>
\Controllers\HomeController.cs
1: [HttpPost]
2: public ActionResult Edit(string id, FormCollection collection)
3: { 4: try
5: { 6: var db = GetDB();
7: var update = new UpdateBuilder();
8: foreach (string key in collection.Keys)
9: { 10: update.Set(key, collection[key]);
11: }
12:
13: db["testTable"].Update(Query.EQ("_id", new BsonObjectId(id)), update); 14: return RedirectToAction("Index"); 15: }
16: catch
17: { 18: return View();
19: }
20: }
删除功能:
\Controllers\HomeController.cs
1: public ActionResult Delete(string id)
2: { 3: try
4: { 5: var db = GetDB();
6: db["testTable"].Remove(Query.EQ("_id", new BsonObjectId(id))); 7: return RedirectToAction("Index"); 8: }
9: catch
10: { 11: return View();
12: }
13: }
Global.asax.cx也可以改回Index开始:
1: routes.MapRoute(
2: "Default", // Route name
3: "{controller}/{action}/{id}", // URL with parameters 4: new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults 5: );
搞定。
不用设计数据库好爽啊。。想怎么插就怎么插。。。无Schema好爽啊!Razor基本很人性化,缺点很少。
不过感觉MongoDB关方的Connector功力比较弱,不支持Linq也就罢了,implicit也不实现,无法隐式转化格式,写起来很是别扭,不够流畅。
接下来准备研究一下MongoDB的高级功能,敬请期待。
完整代码下载: http://files.cnblogs.com/pandora/MvcApplication1_CRUD.zip
2011年5月26日
晚上闲来无事,研究了一下最近很火的MongoDB。感受只有一个:“自由”。
闲话略过,先让MongoDB在Win7 64下跑起来:
1. MongoDB主页
http://www.mongodb.org/
2. 下载
http://downloads.mongodb.org/win32/mongodb-win32-x86_64-1.8.1.zip
3. 解压
e.g. H:\mongodb-win32-x86_64-1.8.1
4. 准备目录
e.g. H:\mongo\data --保存数据库文件 H:\mongo\logs --保存log,在此目录下手动创建一个log.txt
5. 安装
Win键,输入cmd,右键-Run as Administrator
cd H:\mongodb-win32-x86_64-1.8.1\bin
H:
mongod --bind_ip 127.0.0.1 --logpath H:\mongo\logs\log.txt --logappend --dbpath H:\mongo\data --directoryperdb –install
net start “MongoDB”
成功。
6. 测试安装
在之前的CMD窗口中输入:mongo
应该能无异常进入query界面
输入一个3+3,应该能得到6
关掉cmd,这玩意儿没用了。
7. 下载.Net Connector
https://github.com/mongodb/mongo-csharp-driver/archives/master
解压
打开CSharpDriverSetup-2010.sln
Build
失败
删除DriverSetup中对CHM的引用
重新编译,成功
右键DriverSetup – Install
下一步下一步下一步。。。
8. 创建Asp.Net MVC3 Empty project
若无,请自行安装MVC3的TOOL
9. Add ConnectionString in web.config
<connectionStrings>
<add name="MongoDB" connectionString="mongodb://localhost/test"/>
</connectionStrings>
10. Add Controller
Controller with empty read/write actions
11. Access to the DB
private MongoDatabase GetDB()
{
return MongoDatabase.Create(ConfigurationManager.ConnectionStrings["MongoDB"].ConnectionString);
}
12. Create Action [HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
var db = GetDB();
foreach (var key in collection.AllKeys)
{
db["testTable"].Insert(new MongoDB.Bson.BsonDocument{
{key, collection[key]}
});
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
13. Index Action
public ActionResult Index()
{
try
{
var db = GetDB();
var testTable = db["testTable"].FindAll();
var result = new StringBuilder();
foreach (var testData in testTable)
{
foreach (var property in testData.Names)
{
result.AppendFormat("{0}:{1} ", property, testData[property]);
}
result.Append("<br />");
}
return Content(result.ToString());
}
catch
{
return View();
}
}
14. Create Views under Views/Home/
Create.cshtml:
@{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>
<form method="post" action="/Home/Create">
Name:
<input name="name" type="text" /><br />
Age:
<input name="age" type="text" /><br />
Gender:
<input name="gender" type="text" /><br />
Married:
<input name="married" type="text" /><br />
<input type="submit" value="Add" />
</form>
Index.cshtml
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<a href="Create">Create New</a>
15. Modify Global.asax.cs
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Create", id = UrlParameter.Optional } // Parameter defaults
);
11. Build & Run

Press Add

All Done. Happy and enjoy.
补上项目下载:
http://files.cnblogs.com/pandora/MvcApplication1.zip
2010年12月27日
WebGL是一个附加的渲染上下文(context),支持HTML5的canvas对象。这个上下文允许通过一种与OpenGL ES 2.0 API非常相似的API来进行图像渲染。
创建WebGL上下文
通过使用canvas的getContext(“experimental-webgl”)方法来获取一个WebGLRenderingContext。
创建Shaders
Shaders是用来将图形信息(shape data)转换为屏幕上的像素。当使用GLSL这种shader格式时,我们会用到两种不同的shaders。
Vertex shader 使用在被渲染的三角形的每个顶点(corner)上。这个shader会转换点信息,传入贴图对其信息并且使用每个三角形的normals来计算光照。GLSL提供给用户一个特殊的变量gl_Position来存储经过转换的顶点信息。WebGL使用三角形每个顶点存储的信息来生成并填充其他所有需要输出的像素。贴图对齐和光照信息通过varying变量传入。
所有Vertex shader的信息都会传递到fragment shader中,此shader会在每个被传入的经过转换的三角形的每个点上运行,从贴图得出对应的像素,调整光照并且输出。GLSL为此定义了一个专用的变量gl_FragColor,此变量存储的信息即为像素的颜色。
下面这个例子说明了如何通过HTML script标签来声明vertex及fragment shader。

vshader / fshader
<script id="vshader" type="x-shader/x-vertex">
uniform mat4 u_modelViewProjMatrix;
uniform mat4 u_normalMatrix;
uniform vec3 lightDir; attribute vec3 vNormal;
attribute vec4 vTexCoord;
attribute vec4 vPosition; varying float v_Dot;
varying vec2 v_texCoord; void main()
{
gl_Position = u_modelViewProjMatrix * vPosition;
v_texCoord = vTexCoord.st;
vec4 transNormal = u_normalMatrix * vec4(vNormal, 1);
v_Dot = max(dot(transNormal.xyz, lightDir), 0.0);
}
</script>
<script id="fshader" type="x-shader/x-fragment">
uniform sampler2D sampler2d; varying float v_Dot;
varying vec2 v_texCoord; void main()
{
vec2 texCoord = vec2(v_texCoord.s, 1.0 - v_texCoord.t);
vec4 color = texture2D(sampler2d, texCoord);
color += vec4(0.1, 0.1, 0.1, 1);
gl_FragColor = vec4(color.xyz * v_Dot, color.a);
}
</script>
上例中的vertex shader只是简单的使用一个modelViewProjMatrix来定义如何转换vertex position,并且与vertex position即vPosition一起传给fragment shader。然后vertex shader会将贴图对齐信息vTexCoord与通过normal即vNormal生成的光照信息v_Dot一并传入fragment shader。
Fragment shader就更简单了。它仅仅是从贴图中得到像素信息,然后乘以通过vertex shader传入的光照信息。这使得面对我们的像素更亮,从而获得一个较为真实的光照效果。
初始化引擎
我们通过以下代码可以简单的初始化一个WebGL引擎。

init
function init()
{
// Initialize
var gl = initWebGL("example", "vshader", "fshader",
[ "vNormal", "vColor", "vPosition"], [ 0, 0, 0, 1 ], 10000); // Set some uniform variables for the shaders
gl.uniform3f(gl.getUniformLocation(gl.program, "lightDir"), 0, 0, 1);
gl.uniform1i(gl.getUniformLocation(gl.program, "sampler2d"), 0); // Create a box. On return 'gl' contains a 'box' property with
// the BufferObjects containing the arrays for vertices,
// normals, texture coords, and indices.
gl.box = makeBox(gl); // Load an image to use. Returns a WebGLTexture object
spiritTexture = loadImageTexture(gl, "resources/spirit.jpg"); // Create some matrices to use later and save their locations in the shaders
gl.mvMatrix = new CanvasMatrix4();
gl.u_normalMatrixLoc = gl.getUniformLocation(gl.program, "u_normalMatrix");
gl.normalMatrix = new CanvasMatrix4();
gl.u_modelViewProjMatrixLoc = gl.getUniformLocation(gl.program, "u_modelViewProjMatrix");
gl.mvpMatrix = new CanvasMatrix4(); // Enable all of the vertex attribute arrays.
gl.enableVertexAttribArray(0);
gl.enableVertexAttribArray(1);
gl.enableVertexAttribArray(2); // Set up all the vertex attributes for vertices, normals and texCoords
gl.bindBuffer(gl.ARRAY_BUFFER, gl.box.vertexObject);
gl.vertexAttribPointer(2, 3, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, gl.box.normalObject);
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, gl.box.texCoordObject);
gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0); // Bind the index array
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.box.indexObject); return gl;
}
initWebGL()方法接受以下的参数:
“example” – canvas元素的Id
“vshader”,"fshader" – vertex/fragment shader对应的Id
“vNormal”, “vColor”, “vPosition” – 定义vertex shader属性的名称
[0,0,0,1],10000 - 底色与深度信息
上面这段初始化代码加在了shaders并且将其传入一个定义了shader接口信息的GLSL程序。shader上的uniform参数用来存储不会变化的值;vertex属性用来存储会发生变化的值,例如顶点。这段代码也告诉WebGL通过makeBox()方法来生成顶点,光照和贴图信息。
设置观察点
在渲染之前,我们必须告诉canvas如何在模型空间和屏幕空间之间映射对象。在最开始,一个对象的位置信息被描述为模型,而本地信息描述了对象的形状。这两个信息将会被转换为其他信息:
MODELING COORDINATES ->> WORLD COORDINATES ->> VIEW COORDINATES ->> VIEWPORT COORDINATES
简单的说,就是将模型信息转换为通过世界来描述的信息(世界包含了所有需要渲染的对象)。然后通过设置观察点,即你从哪里观看这个世界来生成观察点信息,也就是最终渲染在屏幕上的像素点(再次步骤会进行3D至2D的转换。canvas只能显示2D信息。)。
我们会使用转换矩阵(transformation matrix)来完成从一个coordinates到下一个的转换。在上例中,从modeling coordinates到view coordinates的转换使用过model-view matrix进行的,这个matrix合并了modeling->world, world->view两个步骤。然后通过透视矩阵pMatrix来进行最后一步view->viewport的转换。透视矩阵在reshape()方法中生成并存储到转换流水线的最后。

reshape
function reshape(gl)
{
var canvas = document.getElementById('example');
if (canvas.clientWidth == width && canvas.clientHeight == height)
return; width = canvas.clientWidth;
height = canvas.clientHeight; // Set the viewport and projection matrix for the scene
gl.viewport(0, 0, width, height);
gl.perspectiveMatrix = new CanvasMatrix4();
gl.perspectiveMatrix.lookat(0, 0, 7, 0, 0, 0, 0, 1, 0);
gl.perspectiveMatrix.perspective(30, width/height, 1, 10000);
}
绘制立方体
经过上述步骤,我们已经可以最终绘制了。我们要求在最终效果中,我们的立方体是可以转动的。我们应该在model-view matrix中加入这个操作,因为正是在这个转换矩阵中生成了在哪里和哪个角度来显示立方体。然后我们把model-view矩阵与透视矩阵相乘来完成所有步骤。请注意因为矩阵的相乘是不符合交换律的,所以顺序很重要。你也可以把一个model-view矩阵变为光照矩阵,这样你就能为此立方体得到合适的光照:

drawPicture
function drawPicture(gl)
{
//Make sure the canvas is sized correctly.
reshape(gl); // Clear the canvas
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Make a model/view matrix.
gl.mvMatrix.makeIdentity();
gl.mvMatrix.rotate(currentAngle, 0,1,0);
gl.mvMatrix.rotate(20, 1,0,0); // Construct the normal matrix from the model-view matrix and pass it in
gl.normalMatrix.load(gl.mvMatrix);
gl.normalMatrix.invert();
gl.normalMatrix.transpose();
gl.uniformMatrix4fv(gl.u_normalMatrixLoc, false, gl.normalMatrix.getAsWebGLFloatArray()); // Construct the model-view * projection matrix and pass it in
gl.mvpMatrix.load(gl.mvMatrix);
gl.mvpMatrix.multRight(gl.perspectiveMatrix);
gl.uniformMatrix4fv(gl.u_modelViewProjMatrixLoc, false, gl.mvpMatrix.getAsWebGLFloatArray()); // Bind the texture to use
gl.bindTexture(gl.TEXTURE_2D, spiritTexture); // Draw the cube
gl.drawElements(gl.TRIANGLES, gl.box.numIndices, gl.UNSIGNED_BYTE, 0); // Finish up.
gl.flush(); // Show the framerate
framerate.snapshot(); currentAngle += incAngle;
if (currentAngle > 360)
currentAngle -= 360;
}
最终,我们完成了这个例子。生成了一个带贴图的持续旋转的立方体。相信你已经可以通过这个例子完整的理解WebGL的工作模式与步骤。个人感觉还是非常简明易用的。
在线欣赏此例子
WebGL的支持现状
在最新版的Apple WebKit / Google Chrome / Mozilla Firfox / Opera中,均对WebGL提供了支持。但此标准依然是draft状态,各浏览器的支持也仅处于测试阶段,并不完整。
2010年12月12日
经过这段时间国内(百度,淘宝,新浪)及国外(Facebook,Youtube,Yahoo)各大公司的集中自曝,我们可以从中总结出2010 Web前端技术的一些趋势。总的来说,随着后端技术(存储,并发,分布式)的成熟,各大公司已经把重点从后端架构调整/建设转移至前端(TTI时间,快速发布,带宽利用率)。但作为明星技术的HTML5/CSS3,都未正式成为各公司的考虑重心,虽有所尝试,但在关键功能上,均未成为主力。这也W3C对当前HTML5/CSS3标准现状的表述:“不适宜用作生产环境”一致。
基本概念
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获取页面内容,参见BigPipe),若来自于缓存,通过Ajax来更新所需更新的模块(猜测:通过JS预先定义本页面所需更新的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倍的页面响应时间。
YouTube面临的问题
每天2Billion的访问。每分钟上传35小时的内容。可YouTube需要即时播放视频!越快越好。
YouTube解决方案
1. 将JS引用位置从页首移至页尾。
2. 直接嵌入Flash Player(YouTube之前使用JS来加载Flash Player)。通过页尾的JS来判断客户端的Flash版本(或不支持Flash),来替换预先嵌入的Flash Player或内容(如果需要的话),用来支持特定的客户群。
效果:页面渲染时间从~400ms降低为~200ms。Flash播放时间从~1200ms降低为~1100ms。
3. 预加载视频连接: 通过使用JS创建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,非阻碍式JS加载,静态内容提供,利用浏览器cache等等),所以也鲜有创新了。某种程度上来说,Facebook的一些所谓创新也不过是后知后觉,Yahoo早已考虑并实现了这些方案,只是也许不是那么有针对性而已。
Baidu
感觉整体倾向于组织结构介绍及一些比较过时的内容。如有兴趣可移驾至http://v.youku.com/v_show/id_XMjE5OTM0NTA4.html 自行观赏。
Taobao
http://www.docin.com/p-54097869.html 没有什么特别值得一提的内容。需要的同学可自行观赏。
相反的,淘宝的精益测试倒是引起了我的兴趣,出自微软的淘宝员工鹤云讲述了淘宝是如何进行CI(持续集成)的。有一些经验例如代码覆盖率测试也给人一些启发。感兴趣的同学可移驾至http://www.infoq.com/cn/presentations/hy-tabao-lean-test 观赏。
新浪博客
也是一些组织架构,开发方式的内容。介绍了一下新浪自己的JS框架。并无太多亮点。有兴趣的移驾http://v.youku.com/v_show/id_XMjE5OTYzMTI4.html 自行观赏。
大概就总结了这么多吧,感觉还是国外在主导。国内也在越来越重视这个方向,一些有实力的企业也做出了一些成绩,但还是与国际潮流有差距,也许是重视程度的区别吧。
欢迎大家补充讨论。
2010年9月2日
原文链接:http://firecacada.blog.163.com/blog/static/7074376201072894439630
作者:纯银
忍了半天还是没忍住。转了。感觉跟我的状态太像了。看完之后舒服了很多。
1、近日
好久没写杂念系列了,也许是因为太忙,忙到心中没有杂念吧。
最近三个月的精神状态很糟糕,四年来最差,而且是一天比一天更差。烦躁,抑郁,孤独,易怒,一点点小挫折就会搞得情绪低落。独自工作的时候注意力不容易集中,只有在开会时才能保持正常——人越多越活跃,被扎了两针似的。一个人的时候则沮丧消沉。
究其原因,并不能归于工作疲劳或目标压力,更多是长期以来积压的孤独感作祟。有时候想,我是否更适合一支狂热的的创业团队呢?4年前贪慕虚荣加入门户,是否是个错误?
摇摇头,人生没有这么多假设。
我身上有很多与所谓“领袖气质”截然相反的特点。比如情绪化,过于敏感而不够强韧,缺乏足够的魄力与感染力。简单来说,8年管理工作对我的改造还是敌不过与生俱来的书生气。也许更适合放在一队管理组合里做二把手、三把手一类的角色。
也许我只是需要放一个长假。
最近在跟两个产品项目,时间进度都很紧,下个月和四季度分别发布两次。除了国庆之外,今年别说休长假,连续休半周假都不可能。这意味着我的抑郁状态还将持续半年之久,叹口气。
06年去稻城,徒步上青蛙海,在1米宽,50度倾斜,又窄又陡的山路上爬了两个小时,从海拔3900米到4500米,累得快要死在路上。最后藏族向导说,到了,翻过前面那个坡就到了。说完健步如飞就冲了出去。我在原地喘了10分钟,抬头一看,眼泪都快流下来。他正站在远方的一个山脊上,小小的身影向我挥手示意。当时真以为自己一步都走不动了,完了,我放弃算了。结果还是一步十喘地蹭到了青蛙海边。那是一个群山环绕的高山湖泊,湖水被五颜六色的艳丽的高山草甸包围着,一个人影都没有,风很大,很适合发呆。
把泡泡名字改成了“青蛙海”,你懂的。
2、抓重点
说我傲慢也好,愚钝也好,反正是很少从身边的人那里学什么东西。工作这么多年,主要靠自己摸索经验。但这半年从boss身上领教了一样宝贵的技能:抓重点。
这三个字说出来是不是很可笑?
知易行难。
抓重点的意思是,很快从千头万绪之中,找出决定成败的关键点所在,而不被旁枝末节所迷惑。尤其是不被自诩为专业人士的产品完美主义所迷惑。一款产品有300个细节,但可能只有20人的团队。对投入产出比,边际效应,需求突破口进行评估,把任务优先级划分清楚,就是一个产品经理的核心能力。
做产品,前期有三个关键环节。第一是确定目标,第二是描绘框架,第三是抓住重点。目标代表着正确的方向,框架代表着有效的策略与清晰的流程,重点代表着合理的资源分配。大到一整个产品,小到单个模块策划,都适用于这个道理。正如我近期状态不佳,但依然能推动两个项目,很大程度上得益于对重点的把握,不会事无巨细地投入进去。当然这不是刚修炼出来的技能,但最近半年确有较大的提高,算是来到杭研的意外收获,感谢boss。
产品设计有两派,一派以马化腾为代表,讲究细腻的产品细节改进;一派以周鸿祎为代表,追求搔到用户的痒处。其实都在强调用户体验,但马派偏学院,偏重于全面提升;周派偏实用,偏重于抓住要点。我当然倾向于后者。不过,如果自己投入到一线策划中去,有时也会在无关紧要的细节上与人纠缠不清。这是性格缺陷,很难改,必须退后一步,静观全局的时候才有清醒的判断。只是退后一步谈何容易?怕是退无可退,唉,人生常入困局。
3、中年危机
快满34岁的时候,终于体会到传说中的中年危机。大抵是失败者与无趣者对命运的哀鸣罢了。
我的经历比较特殊,4岁上小学,18岁大学毕业,至今已工作15年。正常人入世这么长时间,已是37岁的高龄,而我仔细对比公司里的新同学,发觉自己还残留了颇多25岁的少年意气,即便努力掩饰也常常遮盖不住,任性地拖在身后。
青春之光终将消逝殆尽,令人恐惧的中年人疲惫的面容,也渐渐浮出在曾经的娃娃脸上。34岁的身体与34岁的压力,不断提示你活力四射已成追忆,路越走越窄,人生的选择越来越少。所谓理想与追求这种东西,逐个破灭,唯有对自己的失望越积越厚,总有一天会厚到像其他同龄人那样呆滞麻木的境地吗?像包裹着旧梦的茧。只好将希望都寄托在儿女的身上。然而我却是一枚丁克。
从23岁离开检察院,直到30岁,我的职业生涯每两年都会上升一个台阶。那时总是很担心有一天驻步不前——不幸而言中。在现在这家公司的四年里原地踏步,倒不完全是职位薪水之类,确实也没拿出什么耀眼的业绩来。我曾经归咎于自己心气浮躁,四年不到换了三个部门,但静下来想想,即便我从虫洞穿越到四年前做同样的事情,以现在的经验再来一次,力战不退,难道就会有个理想结局吗?
妄想。
也不必解释。
在年轻的互联网行业,如果27岁之前没做到经理,30岁之前没做到总监,35岁之前没做到大公司的高级总监或中型公司的VP,则后继乏力。根本不存在“大器晚成”这么一说。晚熟只意味着腐烂。
我最近不断问自己,是不是应该放弃对功名的拼命追逐(反正追不到),换成其他的精神追求,让自己好过一点,至少让工作之外的时间好过一点。可能这也是中年危机的一个变种,即绝望中的自救。当你发现愿望愈发遥远,而生活又愈发无趣时,已经很难踏上另一条崭新的职业道路。只能尝试着改变人生目标——从庸俗的到不庸俗的,从被承认到自得其乐。
然而能做到这一点的人,几希。
7年前,我还算是一个挺有生活情趣的文艺青年。其后背井离乡,放弃了生活中的诸多乐趣,也放弃了自己的文学天赋,只为了“出人头地”。结果7年来只得“乏善可陈”四个汉字。我总是希望自己能做到正直、单纯、清秀、才华横溢、功勋卓著、名利双收、颇受人尊重尤其受女人爱慕,人到中年,却只拥有最廉价的正直与单纯(始终如一),甚至连其他曾拥有的都已失去。对未来不再期待并充满无力感,不再幻想,不再哄骗自己明天会更好。你知道的,明天和今天一样糟糕,一样乏味。你都知道的。
人生是一个令人困惑的哲学问题。你总想追求天底下一切美好的东西,总是自我暗示,以为它们即便不是触手可及,也在不远的未来乖乖等着自己。直到有一天,忽然对自己大喊大叫:Liar!Loser!傻逼,你完了!
然后你又安静下来,小声对自己说:瞧,大家不都这样吗?人生不总是这样吗?洗洗睡吧。
天下风云出我辈,一入江湖岁月催。 皇图霸业谈笑中,不胜人生一场醉。
2010年9月1日
摘要: 定义递归函数简单的说,就是使用rec前缀:众所周知,上例是一个阶乘函数。使用rec前缀使得其可以使用其定义本身。基于区分递归函数与非递归函数的考虑,函数本身默认不可递归调用,这会帮助你控制算法逻辑和增加代码可维护性。上例可以形象的表示为:很多方法都可以使用递归调用的方式编写。例如List.length:有时递归也会在流程控制上使用,例如下述代码会持续的获取HTML代码,并且输出到屏幕上:递归很强大...
阅读全文
2010年8月31日
摘要: 使用选项(Option)下面看一个例子:使用模式匹配(Pattern matching)来生成option:Option的一些有用的方法: 方法 类型 描述 Option.get 'T option -> 'T 返回一个Some类型的值。或抛异常 Option.isNone 'T option -> bool 返回一个Option是否是None Option.map ('T ->...
阅读全文
2010年8月30日
摘要: 使用F#的List创建一个list: 语法 描述 示例 [] 空list [] expr :: expr 将一个项加入一个list 1 :: [2; 3] [expr; ...; expr] 列举项创建list [1; 2; 3] [expr .. expr] 连续的数字 [1 .. 99] [ for x in list ... ] 通过运算生成list(类似于Linq里的select) [ f...
阅读全文
2010年8月27日
摘要: F#的string类型是.NET的System.String的缩写形式,代表了一连串的UnicodeUTF-16字符。使用String几种不同的String书写方式: 示例 种类 类型 "Humpty Dumpty" 字符串 string "c:\\Program Files" 字符串 string @"c:\Program Files" 无转义(Verbatim) string string "...
阅读全文
2010年8月26日
摘要: F#高效高产的源头就在于其构建在久经考验的函数式编程理念之上。使用F#进行算术操作基本类型: 类型 描述 示例 .NET 类型 bool True/false values true,false System.Boolean byte 8-bit unsigned integers 0uy,19uy,0xFFuy System.Byte sbyte 8-bit signed integers 0y...
阅读全文