2015前端组件化框架之路 您所在的位置:网站首页 封装公共组件是什么意思 2015前端组件化框架之路

2015前端组件化框架之路

#2015前端组件化框架之路| 来源: 网络整理| 查看: 265

1. 为何组件化这么难作

Web应用的组件化是一个很复杂的话题。css

在大型软件中,组件化是一种共识,它一方面提升了开发效率,另外一方面下降了维护成本。可是在Web前端这个领域,并无很通用的组件模式,由于缺乏一个你们都能认同的实现方式,因此不少框架/库都实现了本身的组件化方式。html

前端圈最热衷于造轮子了,没有哪一个别的领域能出现这么混乱而欣欣向荣的景象。这一方面说明前端领域的创造力很旺盛,另外一方面却说明了基础设施是不完善的。前端

我曾经有过这么一个类比,说明某种编程技术及其生态发展的几个阶段:vue

最初的时候人们忙着补全各类API,表明着他们拥有的东西还很匮乏,须要在语言跟基础设施上继续完善 而后就开始各类模式,标志他们作的东西逐渐变大变复杂,须要更好的组织了 而后就是各种分层MVC,MVP,MVVM之类,可视化开发,自动化测试,团队协同系统等等,说明重视生产效率了,也就是所谓工程化

那么,对比这三个阶段,看看关注这三种东西的人数,以为Web发展到哪一步了?node

细节来讲,大概是模块化和组件化标准即将大规模落地(好坏先不论),各种API也大体齐备了,终于看到起飞的但愿了,各类框架几年内会有很是强力的洗牌,若是不考虑老旧浏览器的拖累,这个洗牌过程将大大加速,而后才能释放Web前端的产能。git

可是咱们必须注意到,如今这些即将普及的标准,不少都会给以前的工做带来改变。用工业体系的发展史来对比,前端领域目前正处于蒸汽机发明以前,早期机械(好比《木兰辞》里面的机杼,主要是动力与材料比较原始)已经普及的这么一个阶段。github

因此,从这个角度看,不少框架/库是会消亡的(专门作模块化的AMD和CMD相关库,专一于标准化DOM选择器铺垫的某些库),一些则必须进行革新,还有一些受的影响会比较小(数据可视化等相关方向),能够有机会沿着本身的方向继续演进。web

2. 标准的变革

对于这类东西来讲,能得到普遍群众基础的关键在于:对未来的标准有怎样的迎合程度。对前端编程方式可能形成重大影响的标准有这些:ajax

module Web Components class observe promise

module的问题很好理解,JavaScript第一次有了语言上的模块机制,而Web Components则是约定了基于泛HTML体系构建组件库的方式,class加强了编程体验,observe提供了数据和展示分离的一种优秀方式,promise则是目前前端最流行的异步编程方式。spring

这里面只有两个东西是绕不过去的,一是module,一是Web Components。前者是模块化基础,后者是组件化的基础。

module的标准化,主要影响的是一些AMD/CMD的加载和相关管理系统,从这个角度来看,正如seajs团队的@afc163 所说,无论是AMD仍是CMD,都过期了。

模块化相对来讲,迁移还比较容易,基本只是纯逻辑的包装,跟AMD或者CMD相比,包装形式有所变化,但组件化就是个比较棘手的问题了。

Web Components提供了一种组件化的推荐方式,具体来讲,就是:

经过shadow DOM封装组件的内部结构 经过Custom Element对外提供组件的标签 经过Template Element定义组件的HTML模板 经过HTML imports控制组件的依赖加载

这几种东西,会对现有的各类前端框架/库产生很巨大的影响:

因为shadow DOM的出现,组件的内部实现隐藏性更好了,每一个组件更加独立,可是这使得CSS变得很破碎,LESS和SASS这样的样式框架面临重大挑战。 由于组件的隔离,每一个组件内部的DOM复杂度下降了,因此选择器大多数状况下能够限制在组件内部了,常规选择器的复杂度下降,这会致使人们对jQuery的依赖降低。 又由于组件的隔离性增强,致力于创建前端组件化开发方式的各类框架/库(除Polymer外),在本身的组件实现方式与标准Web Components的结合,组件之间数据模型的同步等问题上,都遇到了不一样寻常的挑战。 HTML imports和新的组件封装方式的使用,会致使以前经常使用的以JavaScript为主体的各种组件定义方式处境尴尬,它们的依赖、加载,都面临了新的挑战,而因为全局做用域的弱化,请求的合并变得困可贵多。 3. 当下最时髦的前端组件化框架/库

