简介:这是一份面向微信小程序学习者的完整源码资源,以模拟同花顺炒股应用为项目案例,重点覆盖股票行情展示、自选股、登录分享等典型功能模块。压缩包内共46个文件,包含12个JSON配置、11个JS逻辑脚本、9个WXSS样式表、8个WXML页面模板及5个PNG图表资源,整体仅702KB,结构清晰,适合快速下载阅读。JSON负责应用与页面配置,JS承载业务逻辑,WXSS和WXML分别控制样式和结构,PNG则用于界面图表与图标展示。资源提供了自选股、登录、详情、个人中心、日志等主要页面模块的完整实现代码,可帮助开发者直观理解多页面导航、用户登录流程、自选股管理以及图表渲染的实现方式。目前已有422人学习使用,尤其适合想要通过真实项目复刻来巩固小程序开发技能的中初级开发者。
1. 模拟同花顺炒股小程序:一套源码加图表,把行情链路完整跑通
做小程序最怕的不是写不出页面,而是页面画完了数据却动不起来。这个模拟同花顺炒股小程序,正好是一套能让你看到行情、自选、K线图和模拟交易完整跑通的案例,源码和图表全都打包好,适合正在学微信小程序、或者想把 H5 行情页迁到小程序的人直接照着改。它不接真实行情,所有涨跌、K线、委托结果都是本地 mock 出来的,也就是说不需要服务器、不需要金融资质,clone 下来就能跑,也更容易看懂一条数据从加载、渲染到交互的完整链路。我复现时最关心的是它的图表适配和自选股数据流,这两个点恰恰是很多新项目里最容易翻车的地方。
2. 把项目骨架先跑通:目录结构、TabBar 与 request 封装
拿到一份小程序源码,我习惯先不看页面长什么样,而是先看目录和请求层。页面长得再像同花顺,如果 request 封装得不行,后面接真实接口时一定返工。这一章就把骨架拆开,讲清楚每个目录干啥、请求怎么封装、mock 数据怎么接。
2.1 目录结构:数据、页面、组件各管一摊
一份能长期维护的 demo,目录一定不是所有页面平铺在pages里。这个项目拆了三层,我用 tree 命令看到的大致结构如下:
project-root/ ├── app.js # 全局启动逻辑,读配置、初始化全局变量 ├── app.json # 页面注册与 tabBar 配置 ├── app.wxss # 全局样式,主要是行情颜色变量 ├── pages/ │ ├── index/ # 行情首页:指数、自选列表 │ ├── quote/ # 个股详情:分时/K线/五档 │ └── trade/ # 模拟交易:买入、卖出、持仓 ├── components/ │ └── charts/ # 图表组件,封装了 ucharts 的初始化 ├── utils/ │ ├── request.js # wx.request 的 Promise 封装 │ └── format.js # 数字格式化、涨跌颜色计算 ├── mock/ │ ├── quotes.js # 行情数据 mock │ └── kline.js # K线数据 mock └── images/ └── tabbar/ # tab 图标pages/index管行情总览,pages/quote管详情图表,pages/trade管模拟交易,三个页面刚好覆盖了炒股类小程序的核心闭环。components/charts把图表初始化收敛成组件,而不是让每个页面自己写 canvas,这是后面改图表样式时最省事的设计。mock目录独立出来之后,真实接口接入时只需替换utils/request.js里的 baseURL 和数据解析函数。
app.json里 tabBar 的配置也值得说一下,三个页面都注册成 tab,切页面不用走 navigateTo,体验更接近 App。这里要注意 tabBar 的list最多 5 项,icon 路径不能放在网络地址上,必须本地文件,否则真机预览会直接黑图标。
2.2 request 封装:mock 与真实接口无缝切换
这个项目的请求封装并不复杂,但做了一个很关键的设计:把 mock 数据开关放在 request 层,而不是散落在每个页面里。代码如下:
// utils/request.js const BASE_URL = 'https://your-api.example.com'; // 换真实接口时只改这里 const USE_MOCK = true; // 全局 mock 开关 function request(options) { return new Promise((resolve, reject) => { if (USE_MOCK) { // mock 模式:从本地加载数据,模拟网络延迟 setTimeout(() => { resolve({ code: 0, data: options.mockData }); }, 200); return; } wx.request({ url: BASE_URL + options.url, method: options.method || 'GET', data: options.data || {}, timeout: 10000, header: { 'Content-Type': 'application/json' }, success(res) { if (res.data.code === 0) { resolve(res.data.data); } else { wx.showToast({ title: res.data.msg || '请求失败', icon: 'none' }); reject(res.data); } }, fail(err) { wx.showToast({ title: '网络异常', icon: 'none' }); reject(err); } }); }); } module.exports = { request, BASE_URL };代码逻辑很简单:USE_MOCK为 true 时直接返回mockData,为 false 时走真实wx.request,成功回调里统一判断业务码code。这样一个项目在演示阶段能完全脱离后端跑通,接入真实行情源时只需把USE_MOCK改成 false,再给每个请求传入对应的url与data。
参数说明里有几个点值得注意。timeout: 10000是给弱网环境留的余量,行情类接口如果设 5 秒,用户在电梯里会频繁看到加载失败。wx.showToast放在 request 层统一弹错,页面里就不需要每个接口都写一遍错误提示,这是减少重复代码的关键。mock 延迟 200ms也不是为了好看,而是让页面骨架屏和 loading 状态有机会被看到,否则瞬间出数据,你会误以为所有 UI 状态都写对了。
2.3 mock 数据与全局启动:先让页面有东西可看
用 mock 数据跑通项目时,数据结构必须贴近真实接口,不然后面切真实数据时同样要改页面。这个项目的行情 mock 大致长这样:
{ "code": 0, "data": { "indexList": [ { "name": "样例指数", "value": 3521.35, "change": 12.48, "changePercent": 0.36 }, { "name": "样例成长", "value": 2140.82, "change": -8.63, "changePercent": -0.40 } ], "stockList": [ { "symbol": "600001", "name": "样例股份", "price": 12.35, "prevClose": 12.10, "changePercent": 2.07 }, { "symbol": "600002", "name": "样例科技", "price": 8.42, "prevClose": 8.60, "changePercent": -2.09 } ] } }字段设计是照着真实行情接口的习惯来的:prevClose表示昨收,changePercent直接给到小数点后两位,页面里不再做除法。注意changePercent是服务端算好下发,还是客户端用(price - prevClose) / prevClose现算,两种方式我都见过。这个 demo 选择直接下发,好处是列表页做排序和颜色判断时少一步计算,坏处是你必须时刻注意数据里的prevClose和price是否一致,否则会出现价格没变但涨幅很大的诡异现象。
启动流程也值得一提。app.js里只做两件事:读 mock 配置到全局变量globalData,然后调用一次wx.setStorageSync('lastOpenTime', Date.now())。后面自选股页面会用到这个时间戳判断是不是当天首次打开、要不要刷新数据。整个启动逻辑非常轻,没有把行情数据塞进 globalData,因为行情数据是高频变动的,放全局变量反而容易造成页面间数据不同步。
3. 行情与自选股页面:红涨绿跌、下拉刷新与本地存储
骨架跑通后,接下来是行情首页。这一章看起来只是列表页,但实际包含了颜色计算、下拉刷新、定时轮询、本地存储跨页同步四个点。每个点单独看都不难,凑在一起就很容易出问题,尤其是刷新频率和存储时机的配合。
3.1 红涨绿跌计算与单元格布局
行情列表最重要的不是 UI,而是涨跌颜色不能算错。这个项目用了一个简单的函数来控制颜色:
// utils/format.js function getChangeColor(changePercent) { // changePercent 为正数返回红,负数返回绿,0 返回灰 const value = parseFloat(changePercent); if (value > 0) return '#e54038'; if (value < 0) return '#1bb289'; return '#8a8a8a'; } function formatPrice(price) { return Number(price).toFixed(2); } module.exports = { getChangeColor, formatPrice };toFixed(2)看起来简单,但有个隐藏问题:(0.015).toFixed(2)的结果在某些基础库版本里会是0.01,而不是0.02。所以formatPrice内部先把price做了Number()强转,避免字符串型数字直接调用toFixed报错。颜色函数里我特意写了parseFloat,原因是后端可能会下发"2.07"这样的字符串,直接if (changePercent > 0)在字符串数字下也能比较,但如果字段是null就会一路踩进灰色分支,用户看到的价格没颜色,排查时又不容易察觉。
页面 WXML 里的做法是用内联 style 控制颜色,而不是写一堆条件 class:
<view class="stock-row" wx:for="{{stockList}}" wx:key="symbol"> <view class="stock-info"> <text class="stock-name">{{item.name}}</text> <text class="stock-symbol">{{item.symbol}}</text> </view> <view class="stock-price" style="color: {{item.color}}"> <text>{{item.price}}</text> <text>{{item.changePercent}}%</text> </view> </view>这里有一个新手常踩的坑:wx:key如果只写index,当列表做增量更新时,setData 会频繁 re-render 整行。用symbol做 key,小程序 diff 时才能精确定位哪一行变了。颜色字段是在 setData 之前就计算好放进 item 里的,而不是在 WXML 里调用函数,目的就是减少模板层的计算量,毕竟列表一长,每个数据项都重复执行颜色函数是很浪费的。
3.2 下拉刷新与定时轮询:让自选股“活”起来
静态列表没意义,行情页必须能刷新。这个项目同时做了下拉刷新和定时轮询,两个动作共用同一个数据加载函数:
// pages/index/index.js Page({ data: { stockList: [] }, onPullDownRefresh() { this.fetchQuotes().finally(() => { wx.stopPullDownRefresh(); }); }, onShow() { this.startPolling(); }, onHide() { this.stopPolling(); }, fetchQuotes() { const mockData = require('../../mock/quotes.js').stockList; return getApp().globalData.useMock ? Promise.resolve(mockData) : request({ url: '/api/quotes' }).then(res => res.stockList); }, startPolling() { this.pollingTimer = setInterval(() => { this.fetchQuotes().then(list => { this.setData({ stockList: this.decorateList(list) }); }); }, 5000); }, stopPolling() { if (this.pollingTimer) { clearInterval(this.pollingTimer); this.pollingTimer = null; } } });关键点在生命周期配合。onShow启动轮询,onHide停止,这样可以保证用户切到别的 tab 时定时器不会在后台空转。很多人在模拟器里看不出问题,因为模拟器切后台不一定会触发 onHide,但真机上频繁“切走、切回”会让多个定时器叠加,好几份 setInterval 同时在跑,系统资源就白白浪费了。
轮询间隔这里设了 5 秒,这是模拟场景的合理值。如果接真实行情,这个间隔最好拉到 10 秒以上,否则很容易触发行情服务端的频率限制。下拉刷新的finally里调用wx.stopPullDownRefresh,避免刷新动画一直转。注意wx.stopPullDownRefresh是必须调用的,有些新手只配置了enablePullDownRefresh,忘记了停止,页面就一直处于下拉状态,看起来像卡死。
3.3 自选股增删:本地存储与跨页同步
自选股是这个项目里数据一致性最值得研究的部分。首页维护一份自选列表,详情页可以添加和删除,两个页面之间没有 router 参数传递,靠的是本地存储加一个轻量同步机制:
// utils/storage.js const KEY = 'watchlist'; function getWatchlist() { return wx.getStorageSync(KEY) || []; } function addStock(stock) { const list = getWatchlist(); if (!list.some(item => item.symbol === stock.symbol)) { list.unshift(stock); wx.setStorageSync(KEY, list); } return list; } function removeStock(symbol) { const list = getWatchlist().filter(item => item.symbol !== symbol); wx.setStorageSync(KEY, list); return list; } module.exports = { getWatchlist, addStock, removeStock };这里的核心思路是:本地存储是唯一数据源,页面渲染永远基于getWatchlist()的结果。详情页添加自选后,不做跨页通知,而是依赖首页onShow时重新读 storage,这样虽然牺牲了一点即时性,但换来了逻辑简单。用unshift把新添加的股票放在列表最前面,模拟真实 App 的习惯。
同步机制里有一个细节:some判断重复时,比较的是symbol而不是name。原因是股票名称可能带后缀变化,但代码不会变。另外wx.setStorageSync是同步方法,在数据量小的情况下没问题,如果自选股将来超过几十条,建议改成wx.setStorage异步版本,避免在页面加载路径上阻塞渲染。跨页同步时不建议用全局变量存 list 再手动触发页面刷新,因为小程序页面栈不可控,容易漏掉页面导致数据不一致。用 storage 加 onShow 读取,代价最小,也最不容易出错。
4. 把 K 线图装进页面:图表库选型与 canvas 配置
图表是这份源码的卖点之一,但它也是整个项目里坑最多的地方。先别急着写 canvas,选型这一步走错,后面所有页面都会跟着遭殃。这一章把图表库选择、数据映射、tooltip 与横屏参数依次拆开。
4.1 先选图表库:原生 canvas 为什么不够用
小程序里画 K 线图可以完全用原生 canvas,但你需要自己处理坐标轴、刻度、tooltip、手势缩放,这些工作量加起来不亚于再写一个小程序。常见的选择有三个,我整理了一个对比表格:
| 图表库 | 包体积 | 学习成本 | 小程序适配 | 适合场景 |
|---|---|---|---|---|
| ucharts | 约 100KB | 低,文档例子多 | 支持 canvas 2d,组件化友好 | 新手推荐 |
| echarts-for-weixin | 约 1MB+ | 中,web 生态丰富 | 需要手动分包处理体积 | 复杂图表场景 |
| wx-charts | 约 50KB | 低 | 老项目常用,许久未更新 | 简单折线图 |
这个项目用的是 ucharts 思路,组件里初始化图表,页面只需传入数据和配置。选它不是因为功能最全,而是因为它的 canvas 操作屏蔽了很多底层细节,而且图表挂在cover-view之上你也可以很快找到解决方案。如果用 echarts 那套,体积问题会让你不得不上分包,K 线页面如果还带 tabBar,分包配置还要处理 tabBar 页面不能进分包的约束,非常麻烦。选型结论先记住:能用轻量库解决的,不要轻易上重型方案,小程序包体积是硬约束。
4.2 图表组件初始化与 K 线数据映射
图表组件内部负责创建 canvas 上下文,页面只给数据和配置。核心代码简化如下:
// components/charts/charts.js import ucharts from '../../utils/ucharts.js'; // 这里放 ucharts 封装后的模块 Component({ properties: { chartType: { type: String, value: 'kline' }, klineData: { type: Array, value: [] } }, observers: { 'klineData': function (data) { if (data && data.length) { this.renderChart(data); } } }, methods: { renderChart(data) { const ctx = wx.createCanvasContext('klineCanvas', this); const chart = new ucharts({ type: 'candle', context: ctx, width: this.data.canvasWidth, height: this.data.canvasHeight, categories: data.map(item => item.time), series: [{ name: 'K线', data: data.map(item => [item.open, item.close, item.low, item.high]) }] }); chart.updateData({ categories: data.map(item => item.time) }); this.chart = chart; } } });代码逻辑不算复杂,但有几个参数必须抠明白。type: 'candle'是 ucharts 里 K 线图的类型,如果写错成line,数据照常传入但画出来是一条折线,很容易让人误以为是数据源问题。series里每一项的data格式是[open, close, low, high],顺序不能乱,同花顺里看到的 K 线是 OHLC 顺序,但有些行情接口给你的是[open, high, low, close],这里必须做一次重排,不然蜡烛图会画出上下影线颠倒的怪图。
observers监听klineData,当数据从页面 setData 进来时自动重新渲染,这个设计比页面手动调用chart.updateData更符合小程序组件的使用习惯。组件里wx.createCanvasContext的第二个参数this是指定组件所在的 canvas 上下文,很多从 web 转过来的开发者会漏掉这个参数,结果图表永远渲染在页面最外层的 canvas 上,出现层级错乱。
canvas 宽度也不要写死 750rpx,建议在组件 attached 生命周期里通过wx.createSelectorQuery()获取父容器实际宽度,再乘上pixelRatio传给图表,否则在部分高分屏上 K 线会出现模糊或截断。pixelRatio 的获取方式是wx.getSystemInfoSync().pixelRatio,拿不到时默认按 1 处理,图也不至于白屏,只是清晰度略差。
4.3 tooltip、横屏缩放与滑动冲突的参数调整
K 线图做出来不难,难在交互。下面是图表配置里几个影响手感的参数:
| 参数 | 作用 | 推荐值 | 说明 |
|---|---|---|---|
enableScroll | 是否允许图表横向滚动 | true | K 线需要看历史数据,必须开 |
enableZoom | 双指缩放 | true | 真机上测试 60% 以上的操作是双指缩放 |
xAxis.rotate | 横轴文字旋转角度 | 45 | 不旋转只显示时间会重叠 |
tooltip | 十字线提示 | 开启 | 关闭后用户无法精确看价格 |
legend | 图例显示 | 关闭 | 移动端 K 线图不需要图例占高度 |
这些参数在 ucharts 初始化时传入,每个都有默认值,但不调就会废掉。xAxis.rotate设 45 度时,时间标签不会全部重叠,但也别设到 90 度,那个方向在窄屏上会顶出横向滚动条。tooltip开启后,默认的 marker 会显示高开低收四个值,如果觉得遮挡 K 线主体,可以把它改成只显示收盘价,代码里在tooltip配置里设showType: 'candle'。
横屏适配是另一个大坑。整个页面默认竖屏,用户一旦横屏看 K 线,canvas 的高度和宽度不会自动更新。我一般会在页面onResize里重新获取容器尺寸,再调用chart.resize()。这个项目里图表组件没有自动监听窗口变化,需要页面配合。如果你在真机上发现横屏后图表只显示左上角四分之一,优先检查resize是否已经把新宽度传给了 canvas,而不是怀疑数据丢了。canvas 的重新绘制是一个异步过程,横屏瞬间数据映射也要重新走一遍,否则图表宽度变了、数据还是旧的,视觉上就会出现右边空一大块的情况。
滑动冲突也需要特别小心:K 线页如果外面套了一个swiper做分时/K线切换,双指缩放和左右滑动两个手势会打架。常见做法是分时图用 swiper 切换,K 线区域内部不再用 swiper,只保留 canvas 自己的滚动和缩放。组件里通过catchtouchmove阻止事件冒泡到外层滚动容器,这一步不做的话,用户想横滑 K 线,结果整个页面跟着左右切,体验直接归零。
5. 避坑:编译报错、图表白屏与涨跌计算错位的五个高频问题
前面几章把正常路径走完了,这一章写我复现和给 A 同学调代码时遇到的高频问题。每条我都按排查过程记录,现象、原因、解决一条线写清,看完可以少走不少弯路。
5.1 setData 太频繁:行情一刷新页面就卡
现象:开启 5 秒轮询后,模拟器上流畅,真机上滑列表明显掉帧,滚动时有明显卡顿,甚至出现短暂白屏。
原因:fetchQuotes返回整个列表后在setData里更新了全部字段,而列表项里包含了 10 多个字段,哪怕只涨了一分钱,整行所有字段都参与 diff。低端安卓机上小程序 setData 的性能瓶颈非常明显,数据量大时渲染线程会被打满。
解决:setData 前先做一层 diff,只更新变化的行和变化的字段。最粗暴有效的方式是先对比symbol和price,价格没变的行不放进 setData。另一个方式是改成单字段更新,把stockList[0].price这种路径作为 key,小程序只重绘那一行。从那以后我做行情列表都会强制把“最小更新集”写成一个独立函数,而不是直接塞整个数组。
5.2 canvas 层级太高:下拉菜单被图表盖住
现象:K 线页里点击右上角“更多”,弹出的菜单在部分安卓机上显示不出来,或者显示到图表下面,只露出半个角。
原因:原生 canvas 组件在小程序里的层级高于普通 view,历史版本里它永远盖在页面上层。虽然新版基础库支持同层渲染,但低版本基础库仍然会有这个问题。
解决:把弹出菜单改成cover-view实现,或者把菜单改成页面级半屏弹层,让它也使用原生组件渲染。实际项目里我推荐后者,因为 cover-view 的样式限制非常多,内部不能放 input 这类组件,维护成本高。如果你只是要显示几个操作按钮,用半屏弹层加position: fixed就能解决问题,记得在弹层打开时暂停 canvas 手势,避免用户手指划过弹层时图表还在滚动。
5.3 涨跌幅颜色错位:“-0”字段导致的绿红混乱
现象:部分股票涨幅明明是 0.00%,却显示成绿色,个别价格显示为“-0.00”,用户反馈很困惑。
原因:后端或 mock 数据在下发时,涨跌幅有可能是-0.001之类的负浮点数,经过toFixed(2)之后变成-0.00。而颜色计算函数里value > 0、value < 0的判断在-0.00下不成立,走了灰色分支,页面颜色自然就乱了。
解决:在做颜色判断和数据展示前统一做一次归一化处理。我的写法是const displayPercent = parseFloat(changePercent)后,如果Math.abs(displayPercent) < 0.005,直接把值强制设为 0,再传给颜色函数和格式化函数。同理,价格字段也要做一次Math.abs(price) < 0.005 ? 0 : price的兜底,避免出现“-0.00 元”这种反直觉展示。
5.4 长列表渲染白屏:滚太快页面只剩占位符
现象:自选股加到 30 只以后,快速上滑再松手,列表区域短暂变白,过一秒又恢复正常。
原因:小程序列表没有做虚拟滚动,一次性渲染 30 个 stock-row 的问题不算大,但每个 row 里有 price、name 等多个 text 节点,加上 canvas 页面同 tab 切换时内存回收不及时,快速滚动会让渲染层 GC 短暂卡住,视觉表现就是白屏。
解决:先把列表拆成block加wx:if分页渲染,比如每次只渲染 20 条,滚动到底再加载剩余。如果还不行,就不要在同一个 tab 里同时保留列表页和图表页,改成点击列表项后再跳转详情,减少常驻节点数量。我在这个项目里把指数列表和自选列表分成两个scroll-view,各自独立滚动,页面卡顿明显改善。记住一个原则:tab 页里只放轻量列表,重型 canvas 页面永远单独开页面。
5.5 模拟交易可买数量没做截断:买到小数股还会透支
现象:模拟交易里输入买入数量,成交后持仓出现12.345 股,余额算出来是负数还能继续下单。
原因:代码里买入数量直接用parseFloat解析输入框值,没有做整数约束,也没有在卖出数量上做持仓上限校验。本金不足时委托单也没有被拦截,状态机里漏了余额判断。
解决:所有数量输入统一走parseInt(value, 10),并且校验value > 0。买入前比较金额 <= 可用余额,卖出前比较数量 <= 可用持仓,如果不满足直接给 toast 提示。另外,成交金额的计算要用Math.round(price * quantity * 100) / 100做两位小数截断,不能依赖浮点数运算,否则累计几笔后会出现0.30000000000000004这种余额。
6. 模拟交易闭环与收尾验证:把买入、卖出、持仓状态机跑通
6.1 交易页的状态字段与买卖计算
模拟交易页是这个项目的最后一块拼图,它把前面所有数据串成了一个闭环。交易状态我用四个字段维护,统一放在页面data里:
data: { cash: 100000.00, // 可用余额,初始 10 万 positions: [], // 持仓列表 orders: [], // 成交记录 currentStock: null // 当前正在操作的股票 }买入逻辑按顺序做三件事:校验数量为正整数、校验金额不超余额、更新持仓与余额。卖出逻辑则要校验持仓数量足够,并回收资金。核心计算如下:
// pages/trade/trade.js buy(quantity, price) { const cost = Math.round(price * quantity * 100) / 100; if (cost > this.data.cash) { wx.showToast({ title: '可用余额不足', icon: 'none' }); return false; } const exists = this.data.positions.find(p => p.symbol === this.data.currentStock.symbol); const newPositions = exists ? this.data.positions.map(p => p.symbol === exists.symbol ? { ...p, quantity: p.quantity + quantity, cost: p.cost + cost } : p ) : [...this.data.positions, { symbol: this.data.currentStock.symbol, quantity, cost }]; this.setData({ cash: Math.round((this.data.cash - cost) * 100) / 100, positions: newPositions }); }这套逻辑演示的是“实时成交”,不涉及涨停跌停的委托撮合,作为教学 demo 已经足够。每个字段都有明确的用途:cash是账户维度唯一资金字段,positions里的cost记录累计买入金额,用于计算浮动盈亏。持仓列表页的盈亏计算可以简单做:currentPrice * quantity - cost,就不再单独展开了。
6.2 装完以后我会这样验证
每次把这份源码在真机上跑通,我都会强制走一遍五个验证点。第一,下拉刷新和轮询同时触发时,页面不能出现两套数据源交错导致的价格回跳。第二,K 线图在 iOS 和安卓各看一次,横竖屏旋转后图表宽度能正确跟随。第三,自选股在首页添加、详情页删除、再回首页,数据保持一致。第四,连续买入三笔再全部卖出,余额能精确回到买入前,误差不超过一分钱。第五,把USE_MOCK改成 false 并传入错误 url,看报错提示是否清晰、页面是否有兜底态。
顺手给大家一个调试技巧:用console.time包住fetchQuotes到setData这段链路,真机上如果耗时超过 200ms,就优先查列表 diff 和 canvas 重绘,这两个是性能消耗大头。从那以后我每次做这类带行情的小程序,都强制在开始写页面之前先画一遍数据流草图,标注哪些字段是高频更新项、哪些页面需要跨页同步、图表在 tab 页还是独立页面,画完再动手,省掉的不只是返工时间,还有半夜查问题的头发。希望帮到你。
本文还有配套的精品资源,点击获取