从JavaScript框架的演变思考前端开发

JavaScript框架的发展是一个不断带给人们惊喜的过程。JavaScript一度被认为只能做出网页上飞来飞去、令人讨厌的广告动画,到jQuery的出现人们开始用jQuery插件实现更为丰富的控件。再到后来的MVC、MVVM模式、Flux模式,让前端从制作网页变成运行在浏览器中的“应用程序”。

在不长的软件工程历史中,框架或者库影响了一门语言的思想,甚至人们因为框架改变了一门语言的的看法的的例子比比皆是,而JavaScript就是其中的代表。

js-frameworks.jpg

思考JavaScript框架和库发展过程中的一些重要时期,对我们更好的理解前端开发或许有一些帮助。鉴于此,本篇对JavaScript发展过程中那些有深远意义的框架和库进行梳理,即使它们在历史的演变中被人们遗忘,它们的设计思想我相信也会让人受益。

框架之前

web前端的发展实在是迅猛,距离我大学时期也不过6-7年,那个时候的网页设计课还在使用“网页三剑客”。老师教我们使用Dreamweaver制作网页,Dreamweaver是一个类似于Word的可视化网页设计工具,使用了表格布局和行内样式,大部分是用来制作静态网页。

网页设计的高级内容就是制作JavaScript特效,制作类似文字随着鼠标浮动的特效,能独立制作一个轮播图组件已经算是厉害的角了。

在CSS3出现之前,网页设计中使用了各种“奇技淫巧”,JavaScript最大的作用就是实现一些页面特效(例如动画)。

js.gif

在前端开发框架变得流行之前,开发者的主要目的是完成各种前端特效,也会使用Ajax从后端返回HTML片段实现简单的动态内容加载。因此大量的DOM操作就不可避免了,为了简化DOM操作,出现了jQuery这类工具库。

DOM操作时期

实际上在jQuery之前已经出现了一些非常成熟的JavaScript库,如Prototypejs、Mootools以及Dojo等。(在JavaScript中有时候库和框架的界限并不明显)jQuery的大量灵感来源于Prototypejs,下面简单说下Prototypejs。

Prototypejs正如其名,通过JavaScript原型链的机制实现了继承、接口等面向对象的操作。也提供了简单的DOM选择器、Ajax工具、JSON数据操作和事件代理。某种意义来说,Prototyojs已经是一个专业的前端开发框架了,但就当时来说它的缺点也很明显:当是很多网页制作者并不是专业的程序员,Prototypejs的API过于复杂,面向对象机制太难理解,选择器也不够强大。

jQuery简化了一些操作,并提供更为人性化的API,最重要的是开创性的使用了构造器模式实现了链式调用,并且将大量方法提供到jQuery的DOM包装对象上,为jQuery插件的编写提供了更大的空间。

Prototyojs 的操作DOM并注册事件

image.png

jQuery操作的方式

image2.png
(在jQuery作者John Resig博客上建议的一个例子)

jQuery不仅更为容易使用,在内功的修炼上也是出类拔萃。操作DOM的第一步是从DOM树中找到目标DOM对象,如果大量使用id来获取DOM节点,这会让代码变得难以维护。jQuery提供了一个强大的选择器引擎,能通过元素标签名、类名、属性等各种能想到的方式获取DOM元素,并注册jQuery拓展的方法。jQuery内置的选择器引擎被剥离出来,现在名叫sizzlejs,和jQuery同属于JS基金会。对于前端开发者来说,jQuery非常值得一读,开场的自执行函数非常惊艳。

于是jQuery凭借容易使用的DOM操作API、强大的选择器和插件机制普及开来,并深深的影响了JavaScript这门语言。Prototypejs是一款被低估的框架,但并不是因为技术落后被代替的,它的模块化和架构性甚至比jQuery更好,在后面要介绍的Backbone中也能找到它的影子。

富前端的思考

前端开发最大的特点是基于页面的开发,在浏览器中用户载入一个页面,即使有丰富的交互和特效,在离开页面的时候,网页的运行环境就被重置了。这种开发模式的好处相对于桌面和移动APP开发更为简单,页面不需要维护整个应用程序的状态和数据,但是对于用户来说体验上有巨大差别。

人们不断思考,能不能把桌面和移动APP的体验带到浏览器上呢?用户打开一个网页就像启动了一个应用程序,无需页面重新加载就可以完成所有的操作,用户当前的操作无需被页面加载打断。

事实上各种各样的网站尝试类似的做法,不过直到Gmail 的出现让这个想法变得更为现实。使用Gmail的时候,邮件列表和编辑邮件等操作无需重新加载网页,而像普通的邮件客户端一样,只不过这一切发生在浏览器中。

大家可以对比Gmail 和 QQ邮箱,你会发现体验截然不同,QQ邮箱在进行各项操作时会发生页面跳转,Gmail可以在同一个页面完成所有操作。

早期的富前端开发

当DOM操作和Ajax技术变得愈来愈成熟的时候,人们不仅仅满足于制作静态页面和特效了,前端开发开始慢慢地往复杂交互和类似于桌面应用的方向发展。