在2015年初这个时间点看,前端领域有三个框架/库引领时尚,那就是Angular,Polymer,React(排名按照首字母),在知乎的这篇2014 年底有哪些比较火的 Web 开发技术?里,我大体回答过一些点,其余几位朋友的答案也很值得看。关于这三者的细节分析,侯振宇的这篇讲得很好:2015前端框架何去何从

咱们能够看到,Polymer这个东西在这方面是有先天优点的,由于它的核心理念就是基于Web Components的,也就是说,它基本没有考虑如何解决当前的问题,直接以将来为发展方向了。

React的编程模式其实没必要特别考虑Web标准,它的迁移成本并不算高,甚至因为其实现机制,屏蔽了UI层实现方式,因此你们能看到在native上的使用,canvas上的使用,这都是与基于DOM的编程方式大为不一样的,因此对它来讲,处理Web Components的兼容问题要在封装标签的时候解决,反正以前也是要封装。

Angular 1.x的版本,能够说是跟同时代的多数框架/库同样,对将来标准的兼容基本没有考虑,可是从新规划以后的2.0版本对此有了不少权衡,变成了激进变动,忽然就变成一个将来的东西了。

这三个东西各有千秋,在能够预见的几年内将会鼎足三分,也许还会有新的框架出现,能不能比这几个流行就难说了。

此外,原Angular 2.0的成员Rob Eisenberg建立了本身的新一代框架aurelia,该框架将成为Angular 2.0强有力的竞争者。

4. 前端组件的复用性

看过了已有的一些东西以后,咱们能够大体来讨论一下前端组件化的一些理念。假设咱们有了某种底层的组件机制,先无论它是浏览器原生的,或者是某种框架/库实现的约定,如今打算用它来作一个大型的Web应用,应该怎么作呢?

所谓组件化,核心意义莫过于提取真正有复用价值的东西。那怎样的东西有复用价值呢?

控件 基础逻辑功能 公共样式 稳定的业务逻辑

对于控件的可复用性,基本上是没有争议的,由于这是实实在在的通用功能,而且比较独立。

基础逻辑功能主要指的是一些与界面无关的东西,好比underscore这样的辅助库,或者一些校验等等纯逻辑功能。

公共样式的复用性也是比较容易承认的,所以也会有bootstrap,foundation,semantic这些东西的流行,不过它们也不是纯粹的样式库了,也带有一些小的逻辑封装。

最后一块,也就是业务逻辑。这一块的复用是存在不少争议的,一方面是,不少人不认同业务逻辑也须要组件化,另外一方面,这块东西究竟怎样去组件化,也很须要思考。

除了上面列出的这些以外,还有大量的业务界面,这块东西很显然复用价值很低,基本不存在复用性,但仍然有不少方案中把它们“组件化”了,使得它们成为了“不具备复用性的组件”。为何会出现这种状况呢?

组件化的本质目的并不必定是要为了可复用,而是提高可维护性。这一点正如面向对象语言,Java要比C++纯粹,由于它不容许例外状况的出现,连main函数都必须写到某个类里,因此Java是纯面向对象语言,而C++不是。

在咱们这种状况下,也能够把组件化分为:全组件化,局部组件化。怎么理解这两个东西的区别呢,有人问过js框架和库的区别是什么,通常来讲,有某种较强约定的东西,称为框架,而约定比较松散的,称为库。框架不少都是有全组件化理念的,好比说,不少年前就出现的ExtJS,它是全组件化框架,而jQuery和它的插件体系,则是局部组件化。因此用ExtJS写东西,无论写什么都是差很少同样的写法,而用jQuery的时候,大部分地方是原始HTML,哪里须要有些不同的东西,就只在那个地方调用插件作一下特殊化。

对于一个有必定规模的Web应用来讲,把全部东西都“组件化”,在管理上会有较大的便利性。我举个例子,一样是编写代码,短代码明显比长代码的可读性更高,因此不少语言里会建议“一个方法通常不要超过多少行,一个类最好不要超过多少行”之类。在Web前端这个体系里,JavaScript这块是作得相对较好的,如今入门水平的人,也已经不多会有把一堆js都写在一块儿的了。CSS这块,最近在SASS,LESS等框架的引领下,也逐步往模块化方面发展,不然直接编写bootstrap那种css,会很是痛苦。

