news 2026/10/22 19:18:33

纯前端 localStorage 实现配送地址跨页面同步方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
纯前端 localStorage 实现配送地址跨页面同步方案

简介:一套面向毕业设计的民航售票管理系统完整资料包,基于ASP与SQL Server开发,采用B/S结构,适合计算机相关专业学生用来完成课程设计或毕业论文参考。系统涵盖会员注册管理、管理员后台、留言管理、航班查询、网上订票与网上退票等核心功能,从需求分析、数据库设计到编码实现均有完整支持。整个压缩包共96个文件,以35个ASP页面、42张GIF图片、7个SWF动画为主,另含数据库文件及毕业论文文档,整体仅1.14MB,轻量便于本地运行与调试。目前已有139人学习下载。资料包含可直接部署的源代码、配套数据库和完整毕业论文,并附有系统设计与实现文档,可帮助读者快速理解民航售票业务的模块划分、数据库表设计与ASP编程思路,是毕业设计备题和ASP技术练手的实用参考。 我们来做一版纯前端的方案。核心思路:把配送至页保存的地址写进 localStorage,商品详情页的“由我享用 / 送TA礼物”列表从 localStorage 读取并支持勾选删除,订单确认页再从 localStorage 读取被勾选的地址来完成回填。

整体流程:

  1. 用户在 A 商品详情页点击“由我享用 / 送TA礼物”进入配送至页;
  2. 配送至页选择配送方式、填写收货地址,点击保存;
  3. 保存后地址写入 localStorage,并跳转回 A 商品详情页;
  4. A 商品详情页的列表展示已保存地址,可勾选“由我享用/送TA礼物”,也可删除;
  5. 用户进入 B 商品详情页,同样能看到同一份地址列表(因为 localStorage 是全局共享的),勾选某条地址后点击“立即购买”;
  6. 跳转订单确认页时,从 localStorage 读取勾选状态,把地址回填到订单确认页。

下面给出最简实现,你可以直接复制到现有项目里对照修改。


1. localStorage 数据结构设计

建议统一用两个 key:

  • shippingAddresses:保存全部地址列表,数组对象。
  • selectedShippingId:保存当前选中的地址 id(用于“由我享用/送TA礼物”的勾选状态以及订单确认页回填)。

每条地址对象结构:

