开始
介绍
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>
显示结果

按键去抖优化
某些物理遥控器在按下按钮时, 有可能高速触发按键事件, 这对应用会产生不良后果, 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 () {
},
...
}