这个时候咱们再看HTML的部分,若是不考虑模板等技术的使用,某些界面光布局代码写起来就很是多了,像一些表单,都须要一层套一层,不少简单的表单元素都须要套个三层左右,更没必要说一些有复杂布局的东西了。尤为是整个系统单页化以后,界面的header,footer,各类nav或者aside,极可能都有必定复杂性。若是这些东西的代码不做切分,那么主界面的HTML必定比较难看。

咱们先无论用什么方式切分了,好比用某种模板,用相似Angular中的include,或者Polymer,React中的标签,或者直接使用原生Web Components,总之是把一块一块都拆开了,而后包含进来。从这个角度看,这些拆出去的东西都像组件,但若是从复用性的角度看,极可能多数东西,每一块都只有一个地方用,压根没有复用度。这个拆出去,纯粹是为了使得整个工程易于管理,易于维护。

这时候咱们再来关注不一样框架/库对UI层组件化的处理方式,发现有两个类型,模板和函数。

模板是一种很常见的东西,它用HTML字符串的方式表达界面的原始结构,而后经过代入数据的方式生成真正的界面,有的是生成目标HTML,有的还生成各类事件的自动绑定。前者是静态模板,后者是动态模板。

另外有一些框架/库偏心用函数逻辑来生成界面,早期的ExtJS,如今的React(它内部仍是可能使用模板,并且对外提供的是组件建立接口的进一步封装——jsx)等,这种实现技术的优点是不一样平台上编程体验一致,甚至能够给每种平台封装相同的组件,调用方轻松写一份代码,在Web和不一样Native平台上可用。但这种方式也有比较麻烦的地方,那就是界面调整比较繁琐。

本文前面部分引用侯振宇的那篇文章里,他提出这些问题:

如何能把组件变得更易重用? 具体一点:

我在用某个组件时须要从新调整一下组件里面元素的顺序怎么办? 我想要去掉组件里面某一个元素怎么办? 如何把组件变得更易扩展? 具体一点: 业务方不断要求给组件加功能怎么办?

为此,还提出了“模板复写”方案,在这一点上我有不一样意见。

咱们来看看如何把一个业务界面切割成组件。

有这么一个简单场景:一个雇员列表界面包括两个部分,雇员表格和用于填写雇员信息的表单。在这个场景下,存在哪些组件?

对于这个问题,主要存在两种倾向,一种是仅仅把“控件”和比较有通用性的东西封装成组件,另一种是整个应用都组件化。

对前一种方式来讲,这里面只存在数据表格这么一个组件。对后一种方式来讲,这里面有可能存在:数据表格,雇员表单,甚至还包括雇员列表界面这么一个更大的组件。

这两种方式,就是咱们以前所说的“局部组件化”,“全组件化”。

咱们前面提到,全组件化在管理上是存在优点的,它能够把不一样层面的东西都搞成相似结构,好比刚才的这个业务场景,极可能最后写起来是这个样子:

对于UI层,最好的组件化方式是标签化,好比上面代码中就是三个标签表达了整个界面。但我我的坚定反对滥用标签,并非把各类东西都尽可能封装就必定好。

全标签化的问题主要有这些:

第一,语义化代价太大。只要用了标签,就必定须要给它合适的语义,也就是命名。但实际用的时候,极可能只是为了把一堆html简化一下而已,到底简化出来的那东西应当叫什么名字,光是起名也费不知多少脑细胞。好比你说雇员管理的表单,这个表单有heading吗,有footer吗,能折叠吗,等等,很难起一个让别人一看就知道的名字,要么就是特别长。这还算简单的,由于咱们是全组件化,因此极可能会有组合了多种东西的一个较复杂的界面,你想来想去也无法给它起个名字,因而写了个:

这尼玛……可能我夸张了点,但不少时候项目规模够大,你不起这么复杂的名字,最后极可能无法跟功能相似的一个组件区分开,由于这些该死的组件都存在于同一个命名空间中。若是仅仅是看成一个界面片断来include,就不存在这种心理负担了。

好比Angular里面的这种:

就不给它什么名字,直接include进来,用文件路径来区分。这个片断的做用能够用其目录结构描述,也就是经过物理名而非逻辑名来标识,目录层次充当了一个很好的命名空间。

