多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Hippy-Vue 路由实战:@hippy/vue-router 接口、原生返回键与 HippyHistory 实现解析

Hippy-Vue 路由实战:@hippy/vue-router 接口、原生返回键与 HippyHistory 实现解析 跨平台移动开发前端【免费下载链接】HippyHippy is designed to easily build cross-platform dynamic apps. 项目地址https://gitcode.com/gh_mirrors/hi/Hippy点击查看免费下载Hippy-Vue 使用对 vue-router 的官方修改版hippy/vue-router实现页面路由它提供与 vue-router 一致的接口同时在原生环境Hippy 引擎下增加了 Android 硬件返回键导航等终端侧能力。本篇以 hippy-vue 路由文档 为主体结合仓库中 hippy-vue-router 源码 与 hippy-vue-demo 示例讲清这个路由库的接入方式、可用接口、与标准 vue-router 的差异尤其是 Android 返回键处理和页面切换动画的限制以及底层HippyHistory的导航栈实现原理。一、它是什么vue-router 的小幅修改版官方文档对该包的定位非常明确见 docs/api/hippy-vue/router.mdvue-router 通过小幅度修改官方路由实现了 hippy/vue-router提供一样的接口。也就是说路由配置语法routes、params、query、嵌套路由、导航守卫、动态路由等与 vue-router 完全一致官方文档的绝大部分内容可以直接参考仓库内的实现位于 driver/js/packages/hippy-vue-router包名为hippy/vue-router其 package.json 中name字段即为hippy/vue-routermain指向构建产物dist/index.js。从源码结构看该包整体沿用了 vue-router v3 的目录组织src/index.js 是VueRouter主类src/install.js 是Vue.use安装逻辑src/components/ 下有RouterView与RouterLink两个内置组件src/history/hippy.js 则是针对 Hippy 终端环境新写的HippyHistory。与官方 vue-router 最大的区别在于官方包有WebHistoryHTML5 History和HashHistory两种模式而 Hippy 运行在原生容器中不存在window/location因此路由历史完全由内存中的导航栈维护这正是HippyHistory的职责。二、接入方式在原生环境中安装路由2.1 安装与运行环境约束VueRouter构造函数中有一个硬性检查src/index.js// Running in Hippy if (global.__GLOBAL__ global.__GLOBAL__.appRegister) { this.history new HippyHistory(this, options.base); } else { throw new Error(Hippy-Vue-Router can\t work without Native environment); }从源码结构看hippy/vue-router只能在 Hippy 原生环境存在global.__GLOBAL__.appRegister的引擎运行时中创建脱离 Hippy 容器直接new VueRouter()会抛错。因此它不与浏览器版 vue-router 混用而是在构建时被替换掉vue-configs.js 中定义了别名router指向本包源码并配置了hippy/vue-router的构建入口src/index.js与产物dist/index.js即打包时vue-router的引用会被映射到这份原生实现。HippyHistory构造函数还要求根路由必须存在history/hippy.jsconst defaultRoute this.router.match(/, this.current); if (!defaultRoute) { throw new Error(Root router path with / is required); }所以配置routes时必须包含一条path: /的路由否则初始化即抛Root router path with / is required。2.2 官方示例工程中的完整接法仓库自带示例 hippy-vue-demo 展示了标准用法。其入口 main-native.js 中import Vue from vue; import VueRouter from vue-router; // 构建时由 hippy/vue-router 替换 import routes from ./routes; // Hippy 终端组件扩展中间件可以使用 modal、view-pager、tab-host、ul-refresh 等原生组件。 Vue.use(HippyVueNativeComponents); Vue.use(VueRouter); const router new VueRouter(routes); const app new Vue({ appName: Demo, rootView: #root, render: h h(App), router, // 路由 });路由表 routes.js 的写法与 vue-router 一致并额外携带了一个 Hippy 专有的选项export default { /** * 阻止 Android 上 Back 键触发页面返回 * 默认值为 false就是开启 Back 键返回。 */ disableAutoBack: false, routes: [ { path: /, component: Menu }, { path: /remote-debug, component: RemoteDebug, name: 调试 }, // ...业务路由 ], };安装逻辑在 install.js 中完成与 vue-router 相同的三件事通过全局mixin的beforeCreate钩子把根实例的router选项交给_router.init(app, Vue)并Vue.util.defineReactive(this, _route, ...)使$route具备响应式在Vue.prototype上以 getter 注入$router与$route注册全局组件RouterView与RouterLink并把beforeRouteEnter / beforeRouteLeave / beforeRouteUpdate的合并策略对齐到created钩子保证路由守卫能随组件选项被正确继承。页面根模板app.vue中只需用router-view作为出口并可用keep-alive包裹以缓存各页面实例keep-alive router-view classfeature-content / /keep-alive三、与 vue-router 一致的公开 APIVueRouter类src/index.js暴露了 vue-router 的标准方法接口签名保持一致方法说明push(location, onComplete, onAbort)编程式导航并压入历史栈replace(location, onComplete, onAbort)替换当前路由不入栈go(n)/back()/forward()在导航栈内前进后退n步beforeEach(fn)/beforeResolve(fn)/afterEach(fn)全局导航守卫返回取消注册函数onReady(cb, errorCb)/onError(errorCb)首次导航完成 / 导航错误回调resolve(to, current, append)解析 location返回{ location, route, href }供RouterLink使用getMatchedComponents(to)取目标路由匹配到的全部组件常用于守卫内取activated组件addRoutes(routes)动态追加路由追加后会对当前位置重新transitionTomatch(raw, current, redirectedFrom)直接调用内部 matcher 做路由匹配与官方 vue-router 的差别主要体现在两点不再存在mode: history | hash的概念new VueRouter(options)中只关心routes以及 Hippy 扩展的base、disableAutoBackcurrentRoute始终来自history.current而history固定是HippyHistory。四、HippyHistory内存导航栈如何工作HippyHistoryhistory/hippy.js是理解整个路由库的关键。它用两个字段维护历史stackRoute 数组与index当前指针完全替代了浏览器的 History API。4.1 push / replace / gopush(location, onComplete, onAbort) { this.transitionTo(location, (route) { this.stack this.stack.slice(0, this.index 1).concat(route); this.index 1; // ... }, onAbort); } replace(location, onComplete, onAbort) { this.transitionTo(location, (route) { this.stack this.stack.slice(0, this.index).concat(route); // 指针不变 // ... }); } go(n) { const targetIndex this.index n; if (targetIndex 0 || targetIndex this.stack.length) return; // 越界静默失败 const route this.stack[targetIndex]; this.confirmTransition(route, () { this.index targetIndex; this.updateRoute(route); this.stack this.stack.slice(0, targetIndex 1); // 截断后续历史 }); }可以推断出几个行为特征push先截断当前位置之后的“未来”分支再追加保证历史是一条直线replace保持index不变只替换当前指针指向的路由go(n)越界时直接返回不抛错且回退会截断栈尾——这与浏览器history.go后按返回键的体感一致。4.2 导航守卫的完整执行链confirmTransition中的守卫队列与 vue-router v3 相同history/hippy.js组件内beforeRouteLeavedeactivated倒序全局beforeHooksbeforeEach组件内beforeRouteUpdateupdated路由配置里的beforeEnteractivated异步组件的解析resolveAsyncComponents组件内beforeRouteEnter随后接全局resolveHooksbeforeResolve。next的三种语义同样保留next(false)取消导航并回滚 URL 状态next(/)或next({ path, replace })触发重定向replace: true时走replace否则push其余值透传给下一个钩子。全部钩子执行完毕后进入updateRoute更新this.current、触发listen回调该回调由init注入负责把_route同步到所有已挂载的 app 实例最后依次执行afterHooks。init中还有一段与 vue-router 相同的启动逻辑对HippyHistory会立即history.transitionTo(history.getCurrentLocation())完成首次导航随后通过onReady的首次回调判定路由系统“就绪”。五、Android 硬件返回键路由库的核心增强这是 Hippy 版路由相对官方 vue-router 最重要的差异化能力也是官方 READMEhippy-vue-router/README.md明确列出的卖点之一“Android hardware back button to navigate to previous page”。5.1 自动监听 Back 键VueRouter.init末尾src/index.jsif (Vue.Native.Platform android isFunction(history.hardwareBackPress) !this.options.disableAutoBack) { // Enable hardware back event. setTimeout(() Vue.Native.callNative(DeviceEventModule, setListenBackPress, true), 300); // Listen the hardware back event and redirect to history. app.$on(hardwareBackPress, () history.hardwareBackPress()); }即仅在 Android 上且未显式传入disableAutoBack: true时路由库会通过原生桥DeviceEventModule.setListenBackPress打开硬件返回事件监听并把终端回调的hardwareBackPress应用事件转发给HippyHistory。这里的setTimeout(..., 300)是有意为之——源码注释说明DeviceEventModule初始化稍晚不能立即callNative。5.2 返回键的决策逻辑hardwareBackPress()history/hippy.js的行为分三级hardwareBackPress() { if (this.stack.length 1) { return this.go(-1); // 1. 栈里还有上一页路由回退 } const { matched } this.stack[0]; if (matched.length) { const { components, instances } matched[0]; // 2. 根页面组件若声明了 beforeAppExit交给业务处理 if (components components.default isFunction(components.default.beforeAppExit)) { return components.default.beforeAppExit.call(instances.default, this.exitApp); } } return this.exitApp(); // 3. 兜底执行默认返回 } exitApp() { const Vue getVue(); Vue.Native.callNative(DeviceEventModule, invokeDefaultBackPressHandler); }导航栈长度大于 1 时Back 键等价于router.back()已在首页根路由时若根页面组件定义了beforeAppExit(callback)可以拿到exitApp回调业务可以先做确认弹窗例如“确定退出”用户确认后手动调用callback才会真正退出否则直接调用原生DeviceEventModule.invokeDefaultBackPressHandler把行为交还给终端默认的返回处理通常是关闭页面。5.3 关闭自动返回disableAutoBack如果业务希望自己接管 Back 键例如全局弹层未关闭前不允许返回在new VueRouter的 options 中传disableAutoBack: true即可路由库将完全不注册监听。此时可以在应用层面自行监听终端返回事件示例中app.$start回调里的Vue.Native.BackAndroid.addListenermain-native.js就是这类手动监听的示例app.$start((instance, initialProps) { // listen Android back press Vue.Native.BackAndroid.addListener(() { // set true interrupts native back return true; }); });返回true表示拦截原生返回。示例工程的 menu.vue 中也保留了disableAutoBack为假时用于 Back 键返回的说明两条路线路由库自动处理 / 业务手动处理在 demo 里都有对照。六、RouterView 与 RouterLink 的行为差异两个内置组件源码位于 components/view.js 与 components/link.js与 vue-router v3 的逻辑基本同源但有一处与原生环境相关的实质差别RouterView保持功能式组件实现通过_routerViewCache、routerViewDepth支持嵌套router-view与keep-alive缓存示例的app.vue正在使用registerRouteInstance钩子把页面实例登记到matched.instances供beforeRouteEnter的next(cb)回调轮询poll每 16ms 重试使用。RouterLink的 propsto、tag、exact、append、replace、activeClass、exactActiveClass、event与官方一致点击后按replace与否调用router.replace(location)或router.push(location)。不同之处在于原生容器没有 DOM 锚点tag默认值a在 Hippy-Vue 中会被渲染为对应的原生元素/span组件里通过findAnchor(children)递归查找锚点只是保留的兼容性代码激活态仍会按isSameRoute/isIncludedRoute计算router-link-active与router-link-exact-active两个 class。七、已知限制页面切换动画官方文档docs/api/hippy-vue/router.md明确了一条限制不支持页面切换时的动画效果因为transition组件尚未实现。即不能像 Web 端 vue-router 那样在router-view外包裹transition获得页面进出动画。从 hippy-vue 运行时 的实现看其内置元素节点document-node、element-node、view-node等与 Web 版 Vue 的过渡系统并非同一套机制因此在编写页面切换体验时应以即时切换为准必要时用原生组件自带的显隐/位置属性自行模拟过渡而不依赖transition。八、小结hippy/vue-routerdriver/js/packages/hippy-vue-router接口与 vue-router 对齐可直接按 vue-router 官方文档编写路由配置与守卫它只运行于 Hippy 原生环境要求routes中存在path: /的根路由且不提供 history/hash 模式——历史由HippyHistory的内存栈stackindex维护相对官方的核心增强是 Android 硬件 Back 键与导航栈联动setListenBackPresshardwareBackPress 首页beforeAppExit退出确认可用disableAutoBack: true关闭唯一明确的体验级限制是不支持transition页面切换动画。参考路径路由文档 docs/api/hippy-vue/router.md、核心实现 src/index.js、src/install.js、src/history/hippy.js、构建配置 vue-configs.js、示例入口 main-native.js 与路由表 routes.js。赞分享跨平台移动开发前端【免费下载链接】HippyHippy is designed to easily build cross-platform dynamic apps. 项目地址https://gitcode.com/gh_mirrors/hi/Hippy点击查看免费下载相关推荐Hippy-Vue 入门基于 Vue 2.x 与 node-ops 改写实现原生渲染的跨端框架Hippy Vue 入门基于 Vue 2.x 与 node ops 改写实现原生渲染的跨端框架 hippy vue 是 Hippy 中面向 Vue 开发者的框跨平台移动开发前端Hippy hippy-vue 自定义组件与自定义模块registerElement、事件绑定与 callNative 实战指南Hippy hippy vue 自定义组件与自定义模块registerElement、事件绑定与 callNative 实战指南 hippy vue 允许开发跨平台移动开发前端FiftyOne实战5大技巧突破边缘设备计算机视觉部署瓶颈FiftyOne实战5大技巧突破边缘设备计算机视觉部署瓶颈 FiftyOne作为专业的计算机视觉数据集管理和模型评估平台为开发者和数据科学家提供了强大的工具人工智能计算机视觉数据集数据可视化数据标注模型评测上一篇如何用 react-doctor/fuzz 对单条规则做对抗性模糊测试下一篇终极指南如何使用NGraphics打造跨平台矢量图形渲染应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表