{ id: Date.now() + Math.random().toFixed(4), // 确保唯一,方便删除 receiver: '张三', phone: '13800000000', province: '浙江省', city: '杭州市', district: '西湖区', detail: '文一西路 100 号', deliveryType: 'express', // express 快递 / pickup 门店自提 tag: '由我享用' // 或 '送TA礼物' }

当然你也可以把 tag 单独拆出去,但最简单的做法是把它作为地址的一个属性保存。这样后续勾选时可以区分是“由我享用”还是“送TA礼物”。


2. 配送至页:保存地址并回写 localStorage

页面流程里,用户填完表单点击“保存”,我们做这些事:

  • 校验表单必填项;
  • 组装地址对象;
  • 从 localStorage 读出旧列表(或初始化为空数组);
  • 往数组头部 unshift 新地址(方便用户看到最新保存的);
  • 把新地址的 id 设置为当前选中项,写入selectedShippingId;
  • 跳转回来源商品详情页,可以用window.history.back()或location.href。

代码示例:

function saveAddress(formData) { // 1. 基本校验,不通过则 return if (!formData.receiver || !formData.phone || !formData.detail) { alert('请填写完整收货信息'); return false; } // 2. 构造地址对象 const newAddress = { id: Date.now() + '_' + Math.floor(Math.random() * 1000), receiver: formData.receiver, phone: formData.phone, province: formData.province, city: formData.city, district: formData.district, detail: formData.detail, deliveryType: formData.deliveryType || 'express', tag: formData.tag === '送TA礼物' ? '送TA礼物' : '由我享用' }; // 3. 读取旧列表 const existing = JSON.parse(localStorage.getItem('shippingAddresses') || '[]'); // 4. 新增到头部,并保存 existing.unshift(newAddress); localStorage.setItem('shippingAddresses', JSON.stringify(existing)); // 5. 设置当前选中地址 id localStorage.setItem('selectedShippingId', newAddress.id); // 6. 跳回商品详情页 history.back(); // 或者 location.href = '商品详情页地址'; return true; }

逻辑说明:

  • 用unshift保持最新地址在最上面,后续列表展示时直接按数组顺序渲染;
  • 新保存的地址默认设为选中,这是为了贴近用户操作习惯:刚填完的地址大概率就是想用的;
  • id使用时间戳+随机数,不需要依赖后端返回;
  • 如果你希望保留“由我享用”和“送TA礼物”两个独立入口对应两套独立标签,可以在tag字段区分,后续在商品详情页根据按钮类型筛选显示。

3. 商品详情页:“由我享用 / 送TA礼物”列表读写与删除

商品详情页打开时,需要:

  • 读取 localStorage 里的地址列表;
  • 判断当前商品详情页是从哪个入口进来的(由我享用 / 送TA礼物),可以通过链接参数或者按钮点击时记录的方式;
  • 列表里展示符合条件的地址(例如 tag 等于当前入口的值);
  • 支持删除某条地址;
  • 支持勾选某条地址,并把选中 id 写入selectedShippingId。

这里给一个列表渲染与事件处理的示例:

let currentTag = '由我享用'; // 实际可根据页面参数或点击入口动态设置 function getAddressList() { return JSON.parse(localStorage.getItem('shippingAddresses') || '[]'); } function getSelectedId() { return localStorage.getItem('selectedShippingId') || ''; } // 渲染地址列表到容器 function renderAddressList() { const list = getAddressList().filter(item => item.tag === currentTag); const selectedId = getSelectedId(); const container = document.getElementById('addressList'); container.innerHTML = list.map(item => ` <div class="address-item"> <label> <input type="radio" name="selectedAddress" value="${item.id}" ${item.id === selectedId ? 'checked' : ''} onchange="selectAddress('${item.id}')"> <span>${item.receiver} ${item.phone}</span> <span>${item.province}${item.city}${item.district}${item.detail}</span> </label> <button onclick="deleteAddress('${item.id}')">删除</button> </div> `).join(''); } // 勾选地址 function selectAddress(id) { localStorage.setItem('selectedShippingId', id); } // 删除地址 function deleteAddress(id) { let list = getAddressList(); list = list.filter(item => item.id !== id); localStorage.setItem('shippingAddresses', JSON.stringify(list)); // 如果删除的是当前选中项,需要把选中项清空或重设为 null if (getSelectedId() === id) { localStorage.removeItem('selectedShippingId'); } renderAddressList(); }

代码说明:

  • 通过tag过滤地址列表,这样“由我享用”入口和“送TA礼物”入口看到的列表是分开的;
  • 使用 radio 单选,更贴合“勾选一条地址去下单”的语义;
  • 删除时从数组中过滤掉对应的 id,如果删掉的是当前选中项,需要同步清空selectedShippingId,否则后续下单时会读到已经不复存在的地址;
  • renderAddressList里的onchange事件依赖selectAddress是全局函数,如果你在模块化开发中,建议改成事件委托或绑定函数引用。

4. 立即购买 与订单确认页回填

当用户点击“立即购买”时,需要把当前选中的地址 id 和商品信息一起带到订单确认页。

最简做法有两种:

  • URL query 传参:order.html?addressId=xxx&skuId=123
  • sessionStorage 传递:把订单相关数据暂存到 sessionStorage

由于商品详情页可能已经存在于历史栈中,且订单确认页刷新后 query 参数还在,用 URL query 会更直观。

示例代码:

function buyNow() { const selectedId = localStorage.getItem('selectedShippingId'); if (!selectedId) { alert('请先选择收货地址'); return false; } // 携带地址 id 跳转到订单确认页 location.href = './orderConfirm.html?addressId=' + selectedId + '&skuId=' + skuId; }

订单确认页侧:

// 从 query 获取 addressId function getQueryParam(key) { const url = new URL(window.location.href); return url.searchParams.get(key); } function loadSelectedAddress() { const addressId = getQueryParam('addressId'); if (!addressId) return null; const list = JSON.parse(localStorage.getItem('shippingAddresses') || '[]'); const address = list.find(item => item.id === addressId); if (address) { // 回填到订单确认页的地址区域 showAddress(orderAddressEl, address); } else { alert('地址不存在或已被删除,请重新选择'); redirectToCart(); // 回到商品详情页重新选 } } function showAddress(el, address) { el.innerHTML = ` <div>收货人:${address.receiver}</div> <div>手机号:${address.phone}</div> <div>地址:${address.province}${address.city}${address.district}${address.detail}</div> <div>配送方式:${address.deliveryType === 'pickup' ? '门店自提' : '快递'}</div> `; }

整体流程到这里就闭环了。订单确认页提交订单时,也不需要额外传地址,直接从 localStorage 里根据 addressId 再读一次即可。


5. 避坑要点

这套方案整体不难,但有几个坑容易翻车,提前注意:

  1. radio 的 onchange 和 label 冲突
    在渲染时如果label包裹了input和文本,在部分浏览器里点击文本不会触发 radio 的 change 事件。建议在容器上用事件委托,或者给label加onclick手动赋值。

  2. 删除当前选中地址后没有清空 selectedShippingId
    不删除的话,订单确认页会 read 到一个不存在的地址,导致回填缺失。上面的代码里已经处理,但如果你用框架(Vue/React),需要在删除时同步setSelectedId(null)。

  3. 多个入口共用 localStorage 时的 key 冲突
    如果项目里其他地方也用shippingAddresses,会互相覆盖。建议给 key 加前缀,例如mall_shipping_addresses。

  4. 异步渲染时序问题
    如果你在 Vue 的mounted里读取 localStorage,注意renderAddressList要等在 DOM 渲染后再执行,或者用nextTick。否则事件绑定可能找不到元素。

  5. 订单确认页刷新后 addressId 还在,但地址已被删除
    页面 load 时调用loadSelectedAddress发现查不到地址时,要不就自动跳到商品详情页重新选,要不弹窗提示。避免拿着一个失效的地址继续提交。


6. 扩展建议

如果后续要对接真实接口,这套前端方案可以平滑迁移:

  • 删除时调用后端接口,删除成功后再更新 localStorage;
  • 保存地址时把后端返回的数据结构(如 addressId)一并保存;
  • 订单确认页提交时向后端传addressId,后端再去查地址,而不是信任前端传的完整地址文本。

纯前端方案先跑通交互,后续接后端只改数据源,界面和流程不需要大改。

希望这套最简方案能帮到你,有问题可以随时对照调试。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 19:18:25

主动配电网SOCR最优潮流:从论文公式到可运行Python代码

简介&#xff1a;本资源是一套面向电力系统方向研究生与高年级本科生的毕业设计级科研实践材料&#xff0c;聚焦双碳目标下综合能源系统&#xff08;IES&#xff09;低碳经济运行优化问题&#xff0c;提供从建模、求解到结果分析的完整技术方案。压缩包共25个文件&#xff0c;含…

作者头像 李华
网站建设 2026/10/21 20:07:50

微信AI帮写朋友圈文案实测:技术逻辑、使用技巧与避坑指南

1. 微信AI帮写功能到底解决了什么问题朋友圈发一条动态&#xff0c;从选图到配文&#xff0c;很多人能纠结十几分钟。拍了张好看的咖啡照&#xff0c;想配一句“周末的仪式感”&#xff0c;又觉得太装&#xff1b;想写“今天真开心”&#xff0c;又觉得太干。最后要么发个表情包…

作者头像 李华
网站建设 2026/10/13 19:47:12

级联延迟反馈建模:破解展示广告CVR预估的两段式延迟难题

展示广告的CVR预估&#xff0c;做久了你会发现有两件事最让人头疼&#xff1a;一是样本极度稀疏&#xff0c;二是转化反馈总是迟到。市面上解决延迟反馈的模型已经不少&#xff0c;但多半是把“曝光到转化”当成一个整体延迟来建模。阿里妈妈在WWW 2026上提出的这篇级联延迟反馈…

作者头像 李华
网站建设 2026/10/16 7:44:31

用Python爬虫打造城市图书馆活动采集系统:从页面解析到CSV导出

城市图书馆的活动信息其实很有价值&#xff0c;讲座、展览、读书分享会都是公开资源&#xff0c;就是散落得厉害。我年前想做一份自己的活动日历&#xff0c;发现光靠人工去翻官网&#xff0c;一遍遍登记时间地点&#xff0c;效率实在低得可怕。后来我索性用 Python 爬虫 写了套…

作者头像 李华
网站建设 2026/10/21 22:39:44

阿里 Qwen3.6-35B-A3B 实测:MoE 架构下智能体编程与多模态能力全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/17 13:35:46

LaTeX中优雅输出钩号与叉号:方案对比与实战指南

1. 为什么要在文档里折腾钩和叉写文档这件事&#xff0c;最让人抓狂的往往不是内容本身&#xff0c;而是那些看起来微不足道的符号。你辛辛苦苦排好了一篇技术报告或者一份检查清单&#xff0c;结果到了要标记"通过"和"未通过"的地方&#xff0c;发现键盘上…

作者头像 李华