如今的一些主流MVVM框架,好比knockout,angular,avalon,vue等等,都有一种“界面模板”,但这种模板并不只仅是模板,而是能够视为一种配置文件。某一块界面模板描述了自身与数据模型的关系,当它被解析以后,按照其中的各类设置,与数据创建关联,而且反过来再更新自身所对应的视图。

不含业务逻辑的UI(或者是业务逻辑已分离的UI)基本不适合做为组件来看待,由于即便在逻辑不变的状况下,界面改版的可能性也太多了。好比即便是换了新的CSS实现方式,从float布局改为flex布局,都有可能把DOM结构少套几层div,所以,在使用模板的方案中,只能把界面层视为配置文件,不能当作组件,若是这么作,就会轻松不少。

部队行军的时候讲究“逢山开路,遇水搭桥”,这句话的重点在于只有到某些地形才开路搭桥,使用MVVM这类模式解决的业务场景,多数时候是一马平川,横着走均可以,没必要硬要造路。因此从整个方案看的话,UI层实现应该是模板与控件并存,大部分地方是模板,少数地方是须要单独花时间搞的路和桥。

第二,配置过于复杂。有不少东西其实不太适合封装,不但封装的代价大,使用的代价也会很大。有时候会发现,调用代码的绝大部分都是在写各类配置。

就像刚才的雇员表单,既然你不从标签的命名上去区分,那必定会在组件上加配置。好比你原来想这样:

而后在组件内部,判断有没有设置heading,若是没有就不显示,若是有,就显示。过了两天,产品问能不能把heading里面的某几个字加粗或者换色,而后码农开始容许这个heading属性传入html。没多久以后,你会惊奇地发现有人用你的组件,没跟你说,就在heading里面传入了折叠按钮的html,而且用选择器给折叠按钮加了事件,点一下以后还能折叠这个表单了……

而后你一想,这个不行,我得给他再加个配置,让他能很简单地控制折叠按钮的显示,可是如今这么写太不直观,因而采用对象结构的配置:

雇员表单

而后又有一天,发现有不少面板均可以折叠,而后特地建立了一个可折叠面板组件,又建立了一种继承机制,其余普通业务面板从它继承,今后一发不可收拾。

我举这例子的意思是为了说明什么呢,我想说,在规模较大的项目中,企图用全标签化加配置的方式来描述全部的普通业务界面,是必定事倍功半的,而且这个规模越大就越坑,这也正是ExtJS这类对UI层封装过分的体系存在的最大问题。

这个问题讨论完了,咱们来看看另一个问题:若是UI组件有业务逻辑,应该如何处理。

好比说,性别选择的下拉框,它是一个很是通用化的功能,照理说是很适合被当作组件来提供的。可是究竟如何封装它,咱们就有些犯难了。这个组件里除了界面,还有数据,这些数据应当内置在组件里吗?理论上从组件的封装性来讲,是都应当在里面的,因而就这么造了一个组件:

这个组件很是美好,只需直接放在任意的界面中,就能显示带有性别数据的下拉框了。性别的数据很天然地是放在组件的实现内部,一个写死的数组中。这个太简单了,咱们改一下,改为商品销售的国家下拉框。

表面上看,这个没什么区别,但咱们有个要求,本公司商品销售的国家的信息是统一配置的,也就是说,这个数据来源于服务端。这时候,你是否是想把一个http请求封装到这组件里?

这样作也不是不能够,但存在至少两个问题:

若是这类组件在同一个界面中出现屡次,就可能存在请求的浪费,由于有一个组件实例就会产生一个请求。 若是国家信息的配置界面与这个组件同时存在,当咱们在配置界面中新增一个国家了,下拉框组件中的数据并不会实时刷新。

第一个问题只是资源的浪费,第二个就是数据的不一致了。曾经在不少系统中,你们都是手动刷新当前页面来解决这问题的,但到了这个时代,人们都是追求体验的,在一个全组件化的解决方案中,不该再出现此类问题。

如何解决这样的问题呢?那就是引入一层Store的概念,每一个组件不直接去到服务端请求数据,而是到对应的前端数据缓存中去获取数据,让这个缓存本身去跟服务端保持同步。

因此,在实际作方案的过程当中,无论是基于Angular,React,Polymer,最后确定都作出一层Store了,否则会有不少问题。

5. 为何MVVM是一种很好的选择

咱们回顾一下刚才那个下拉框的组件,发现存在几个问题:

