做 Web 应用要知道的那些事

2015/07/21 · HTML5 ·
Web应用

本文由 伯乐在线 –
刘健超-J.c
翻译,黄利民
校稿。未经许可,禁止转载!
英文出处:blog.venanti.us。欢迎加入翻译组。

在过去一年里,我从零开始开发一直在我的首个重要的 Web
应用程序。这次经历教会了我许多之前不知道的东西,特别在安全和用户体验方面。

我最后一次尝试开发足够复杂的应用是在 2005
年,所以就我的立场来说,有很多东西需要补充。

除了我所知所见外,要记住本文清单里的内容。因为在开发 Web
应用时,尤其是刚开始做的时候,容易忘记一些重要的事情。

这个检查清单并不是面面俱到,如果你是一个经验丰富的开发者,这里可能没有让你感到惊喜的东西,但我希望能证明它是有助于让你回忆起一些错过的东西。

正在从事web相关工作的小伙伴们你们是否知道什么是单页面应用,是否知道该如何构建单页面web应用?下面就来和我一起来看一看吧!

原文:http://www.developer.com/lang/jscript/7-things-you-need-to-know-about-web-workers.html 译者:
Rock(ruanqig@gmail.com)

图片 1

1.安装node
2.安装git
3.npm install –global yo bower grunt-cli
4.npm install -g generator-angular
5.yo angular
6.grunt serve
错误:[Error: Cannot find where you keep your Bower packages. )
解决:在git bash下执行
bower install

安全性

确认邮件:当用户注册时,应向他们发送带有点击确认邮箱的链接的邮件。如果用户更新他们的邮箱地址,则要再次重复这个工作流程。

身份管理:当存储密码时,首先对它们进行加盐和散列操作,然后再用现在广泛使用的
crypto 库。如果你不这样做的话,把身份管理转由给 Facebook / GitHub /
 Twitter / 等,用 OAuth 就能做到。

加密:所有证书问题,还有什么比 SSL
更好。使用它吧。还可以使用 HSTS。

凭证:不要把服务器身份信息(API
密钥、数据库密码等)放到版本控制里,否则就泄密了。

图片 2

介绍

通过使用Web Worker, 我们可以在浏览器后台运行Javascript, 而不占用浏览器自身线程。Web Worker可以提高应用的总体性能,并且提升用户体验。如果你想在自己的Web应用中使用Web Worker, 不妨来了解一下有关Web Worker的7件事。

1、整理旧物:把无关紧要的总想着以后也许会用到的东西丢掉,缺失了几颗水钻的发卡,不再行走的钟表,如果没有特别珍藏的含义,就丢了吧,即使再昂贵的价格,也失去了使用价值。

问题:
grunt test
Error during loading “karma-phantomjs-launcher” plugin:
Path must be a string. Received null

工程:动画

所有的爱,都是神圣的。但别为应用里的所有元素添加动画。因为大多数 CSS
动画都会触发布局重绘;最好尽可能地限制自己使用 transform 和 opacity。

避免进行缓慢的过渡运算,如果非要使用,那么确保它是针对某个属性的(如,”transition:
opacity 250ms ease-in” ,而不是 “transition: all 250ms ease-in”)。

首先我们来看一看单页应用是什么?

1. Web Worker 可以让你在后台运行Javascript

一般来说Javascript和页面的UI会共用一个线程,所以当点击一个按钮开始运行Javascript后,在这段代码运行完毕之前,页面是无法响应用户操作的,换句话来说就是被“冻结”了。而这段代码可以交给Web Worker在后台运行,那么页面在Javascript运行期间依然可以响应用户操作。后台会启动一个worker线程来执行这段代码,用户可以创建多个worker线程。所以你可以在前台做一些小规模分布式计算之类的工作,不过Web Worker有以下一些使用限制:

  • Web Worker无法访问DOM节点;
  • Web Worker无法访问全局变量或是全局函数;
  • Web Worker无法调用alert()或者confirm之类的函数;
  • Web Worker无法访问window、document之类的浏览器全局变量;

不过Web Worker中的Javascript依然可以使用setTimeout(),setInterval()之类的函数,也可以使用XMLHttpRequest对象来做Ajax通信。

2、收拾衣柜:衣柜总是最秘密和凌乱的地方,压在箱底的衣服,不再喜欢了的送给需要的人,过于陈旧的就剪开擦地板。

