有线网络自动掉线正常使用,无线网络经常掉线,无互联网连接。

应用被强杀了怎么办
来源:博客园
应用在后台运行时很容易被强杀,这很正常,但是回到前台时,很容易出现空指针的情况。怎么解决这样的问题,且看看Stay的见解。 我们先跳出来看看android的app运行原理。 app在后台被强杀,是在内存不足的情况下被强制释放了,也有一些恶心的rom会强制杀掉那些后台进程以释放缓存以提高所谓的用户体验。 我们都觉得android rom很恶心,但同时还是用些更恶心的手法去绕开这些瓶颈。乱,是因为在最上层没有一个很好的约束,这也是开源的弊端。anyway。我们还是得想破脑袋来解决这些问题,否则饭碗就没了。 我们先来重现这个bug: 假设: App A -> B -> C -> D 在D activity中点Home键后台运行,打开ddms,选中该App进程,强杀。 然后从“最近打开的应用”中选中该App,回到的界面是D activity,假设App中没有静态变量,这个时候是不会crash的,点击返回到C,这个时候也只是短暂黑屏后显示C界面。但如果C中有引用静态变量,并想要获取静态变量中的某个值时,就NullPointer了。 以上复现的流程就几个点,我们展开说下: 1. 当应用被强杀,整个App进程都是被杀掉了,所有变量全都被清空了。包括Application实例。更别提那些静态变量了。 2. 虽然变量被清空了,但Android给了一些补救措施。activity栈没有被清空,也就是说A -> B -> C -> D这个栈还保存了,只是ABCD这几个activity实例没有了。所以回到App时,显示的还是D页面 3. 另外当activity被强杀时,系统会调用onSaveInstance去让你保存一些变量,但我个人觉得面对海量的静态变量,这个根本不够用。 4. 返回到C会黑屏,是因为C要重绘,重走onCreate流程,渲染上需要点时间,所以会黑屏。 大概是以上这些点。如果App中没有静态变量的引用,那就不用出现NullPointer这个crash,也就不需要解决。一旦你有静态变量,或者有些Application的全局变量,那就很危险了。比如登录状态,user profile等等。这些值都是空了。 肯定会有人说,这没关系啊,所有的静态变量都改到单例去不就好了吗?然后附加上一些持久化cache,空了再取缓存就ok了嘛。嗯,这肯定也是一个办法,但是这样的束手束脚对开发来说也是痛苦,至少需要多30%的编码时间才能全部cover。另外,还有那么多帮你挖坑的队友,难省心啊。 既然App都被强杀了,干嘛不重新走第一次启动的流程呢,别让App回到D而是启动A,这样所有的变量都是按正常的流程去初始化,也就不会空指针了,对吧?有人说这方案用户体验一点都不好呀。但哪有十全十美的事呢,是重走流程好,还是一点一个NullPointer好?好好去沟通,相信产品也不会为难你的。当然你也可以拿iOS来举例,iOS在最近打开的应用里杀了某个App,重新点击那个App,还是会重走流程的啊。 如果你接受我的这个解决方案,那且想想如何让它不回到D而是重走流程呢?也就是说中断D的初始化而回到A,并且按back键,不会回到D,C,B。考虑一下。 我们先实例化这个场景吧。 A 为App的启动页 B 为登录页 C 为首页 D 为二级页面 简单说下解决方案,剩下的自己思考。 1. 把首页launchMode设置为singleTask,具体为什么我就不说了,自己google。 2. 在BaseActivity中onCreate中判断App是否被强杀,强杀就不往下走,直接重走App流程。 3. 首页起一个承接或者中转的作用,所有跨级跳转都需要通过首页来完成。 再给个提示,以上场景的解决方案也可以用于解决其它相关问题: 1. 在任意页面退出App 2. 在任意页面返回到首页 3. 在任意页面注销或者token失效回到登录页 其实最重要的知识点就是launchMode,很多人面试的时候都能背出来,甚至是原理。但真正会合理应用它们的少之又少。有的时候,技术的优劣体现于此。生搬硬套肯定是站不到最高点的。 题外话: 当我第一次碰到这种问题的时候就在想,为啥Android非得这么来实现,既然都已经把应用强杀了,为什么还把栈信息保存下来了。既然把栈信息保存下来,为什么不把整个App变量都cache到硬盘上呢。这样还能节省ram,每个当前运行的App分到的最大内存也不用再加限制了啊。这样的话Bitmap的OOM也很难发生了。多好。好吧,iOS的内存管理貌似就是这样的机制(我这是白话文,勿较真) 有很多bug都是系统级的限制,虽说没有解决不了的技术,但是偏要钻牛角尖偏要用自以为的方式去解决问题,那么就是坑自己,并且也坑了队友。 做技术越久,越能感受到,难做的不是技术,而是业务。如何理解业务,以及背后的需求本质,是开发中最最重要的事情。与其盲目的拿需求就开始写代码,不如花上些时间去理解需求。弄清前因后果,想好封装与扩展。这些是对思维逻辑的锻炼,并且也是技术提升最快的方式。 有心课堂,传递给你的不仅仅是技术。?
免责声明:本站部分内容、图片、文字、视频等来自于互联网,仅供大家学习与交流。相关内容如涉嫌侵犯您的知识产权或其他合法权益,请向本站发送有效通知,我们会及时处理。反馈邮箱&&&&。
学生服务号
在线咨询,奖学金返现,名师点评,等你来互动React.js在Codecademy中的实际应用
来源:open开发经验库
2014年八月,Codecademy为了更新用户的学习体验,决定采用Facebook的React.js库,这是一个用于编写 JavaScript UI的类库。在开始阶段,我们的问题是很难找到React在复杂应用中应用的示例,例如像Codecademy这样复杂的应用。因为大多数的教程都是针对 小型示例应用上的特性,而不是针对在开发大型应用中更常见的问题。在本文中,我不仅会对React的使用进行一番概述,还会特别说明在大型web应用程序 中使用React的某些特别注意事项。 React是什么? 
简单来说:React是一个使用JavaScript创建用户界面的代码库。与编写用户界面常见的方式不同,React将每个UI元素视为一个抑制 的状态机。它并不是类似于AngularJS这样的“框架”。虽然Facebook有时会将React描述为“MVC中的V”,但我发觉这一描述并没有什 么帮助,因为React应用并不需要遵守MVC模型。React能够帮助你创建快速的用户界面,处理复杂的交互,而无需编写大量糟糕的代码。 
如果你打算在工作中使用React,你需要了解以下特性: 
React会为你处理DOM。 
DOM操作的开销很大,而React的吸引力很大程度上来自于它对这一问题的处理方式。React通过对自身虚拟DOM的维护,只在需要时进行重新渲染,将DOM操作的数量降至了最低,这要归功于React中高性能的比较操作的实现。 
这就意味着你很少会需要直接与DOM打交道,与之相反,React会替你处理DOM的操作。这一特性也是诸多React设计的基础。如果你打算滥用React的API,或是打算按照自己的方式进行改动,那有可能会影响到React对DOM的理解。 
该特性也使得使用Node.js进行内置的服务端渲染成为可能,这一点就使你能够轻易地创建对于SEO友好的页面。 
React使用声明式风格以及组件。 
在React中,所有的组件都必须继承自Component类。组件中包含了属性(由父类决定)和状态(能够自行改变,通常是基于用户行为进行改变),组件的渲染和行为应当完全由它们的状态和属性所决定(而不依赖于任何其它值),因此组件就是状态机。这一模型鼓励使用者创建模块化的UI,并且在实践中能够简化UI的操作与创建工作。 
React将标记紧密地结合在JavaScript中。 
虽然在JavaScript中编写HTML代码听起来很奇怪,但在React中这是一种自然的选择。JSX是原生的JS与HTML标记相结合的一种 语言,对于它的使用是可选的,但我强烈建议你选择这种方式。React认为,由于你的标记已经紧密地结合在控制这些标记的JavaScript中,因此可 以将它们安置在同一个文件中,我也同意这一看法。 
单向信息流动。 
这一点更多的是一种通用的React模式,而不是一种严格的规则。信息的流动在React中倾向于单向流动。在本文的稍后部分中,当我们开始考虑在大型应用程序中如何处理信息流动时,会再次提及这一模式。 
解析React应用程序 
为了让这些原则显得更为清晰,让我们看看Codecademy的学习环境是如何使用React进行构建的。 
(单击图片以放大) 
图1:学习环境。 
正如你在这个屏幕截图中所看到的一样,主要的学习环境由多个不同的UI元素所组成。某些元素是始终展现在页面上的,例如header、menu和 navigation。而有些组件会根据当前的练习的不同,处于显示或是不显示的状态。比方如,根据课程的不同,web浏览器、命令行和代码编辑器可能会 进行混合或是匹配。 
创建该界面的逻辑解决方案是为各个部分创建React组件。在下面的屏幕截图中,我将特别指出主要的React组件: 
(单击图片以放大) 
图2:学习环境以及对应的组件。 
每个组件都有可能包含多个子组件:比方如,屏幕左方的Lesson面板实际上就包含了多个组件: 
(单击图片以放大) 
图3:组成Lesson组件的各个子组件。 
在这个示例中,我们将使用React以决定哪些组件应该显示在该lesson面板中。举例来说: 
只有在用户已登录的前提下,才会显示“Report a Problem”按钮 
只有在该练习中包括测试的情况下,才会显示Instructions部分 
此外,React会处理该组件与其它组件之间的信息流动。整个学习环境对应着一个父组件,该组件会持续跟踪多个状态,例如当前用户处于哪个练习中。父组件会为子组件的属性进行相应的赋值,以决定这些子组件如何显示。 
现在,让我们来看一个组件通信的示例,以下组件来自经过大量简化后的组件树结构: 
LearningEnvironment 
CodeEditor 
RunButton 
ErrorDisplayer 
Navigation 
(单击图片以放大) 
图4:与代码提交相关的某些组件。 
如果某个用户打算运行他的代码,我们该如何处理这一工作流?我们将尝试对他们提交的代码进行测试,然后显示错误信息,或是允许用户继续下一题。以下是某个可能发生的工作流: 
当用户单击RunButton之后,该组件会在事件的调用中,通过回调方式通知它的父组件CodeEditor。 
CodeEditor组件会通过另一个回调函数通知它的父组件,即Learning Environment组件,并将用户的当前代码传递给父组件。 
LearningEnvironment组件将针对用户的代码进行测试。 
根据结果的不同…… 
LearningEnvironment组件会对CodeEditor中的属性errorMessage赋值,CodeEditor则会依次为它的子组件ErrorDisplayer中的属性errorMessage赋值。 
如果用户已经完成了该练习的所有测试,LearningEnvironment组件就会为Navigation组件中的属性progress赋值。 
如果我们的组件都能够像在LearningEnvironment组件中的render方法一样进行声明(同样进行了大量简化),那么就可以通过一个单一的函数调用实现整个UI的更新: 
render: function() {
return(
&div&
&CodeEditor
error={this.state.error}
&Navigation
mayProceed={this.state.mayProceed}
&/div&);
} 请记住,React中混合了JavaScript和HTML标记。在这个例子中,render方法定义了一个LearningEnvironment组件,其中包括了一个CodeEditor组件和一个Navigation组件。 
我们可以更新LearningEnvironment组件的状态,它会触发组件的重绘,并在必要时更新子组件。 
handleTestResult: function(currentIndex, passing, error) {
this.setState({
error: error,
mayProceed: passing && currentIndex === this.state.currentExercise.tests.length-1
} 这就是全部的代码了。React以一种优雅而简单的方式替我们处理UI的更新操作。 
大型应用程序中的考虑因素 
信息流动 
正如我之前所说的一样,React不一定要遵循MVC模型,实际上,你可以按照任何你喜欢的方式处理信息流动,但你需要一种确切的信息策略。为了让 属性的变化传递给子-子-子-子-子组件,你是否需要将该属性一路传递下去,哪怕中间的那些子组件完全不需要了解该属性?如果该叶子节点接受用户输入,它 又该如何将这一变更通知它的父-父-父-父组件呢? 
在大型应用程序中,这种处理方式是很令人受挫的。即使是在以上那个简单的示例中,Run按钮该如何与LearningEnvironment组件之间传递用户的行为呢?我们需要传递回调函数,但这种方式很难写出真正模块化、可重用的组件。 
Codecademy的解决方案是通过创建通信适配器(Adapter),以管理各别组件间的信息流动。与传递回调函数的方式不同,高层次的组件, 例如CodeEditor会接收到一个Adapter,它为重要的通信任务提供了一种单一的接口。举例来说,当CodeEditor处于显示状态 时,LearningEnvironment会创建一个Adapter,它能够生成和处理与用户提交代码相关的事件。 
这种方式也不是完全没有缺陷的,我也在的演讲中针对这一点进行了详细的论述。我的主要观点在于,无论你如何处理组件树中的信息流动,你的团队都应该坚持一种一致的策略。 
整合 
React的上手非常简单,但要在你的工作流中高效地使用它,你需要一些工具的支持。举例来说,我们使用了以下工具: 
用一段脚本对.jsx文件的本地文化进行监控,并在必要时对它们进行重新编译 
一个独立的node.js服务器,用于处理服务端的展示 
用于在需要时自动生成新组件文件的开发者工具 
以上这些工具都不是非常复杂。对于.jsx的监控来说,Gulp是一个很好的选择,不过我们选择了使用Go语言自行编写脚本。我们使用了一个简单的 批处理脚本负责生成新的组件文件,这种方式也能够确保命名规范。如果你打算使用一个node.js服务器以负责服务端展示,你需要当心的是,要强制 require.js能够获取到React代码中的变更可能会有些困难,因此我们创建了一个监控器,让它在必要时重启node服务器。 
为什么使用React? 
在我们重新设计整个学习环境时,我们需要决定选择使用哪一套工具或框架。我们最终选择了React,对这一决定我们感到非常满意。(关于我们如何选择一套JavaScript框架的详细过程,可以在以下演讲视频中找到:) 
我们对于React的欣赏之处主要在于以下几个方面: 
它经过了实战检验 
React已经在Facebook和Instagram的生产环境中得到应用,因此我们对于它的性能和可靠性很有信心。目前为止,它在我们的平台上同样表现良好,我们也没有遇到过任何严重的问题。 
组件化的方式便于理解。 
React对每个独立的组件进行单独处理,这些组件会按照它内部的状态进行展现,因此对于某一时刻应该发生什么事,很容易形成概念化的理解。你的应 用程序会有效地成为一个大型状态机。这意味着你可以单独测试UI中的每个片段,同样可以自由地添加新组件,而无需担心会影响整个应用程序中其它部分的代 码。 
SEO非常容易实现。 
因为React本身就支持服务端展现,因此在搜索引擎看来,你提供的是一个基本已完成的页面,这对于SEO来说是一个极大的优势,而所需的工作量非 常小。的确,这一点必需由Node完成。由于Codecademy的主应用是由Rails编写的,因此我们搭建了一个独立的Node服务器,专门用于处理 React的展现。 
React能够兼容遗留代码,并且它的灵活性足以应对未来。 
虽然采用一整套框架的确是一件大事,但你也可以慢慢地尝试将React添加到现有的代码库中。与之类似,如果将来我们需要移除React,我们也可 以轻易地实现这一点。在Codecademy,我们首先决定完全使用React来编写一个全新的项目,以便尝试它的功能,并学习如何以最佳的方式使用它。 这个项目很成功,因此我们现在基本上在所有的新UI元素中都使用React了。我建议你首先做些功课,创建一些实验项目,然后再考虑怎样让React适应 于你的现有代码库。 
不必担心编写样板代码了。 
在编写样板代码上所花的时间越少,就意味着你可以将更多的时间花在更有意义的问题上了。从这个角度上来说,React是个既简洁又轻量级的类库。以下代码是创建一个新的组件所需的最少代码: 
var dummyComponent = React.createClass({
render: function() {
return (&div&HTML markup in JS, what fun!&/div&);
}); 简短且切题,还有什么不满意的? 
我们的社区正在成长 
React社区的发展非常迅速。当你遇到各种问题时,你可以和许多社区成员讨论这一问题。并且,由于许多公司都已经在生产环境中使用了React(仅举几例,Facebook、Instagram、Yahoo!、Github和Netflix),因此我们并不独孤。 
总结 
React是一个轻量级、强大,并且经过实战检验的使用JavaScript创建用户界面的类库。它不是一个框架,而是一个强大的工具,或许会改变 你进行前端开发的方式。我们认为它对于我们的前端开发来说,作用之大是难以置信的,而我们对于自己的选择也感到相当满意。对我自己来说,使用React进 行工作至少是极大地影响了我思考编写用户界面的方式。我也乐于看到React的不断成长:现在Facebook已经通过将React的功能带到移动开发上了,我想它的未来一定会是一片光明。 
如果你打算上手使用React,它的是一个不错的逻辑起点。互联网上也有着大量介绍React中的关键概念的帖子(是我最爱的教程之一)。不要停下脚步,学习钻研,尝试着创建些什么,然后看看你对于React这种前端开发方式是怎么想的。我非常乐于聆听你的想法,请将你的想法发送至我的Twitter帐号。 
关于作者 
Bonnie Eisenman是一位来自于</的软件工程师。她最近刚刚从普林斯顿大学的计算机科学专业毕业。她对硬件也有一定兴趣,在业余时间喜欢从事一些Arduino方面的工作,以及乐曲编辑。她的Twitter帐号是@brindelle。 &#13;
自从HTML5变得流行以来,整个Web平台取得了长足的进步,人们也开始将JavaScript视为一门能够创建复杂应用的语言。许多新的API纷纷浮现,而关于浏览器如何应用这些技术的文章也大量涌现。 &#13;
这一系列文章的视角更进一步,它们将关注于如何在实践中应用这些强大的技术,这并不是指创建多么酷炫的示例和原型,而是在第一线进行实际应用。在这个(后)HTML5系列文章中,我们不需要响亮的口号,而是基于行业专家的实际经验,获得实践性的见解。我们也将讨论那些更进一步的技术(例如AngularJS),并对web标准和web开发的未来进行定义。 &#13;
查看英文原文: &#13;
来自:q.com/cn/articles/reactjs-codecademy&#13;
免责声明:本站部分内容、图片、文字、视频等来自于互联网,仅供大家学习与交流。相关内容如涉嫌侵犯您的知识产权或其他合法权益,请向本站发送有效通知,我们会及时处理。反馈邮箱&&&&。
学生服务号
在线咨询,奖学金返现,名师点评,等你来互动在开发强大的Web应用程序领域,React.js无疑可算当之无愧的赢家。首先,最令人振奋的就是它为开发人员带来了多年以来在Web开发框架当中始终无法找到的各类要素。很多Web开发人员之所以对其青眼有加,正是因为它能够帮助我们开发出不同于以往且极具交互特性的Web应用。它几乎无所不包,因此我们也应当对其加以关注,但利用它在未来的开发工作当中打造出更为出色的成果。
关于React.js,首先需要留意的就是它在默认情况下允许大家保证动态站点能够始终拥有强大的稳定性,这意味着我们能够充分运用各类灵活的交互设计效果而不必担心给站点运行带来影响。这绝对是一项伟大的壮举,事实上我们甚至很难在利用React.js进行应用程序开发时造成任何故障。
在利用React.js开发强大Web应用时,另外还有几项提示必须加以强调——这些提示绝对值得一试而且极具实用性,特别是对于那些刚刚上手这套框架的朋友来说。它们将帮助大家更好地处理未来开发工作中可能面对的各种问题。再有,大家还能够以远超想象的速度实现应用开发。
如果大家打算利用React.js创建一款应用程序,那么各位无需完成整套工具设置即可在自己的浏览器当中轻松便捷地搞定原型设计。是的,其实际操作过程与我们的表述同样简单。举例来说,如果大家决定利用可选JSX语法自行编写组件,其编写方式以及代码内容看起来几乎与HTML如出一辙。
作为开发第一步,我们首先需要制作一份简单的文档,其中同时包含React.js与JSX转换工具。以下列代码作为范例:
&script src="http://fb.me/react-0.13.0.js"&&/script&
&script src="http://fb.me/JSXTransformer-0.13.0.js"&&/script&
&script type="text/jsx"&
/** @jsx React.DOM */
React.js当中的应用程序必须通过已经在层级结构当中布置完成的组件加以构成。如果大家希望在开发工作当中轻松使用应用程序的每个组成部分,那么必须首先拿出时间弄清楚其在层级结构中的具体作用并以此为基础勾勒应用原型。这意味着,每个组件都负责解决一项特定任务。而在某些复杂组件当中,我们还需要将其拆分成数个简单组件,从而确保一次只解决一个问题。这也是我们充分发挥React.js强大能力的必要前提。
属性与状态
React.js当中的数据主要分为两种类型:
属性:这类数据会在不同组件之间往来传递
状态:这类数据会始终被保存在某组件当中
组件的属性(即往来于不同组件间的信息)不可修改与变更,但组件的状态却能够随时加以调整(即组件内部的信息)。这代表着React.js中的一切都具备与之对应的真实源。
因此,当我们利用React.js创建一款应用程序时,必须要在Web应用开发中做出一项决策——各组件拥有怎样的数据,这些数据的主来源又是什么。一旦解决了这个问题,大家就能够轻松完成应用创建的其它工作。
在这种情况下,我们只需要考量三种数据类型:
用户输入数据
具体来参考以下示意图:
其中网络数据将由网络及线路组件所获取。其通常代表着大量数据,而且为了不影响应用的运行速度,大家需要在外部对其加以处理,而后再把结果交付至我们创建的应用。
组件通信机制
在这里,数据被设计为自上而下贯穿整个组件结构,但大家有时候也需要以自下而上的方式逆向交付数据以实现应用程序交互性。在这种情况下,我们就需要利用特定的技术手段实现这种“数据逆流”。下面来看几种实现此类目标的方式:
大家可以选择使用回调属性的方式,这是最理想也最简单的解决方案,因为此时组件只需要同其直接上游对象进行数据共享。React.js能够自动对每个实例者组件方法绑定,因此维护工作不会占用我们大量精力。下面来看具体示例:
var Child = React.createClass({
render: function() {
如果大家希望实现的是其它抵达通知机制,那么可以利用单一系统实现发布/订阅。这种方式非常灵活而且同样易于维护。只需使用PubSubJS这类库,大家就能够随意对某一组件的生命周期方法进行绑定与解绑。
相关代码示例如下:
var Parent = React.createClass({
handleMyEvent: function(e) {...},
componentWillMount: function() {
window.addEventListener("my-event", this.handleMyEvent, false);
componentWillUnmount: function() {
window.removeEventListener("my-event", this.handleMyEvent, false);
render: function() {...}
var Grandchild = React.createClass({
handleClick: function(e) {
var customEvent = new CustomEvent("my-event",
detail: { ... },
bubbles: true
React.findDOMNode(this.refs.link).dispatchEvent(customEvent);
render: function() {
组件生命周期
组件永远拥有着与其API紧密关联的生命周期。在这种情况下,其生命周期包括启动、更新与卸载三种状态。而这些功能已经被内置在组件的定义当中。举例来说:
componentWillMount与componentWillUnmount 方法都被用于添加或者移除事件侦听机制。当然还有其它多种方法能够帮助我们实现对组件状态及属性的控制。
一旦我们建立起一套浏览器内运行环境,接下来就可以将UI方案拆分为多个简单组件。接下来的任务是弄清应用程序运行需要具备哪些数据,这些数据将处于何种位置且如何与应用进行共享。当这些问题得到解决,大家将能够获得可进行试用体验的已创建应用。
利用React.js,我们能够非常轻松地开发出强大且稳定的Web应用程序。这主要是因为大家需要使用的全部功能都能够由该框架自行提供,而且其在初始设计之时就充分考虑到创建高复杂性应用程序的种种需要。
在文章中找不到问题答案?您还可以
热门栏目订阅中国领先的IT技术网站
51CTO旗下网站
一款Facebook出品的JS框架--使用React.js和应用缓存构建快速同步应用程序
JavaScript框架层出不穷,在很多程序员看来,React.js是创建大型、快速的Web应用的最好方式。这一款由Facebook出品的JS框架,无论是在Facebook还是在Instagram中,它的表现都非常出色。如何权衡取决于特定的应用系统和业务要求,本文就是我们的团队使用React.js和应用缓存来解决这一问题的一个实例。
作者:来源:| 09:44
对大部分应用系统来说,在某种程度上,应用程序的快速加载和及时取得最新数据两个方面同样重要。倾向于积极使用缓存数据,可能会导致提供的数据陈旧;而倾向于及时获取最新数据,可能会牺牲加载时间。当然,也可以鱼与熊掌兼得,但是可能会需要更多的硬件,更复杂的软件,或两者都需要(意味着一个字:钱)。
如何权衡取决于特定的应用系统和业务要求,本文就是我们的团队使用React.js和应用缓存来解决这一问题的一个实例。
我们从哪里开始
标签是每当你在浏览器上打开一个标签去送出一份慈善捐助的好理由。这是一件很伟大的事&&但事实上,我们仅仅点击了一个价值 100,000美元的里程碑来完成慈善捐助&&但是,我们有一个疑问。
我们的应用也太慢了。大家都明白这点。当用户更换新的标签页时,他们需要得是速度与连贯性。而且,我们也没有宣布:载入页面的延迟成为了人们关闭标签的首选理由。
我们想让我们的页面除了更有用,还要更好地被接受。但随着我们向页面中加了些附加功能后, 我们的页面载入问题也越来越突出了。因为人们需要我们的 APP 能快速地提供内容信息。
我们正在用 Django 的模板系统做一个交互式服务器来召唤或服务一个页面。当使用者是在快速的网络环境中,而且我们的服务状态是健康的情况下,服务器响应时间是 ~65毫秒,还不是比较惨。然而,如果在你父母的房子*里打开一个标签,或者我们的数据库产生了一个短暂的停顿时,这可能会给你在对其的信任上,泼了一盆冷水。
比较让人烦恼,我应该承认我们所建立的 APP 并没有采用标准的前端框架,除了仅仅是使用了 JQuery。 考虑到我们的 APP 有太多的互动,而且太混乱了。在各种各样的代码类型上,我要怎么才能喜欢它。
我们需要去修改它。
* 我爱你们,老妈、老爸!时代华纳有线电视,&。
明确我们的需求
当准备去处理这个问题时,我们必须决定优先处理哪些以及放弃哪此需求。在这里我们提出了一些建议:
页面必须能快速载入。这是没得讨价还价的。
我们的页面必须是非本地 URL。我们提高了 VIA 捐助广告的价格,网络在线广告需要去核识真实性来确保这些广告是够安全的。由于浏览器端的用户页面插件总是将我们的广告移除,以至于网络广告只能使用 http 或 https 协议。
我们希望页面中的内容是最新的,但不必是实时性的。我们通过设备对用户数据进行同步, 并保持完美的体验。我们以分页的形式显示出用户的反馈;例如,我们显示出新用户的统计数据;我们有时也要运行捐助设备来以滚动条的形式显示出 &募集资金& 量。虽然我们愿意去接收一定程度上稍旧的数据(就像页面展示后才提交数据),但理想得是在提交数据的瞬间发生。
我们要减少前端混乱的代码。将非优先权最高的代码肃清,这是一件让人兴奋的事。
让我们动起手来实践关于处理这些问题的思路。
一、采用主动的服务器端缓存
我们一度认为应该增加首先扩展服务器端缓存结构,目前我们已经十分依赖Django的低级缓存(),它有助于达到我们的目标,但是我们不得不在每次都要写语句来判断是否存在缓存到期或失效情况,我想这张摘自一场精彩演讲()的幻灯片能够反映出Django在缓存问题上面临的挑战:
此外,为了更好地从服务器端缓存中获益,我们的缓存系统看起来是一个多层次的结构:(先是)每个用户完整的页面缓存,然后是用户数据的模块化的缓存,(同时)每当数据变化时还要智能判断数据是否失效。因为在实施过程中已经遭遇到一些与缓存相关的(系统)错误(bugs),所以我们并不希望继续增加了缓存系统的复杂性。
更重要的是,还存在网络传递差异的问题,例如对一个新的TAB页面来说,在快速和慢速的因特网网络上的表现有着显著的差异,即使将我们的服务响应时间降低到小于1毫秒,对大部分用户而言,这个页面显示的还是不够快的。
不,这样可不行。
二: 在我们的页面上使用应用缓存
&应用缓存? 他不是个么?&
不,别这么粗鲁!
& 好吧, 也许他是有点儿.&在使用应用缓存之前,充分了解它的怪癖和陷阱是明智的.我们主要关心的是应用缓存会降低我们在调试时的透明度,因为我们服务器在轻便的请求上没有日志 &(接下来我们将解决这个问题). 在代码变更后的另一个与之前不同的小问题是,在两个视图页上应用了这些变更:&它需要一个页面去提示浏览器获取资源, 另一个页面则去使用新的资源.这不是很理想, 但是在我们的案例中是可以接受的.&在一般情况下, 我们的团队在应用缓存的限制下相对没有多少烦恼; 更多我们的app不适用的情况下解决起来会更轻松.
好吧, 也许我们可以与应用缓存合作. 可能这是一个方法在不必通过大量的重构去实现它?
我们快速而粗糙的主意就是,使用 Django 处理视图模版并返回一个html页面来保持我们当前页面的原状.在任何用户数据变更时,浏览器会从服务器和应用缓存那里获取一个重新渲染的页面 .
我们的游戏计划:
我们将在当前页面上激活应用缓存, 所以它将会绕过服务器去加载.
当一个用户制造了一些数据改动而我们又想保留时, 我们的页面将会使用一个ajax请求去保存数据到数据库里,通常我们就是这么做的.
我们将会从应用缓存清单引入一个对用户特殊的版本号,所以对于每个用户来说这份清单都是独一无二的. 当用户更新任意数据时, 我们将会对这个用户的应用缓存清单的内容创建一个新的版本,而且浏览器会知道并获取页面资源来更新.
在客户端方面, 我们将会在用户修改任意数据时.浏览器将会获取用户的缓存清单, 查看已经被处理成一个新版本号的被更改的内容,并且重新获取页面的内容.
理论上, 当用户下次浏览这个页面时, 应用缓存会提供一个在服务端重新渲染过的最新的页面.
从好的方面讲,这些选择将会引入极小的工程投资.
一个小缺点:这个选项没起作用。
浏览器获取资源的速度不够快是主要的问题。&如果你在新的标签页修改了数据(例如,在你的便签里添加了一条笔记),然后在几秒内打开了一个新的标签,应用缓存可能还没有获取到你修改的新的页面,显示的依旧是你没添加笔记的旧页面。从用户体验的角度看,这就像是数据丢失&&&即使是技术上的数据延迟 ,也是我们无法接受的。
当多个设备参与时这个问题会变得更严重。如果你在设备A上对你的新标签页有修改,接着在设备B打开一个标签页,保证你得到的是旧的数据。在随后的页面加载之前你都看不到新的数据。
这不是很好。 抱歉,这是个快速而粗糙的选择。
三:面向模板的本地存储和应用程序存储
更简洁地做到这一点,我们可以结合客户端模板使用应用缓存,在本地存储数据。这看起来是个很好的选择,除了应用缓存的&第二页加载&那个问题所出现的糟糕情况,它是非常快的,并且它还可以清理掉我们的前端(重构...哇?)。作为奖励,我们的新标签页在在线的时候将被访问。
我们选择使用 作为模板是有一些原因的。最主要的一个就是我们有一些在其他领域使用应用的经验。我们也觉得学习曲线比Angular更浅显些,我们也是严肃地考虑过其他方案的。说来奇怪,长久以来建立一个前端框架都是在我们已有的jQuery上努力,我们的数据被改变更像是React中的&状态&,这会让我们转换到React更容易些。
我们还选用了 Facebook 的 Flux 构型, 因为我们认可单向的数据流可以让我们的代码更顺理成章. Flux 的调度器也能让我们更加容易的进行数据同步,下面我会对此进行描述.
它是如何运作的
新的tab一打开,浏览器就会从应用缓存中获取我们的页面。我们的React应用或从Flux存储中获取数据 (1), 后者会去本地存储里抽取数据 (2). React 应用一安装,页面就会被加载 (3, 4). 然后,我们的页面会向应用服务器进行一次Ajax调用 (5), 发送应用的所有数据&其实就是一个带有所有Flux存储数据的对象. 服务器接收到用户所有的本地数据,并使用用户在数据库中的数据对其进行调和 (这会在下面的 &数据同步& 中有更详细的描述), 然后向应用返回最新的数据 (6). 应用会从服务器接收到最新的数据,并且更新每一个Flux存储 (7, 8). &Flux 存储一更新,存储会触发一个变化事件 (3), 而 React 组件就会更新他们的状态 (4). 当用户改变了什么东西的时候,就会发起一个动作 (11), 更新存储的数据 (7, 8); 当存储更新并触发变化时间的时候,我们会将数据持久化到本地存储中 (9) 而如果用户在线的话,就将数据持久化到数据库中 (10).
如果你了解过有关 Flux 的东西, 下面这幅图看起来应该会很熟悉:
(抱歉,这图看起来有点乱.)
这幅数据流图的意思是,我们会向你快速的显示新的tab页,然后在一秒钟左右之内,我们将会用来自服务器的新数据更新你的页面. 这份新数据可能包含你在另外一个设备上对一个窗口小组件做出的变化 (像便条里的内容) , 或者也可能是一个慈善活动中&筹集资金& 实时统计.
有关使用 Flux 构型最令人惊奇的一件事情就是通过调度器的远程数据流同步时间完全同用户的操作保持一致,使得调试异常的简单. 因为存储是我们应用状态的真实来源, 所以我们可以放心的让应用在存储的数据被远程的数据同步或者用户的输入改变时,仍然可以始终如一地响应. 我们仍然可以再整个应用中保持单项的数据流, 这使得代码理所当然的变得简单很多.
在应用速度和数据实时性两者之间,我们已经找到了理想的平衡.
我提到过我们会在每次页面加载的时候向服务器同步你的数据。那我们是如何去实现这个东西的呢?
我们通过为数据&块&的最后一次更新打上时间戳,然后在客户端(的Flux存储)上,以及远程的数据库中保存数据发生变化的时间戳(modified_at),这样的方式来处理同步. 例如,如果在你的一个便条窗口中进行了输入,就会把窗口的modified_at时间戳设置成现在,然后把你的便条内容保存到本地存储中,并入如果条件可能的话,也会保存到远程数据库中. 而后,下次你打开一个tab的时候,我们将会把有关窗口的数据发送到应用服务器,在那里会对跟该窗口相关的客户端时间戳跟数据库中保存的时间戳进行比对,并返回最新的数据.
为了简单起见,我们用Flux存储对象来进行数据的发送和接收. 这让我们可以无痛的用发送自应用的数据更新存储, 因为我们明白它将会被保持最新,并且同我们的存储一样具有相同的数据结构.
我们当前的同步过程肯定是不完美的: 在发生同步冲突的情况下,我们会简单地去获取最新的数据. 对我们而言,这只是一个可以接受的细节状况; 毕竟,我们可不是 Evernote. 即使这会变得不可接受,也可以在以后用更智能的数据合并和用户消息进行解决.
让我们运行得更快一些 ! (或者说与呈现)
加载应用缓存的页面很不错,但在我们向用户展示之前,我们仍然要运行应用代码,并对所有的组件进行渲染. 对于一个相当大的应用而言,这可能要花上几百毫秒甚至超过1秒.
为了能有一个快速的第一次加载体验, React 提供了一个方便的&,让你可以先向浏览器发送DOM(让页面先出现) ,然后再连接上所有的侦听器 (让页面可交互). 这样就适应服务器端的预呈现了. 在我们的案例中,我们想是否可以用把它用在客户端上 & 而我们做到了.
每次我们将数据持久化到本地存储时,我们也会将我们的React应用做成字符串,并将这个字符串保存到本地存储中. 然后,页面一加载,在我们做任何事情之前,我们会从本地存储中加载渲染好的应用并将它放到一个HTML元素中. 换言之,页面只用了3行JavaScript就加载了DOM! 对于我们的应用而言,预呈现减少了大概400毫秒的预加载时间。
&见鬼&:挑战和缺陷
没有什么东西是完美的。重构的时候还是有些事情不那么有趣的。
再见吧, JQuery UI
在转换到React过程中的一个速度损失让我们放弃了几乎所有的JQuery UI组件,比如&. 这稍微烦人地让我们花了点时间来重新做之前已经做过的事情. 不过,事实证明我们还是可以依靠不断增长的实用的&&来构建我们自身想要的东西.
&为什么, renderToString, 为什么?&
另外一个小的实现上的挑战: 如果你用过React的 renderToString 方法, 你可能已经看到过这个错误:
React&attempted&to&use&reuse&markup&in&a&container&but&the&checksum&was&invalid. &
当React在已经有预渲染DOM存在之后渲染它的应用时,它就要预计预渲染好的DOM应该同将要被渲染的DOM相同. 那就意味着你不能让像 Date.now() 和 Math.random() 这样的东西影响到你的DOM. 为了解决这个问题,你将可能要花点时间在你的差异编辑器上面,来比对这两个DOM字符串.
不够灵活的存储数据结构
我们设计为应用同服务器返回的应用数据结构之间的不匹配敞开了大门. 在我们想生产环境推送新的代码之后,你第一次加载的页面视图会包含从应用缓存加载的老应用代码. 不过,从应用服务器返回的同步数据将会是结构化的,而我们的新版本会对其进行构造.
所以,如果在新版本中我们决定对存储中的一块数据进行重命名或者移除,你的页面就会在新的tab第一次打开时被打断; 老的应用代码不会知道如何去处理它. 在打开下一个tab之前,你的浏览器可能已经获取到了最新的应用代码,并将其放到了应用缓存中,因此页面会运作得很好.
为了防止新tab的打断, 我们需要为我们的存储数据维护一套可靠的内部API. 那样会有点儿痛苦.
说到代码的推送...
如果我们搞砸了,弄坏了应用,每个看到一个破页面的用户都会在我们修复它之前看到一个额外的破页面. 应用程序缓存就会进行恼人的二次重新加载更新。
大家好,结局才会好
切换到React和Flux是一件令人很愉快的事情. 我们的团队发现我们自己重新爱上了前端开发, 而我们做出的变化让新进工程师接触代码库容易了许多。
在用户体验方面,我们的新tab一直在快速推进. 对于拥有优良网络条件的用户而言,这次的版本不会有太多的变化;但是对于其他人,他们是能在发现我们的应用不可用和喜欢上使用它之间发现不同的。
因为Tab需要从横幅广告展示为慈善机构筹集基金的原因,更快的页面加载能增加在用户离开我们的页面之前看到的广告的数量. 这次的版本增加了大约12%的广告展示 (还有对应的筹资收入).
当然,一个快速的应用并不会是一个好的应用; 它只是好的应用不会是一个马上就会让人讨厌的应用. 对于我们而言,它提供了未来更多有趣动人的事情的基础.
这是不是很有趣 ? 你想要通过一个有趣,充满活力的团队工作不 ?&&! 还有,如果你本周就在 San Francisco 附近, 我就会在周五的&&上&& 如果你想要一次会谈的话就让我知道吧.
感谢 Ti Zhao 和 Josiah Gaskin 对这篇文章的评论。
英文原文:【责任编辑: TEL:(010)】
大家都在看猜你喜欢
原创头条外电外电热点
24H热文一周话题本月最赞
讲师:1人学习过
讲师:24人学习过
讲师:0人学习过
精选博文论坛热帖下载排行
Java学习笔记在JavaWorld技术论坛(.tw/)和作者的网站(http://caterpillar.onlyfun.net/Gossip/)提供免费下载...
订阅51CTO邮刊

我要回帖

更多关于 无线网络与有线网络 的文章

 

随机推荐