界面很差调整。刚才的那个例子相对简单,若是咱们是一个省市县三级联动的组件,就比较麻烦了。好比说,咱们想要把水平布局改为垂直的,又或者,想要把中间的label的字改改,都会很是麻烦。按照传统的作组件的方式,就要加若干配置项,而后组件里面去分别判断,修改DOM结构。 若是数据的来源不是静态json,而是某个动态的服务接口,那用起来就很麻烦。 咱们更多地须要业务逻辑的复用和纯“控件”的复用,至于那些绑定业务的界面组件,复用性其实很弱。

因此,从这些角度,会尽可能指望在HTML界面层与JavaScript业务逻辑之间,存在一种分离。

这时候,再看看绝大多数界面组件存在什么问题:

有时候咱们考虑一下DOM操做的类型,会发现实际上是很容易枚举的:

建立并插入节点 移除节点 节点的交换 属性的设置

多数界面组件封装的绝大部份内容不过是这些东西的重复。这些东西,实际上是能够经过某些配置描述出来的,好比说,某个数组以什么形式渲染成一个select或者无序列表之类,当数组变更,这些东西也跟着变更,这些都应当被自动处理,若是某个方案在如今这个时代还手动操做这些,那真的是一种落伍。

因此咱们能够看到,以Angular,Knockout,Vue,Avalon为表明的框架们在这方面作了不少事,尽管理念有所差别,但大方向都很是一致,也就是把大多数命令式的DOM操做过程简化为一些配置。

有了这种方式以后,咱们能够追求不一样层级的复用:

业务模型由于是纯逻辑,因此很是容易复用 视图模型基本上也是纯逻辑,界面层多数是纯字符串模板,同一个视图模型搭配不一样的界面模板,能够实现视图模型的复用 同一个界面模板与不一样的视图模型组合,也能直接组合出彻底不一样的东西

因此这么一来,咱们的复用粒度就很是灵活了。正由于这样,我一直认为Angular这样的框架战略方向是很正确的,虽然有不少战术失误。咱们在不少场景下,都是须要这样的高效生产手段的。

6. 组件的长期积累

咱们作组件化这件事,必定是一种长期打算,为了使得当前的不少东西能够做为一种积累,在未来还能继续使用,或者仅仅做较小的修改就能使用,因此必须考虑对将来标准的兼容。主要须要考虑的方面有这几点:

尽量中立于语言和框架,使用浏览器的原生特性 逻辑层的模块化(ECMAScript module) 界面层的元素化(Web Components)

以前有不少人对Angular 2.0的激进变动很不认同,但它的变动很大程度上是对标准的全面迎合。这不只仅是它的问题,实际上是全部前端框架的问题。不面对这些问题,无论如今多么好,未来都是死路一条。这个问题的根源是,这几个已有的规范约束了模块化和元素化的推荐方式,而且,若是要对当前和将来两边作适配的话,基本就无法干了,致使之前的都不得不作必定的迁移。

模块化的迁移成本还比较小,不管是以前AMD仍是CMD的,均可以根据一些规则转换过来,但组件化的迁移成本太大了,几乎每种框架都会提出本身的理念,而后有不一样的组件化理念。

仍是从三个典型的东西来讲:Polymer,React,Angular。

Polymer中的组件化,其实就是标签化。这里的标签,并不仅是界面元素,甚至逻辑组件也能够这样,好比这个代码:

注意到这里的core-ajax标签,很明显这已是纯逻辑的了,在大多数前端框架或者库中,调用ajax确定不是这样的,但在浏览器端这么干也不是它首创,好比flash里面的WebService,好比早期IE中基于htc实现的webservice.htc等等,都是这么干的。在Polymer中,这类东西称为非可见元素(non-visual-element)。

React的组件化,跟Polymer略有不一样,它的界面部分是标签化,但若是有单纯的逻辑,仍是纯JavaScript模块。

既然你们的实现方式都那么不一致,那咱们怎么搞出尽可能可复用的组件呢?问题到最后仍是要绕到Web Components上。

在Web Components与前端组件化框架的关系上,我以为是这么个样子:

各类前端组件化框架应当尽量以Web Components为基石,它致力于组织这些Components与数据模型之间的关系,而不去关注某个具体Component的内部实现,好比说,一个列表组件,它究竟内部使用什么实现,组件化框架实际上是没必要关心的,它只应当关注这个组件的数据存取接口。

