开始

介绍

TVVM是一个专门为TV WEB APP开发的MVVM模式框架, 它帮助开发者快速开发应用而无需关心焦点控制, 键盘绑定, 数据绑定等通用逻辑。 它没有依赖, 体型小巧(20kb)

下载

通过npm下载

你的系统上需要安装有nodejs
          $ npm install -S tvvm
        

手动下载

你也可以通过手动下载的方式, 然后通过<script>标签进行引用, 下载链接

使用

          
  // es module
  import * as TVVM from 'tvvm'

  var tv = new TVVM({
  ...options
  })
          
          
  <!-- html -->
  <script src="https://unpkg.com/tvvm/dist/tvvm.min.js"></script>
  <script>
      var tv = new TVVM({
        ...options
      })
  </script>
          
        

特色

焦点自动控制

TV应用的特点是使用遥控器控制焦点移动, 而并非鼠标点击/手势触摸事件来触发控件, 从而进行下一步操作。 这需要我们通过编程的方式控制焦点的移动和跳转。TVVM致力于减少焦点移动的代码逻辑, 通过配置t-index索引, 而非编程的方式解决焦点的移动。
TVVM把应用分成横轴空间和纵轴空间, 例如 声明 t-index="0-1" 代表应用是第1行 (row=0) 第2列 (col=1)为当前焦点 , 当你点击遥控器的向下移动按钮时 row 增加,col 不变, 此时焦点从 t-index="0-1"移动至 t-index="1-1"的元素上 。 这些在TVVM内部已经做好了, 开发者只需要在焦点块元素上声明 t-index索引即可。 除此之外, TVVM还提供了丰富的选项, 例如默认焦点, 焦点元素样式, 边界穿越, 按键值配置等。

focus选项

          
  new TVVM({
      ...,
      focus: {
        defaultFocusIndex: '0-1', // 默认焦点
        circle: {
          horizontal: true, // 当焦点移动到边界时在水平方向可穿越
          vertical: true // 当焦点移动到边界时在垂直方向可穿越
      },
          ...options
    }
  })
          
          
  <style>
    *:focus {
        outline: none;
        border: 2px solid #fff;
    }
  </style>
  <div class="tv">
      <div>
          <div>
              <div t-index="0-1">0-1</div>
              <div t-index="0-2">0-2</div>
              <div t-index="0-3">0-3</div>
          </div>
      </div>
      <div>
          <div t-index="1-0, 2-0" real-focus="true">
              <span>1-0,</span> <span>2-0</span>
          </div>
          <div>
              <div t-index="1-1, 1-2, 1-3">
                  <span>1-1,</span> <span>1-2,</span> <span>1-3</span>
              </div>
              <div>
                  <div t-index="2-1">2-1</div>
                  <div t-index="2-2">2-2</div>
                  <div t-index="2-3">2-3</div>
              </div>
          </div>
      </div>
  </div>
          
        

显示结果

t-index

按键去抖优化

某些物理遥控器在按下按钮时, 有可能高速触发按键事件, 这对应用会产生不良后果, TVVM在绑定事件的同时在内部会优化操作逻辑, 利用函数去抖控制按键触发频率, 防止因为物理设备差异导致应用逻辑混乱。

数据驱动

TVVM与大多数mvvm思想的前端框架一样, 采用数据驱动的开发模式, 简单来讲,数据驱动使开发者只用关系数据的修改, 而无需手动将数据同步至视图。 以下是 t-value 指令来实现双向数据绑定的例子, span标签内的内容会随着input输入框的值的改变而改变
          
    data: function () {
        return {
            demoInputValue: 'demo'
        }
    }
          
          
  <input t-value="data.demoInputValue" />
  <span>{{data.demoInputValue}}</span>
          
        

demo

{{data.demoInputValue}}

API

实例化

new TVVM 返回一个tv实例, 作为该页面的全局单例入口
            
  var tv = new TVVM({
    el: '#tv',
    data () {
        return {

        }
    },
    focus: {

    },
    methods: {
        testFn: function (a, b) {

        }
    }
  })
            
          

指令

TVVM内置指令系统, 包含了一些常用的指令, 用于处理简单的模版逻辑.TVVM内置指令通常以 t- 开头作为标识
            
  <html>
    <head></head>
    <body>
        <div id="tv">
            <div t-if="data.dialogVisible" class="dialog">{{data.dialog.title}}</div>
            <nav>
                <a t-for="item in data.linkList">{{item.label}}</a>
            </nav>
            <form>
              <input t-value="data.dialog.title" />
            </form>
            <div>
            <div " t-index="1-1, 1-2, 1-3"></div>
            <div>
              <div t-index="2-1"></div>
              <div t-index="2-2"></div>
              <div t-index="2-3"></div>
            </div>
          </div>
        </div>
    </body>
  </html>
            
          

t-index

用于指定焦点区块元素的二维空间位置索引,以便用户点击遥控器方向按键时移动焦点,t-index="x-y", 例如t-index="0-0"代表第一排第一列的元素
              
 <div class="tv">
    <div t-index="0-0">0-0</div> <!-- 第1排第1个元素 -->
    <div t-index="0-1">0-1</div>
    <div t-index="0-2">0-2</div>
    <!-- 第2排第1个元素, 纵向占据2个空间 -->
    <div t-index="1-0, 2-0">1-0, 2-0</div> 
    <div>
        <!-- 第2排第2个元素, 横向占据3个空间 -->
        <div t-index="1-1, 1-2, 1-3">1-1, 1-2, 1-3</div> 
        <div>
            <div t-index="2-1">2-1</div> <!-- 第3排第2个元素 -->
            <div t-index="2-2">2-2</div> <!-- 第3排第3个元素 -->
            <div t-index="2-3">2-3</div> <!-- 第3排第4个元素 -->
        </div>
    </div>