使用Ajax带来了全新的用户体验,用户不必刷新整个页面,就可以动态的加载HTML片段或者JSON。大量的项目开始用局部刷新来提高用户体验,比较明显的例子就是QQ空间。

同时随着jQuery的不断流行,业界出现了一大批优秀的jQuery插件,例如模态框、下拉菜单、表格、文件上传等。

image3.png

伴随着jQuery和jQuery插件生态的成熟,Twitter推出的一个基于jQuery和jQuery插件的前端开发的开源工具包 Bootstrap,Bootstrap几乎成为了事实上HTML和CSS代码编写的规范。Bootstrap不仅提供了基础的CSS样式库,还通过jQuery插件的形式,提供了后台系统常用的组件。

至此前端开发进入了一个比较成熟的阶段,Bootstrap这类大而全的的前端开发框架出现,让类似表单提交等后端的逻辑演化到了前端。但是,Bootstrap依然是网页开发的模式,服务器动态脚本来加载页面和一部分数据。能不能让所有的逻辑都放到前端来,然后使用ajax和服务器统一通信呢?

此时业界还有另外一个前端框架在发展,它提供了及其丰富的控件,允许开发者通过JavaScript在前端完成几乎所交互,最后通过ajax把这些数据传回服务器。这样的框架能实现类似于桌面上的交互能力,它就是ExtJS,一款致力于构建富客户端的Ajax应用的前端框架。

image4.png

不过Bootstrap和Extjs都有他们的局限性。

Bootstrap依赖jQuery和jQuery插件,其本质思想还是针对页面的DOM进行操作,当页面上存在很多数据和状态时,会显得非常吃力。并且基于多page原因,前端代码会被碎片化分割到不同的页面,没有统一的入口,使用jQuery的业务代码往往会被“意大利面式的代码”。

Extjs 因为过多特性和过强约束,让富客户端变得很容易,但这又让它的适用性变得很窄,往往适合大型的WEB行业软件或后台管理系统,例如经销存或者CRM系统。随着互联网发展,人们对交互和体验有了更多的个性化要求,Extjs也不太适合互联网公司的开发模式。

MVC初现

随着浏览器性能的提升和各种JavaScript框架、库的发展,限制网页变成应用的瓶颈变成了如何管理数据和合理的架构。

jQuery只是一个DOM操作库,没有数据和状态管理组件。即使提供了$.data 方法,允许开发者将数据绑定到到DOM的属性上,但总归不是一个成熟的想法。

不过好在服务器编程领域已经思考过一遍类似的问题了,服务器编程经历了多入口多页面开发到业务逻辑分层的架构,最终发展成较为成熟的MVC架构。前端也出现了类似MVC架构的框架,Backbone是其中设计上比较优雅和简单的框架,Emberjs则是其中大而全和功能丰富的代表。
image5.png

如果还没有了解过MVC模式,可以参考阅读https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller

但是在前端的MVC框架实现上毕竟只是参考了服务器端的设计思想,毕竟语言和运行环境的不同,实现上有一些不同,这里以Backbone为例指出这些不同。

  • 在前端的C(controller)往往被路由和数据集合代替,在Backbone中C指的是Collection
  • 在服务器端V(View)非常好理解,就是输入/输入,但是前端的输入是通过处理用户交互事件获得的,数据的输出是通过HTML模板。(这部分比较复杂,于是后面发展出了双向绑定、虚拟DOM)
  • 前端开发最大的特点是大量的异步操作,因此MVC中数据的流动是异步的。虽然这两年随着Nodejs、Spring Flux的发展,后端也有同样的趋势。

简单来说使用前端MVC框架的基本逻辑是:

路由渲染视图给用户,然后通过和用户交互收集用户输入的数据,写入模型中,然后通过模型和服务器API交互。

使用Backbone开发一个Todos的例子:http://backbonejs.org/examples/todos/index.html

双向绑定和MVVM模式

在前端开发中,最繁琐的工作就是处理大量复杂的表单。一般的方法就是通过监听Submit事件统一收集数据,或者通过onchange事件实时的收集用户输入。

人们想象如果有什么方法能把用户界面上的元素和程序对象关联起来该多好啊,只需要改变相关对象的属性,即可得到用户的输入和更新界面上的显示。

好在,微软在WPF技术中提出了MVVM模式,实现了用户界面和程序对象的绑定和映射。WPF的出现大大提高了桌面应用的开发效率,如果从事过MFC(VC6.0 使用的这种框架,如果你对大学生活还有映像的话)框架开发的开发者应该能体会到这种巨大变化。
image6.png

Google的angularjs给我们带来了浏览器环境中的双向绑定,虽然因为浏览器的某些限制,并不能带来和WPF媲美的体验,但是也属于前端领域的突破性进展了。PS:注意这里的angularjs指的事angularjs 1.x和目前的angular 不是完全相同。

Angularjs的一些特点:

  • 由于低版本浏览器中的JavaScript不支持对象属性的赋值、取值钩子(setter、getter),Angular为了实现使用了定时器循环的脏检查,这对低版本和移动端的性能造成影响。
  • Angularjs为了能收集用户输入,覆盖了window上一些特定的对象,造成一定的侵入性,在技术选型上需要特别注意。

