从 1.x 升级到 2.0
2.0 是一次完整重构:修复了 1.x 在注册、失效、事件上的一系列问题,统一了命名,去掉了 vue-demi。大部分页面只需要改几处写法。
暂时无法升级的项目,可以继续查阅 1.x 文档。
升级步骤
- 确认 Vue 版本 ≥ 2.7(Vue 3 不受影响);
- 升级依赖:
pnpm add vuepg@^2; - 按下面的对照表逐项替换;
- 开启
debug: true走一遍主要页面,确认焦点行为符合预期。
环境
| 1.x | 2.0 |
|---|---|
支持 Vue 2.6(需 @vue/composition-api) | 最低 Vue 2.7 |
| 依赖 vue-demi | 无运行时依赖 |
| 产物为 ESNext | 产物为 ES2015,兼容 webpack 4 |
导入时用 addEventListener 监听键盘 | 安装插件时用 addEventListener 监听键盘 |
安装与配置
| 1.x | 2.0 |
|---|---|
app.use(vuEPG) 后调用 setConfig | 可直接 app.use(VuEPG, config),setConfig 仍可用 |
defBackHandler | backHandler |
tempBackHandler | 移除(内部状态,使用 onBack) |
inject("epg") | 移除,使用 useVuEPG() 或 $epg |
| — | 新增全局属性 $epg |
ts
// 1.x
const epg = useVuEPG();
epg.setConfig({ defBackHandler: () => router.back() });
app.use(vuEPG);
// 2.0
app.use(VuEPG, { backHandler: () => router.back() });指令
| 1.x | 2.0 |
|---|---|
v-epg-item="{ class: 'x' }" | v-epg-item="{ focusClass: 'x' }" |
| — | 新增 disabled(item 与 group 均支持) |
事件
2.0 的事件都是原生 CustomEvent,并且统一加上了 epg- 前缀,避免与浏览器原生的 focus、blur 冲突(<button>、<a> 被鼠标点击时,浏览器也会派发原生 focus)。详见 事件。
| 1.x | 2.0 |
|---|---|
@focus / @blur | @epg-focus / @epg-blur |
@up / @down / @left / @right | @epg-up / @epg-down / @epg-left / @epg-right |
组的 @enter | @epg-enter,焦点从组外进入时总会触发(1.x 仅在进入嵌套组时触发) |
组的 @leave(未实现) | @epg-leave |
item 的 @enter(获得焦点时触发) | 移除,使用 @epg-focus |
@up="":存在监听器即阻止默认移动 | @epg-up.prevent:显式取消;只监听不取消时照常移动 |
@up="epg.move(top)" | @epg-up="epg.move(top)":处理函数中移动了焦点,默认移动自动跳过 |
方向处理函数参数为 (item, next) | 参数为事件对象,event.detail 为 { node, direction } |
| 组的方向事件只检查最内层组 | 依次检查焦点即将离开的每一层组 |
组的方向事件在调用 epg.move("up") 时也会触发 | 只在按键时触发 |
vue
<!-- 1.x -->
<div v-epg-item @focus="onFocus" @up="">...</div>
<!-- 2.0 -->
<div v-epg-item @epg-focus="onFocus" @epg-up.prevent>...</div>行为变化
| 场景 | 1.x | 2.0 |
|---|---|---|
| 没有焦点时按方向键 | 以第一个注册的元素为起点移动 | 焦点落在页面入口(default 优先) |
| 焦点元素被隐藏 / 被 KeepAlive 缓存后按方向键 | 不响应,焦点卡住 | 焦点回到页面入口 |
| 移动到空组 | 不移动焦点,不返回结果 | 返回 false,空组不会成为目标 |
焦点元素重渲染(如 :class 变化) | 焦点 class 可能被冲掉 | 自动补回 |
嵌套组件都注册了 onBack | 互相覆盖,子组件卸载时清空父组件的 | 最内层优先,卸载后自动恢复 |
move() 等方法的返回值 | 无 | boolean,表示焦点是否移动 |
API 对照
| 1.x | 2.0 |
|---|---|
getFoucsClass() | getFocusClass() |
getTargetByDirection(direction) | findTarget(direction) |
getItemByHTMLElement(el) | getNodeByElement(el) + isEPGItem() |
getGroupByHTMLElement(el) | getNodeByElement(el) + isEPGGroup() |
getChild(el) | getNodeByElement(el) |
isEPGItem(el) / isEPGGroup(el)(参数为元素) | isEPGItem(node) / isEPGGroup(node)(类型守卫);判断元素用 isEPGItem(getNodeByElement(el)) |
getGroupByItem(item) | getParentGroup(item) |
getParentGroupByHTMLElement(el) | getParentGroup(el) |
getGroupChildrenByHTMLElement(el) | getChildren(group) |
getGlobalGroupChildren() | getChildren() |
group.children | getChildren(group) |
getItemsByGroup(group)(返回元素数组) | getItemsInGroup(group)(返回 EPGItem 数组) |
getParentsByHTMLElement(el) | 移除,请直接使用 DOM API |
item.isFocus | getCurrentItem() === item |
dataContainer、currentConfig、keyActions、registerItem 等内部成员 | 移除 |
按键 API 对照
| 1.x | 2.0 |
|---|---|
getCurrentKeyActions() | getKeyActions()(只读快照) |
setAction(name, codes, callback, preventDefault) | setKeyAction(name, { codes, callback, preventDefault }) |
removeAction(name) | removeKeyAction(name);删除内置事件时抛出错误 |
setActionCallback(name, callback) | updateKeyAction(name, { callback }) |
setActionPreventDefault(name, value) | updateKeyAction(name, { preventDefault: value }) |
addCodeToAction(name, codes) | addKeyCodes(name, codes) |
removeCodeFromAction(name, codes) | removeKeyCodes(name, codes) |
操作不存在的事件时 console.error | 抛出错误 |
PAGEACTION | PAGE |
回调参数 (code) | (code, event) |
ts
// 1.x
epg.setAction("ALERT", ["KeyK", 75], () => alert("K"), false);
epg.setActionPreventDefault("ALERT", true);
// 2.0
epg.setKeyAction("ALERT", { codes: ["KeyK", 75], callback: () => alert("K") });
epg.updateKeyAction("ALERT", { preventDefault: true });弹窗与原生返回
2.x 会拒绝尚未渲染的目标;设置 v-show 或挂载弹窗后,先等待 nextTick() 再移动焦点:
ts
show.value = true;
await nextTick();
epg.move(cancel.value);旧版空方向监听器拦截移动的写法要换为 @epg-left.prevent 等明确取消事件的写法。关闭弹窗时检查原目标仍然挂载、可用,再复焦;目标失效时回到业务入口。
Android 返回桥可以直接调用 epg.back(),方向键使用 epg.navigate()。完整示例见完整示例和原生按键接入。