
简介这是一份围绕 frighten9k3 出品的 kline.js 而整理的实战指南与配套资源包面向需要快速实现金融 K 线图可视化的前端开发者无论初学者还是有一定经验的开发人员都能从中受益。压缩包内的文档与示例紧密结合详细讲解了库的安装引入、图表初始化、K 线数据格式与加载、常用配置项、交互事件监听、动态更新以及自定义技术指标等核心用法并配有可运行的 HTML 页面、mock 数据和效果截图能帮助读者从零搭建专业级 K 线图表也可在项目开发中作为速查参考。整包共 26 个文件以 kline.js 主库文件、CSS 样式、示例页面和 JSON 数据为主另含 README 说明与多张界面截图整体压缩后约 3.62MB目录结构清晰便于按需取用。目前已有 223 人学习或下载体现出这类实现方案的实际参考价值。通过文档与示例配合学习读者可快速掌握 K 线图渲染、更新与交互配置的要点进而直接复用或改造到自己的行情可视化项目中节省大量调研与排错时间。1. 先弄明白frighten9k3 的 kline.js 到底解决什么问题做金融类前端页面kline.js 是最绕不开的坎之一。frighten9k3 的这份 kline.js 压缩包解压出来是完整的 kline-master 工程——它不是一个只画几根蜡烛的 demo而是把 K 线绘制、缩放平移、成交量副图、十字光标、增量更新全部封装好的 JS 图表库。examples 目录里直接给了 requirejs、polling、websocket 三个可运行示例lib 下备齐了 jquery、jquery.mousewheel、sockjs、stomp 这些依赖明摆着是奔着真实行情页去的。适合谁用一类是正在做股票、期货、数字货币行情页的前端开发者另一类是接手了老项目、不想从零手写 canvas 绘制逻辑的人。包里的 mock.json 备好了现成的假数据README 挂在根目录解压之后照着 examples 跑就能看到图学习成本比对着 ECharts 文档拼 candlestick 配置低得多。这篇笔记按我实际拆包的顺序来写先分辨文件结构再讲数据格式和初始化然后是配置、事件与动态更新最后把实时推送和踩坑记录一起摊开。你拿到压缩包后照着一路操作比干读 README 快得多遇到异常也能在对应章节直接找排查路径。2. 安装与引入解压 kline-master 之后三种 js 引入方式怎么选2.1 文件清单先分类核心、依赖、示例各是什么角色解压 js-KLine.rar 之后看到的是 kline-master 目录顶层文件有 package.json、README.md、kline.js、kline.css、mock.json还有 examples、lib、img 三个子目录。我第一眼觉得有点乱所以先把每个文件按运行时必需、示例需要、纯参考分了个类后面引入时就不抓瞎。类别文件角色说明图表核心kline.js、kline.css绘制逻辑与图表样式必须引入基础依赖lib/jquery.jsDOM 操作与事件绑定基础kline.js 内部会调用缩放辅助lib/jquery.mousewheel.js鼠标滚轮缩放支持依赖 jQueryAMD 加载器lib/require.js仅供 requirejs.html 示例使用实时通信lib/sockjs.js、lib/stomp.jsWebSocket 降级兼容 STOMP 消息协议可运行示例examples/requirejs.html、polling.html、websocket.html三种引入与更新方案示例数据mock.json模拟K线数据结构接近真实接口返回package.json 的存在说明这库理论上可以走 npm 安装.npmignore 则划定了哪些文件不进 npm 包。但我的实际建议是离线环境下直接用 rar 里的本地文件最稳因为 npm 上的包版本可能和这份压缩包里的源码存在差异示例里的相对路径也会对不上。先跑通本地文件再考虑接构建工具这是风险最低的顺序。img/ 和两张 screenshot 截图属于参考素材运行时用不到不用管它们。2.2 最省事的 script 引入先把最小实例跑起来最简单的验证方式是把 kline.js、kline.css 和两个 jQuery 依赖直接引到 HTML 里。注意引入顺序jquery.js 要在 jquery.mousewheel.js 之前两者又要在 kline.js 之前。这个顺序不能乱因为 kline.js 初始化时会直接调用 jQuery 的滚轮插件来绑定缩放事件。!DOCTYPE html html langzh-CN head meta charsetUTF-8 titlekline.js 最小示例/title link relstylesheet hrefkline.css style #chart { width: 900px; height: 500px; } /style /head body div idchart/div script srclib/jquery.js/script script srclib/jquery.mousewheel.js/script script srckline.js/script script // 初始化代码在下一章讲先确认三个脚本加载顺序 /script /body /html这里有一个很容易翻车的点容器必须同时有明确的宽和高。kline.js 内部拿 container.clientWidth 和 clientHeight 计算绘图区高度没设的话画布高度就是 0你只会看到一根横线还以为是库坏了。kline.css 也不能漏它是给图表内部元素用的漏引之后副图、十字光标、网格线的样式会全部错乱。我的习惯是写任何初始化代码之前先把 CSS 引好并且在浏览器里按 F12 确认样式文件确实返回了 200而不是 404。2.3 require.js 的 AMD 路径官方示例为什么这么组织examples/requirejs.html 走的是 AMD 模块加载核心逻辑是 require.config 里的 paths 和 shim 配置。为什么要 shim因为 kline.js 和 jquery.mousewheel.js 都不是严格意义的 AMD 模块它们没有用 define 包裹只是往全局挂对象。require.js 不认这种文件所以必须手动声明依赖关系它才会按正确顺序加载。require.config({ paths: { jquery: ../lib/jquery, jqueryMousewheel: ../lib/jquery.mousewheel, kline: ../kline }, shim: { jqueryMousewheel: { deps: [jquery] }, kline: { deps: [jquery, jqueryMousewheel] } } }); require([kline], function (KLine) { var chart new KLine(document.getElementById(chart), { width: 900, height: 500 }); // chart.setData(bars) 在数据加载之后再调用 });paths 里的地址是相对 require.js 所在页面解析的。示例 HTML 在 examples/ 目录下所以 lib 要写成 ../libkline 要写成 ../kline。如果你把页面移到别的位置这三个路径必须同步改否则 require.js 加载阶段就会报 404。shim 的 deps 数组声明了加载顺序kline 依赖 jquery 和 jqueryMousewheel而 jqueryMousewheel 又依赖 jqueryrequire.js 会按这个依赖图排序。这种写法适合要接 webpack、requirejs 这类构建体系的团队把 kline.js 纳入模块依赖管理而不是靠一堆 script 标签手工排顺序——手工排顺序在页面多了之后很容易漏。2.4 用 npm 还是拷 vendorpackage.json 与 CommonJS 导入package.json 里的 main 字段一般会指向 kline.js意思是在 Node.js 或打包器环境下可以直接 require 导入。但在我实际的项目里更常做的反而是把 rar 里的 kline.js、kline.css 直接拷进项目的 vendor/ 目录。原因有两个一是本地文件经过验证行为确定二是 npm 拉下来的版本可能和这份 rar 里的源码对不上一旦出现文档和实际 API 不一致排错成本很高。如果你决定走 CommonJS常见写法是这样var KLine require(./vendor/kline.js);然后用打包器的 css 处理把 kline.css 一起引进去。需要提醒的是kline.js 内部依赖 jQuery用打包器时要把 jquery 显式声明成依赖否则运行时会报找不到 $。这个错误是最常见的报错之一我会在第五章的避坑里展开。另外不管哪种引入方式凡是运行时报错指向 kline.js 内部某一行优先怀疑依赖没引全或顺序不对而不是怀疑库本身有 bug。3. 数据格式与初始化从 mock.json 到屏幕上的第一根K线3.1 数据格式拆解时间戳、开盘、最高、最低、收盘、成交量的列顺序K 线图的核心是四价开盘价open、最高价high、最低价low、收盘价close加上时间戳和成交量就是完整的一根K线数据。kline.js 的数据格式是数组套数组每一行是一根K线而列顺序是首先要确认的东西。我解压后直接打开 mock.json 看结构类似下面这样// mock.json 解析后的数组我给每列加了注释 [ [1546300800000, 3467.5, 3490.2, 3455.8, 3481.3, 126530], // ↑时间戳(毫秒) ↑开 ↑高 ↑低 ↑收 ↑成交量 [1546387200000, 3481.3, 3512.6, 3476.9, 3505.4, 158372], [1546473600000, 3505.4, 3489.7, 3460.2, 3472.8, 142119] ]列顺序是时间戳、开盘、最高、最低、收盘、成交量。这个顺序真的要看清楚因为不同K线库的习惯不一样有的用开、收、低、高有的用开、高、低、收还有的直接用对象 [{time, open, high, low, close, volume}]。我第一次接一个后端接口时对方给的是开、收、低、高的列我没核对就往 kline.js 里灌结果所有K线实体方向反了——明明涨的显示成绿明明跌的显示成红查了一下午才发现是列顺序问题图表本身一点毛病没有。时间戳单位也要确认。mock.json 里用的毫秒如果你的后端给的是秒级时间戳要在灌进图表之前乘 1000否则时间轴会显示成 1970 年附近的时间。我一般在接入层写一个 adapter 函数把后端字段名、单位、列顺序统一成 kline.js 能认的格式后续接任何数据源都走这个入口不直接往图表里塞原始数据。function adaptBars(raw) { return raw.map(function (row) { // 兼容对象形式和时间戳秒级/毫秒级两种输入 var ts Array.isArray(row) ? row[0] : row.time; if (typeof ts number ts 1e12) { ts * 1000; // 秒转毫秒 } if (Array.isArray(row)) { return [ts, row[1], row[2], row[3], row[4], row[5] || 0]; } return [ts, row.open, row.high, row.low, row.close, row.volume || 0]; }); }这里的时间戳判断逻辑是通用的毫秒时间戳是 13 位秒级是 10 位小于 1e12 就按秒处理。虽然不算严谨但对付绝大多数后端足够了。数据是倒序的话建议先按时间戳升序排序再传避免K线顺序错乱导致连线画成麻花。排序的细节我在 3.2 里展开。3.2 初始化流程KLine 构造参数与 setData 的时序初始化分两步先 new KLine 创建图表实例再调 setData 灌入数据。先后顺序是固定的因为 setData 内部要触发布局计算、坐标映射和首次绘制图表对象必须先存在。var container document.getElementById(chart); var kline new KLine(container, { width: 900, height: 500, show: { volume: true // 显示成交量副图 } }); var bars adaptBars(mockData); // 先过 adapter 标准化 bars.sort(function (a, b) { return a[0] - b[0]; // 按时间戳升序防止倒序数据 }); kline.setData(bars); // 全量灌入并重绘构造参数里width 和 height 控制绘图区初始尺寸不传的话有些版本会尝试自适应容器。show.volume 控制是否渲染下方成交量副图行情页一般默认开。setData 是清空重绘的全量方法首次加载和重大数据变动都用它。关于排序我的经验是不要依赖库帮你排因为有的版本不排序直接连线有的版本会悄悄排但你不知道它的排序稳定规则。自己先 sort 一遍最稳妥。初始化完成后如果窗口尺寸变化图表不会自动跟着缩放。常见做法是监听 resize 事件调用图表实例的 resize 方法如果你解压的版本没有暴露 resize 方法那就销毁实例重建。重建成本不高因为 setData 本来就会全量重算只要数据还在内存里重建也就是几行代码的事window.addEventListener(resize, function () { if (kline typeof kline.resize function) { kline.resize(container.clientWidth, container.clientHeight); } });3.3 异步加载与格式兼容接口返回不一定叫 bars真实项目里数据几乎都是异步来的。mock.json 可以直接 fetch但后端接口的返回结构五花八门有的包一层 data有的叫 klines有的干脆直接给数组。我习惯在加载函数里做一层兼容判断function loadBars(url) { return fetch(url) .then(function (res) { return res.json(); }) .then(function (json) { // 兼容裸数组、{bars: ...}、{data: ...}、{klines: ...} 四种结构 if (Array.isArray(json)) { return json; } var bars json.bars || json.data || json.klines; if (!Array.isArray(bars)) { throw new Error(无法识别的K线数据格式: JSON.stringify(json).slice(0, 200)); } return bars; }); } loadBars(mock.json) .then(function (raw) { kline.setData(adaptBars(raw)); // adapter setData }) .catch(function (err) { console.error(加载K线数据失败:, err); });这个兼容层看着简单在多项目复用时能省很多事。后端改字段名是家常便饭你把格式判断集中在一个函数里改一处就行不用在图表实例化之后到处救火。如果接口返回的是 CSV 文本逻辑也类似先按换行拆行第一行是表头就跳过再按逗号拆分数字字段用 parseFloat 转换时间戳字符串记得 parseInt。这些细节都是我踩过的坑提前写出来帮你少走弯路。4. 配置、事件与动态更新把静态K线图变成可交互行情页4.1 外观配置涨跌颜色与时间轴格式的参数表kline.js 的配置集中在 setConfig 里最常见的需求是配色。国内行情习惯红涨绿跌欧美习惯绿涨红跌color 配置就是为这种差异准备的。我实际用到的主要参数如下配置项取值示例作用color.up#e44a4a阳线颜色收盘 开盘color.down#16a864阴线颜色收盘 开盘color.border#888888边框与网格线颜色color.cross#cccccc十字光标颜色time.formatYYYY-MM-DD HH:mm时间轴刻度显示格式show.volumetrue / false是否显示成交量副图kline.setConfig({ color: { up: #e44a4a, // 阳线偏暖色 down: #16a864, // 阴线偏冷色 border: #888888, cross: #cccccc }, time: { format: YYYY-MM-DD HH:mm } });实际项目里需要调的远不止这几个但这四个是我每接一个需求都会确认一遍的边框颜色决定整体观感十字光标颜色在深色主题下尤其重要。包里附带的 screenshot_dark.png 和 screenshot_light.png 两张截图就是给你对比深浅主题效果的我一般先看截图定深浅基调再微调具体色值。要注意的是setConfig 可以在运行期随时调用改完会触发重绘所以配色切换按钮的实现成本很低深色浅色主题各存一份配置对象切换时重新 setConfig 就行。4.2 事件回调click 和十字光标到底返回什么交互是行情页的刚需kline.js 的事件用 on 方法绑定。我开发中最高频用的是 click 和 crossMove 两个kline.on(click, function (data) { // data 是当前点击K线的完整信息对象 console.log(clicked bar:, data); // 常见字段data.time时间戳、data.open、data.high、 // data.low、data.close、data.volume以及 data.index updateTradePanel(data); // 联动右侧的成交明细面板 }); kline.on(crossMove, function (data) { // 十字光标移动时触发适合做跟随显示的价格标签 movePriceLabel(data.close); });这里的回调对象是库内部解析好的直接取字段用就行不用按数组下标去拿。有个细节需要注意crossMove 在鼠标移出绘图区后不一定会触发离开事件如果你做了浮动价格标签记得在容器的 mouseleave 事件里手动隐藏否则标签会一直残留在图表边缘。数据对象里通常还有个 index 字段表示这是第几根K线配合你的原始数据数组做详情弹窗很方便不需要再遍历查找。4.3 增量更新addData 的动画与高频推送取舍实时行情下数据是逐根更新的。kline.js 提供 addData 处理增量第二个参数控制是否播放过渡动画。我用一个模拟推送来演示完整过程// 模拟实时行情每 2 秒推入一根新K线 setInterval(function () { var bars currentBars; // 你自己维护在页面里的K线数组 var last bars[bars.length - 1]; var next [ last[0] 60000, // 时间戳加 1 分钟 last[4], // 新开盘价 前一根收盘价 last[4] * 1.01, // 最高价 last[4] * 0.99, // 最低价 last[4] * (1 (Math.random() - 0.5) * 0.02), // 收盘价随机波动 Math.floor(Math.random() * 10000) 500 // 随机成交量 ]; currentBars.push(next); // 记得同步维护自己的数据源 kline.addData(next, true); // true 播放过渡动画 }, 2000);关于动画的取舍我的经验是低频推送分钟级开动画体验确实好最后一根K线平滑刷新但 tick 级高频推送开动画会造成渲染堆积页面越来越卡。高频场景把第二个参数改成 false只做静默重绘。另外 addData 只负责追加如果你要改最后一根K线比如当前这根还没收盘、价格一直在变常见做法是先把最后一根从数组里弹出来改完再 addData 回去或者直接 setData 全量重灌。具体支持哪种README 里一般会写明你也可以临时在控制台打印实例方法列表确认。4.4 自定义指标通过 kline.indicators 注册均线除了库内置指标kline.js 暴露了 indicators 接口给你挂自定义指标函数。接口接收全量K线数组返回等长的计算结果数组图表会把结果叠加到主图或副图上。以 5 日均线MA5为例// 注册 MA5 指标 kline.indicators.register(MA5, function (bars) { var result []; for (var i 0; i bars.length; i) { var count Math.min(5, i 1); // 前 4 根不足 5 根取可用数量 var sum 0; for (var j i - count 1; j i; j) { sum bars[j][4]; // 第 4 列是收盘价 } result.push({ value: sum / count }); } return result; });这里 bars 参数和 setData 传入的是同一份数据列下标规则一致第 4 列是收盘价。返回值用 { value: xxx } 的对象结构图表取 value 字段画线。前 4 根K线不足 5 日均值我取可用数量做平均这是均线算法的基本处理不处理的话前几根线会断。注册之后怎么让它显示不同版本方式不同有的在配置项里选指标列表有的在 setConfig 里指定调试时先注册再调一次 setData 触发全量重算如果图上没出现预期线条优先检查注册是否在 setData 之前完成。顺着这个模式MACD、RSI、布林带都可以往里挂只要返回数组长度和 bars 对齐即可。5. 避坑指南kline.js 实践中的五条血泪经验5.1 容器高度塌陷K线挤成一团现象图表渲染出来了但所有K线都堆在顶部一条线上竖向完全没有展开像被压扁一样。原因容器只给了宽度没给高度或者 kline.css 没引入。kline.js 初始化时读取 container.clientHeight拿到 0 以后整个绘图区高度就是 0K线自然叠成一条线。漏引 CSS 的情况表现类似但还会伴随网格线、十字光标样式错乱比较容易区分。解决给容器在 CSS 里写死高度比如 500px用 flex 布局时注意父容器不要让子元素被压缩。排查时第一步打开 DevTools 看容器的 computed style第二步确认 kline.css 在 kline.js 之前加载并且 network 面板里样式文件返回的是 200。我把这条放在第一位因为它是新手期出现频率最高的假故障实际跟库本身一点关系都没有。5.2 require 加载后 KLine 是 undefined现象按 requirejs.html 的方式配置执行到 new KLine 时报 KLine is not defined。原因shim 配置缺失或依赖顺序不对。kline.js 没有用 define 包裹require.js 只能按 shim 声明的顺序把它当普通脚本加载。deps 里没写 jquery 和 jqueryMousewheelkline.js 可能在 jQuery 加载之前就执行了内部初始化直接失败导出对象自然不存在。解决检查 require.config 的 shim 段确认 kline 的 deps 同时包含 jquery 和 jqueryMousewheel且 jqueryMousewheel 的 deps 指向 jquery。还有一个隐蔽问题paths 里的地址是相对 require.js 所在页面解析的如果你调整过目录存放位置路径要跟着改。排查这种问题最快的方式是在 DevTools 的 Network 里看加载顺序如果 jquery 排在 kline.js 后面那肯定是 shim 没写对。5.3 第一根K线阴阳颠倒现象数据从接口取回来看似正常但历史上明显的阳线全渲染成阴线涨跌颜色彻底反了。原因数据列顺序理解错了。有的数据源给的是开、收、低、高有的给的是开、高、低、收kline.js 默认按开、高、低、收、量解析。你把开、收、低、高直接灌进去库会把你的收盘价当成最低价、最低价当成收盘价实体方向和颜色自然全部错乱。这在 3.1 里预警过但实际项目中依然是最容易被忽略的一步。解决收到任何新数据源先打印第一行数据拿它和行情软件上的同一根K线对照确认四价对应关系后再接入。我现在的习惯是所有外部数据进入 kline.js 之前统一过一道 adapter 函数把列顺序、时间戳单位、字段别名标准化洗完再 setData。这个函数是血的教训换来的一次写对之后换任何数据源都不用在图表层改代码。5.4 polling 增量更新越来越卡现象用 setInterval 每隔一两秒调一次 addData跑了十几分钟后页面明显变迟钝滚动、缩放全部掉帧。原因addData 每次触发全量重算和重绘K线数量积累到几百上千根之后坐标换算和 canvas 绘制都压在同一个渲染循环里主线程被占满。动画参数还开着 true 的话每帧还要做插值计算雪上加霜。解决高频推送把动画关掉对数据总量设上限只保留最近 500 或 1000 根超出就裁掉旧数据。裁剪时我通常这样做// 本地维护 bars超过上限时裁剪后全量重灌 if (bars.length 600) { bars bars.slice(bars.length - 500); kline.setData(bars); // 裁剪后用 setData而不是继续 addData } else { kline.addData(nextBar, false); // 正常增量关动画 }裁剪后一次性 setData 比连续 addData 效率高因为避免了多次重复计算已经存在的坐标变换。如果数据量大到即使裁剪还是卡就要考虑开 canvas 的离屏渲染或者降低重绘频率把每秒重绘次数限制到 10 次左右肉眼基本分辨不出差别CPU 占用能降一大截。5.5 websocket 连接反复失败现象websocket.html 跑起来之后控制台持续报 STOMP 连接错误或者连接显示成功但一直收不到任何行情消息。原因sockjs.js 和 stomp.js 是一对组合。sockjs 负责建立 WebSocket 连接并做降级不支持 WebSocket 时退到长轮询stomp 负责在连接之上跑 STOMP 消息协议。如果后端没有提供 STOMP 端点ActiveMQ、RabbitMQ 的 STOMP 插件没开或者端口配置不一致sockjs 握手阶段就会失败。另一个高频原因是订阅的 topic 路径和后端广播地址不一致stomp 订阅 /topic/kline后端往 /queue/kline 发前端永远收不到。解决先用 SockJS 的 onclose 事件确认连接层状态再进 STOMP 的 CONNECT 帧确认握手成功最后核对订阅 topic 字符串与后端完全一致。如果本地没有现成的 STOMP broker我建议先切到 polling.html 验证图表渲染把数据链路的问题和图表自身的问题分开排查别一上来就调 websocket否则你很难分清到底是哪一层的锅。WebSocket 断线重连也很容易漏写我放到最后一章专门讲验证方法。6. 实时推送的两种姿势polling 和 websocket 怎么选、怎么验6.1 polling 与 websocket 的边界在哪里examples 里的 polling.html 和 websocket.html正好代表两种实时方案。polling 是定时拉取实现简单、无状态、任何后端都能做缺点是延迟受轮询间隔限制、请求冗余多websocket 是长连接推送延迟低、事件驱动但需要后端配合维护连接状态。两者的取舍关系可以浓缩成一张表维度pollingwebsocketsockjs stomp实现难度低setInterval fetch 即可高前后端都要支持 STOMP实时性受轮询间隔限制事件驱动毫秒级服务端压力请求量大空轮询多长连接占资源但无冗余断线恢复下一次轮询自动恢复需要自己写重连和补齐逻辑我的建议是行情频率在分钟级、用户量大的场景polling 配合合理轮询间隔完全够用真正做 tick 级行情、对延迟敏感的才值得上 websocket。websocket 示例的核心代码是 sockjs 建连加 stomp 订阅var socket new SockJS(http://localhost:8080/stomp); var stompClient Stomp.over(socket); stompClient.connect({}, function () { stompClient.subscribe(/topic/kline, function (message) { var bar JSON.parse(message.body); // 后端推来的单根K线 kline.addData(bar, false); // 推送频繁关闭动画 }); });SockJS 的 URL 指向 broker 的 STOMP 端点地址不是随便一个 WebSocket 地址topic 路径必须和后端广播配置完全一致。这段代码至少告诉我两件事一是 K 线增量推送是单根数据低延迟下发二是订阅路径是前后端协同的契约改哪边都要同步通知对方。6.2 一套验证链路渲染、增量、断线重连全部过一遍资源拿到手我建议按三步验收每一步独立任何一步失败都能快速定位问题。第一步用 mock.json 加 setData 验证基础渲染通过说明引入方式和数据格式没问题。第二步用 polling 的思路模拟每分钟推一根新K线观察 addData 是否平滑、时间轴是否正常滚动通过说明增量更新链路正常。第三步接真实 websocket断开 broker 再重启确认前端能自动重连并补齐断线期间的K线。第三步最容易漏也最影响线上稳定性。有一次我就是把重连逻辑写漏了后端夜里重启一次前端图表就永远停在断开的那一刻第二天开盘所有增量数据都接不上最后只能靠手动刷新页面救急。从那以后我每次接实时行情都会强制走一遍广播端断开 → 前端重连 → 补齐离线K线的验证流程重连成功先拉一次历史接口补齐缺口再恢复增量订阅宁可多拉几根重复的也不能漏。这套流程同样适用于验证这份资源里的 websocket.html——先按原样跑通再改造成你自己的行情页。希望帮到你。本文还有配套的精品资源点击获取