</div>
              
            

t-if

用于显示/隐藏dom节点, 该指令接收一个在data参数上存在的布尔值
              
<div t-if="data.dialogVisible"></div>
              
            

t-for

用于循环指定的dom节点, 该指令接收一个表达式,如下item代表数组的每一项, array是data上定义的一个数组
              
<ul>
  <li t-for="item in data.array">{{item.label}}</li>
</ul>
              
            

data bind

数据插值, 双花括号({{}})内接收一个data的属性.用于页面内数据插值
              
data: { name: 'float' }
              
              
<span>{{data.name}}</span>
<!-- 渲染为 -->
<span>float</span>
              
            

t-class

样式表绑定, t-class接收一个data上已经定义的样式名数组或对象
              
  data: function () {
      return {
          classList1: ['container', 'container-row'],
          classList2: {
              'container': true,
              'container-row': false,
              'container-col': true
          }
      }
  }
              
              
  <div t-class="data.classList2"></div>
  <!-- 渲染为 -->
  <div class="container container-col"></div>
              
            

t-bind

属性绑定,t-bind:name="data.name" 简写形式为 :name="data.name"
              
  data: function () {
      return {
          id: 'billboard',
          height: '365',
          classname: 'spin'
      }
  }
              
              
    <div :id="data.id" :height="data.height" :class="data.classname"></div>
    <!-- 渲染为 -->
    <div id="billboard" height="365" class="spin"></div>
              
            

t-value

用于input输入数据与data上数据的绑定, input的value会实时同步至t-model绑定的数据
              
    data: function () {
        return {
            demoInputValue: 'demo'
        }
    }
              
              
    <input t-value="data.demoInputValue" />
    <span>{{data.demoInputValue}}</span>
              
            

demo

{{data.demoInputValue}}

event bind

事件绑定
              
    methods: {
      clickHandler: function () {
          // do something
      },
      clickHandler2: function (param1, param2) {
              
      }
    }
              
              
    <div @click="methods.clickHandler"></div>
    <div @click="methods.clickHandler2(data.inputValue)"></div>
              
            

选项

new TVVM接收一个选项对象作为唯一参数
            
var tv = new TVVM({
  el,
  data,
  focus,
  methods,
  lifeHooks,
})
            
          

el

new TVVM() 实例挂载的dom元素, 可以是一个元素查找符或者dom节点对象
              
new TVVM({
    el: '#tv',
})
              
            

focus

focus选项用于设置焦点移动, 键值绑定, 默认焦点等逻辑
              
new TVVM({
  focus: {
    defaultFocusIndex: '1-0',
    keysMap: {
      'up': {
        codes: [38, 103],
        handler: function (event, node, index, prevNode) {

        }
      },
      'g': {
        codes: [71],
        handler: function (event, node, index, prevNode) {
          console.log('you press g')
        }
      },
    },
    keysMapMergeCoverage: false,
    circle: {
      horizontal: true,
      vertical: true,
    }
  }
})
              
            

defaultFocusIndex (可选)

进入应用默认聚焦的元素的索引, 该参数为空时, 默认聚焦到页面首个焦点元素上
                
    focus: {
        defaultIndex: '0-1'
    }
                
              

activeClass (可选)

焦点元素的样式名
                
    focus: {
      activeClass: 'high-light'
    }
                
              

circle (可选)

定义焦点在水平/垂直方向上是否可循环移动, 默认false.
- horizontal (可选) 焦点元素在水平方向是否可以循环移动, 默认false
- vertival (可选) 焦点元素在水平方向上是否可以循环移动, 默认false
                
    focus: {
        circle: {
            horizontal: true,
            vertical: true
        }
    }
                
              

keysMap

遥控器键盘键值码映射表, 该参数为空时使用默认键值码映射表
- 'alias' 对应键值的别名
- codes 对应键值数组
- handler 对应按键值绑定的事件处理函数 参数分别是event(事件), node(当前焦点dom节点索引), index (当前焦点dom节点的t-index值), prevNode(上一个焦点dom节点索引)
                
    focus: {
      keysMap: {
      'up': {
        codes: [38, 104],
        handler: function (event, node, index, prevNode)
      },
      'down': {
        codes: [40, 98],
        handler
      },
      'left': {
        codes: [37, 100],
        handler
      },
      'right': {
        codes: [39, 102],
        handler
      },
      'enter': {
        codes: [13, 32],
        handler
      }
    }
  
                
              

keysMapMergeCoverage (可选)

键值映射表合并策略true为覆盖, false为合并
                
  focus: {
    keysMapMergeCoverage: false,
  }
                
              

data

tv单例的数据对象, 可以是一个函数或者对象, 当该参数为函数时, 取值为该函数的返回值
              
  new TVVM({
    data: function () {
        return {
            title: 'tvvm demo page',
            index: '0'
        }
    }
  })
              
            

methods

该参数是一个对象, 存放所有的方法函数
              
  methods: {
    methods1: function () {
        console.log('methods1')
    }
  }
              
            

hooks

生命周期钩子函数集合 - beforeCreate 在实例化之前调用, 此时不可访问data
- created 在实例化后调用,此时data已经设置响应式, 并可访问
- beforeMount 在实例被挂在到真实dom前调用
- mounted 在实例被挂在到dom上时调用
- beforeUpdate 响应式data变动从而导致视图更新前调用
- updated 响应式data变动从而导致视图更新后调用
- beforeDestory 在实例被销毁前调用
              
    hooks: {
      beforeCreate: function () {
          // this 指向tv实例
      },
      mounted: function () {
                
      },
      ...
    }