简介:这是一份面向微信小程序初学者的同花顺风格炒股小程序源码包,涵盖项目配置、页面交互与数据展示的完整实现,可作为课程设计或金融类小程序开发练习的参考范本。资源包共46个文件,以js业务逻辑、json配置、wxml页面结构、wxss样式表为核心,json负责路由与全局配置,wxml/wxss搭建页面骨架和视觉样式,js处理用户操作与数据请求,png图片提供图标和走势图等可视化素材;整体压缩包仅702KB,结构紧凑且目录清晰。项目内包含登录、自选股、股票详情、个人信息、日志等典型业务页面,并设置全局配置文件与utils工具模块,图表部分用于展示股票价格与走势,可帮助初学者理解小程序生命周期、页面导航、事件绑定、数据请求及图表渲染等常见开发流程。目前已有422人学习下载,适合打算通过实战案例掌握微信小程序开发的读者,源码功能覆盖股票类应用的基础场景,便于在原有基础上继续扩展。
1. 模拟同花顺炒股小程序:图表好画,数据难造
真让你照着一个行情软件的样子去做小程序,第 1 个卡住的往往不是 canvas,而是数据。模拟同花顺炒股小程序这个方向,核心矛盾在于:界面和交互是死的,照着截图能临摹;行情数据是活的,既要有分时跳动的随机性,又要保证 K 线历史形态不穿帮,还得有涨跌停边界。图表组件反而是这几个模块里最容易被参数调明白的。本文给你一套可直接照搬的实现骨架,把模拟数据、页面状态、canvas 绘图、高频刷新这几个环节拆开讲透,适合正在做金融类 Demo、行情展示类小程序,或者想在小程序里练 canvas 绘图的开发者。读完你能独立跑通一个带自选列表、分时图、K 线图、成交量联动的模拟行情小程序,并且知道哪些坑是模拟数据特有的。
2. 先解决数据问题:像行情源一样的模拟数据生成器
2.1 为什么先造数据而不是先画界面
很多人在做这类项目时第一反应是去接真实行情接口,结果一是没有合规的免费数据源,二是拿到的数据字段和小程序的渲染需求对不上,调试时还要考虑网络波动。我一般会先写一个纯本地的模拟行情生成器,把它当成一个“假的行情服务器”来用。这样做的好处是:开发阶段不依赖网络,UI 和逻辑可以并行推进;测试边界条件时可以主动制造涨跌停、停牌、剧烈波动等特殊数据;后续想换真实数据源时,只需要把生成器替换成请求层,页面代码一行不用改。
数据生成的粒度要和渲染需求对齐。行情软件里最常看到的是两种数据:分时图和日 K。分时图需要每个交易分钟一个价格点,一天 240 分钟;日 K 需要每天的开高低收四个价格。两者都还需要成交量配合。先明确渲染粒度,再写生成器,不然画图时会发现数据量和坐标轴对不上。
2.2 用随机游走生成分时行情
分时行情的本质是一个带漂移的随机过程,我在项目里用的是基于昨收价的布朗运动。核心思路是:给定昨收价作为锚点,每一次 tick 都基于上一笔价格加一个随机增量,增量大小控制波动幅度。为了让走势不完全是白噪声,我会引入一个趋势系数,让价格在一段时间内有方向性,这样分时图看起来才像真实的交易曲线。
// utils/mockQuotes.js function generateMinuteData(prevClose, sessions = 240) { const points = []; let price = prevClose; let trend = 0; // 当前趋势方向,-1 ~ 1 for (let i = 0; i < sessions; i++) { // 随机游走:涨跌方向附带趋势惯性 trend = trend * 0.7 + (Math.random() - 0.5) * 0.6; const step = prevClose * 0.0008 * trend; // 单步幅度控制在 0.08% 以内 price = Math.min( Math.max(price + step, prevClose * 0.9), prevClose * 1.1 ); points.push({ price: Number(price.toFixed(2)), vol: Math.floor((0.5 + Math.random()) * 1000), time: formatMinute(i) }); } return points; }核心参数是 trend 的衰减系数和单步幅度。衰减系数设成 0.7,表示趋势有惯性但不会永远朝一个方向走,这样生成的曲线会出现波段。单步幅度 0.0008 是相对于昨收价比率,按 A 股主板 10% 涨跌停限制,240 分钟最多累计 19.2% 的空间,极端情况下会触到涨停,但概率很低,不会出现频繁涨停的情况。如果想让走势更平稳,把这个系数往 0.0005 调;想模拟妖股,就调到 0.0012 以上。
这里有个容易忽略的细节:分钟数据的时间戳应该是字符串而不是 Date 对象。小程序中 Date 对象在 setData 序列化时会被转成字符串,iOS 上个别格式还会被解析成 undefined,我一般直接生成"09:30"这类格式化字符串,渲染层和 canvas 坐标轴直接用,省掉一层格式化逻辑。
2.3 生成日 K 线数据与涨跌停边界
日 K 的生成逻辑和分时不同,分时是逐笔推着走,日 K 则需要满足一个硬约束:当天的收盘价是下一天的开盘价。很多人在这一步直接随机生成每一天的高开低收,结果相邻 K 线之间开盘价和收盘价对不上,画出来像一堆乱码。正确的做法是用前收推动后开。
function generateKlineData(days = 120, basePrice = 12) { const list = []; let prevClose = basePrice; for (let i = 0; i < days; i++) { const open = prevClose + (Math.random() - 0.5) * prevClose * 0.03; const close = open + (Math.random() - 0.48) * prevClose * 0.04; const high = Math.max(open, close) + Math.random() * prevClose * 0.02; const low = Math.min(open, close) - Math.random() * prevClose * 0.02; const volume = Math.floor((0.6 + Math.random()) * 80000); list.push({ date: formatDate(i), open: Number(open.toFixed(2)), close: Number(close.toFixed(2)), high: Number(high.toFixed(2)), low: Number(low.toFixed(2)), volume }); prevClose = close; } return list; }涨跌停边界的处理在 Day 0 阶段就要内置进生成器。上面这段代码用Math.min/max限制了 high 不能超过收盘价 2%、low 不能低于开盘价 2%,但这是相对比例而不是绝对涨跌停。真正要防的是生成连续 3 个涨停这种极端序列:一旦 close 相对 prevClose 超过 10%,就强制回拉。简便做法是生成后做一次清洗,把所有涨跌幅超过 9.9% 的数据点重新生成。另一个边界是 ST 股 5% 的涨跌幅区间,模拟数据里如果同时模拟多只股票,建议把涨跌停幅度做成参数,默认主板 10%,传 0.05 给 ST,传 0.2 给创业板。
数据生成器写完后,建议先做一个自检脚本,连续生成 10 组数据检查两件事:所有价格都是正数;所有 high 都不小于 open 和 close 的最大值、low 都不大于 open 和 close 的最小值。这个检查能避免在 canvas 绘图时出现蜡烛图上下影线倒挂的尴尬。
3. 页面骨架与状态流:自选列表、详情页与轮询刷新
3.1 项目文件结构与页面划分
行情类小程序的最小可运行结构,我一般按四个页面加两个公共模块来组织:首页做自选列表,详情页承载分时图和 K 线图,再加一个搜索页(可以先用静态数据占位)和持仓页(如果需求里需要)。公共模块是 utils/mockQuotes.js 负责生成数据,utils/format.js 负责数字格式化,组件目录放行情卡片组件。如果项目只做技术验证,搜索和持仓可以直接砍掉,保留两页就够。
页面之间的通信依赖 URL 参数传递股票代码。微信小程序的 navigateTo 支持在 url 上带 query,但参数只能拼字符串。股票代码里带着数字和点号,比如600519,直接拼没问题,但如果你模拟港股或者美股,代码里有.HK、.OQ这种带点的后缀,就需要先encodeURIComponent再传,否则 onLoad 里解析会对不上。
自选列表的数据结构应该是扁平数组加索引映射。我在 data 里维护两个字段:stockList是渲染用的数组,stockMap是代码到数据的映射表。行情推送过来时,先更新 stockMap 再通过 setData 更新列表,这样可以在不重建整个数组的情况下实现局部刷新。如果直接遍历列表去匹配代码,数据量大时会有明显卡顿。
3.2 自选列表页的渲染与定时刷新
列表页的核心是一个循环渲染的wx:for,每个列表项展示股票名称、代码、当前价、涨跌幅。模拟行情下,价格需要周期性变化,我用定时器模拟行情推送。定时器不能驱动 setData 做全量更新,否则列表项多时会出现明显掉帧,常见做法是让定时器只更新 data 层的数据,再通过一个刷新标记触发视图更新。
// pages/index/index.js Page({ data: { stockList: [], quotesMap: {}, lastUpdateTime: '' }, onLoad() { this.initQuotes(); }, initQuotes() { const quotesMap = {}; const stockList = buildMockStockList(10); stockList.forEach(item => { quotesMap[item.code] = { ...item, minuteData: generateMinuteData(item.prevClose) }; }); this.setData({ stockList, quotesMap }); this.startRefreshTimer(); }, startRefreshTimer() { // 每 2 秒模拟一次行情 push,只更新最新价格 this.timer = setInterval(() => { const map = this.data.quotesMap; Object.keys(map).forEach(code => { const q = map[code]; const newPrice = generateNextTick(q.prevClose, q.price); q.price = newPrice; q.change = Number(((newPrice - q.prevClose) / q.prevClose * 100).toFixed(2)); }); this.setData({ quotesMap: map }); }, 2000); }, onUnload() { if (this.timer) clearInterval(this.timer); } });刷新间隔是个需要实际调试的参数。2 秒是体验和性能的折中:1 秒刷新价格跳动过于频繁,用户神经一直绷着;5 秒又显得行情太死。定时器更新的是同一个 quotesMap 对象,setData 拿到的引用没有变化,但小程序对对象内部字段的修改是能感知到的,因为 setData 会把整个对象序列化后交给渲染层做 diff。数据量大时要注意控制 stockList 的数量,模拟阶段 10 到 20 只股票足够展示效果,不要做几百只。
这里容易踩的坑是定时器的生命周期管理。onHide 时定时器没有销毁,用户跳转到详情页再回来,列表页的定时器还在跑,而页面上又新增了定时器,导致双重刷新。我习惯在 onShow 和 onHide 里成对地启停定时器,而不是在 onLoad 和 onUnload 里管理。
3.3 详情页的参数传递与涨跌配色
详情页的 canvas 图只做一次性绘制,不需要每两秒重绘整个图。页面加载时通过 options.code 从全局状态里找到对应的行情数据,根据当前时间判断画分时还是日 K,然后一次性 setData 传出绘图数据。详情页和列表页之间的数据传递我建议用全局 store 而不是重新生成数据,因为详情页打开时列表页的定时器还在跑,重新生成的模拟数据和列表页展示的数据会不一致,用户切回列表发现价格变了,会有穿帮感。
涨跌配色是行情类项目最容易做反的细节。A 股软件是红涨绿跌,欧美是绿涨红跌,小程序里没有全局配置项,需要自己做一套颜色常量:
// utils/theme.js export const UP_COLOR = '#e23737'; // 涨 export const DOWN_COLOR = '#2f9b4f'; // 跌 export const FLAT_COLOR = '#8a8a8a'; // 平 export function getPriceColor(change) { if (change > 0) return UP_COLOR; if (change < 0) return DOWN_COLOR; return FLAT_COLOR; }涨跌百分比的计算也要统一:涨跌幅 = (当前价 - 昨收) / 昨收 * 100。很多人顺手用 (当前价 - 开盘价) / 开盘价,这在一天之内大部分时间是错的,因为开盘价不等于昨收价。分时图的基准线永远是昨收价,这和日 K 的开盘价是两回事,画分时图坐标轴时也以昨收价为中心对称轴。
4. 图表是门面:分时图与 K 线图的 canvas 实现
4.1 分时图坐标轴与均价线
分时图在 canvas 里绘制时,先把坐标空间分成三块:顶部是价格主图、底部是成交量、右侧是价格标签。小程序 canvas 的单位是 px,但不同机型的逻辑像素不一致,为了让图表在不同尺寸上不变形,我用的是wx.createSelectorQuery动态测量 canvas 节点的实际宽度,然后在这个宽度上做等比例缩放。
分时图的坐标轴计算有个关键点:纵轴范围由当日最高价和最低价决定,而不是由昨收价的上下 10% 决定。否则股价全天在均价附近小幅波动时,曲线会压在中线上,视觉上非常扁平。坐标轴范围一般是在最高价和最低价基础上各加 3% 的余量,再把昨收价标在图上作为参考线。
function drawMinuteChart(canvas, data, prevClose) { const ctx = canvas.getContext('2d'); const width = canvas.width; const height = canvas.height; const contentHeight = height - 70; // 底部 70px 留给成交量 // 计算主图纵轴范围 let maxPrice = prevClose; let minPrice = prevClose; data.forEach(d => { if (d.price > maxPrice) maxPrice = d.price; if (d.price < minPrice) minPrice = d.price; }); const padding = (maxPrice - minPrice) * 0.03; maxPrice += padding; minPrice -= padding; const priceToY = price => contentHeight - (price - minPrice) / (maxPrice - minPrice) * contentHeight; const xStep = width / data.length; // 画昨收参考线 ctx.strokeStyle = '#d0d0d0'; ctx.setLineDash([3, 3]); ctx.beginPath(); ctx.moveTo(0, priceToY(prevClose)); ctx.lineTo(width, priceToY(prevClose)); ctx.stroke(); ctx.setLineDash([]); // 画分时曲线 ctx.strokeStyle = '#2f6ff0'; ctx.lineWidth = 1.5; ctx.beginPath(); data.forEach((d, i) => { const x = i * xStep; const y = priceToY(d.price); i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y); }); ctx.stroke(); return { maxPrice, minPrice, prevClose }; }setLineDash([3, 3])是虚线接口,在部分安卓机上需要确认基础库版本支持。边界情况是 maxPrice 等于 minPrice,如果某只股票全天一字涨停或者停牌,这个除法会算出 Infinity。我在计算前加了一个判断,如果价差小于 0.01 就让坐标轴采用昨收价上下 1% 的范围,保证渲染不崩。
分时图还有一个常被忽略的细节是均价线。真实的行情软件会画一条黄线表示当天均价,它等于累计成交额除以累计成交量。我的模拟数据里没有成交额字段,所以把vol当作权重,均价就是价格按成交量加权的平均值。画法是在分时图上叠加一条平滑曲线,颜色用黄色系,这样整张图的信息量更接近真实行情。
4.2 K 线蜡烛图绘制与均线叠加
K 线图的核心是蜡烛图,一个蜡烛由实体和上下影线组成。实体是开盘价到收盘价的矩形,影线是最高价到最低价的线段。绘制时有三套坐标需要换算:价格到 Y 轴的映射、数组下标到 X 轴的映射、实体宽度的像素计算。前两个和分时图相同,第三个是 K 线特有的。
实体宽度的算法:可绘制宽度除以可视 K 线根数乘以 0.7。比如屏幕宽度 375px,左右各留 10px 边距,显示 60 根 K 线,那每根 K 线的总宽度是 355/60≈5.9px,实体宽度取 70% 约 4.1px。如果实体宽度小于 2px,画出来的就是一条线,需要在绘图前做一个最低宽度保护。
function drawCandlestick(ctx, data, chartWidth, chartHeight) { const visibleCount = 60; // 一屏显示 60 根 const startIndex = Math.max(0, data.length - visibleCount); const xStep = chartWidth / visibleCount; const barWidth = Math.max(2, xStep * 0.6); // 先算可视范围内的最高最低价 let maxH = 0, minL = Infinity; for (let i = startIndex; i < data.length; i++) { if (data[i].high > maxH) maxH = data[i].high; if (data[i].low < minL) minL = data[i].low; } data.slice(startIndex).forEach((d, i) => { const x = i * xStep; const openY = y(d.open), closeY = y(d.close); const highY = y(d.high), lowY = y(d.low); const isUp = d.close >= d.open; ctx.strokeStyle = isUp ? UP_COLOR : DOWN_COLOR; ctx.fillStyle = isUp ? UP_COLOR : DOWN_COLOR; // 画影线 ctx.beginPath(); ctx.moveTo(x + barWidth / 2, highY); ctx.lineTo(x + barWidth / 2, lowY); ctx.stroke(); // 画实体 const top = Math.min(openY, closeY); const bottom = Math.max(openY, closeY); const bodyHeight = Math.max(1, bottom - top); ctx.fillRect(x, top, barWidth, bodyHeight); }); }这里Math.max(2, xStep * 0.6)是实体宽度下限,当 K 线数量超过 100 根时,xStep 会变得很小,不设下限会导致实体太细、视觉上只剩影线。K 线宽度和间距的关系里,通用习惯是实体宽占 60% 到 70%,剩下的 30% 到 40% 是留白,太挤或太松都影响观感。
均线叠加是 K 线图最实用也最容易出问题的地方。MA5、MA10、MA20 三根线分别取最近 5、10、20 个收盘价平均值,然后绘制成折线。计算均线时要注意数组边界:如果数据不足 20 天,MA20 的前几天没有值,应该在画线时跳过而不是补 0,否则折线会从 0 开始画。另一个容易错的地方是均线数据的对齐:MA5 的第 5 个值对应 K 线数组的第 5 根,下标差为 4,画线时要从第 5 根 K 线的位置开始画第一段。
4.3 成交量与主图联动
成交量柱状图通常画在主图下方,有两种常见实现:先单独画在底部区域,绘制完主图后用ctx.clearRect清掉重叠区域;或者直接用 canvas 的裁剪区域,把成交量绘制限制在底部区域。我用的是裁剪区域的方式,因为 clearRect 在清掉重叠区域时会顺便把背景色也清掉,导致出现一块与背景不一致的白斑。
// 主图和成交量分区的裁剪方案 ctx.save(); ctx.beginPath(); ctx.rect(0, chartHeight, chartWidth, volumeHeight); ctx.clip(); // 在这里绘制成交量柱 data.forEach((d, i) => { const vH = d.volume / maxVolume * volumeHeight; ctx.fillStyle = d.close >= d.open ? UP_COLOR : DOWN_COLOR; ctx.fillRect(i * xStep, chartHeight + volumeHeight - vH, barWidth, vH); }); ctx.restore();成交量的颜色规则和蜡烛颜色联动:当天的涨跌幅为正就画红色,为负就画绿色。有一个常见误用是把成交量柱画成单一颜色,虽然不影响数据表达,但视觉上会少掉“这一天是涨是跌”这个信息维度。成交量柱的宽度要和蜡烛实体保持一致,这样技术指标和 K 线能对齐。
图表交互方面,模拟阶段建议先做两个手势:双指缩放改变 visibleCount、单指拖动平移可视窗口。实现平移时要注意下标边界,不能把结束下标推到数组最后一个元素之后。canvas 的 touch 事件和页面的滚动手势是冲突的,我一般会给 canvas 外层套一个catchtouchmove的 view,阻止事件冒泡到页面。
5. 模拟行情翻车的 5 个高频坑与排查思路
5.1 列表 setData 高频更新导致的掉帧
现象:行情模拟定时器每 2 秒更新一次,同时渲染 20 只股票的价格和涨跌幅时,页面滚动变得非常卡,帧率明显下降,列表项在滚动时会闪白。原因:setData 每次更新的是整个 quotesMap 对象,对象里有分时数组、均线计算值、格式化字符串等体积不小的字段,渲染层要对整棵 diff 树做比对,数据量上去之后自然变卡。解决:把 setData 的负载拆开,定时器只更新price和change这两个字段路径,格式化留在 WXML 的wxs里做;再把定时器间隔从 2 秒调到 3 秒。如果数据量仍然大,就只更新可视区域内列表项的路径:quotesMap[\${code}`].price` 这种带变量下标的路径是支持部分更新的。
提示:setData 的路径更新是模拟行情类小程序性能调优的第一抓手,不要图省事一整个对象丢进去。
5.2 canvas 新旧接口混用引发的白屏
现象:部分安卓真机上图表偶尔白屏,清缓存后恢复正常,但过一段时间再次出现,iOS 和开发者工具都无法复现。原因:代码里混用了两种 canvas 创建方式,canvas-id老接口和type=\"2d\"新接口在同一个页面里各自持有一份 canvas 实例,绘图上下文通过wx.createCanvasContext只能操作老实例,而部分基础库版本对type=\"2d\"的 canvas 节点不支持通过 canvas-id 获取绘图上下文,获取失败时没有报错,绘制的图就始终不渲染。解决:整个项目只使用新接口type=\"2d\",通过canvas.getContext('2d')获取上下文,并在ready回调里用wx.createSelectorQuery().select('#klineCanvas').fields({ node: true, size: true })拿到节点后初始化绘图环境。新节点拿到之前不能调用任何绘图方法,判断条件是 node 是否存在。
5.3 iOS 日期解析异常导致的 K 线错位
现象:iOS 真机上 K 线图 X 轴的日期标签显示成 undefined,安卓正常。原因:模拟数据里的日期字符串是"2025-03-04"这种格式,iOS 的new Date(\"2025-03-04\")返回 Invalid Date,导致 toFixed 和取月份之类的操作全部失效。这不是 canvas 的问题,是 JS 引擎的日期解析差异。解决:生成模拟数据时统一输出\"2025/03/04\"这种斜杠格式,或者直接存年月日数字{ year: 2025, month: 3, day: 4 },渲染时自己拼字符串。我在工具函数里写了一个formatDate,内部把数字格式化为两位数并加斜杠。
5.4 定时器泄漏与从后台恢复时闪烁
现象:小程序切到后台再回来,详情页的分时图会突然闪一下,像是重新绘制了一遍,而且首页价格出现一次跳变。原因:详情页 onLoad 里启动了定时器,但没有在 onHide 里清理,切后台时定时器还在跑,回前台时又重新加载了一次数据,两个流程抢 canvas 导致的闪烁。解决:统一用onShow启动定时器、onHide清理定时器,并把数据结构重置和定时器建立拆开。这里有自己的血泪教训:最初图省事只在 onLoad 里写了一次setInterval,结果页面在详情和列表之间反复切换时,定时器叠加了四五个,最终只能靠重启小程序来恢复。
5.5 涨跌颜色习惯在模拟数据里的处理差异
现象:模拟出来的日 K 连续上涨几天后,所有柱子全红,用户觉得“假”,而且在做颜色拿捏不准的时候,红绿系选错导致整个页面观感不对。原因:模拟数据生成器没有控制连续涨跌天数,自然生成的数据经常出现单边行情,其次颜色定义放在了组件内部而不是全局常量,每个页面各写一套。解决:生成器里加一条规则,连续上涨超过 5 天就强制让趋势系数回调到负区间;颜色常量统一放进 utils/theme.js,四个页面共用。另外把涨跌色的明度调低 10%,模拟数据底色是浅色背景时,纯红纯绿会刺眼,用#e23737和#2f9b4f这种带降饱和度的值会更接近真实行情软件的观感。
6. 最后一公里:把模拟器改成 WebSocket 实时推送,体验才像同花顺
模拟数据生成器做得再好,本质还是本地随机数,一旦按下暂停按钮或切页面再回来,行情会重新生成,用户很容易看出来“这是假的”。要让这个模拟项目有真实产品的质感,最后一步是把本地生成器封装成一套推送协议,用 WebSocket 从服务端实时推数据,本地只做接收和渲染。
6.1 设计一个最少字段的行情推送消息
真实行情推送的字段非常多,但模拟项目里最少只需要六个字段就能支撑现有页面:代码、最新价、昨收、涨跌幅、成交量、时间戳。每条推送消息控制在 200 字符以内,这样可以避免在小程序的 WebSocket 连接上产生大量流量。服务端每 2 秒推送一次全量快照,每 1 秒推送一次增量 tick,客户端用增量 tck 更新中间价。
// utils/pushClient.js function connectQuoteChannel(onMessage) { const ws = wx.connectSocket({ url: 'wss://your-quote-server/ws' }); ws.onOpen(() => console.log('quote channel opened')); ws.onMessage(res => { const msg = JSON.parse(res.data); if (msg.type === 'snapshot') { onMessage(msg.quotes); // 全量快照,覆盖本地数据 } else if (msg.type === 'tick') { onMessage([msg.quote]); // 单条增量,局部更新 } }); ws.onClose(() => { setTimeout(() => connectQuoteChannel(onMessage), 3000); // 断线重连 }); return ws; }6.2 增量推送和列表局部更新的配合
服务端推送 tick 时,客户端直接把quotesMap[code]对应字段更新掉,再 setData 这个局部路径。这个过程和之前本地模拟器完全兼容,只需把initQuotes里的startRefreshTimer换成connectQuoteChannel。之前是模拟器推给页面,现在是服务端推给页面,页面代码不用改。
6.3 WebSocket 的边界参数和降级方案
断线重连间隔设 3 秒比较稳妥,太短容易出现连续失败;小程序对 WebSocket 没有心跳要求,但服务端如果设置了连接超时,客户端要在onOpen里启动一个每 15 秒的心跳定时器。降级方案是保留本地生成器,WebSocket 连接失败时自动切回定时器模式,这样演示时断网也不会白屏。这就是我在这类项目里养成的习惯:任何推送通道都要留一条本地降级的路,不然演示现场没有网就会变成大型翻车现场。希望这些拆解能让你少走几步弯路,按这套路径做出来的模拟行情小程序,基本摸得着真实产品的门槛。
本文还有配套的精品资源,点击获取