而后,这些组件化框架再去根据本身的理念,进一步对这些标准Web Components进行封装。换句话说,业务开发人员使用某个组件的时候,他是应当感知不到这个组件内部究竟使用了Web Components,仍是直接使用传统方式。(这一点有些理想化,可能并非那么容易作到,由于咱们还要管理像import之类的事情)。

7. 咱们须要关注什么

目前来看,前端框架/库仍然处于混战期,可比中国历史上的春秋战国,百家齐放,做为跟随者来讲,这是很痛苦的,由于无所适从,极可能你做为一个企业的前端架构师或者技术经理,须要作一些选型工做,但选哪一个能保证几年后不被淘汰呢?基本没有。

虽然咱们不知道未来什么框架会流行,但咱们能够从一些细节方面去关注,某个具体的方面,未来会有什么,也能够了解一下在某个具体领域存在什么样的方案。一个完整的框架方案,无非是如下多个方面的综合。

7.1 模块化

这块仍是不讲了,支付宝seajs还有百度ecomfe这两个团队的人应该都能比我讲得好得多。

7.2 Web Components

本文前面讨论过一些,也不深刻了。

7.3 变动检测

咱们知道,现代框架的一个特色是自动化,也就是把原有的一些手动操做提取。在前端编程中,最多见的代码是在干什么呢?读写数据和操做DOM。很多现代的框架/库都对这方面做了处理,好比说经过某种配置的方式,由框架自动添加一些关联,当数据变动的时候,把DOM进行相应修改,又好比,当DOM发生变更的时候,也更新对应的数据。

这个关联过程可能会用到几种技术。首先咱们看怎么知道数据在变化,这里面有三种途径:

1、存取器的封装。这个的意思也就是对数据进行一层包装,好比:

var data = { name: "aaa", getName: function() { return this.name; }, setName: function(value) { this.name = value; } }

这样,不容许用户直接调用data.name,而是调用对应的两个函数。Backbone就是经过这样的机制实现数据变更观测的,这种方式适用于几乎全部浏览器,缺点就是比较麻烦,要对每一个数据进行包装。

这个机制在稍微新一点的浏览器中,也有另一种实现方式,那就是defineProperty相关的一些方法,使用更优雅的存取器,这样外界能够不用调用函数,而是直接用data.name这样进行属性的读写。

国产框架avalon使用了这个机制,低版本IE中没有defineProperty,但在低版本IE中不止有JavaScript,还存在VBScript,那里面有存取器,因此他巧妙地使用了VBS作了这么一个兼容封装。

基于存取器的机制还有个麻烦,就是每次动态添加属性,都必须再添加对应的存取器,不然这个属性的变动就没法获取。

2、脏检测。

以Angular 1.x为表明的框架使用了脏检测来获知数据变动,这个机制的大体原理是:

保存数据的新旧值,每当有一些DOM或者网络、定时器之类的事件产生,用这个事件以后的数据去跟以前保存的数据进行比对,若是相同,就不触发界面刷新,不然就刷新。

这个方式的理念是,控制全部可能致使数据变动的来源(也就是各类事件),在他们可能对数据进行操做以后,判断新旧数据是否有变化,忽略全部中间变动,也就是说,若是你在同一个事件中,把某个数据任意修改了不少次,但最后改回来了,框架会认为你什么都没干,也就不会通知界面去刷新了。

不能否认的是,脏检测的效率是比较低的,主要是不能精确获知数据变动的影响,因此当数据量更大的状况下,浪费更严重,须要手动做一些优化。好比说一个很大的数组,生成了一个界面上的列表,当某个项选中的时候,改变颜色。在这种机制下,每次改变这个项的数据状态,就须要把全部的项都跟原来比较一遍,而后,还要再所有比较一次发现没有关联引发的变化了,才能对应刷新界面。

3、观察机制。

在ES7里面,引入了Object的observe方法,能够用于监控对象或数组的变更。

这是目前为止最合理的观测方案。这个机制很精确高效,好比说,连长跟士兵说,你去观察对面那个碉堡里面的动静。这个含义很复杂,包括什么呢?

是否是加人了 是否是有人离开了 谁跟谁换岗了 上面的旗子从太阳旗换成青天白日了

所谓观察机制,也就是观测对象属性的变动,数组元素的新增,移除,位置变动等等。咱们先思考一下界面和数据的绑定,这原本就应当是一个外部的观察,你是数据,我是界面,你点头我微笑,你伸手我打人。这种绑定原本就应当是个松散关系,不该当由于要绑定,须要破坏原有的一些东西,因此很明显更合理。

