简介:这是一份JavaScript实用脚本资源,面向Web前端开发者与网页设计人员,解决页面中弹出新窗口无法居中显示、影响交互体验的问题。资源以PDF文档形式呈现,共1个文件,压缩包整体约25KB,内容既包含可直接运行的JS函数源码,也包含对实现原理的逐步拆解,适合具备一定JS基础并希望提升弹窗交互细节的开发者参考学习。文档重点讲解了MM_openBrWindow函数中url、winName、width、height四个参数的作用,以及如何结合screen.width、screen.height和浏览器差异计算窗口的xposition与yposition,并通过window.open()方法将left、top或screenx、screeny等属性拼接为窗口特性字符串,从而适配不同浏览器环境。阅读后可以获得一套完整可复用的居中弹窗脚本,理解其兼容性处理思路,便于后续直接嵌入项目或修改扩展。该资源在CSDN已有343人学习浏览,对日常Web开发中需要实现模态式弹窗或新窗口居中展示的场景具有不错的实用参考价值。
1. 弹出新窗口居中显示的脚本:先从 window.open 的 features 参数说起
后台管理系统里最常被吐槽的交互,就是点“查看详情”或“打印预览”按钮后弹出来的新窗口落在屏幕左上角。明明写了一段 js 让弹出新窗口居中显示,结果有的机器上生效、有的机器上纹丝不动,坐标像随机的,这就是把 window.open 的 features 参数用错了一半的症状。下面从坐标怎么算讲起,落到一个可直接复制的脚本,再拆浏览器拦截、多显示器、窗口尺寸钳制这些常见卡点。适合被这类“小问题却反复返工”困扰的前端,也适合要封装弹窗工具函数的人。
2. 用 screen.availWidth 计算居中坐标:最小可运行脚本与任务栏修正
2.1 window.open 的第三个参数:窗口位置与尺寸都由它决定
“弹出新窗口”这件事的标准做法是 window.open(url, name, features)。很多人只传前两个参数,第 3 个 features 要么留空要么随手写个 width=800,height=600,然后发现不管怎么改,窗口都在浏览器默认位置打开。features 是一个逗号分隔的字符串,left 和 top 对应窗口左上角相对屏幕左上角的坐标,单位是 CSS 像素;width 和 height 是窗口内容区尺寸。下面这张表列出了平时最常用的几个参数。
| 参数名 | 取值示例 | 作用 | 注意点 |
|---|---|---|---|
| left | 200 | 窗口左上角水平坐标 | 相对屏幕左上角,含边框 |
| top | 100 | 窗口左上角垂直坐标 | 相对屏幕左上角,含边框 |
| width | 800 | 窗口内容区宽度 | 不带单位,纯整数 |
| height | 600 | 窗口内容区高度 | 同样不带单位 |
| name(第 2 参数) | detail_2026 | 窗口名字 | 同名会复用已有窗口 |
第 2 参数 name 与窗口定位是绑定关系。第一次用 detail_001 打开,如果它没被关闭,第二次再以同名打开,浏览器直接复用旧窗口,新传入的 left/top 会被忽略。所以做居中脚本时,name 不能一成不变,后面第 3、4 章都会回到这个点。
写一个能理解坐标的脚本前,先记住一句判断:屏幕上的可用区域和物理屏幕不是一回事。screen.width 是整块屏幕宽度,screen.availWidth 是去掉任务栏之后浏览器可以使用的宽度。任务栏在底部时,两者高度差正好是任务栏高度;任务栏在左侧或右侧时,差的是宽度。居中公式里优先用 avail 系列,否则弹窗会被任务栏“顶”偏,视觉上明显就是高低左右各差一截。
2.2 最小居中脚本:availWidth 与 Math.max 兜底
一个能直接跑的最小脚本长这样,打开一个 800x600 的新窗口,让它相对可用区域居中。
const targetUrl = 'https://example.com/detail.html'; const winWidth = 800; const winHeight = 600; const left = Math.max(0, Math.round((screen.availWidth - winWidth) / 2)); const top = Math.max(0, Math.round((screen.availHeight - winHeight) / 2)); const win = window.open( targetUrl, '_blank', `width=${winWidth},height=${winHeight},left=${left},top=${top}` );逻辑说明:先用可用区域减去窗口宽高,除以 2 得出左右、上下的留白,这个值就是让窗口居中的左上角坐标。Math.round 是为了得到整数坐标,某些浏览器对小数坐标会直接截断或四舍五入,统一取整更稳定;Math.max(0, ...) 是防止窗口尺寸比屏幕还大时算出的负数坐标,负坐标会让窗口一部分跑到屏幕外。最后用模板字符串拼 features,传给 window.open 的第三个参数。
参数说明:winWidth 和 winHeight 改成实际业务尺寸即可,但一般别小于 300,具体原因第 4 章会讲;name 用 '_blank' 表示每次开新窗口,这样第一次就不会被“复用”干扰,代价是如果连点两次同一按钮会出两个窗口,这个行为后面再按需调整。这段脚本在 file:// 页面里也能跑,不过新窗口跳转的 url 必须是能被浏览器访问的地址,否则打开就是空白页。
任务栏在顶部或左侧时,仅靠 availWidth/availHeight 仍有可能差一点点。更稳的写法是把屏幕原点偏移也算进来,用 screen.availLeft 和 screen.availTop 作为坐标基点:这两个字段在任务栏贴着屏幕边缘时能反映可用区域从哪个像素开始。我一般会把它合并成下面这个健壮版。
const left = Math.max(0, Math.round((screen.availWidth - winWidth) / 2) + (screen.availLeft || 0)); const top = Math.max(0, Math.round((screen.availHeight - winHeight) / 2) + (screen.availTop || 0));逻辑说明:availLeft 在 Chrome 等主流浏览器里多数时候返回 0,因为任务栏默认在底部时可用区域从左上角开始;一旦用户在系统里把任务栏拖到左侧,availLeft 就表示可用区域左边界的位置,加上它之后算出的 left 才不会盖住任务栏。(screen.availLeft || 0) 是为了兼容不支持这个属性的老浏览器,拿不到就当 0 处理。top 同理对应任务栏在顶部的情况。
3. 封装 openCentered 函数:默认宽高、唯一 name 与多显示器边界
3.1 函数签名与默认宽高:先从“能用”到“好调”
第 2 章的脚本在控制台验证没问题,但放到项目里会有几个不顺手的地方:每个页面都要重复写一遍坐标计算,调用者改尺寸时会忘记同步改 left/top,而且 window.open 被拦截时返回 null,调用方不知道接下来该干嘛。所以我一般会把它封装成一个模块级工具函数,放在 utils/dom.js 里。
function openCentered(url, options = {}) { const width = options.width || 900; const height = options.height || 650; const name = options.name || '_blank'; const features = [ `width=${width}`, `height=${height}`, `left=${Math.max(0, Math.round((screen.availWidth - width) / 2) + (screen.availLeft || 0))}`, `top=${Math.max(0, Math.round((screen.availHeight - height) / 2) + (screen.availTop || 0))}`, 'resizable=yes', 'scrollbars=yes' ].join(','); return window.open(url, name, features); }逻辑说明:封装的价值在三点。第一,坐标计算只有一处,调整任务栏补偿时不用全项目找。第二,features 里的 resizable=yes 和 scrollbars=yes 不写的话,部分浏览器默认不让拖拽窗口大小、不显示滚动条,报表类页面很容易吃这个亏。第三,把 name 作为参数暴露,调用方可以决定每次开新窗口还是复用旧窗口。
参数说明:width 默认 900、height 默认 650,这个尺寸在 1366x768 的笔记本上刚好能显示完整内容且不贴边;如果你的后台面向 1920 以上大屏,默认值可以提到 1100。options.name 不传时用 '_blank',每次打开都是新窗口;需要复用时传一个固定的业务标志,比如 'printPreview',同一个窗口没关就一直复用。
提示:如果项目里同时存在多个弹窗工具,把 openCentered 放进公共 utils,避免每个页面各算一遍坐标。
这里有个必须接受的现实:window.open 返回的是一个指向新窗口的引用,也可能被浏览器拦截策略拦成 null。调用方得判空,否则 null.location 会直接抛异常。常见写法是把返回值交给调用方自己处理,而不是在函数内部吞掉。
const win = openCentered('/print/order/10086', { width: 1000, height: 700 }); if (win) { win.focus(); } else { console.warn('popup blocked, waiting for user gesture fallback'); }逻辑说明:win.focus() 是让刚打开的窗口拿到焦点,不然在部分浏览器里新窗口可能在后台打开,用户以为没反应。win 为 null 时意味着弹窗被拦截,这时候要看第 4 章的降级处理,不能只在控制台里打一行警告了事。
3.2 多显示器场景:主屏居中为主,双屏只能近似
screen.availWidth 只描述“主显示器”的可用区域。用户把浏览器窗口拖到副屏上点击按钮,window.open 仍然按主屏坐标打开:如果副屏在主屏右侧,弹窗会出现在主屏左侧边缘附近;如果副屏在主屏左侧,甚至可能直接开在看不见的区域。这是居中脚本最典型的“翻车”现场之一。
原理上浏览器没有暴露“哪块屏幕、每个屏幕的尺寸”这套完整的系统级接口,所以精确做到“鼠标在哪块屏,窗口就在哪块屏居中”目前做不到。我一般会按两种策略处理:业务上要求不高时,接受主屏居中,这在办公室里大量用户其实只用一个屏幕的场景下已经够用;对副屏用户,做个近似——通过 window.screenX 推断主窗口在屏幕坐标系中的位置,再给弹窗坐标叠加一个与主窗口位置相关的偏移,让弹窗至少落在与主窗口同一块屏的范围内。
function openCentered(url, options = {}) { const width = options.width || 900; const height = options.height || 650; const baseLeft = Math.round((screen.availWidth - width) / 2) + (screen.availLeft || 0); const baseTop = Math.round((screen.availHeight - height) / 2) + (screen.availTop || 0); const shiftX = window.screenX < 0 ? window.screenX * 0.8 : 0; const left = Math.max(0, Math.round(baseLeft + shiftX)); const top = Math.max(0, Math.round(baseTop)); return window.open(url, options.name || '_blank', `width=${width},height=${height},left=${left},top=${top}`); }逻辑说明:window.screenX 是当前页面所在窗口的左边界在整个屏幕坐标系中的位置,主屏从 0 开始的,如果为负说明主窗口在左侧副屏上。把负值乘以 0.8 叠加到弹窗的 left 上,弹窗会朝副屏方向靠近,虽然不能精确居中,但至少用户能看到窗口弹出来。这个系数没有标准答案,属于根据显示器布局调出来的经验值。参数说明:shiftX 只在 screenX 为负时生效,右侧副屏因为主窗口 screenX 为 0 无法区分,这是接口限制,能改进的空间有限。
3.3 feature 字符串:顺序无关但漏掉 width 等于白算
window.open 的 features 参数解析时,参数顺序无所谓,但缺关键项会改变整条路径。最典型的是只写 left 和 top 不写 width、height:Chrome 会在这种情况下把窗口当成一个普通标签页打开,而标签页没有“独立窗口位置”的概念,left/top 被直接忽略,脚本表现就是“明明算好了坐标,窗口还是正常标签页”。我见过不止一个人在这里调试半天。
另一个相关坑是 name 复用。如果给固定名字,比如 'report',第一次开窗口后关闭再开,一切正常;但没关就再点一次按钮,浏览器会激活旧窗口并忽略新传入的坐标。换个时间戳 name 可以让每次点击都得到独立窗口,代价是会产生多个窗口:
const name = options.name || `win_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;逻辑说明:时间戳加随机片段防止两次点击间隔太短导致的同名冲突。当你不希望复用窗口时,用这段;当你明确想复用(比如同一份报表只开一个窗口),把 options.name 传成固定字符串即可,同时要接受坐标只在第一次生效的事实。这个行为我常跟人解释成一句口诀:window.open 的坐标是“创建时生效”,不是“激活时生效”。
4. 避坑排查:弹窗被拦截、尺寸被钳制与坐标失效的 5 个现场
4.1 按钮点了没反应,控制台不报错
现象:页面里绑定点击事件调用 openCentered,本地开发正常,部署到线上后点按钮没有任何反应,控制台也没有 error,甚至返回 null 被静默丢掉了。
原因:浏览器弹窗拦截并不是看“这段代码是不是 js”,而是看执行时机是否属于用户手势的同步调用链。只要你在回调里加了 await、setTimeout、或者先请求接口拿到数据再 window.open,就脱离了同步链,拦截策略立刻生效。
解决:保持 window.open 和 click 事件在同一个同步执行栈内。需要先拿数据再弹窗的业务,改成先开一个空窗口拿到引用,再异步往这个引用里塞地址,或者把打开动作提前到点击瞬间,数据加载用页面内部逻辑处理。
button.addEventListener('click', () => { const win = openCentered('', { width: 900, height: 650 }); if (!win) return; fetch('/api/detail?id=' + id) .then(res => res.json()) .then(data => { win.location.href = '/detail-page?data=' + encodeURIComponent(JSON.stringify(data)); }); });逻辑说明:window.open 的时机是同步的,所以它不会被拦截;真正的页面跳转发生在接口返回后,此时 win 对象已经存在,改 location 不涉及弹窗拦截判断。参数说明:url 在打开瞬间传空字符串,随后用 win.location.href 赋值,注意这种做法会让新窗口先闪现一个空白页面,如果介意就在赋值前用 win.document.write 渲染一版 loading 占位。
4.2 窗口确实弹出来了,但明显偏右上
现象:单显示器、任务栏在底部的情况下,窗口按脚本计算应该居中,实际偏右上,左和下都空出一块。
原因:用的是 screen.width / screen.height 而不是 availWidth / availHeight,或者高 DPI 下浏览器缩放比例不是 100%。前者把任务栏高度错误地算进了居中公式,窗口垂直重心被抬高;后者是系统缩放 125% 时 CSS 像素和物理像素不一致,但 window.open 的坐标按 CSS 像素走,理论上不会偏,偏了多半是用了 devicePixelRatio 做了多余换算。
解决:回归 availWidth/availHeight 公式,不要在脚本里乘 devicePixelRatio。这种偏差往往不是玄学,就是单位或基准选错。另外给 top 加一个小的经验补偿值,比如 20px,把窗口标题栏高度从视觉中心里扣掉,让内容区看起来更居中。
const top = Math.max(0, Math.round((screen.availHeight - height) / 2) + (screen.availTop || 0) - 20);逻辑说明:-20 是给浏览器窗口自带的标题栏让位。窗口左上角坐标是外框的左上角,用户视觉感知的“窗口中心”其实是内容区中心,标题栏占了约 20 到 30 像素,不补偿的话内容区视觉上会偏低。这个值没有标准,可以根据公司主流浏览器微调。
4.3 同样一个按钮,第二次打开就不居中了
现象:第一次点击,新窗口按预期居中;关闭窗口后再点第二次,窗口出现在上一次的位置,甚至直接复用旧窗口显示旧数据。
原因:window.open 的第二个参数 name 是固定值,比如 'printWin'。浏览器记住了这个名字对应的窗口,第二次不再新建,而是把已有窗口重新激活,features 里的 left/top 就不生效了。
解决:在没有关闭需求时使用固定 name;每次都要独立窗口时,用时间戳生成唯一 name。数据刷新需求则配合第 3 章定义的 name 规则:在打开后往 win.location 赋值,避免复用旧窗口里的旧页面。
4.4 设置了 width=250,打开后窗口不是 250 宽
现象:脚本里写 width=200,height=160,窗口打开后发现比设定值大一圈,而且位置明显不在计算得到的坐标上。
原因:浏览器对独立弹窗有最小尺寸限制,Chrome 的逻辑像素最小宽度通常要到 250 以上,你请求 200 时浏览器按最小尺寸创建窗口,但 left/top 还是按 200 算出来的,于是窗口左边缘按小窗口坐标放,实际宽度却被撑大,看起来就是“偏了”。
解决:把业务参数的下限约束在 300 以上;实在要极小窗口,打开后用 moveTo 做二次校正,先读实际 outerWidth/outerHeight,再重新计算坐标。
const win = openCentered('/minimal-panel', { width: 260, height: 200 }); if (win) { setTimeout(() => { const realW = win.outerWidth; const realH = win.outerHeight; win.moveTo(Math.max(0, Math.round((screen.availWidth - realW) / 2)), Math.max(0, Math.round((screen.availHeight - realH) / 2))); }, 50); }逻辑说明:延迟 50ms 是为了让浏览器完成窗口创建和布局,立刻读 outerWidth 可能拿到的还是旧值。moveTo 直接设置窗口左上角位置,相当于把坐标按真实尺寸重算了一遍。参数说明:50ms 是我常用的延迟,网络慢或页面初始化重的时候可以调到 120ms,但不能完全依赖这个补偿,主窗口被拦截时 win 为 null,moveTo 会抛错,调用前仍要判空。
4.5 页面在 iframe 里,window.open 返回 null
现象:后台管理系统左侧菜单是 iframe,右侧内容也是 iframe,点菜单里的按钮打开居中窗口,本地能弹,集成到门户后失灵。
原因:iframe 如果带 sandbox 属性且没有 allow-popups,window.open 会被直接禁止;另外 iframe 里 window.screen 拿到的还是顶层屏幕信息,坐标未必代表用户的真实显示器布局。
解决:优先检查 sandbox 属性,补上 allow-popups;更干净的方案是把“打开窗口”的动作提升到顶层页面,iframe 内通过 postMessage 把 url 和尺寸发给顶层,由顶层调用 openCentered。这样既绕开 iframe 权限限制,也避免每个 iframe 里塞一份脚本。
// iframe 内 parent.postMessage({ type: 'OPEN_CENTERED', url, width: 1000, height: 700 }, '*'); // 顶层页面 window.addEventListener('message', (e) => { if (e.data.type === 'OPEN_CENTERED') { openCentered(e.data.url, { width: e.data.width, height: e.data.height }); } });逻辑说明:postMessage 是跨窗口通信的标准做法,顶层页面统一收口所有弹窗打开动作,后续要加拦截降级或统一日志都只需要改一处。参数说明:postMessage 的第二个参数理论上应写明确的目标源,内网系统里写 '' 省事但不推荐,能确定顶层页面域名时建议把 '' 换成具体 origin,防止其他页面也能触发顶层弹窗。
5. 把居中脚本接入真实页面:批量多开、同步触发与降级链接
5.1 列表页批量查看:多窗口错位打开
后台报表系统最常见的场景不是单个详情窗口,而是勾选多行数据后点“批量查看”。如果每行都按同一个居中坐标打开,窗口会全部叠在一起,后开的窗口遮住先开的,用户要挨个拖动才能看到全部内容。这里需要的不是严格的“居中”,而是“居中后错位”——我先按居中公式算出基准点,再给每个窗口叠加一个行列偏移。
function openCenteredBatch(urls, options = {}) { const width = options.width || 900; const height = options.height || 650; const gridCols = 3; const stepX = 30; const stepY = 30; const baseLeft = Math.max(0, Math.round((screen.availWidth - width) / 2) + (screen.availLeft || 0)); const baseTop = Math.max(0, Math.round((screen.availHeight - height) / 2) + (screen.availTop || 0)); return urls.map((url, index) => { const col = index % gridCols; const row = Math.floor(index / gridCols); const left = baseLeft + col * stepX; const top = baseTop + row * stepY; const name = `batch_${Date.now()}_${index}`; return window.open(url, name, `width=${width},height=${height},left=${left},top=${top}`); }); }逻辑说明:第一行三个窗口分别按 0、30、60px 的水平偏移错开,第二行开始向下叠加 30px,让所有新窗口呈轻微的阶梯状排列。name 里带时间戳和 index,保证每次批量打开都不会复用旧窗口。参数说明:gridCols 一般取 3,超过 3 个窗口时按 3 列往下排;stepX 和 stepY 按屏幕宽度调整,1920 宽下 30px 够用,1366 宽下可以缩到 20。
这个函数返回的是窗口引用数组,调用方可以统一做后续处理:比如全部打开后通知用户“已打开 N 个窗口”,被拦截的引用为 null 时单独统计。
5.2 打开后被拦截:给用户一个手动降级链接
弹窗被拦截是外部环境行为,脚本永远无法完全绕过。从业者都会在 openCentered 返回值判空后准备一条降级链路,让用户至少能通过一次普通点击到达目标页面。常见做法是在按钮下方动态插入一个链接,提示用户以新标签页方式打开。
const win = openCentered('/report/2026/01', { width: 1200, height: 800 }); if (win) { win.focus(); } else { const tip = document.createElement('div'); tip.innerHTML = '浏览器拦截了弹窗,请点击 <a href="/report/2026/01" target="_blank">这里</a> 以新标签页打开'; document.querySelector('.btn-wrap').appendChild(tip); }逻辑说明:降级链接用普通 a 标签加 target="_blank",这不算 window.open,属于用户手势触发的导航,浏览器不会拦截,用户看到的是新标签页而不是居中窗口,体验降级但操作没有中断。参数说明:降级提示应当带样式,不能一闪而过,最好留在页面上直到用户点了链接。
5.3 同一个窗口中重复刷新:居中一次,数据自动轮换
有些业务不希望每次都开新窗口,比如设备状态监控,弹窗固定一块屏幕区域,数据每隔几秒刷新。这种场景反而要利用 name 复用,避免窗口越开越多。做法是固定 name,打开后等页面加载完,再往窗口里注入定时刷新逻辑。
const WIN_NAME = 'monitor-panel'; function openMonitor(url) { const win = openCentered(url, { name: WIN_NAME, width: 900, height: 600 }); if (win) { win.addEventListener('load', () => { const refreshScript = win.document.createElement('script'); refreshScript.textContent = 'setInterval(() => location.reload(), 30000);'; win.document.body.appendChild(refreshScript); }); } }逻辑说明:name 固定为 monitor-panel 后,第二次调用时 window.open 返回的是已有窗口,坐标不会变更,但 win 引用有效,可以在 load 事件后往它里面注入刷新逻辑。setInterval 配合 location.reload 是监控页最简单的兜底刷新,30 秒一次的频率对多数报表足够,太频繁会加重后端负担。参数说明:win.document 在跨域页面下访问会抛错,这段只适用于同源监控页;跨域页面要把刷新逻辑写在目标页面自己内部。
到这里,居中脚本已经从“单次弹窗居中”扩展成了“批量错位打开”“拦截降级”“复用刷新”三种业务形态,基本覆盖了后台系统里弹窗的绝大多数使用场景。
6. 验证居中效果的一个便宜技巧:让子窗口自证坐标
每次改动尺寸参数后,肉眼判断“居不居中”其实很不准,尤其在高 DPI 屏幕上,偏差可能只有十几像素,眼睛根本分不出来。我能给你一个几乎零成本的验证方法:让新窗口自己把期望坐标和实际坐标打印出来对比。
在打开弹窗的页面里,往空窗口写入一段自检脚本,子窗口读取自己的 window.screenX、window.screenY、outerWidth、outerHeight,和父页面传过去的期望 left/top、期望宽高一并显示。
function verifyCentered(openUrl, width = 800, height = 600) { const left = Math.max(0, Math.round((screen.availWidth - width) / 2) + (screen.availLeft || 0)); const top = Math.max(0, Math.round((screen.availHeight - height) / 2) + (screen.availTop || 0)); const win = window.open('', 'verify_centered', `width=${width},height=${height},left=${left},top=${top}`); if (!win) return; const tolerance = 8; win.document.write('<pre>加载中...</pre>'); win.document.close(); setTimeout(() => { const diffX = Math.abs(win.screenX - left); const diffY = Math.abs(win.screenY - top); const status = diffX <= tolerance && diffY <= tolerance ? 'PASS' : 'FAIL'; win.document.body.innerHTML = `<pre>期望位置: ${left}, ${top}\n实际位置: ${win.screenX}, ${win.screenY}\n` + `实际尺寸: ${win.outerWidth} x ${win.outerHeight}\n结果: ${status}</pre>`; }, 100); }逻辑说明:win.document.write 在 about:blank 空窗口里可以写内容,document.close() 结束文档流后内容才稳定渲染。setTimeout 等 100ms 是给窗口创建和布局留时间,直接读 screenX 可能还是 0。偏差小于 8px 判 PASS,因为在多显示器或系统缩放下,1 到 2px 的锯齿是正常的。参数说明:容差 8px 是我调出来的经验值,新窗口的边框粗细从 1 到 6px 不等,太小会天天误报,太大会掩盖真实偏移。
我最早只是在 win10 单屏上自测这段,自我感觉很良好,结果被同事在双屏扩展模式下现场抓包,弹窗开在看不见的副屏区域,那次之后我把这段验证脚本固化成了项目里的一个 util 函数,每次改尺寸先跑一遍,再交给别人验收。你如果刚接入居中脚本,建议同样先把这段验证跑通,实际坐标和你期望坐标对上了,再继续往里面加业务逻辑,会省下不少来回调试的时间。希望帮到你。
本文还有配套的精品资源,点击获取