Globals in Polymer


作者:王納米

責任編輯:《代碼指南》編輯部

首發於《代碼指南》(daimazhinan.com

《代碼指南》編輯部保留所有權利


0 Introduction

Polymer 中 element(component)主宰一切。一個自然的需求是,在 element 間進行通訊與數據共享。

1 Communication

1.1 Published property

Polymer 會自動根據 element 的 attributes="valueA"生成相應的 getter,setter,以及 change watcher(即@valueAChanged(oldValue, newValue)),這就是所謂的published property。這實質上就是 element 的 public API。

通過調用其它 element 的 API 就是最基本的通訊方式。即,在 elementA 中:

@.$.elementB.valueB = 'foo'

而在 elementB 中使用 watcher:

Polymer
  valueBChanged: (oldValue, newValue) ->
    console.log "valueB changed from #{oldValue} to #{newValue}"  

1.2 core-signals

上述通過 public attribute 進行通訊的方式十分繁瑣。Polymer 中提供了core-signals用以進行通訊:

<link rel="import" href="bower_components/core-signals/core-signals.html">
<polymer-element name="my-app">
  <template>
    <core-signals on-core-signal-event-foo="{{ handle_event_foo }}"></core-signals>
    <child-element></child-element>
  </template>
  <script>
    Polymer
      handle_event_foo: (e, message, sender) ->
        console.log "event foo got fired with message: #{message}"
  </script>
</polymer>

<polymer-element name="child-element">
  <template>
    <paper-button on-click="{{ fire_foo }}">fire_foo</paper-button>
  </template>
  <script>
  switch_to_world_map: ->
    @fire 'core-signal',
      name: 'event-foo'
      data: 'one message'
  </script>
</polymer-element>

TIPS: 代碼並不嚴謹,比如沒有 import element,而且實際上也不能直接在<script>中寫 coffee,僅作示意之用。下同。

有關core-signals還有幾點額外說明:

  1. 一個core-signals可以接收多個 signal,即
<core-signals
    on-core-signal-event-a="{{ handle_event_a }}"
    on-core-signal-event-b="{{ handle_event_b}}">
  1. signal 是發往全局的,多個 element 也可以接收同樣的 signal,它們都會被觸發。
  2. 可用asyncFire替代fire來進行異步觸發。
  3. 如果很多 element 需要引入 <core-signals>,可以建立一個 <base-element> 然後 extend 它。

1.3 Others

理論上也可以使用其它 Javascript 通用的方式進行通訊,例如 sharedWorker 等。此處不展開。

2. Global sharing

2.1 Published property

依然是最基本的方式,在使用 element 時通過 attributes 共享 object,即:

<polymer-element name="parent-element" attributes="valueFoo">
  <template>
    <child-element valueFoo="valueFoo"></child-element>
  </template>
  <script>
    Polymer
      ready: ->
        valueFoo = { foo: 'bar' }
  </script>
</polymer-element>

其中 child-element 有 attributes="valueFoo"。

2.2 A custom global element

文檔中此處介紹了一種在全局共享數據的方式。具體來講,可以定義一個名爲 app-globals 的 element,通過 closure,使得所有的<app-globals>的 instance 都共享同樣的數據:

<polymer-element name="my-globals" attributes="userName">
  <script>
  (function() {
    var userName = 'Farmer John'

    Polymer({
       ready: function() {
         this.userName = userName;
       }
    })
  })()
  </script>
</polymer-element>

(TIPS:如果使用 coffee 的話,默認即有此 closure wrapper。)

此時在其它任何 elemet 中,只要引入<my-globals>即可獲取其中的數據。

<polymer-element name="my-app">
  <template>
    <my-globals id="globals"></my-globals>
  </template>
  <script>
    Polymer
      ready: ->
        console.log @.$.globals.userName
  </script>
</polymer-element>

但是,這裏還有兩個問題:

  1. 目前只能支持一個名爲userName的變量。更多的變量就要寫成諸如attributes="valueA valueB valueC"的形式。
  2. 基於 javascript 的特性,如果此處共享一個字符串變量,那麼在一個 element 中對其進行修改是無法傳遞到全局的,即這隻是一個全局共享的只讀變量。

這兩個問題的答案是一樣的:使用一個 object 來包裝共享的數據:

<polymer-element name="my-globals" attributes="values">
  <script>
    values = {}
    Polymer
      ready: ->
        @values = values
  </script>
</polymer-element>

如此,我們便構造了一個全局共享的,可讀可寫的 object。

此時,我們已經可以通過<span>{{ globals.values.userName }}</span>來在頁面中直接獲取全局共享的值。但若要修改其中的值,還需要使用代碼(雖然本刊名爲《代碼指南》,但我們依然不喜歡寫無用的重複的代碼)。我們希望可以隨時直接使用<my-globals userName="Farmer John"></my-globals>來修改全局變量。顯然,爲達此目的,我們需要能夠獲取 element 的 attributes,此時需要的是 Polymer 在 element 上維護的this.attributes值。我們作出如下修改:

<polymer-element name="my-globals" attributes="values">
  <script>
    values = {}
    Polymer
      ready: ->
        @values = values
        for attr in @attributes
          @values[attr.nodeName] = attr.value
  </script>
</polymer-element>

此時,我們既可以在頁面中使用<my-globals userName="Farmer John"></my-globals>,也可以在代碼中使用@.$.globals.values.userName = 'Farmer John'來修改全局變量。

更多的討論:

  1. 其中的@attributes,即是 Polymer 在 element 上維護的this.attributes。對於<my-element vertical layout foo="bar"></my-element>,其this.attributes大致形如:
{
    0: { nodeName: 'vertical', value: '' },
    1: { nodeName: 'layout', value: '' },
    2: { nodeName: 'foo', value: 'bar' }
}
  1. 同<core-signals>一樣,可以定義<base-element>,在其中引入<my-globals>,其它 elements 對 base 進行 extend。

實踐中,可能需要在 values 有修改的時候進行操作。比如,實時地將其值存入 localStorage。

此時如果要使用一個change watcher(即@valuesChanged)來監視values的變化,那麼一定要注意:如果 watch 的對象是一個 object,那麼對 object 進行修改@values.foo = 'bar',是不會觸發 watcher 的。只有修改 values 本身(@values = 'new value as a string',纔會觸發 watcher。

而 Polymer 中的observe雖然可以 watch「某 object 某 attr」的變化,但是這需要我們事先知道該 attr 的名稱,通用性不足。

因此實踐中,可以在<my-globals>上手動定義 getter 與 setter 作爲操作數據的接口:<my-globals attributes="set_values get_values"></my-globals>,然後在set_values method 中進行其它操作,舉例如下:

set_values: (hash) ->
   new_values = @.$.localStorage.value
   for key of hash
     new_values[key] = hash[key]
   @.$.localStorage.value = new_values
   @.$.localStorage.save()  

相關閱讀:

2.3 core-meta

Polymer 提供了core-meta來進行簡單的數據存儲。其在功能上類似於我們在上一節中實現的全局共享的變量,但是更加輕量:

要添加或修改一個全局變量,可以在任意位置引用:

<core-meta id="user" userName="Farmer John"></core-meta>

要讀取一個值,也要先有一個<core-meta>,可以是直接添加在 html 中的,也可以在需要讀取數據時動態添加:

meta = document.createElement('core-meta');
# 通過 id 來讀取一個值:
console.log meta.byId 'user' # Farmer John
# 列出所有定義過的<core-meta>
console.log meta.metaArray
# 列出所有定義過的 meta 值
console.log meta.metaData

一個<core-meta>中還可以包含更多的值:

<core-meta id="user" userName="Farmer John">
  <property name="lastName" value="John"></property>
</core-meta>

(注意:目前,這種方式定義的 property,要得到其值似乎有些繁瑣,需要手動遍歷meta.metaArray所返回的 DOM)

相關閱讀:

2.4 core-shared-lib

項目變得複雜之後,需要共享的往往不止有數據,還有代碼本身。

core-shared-lib用於引用支持 JSONP 的 javascript 代碼:

<core-shared-lib on-core-shared-lib-load="{{load}}" url="https://apis.google.com/js/plusone.js?onload=%%callback%%">

在實踐中不推薦使用此方法,應儘量使用<script src="lib.js"></script>,因爲它會破壞 Polymer(實際上是 webcomponents,以後應該是由瀏覽器)所維護的 imports。

更多內容參見文檔:

相關閱讀:

FIN.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容