待解决

用户体验(UX)

表单:当提交一个表单后,用户应收到提交后的反馈。如果提交后不向用户发送一个不同的页面,那么就应该有弹框或
alert 一些信息,以便让用户知道这次提交是否成功。

登录重定向:如果用户打算在你的网站打开一个页面,但并没有登录,那么他们应该首先接收到一个能登录的页面,并在登录后重定向到一个他们原本想打开的一个页面(当然,前提是已得到授权)。

如果他们尝试登录,但提供了一个错误的密码,这时,用户有可能是忘记了密码,那我们就应该提供一个视觉线索来提醒他们,要有一个重置密码的选项。

所谓单页应用,指的是在一个页面上集成多种功能,甚至整个系统就只有一个页面,所有的业务功能都是它的子模块,通过特定的方式挂接到主界面上。它是AJAX技术的进一步升华,把AJAX的无刷新机制发挥到极致,因此能造就与桌面程序媲美的流畅用户体验。

2. 有两种Web Worker

Web workers可分为两种类型:专用线程dedicated web worker,以及共享线程shared web worker。
Dedicated web worker随当前页面的关闭而结束;这意味着Dedicated web worker只能被创建它的页面访问。与之相对应的Shared web worker可以被多个页面访问。在Javascript代码中,“Work”类型代表Dedicated web worker,而“SharedWorker”类型代表Shared web worker。

在绝大多数情况下,使用Dedicated web worker就足够了,因为一般来说在web worker中运行的代码是专为当前页面服务的。而在一些特定情况下,web worker可能运行的是更为普遍性的代码,可以为多个页面服务。在这种情况下,我们会创建一个共享线程的Shared web worker,它可以被与之相关联的多个页面访问,只有当所有关联的的页面都关闭的时候,该Shared web worker才会结束。相对Dedicated web worker,shared web worker稍微复杂些。

3、随手记帐:浑浑噩噩,不知道怎样度过了一天天,不知道手里的money怎样悄悄的不见,那就记录下来,不是自我苛责,而是一种清晰条理,是怎样把它们花费在了我所必需和而热爱的事物上。

电子邮件

订阅设置:任何发送到用户的 email
,都应该至少包含一个链接,能链接到修改他们的邮箱设置的应用程序页面,并且最好每个邮件都有一个单独的链接,能取消订阅。

千万别让用户为了取消订阅而向你发送邮件。

其实单页应用我们并不陌生,很多人写过ExtJS的项目,用它实现的系统,很天然的就已经是单页的了,也有人用jQuery或者其他框架实现过类似的东西。用各种JS框架,甚至不用框架,都是可以实现单页应用的,它只是一种理念。有些框架适用于开发这种系统,如果使用它们,可以得到很多便利。

3. “Worker”对象代表Dedicated Web Worker

现在来看如何使用Dedicated web worker。下面的例子中用到了jQuery以及Modernizr作为Javascript库,然后往HTML页面中加入以下代码:

[html] view
plain
copy

print?

  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4. <title></title>  
  5. <script type=“text/javascript” src=“script/modernizr.js”></script>  
  6. <script type=“text/javascript” src=“script/jquery-2.0.0.js”></script>  
  7. <script type=“text/javascript”>  
  8. (document).ready(function(){  </span></li><li class=”alt”><span>if(!Modernizr.webworker){  </span></li><li class=””><span>alert(“This browser doesn’t support Web Worker!”);  </span></li><li class=”alt”><span>return;  </span></li><li class=””><span>}  </span></li><li class=”alt”><span>
    (“#btnStart”).click(function(){  
  9. var worker = new Worker(“script/lengthytask.js”);  
  10. worker.addEventListener(“message”, function(evt){  
  11. alert(evt.data);  
  12. }, false);  
  13. worker.postMessage(10000)  
  14. });  
  15. });  
  16. </script>  
  17. </head>  
  18. <body>  
  19. <form>  
  20. <input type=“button” id=“btnStart” value=“Start Processing”/>  
  21. </form>  
  22. </body>  
  23. </html>  










这个HTML页面中有个按钮,点击后会运行一个Javascript文件。上面的代码中首先检测当前浏览器是否支持Web Worker,不支持的话,就跳出提醒信息。

