尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Vue3+Vite打造H5结婚请帖:移动端适配与微信分享实战

Vue3+Vite打造H5结婚请帖:移动端适配与微信分享实战 简介这份基于Vue 2打造的H5结婚请帖前端源码面向需要快速搭建婚礼、宴会二维码邀请页的前端开发者和婚庆从业者以现代交互形式替代传统纸质请帖覆盖邀请展示、祝福留言、时间线回顾等典型场景。压缩包内共87个文件体积约34.07MB包含14个JavaScript逻辑脚本、6个Vue组件、24张PNG与31张JPG图片素材另提供HTML入口、CSS样式、环境变量、依赖锁文件及license说明图片资源覆盖背景、装饰及地址图标脚本与组件则分别承担路由、接口请求、音乐播放和页面结构等职责目录清晰便于二开。项目还集成音乐控制、留言板、时间线、地图地址、阿里巴巴OSS上传等模块并封装了axios请求与正则校验工具适合系统学习Vue全家桶在移动端H5中的工程化实践。当前已有500人学习对希望直接复用完整邀请模板并研究组件拆分、路由配置及环境切换的开发者来说具备不错的参考价值。1. 基于Vue的H5结婚请帖一个前端需求的完整落地样本微信群里点开一张电子请帖音乐响起、照片缓缓滚动、倒计时跳动最后弹出地图导航。这就是基于Vue的H5结婚请帖前端源码要解决的事用 Vue 组件化一套移动端网页把邀请函做成可直接部署的单页应用。这类需求在个人接单、婚庆外包和公司活动页里出现频率不低难点不在于业务复杂度而在于移动端适配、路由传参、动效性能和微信生态兼容。以下内容按工程初始化、页面交互、分享集成、构建排错顺序展开面向有 Vue 基础、想直接接手或改造一套 H5 请帖源码的读者。2. 技术选型与工程初始化Vue 3 Vite 还是 Vue 2 Webpack2.1 版本选型为什么新项目优先 Vue 3结婚请帖页面虽然不大但组件拆分、路由管理、定时器清理、图片懒加载这些点一个不少。Vue 3 的组合式 API 让这部分逻辑天然聚合一个setup函数里就能把倒计时、音乐播放、滚动监听的逻辑管理清楚不会出现 Vue 2 选项式写法里 data、methods、watch 各处分散的问题。Vue 3 对应的构建链也成熟了。Vite 的开发服务器秒级启动HMR 快对改样式和调动画非常友好。Vue 2 Webpack 不是不能用但如果是新起项目面对 2026 年这时间点Vue 3 已是默认。唯一需要犹豫的场景是客户运营后台还在用老版 WebView 内核且明确要求兼容 Android 7 以下的老机器。遇到这种情况先把目标机器的浏览器内核版本摸清Vue 3 本身支持到 IE 11 以上的现代浏览器真正卡住你的往往不是 Vue 版本而是 ES2015 语法和 CSS 变量。解决方式是构建时开启vitejs/plugin-legacy而不是退回 Vue 2。2.2 create-vite 初始化工程与依赖安装命令行操作直接给出来项目名用wedding-invitenpm create vitelatest wedding-invite -- --template vue cd wedding-invite npm install npm install vue-router4 npm run dev--template vue拉下来的是 Vue 3 Vite 的最小骨架默认不带 TypeScript适合快速改造。vue-router 单独装是因为注意包版本要与 Vue 3 匹配vue-router 4.x 才是对应 Vue 3 的版本。装完依赖后npm run dev浏览器打开http://localhost:5173就可以看到默认页面。安装依赖过程中常见的报错是peerDependencies冲突一般发生在 node 版本过旧或 npm 版本不兼容。遇到这种情况执行node -v确认版本在 18 以上然后删除node_modules和package-lock.json重新安装。网络不好时把 npm 源切到国内镜像执行npm config set registry https://registry.npmmirror.com这两步能解决九成安装问题。2.3 移动端适配rem 与 vw 双方案的取舍H5 请帖主要跑在微信内置浏览器里屏幕从 320px 到 430px 不等适配是第一道坎。当前主流做法是 vw 方案和 rem 方案并存我一般按团队习惯选但推荐新项目直接用 vw。方案核心计算优点坑点rem根字号 屏幕宽度 / 设计稿宽度老项目标准方案配合 postcss-pxtorem 自动换算根字号会被系统字体设置影响需额外处理vw直接把 px 换算成 vw原生支持、不依赖根字号1px 边框和字体大小需要单独控制vw 方案里设计稿按 750px 出图那么 1px 对应100vw / 750 ≈ 0.1333vw。手写太累交给 postcss 插件处理// postcss.config.js export default { plugins: { postcss-px-to-viewport: { viewportWidth: 750, // 设计稿宽度 unitPrecision: 5, // 换算后保留小数位 viewportUnit: vw, minPixelValue: 1, // 小于等于 1px 不转换 exclude: /node_modules/ } } }这个配置会把样式文件里所有 px 自动转成 vw写代码时仍然按设计稿 750 的尺寸写 px不会有换算负担。minPixelValue: 1是为了保留 1px 的细边框防止某些机器上边框直接消失。exclude很重要node_modules 里的第三方样式不用转换。2.3.1 字体与安全区域的边界字号不建议全部交给 vw 转换标题、正文等关键文字我习惯单独写媒体查询或使用clamp()。原因很简单vw 是按屏幕宽度等比缩放但人眼阅读习惯里手机上字太小、平板上字太大的问题必须手动兜底。一个常用的方案是body { font-size: clamp(14px, 3.2vw, 18px); }clamp的三参数分别是最小值、首选值、最大值这样屏幕宽度变化时字号被限制在 14px 到 18px 之间。此外iPhone 的刘海屏和底部横条会遮挡内容需要在页面最底部加安全区域适配.safe-bottom { padding-bottom: env(safe-area-inset-bottom); }2.4 目录结构与全局样式请帖项目不需要复杂的目录层级清晰即可。一个常规划分如下src/ ├── assets/ # 静态图片、音乐 ├── components/ # 相册、倒计时、留言等组件 ├── router/ # 路由配置 ├── views/ # 打开邀请函、详情页、祝福页 ├── utils/ # 格式化、分享等工具函数 ├── App.vue └── main.js全局样式里首先要做 reset。微信浏览器默认样式比标准浏览器更不可控特别是-webkit-tap-highlight-color这个属性不处理的话点击任何元素都会闪一下灰色遮罩。另外请帖页都是全屏滚动所以html, body的高度和滚动行为要显式声明。html, body { margin: 0; padding: 0; height: 100%; overflow-x: hidden; -webkit-tap-highlight-color: transparent; }禁掉横向滚动是关键图片错位偶尔会造成横向溢出一旦出现横向滚动条整个页面体验就很廉价。给overflow-x: hidden是兜底但布局上还是要保证每个区块宽度不超过视口宽度。3. 邀请函页面与路由URL 传参、倒计时与滚动动效3.1 路由模式hash 还是 historyH5 请帖一般部署在对象存储或 nginx 下进入路径是https://域名/invite/index.html这种形式。路由模式上我建议直接用createWebHashHistory。原因很直接hash 路由不需要后端配置重写规则部署到任何静态服务器都不会出现刷新后 404 的问题。history 路由在本地开发时很舒服但一旦部署到 CDN 或 OSS刷新非根路径就大概率白屏彼时需要配置一大段重写规则为一个小项目不值当。路由结构本身非常简单三个页面足够打开邀请函首页、婚礼详情页、宾客留言页。// src/router/index.js import { createRouter, createWebHashHistory } from vue-router const routes [ { path: /, name: invite, component: () import(/views/InvitePage.vue) }, { path: /detail, name: detail, component: () import(/views/DetailPage.vue) }, { path: /message, name: message, component: () import(/views/MessagePage.vue) } ] const router createRouter({ history: createWebHashHistory(), routes }) export default routercreateWebHashHistory()会让地址栏带上#/例如https://域名/index.html#/detail。很多不熟悉 H5 的人会觉得带#不美观但换来的是部署时零配置这个取舍在移动端小项目里非常划算。3.2 嘉宾入口与 URL 参数传递有一个高频业务需求新人发出去的请帖希望知道每位宾客是否打开过。常见做法是把宾客姓名拼到 URL 上例如index.html#/detail?guest张伟typefriend。这样页面打开时就能在代码里读到参数展示不同的欢迎语也可以把访问行为上报给后端。script setup import { useRoute } from vue-router const route useRoute() const guestName route.query.guest || 朋友 const inviteType route.query.type || friend // guest 参数为空时的兜底文案 const greeting guestName ! 朋友 ? 亲爱的 ${guestName}诚邀您参加我们的婚礼 : 亲爱的朋友诚邀您参加我们的婚礼 /script这里有两个注意点。第一route.query拿到的是字符串参数值里如果包含中文或空格URL 上会显示编码后的%E5%BC%A0%E4%BC%9F但不影响取值Vue Router 会自动解码。第二参数缺失要有兜底否则显示空名字很尴尬。更严谨的做法是把参数校验封装成一个工具函数保证每个入口页面拿到的都是合法值。3.2.1 传多个参数的拼装方法如果你是给运营或者新人做分享链接最常用的拼装方式有两种手动拼字符串或者用 Vue Router 内置的router.push方法。前者直观但容易漏掉编码后者更安全import { useRouter } from vue-router const router useRouter() function generateShareUrl(guestName, type) { router.push({ path: /detail, query: { guest: guestName, type: type, from: wechat } }) }调用generateShareUrl(李婷, colleague)后地址栏会生成#/detail?guest李婷typecolleaguefromwechat。Vue Router 内部会把中文自动编码后台解码后即可解析。这里强调使用router.push而不是直接修改window.location为的是保留 SPA 内部状态避免整个页面刷新。3.3 倒计时组件时间计算与定时器清理婚礼主题页几乎都有一个倒计时。倒计时看起来简单但写错的人不少常见问题包括日期写死导致每年都要改、组件卸载后定时器未清理、时区导致计算偏差。script setup import { ref, onMounted, onUnmounted, computed } from vue const targetTime new Date(2026-10-01T10:00:0008:00).getTime() const currentTime ref(Date.now()) let timer null const countdown computed(() { const diff targetTime - currentTime.value if (diff 0) { return { days: 0, hours: 0, minutes: 0, seconds: 0 } } return { days: Math.floor(diff / (1000 * 60 * 60 * 24)), hours: Math.floor((diff / (1000 * 60 * 60)) % 24), minutes: Math.floor((diff / (1000 * 60)) % 60), seconds: Math.floor((diff / 1000) % 60) } }) onMounted(() { timer setInterval(() { currentTime.value Date.now() }, 1000) }) onUnmounted(() { clearInterval(timer) }) /script核心逻辑是维护一个currentTime的 ref每秒更新一次倒计时结果通过computed推导。这里有个容易被忽视的点不要直接修改targetTime也不要拿系统时间减本地时间应该统一使用08:00这种带时区的 ISO 字符串。很多人踩过这个坑部署在海外服务器的前端拿到的时间跟北京时间不一样倒计时就错了。onUnmounted里clearInterval是必须的否则组件切换后定时器还在跑性能和内存都会被拖累。如果你用 Vue 2 的选项式写法则对应destroyed钩子。不管哪种框架定时器清理都是倒计时组件的安全红线。3.4 滚动动效IntersectionObserver 比滚动监听更优请帖页面通常是一张长图流从封面滑到相册再到地图。早期做法是监听scroll事件判断元素距视口位置后添加动画类但scroll事件触发频率极高容易造成掉帧。推荐使用IntersectionObserver它是浏览器原生 API只在元素进入视口时触发回调性能和可维护性都更好。template section refsectionRef classfade-section h2我们的故事/h2 p从相识到相守一路走来的每个瞬间/p /section /template script setup import { ref, onMounted, onUnmounted } from vue const sectionRef ref(null) let observer null onMounted(() { observer new IntersectionObserver((entries) { entries.forEach(entry { if (entry.isIntersecting) { entry.target.classList.add(visible) observer.unobserve(entry.target) } }) }, { threshold: 0.3 }) if (sectionRef.value) { observer.observe(sectionRef.value) } }) onUnmounted(() { if (observer) observer.disconnect() }) /script style scoped .fade-section { opacity: 0; transform: translateY(30px); transition: opacity 0.6s ease, transform 0.6s ease; } .fade-section.visible { opacity: 1; transform: translateY(0); } /stylethreshold: 0.3表示元素有 30% 的面积进入视口时才触发动画。先设opacity: 0加translateY(30px)做隐藏态添加.visible类后过渡到显示态这组参数能做出很常见的上浮浮现效果。有一点要注意初始隐藏的动画元素如果 JS 执行失败或观察器未生效页面会出现大面积空白。解决办法是在onMounted里给根元素加一个兜底类确保 JS 报错时元素仍然可见。4. 背景音乐、相册与微信分享H5 交互集成的几个关键点4.1 背景音乐自动播放策略用户打开请帖就想起音乐这是产品方的执念但移动端浏览器对自动播放限制非常严格。微信浏览器里audio.play()只有在用户触发过一次触摸或点击事件后才会被允许。常见的默认策略是页面首次加载时显示一个有音乐按钮的遮罩层用户点击进入后才开始播放音乐这样既保证有声音也满足了浏览器的交互要求。script setup import { ref } from vue const audioRef ref(null) const isPlaying ref(false) const showCover ref(true) function enterInvite() { showCover.value false const audio audioRef.value if (audio) { audio.play().then(() { isPlaying.value true }).catch(() { // 自动播放被拦截时等待用户下一次手势 isPlaying.value false }) } } function toggleMusic() { const audio audioRef.value if (isPlaying.value) { audio.pause() isPlaying.value false } else { audio.play() isPlaying.value true } } /script template div audio refaudioRef src/music/wedding.mp3 loop/audio div v-ifshowCover clickenterInvite classcover-mask点击进入/div button clicktoggleMusic classmusic-btn{{ isPlaying ? 暂停 : 播放 }}/button /div /templateaudio.play()返回一个 Promisethen里设置播放状态catch里处理被拦截的情况。这个细节值得写清楚因为很多人在初学阶段会直接audio.play()后不管结果在 Safari 或微信里按钮反应异常其实是捕获到异常后没有回退处理。音乐文件建议压缩到 1MB 以内长音频用 AAC 编码用户流量成本和时间成本都低。4.2 图片懒加载与相册预览请帖页的图片数量动辄十几张全量加载会让首屏时间暴涨。移动端 4G 网络环境下单张 200KB 的图片 20 张就是 4MB用户打开一秒内看不到内容就会关掉。懒加载是必须项。可以直接用loadinglazy属性这是现代浏览器的原生能力零成本。但要注意微信浏览器内置 X5 内核对新属性的支持并不一致兼容起见我仍会选择基于IntersectionObserver的自定义懒加载指令。script setup const vLazy { mounted(el, binding) { const observer new IntersectionObserver((entries) { if (entries[0].isIntersecting) { el.src binding.value observer.unobserve(el) } }) observer.observe(el) } } /script template img v-foritem in photos :keyitem.id v-lazyitem.url alt婚礼照片 /template指令里把真实的图片地址放在binding.value上初始img标签不写src或使用一个 1x1 像素占位图。当图片进入视口时再把真实地址赋给src。图片加载失败的情况也需要兜底el.onerror的时候替换成一张默认图否则页面上会出现碎图图标。相册预览用原生方式做最简单点击图片打开一个全屏遮罩层里面展示大图并支持左右滑动。如果不想引入 swiper 这类重型库可以只做单张全屏展示左右切换通过监听touchstart和touchend的坐标差实现逻辑量大约 30 行足够应付请帖场景。4.3 留言与祝福表单校验与提交留言功能通常需要后端配合源码里一般只做前端部分表单收集、校验、提交以及提交后的成功反馈。为了不依赖具体后端地址常见的做法是把接口地址抽到环境变量里用import.meta.env.VITE_API_BASE配置。script setup import { ref } from vue const nickname ref() const message ref() const submitting ref(false) async function submitMessage() { if (!nickname.value.trim() || !message.value.trim()) { alert(请填写昵称和祝福语) return } submitting.value true try { const res await fetch(${import.meta.env.VITE_API_BASE}/api/messages, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ nickname: nickname.value.trim(), message: message.value.trim(), createdAt: Date.now() }) }) if (!res.ok) throw new Error(submit failed) alert(祝福成功感谢您的留言) nickname.value message.value } catch (e) { alert(提交失败请稍后再试) } finally { submitting.value false } } /script校验只做非空判断是最低标准。如果要做字数限制建议把maxlength直接写在input和textarea上从源头控制。submitting状态用来防重复提交按钮点击后立即禁用避免用户连点导致同一条留言重复入库。使用fetch而不是 axios是考虑到大多数留言接口极其简单引入 axios 只是增加包体积。4.4 微信 JSSDK 分享配置请帖在微信里传播的核心是分享卡片。用户在微信右上角分享默认展示的是链接标题和缩略图这不够体面也体现不出婚礼氛围。接入微信 JS-SDK 后可以自定义分享标题、描述和分享图标。import wx from weixin-js-sdk function initWechatShare(config) { wx.config({ debug: false, appId: config.appId, timestamp: config.timestamp, nonceStr: config.nonceStr, signature: config.signature, jsApiList: [updateAppMessageShareData, updateTimelineShareData] }) wx.ready(function () { const shareData { title: 诚邀您参加我们的婚礼, desc: 这是一份来自新郎新娘的邀请, link: window.location.href, imgUrl: config.shareImage } wx.updateAppMessageShareData(shareData) wx.updateTimelineShareData(shareData) }) wx.error(function (res) { // 签名失败需重新获取签名 console.error(wx config error, res) }) }wx.config所需的四个参数全部由后端接口获取。常见做法是前端在路由加载时请求一次签名接口传入当前页面的window.location.href.split(#)[0]后端用这块 URL 生成签名。这里有个典型坑link参数如果填了带#的完整地址部分安卓机的分享卡片会打不开。必须去掉#之后的部分让用户点开卡片时重新落到正确的 hash 路由上。如果后端还没就绪前端可以用meta标签做降级方案。微信支持og:title、og:description、og:image三个 meta 属性虽然灵较度不如 JSSDK但至少分享出去有标题有图不裸奔。5. 构建部署与真机调试打包路径、布局异常和验证清单5.1 base 路径与静态资源定位部署到子目录是最常见的场景比如https://example.com/wedding/。如果打包时不做任何配置Vite 默认资源路径是根目录/部署到子目录后所有 JS、CSS、图片都会 404。解决办法是在vite.config.js里设置base或者在执行构建命令时通过环境变量控制。// vite.config.js export default { base: process.env.VITE_BASE_PATH || ./ }打包时执行VITE_BASE_PATH/wedding/ npm run build产物里的资源路径就会自动带上/wedding/前缀。如果部署在 nginx 的根路径直接用默认/即可。base设为./也可以它让资源加载变成相对路径适合放在任何目录但对路由和懒加载模块的路径解析有兼容风险不推荐在vue-router的 hash 模式下混用相对路径容易出问题。5.2 打包后布局异常的常见原因开发环境一切正常npm run build部署上去后发现布局乱了这是 Vue 打包后布局异常的高频场景。原因集中在三处。第一处是样式文件的顺序。开发环境里 Vite 按依赖图动态注入样式打包时按照模块顺序合并如果组件里的 scoped 样式和全局样式发生覆盖关系两者在产物里的顺序可能和预期不同。解决方法是只把真正的公共样式放全局组件内的关键样式全部加scoped并且不要全局去改第三方组件的内部类名比如vant这类 UI 库的样式覆盖会非常痛苦。第二处是图片路径问题。打包后的 CSS 里引用的图片如果被编译成 base64 嵌入体积会变大如果通过相对路径引用一旦部署路径和预期不符就白屏。出现图片丢失时第一时间打开 DevTools 看 Network 面板里图片资源的完整 URL判断是路径问题还是 404。第三处是字体文件。移动端 H5 通常用到自定义字体但字体文件体积大且跨域限制多。建议只用一种字重用font-display: swap避免阻塞渲染把字体格式转为 woff2裁剪掉不需要的字形子集。简单来说字体少用用了就压。5.3 用 vConsole 做真机调试开发环境有 DevTools真机调试就得靠 vConsole。它是一个移动端可引入的控制台面板能看 console、网络请求和 cookie微信里打开特别方便。script srchttps://unpkg.com/vconsole/script script var vConsole new VConsole(); /script生产环境需要调试时可以在 URL 加参数控制开启条件避免用户看到控制台按钮。另一个方式是写进代码里import VConsole from vconsole if (location.hash.includes(debug)) { new VConsole() }这样分享出去的链接只要带上#debug开发者打开就是调试模式用户正常打开不受影响。vConsole 里能直观看到 fetch 请求的报错信息、图片加载失败对象以及页面上 JS 报错的堆栈。我用它在微信里排查过很多次签名失败和路由 404 的问题是 H5 项目的常备工具。还需要强调一个问题vConsole 只适合开发自测正式版应移除这段代码或用条件语句隔离不要在产品环境裸奔。5.4 上线前验证清单上线前最后一件事打开手机微信扫一扫本地构建产物按以下顺序过一遍首屏加载速度要求在 3 秒内出现主体内容超过这个时间用户基本流失检查图片是否压缩、音乐是否过大倒计时是否与北京时间一致尤其跨时区用户看到的数字分享卡片标题、描述、图片是否正确曾在生产环境出现过分享卡片用的是上一次签名的旧缓存清除微信缓存后立即恢复的情况路由刷新后是否正常hash 模式下一般没问题底部按钮是否被 iPhone 横条遮挡加env(safe-area-inset-bottom)解决。最后验证一个最容易被忽略的点用 Chrome DevTools 的 Device Toolbar 把 UA 设成 iPhoneNetwork 面板勾选 Fast 3G刷新页面复现一遍用户第一次打开时的体验重点看首屏图片和背景音乐是否按预期加载这比模拟器更接近真实情况。本文还有配套的精品资源点击获取
返回列表