除了数据的变更能够被观察,DOM也是能够的。可是目前绝大多数双向同步框架都是经过事件的方式把DOM变动同步到数据上。好比说,某个文本框绑定了一个对象的属性,那极可能,框架内部是监控了这个文本框的键盘输入、粘贴等相关事件,而后取值去往对象里写。

这么作能够解决大部分问题,可是若是你直接myInput.value="111",这个变动就无法获取了。这个不算大问题,由于在一个双向绑定框架中,一个既被监控,又手工赋值的东西,自己也比较怪,不过也有一些框架会尝试从HTMLInputELement的原型上去覆盖value赋值,尝试把这种东西也归入框架管辖范围。

另一个问题,那就是咱们只考虑了特定元素的特定属性,能够经过事件获取变动,如何得到更普遍意义上的DOM变动?好比说,通常属性的变动,或者甚至子节点的增删?

DOM4引入了MutationObserver,用于实现这种变动的观测。在DOM和数据之间,是否须要这么复杂的观测与同步机制,目前尚无定论,但在整个前端开发逐步自动化的大趋势下,这也是一种值得尝试的东西。

复杂的关联监控容易致使预期以外的结果:

慕容复要复国,天天读书练武,各类谋划 王语嫣观察到了这种现象,认为表哥不爱本身了 段誉看到神仙姐姐闷闷不乐,天天也茶饭不思 镇南王妃心疼爱子,处处调查这件事的原委,意外发现段正淳还跟旧爱有联系 ……

总之这么下来,最后影响到哪里了都不知道,谁让丘处机路过牛家村呢?

因此,变动的关联监控是很复杂的一个体系,尤为是其中产生了闭环的时候。搭建整个这么一套东西,须要极其精密的设计,不然熟悉整套机制的人只要用特定场景轻轻一推就倒了。灵智上人虽然武功过人,接连碰到欧阳锋,周伯通,黄药师,所有都是上来就直接被抓了后颈要害,大体就是这意思。

polymer实现了一个observe-js,用于观测数组、对象和路径的变动,有兴趣的能够关注。

在有些框架,好比aurelia中,是混合使用了存取器和观察模式,把存取器做为观察模式的降级方案,在浏览器不支持observe的状况下使用。值得一提的是,在脏检测方式中,变动是合并后批量提交的,这一点经常被另外两种方案的使用者忽视。其实,即便用另外两种方式,也仍是须要一个合并与批量提交过程。

怎么理解这个事情呢?数据的绑定,最终都是要体现到界面上的,对于界面来讲,其实只关注你每一次操做所带来的数据变动的始终,并不须要关心中间过程。好比说,你写了这么一个循环,放在某个按钮的点击中:

