[Tips on Ember 2] How components works when out of Application?

这一篇照样一个简朴的例子所激发的思索。

你看,如今的框架和库,不论范围大小功用若干,它们在本质上都朝着“组件化”的思绪疾速演进着。Angular 有 directives,Angular 2应当也照样这个叫法;Ember 从 View 过渡到了 Component,而且接下来的迭代会朝向 WebComponent 的规范来想象生命周期及其 API;React 本身就是一个组件化的范式;另有 Polymer,那就是 Google 为 WebComponent 搞得一套 polyfills……人人都这么玩。

我们都没法完全准确的展望 WebComponent 能让 Web 进化到什么水平,然则如今已有了这么多可用的东西了,人人自然是摩拳擦掌的搞起。我也没有破例,在之前运用 Angular 的时刻就在全力做组件的笼统,把 directives 那一套算是玩儿转了——那个时刻并没有意想到一件事变,直到近来返回 Ember 今后才最先有所体味。几天前我在 Ember Community 提了个题目来议论此事,虽然也获得许多发起却照样迷迷糊糊的;厥后又和 @darkbaby123 询问了一番,谢谢他给了我许多启示。但是一直没有想到一个确实的运用场景来考证一番。

说了半天预计你们都看糊涂了:究竟什么事变啊?

Components 用来封装可重用的 HTML+CSS+JavaScript 片断,这很好,但是当下的框架们都要处置惩罚事宜托付的题目,这就意味着框架会给你一个范围来开辟你的运用,这个范围的边境就是框架用于事宜托付的临界线,出了这条线就是浏览器本身的事宜处置惩罚机制在作用了。那末,当你不能不“迈过”这条线的时刻,框架(以及它供应的组件化机制)该如何协助你呢?

举例来讲,Angular 的边境在你声明 ng-app 的处所(所以我见过不少人把它放在 <html></html> 上来扩展这个界线);Ember 默许在 <body></body> 上,固然你能够改;React 也是一样,你老是要把你的第一个 component 衬着到 <body></body> 下面。那末,当你要做的事变超越 body 之外,它们应当如何处置惩罚呢?

关于像 jQuery 如许以 DOM 为中间(固然另有 BOM)的东西来讲,这个题目很简朴——以 DOM 为中间就意味着浏览器有什么你就用什么,不过就是它原生的 API 不好用或不够用,你拿过来用 jQuery 封装一下就好了,换汤不换药。所以当你要操纵 body 之外的东西,原生的东西随意你用,比方 document(DOM),比方 window(BOM) 等等,你唯一要做的就是表面套一个 $() 壳子。

在我们的大脑模子里已习惯了把 HTML 和 DOM 视为一体,但除此之外 DOM 和 BOM 还供应了雄厚的接口来处置惩罚许多分外的事变,每个框架或库都邑多若干少供应这些分外接口的封装,比方 Angular 里的 $cookie$location,以至痛快完全的 $document$window,但是当这些东西和 component 关联在一起的时刻,事变会变得玄妙起来:什么能够做不能够做?什么时候/那边来做?这些题目的界线变得摇摆不定。

Angular 有 DI(依靠注入)的机制,在 directives 的层面上,它奇妙的想象了一个 Attribute Level 的 directive 定义,经由历程 DI 你能够把超越 body 之外的操纵经由历程 HTML 的属性绑定给其他的 Tag Level 的 directives。由于组件的存在范围被限定在 body 以内,这就是这类机制(现在)存在的意义地点。我们还不晓得当 WebComponents 尘嚣落定之时会给出我们如何的答案,固然届时 JavaScript 已有了 modules,所以全局污染的题目已不复存在,如今唯一不明朗的就是如何与组件的生命周期关联起来。

让我们来看一个例子。如今有许多运用都有如许的想象:Header 与 Main Content 没有显著的界线,看起来像一个团体。但假如 Main Content 的内容超越了浏览器一屏的高度,那末当用户向下转动的时刻,Header 会“浮”起来(经由历程下放的暗影)并牢固在窗口顶部,很不错的视觉结果。

题目就在于监听用户转动事宜的行动应当是发作在 BOM 范围内的,假如你的运用是基于以组件为中间的头脑开辟的,这个行动究竟应当在那里做?

这个题目实在会有许多变数,比方说你能够想象这个行动和任何详细的组件无关,而是在运用程序初始化的时刻直接实行。很好,然则有两个题目:

  1. 牢固 Header 并为它增加暗影是须要 Header 已存在于 DOM 当中的,通常在运用程序初始化的时刻这个前提还没有杀青

  2. 这个行动并非发作在全局范围以内的,比方说某个路由进入今后或某种组件衬着今后才发作