image7.png

总的来说Angularjs带来了能在浏览器中类似WPF的开发体验,让富客户端的开发进了一大步,于是越来越多的桌面软件被迁移到web平台。

ng.gif

(视图根据数据实时改变)

除Angularjs 之外,MVVM还有Knockoutjs,在某种程度上Vue也算使用了MVVM模式。

数据驱动视图和Flux模式

web开发者追求把桌面应用搬到浏览器中的同时,另一拨人也想把移动端APP搬到浏览器上。构建一个在APP webview中和浏览器上都能运行的APP是多么惬意的事情啊。

不过无论是jQuery到Angularjs在移动端都不好用,我曾经用Ionic(基于Angularjs的移动端前端框架)构建过应用,性能和数据管理能力(对于移动端而言)都不好。

Facebook 同样有此困扰,在移动端APP中需要管理一个全局的状态,进而渲染不同的视图。Facebook创建了通过虚拟DOM渲染视图的React,并使用Flux来驱动。虚拟DOM是指在JavaScript层面使用对象维护一个和浏览器DOM一致的数据结构,然后根据数据创建DOM节点,避免使用HTML模板。

使用虚拟DOM有几个好处:

  • 性能提升,每次数据更新更新对应DOM节点即可,不会大规模造成浏览器重排。
  • 便于数据驱动,因为使用虚拟DOM,视图的渲染由数据结构决定,APP状态的变化可以反映到页面上。
    image8.png

(Redux数据流,Store为APP全局状态)

为了配合React的使用,Facebook使用了Flux作为全局状态管理工具,当然社区使用Redux作为改进型被广泛使用。

使用状态驱动的APP一个最直观的体验就是用户可以自由的前进、后退,每个页面不会因为跳转销毁。因此最开发者而言也拥有了“时间旅行”这一炫酷的能力,即通过切换APP状态切换到APP不同时期的显示效果。这在移动端开发上很常见,但是在传统的MVC、MVVM上很难做到。

无论是Flux和Redux,相对于Angularjs的双向绑定,最显著的区别是数据变成了单向传递的,这就避免了双向绑定时局部状态和APP全局状态冲突的矛盾。

当然Vue生态下的Vuex让这类数据驱动的前端框架更为容易使用,不过在原理上大同小异,不再赘述。

几个为什么

为什么写这篇文章?

思考前端技术选型。每种技术的出现都有特定的背景和解决的问题,如果理解这些框架出现背后的逻辑,对技术选型可以有很好的帮助。举个例子,目前主流的前端技术是React,但是像一些门户网站并不是单页应用,同时有大量的信息展示,这种情况可能jQuery更好用。选择合适的技术比选择流行的技术更为重要,我们不能一说前端开发就是React、VUE。

充分发挥框架的优势。使用一个框架首先需要理解它背后的设计理念,React和Redux可以很好地构建数据驱动的APP。不过如果使用了大量的refs去操作DOM,甚至大量使用jQuery,会对项目带来灾难性的影响。

前后端分离在分离什么?

首先前后端分离的是数据和视图。我们用了React/Vue进行单页面开发,但是却从jsp/php/Aspx等服务器模板中获取数据,数据和视图耦合到一起,即使做足了前端工程实践,这也不是前端分离;由于某些原因,我们不得不多页面开发(例如webview的落地页)使用zepto从专门的API上获取数据并渲染视图,这一样是前后端分离的架构。

其次前后端分离不应该是工程师技术鸿沟。每个从事web开发的工程师都需要了解在HTTP协议两端的工作方式,前后端思想一直在不断融合和碰撞,只有对应用程序整体有一个很好的认识才能做出优秀的设计。

为什么提倡数据驱动视图?

技术服务于业务,在移动端的APP中很多交互需要全局状态管理和避免反复加载。我们想尽可能的让Web APP贴近原生APP,数据驱动可以带来更好地体验和更合理的开发逻辑。

所有的数据都需要放到状态管理吗?

当我在开发React+Redux时,这是最让我头疼的一个问题。DOM像是一棵树,理想的情况下Redux的store数据能一一对应,不过有些视图需要每次进入都要更新,那么如果把这类一次性的数据放入状态管理中是一种负担。如果把路由下的数据比喻成这个树下面的枝丫的话,决定一个下拉菜单是否展开这种状态就是叶子,如果没有特别的需求需要记录这些状态,可以不必把所有的数据放到应用状态中。

jQuery、Backbone、Angularjs等传统框架落后了吗?

jQuery的选择器甚至被内置到浏览器中,Backbone和Angularjs 还有大量的场景需要用到这些框架,当然这些技术由于需求的演变可能用的会越来越少,但尚不能归到落后的类别中去。

more >>

你不是在写前端,而是在编程

从切图开始

在几周以前的一个周末去了趟上海,除了感受了魔都的快节奏生活之外,作为前端的面试官参加了上海分公司的面试Open Day活动。

在Open Day,候选人一天就能拿到Offer,但是需要完成现场作业、结对编程等环节。我们的现场作业题目是模拟操作一个火星车方阵,根据输入的数据,程序来完成放置火星车、左右转、前进等场景。这个题目实现并不难,设计该题目的目的是为了考察候选人程序设计、代码风格和工程化的能力。

