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);
      }
    });
  }
);

其他

相关推荐
拾光拾趣录3 分钟前
CSS 深入解析:提升网页样式技巧与常见问题解决方案
前端·css
莫空00004 分钟前
深入理解JavaScript属性描述符:从数据属性到存取器属性
前端·面试
guojl5 分钟前
深度剖析Kafka读写机制
前端
FogLetter6 分钟前
图片懒加载:让网页飞起来的魔法技巧 ✨
前端·javascript·css
Mxuan6 分钟前
vscode webview 插件开发(精装篇)
前端
Mxuan7 分钟前
vscode webview 插件开发(交付篇)
前端
Mxuan9 分钟前
vscode 插件与 electron 应用跳转网页进行登录的实践
前端
拾光拾趣录9 分钟前
JavaScript 加载对浏览器渲染的影响
前端·javascript·浏览器
Codebee9 分钟前
OneCode图表配置速查手册
大数据·前端·数据可视化
然我10 分钟前
React 开发通关指南:用 HTML 的思维写 JS🚀🚀
前端·react.js·html