按钮的点击事件中创建了Worker对象,并给它指定了Javascript脚本文件——lengthytask.js(稍后会有代码),并且给Worker对象绑定了一个“message”事件。该事件会在后台代码(lengthytask.js)向页面返回数据时触发。“message”事件可以通过event.data来获取后台代码传回的数据。最后,postMessage方法正式执行lengthytask.js,该方法还可以向后台代码传递参数, 后台代码同样通过message事件获取该参数。

下面是lengthytask.js主要包含的代码:

[javascript] view
plain
copy

print?

  1. addEventListener(“message”, function(evt){  
  2. var date = new Date();  
  3. var currentDate = null;  
  4. do {  
  5. currentDate = new Date();  
  6. }while(currentDate – date < evt.data);  
  7. postMessage(currentDate);  
  8. }, false);  

    addEventListener(“message”, function(evt){
    var date = new Date();
    var currentDate = null;
    do {
    currentDate = new Date();
    }while(currentDate – date < evt.data); postMessage(currentDate); }, false);

以上代码在后台监听message时间,并获取页面传来的参数:10000;这里实际上是一个计时函数:在message事件被触发10000毫秒之后,把结果(currentDate)传给页面。

所以当点击“Start Processing”按钮,页面会在10秒钟后把当时的时刻alert出来。在这10秒钟内页面依然可以响应鼠标键盘事件。

4、背单词:总是有想学一门语言的念头,但漫长的过程让人在开始就慌张,背单词枯燥而无聊,开始与坚持都很难,不过既然闲着也难受发慌,不如试试吧。

移动端

虽然你不必开发移动端…但不管你是否做,你都应该确保这是一个积极的决定,因为这会对你的应用程序设计和工程有实质性影响。

下面的注意事项是假设你已选择移动端作为你的平台之一。我碰巧选用 Grunt
作为我的构建工具,所以我得使用一些 Grunt-specific
插件,但你可能使用类似的 JavaScript 构建工具。

一、开发框架

4. “SharedWorker”对象代表Shared Web Worker

前面的代码使用的是dedicated web worker。 这一节会用shared web worker代替dedicated web worker,来区别两者的不同。下面是同一个例子的shared web worker版本:

[javascript] view
plain
copy

print?

  1. addEventListener(“message”, function(evt){  
  2. var date = new Date();  
  3. var currentDate = null;  
  4. do {  
  5. currentDate = new Date();  
  6. }while(currentDate – date < evt.data);  
  7. postMessage(currentDate);  
  8. }, false);  

    addEventListener(“message”, function(evt){
    var date = new Date();
    var currentDate = null;
    do {
    currentDate = new Date();
    }while(currentDate – date < evt.data); postMessage(currentDate); }, false);

请注意加黑的代码,这里创建了一个SharedWorker对象,并把message事件绑定在shared worker的port对象上;同样由port对象发起postMessage, 开始执行后台代码sharedlengthytask.js。

下面是sharedlengthytask.js的主要代码:

[javascript] view
plain
copy

print?

  1. var port;  
  2. addEventListener(”connect”, function(evt){  
  3. port = evt.ports[0];  
  4. port.addEventListener(”message”, function(evt){  
  5. var date = new Date();  
  6. var currentDate = null;  
  7. do {  
  8. currentDate = new Date();  
  9. }while(currentDate – date < evt.data);  
  10. port.postMessage(currentDate);  
  11. }, false);  
  12. port.start();  
  13. }, false);  

    var port;
    addEventListener(“connect”, function(evt){
    port = evt.ports[0];
    port.addEventListener(“message”, function(evt){
    var date = new Date();
    var currentDate = null;
    do {
    currentDate = new Date();
    }while(currentDate – date < evt.data); port.postMessage(currentDate); }, false); port.start(); }, false);

使用SharedWorker对象的后台代码需要绑定connect和message事件, connect事件会在页面上的port被start时触发。之后的message事件的回调函数与之前的基本相同,最后port调用postMessage方法把结果传回给页面。

5、养植物:植物是安静的陪伴,有细小裂纹渗水而不能再使用的水杯,用来随手撒一把种子吧,喜欢一种稳定积累的意义感,每天长出的一片新叶,都是轻轻浅浅呼吸啊。

npm install grunt-karma karma karma-phantomjs-launcher karma-jasmine jasmine-core phantomjs-prebuilt --save-dev 