很快,陆陆续续有一些候选人完成了现场作业,有的是在Node.js平台上完成,用变量模拟数据的输入,用面向对象的方式给火星车分配了实例,然后操作这些实例完成了任务。还有一些使用了JavaScript文件和一个简单的HTML文件加载到浏览器中运行。随着时间的过去,一个女生迟迟没有提交,时间到后,我上前检查,她编写一个HTML页面,然后使用JavaScript操作dom元素来了移动页面上的"火星车",JavaScript脚本耦合在HTML文档中。这个女生告诉我她以为不能使用JQuery,于是使用原生JavaScript,因此遇到了很多问题以至于不能及时完成。结果可想而知。

这件事给我的触动很大,也为这位候选人没能通过面试感到遗憾。其实我们期待的作业是一个可以正确运行的"程序",无论是面向对象的设计还是函数式的设计,而这位候选人关注在"页面",甚至HTML和JavaScript都没有分离,只有几个响应DOM事件的JavaScript函数。显然这位候选人对我们的题目理解并不一样,那么不同工程师之间的思维差异是怎样的呢。

我想到了 "编程思想",我不算正经的科班生,但犹记得在读书期间像老师请教过关于编程的内容,老师提到最多的就是"编程思想"。在后面的前几年开发生涯中,也在不断思考这个问题,怎么样才能算具有开发思想,怎么去设计程序和组织代码才是最好的。貌似在做过不少项目,上线过很多应用后,也没有得到答案,甚至随着身边的人水平不断增高,这个问题不再被提起。 在前几年,前后端开发还没有分离时,当时的开发模式为前端编写页面、CSS和少量的JavaScript,随后由后端开发者来集成到项目中去。前端不仅备受歧视,并且因为交付产物不是完整的程序,甚至并不被当做"程序员",业内黑话叫 "切页面的"。当时的行情是会使用jQuery或原生JS编写组件的很值钱,否则"页面仔"并不值钱。

programming-thinking.jpg

怎么从前端找到编程思维?

前后端分离是一个非常好的契机,改变了前端的开发模式,前后端分离后的前端项目又叫做单页应用(SPA),这种应用不像传统的页面会发生跳转,页面的跳转由视图的切换代替。

image.png

前后端分离后开发者需要理解的第一件事是:前端应用变成了一个运行在浏览器的完整的程序。JavaScript不再是完成页面上特效的小玩意儿了,需要承担起管理数据、处理用户相应和渲染页面的职责。最早带来这种革命的应用是Gmail, Gmail被认为是第一个富前端应用程序。随后Backbone把MVC思维带到了前端,AngularJS的双向绑定让富前端开发变得非常容易和简单,开启了富前端应用第一个高潮。

前端开发是做一个完整的应用程序的例子是一个代办记事列表应用,这也是前端开发框架往往使用的的经典案例,不亚于Hello world。

马上体验:http://todomvc.com

程序是什么?

我们说要具有编程思维,那我们先说说程序是什么,百科的说法是:

计算机程序又称“计算机软件”,通过指令的顺序,使计算机能按所要求的功能进行精确记述的逻辑方法。

这个定义需要首先理解计算机是什么,然而我们不用想的这么复杂。通俗来说,计算机就像一个听话而行事准确的奴隶,人类只需要给出确切的命令和输入就可以干活,否则就会报错。 前端应用程序和服务器、桌面程序的差异在于他们的运行环境不同。桌面程序可以随意调用系统API,实现几乎所有的功能;服务器程序没有界面,就像《黑客帝国》中被关起来的锁匠一样,暗无天日的解决问题即可;前端应用寄宿在浏览器这个容器中,就像银行柜员一样响应用的需求,传递给后台。

传统程序把数据和页面混合后发送给浏览器,现代的前端应用是把页面和JavaScript发送给浏览器,然后前端接了一个小水管(API)获取数据,然后前端自己加工渲染到页面。于是web前端应用程序越来越像真正的客户端应用了。

建立前端的APP的编程思想

有一道经典的前端面试题是考察浏览器载入页面时的加载顺序,从这个角度出发,页面的加载到用户关闭窗口,就类似于桌面客户端启动和退出。 因为传统的的页面,从加载渲染到用户关闭这个窗口只用了很短的时间,窗口随即被销毁,就像一个一次性的用品,浏览器和HTTP协议帮我们管理了很多东西,因此我们不比考虑数据的管理、内存的分配、代码的组织。

当我们的从多页面开发转向到单页应用时,这些问题就不得不重新思考:

APP的生命周期 在多页面开发时,数据和HTML混合后和css、JavaScript片段被发送到浏览器,HTML和CSS被渲染后JavaScript需要做的只是注册事件,大部分事情都是服务器和浏览器来完成的,JavaScript起到的作用并不是特别大。这种方式造成了用户操作时反复请求、加载、渲染,用户会受到白屏、等待和操作不流畅等糟糕体验。在单页应用时代,HTML\css\js被作为入口打包后发送给浏览器,然后使用JavaScript像普通客户端程序一样启动,开始拉取数据、构建页面、绑定事件后对用户做出响应,用户发生操作再进行拉取数据、重新渲染,页面之间使用DOM视图切换完成,最终用户退出该页面后数据被释放。

