
第一步:先搞定“数据从哪来”——别再写死数据了!
以前我犯过最傻的错,就是把省市区、商品分类全写成固定数组,比如const provinces = ['广东', '广西', '湖南']
,结果朋友说“要加海南省”,我得改代码、重新打包部署,来回折腾半天。后来才明白:动态联动的核心,是“选上一级→发请求拿下一级数据”,而不是把所有数据都塞在代码里。
那数据怎么拿?用Axios
或者浏览器原生的Fetch
就行——我一般用Axios
,因为它比Fetch
多了拦截器、错误处理这些方便功能。比如拿省份数据的代码,长这样:
async getProvinces() {
try {
const res = await axios.get('/api/provinces'); // 后端给的省份接口
this.provinceList = res.data; // 把返回的省份列表存到data里
} catch (err) {
alert('省份数据加载失败,换个网试试?'); // 别忘加错误提示,用户不是程序员
}
}
你看,是不是比写死数据灵活多了?就算后端加了新省份,你不用改一行代码——接口返回啥,下拉框就显示啥。
这里有个新手必踩的坑:别忘加async/await
!我之前没加这俩关键词,结果请求还没完成,就想给provinceList
赋值,页面上啥都没显示,查了半小时才发现是“异步”在搞鬼。记住:只要发请求,就得用async
包着函数,await
跟着请求——不然数据永远“慢半拍”。
第二步:让“选择”和“数据”联动起来——状态同步是关键
光拿数据还不够,得让“选上一级”的动作,触发“取下一级数据”的操作。这一步的核心是“状态同步”——比如你选了“广东省”(provinceId=1),就得把这个provinceId
传给后端,让它返回广东省的城市列表。
我用Vue举个例子(React同理,换useState
就行):先在data
里定义三个变量——provinceId
(选中的省份ID)、cityId
(选中的城市ID)、districtId
(选中的区县ID),再给省份下拉框加个@change
事件,选完省份就触发handleProvinceChange
函数:
<!-模板部分:省份下拉框 >
先选省份呀
{{ p.name }}
// methods里的函数:选完省份后做啥?
async handleProvinceChange() {
// 先清空城市和区县的数据——不然选新省份,旧城市还留着!
this.cityId = '';
this.districtId = '';
this.cityList = [];
this.districtList = [];
if (!this.provinceId) return; // 没选省份的话,啥也不做
try {
this.isLoading = true; // 加个loading,避免用户点好几次
const res = await axios.get('/api/cities', {
params: { province_id: this.provinceId } // 把省份ID传给后端
});
this.cityList = res.data; // 把城市列表存起来
} catch (err) {
alert('城市数据加载失败,再试一次?');
} finally {
this.isLoading = false; // 不管成功失败,都关掉loading
}
}
看见没?选完省份后,我先把城市、区县的ID和列表清空了——这步超重要!我之前没清,结果选了“广东”再选“湖南”,城市列表里还留着“广州”“深圳”,用户以为系统坏了,朋友差点把我骂哭。
再给你补个避坑技巧:给下一级下拉框加disabled
属性!比如城市下拉框,得等选了省份才能点——不然用户没选省份就点城市,只能看见“请选择城市”,体验贼差。代码长这样:
<!-城市下拉框:没选省份就禁用 >
选完省份再点我~
{{ c.name }}
第三步:踩过的坑全告诉你——新手别再掉进去!
我做过3个联动下拉框项目,踩了至少5个坑,现在全给你列出来,省得你走弯路:
handleProvinceChange
里,把cityId
、districtId
、cityList
、districtList
全设为空。isLoading
状态,请求的时候把isLoading
设为true
,请求完设为false
,@change
事件里判断isLoading
为false
才发请求。catch
,用alert
或者UI组件提示“数据加载失败”。async/await
,数据慢半拍——记住:发请求就得用这俩关键词!localStorage
里,下次直接取: javascript
const cachedCities = localStorage.getItem(cities_${this.provinceId});
if (cachedCities) {
this.cityList = JSON.parse(cachedCities);
return; // 有缓存就不用发请求了
}
// 没缓存的话,再发请求
const res = await axios.get(‘/api/cities’, { params: { province_id: this.provinceId } });
this.cityList = res.data;
localStorage.setItem(cities_${this.provinceId}, JSON.stringify(res.data)); // 存缓存
最后:给你个能直接跑的Demo——省市区联动照着做就行
我把省市区联动的核心逻辑整理成了表格,后端需要给你的接口长这样(直接拿给后端看,他秒懂):
接口地址 | 请求方式 | 要传的参数 | 返回的字段 |
---|---|---|---|
/api/provinces | GET | 无 | id(省份ID)、name(省份名称) |
/api/cities | GET | province_id(省份ID) | id(城市ID)、name(城市名称) |
/api/districts | GET | city_id(城市ID) | id(区县ID)、name(区县名称) |
然后把我之前写的Vue代码复制过去,改改接口地址,就能直接跑起来——我去年用这个Demo帮朋友做地址填写功能,他说用户再也没吐槽“选地址麻烦”了。
其实多级联动下拉框真没那么难,核心就三点:动态拿数据、同步状态、处理细节。我踩过的坑你别踩,我用过的招你直接用——保准你做出来的联动框,比网上那些“复制粘贴”的代码稳多了。
要是你跟着做的时候遇到问题,或者有啥不懂的地方,评论区留个言——我每天都会看,能帮的肯定帮。毕竟谁没当过“对着下拉框发呆”的新手呢?赶紧去试试吧!
我之前第一次做联动下拉框的时候,选了省份后城市半天没反应,查了半小时才搞明白——没加async/await啊!那时候我以为发请求就是axios.get一下直接赋值,哪知道请求是“异步”的,就跟你点外卖刚付完钱,转头就站在门口等,外卖员还没出商家门呢,你说能拿到餐吗?后来加上async把函数包起来,前面再挂个await,保证等数据完完整整回来再给cityList赋值, 立马就显示城市了。
还有次帮朋友调bug,他选省份后城市一直空着,我打开浏览器控制台看请求参数,差点笑出声——后端明明要求传“province_id”,他给写成“provinceId”,就差个下划线!后端接口根本认不出这个参数,自然返回空数据。这就跟你给朋友发消息说“帮我带杯奶菜”,人家肯定一脸懵,不知道你要的是奶茶还是啥奇怪东西。从那以后,我每次传参数前都要对着后端文档念一遍,确认参数名一模一样才敢写,再也没犯过这种低级错。
最冤的还是没清空旧数据的坑——去年做地址选择功能,我选了广东省,城市列表出来广州、深圳,接着选湖南省,结果城市列表还是广州、深圳!用户截图发过来问“你们系统是不是坏了”,我查代码才发现,handleProvinceChange函数里根本没把cityList设为空数组。就跟你喝奶茶,刚喝完原味的没洗杯子,直接倒珍珠奶茶,喝起来一股混合味儿——后来我学乖了,每次选上一级的时候,第一行代码就是把下一级的ID和列表全清了,cityId设为空,cityList设成空数组,保证新数据能干干净净显示出来。
现在我遇到选上一级后下一级没数据的情况,第一反应就是查这三点:有没有加async/await?参数名和后端对不对得上?旧数据清没清?基本上80%的问题都能解决,比瞎改代码管用多了——毕竟踩过的坑多了,避坑的经验就熟了嘛。
Axios和Fetch选哪个更适合新手?
新手优先选Axios。因为Axios比Fetch多了拦截器(能统一处理请求头、错误)、自带JSON转换(不用手动写res.json()),而且错误处理更直观(catch能直接捕获请求失败)。比如文章里的代码,用Axios只需要await加try/catch,比Fetch少写好几行代码,对新手更友好。
选上一级后下一级没数据,常见原因有哪些?
最常见的3个原因:①没加async/await(请求还没完成就想赋值,数据慢半拍);②参数传错了(比如后端要province_id,你传成了provinceId);③没清空旧数据(选新省份后,旧城市数据没清空,导致新数据没显示)。先检查这三点,80%的问题都能解决。
频繁切换上一级时,请求太多导致加载慢,怎么优化?
可以加“缓存”和“防抖”。缓存就是把请求过的下一级数据存到localStorage(比如把广东省的城市数据存为cities_1),下次再选广东直接取缓存,不用发请求;防抖是用setTimeout,比如用户1秒内连续点两次省份,只发最后一次请求,避免重复请求。文章里提到的缓存方法,新手直接复制代码就能用,简单有效。
错误提示除了alert,还有更友好的方式吗?
当然有。可以用UI组件库的提示功能,比如Element UI的Message、Ant Design的Notification,样式比alert美观,还能自动消失。比如用Element UI的话,把alert换成this.$message.error(‘省份数据加载失败’),用户体验会好很多。如果不用组件库,也可以自己写个带样式的div提示框,比alert更贴合页面风格。