工程

单页面应用:现今单页面(SPA)是王道。它的主要优势是很少加载整个页面 –
只需加载所需资源,并且无须反复重载相同的资源。如果你才刚刚开始开发一个新的
web 应用,那它很可能是 SPA。

ExtJS可以称为第一代单页应用框架的典型,它封装了各种UI组件,用户主要使用JavaScript来完成整个前端部分,甚至包括布局。随着功能逐渐增加,ExtJS的体积也逐渐增大,即使用于内部系统的开发,有时候也显得笨重了,更不用说开发以上这类运行在互联网上的系统。

5. Web Worker使用XMLHttpRequest与服务端通信

有些情况下,web worker还需要与服务器进行交互。比如页面可能需要处理来自数据库中的信息,我们就需要使用Ajax技术与服务器交互,下面代码包含了web worker如何从服务端获取数据:

[javascript] view
plain
copy

print?

  1. addEventListener(“message”, function(evt){  
  2. var xhr = new XMLHttpRequest();  
  3. xhr.open(”GET”, “lengthytaskhandler.ashx”);  
  4. xhr.onload = function(){  
  5. postMessage(xhr.responseText);  
  6. };  
  7. xhr.send();  
  8. },false);  

    addEventListener(“message”, function(evt){
    var xhr = new XMLHttpRequest();
    xhr.open(“GET”, “lengthytaskhandler.ashx”);
    xhr.onload = function(){
    postMessage(xhr.responseText);
    };
    xhr.send();
    },false);

上面的代码向服务端的asp.net服务lengthytaskhandler.ashx发出GET请求。并注册了获取数据后的onload事件。下面的代码是服务端的lengthytaskhandler.ashx:

[csharp] view
plain
copy

print?

  1. namespace WebWorkerDemo  
  2. {  
  3. public class LengthyTaskHandler:IHttpHandler  
  4. {  
  5. public void ProcessRequest(HttpContext context)  
  6. {  
  7. System.Threading.Thread.Sleep(10000);  
  8. context.Response.ContentType = ”text/plain”;  
  9. content.Response.Write(”Processing successful!”);  
  10. }  
  11. public bool IsReusable  
  12. {  
  13. get  
  14. {  
  15. return false;  
  16. }  
  17. }  
  18. }  
  19. }  

    namespace WebWorkerDemo
    {
    public class LengthyTaskHandler:IHttpHandler
    {
    public void ProcessRequest(HttpContext context)
    {
    System.Threading.Thread.Sleep(10000);
    context.Response.ContentType = “text/plain”;
    content.Response.Write(“Processing successful!”);
    }
    public bool IsReusable
    {
    get
    {
    return false;
    }
    }
    }
    }

如你所见,ProcessRequest模拟了一个长时间运行的任务,并返回了“Processing successful!”的消息。

6、记点有意思的东西:刷尽了微博、空间、朋友圈又无所事事的时候,把遇见的在眼前发亮的东西用笔记下来吧,想成为一个有趣的人,用笔记录那些让自己感动的欢笑的有感触的,当遇见有趣的人,再说给他们听,毕竟,一个会讲故事的人是多么迷人啊。

2.报错

用户界面(UI)

分辨率:当你开发 MVP (Minimum Viable Product
–最简化可实行产品)时,不用先急着兼容各种尺寸的 UI
,那是等你的产品一下子火了之后才需要去做的事情,但要确保支持主流设备(尺寸)。

jQuery由于偏重DOM操作,它的插件体系又比较松散,所以比ExtJS这个体系更适合开发在公网运行的单页系统,整个解决方案会相对比较轻量、灵活。

6. 通过Error事件捕捉错误信息

当我们把越来越复杂的逻辑加到Web Worker里时,错误处理机制是必不可少的。而Web Worker恰恰提供了error事件,供开发者捕捉错误信息。下面的代码展示了如何绑定error事件:

[javascript] view
plain
copy

