H5移动端响应式处理,折叠屏快速适配方案设计

介绍

我们在开发一款移动端 H5 应用的时候,往往不会使用响应式设计(开发成本问题),转而使用类似于 px2rem 的方案进行适配。

这种方案很便利,但是也有一些问题:

  • 大屏幕上显示大字体(用户:我买折叠屏不是为了看更大的字体!!!)
  • 横屏下,不仅是字体内容更大,所能展示的信息密度也会缩水

基于这些痛点,同时为了保持快速开发,这里设计了一个基于 iframe 开发的响应式折叠屏原型。

资源共享

主屏幕和副屏幕使用的是同一个应用,因为是同一份代码,能够共享样式、组件和 UI 库。同时为了能够快速拉取资源,还可以使用 PWA 进行打包,这样主副屏可以共享同一个 Service Worker 进行资源来取。

UI

框架通过 iframe 实现了主副屏之间的容器 UI 扩展:

  • 响应式布局,在大屏幕模式下显示双屏,小屏幕模式下显示单屏
  • 通过 window.top 绑定容器 UI,可跨屏幕进行全局展示。

页面布局代码:

html 复制代码
<script setup lang="ts">
import { getMainRouter, getSecondRouter } from "../utils";

const isSM = useMediaQuery("(min-width: 640px)");
const secondScreenPath = ref("");

watch(
  isSM,
  async (val) => {
    if (val) {
      // 从小屏幕 -> 大屏幕
      const mainRouter = getMainRouter();
      const currentRoute = mainRouter.currentRoute.value;
      if (currentRoute.meta.screen) {
        secondScreenPath.value = currentRoute.fullPath;
        await mainRouter.back();
      } else {
        secondScreenPath.value = "";
      }
    } else {
      // 从大屏幕 -> 小屏幕
      const mainRouter = getMainRouter();
      const secondRouter = getSecondRouter()!;
      if (secondRouter.currentRoute.value.meta.screen) {
        const currentRoute = secondRouter.currentRoute.value;
        mainRouter.push(currentRoute.path);
      }
    }
  },
  {
    flush: "pre",
  }
);
</script>

<template>
  <div
    class="h-screen w-screen grid sm:grid-cols-2 overflow-hidden grid-cols-1 gap-2"
  >
    <iframe
      name="main-screen"
      class="w-full h-full"
      src="./screen.html"
    ></iframe>
    <iframe
      v-if="isSM"
      name="second-screen"
      class="w-full h-full"
      :src="`./screen.html#${secondScreenPath}`"
    ></iframe>
  </div>
</template>

路由拦截

实现自动的路由跳转机制:

  • 自动识别可在副屏展示的路由(通过 meta.screen 配置)
  • 在主屏幕点击后自动将详情页在副屏打开
  • 屏幕尺寸变化时智能调整路由状态
    • 符合大屏时,副屏将主屏当前路由作为详情页打开,主屏回退到列表页
    • 符合小屏时,主屏将副屏路由作为新路由推入

主屏路由拦截:

ts 复制代码
router.beforeEach(async (to, from) => {
  /**
   * 当目标路由符合配置了 meta: true, 则代表其可以在副屏幕打开
   * 
   * 此时:
   * 如果是主屏幕,则跳转副屏幕打开
   * @param path 
   */
  if (to.meta.screen) {
    if (isMainScreen) {
      const router = getSecondRouter()
      if (router) {
        router.push(to.path);
        return false;
      }
    }
  }
});

响应式变化后,状态恢复

解决了屏幕尺寸变化后应用状态丢失的问题:

  • 保证用户体验连贯性,避免状态丢失
  • 使用 sessionStorage 保存页面状态(iframe内可共享)
  • 在屏幕模式切换时自动恢复之前的浏览状态
  • 不同 ID 的状态需隔离,同时需处理详情页 ID 相关的状态保存和恢复

状态管理:

ts 复制代码
const { id } = defineProps<{
  id: string;
}>();

const createDefaultDetailData = () => {
  return {
    showDialog: false,
    formData: {
      input1: "",
      input2: "",
    },
  };
};

/**
 * 不同的数据,需要绑定到不同的 sessionStorage 中
 *
 * 否则先点击 id 1,然后刷新页面,点击详情id 2。可能展示的是 id 1 的数据
 */
const detailData = useSessionStorage(
  () => `detail-${id}`,
  createDefaultDetailData()
);

// 在 useSessionStorage 之后执行
watch(
  () => id,
  () => {
    console.log("id changed", id);
    // id 变化了,说明详情内容变化了,需要重置表单,并清空非当前 id 的详情内容
    detailData.value = createDefaultDetailData();
    Object.keys(sessionStorage).forEach((key) => {
      if (key.startsWith("detail-") && key !== `detail-${id}`) {
        sessionStorage.removeItem(key);
      }
    });
  }
);

其他

相关推荐
牧羊狼的狼4 小时前
React 中的 HOC 和 Hooks
前端·javascript·react.js·hooks·高阶组件·hoc
知识分享小能手5 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
魔云连洲5 小时前
深入解析:Vue与React的异步批处理更新机制
前端·vue.js·react.js
mCell6 小时前
JavaScript 的多线程能力:Worker
前端·javascript·浏览器
超级无敌攻城狮7 小时前
3 分钟学会!波浪文字动画超详细教程,从 0 到 1 实现「思考中 / 加载中」高级效果
前端
excel8 小时前
用 TensorFlow.js Node 实现猫图像识别(教学版逐步分解)
前端
gnip9 小时前
JavaScript事件流
前端·javascript
赵得C9 小时前
【前端技巧】Element Table 列标题如何优雅添加 Tooltip 提示?
前端·elementui·vue·table组件
wow_DG9 小时前
【Vue2 ✨】Vue2 入门之旅 · 进阶篇(一):响应式原理
前端·javascript·vue.js
weixin_456904279 小时前
UserManagement.vue和Profile.vue详细解释
前端·javascript·vue.js