数据管理和垃圾回收 前后端分离的本质是HTML/CSS/JS组成的应用程序和数据进行分离,HTML/CSS/JS从web容器中加载,数据从API加载,数据的处理需要JavaScript来完成,这也是为什么最早的MVC框架Backbone强制依赖了集合数据处理库underscore,在React的生态中很多开发者喜欢使用loadash。多页面开发时,页面切换或刷新后,数据和事件会被重置,但现在需要自己管理,直到用户退出前,程序持续运行,因此需要特别考虑一些数据的释放、变量引用指针是否正确、事件是否被重复绑定等问题。

组织代码 在之前,JavaScript只是被当做浏览器的一个简单脚本。几乎没有代码结构而言,大量的行内的代码,好一点的放置到页面底部,或者使用外部JavaScript文件。慢慢的发展出一堆函数和方法集合的库,然后有一些面向对象的实践。因为页面加载一次后就被释放,JavaScript往往是小量的,并且通常每个页面之间用到的独立JavaScript片段构成。单页应用中,应用程序被作为一个整体加载,程序运行的逻辑发生了不小的变化:

  1. 程序应该使用统一的入口来初始化整个应用
  2. 对各个功能组件进行拆分。拆分是为了更好的组合,只有良好的解耦才能最大化的重新组合各个组件
  3. 学习面向对象思想。面向对象可以让组件职责更加清晰
  4. 数据和视图拆分,使用数据驱动视图
  5. 了解设计模式。设计模式是组织大型应用程序代码的"套路",可以自己设计好良好的代码,也可以参考现成的模式。例如当我第一次看到Redux的dispatch实现时,脑子第一想法是这玩意儿和Window上的MFC消息机制非常像。

    总之,单页应用是一个独立的应用程序,而不是"堆砌的意大利面条的代码"。

工程化 既然是独立的应用程序,为什么前端不能工程化。 我们的程序是需要运行在浏览器中,加上可能会使用ES6、Less等非原生的开发工具,就需要进行构建编译然后打包成能在浏览器上运行的程序。 这个过程在C/C++中有make,JAVA中有Maven。而前端的工程化生态比较分散,JavaScript的预处理语言、包管理工具、构建工具都有很多。

前端的工程化需要考虑的有:

  1. 合理的工程结构
  2. 工程构建
  3. 版本管理
  4. 持续集成

写在最后

前端的发展历史正是一个从网页设计到软件设计的过程,在这个过程中,开发者的思维需要发生彻底的变化。前端领域使用的技术越来越和其他平台的客户端贯通,例如Angularjs中的MVVM很大一部分从.net中学习到的;数据和视图分离,面向对象和函数式编程也是前端领域重要的思维;另外一个方面前端也在慢慢的影响其他领域,JavaScript的事件和异步等特性也让java世界发生变化。

我们不一定要成为全栈工程师,但需要拓宽技术眼界然后探究编程世界的内在本质,这样才能在学习和应用新的新的东西时得心应手,快速的在框架、平台、技术栈中切换。
就像在我们认为写AngularJS的时候,其实我们写的是JavaScript;当我们认为在写JavaScript的时,更多的需要意识到我们在编写可以被计算机运行的应用程序。

more >>

javascript 六种基本数据类型转换

 
1、显式转换

通过手动进行类型转换,Javascript提供了以下转型函数:
转换为数值类型:Number(mix)、parseInt(string,radix)、parseFloat(string)
转换为字符串类型:toString(radix)、String(mix)
转换为布尔类型:Boolean(mix)

2、隐身转换

用于检测是否为非数值的函数:isNaN(mix)
递增递减操作符(包括前置和后置)、一元正负符号操作符
加法运算操作符
连接操作符
乘除、减号运算符、取模运算符
逻辑操作符(!、&&、||)
关系操作符(<, >, <=, >=)
相等操作符(==)

3、转换规则


字符串
数字
布尔
数组
对象
null
undefined
字符串
- 如果字符串中只包含数字,则将其转换为十进制(忽略前导0)
如果字符串中包含有效的浮点格式,将其转换为浮点数值(忽略前导0)
如果是空字符串,将其转换为0
如果字符串中包含非以上格式,则将其转换为NaN
非空会转换为true,空字符串会转换为false
单个元素的数组

例子

Object("12")

String {0: "1", 1: "2", length: 2, [[PrimitiveValue]]: "12"}


-

字面量
- 1和0 转换为false,true
单个元素的数组
包装对象

true,false 分别转 "true","false"
true,false 分别转为 1,0

布尔数组
包装对象


数组
使用,隔开的字符串
空数组为0
如果是一个元素是数字,undefined,null,就直接拿出来转换,undefined,null转换为0
否则转换为NaN
恒等为true - 包装对象


对象
使用 toString()
使用 valueof()
恒等为true
对象数组
- - -
null(空对象)
"null"
0 false [null]
- - -
undefined
"undefined"
0 false [undefined]数组
- - -

more >>

写给前端工程师看的运维和架构