以上恣意一点都能够反对初始化实行这个计划,假如你斟酌久远和全面一些的话就必须另寻前途。

好,我不空话了,先把近来用 Ember 完成的这个例子代码写出来,末了我再说一点对此的主意吧。

第一步,把 Header 笼统为组件

这个很简朴,直接 ember generate component app-header 就好了,代码略过。

第二步,在组件衬着今后实行监听用户向下转动的事宜并为组件增加 class,这个 class 完成了暗影等结果。

const SCROLL_THRESHOLD = 50  // header' height is 50px

export default Ember.Component.extend({
  classNameBindings: ['sticky'],
  didInsertElement() {
    window.addEventListener('scroll', () => {
      if (window.scrollY >= SCROLL_THRESHOLD) {
        this.set('sticky', true)
      } else {
        this.set('sticky', false)
      }
    }
  }
})

第三步,当组件烧毁后,注销监听回调

这就能够发作在 body 之外的操纵能和组件的生命周期紧密联系在一起。这一点很主要,不论你用 Ember 照样 Angular/React,肯定要注意组件的生命周期,特别是组件烧毁时这些框架都邑供应对应的 hook,要注意清算“渣滓”,移除绑定,开释内存等等,防止内存走漏

const SCROLL_THRESHOLD = 50  // header' height is 50px

function _stickHeaderHandler() {
  if (window.scrollY >= SCROLL_THRESHOLD) {
    this.set('sticky', true)
  } else {
    this.set('sticky', false)
  }
}

export default Ember.Component.extend({
  classNameBindings: ['sticky'],
  didInsertElement() {
    window.addEventListener('scroll', _stickHeaderHandler.bind(this))  // remember to bind!!!
  },
  willDestroyElement() {
    window.removeEventListener('scroll', _stickHeaderHandler)
  }
})

DONE

我们还能够如何革新它呢?题目有二:

  1. 组件不应当固化特别的行动,假如这个组件是跨运用同享的(比方你宣布成 Addon),那末其他运用多是不须要置顶的运用者希冀的是以下的可选项:

    {{app-header stickyOnScroll=50}}
  2. 监听转动那一套行动假如不是组件特有的(这就派出了发步成 Addon 的前提)而是运用内同享的,则应当想办法笼统出去——监听转动这个事变很典范

关于题目一,答案已展现在那里了。组件都是能够通报参数或外部作用域的,应用此机制举行推断来实行可选行动,这是对用户友爱的行动。

关于题目二,在 Ember 里你至少有三个选项:

  1. 笼统成 Mixin。这个很直观,瑕玷是 Mixin 供应的属性不是 default value,它不能由你主动去掩盖,不够天真;

  2. 定义成新的 Component。须要继续的其他组件能够 extend 它,处理 Mixin 不够天真的题目,范围是只能给组件用——不过关于处置惩罚浏览器事宜和操纵 DOM/BOM 已够用了;

  3. 笼统成 Service。这个等价于 Angular 的 DI,能够由你本身定义雄厚的接口来设置和挪用,最天真,合适封装须要的外部接口等等。

关于 Service,详细的代码先 hold,今后我会特地讲 Service 在 Ember 里的用法。本日这个例子不合适笼统 Service,缘由就是上面的第二点。

当我在几天前对此还很疑心时,我一度以为像 Angular 的 Attribute Level Directives 才是处置惩罚此类题目的最好计划,但是 WebComponent 并没有 Attribute Level Component 这类想象,这也是我疑心的最初缘由。如今想想,Attribute Level Directives 即是疏忽组件的生命周期(固然它有本身的生命周期,然则和要附着的目的组件无关,你得治理两份),它把可选行动附着于目的组件的历程等同于你建立一个新的特别的 Service(特别的地方就在于它能够放在模版里),然后应用这个 Service 去写完成代码而且还能够再 DI 其他的 Services,以此来完成可选性和可复用性。这类想象乍看讨巧但也有许多瑕玷,比方说多个 directives 共存的时刻要斟酌优先级和行动掩盖的题目,比方说和将来的 WebComponents 不兼容革新起来很省事,等等。

如今我们看到,React 一最先做得就很不错(后起之秀自创了许多先辈们的经验教训),不过它只是一个衬着引擎,做大型运用还须要你在团体架构高低工夫;Ember 的架构很完全,之前的题目许多但如今都在逐一完美,想象思绪没有什么毛病,拿来做 UI 交互庞杂的 web 运用的确是很不错的挑选。

    原文作者:n͛i͛g͛h͛t͛i͛r͛e͛
    原文地址: https://segmentfault.com/a/1190000003753572
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