[使用 Weex 和 Vue 開發原生應用] 3 使用 Vue 框架的特性

系列文章的目錄在 ? 這裏html

除了 Vue.js 在 Weex 和 Web 中的差別之外,Vue.js 自身的各類特性都是能夠正常使用的。因此,這篇文章其實和 Weex 並沒多大關係,我就給你們簡單羅列幾個在 weex-hackernews 項目裏用到的特性(這幾個特性愈來愈高階):vue

混合(mixin)

Vue mixin 文檔node

mixin 是一種複用代碼的技巧,能夠將多個組件共同的邏輯抽象成 mixin,官方文檔中寫的很詳細。它會將 mixin 中定義的方法合併到組件上,若是包含生命週期函數,則先調用 mixin 中的方法再執行組件自身的;若是屬性有衝突,組件自身的方法會覆蓋掉 mixin 中定義的。git

定義 mixin

在 weex-hackernews 中的 src/mixin/index.js 裏定義了一個簡單的 mixin:github

export default {
  methods: {
    jump (to) {
      if (this.$router) {
        this.$router.push(to)
      }
    }
  }
}

它只包含了一個方法,就是 jump ,它經過調用路由的 push 方法來實現頁面跳轉。web

註冊 mixin

而後,在 src/entry.js 中全局註冊了這個 mixinshell

import Vue from 'vue'
import mixins from './mixins'

// register global mixins.
Vue.mixin(mixins)

在實際應用中應該謹慎地註冊全局 mixin,若是不是全局通用的操做,建議仍是隻給用到的組件添加 mixin。apache

使用 mixin

如此一來,全部組件都能調用到 jump 方法,例如 app-header.vue 這個組件,它自身沒有 <script> 標籤,可是可以在模板中給點擊事件綁定 jump 函數。在 weex-hackernews 裏,點擊左上角的 logo 能夠返回首頁。segmentfault

<div class="logo" @click="jump('/')">
    <image class="image" src="https://news.ycombinator.com/favicon.ico"></image>
</div>

通常來講你們都傾向認爲組合要優於繼承,在 Vue 裏使用 mixin 實現「組合」,比用 ParentComponent.extend({}) 模擬繼承要好一些。並且在 .vue 文件裏,要想繼承其餘組件也挺麻煩的。從另一個角度上來說,若是真的是有一些邏輯能抽離出來,也優先考慮寫成獨立的模塊,export 可用的接口,要用的時候直接 import 進來便可。依賴框架自己的特性越少,代碼就越容易複用。數組

過濾器(filter)

Vue filter 文檔

在使用 v-bind 指令的時候,支持使用過濾器 (filter) 對綁定的值再進行處理;接收變量中的原始值做爲參數,返回處理後的值,支持將多個過濾器串聯在一塊兒使用,相似 shell 命令中「管道」的寫法。

註冊過濾器

過濾器的使用方法和 mixin 相似,如今 src/filter/index.js 中寫好要註冊的方法,在 src/entry.js 中全局註冊了這些 filters

import Vue from 'vue'
import * as filters from './filters'

// register global utility filters.
Object.keys(filters).forEach(key => {
  Vue.filter(key, filters[key])
})

使用過濾器

在 weex-hackernews 的 story.vue 文件中第六行用到了 host 過濾器,用來獲取 url 中的網站地址。

<text class="small-text" v-if="story.url">({{ story.url | host }})</text>

第十三行用到了 timeAgo 過濾器,會把時間戳轉成可讀時間字符串。

<text class="small-text text-cell"> | {{ story.time | timeAgo }} ago</text>

src/filter/index.js 中的實現是這樣的:

