必须用原生:因formData自动处理同名checkbox、跳过disabled、匹配后端参数名,避免手动维护取值逻辑;多选用getAll(),空值默认不参与过滤,状态需同步URL。

直接用原生 搭结构,配合 FormData 读值 + JavaScript 做交集过滤或发请求,别手写一堆 if 判断或用 div 模拟表单——否则加个分页、换套 UI 或同步 URL 就崩。
为什么必须用 而不是
不用 的后果很具体:form.elements 拿不到控件、new FormData(form) 无法自动合并同名 checkbox、disabled 项不会被跳过、空值处理得靠你自己补一堆逻辑。更麻烦的是,一旦后端参数名变了(比如从 cat_id 改成 category_id),你得翻遍 JS 找所有 getElementById('filter-cat') 并手动改变量名和取值逻辑。
正确做法是让每个筛选项的 name 和后端 API 参数名完全一致:
-
—— 多选品牌,浏览器自动提交所有勾选项
-
—— 单选分类
-
和 —— 拆开写,别塞进一个 price_range 再切分
- 避免
name="brand[]":这是 PHP 风格,FormData 不识别方括号,后端收不到数组
怎么用 FormData 安全读取并判断“已激活条件”
用户没填的字段不该参与过滤,但也不能让整行消失。关键点是:每个条件默认为 true(不限制),只在有值时才加入判断逻辑。
示例片段:
const form = document.querySelector('#filter-form');
function getFilters() {
const formData = new FormData(form);
const filters = Object.fromEntries(formData.entries());
// 注意:多个同名 checkbox 会被最后一个值覆盖
// 正确做法是用 getAll()
const brandValues = formData.getAll('brand'); // 返回 ['apple', 'samsung']
return { ...filters, brand: brandValues.length ? brandValues : null };
}
<p>function applyFilter() {
const { brand, category, price_min, price_max } = getFilters();
const rows = document.querySelectorAll('#product-table tbody tr');</p><p>rows.forEach(row => {
const rowBrand = row.dataset.brand; // 推荐用 data-* 存真实值,别依赖显示文本
const rowCategory = row.dataset.category;
const rowPrice = parseFloat(row.dataset.price);</p><pre class="brush:php;toolbar:false;">const matchBrand = !brand || brand.includes(rowBrand);
const matchCategory = !category || rowCategory === category;
const matchPrice =
(!price_min && !price_max) ||
(price_min && price_max && rowPrice >= price_min && rowPrice = price_min) ||
(!price_min && price_max && rowPrice <p>});
}</p>
常见坑:
-
formData.entries() 对多选 checkbox 只返回最后一个值,必须用 formData.getAll('brand')
-
row.cells[0].textContent 容易因空格、换行、HTML 标签导致匹配失败,优先用 data-price、data-brand 等属性存结构化值
-
price_min 和 price_max 是字符串,比较前要 parseFloat(),且需分别判断是否为空
前端实时过滤 vs 后端查询,怎么选
数据量是唯一硬指标:≤ 1000 行且不涉及权限、关联表、敏感字段脱敏,可前端过滤;其余一律走后端。
前端过滤的典型问题:
- 用户勾选“iPhone”+“¥5000–¥6000”,但页面里根本没有同时满足这两项的商品 → 列表清空,用户以为系统坏了
- 价格区间输入框留空时,JS 误把空字符串当
0,导致 rowPrice >= "" 为 true(因为 "" >= 0 是 true)
- 移动端反复触发
input 事件,没防抖就频繁重绘,卡顿明显
后端方案只需前端做两件事:
- 监听
submit 事件,e.preventDefault() 阻止跳转
- 用
fetch('/api/products?' + new URLSearchParams(formData)) 发请求,响应后替换表格内容
注意:URLSearchParams(formData) 会自动处理空值、数组、特殊字符编码,比手拼 query string 安全得多。
最常被忽略的一点:筛选状态必须能同步到 URL。用户分享链接、刷新页面、回退时,筛选条件不能丢。这需要在每次 applyFilter() 后调用 history.replaceState(),并监听 popstate 事件还原表单值——不是可选项,是真实业务里的必做项。
不用 的后果很具体:form.elements 拿不到控件、new FormData(form) 无法自动合并同名 checkbox、disabled 项不会被跳过、空值处理得靠你自己补一堆逻辑。更麻烦的是,一旦后端参数名变了(比如从 cat_id 改成 category_id),你得翻遍 JS 找所有 getElementById('filter-cat') 并手动改变量名和取值逻辑。
正确做法是让每个筛选项的 name 和后端 API 参数名完全一致:
-
—— 多选品牌,浏览器自动提交所有勾选项 -
—— 单选分类 -
和—— 拆开写,别塞进一个price_range再切分 - 避免
name="brand[]":这是 PHP 风格,FormData不识别方括号,后端收不到数组
怎么用 FormData 安全读取并判断“已激活条件”
用户没填的字段不该参与过滤,但也不能让整行消失。关键点是:每个条件默认为 true(不限制),只在有值时才加入判断逻辑。
示例片段:
const form = document.querySelector('#filter-form');
function getFilters() {
const formData = new FormData(form);
const filters = Object.fromEntries(formData.entries());
// 注意:多个同名 checkbox 会被最后一个值覆盖
// 正确做法是用 getAll()
const brandValues = formData.getAll('brand'); // 返回 ['apple', 'samsung']
return { ...filters, brand: brandValues.length ? brandValues : null };
}
<p>function applyFilter() {
const { brand, category, price_min, price_max } = getFilters();
const rows = document.querySelectorAll('#product-table tbody tr');</p><p>rows.forEach(row => {
const rowBrand = row.dataset.brand; // 推荐用 data-* 存真实值,别依赖显示文本
const rowCategory = row.dataset.category;
const rowPrice = parseFloat(row.dataset.price);</p><pre class="brush:php;toolbar:false;">const matchBrand = !brand || brand.includes(rowBrand);
const matchCategory = !category || rowCategory === category;
const matchPrice =
(!price_min && !price_max) ||
(price_min && price_max && rowPrice >= price_min && rowPrice = price_min) ||
(!price_min && price_max && rowPrice <p>});
}</p>
常见坑:
-
formData.entries()对多选checkbox只返回最后一个值,必须用formData.getAll('brand') -
row.cells[0].textContent容易因空格、换行、HTML 标签导致匹配失败,优先用data-price、data-brand等属性存结构化值 -
price_min和price_max是字符串,比较前要parseFloat(),且需分别判断是否为空
前端实时过滤 vs 后端查询,怎么选
数据量是唯一硬指标:≤ 1000 行且不涉及权限、关联表、敏感字段脱敏,可前端过滤;其余一律走后端。
前端过滤的典型问题:
- 用户勾选“iPhone”+“¥5000–¥6000”,但页面里根本没有同时满足这两项的商品 → 列表清空,用户以为系统坏了
- 价格区间输入框留空时,JS 误把空字符串当
0,导致rowPrice >= ""为true(因为"" >= 0是true) - 移动端反复触发
input事件,没防抖就频繁重绘,卡顿明显
后端方案只需前端做两件事:
- 监听
submit事件,e.preventDefault()阻止跳转 - 用
fetch('/api/products?' + new URLSearchParams(formData))发请求,响应后替换表格内容
注意:URLSearchParams(formData) 会自动处理空值、数组、特殊字符编码,比手拼 query string 安全得多。
最常被忽略的一点:筛选状态必须能同步到 URL。用户分享链接、刷新页面、回退时,筛选条件不能丢。这需要在每次 applyFilter() 后调用 history.replaceState(),并监听 popstate 事件还原表单值——不是可选项,是真实业务里的必做项。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