前端童鞋接触后端机会可能大多都在API层面,甚至对session这些东西都有些不是很明白原理,当然大牛除外。那么像Linux、负载均衡、并发、热备、云计算,这些时髦的词语可能更多是听得多,如果公司有运维那么更难插手,那么我根据公司实际使用情况试着回答一些网友的问题。

DevOps-cycle-Extended.png

1、公司实际项目中,服务器选择什么操作系统比较好?
在服务器操作系统市场上有window server、linux、unix 三类操作系统。那我们从实际工作中来看,大多数专业公司(除了.net开发者)采用了linux,因为开源、相关软件丰富; 一些很牛的公司比如IBM使用的unix当然不用说了;还有一些公司或者个人使用了window server,因为操作非常简单,使用远程桌面即可管理。

2、负载均衡。
一般公司都会做,是为了解决访问压力,当业务量大到单台服务器无法满足需要的情况下,使用负载均衡技术把流量导入到多个主机上,达到高并发访问的目的。负载均衡可以使用硬件(例如F5)或者软件(普通服务器+LVS软件)实现。如果访问量比较小很多公司直接使用了nginx转发到多台服务器上简单实现。

3、热备。
和上面负载均衡不同,是为了防止单台服务器宕机,然后准备了一套备用机器,这些机器会在宕机的情况下快速启用,并顶上来。实际工作中,大多数公司会使用阿里云这些云服务商提供,并不会自己来实现,已经是很成熟的技术了。

4、nginx
这个应该大家都知道,为啥还要来说,就是很多LAMP 开发者对apache很熟悉,实际上公司Nginx 用的更多,因为不仅仅作为服务器来使用,更多作为反向代理来使用。 举个例子:Nginx 启动后监听80端口然后对外提供http服务,那么如果系统有nodejs服务为9000,就可以使用 nginx 把业务转发到9000端口,由nodejs提供服务。这样可以实现在系统中多个应用共存,也是公司普遍的做法。

5、主从数据库
我们自己开发的时候,很多都是程序运行和数据库在一台机器上,那么当使用了多台应用服务器的时候,那就把数据库拿出来单独放到一台服务器上。因为这样才能让多台机器上应用程序使用同一个数据库。很多公司单独一台数据库服务器性能不够使用,配置主从数据库服务器来做读写分离,从主机写入数据保证数据一致,从丛机读取数据保证速度,这种优化方案在公司非常普遍,但是具体要看具体应用的读写比。

more >>

backbone移动webapp框架(backbone-mobile)发布

backbone-mobile

工程化构建高性能移动webAPP项目骨架。使用backbone、requirejs、amazeui等项目构建

本项目解决的问题:

  1. 足够好的性能
  2. 多人协同开发和统一的开发方式
  3. 包管理和打包发布
  4. 可维护性
  5. 集成UI套件以及页面过渡动画
  6. 资源按需加载和预加载

demo 预览

http://linksgo2011.github.io/backbone-mobile/

为什么需要 backbone-mobile

自己从事纯前端工作一年多而已,但是却遇到了很多的webapp项目需求。尝试过使用很多框架以及现成的解决方案,这些方案非常不错,但是对于自己项目来说
借鉴意义大于拿来自己用。我把学习过的相关项目罗列出来细说12

1、https://github.com/zhangxinxu/mobilebone 张鑫旭大牛的过渡动画骨架。这个是最先吸引我的一个东西,理念很好但是并没有用于项目。
原因是这个项目重点在于过场动画以及切换方式,需要配合其他框架使用,需要添加路由、包管理、模型、控制器等等。还有一个项目官网
http://www.mobilebone.org/

2、https://github.com/yexiaochai/blade 叶小钗的blade框架。也有过渡动画效果,加入了requirejs,比较有特色的地方是使用了web组件。
对于小型小项目还是够用,带有简单的UI,同样需要路由、包管理、模型、控制器等特性。

3、https://github.com/driftyco/ionic ionic框架。非常强大的框架。使用angularjs作为路由、控制器、模型的实现。带有一套全面的UI框架,
同时可以打包成APP,非常方便。不好的地方就是需要学习angularjs,这个问题不是太大,angular在移动端有个致命的问题,就是双向绑定造成的性能
低下。同时也需要抛弃jquery生态系统。

4、https://github.com/shixy/Jingle 和blade类似,也是比较完善的框架,同样存在无法工程化的问题。

backbone-mobile 工程化思路

使用backbone + require 做项目骨架,拓展出controller 划分 controller、model、view、templetes目录来进行协作开发。

如何使用

首先我们说下目录项目的目录结构及其功能

├─css
├─fonts
├─img
└─js
    ├─controller       控制相关代码
    ├─lib              依赖的第三方库
    │  ├─backbone
    │  │  └─plugins
    │  ├─jquery
    │  │  └─plugins
    │  ├─modernizr
    │  ├─require
    │  │  └─plugins
    │  ├─underscore
    │  └─vsf
    │      ├─log
    │      └─store
    ├─template      模板文件,默认使用的html
    │  ├─Layzload   每个控制器推荐对应一个目录
    │  ├─Pages
    │  └─Preload
    └─view          视图目录