function host (url) {
  if (!url) return ''
  const host = url.replace(/^https?:\/\//, '').replace(/\/.*$/, '')
  const parts = host.split('.').slice(-3)
  if (parts[0] === 'www') parts.shift()
  return parts.join('.')
}

function timeAgo (time) {
  const between = Date.now() / 1000 - Number(time)
  if (between < 3600) {
    return pluralize(~~(between / 60), ' minute')
  } else if (between < 86400) {
    return pluralize(~~(between / 3600), ' hour')
  } else {
    return pluralize(~~(between / 86400), ' day')
  }
}

效果以下:

filter

除此以外

內容槽(<solt>

Vue slots 文檔

槽是 Vue.js 中用來實現「內容分發」的功能的,能夠理解爲內容的佔位符。參考 external-link.vue 例子:

<template>
  <div @click="open">
    <slot></slot>
  </div>
</template>

story.vue 裏使用時,<external-link> 標籤中的內容將會替換到 <slot>

<external-link :url="story.url" class="story-link">
  <text class="story-title">{{story.title}}</text>
  <text class="small-text" v-if="story.url">({{ story.url | host }})</text>
</external-link>

遞歸組件

Vue 遞歸組件的文檔

Vue 能夠實現遞歸組件,能夠在本身的模板中調用本身,只須要你寫上 name屬性就能夠了。

支持寫遞歸組件,聽起來好像是框架在故意炫技,爲何會有這種奇葩功能?由於的確有這種奇葩需求…… 例如 Hacker News 裏的評論,是能夠無限展開的。

圖片描述

其實每條評論都有一個惟一 id 的,每條評論下邊的回覆的 id 都存在 kids 屬性上;存的只是 id 不是真實的評論數據。從網絡獲取到某條評論以後,還有根據 kids 數組中的 id 獲取評論下的全部回覆,而後獲取回覆下的全部評論,而後獲取評論下的全部回覆…… 這很明顯是個遞歸過程。即便評論的數據用樹形結構去存,你不知道樹的深度,仍是得用遞歸的方式把全部評論渲染出來。

【評論】和【回覆】是一個意思,爲了好表達才用的倆詞,漢語就是比英語詞多……

下面問題來了,如何渲染這種遞歸的評論?用正常的組件好像很難實現這種效果,我沒想到很合適的寫法,有興趣能夠試着想一下。

簡單的遞歸組件

讓組件支持遞歸很簡單,只要加上 name 屬性就好了,而後就能夠在本身的模板中調用本身。

<!-- comment.vue -->
<template>
  <div>
    <text>tips:</text>
    <comment></comment>
  </div>
</template>

<script>
  export default {
    name: 'comment'
  }
</script>

上邊就是一個最簡單的遞歸組件的例子,寫了 name 屬性並且在模板中用了本身。可是它死循環了,沒有結束條件,最終會報一個 "max stack size exceeded" 的錯。

真實的遞歸組件

在 weex-hackernews 裏,comment.vue 就是一個遞歸組件。它用於渲染一條評論,在內部有使用它本身渲染本身的評論。效果以下:

圖片描述

下邊是 <comment> 組件簡化後的核心代碼:

<template>
  <div v-if="comment">
    <text>{{comment.text}}</text>
    <comment v-for="id in comment.kids" :id="id"></comment>
  </div>
</template>

<script>
  import store from '../store'

  export default {
    name: 'comment',
    props: ['id'],

    computed: {
      comment () {
        return store.state.items[this.id]
      }
    }
  }
</script>

<comment> 組件中,comment 屬性的數據是根據當前的 id 屬性從 store 中取出來的,而後根據 comment.kids 循環建立多個 <comment> 標籤,而且把 id 屬性傳下去。子 <comment> 標籤根據傳遞過來的 id 屬性從 store 中獲取 comment 數據渲染自身,而後根據 comment.kids 循環建立多個 <comment> 標籤,而且把 id 屬性傳下去。…… 依次遞歸。

再具體的細節,就建議直接看 comment.vue 的代碼了。至於 store 是如何獲取數據的,關注後續講 Vuex 的文章。

小結

通篇講的是 Vue 2.0 的特性,與 Weex 沒有半毛錢關係,我想說明的是,這些特性在 Weex 裏都是徹底可用的。

Vue 2.0 的特性比較多,能力很強大,這裏只講了很小一部分;只要思路清晰,各類奇葩效果也能優雅的實現。在 Vue 2.0 的全部特性中,只要不是強依賴與 Web 自己的特性,均可以在 Weex 裏用。若是你對 Web 平臺有足夠的瞭解,在寫代碼的時候就能時刻清楚哪些特性是 Web / DOM 強相關的,哪些是跨平臺通用的,這對你寫跨端(Weex)或者跨棧(node.js)的程序頗有幫助。

相關文章
相關標籤/搜索