print?

  1. (</span><span class=”string”>”#btnStart”</span><span>).click(</span><span class=”keyword”>function</span><span>(){  </span></span></li><li class=””><span><span class=”keyword”>var</span><span> worker = </span><span class=”keyword”>new</span><span> Worker(</span><span class=”string”>”scripts/lengthytask.js”</span><span>);  </span></span></li><li class=”alt”><span>worker.addEventListener(<span class=”string”>”error”</span><span>, </span><span class=”keyword”>function</span><span>(evt){  </span></span></li><li class=””><span>alert(<span class=”string”>”Line #”</span><span> + evt.lineno + </span><span class=”string”>” – “</span><span> + evt.message + </span><span class=”string”>” in “</span><span> + evt.filename);  </span></span></li><li class=”alt”><span>}, <span class=”keyword”>false</span><span>);  </span></span></li><li class=””><span>worker.postMessage(10000);  </span></li><li class=”alt”><span>});  </span></li></ol></div><pre code_snippet_id=”327224″ snippet_file_name=”blog_20140505_7_9445951″ name=”code” class=”javascript” style=”display: none;”>

    (“#btnStart”).click(function(){ var worker = new
    Worker(“scripts/lengthytask.js”); worker.addEventListener(“error”,
    function(evt){ alert(“Line #” + evt.lineno + ” – ” + evt.message +
    ” in ” + evt.filename); }, false); worker.postMessage(10000); });
    如上可见, Worker对象可以绑定error事件;而且evt对象中包含错误所在的代码文件(evt.filename)、错误所在的代码行数(evt.lineno)、以及错误信息(evt.message)。

    7. 通过terminate()方法终止Web Worker

    有些情况下,我们可能需要强制终止执行中的Web Worker。Worker对象提供了terminate()来终止自身执行任务,被终止的Worker对象不能被重启或重用,我们只能新建另一个Worker实例来执行新的任务。

    总结

    Web Worker可以在后台执行脚本,而不会阻塞页面交互。Worker对象分为两种:专用式Web Worker和共享式Web Worker:专用式的Web Worker只能被当个页面使用,而共享式的Web Worker可以在被多个页面使用。另外,本文还介绍了Web Worker的错误处理机制,以及使用Ajax与服务端交互。

7、看一看天气预报:虽然没那么准确,但是在每天早晨,不用那么慌张的穿什么,纠结来纠结去,接着一整天还是觉得穿着不合适不自在。

: Cannot find module 'D:\node-v6.10.0-win-x64\node_modules\phantomjs-prebuilt\bin\phantomjs'

UX:带宽

相对于桌面端,移动端的一个大主题是带宽,它是非常珍贵的资源。因此,不应该放过任何能减少请求的机会,让它们尽可能地采用异步请求,并减少请求资源的大小。

JS & CSS – 合并与压缩:把面向具体应用的 JavaScript 和 CSS
 合并到单独文件里(一个 JS,一个
CSS),并进行压缩。Grunt-contrib-concat、Grunt-contrib-cssmin 和 Grunt-contrib-uglify 都是你的好朋友。

所有资源 – 使用
CDN:它有两个主要的优势。第一个是适用托管所有资源,并本地化。CDN
确保资源服务都位于一个区域,而该区域在地理位置上是靠近用户请求资源的位置,从而减少加载时间。

第二个优势是更适用于你的依赖文件(比如,非面向特定应用的样式和 JS
代码)。为你所依赖的文件使用 CDN
能极大地减少加载时间。比如,很多网站依赖 Angular.js,使用 CDN 链接
Angular
代码会触发缓存命中,那么移动设备会从设备缓存里检索,而不是额外新建一个
HTTP 请求。

CSS – 减少占用空间:大多数开发者在初始时阶段,很可能使用某些 UI 框架(如
Bootstrap、Foundation 等)。这些框架可以很大,其压缩版通常可以常用的
CDN 上获得,但你不太可能使用它包含的所有样式。因此,类似
uncss 工具(一般配对的有 processhtml)能令你难以置信地移除最终未被使用的样式。

注意这点很重要:uncss 解析器不能提取动态样式(即通过 JavaScript
事件添加的样式),所以你必须在浏览器进行严格的测试,以确保不会去除应用程序实际用到的样式。

CSS –
将关键的文件放在头部:因为样式需要在应用完成加载前看到;次要的样式能在加载完后提供。

JS – 减少占用空间:因为应用一旦上线,程序员就不需要考虑 JavaScript
代码里内部变量的可读性,因此可以将所有如 user.name 变量重命名为
u.e,从而减少文件大小。因此,有一个工具为此而生 –
上面提及到的 uglify,虽然它会使 JS
代码完全看不懂,但极大地减小文件大小。