可以看出我们依赖 backbone、requirejs 因此需要学习相关文档

下载本项目

 git clone https://github.com/linksgo2011/backbone-mobile.git

需要将项目放到web服务器目录下,这里约定使用web根目录,在编辑器中打开后,我们来说明如何开始开发。我们想增加一个 http://localhost/#/Users/update/9
页面。我们需要怎么做呢?

在我们的项目中拓展backbone 增加了路由中的controller以及,因此约定路由 按照

    #/controller/action/params
 

格式来书写。

在template 目录下创建 Users 目录,添加一个 update.html 模板文件

 <div class="page" id="users-update-page" >
    这是更新user的模板
 </div>

  

为了展示页面过场效果,最外层div标签必须按照这个格式书写,并且每个html页面的ID全局唯一,避免页面切换时冲突

接下来,我们在controller 目录下创建
UsersController.js

/**
 */
define(
    [
        'jquery',
        'underscore',
        'backbone',
        'controller/MobileController',
        'view/BaseMobileView',
        'ui',
        'text!../template/Users/update.html'
    ],
    function($, _, Backbone, MobileController, BaseMobileView, ui, update) {
        return MobileController.extend({
            // 批量初始化
            initialize: function() {
                var views = {
                    updateView: update
                };
                $.each(views, $.proxy(function(key,tpl){
                    this[key] = new BaseMobileView({
                        $container: $('body'),
                        appendable: true,
                        controller: this,
                        textTemplate: tpl
                    });
                },this));
            },
            update: function() {
                // TODO

                this.updateView.render({});

                // 给页面绑定自己的事件,因为每次render都会重新渲染页面,不必担心事件重复
                this.updateView.$el.on("click", function(event) {
                    $.alert("你点击了页面!");
                });

            },
        });
    }
);
 

这样路由中的controller对应这文件,而第二段对应了文件中的update方法,当url第一次被请求时,控制器的initialize 方法会被执行。
然后每次请求都会执行update方法。

我们可以在init方法中初始化view,然后在update方法中编写我们的业务逻辑

这样你就可以根据路由划分功能和业务逻辑,更好的协作开发了。如果做过后端开发例如PHP、JAVA MVC相关的应该会很快理解这样编写代码的好处。

项目打包

使用requirejs开发的项目可以直接被打包成一个文件,提高加载效率,使用非常方便,项目根目录下放置了一个gruntfile文件,可以阅读该文件,使用nodejs相关模块打包即可。

配合后端API

移步nodecms项目 https://github.com/linksgo2011/nodecms

最后说几句

这个项目不算自己完成,只能算是整合网络上一些代码然后修改整理了并在实际项目中使用过。
下面我罗列出项目中使用的代码来源

  1. backbone http://backbone.org.au/
  2. controller https://github.com/hoatle/mobile-webapp-template
  3. jquery http://jquery.com/
  4. unnderscode http://underscorejs.org/
  5. requeire http://www.requirejs.org/
  6. amazeui http://amazeui.org

    疏漏之处请指正,谢谢!

    贡献代码或者问题意见联系 120377843@qq.com

more >>

写写原生JavaScript系列(5)-实现选择祖先元素closest()方法

匹配DOM上级元素,实现closest()方法

从包含自己以及自己的祖先元素中选择出符合条件的元素,closest()类似于jQuery的$.closest() 方法,是一个在现代浏览器中内置支持的一个操作元素的方法,比如在火狐中。

// matches polyfill
this.Element && function(ElementPrototype) {
    ElementPrototype.matches = ElementPrototype.matches ||
    ElementPrototype.matchesSelector ||
    ElementPrototype.webkitMatchesSelector ||
    ElementPrototype.msMatchesSelector ||
    function(selector) {
        var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1;
        while (nodes[++i] && nodes[i] != node);
        return !!nodes[i];
    }
}(Element.prototype);

// closest polyfill
this.Element && function(ElementPrototype) {
    ElementPrototype.closest = ElementPrototype.closest ||
    function(selector) {
        var el = this;
        while (el.matches && !el.matches(selector)) el = el.parentNode;
        return el.matches ? el : null;
    }
}(Element.prototype);

由于在现代浏览器中内置支持了这些元素,因此我们为了获得更好的性能,我们建议在原生对象上进行拓展,就可以像下面这样直接使用:

var el = document.querySelector('span');
console.log(el.closest('div'));

原文出处

https://plainjs.com/javascript/traversing/match-element-selector-52/

more >>

写写原生JavaScript系列(4)-获取父节点、兄弟节点

元素节点操作

获取节点父元素,直接使用一个兼容各个浏览器的方法

var el = document.querySelector('div');
var parent = el.parentNode;

获取下一个、前一个或者所有的兄弟元素并且根据选择器过滤
获取兄弟元素并过滤

function getSiblings(el, filter) {
    var siblings = [];
    el = el.parentNode.firstChild;
    do { if (!filter || filter(el)) siblings.push(el); } while (el = el.nextSibling);
    return siblings;
}

// example filter function
function exampleFilter(el) {
    return elem.nodeName.toLowerCase() == 'a';
}

用法