for (var i=0; i

咱们在赋值的时候,通常是在外层总体赋值一个相似数组的数据,而不是本身挨个在每一个列表项上赋值,否则就很麻烦。可是若是内外层持有相同的引用,对组件的封装性很不利。

好比在刚才这个例子里,假设数据源以下:

var arr = [ {name: "Item1"}, {name: "Item2"}, {name: "Item3"} ];

经过相似这样的方式赋值给界面组件,而且由它在内部给每一个子组件分别进行数据项的赋值:

list.data = arr;

赋值以后会有怎样的结果呢?

console.log(list.data == arr); console.log(listitem0.data == arr[0]); console.log(listitem1.data == arr[1]); console.log(listitem2.data == arr[2]);

这种方案里面,后面那几个log输出的结果都会是true,意思就是内层组件与外层共享数据,一旦内层组件对数据进行改变,外层中的也就改变了,这明显是违背组件的封装性的。

因此,有一些方案会引入Immutable Data的概念。在这些方案里,内外层组件的数据是不共享的,它们的引用不一样,每一个组件其实是持有了本身的数据,而后引入了自动的赋值机制。

这时候再看看刚才那个例子,就会发现两层的职责很清晰:

外层持有一个相似数组的东西arr,用于造成整个列表,但并不关注每条记录的细节 内层持有某条记录,用于渲染列表项的界面 在整个列表的造成过程当中,list组件根据arr的数据长度,实例化若干个listitem,而且把arr中的各条数据赋值给对应的listitem,而这个赋值,就是immutable data起做用的地方,实际上是把这条数据复制了一份给里面,而不是把外层这条记录的引用赋值进去。内层组件发现本身的数据改变以后,就去进行对应的渲染 若是arr的条数变动了,外层监控这个数据,而且根据变动类型,添加或者删除某个列表项 若是从外界改变了arr中某一条记录的内容,外层组件并不直接处理,而是给对应的内层进行了一次赋值 若是列表项中的某个操做,改变了自身的值,它首先是把本身持有的数据进行改变,而后,再经过immutable data把数据往外同步一份,这样,外层组件中的数据也就更新了。

因此咱们再看这个过程,真是很是清晰明了,并且内外层各司其职,互不干涉。这是很是有利于咱们打造一个全组件化的大型Web应用的。各级组件之间存在比较松散的联系,而每一个组件的内部则是封闭的,这正是咱们所须要的结果。

说到这里,须要再提一个容易混淆的东西,好比下面这个例子:

若是咱们为了给inner-component作一些样式定位之类的事情,极可能在内外层组件之间再加一些额外的布局元素,好比变成这样:

这里中间多了一级div,也多是若干级元素。若是有用过Angular 1.x的,可能会知道,假如这里面硬造一级做用域,搞个ng-if之类,就可能存在多级做用域的赋值问题。在上面这个例子里,若是在最外层赋值,数据就会是outer -> div -> inner这样,那么,从框架设计的角度,这两次赋值都应当是immutable的吗?

不是,第一次赋值是非immutable,第二次才须要是,immutable赋值应当仅存在于组件边界上,在组件内部不是特别有必要使用。刚才的例子里,依附于div的那层变量应当仍是跟outer组件在同一层面,都属于outer组件的人民内部矛盾。

这里是facebook实现的immutable-js库

7.6 Promise与异步

前端通常都习惯于用事件的方式处理异步,但不少时候纯逻辑的“串行化”场景下,这种方式会让逻辑很难阅读。在新的ES规范里,也有yield为表明的各类原生异步处理方案,可是这些方案仍然有很大的理解障碍,流行度有限,很大程度上会一直停留在基础较好的开发人员手中。尤为是在浏览器端,它的受众应该会比node里面还要狭窄。

前端里面,处理连续异步消息的最能被普遍接受的方案是promise,我这里并不讨论它的原理,也不讨论它在业务中的使用,而是要提一下它在组件化框架内部所能起到的做用。

如今已经没有哪一个前端组件化框架能够不考虑异步加载问题了,由于,在前端这个领域,加载就是一个绕不过去的坎,必须有了加载,才能有执行过程。每一个组件化框架都不能阻止本身的使用者规模膨胀,所以也应当在框架层面提出解决方案。

咱们可能会动态配置路由,也可能在动态加载的路由中又引入新的组件,如何控制这些东西的生命周期,值得仔细斟酌,若是在框架层面全异步化,对于编程体验的一致性是有好处的。将各种接口都promise化,可以在可维护性和可扩展性上提供较多便利。

咱们以前可能熟知XMLHTTP这样的通讯接口,这个东西虽然被广为使用,可是在优雅性等方面,存在一些问题,因此最近出来了替代方案,那就是fetch。

细节能够参见月影翻译的这篇【翻译】这个API很“迷人”——(新的Fetch API)

在不支持的浏览器上,也有github实现的一个polyfill,虽然不全,但能够凑合用window.fetch polyfill

你们能够看到,fetch的接口就是基于promise的,这应当是前端开发人员最容易接受的方案了。

7.7 Isomorphic JavaScript

这个东西的意思是先后端同构的JavaScript,也就是说,好比一块界面,能够选择在前端渲染,也能够选择在后端渲染,值得关注,能够解决像seo之类的问题,但如今还不能处理很复杂的情况,持续关注吧。

8. 小结

很感谢能看到这里,以上这些是我近一年的一些思考总结。从技术选型的角度看,作大型Web应用的人会很痛苦,由于这是一个青黄不接的年代,目前已有的全部框架/库都存在不一样程度的缺陷。当你向将来看去,发现它们都是须要被抛弃,或者被改造的,人最痛苦的是在知道不少东西很差,却又要从中选取一个来用。@严清 跟@寸志 @题叶讨论过这个问题,认为如今这个阶段的技术选型难作,不如等一阵,我彻底赞同他们的观点。

选型是难,可是从学习的角度,可真的是挺好的时代,能学的东西太多了,我天天路上都在努力看有可能值得看的东西,可仍是看不完,只能努力去跟上时代的步伐。

如下一段,与诸位共勉:

It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way--in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有