但由于jQuery主要面向上层操作,它对代码的组织是缺乏约束的。如何在代码急剧膨胀的情况下控制每个模块的内聚性,并且适当在模块之间产生数据传递与共享,就成为了一种有挑战的事情。

8、好好搭配现有的衣服:淘宝逛了一个又一个小时,还是索然无味头昏脑涨的关掉淘宝界面,总觉得衣柜里还差一件衣服,既然那么累,不如想一想所拥有的最好的搭配。

解决
下载phantomjs.exe
放在 ‘D:\node-v6.10.0-win-x64\ 目录下

用户体验:表单

这是一个很好的建议:保持表单和工作流程的简易性,当你针对移动设备作为部署平台时,这点尤其重要。因为没有人愿意在手机上填满
5 页的表单。


我希望这列表对于刚开始开发第一款 Web
应用的你有所帮助,甚至对那些之前不熟悉前端的一些优化技巧的后端或设计师。如果你有其它建议或记起某些东西,那么请让我知道,我会考虑将它添加到该列表。

感谢 Chris Dean (@ctdean),Danny King
(@dannykingme) 和 Allen Rohner
(@arohner),他们不仅审阅本文的草稿,而且添加了建议。

打赏支持我翻译更多好文章,谢谢!

打赏译者

为了解决单页应用规模增大时候的代码逻辑问题,出现了不少MV*框架,他们的基本思路都是在JS层创建模块分层和通信机制。有的是MVC,有的是MVP,有的是MVVM,而且,它们几乎都在这些模式上产生了变异,以适应前端开发的特点。

9、发呆:没事就发发呆吧,回忆也好,幻想也好,它总是不知不觉的在向自己询问真正的所求,安静的和自己相处一会,听一听我们自己。有些事情是不能告诉别人的,有些事情是不必告诉别人的,有些事情事情是根本无法告诉别人的,而且有些事情是,即使告诉了别人,

参考文档
1.http://www.cnblogs.com/Look\_Sun/p/4598045.html
2.http://blog.jobbole.com/65399/

打赏支持我翻译更多好文章,谢谢!

任选一种支付方式

图片 3
图片 4

1 赞 1 收藏
评论

这类框架包括Backbone、Knockout、AngularJS、Avalon等。

10、过去的感情收藏好吧,即使那时的山再青、水再绿、那风再温柔,也是曾经了,太深的流连便是羁绊,绊住的不仅是现在,还有将来。

关于作者:刘健超-J.c

图片 5

前端,在路上…
个人主页 ·
我的文章 ·
19 ·
    

图片 6

二、组件化

这些在前端做分层的框架推动了代码的组件化,所谓组件化,在传统的Web产品中,更多的指UI组件,但其实组件是一个广泛概念,传统Web产品中UI组件占比高的原因是它的厚度不足,随着客户端代码比例的增加,相当一部分的业务逻辑也前端化,由此催生了很多非界面型组件的出现。

分层带来的一个优势是,每层的职责更专一了,由此,可以对其作单元测试的覆盖,以保证其质量。传统UI层测试最头疼的问题是UI层和逻辑混杂在一起,比如往往会在远程请求的回调中更改DOM,当引入分层之后,这些东西都可以分别被测试,然后再通过场景测试来保证整体流程。

三、代码隔离

与开发传统页面型网站相比,实现单页应用的过程中,有一些比较值得特别关注的点。

从单页应用的特点来看,它比页面型网站更加依赖于JavaScript,而由于页面的单页化,各种子功能的JavaScript代码聚集到了同一个作用域,所以代码的隔离、模块化变得很重要。

在单页应用中,页面模板的使用是很普遍的。很多框架内置了特定的模板,也有的框架需要引入第三方的模板。这种模板是界面片段,我们可以把它们类比成JavaScript模块,它们是另一种类型的组件。

模板也一样有隔离的需要。不隔离模板,会造成什么问题呢?模板间的冲突主要存在于id属性上,如果一个模板中包含固定的id,当它被批量渲染的时候,会造成同一个页面的作用域中出现多个相同id的元素,产生不可预测的后果。因此,我们需要在模板中避免使用id,如果有对DOM的访问需求,应当通过其他选择器来完成。如果一个单页应用的组件化程度非常高,很可能整个应用中都没有元素id的使用。