el = document.querySelector('div');
// get all siblings of el
var sibs = getSiblings(el);
// get only anchor element siblings of el
var sibs_a = getSiblings(el, exampleFilter);

更快速的获取前一个或者下一个兄弟节点的方法

var previous = el.previousSibling;
var next = el.nextSibling;
Get all following siblings of an element, optionally filtered:

接受一个过滤条件参数的俄获取后面兄弟节点的方法

function getNextSiblings(el, filter) {
    var siblings = [];
    while (el= el.nextSibling) { if (!filter || filter(el)) siblings.push(el); }
    return siblings;
}

接受一个过滤条件参数的俄获取前面兄弟节点的方法

function getPreviousSiblings(el, filter) {
    var siblings = [];
    while (el = el.previousSibling) { if (!filter || filter(el)) siblings.push(el); }
    return siblings;
}

原文出处

https://plainjs.com/javascript/traversing/match-element-selector-52/

more >>

写写原生JavaScript系列(3)-判断当前元素匹配的CSS选择器

为了检查匹配的元素是否符合某个css选择器,现代浏览器提供了一个matches()、matchesSelector()方法,因此我们来写一个通用的方法,检查元素是否匹配某个css选择器。

    // matches polyfill
    this.Element && function(ElementPrototype) {
        ElementPrototype.matches = ElementPrototype.matches ||
        ElementPrototype.matchesSelector ||
        ElementPrototype.webkitMatchesSelector ||
        ElementPrototype.msMatchesSelector ||
        function(selector) {
            var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1;
            while (nodes[++i] && nodes[i] != node);
            return !!nodes[i];
        }
    }(Element.prototype);

因为现代浏览器提供了一些DOM4级别的内置方法,因此为了让其他浏览器支持,我们写出了通用的方法,但是为了速度更快,推荐使用拓展内置对象的方式实现。

给一个使用的栗子:

    var el = document.querySelector('span');
    console.log(el.matches('.foo'));

原文出处

https://plainjs.com/javascript/traversing/match-element-selector-52/

more >>

写写原生JavaScript系列(2)-根据类名选择元素

getElementsByClassName()方法可以在浏览器中快速获取DOM节点,但是需要注意不能在IE8及以下版本浏览器使用该方法。

var list = document.getElementsByClassName('foo');

// get the number of selected elements
console.log(listlength);

// iterate over elements and output their HTML content
for (var i=0; i<list.length; i++)
console.log(list[i].innerHTML);

配合getElementById() 使用,可以快速从一个元素容器中获取一个元素节点数组

var container = document.getElementById('header');
var list = container.getElementByClassName('foo');

这个方法较为性能较好,但是却只能使用class来选择元素,因此限制了它的用处,上一节说的querySelectorAll()则可以根据CSS选择器来选择元素,因此更加方便。

原文

https://plainjs.com/javascript/selecting/select-elements-by-class-name-3/

more >>

基于nodejs的CMS系统

项目地址

https://github.com/linksgo2011/nodecms

介绍

学习nodejs入门项目,数据库使用的MySQL。
使用nodejs 开发定位于企业网站的简易CMS,目标为容易拓展、部署,前端开发者容易使用的CMS系统。

特性

  • 使用nodejs 开发,为前端开发者准备的,前后端都使用JS,做个企业网站就很容易了
  • 简单(其实是懒),主要是首页、根据数据模型的列表页、详情页,单页,满足一般企业站点需要
  • 容易拓展,功能不满足可以容易拓展
  • 多模板
  • 支持不同模型列表、详情指定模板,更加灵活嵌入单页

安装

  • 使用 克隆或直接下载项目到本地

git checkout https://github.com/linksgo2011/nodecms.git

  • 在根目录下使用命令行执行下面代码,安装nodejs 依赖的模块

npm install

  • 在根目录下找到nodecms.sql文件,导入到你的MySQL数据库中

  • 修改config/connections.js 文件,根据上一步的数据库信息,修改数据库连接

修改数据库连接地址
someMysqlServer: {
host: 'localhost',
user: 'root',
password: '',
database: 'nodecms'
},

  • 启动项目

node app.js

预览

demo 地址 http://nodecms.duapp.com/

后台地址 /admin/user/login

默认用户 admin admin

贡献代码

120377843@qq.com

more >>

写写原生JavaScript系列(1)-使用CSS选择器选择元素

PS:基础决定一个人是否能够在技术路线上走的更远,不积跬步,无以至千里,越来越多的公司看中员工技术基础。那么我们在享受jQuery极大便利的时候,也需要记得返璞归真看看原生的JavaScript 能给我带来些什么,有些时候是有好处的例如移动端可以减少额外的库的加载。偶然发现 plainjs.com 的一系列文章,感觉非常不错,跟着学习的同时搬运到博客,不足之处还望指正。

querySelectorAll() 是一个原生javascript (需要注意兼容性)提供的一个类似jquery选择器的方法。

querySelectorAll() 方法根据你传入的选择字符串返回一组匹配的DOM元素列表。
如果你使用过JQuery操作过DOM元素,你可能对下面的写法不会感到意外:

var matches = document.querySelectorAll('div.foo');

for (i=0; i<matches.length; i++)
    console.log(matches[i].innerHTML);

more >>