[使用 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)的程序颇有帮助。

相关文章
相关标签/搜索