四、代码合并与加载策略

人们对于单页系统的加载时间容忍度与Web页面不同,如果说他们愿意为购物页面的加载等待3秒,有可能会愿意为单页应用的首次加载等待5-10秒,但在此之后,各种功能的使用应当都比较流畅,所有子功能页面尽量要在1-2秒时间内切换成功,否则他们就会感觉这个系统很慢。

从这些特点来看,我们可以把更多的公共功能放到首次加载,以减小每次加载的载入量,有一些站点甚至把所有的界面和逻辑全部放到首页加载,每次业务界面切换的时候,只产生数据请求,因此它的响应是非常迅速的,比如青云的控制台就是这么做的。

通常在单页应用中,无需像网站型产品一样,为了防止文件加载阻塞渲染,把js放到html后面加载,因为它的界面基本都是动态生成的。

当切换功能的时候,除了产生数据请求,还需要渲染界面,这个新渲染的界面部件一般是界面模板,它从哪里来呢?来源无非是两种,一种是即时请求,像请求数据那样通过AJAX获取过来,另一种是内置于主界面的某些位置,比如script标签或者不可见的textarea中,后者在切换功能的时候速度有优势,但是加重了主页面的负担。

在传统的页面型网站中,页面之间是互相隔离的,因此,如果在页面间存在可复用的代码,一般是提取成单独的文件,并且可能会需要按照每个页面的需求去进行合并。

单页应用中,如果总的代码量不大,可以整体打包一次在首页载入,如果大到一定规模,再作运行时加载,加载的粒度可以搞得比较大,不同的块之间没有重复部分。

五、路由与状态的管理

我们最开始看到的几个在线应用,有的是对路由作了管理的,有的没有。

管理路由的目的是什么呢?是为了能减少用户的导航成本。比如说我们有一个功能,经历过多次导航菜单的点击,才呈现出来。

如果用户想要把这个功能地址分享给别人,他怎么才能做到呢?

传统的页面型产品是不存在这个问题的,因为它就是以页面为单位的,也有的时候,服务端路由处理了这一切。

但是在单页应用中,这成为了问题,因为我们只有一个页面,界面上的各种功能区块是动态生成的。所以我们要通过对路由的管理,来实现这样的功能。

具体的做法就是把产品功能划分为若干状态,每个状态映射到相应的路由,然后通过pushState这样的机制,动态解析路由,使之与功能界面匹配。

有了路由之后,我们的单页面产品就可以前进后退,就像是在不同页面之间一样。

其实在Web产品之外,早就有了管理路由的技术方案,Adobe
Flex中,就会把比如TabNavigator,甚至下拉框的选中状态对应到url上,因为它也是单“页面”的产品模式,需要面对同样的问题。

当产品状态复杂到一定程度的时候,路由又变得很难应用了,因为状态的管理极其麻烦,比如开始的时候我们演示的c9.io在线IDE,它就没法把状态对应到url上。

六、缓存与本地存储

在单页应用的运作机制中,缓存是一个很重要的环节。

由于这类系统的前端部分几乎全是静态文件,所以它能够有机会利用浏览器的缓存机制,而比如动态加载的界面模板,也完全可以做一些自定义的缓存机制,在非首次的请求中直接取缓存的版本,以加快加载速度。

甚至,也出现了一些方案,在动态加载JavaScript代码的同时,把它们也缓存起来。比如Addy
Osmani的这个basket.js,就利用了HTML5 localStorage作了js和css文件的缓存。

在单页产品中,业务代码也常常会需要跟本地存储打交道,存储一些临时数据,可以使用localStorage或者localStorageDB来简化自己的业务代码。

七、服务端通信

传统的Web产品通常使用JSONP或者AJAX这样的方式与服务端通信,但在单页Web应用中,有很大一部分采用WebSocket这样的实时通讯方式。

WebSocket与传统基于HTTP的通信机制相比,有很大的优势。它可以让服务端很便利地使用反向推送,前端只响应确实产生业务数据的事件,减少一遍又一遍无意义的AJAX轮询。

由于WebSocket只在比较先进的浏览器上被支持,有一些库提供了在不同浏览器中的兼容方案,比如socket.io,它在不支持WebSocket的浏览器上会降级成使用AJAX或JSONP等方式,对业务代码完全透明、兼容。

八、内存管理

传统的Web页面一般是不需要考虑内存的管理的,因为用户的停留时间相对少,即使出现内存泄漏,可能很快就被刷新页面之类的操作冲掉了,但单页应用是不同的,它的用户很可能会把它开一整天,因此,我们需要对其中的DOM操作、网络连接等部分格外小心。

九、样式的规划

在单页应用中,因为页面的集成度高,所有页面聚集到同一作用域,样式的规划也变得重要了。

样式规划主要是几个方面:

1、基准样式的分离

这里面主要包括浏览器样式的重设、全局字体的设置、布局的基本约定和响应式支持。

2、组件样式的划分

这里面是两个层面的规划,首先是各种界面组件及其子元素的样式,其次是一些修饰样式。组件样式应当尽量减少互相依赖,各组件的样式允许冗余。

3、堆叠次序的管理

传统Web页面的特点是元素多,但是层次少,单页应用会有些不同。

在单页应用中,需要提前为各种UI组件规划堆叠次序,也就是z-index,比如说,我们可能会有各种弹出对话框,浮动层,它们可能组合成各种堆叠状态。新的对话框的z-index需要比旧的高,才能确保盖在它上面。诸如此类,都需要我们对这些可能的遮盖作规划,那么,怎样去规划呢?

了解通信知识的人,应当会知道,不同的频率段被划分给不同的通信方式使用,在一些国家,领空的使用也是有划分的,我们也可以用同样的方式来预先分段,不同类型的组件的z-index落到各自的区间,以避免它们的冲突。

十、单页应用的产品形态

我们在开始的时候提到,存在着很多新型Web产品,使用单页应用的方式构建,但实际上,这类产品不仅仅存在于Web上。点开Chrome商店,我们会发现很多离线应用,这些产品都可以算是单页应用的体现。

除了各种浏览器插件,借助node-webkit这样的外壳平台,我们可以使用Web技术来构建本地应用,产品的主要部分仍然是我们熟悉的单页应用。

单页应用的流行程度正在逐渐增加,大家如果关注了一些初创型互联网企业,会发现其中很大一部分的产品模式是单页化的。这种模式能带给用户流畅的体验,在开发阶段,对JavaScript技能水平要求较高。

单页应用开发过程中,前后端是天然分离的,双方以API为分界。前端作为服务的消费者,后端作为服务的提供者。

在此模式下,前端将会推动后端的服务化。当后端不再承担模板渲染、输出页面这样工作的情况下,它可以更专注于所提供的API的实现,而在这样的情况下,Web前端与各种移动终端的地位对等,也逐渐使得后端API不必再为每个端作差异化设计了。

十一、部署模式的改变

在现在这个时代,我们已经可以看到一种产品的出现了,那就是“无后端”的Web应用。这是一种什么东西呢?基于这种理念,你的产品很可能只需要自己编写静态Web页面,在某种BaaS(Backend
as a
Service)云平台上定制服务端API和云存储,集成这个平台提供的SDK,通过AJAX等方式与之打交道,实现注册认证、社交、消息推送、实时通信、云存储等功能。

我们观察一下这种模式,会发现前后端的部署已经完全分离了,前端代码完全静态化,这意味着可以把它们放置到CDN上,访问将大大地加速,而服务端托管在BaaS云上,开发者也不必去关注一些部署方面的繁琐细节。

假设你是一名创业者,正在做的是一种实时协同的单页产品,可以在云平台上,快速定制后端服务,把绝大部分宝贵的时间花在开发产品本身上。

十二、单页应用的缺陷

单页应用最根本的缺陷就是不利于SEO,因为界面的绝大部分都是动态生成的,所以搜索引擎很不容易索引它。

十三、产品单页化带来的挑战

一个产品想要单页化,首先是它必须适合单页的形态。其次,在这个过程中,对开发模式会产生一些变更,对开发技能也会有一些要求。

开发者的JavaScript技能必须过关,同时需要对组件化、设计模式有所认识,他所面对的不再是一个简单的页面,而是一个运行在浏览器环境中的桌面软件。

构建单页应用应该是web前端开发人员的必备技能,对于初学者而言也许有些难度,但只要加强练习,掌握这个技能不在话下。如果你在学习web前端过程中遇到了什么难题,加入465042726,关于前端方面的更多问题我们可以一起交流!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图