支付宝小程序用不了Echarts怎么办 手把手教你集成图表附真实项目代码与常见报错解决指南
最近有个朋友跑来问我,说他做支付宝小程序的时候想用Echarts做数据图表,结果搞了半天完全跑不起来。我一看他的代码,好家伙,直接把网页版的Echarts搬过来了,肯定跑不通啊!
别急,这篇文章就是专门来救火的。我会从最基础的环境配置到真实的完整项目代码,再到那些让人抓狂的报错,全部给你讲清楚。
先搞清楚,为什么网页版Echarts在支付宝小程序里跑不了
你可能会问,Echarts这么好用的图表库,怎么就不能直接用呢?
这里面有个核心问题:Echarts是基于Canvas和DOM的,而支付宝小程序的Canvas是受限的。
小程序里的Canvas和网页上的Canvas完全是两回事。网页上你可以随便操作DOM、随便调用浏览器API,但小程序是在一个沙箱环境里运行的,很多能力都被阉割了。所以直接把网页版的Echarts代码复制过去,结果必然是报错或者一片空白。
不过别担心,这个坑我们早就踩过了,现在已经有成熟的解决方案。
解决方案:使用 echarts-for-weixin 改造版
虽然叫”for weixin”,但这个库其实是为所有小程序框架设计的,支付宝小程序完全兼容。它的核心思路是:把Echarts的渲染逻辑改写,适配小程序的Canvas环境。
第一步:下载并配置 ECharts 组件
下载库文件
你可以从GitHub下载完整项目,也可以只拷贝需要的文件。推荐直接克隆整个仓库:
git clone https://github.com/ecomfe/echarts-for-weixin.git
克隆完之后,你需要的核心文件在 ec-canvas 目录下,把这个整个文件夹复制到你的小程序项目的 components 目录下。
目录结构应该是这样的
your-miniprogram/
├── app.js
├── app.json
├── app.wxss
├── components/
│ └── ec-canvas/
│ ├── ec-canvas.js
│ ├── ec-canvas.json
│ ├── ec-canvas.wxml
│ └── ec-canvas.wxss
└── pages/
└── index/
├── index.js
├── index.json
├── index.wxml
└── index.wxss
第二步:在页面中引入组件
先注册组件
在你要使用的页面的 .json 配置文件里注册组件:
{
"usingComponents": {
"ec-canvas": "../../components/ec-canvas/ec-canvas"
}
}
在页面结构中插入图表容器
打开你的 .wxml 文件,加上这段代码:
<view class="chart-container">
<ec-canvas id="mychart-dom-line" canvas-id="mychart-line" ec="{{ ecLine }}"></ec-canvas>
</view>
<view class="chart-container">
<ec-canvas id="mychart-dom-bar" canvas-id="mychart-bar" ec="{{ ecBar }}"></ec-canvas>
</view>
这里我放了两个图表容器,一个放折线图,一个放柱状图,方便你后面分别测试。ec-canvas 是这个组件的标签名,canvas-id 是Canvas的标识,ec 属性会绑定我们在JS里配置好的图表实例。
第三步:配置图表的 JS 逻辑
这是最关键的一步,也是最容易出错的地方。
打开你页面的 .js 文件,先引入 ECharts 模块:
// 引入 ECharts 核心模块
const echarts = require('../../components/ec-canvas/echarts');
Page({
data: {
ecLine: {
onInit: null
},
ecBar: {
onInit: null
}
},
onLoad() {
// 初始化折线图
this.initLineChart();
// 初始化柱状图
this.initBarChart();
},
// 初始化折线图
initLineChart() {
this.selectComponent('#mychart-dom-line').init((canvas, width, height) => {
// 初始化图表
const chart = echarts.init(canvas, null, {
width: width,
height: height
});
// 设置图表配置项
const option = {
title: {
text: '近7日用户活跃趋势',
left: 'center',
textStyle: {
fontSize: 14,
color: '#333'
}
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['新增用户', '活跃用户'],
bottom: 0
},
grid: {
left: '3%',
right: '4%',
bottom: '15%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
axisLine: {
lineStyle: {
color: '#ccc'
}
}
},
yAxis: {
type: 'value',
axisLine: {
lineStyle: {
color: '#ccc'
}
},
splitLine: {
lineStyle: {
color: '#f0f0f0'
}
}
},
series: [
{
name: '新增用户',
type: 'line',
smooth: true,
data: [120, 132, 101, 134, 90, 230, 210],
itemStyle: {
color: '#5470c6'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(84, 112, 198, 0.5)' },
{ offset: 1, color: 'rgba(84, 112, 198, 0.1)' }
])
}
},
{
name: '活跃用户',
type: 'line',
smooth: true,
data: [220, 182, 191, 234, 290, 330, 310],
itemStyle: {
color: '#91cc75'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(145, 204, 117, 0.5)' },
{ offset: 1, color: 'rgba(145, 204, 117, 0.1)' }
])
}
}
]
};
chart.setOption(option);
return chart;
});
},
// 初始化柱状图
initBarChart() {
this.selectComponent('#mychart-dom-bar').init((canvas, width, height) => {
const chart = echarts.init(canvas, null, {
width: width,
height: height
});
const option = {
title: {
text: '各部门业绩对比',
left: 'center',
textStyle: {
fontSize: 14,
color: '#333'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value',
axisLine: {
lineStyle: {
color: '#ccc'
}
}
},
yAxis: {
type: 'category',
data: ['销售部', '技术部', '运营部', '市场部', '客服部'],
axisLine: {
lineStyle: {
color: '#ccc'
}
}
},
series: [
{
name: '业绩',
type: 'bar',
data: [18203, 23489, 19053, 21089, 17234],
itemStyle: {
color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
{ offset: 0, color: '#37a2da' },
{ offset: 1, color: '#32c5e9' }
]),
borderRadius: [0, 4, 4, 0]
},
label: {
show: true,
position: 'right',
formatter: '{c}'
}
}
]
};
chart.setOption(option);
return chart;
});
},
// 页面卸载时销毁图表,避免内存泄漏
onUnload() {
this.destroyCharts();
},
// 页面隐藏时销毁图表
onHide() {
this.destroyCharts();
},
destroyCharts() {
// 获取组件实例并销毁
const lineChart = this.selectComponent('#mychart-dom-line');
const barChart = this.selectComponent('#mychart-dom-bar');
if (lineChart && lineChart.chart) {
lineChart.chart.dispose();
}
if (barChart && barChart.chart) {
barChart.chart.dispose();
}
}
});
这段代码我尽量写得详细一些,每个部分都有注释。你可以直接复制,然后根据你自己的项目需求修改数据部分。
第四步:添加样式让图表显示正常
.chart-container {
width: 100%;
height: 300px;
margin-bottom: 20px;
}
page {
background-color: #f5f5f5;
}
view {
box-sizing: border-box;
}
这一步很容易被忽略,但如果不设置容器的高度,图表可能显示不出来或者显示异常。
一个完整的真实项目示例
上面那个例子是基础的,现在给你一个更接近真实项目的完整代码。假设你有一个商城小程序,需要展示销售数据报表。
// pages/report/report.js
const echarts = require('../../components/ec-canvas/echarts');
Page({
data: {
activeTab: 'week',
ecPie: { onInit: null },
ecBar: { onInit: null },
ecLine: { onInit: null }
},
charts: {},
onLoad() {
this.initCharts();
},
onShow() {
// 每次页面显示时重新加载数据
this.loadChartData();
},
initCharts() {
// 饼图 - 品类销售占比
this.selectComponent('#pie-chart').init((canvas, width, height) => {
const chart = echarts.init(canvas, null, { width, height });
chart.setOption(this.getPieOption());
this.charts.pie = chart;
return chart;
});
// 柱状图 - 日销售额
this.selectComponent('#bar-chart').init((canvas, width, height) => {
const chart = echarts.init(canvas, null, { width, height });
chart.setOption(this.getBarOption());
this.charts.bar = chart;
return chart;
});
// 折线图 - 用户增长趋势
this.selectComponent('#line-chart').init((canvas, width, height) => {
const chart = echarts.init(canvas, null, { width, height });
chart.setOption(this.getLineOption());
this.charts.line = chart;
return chart;
});
},
// 饼图配置
getPieOption() {
return {
title: {
text: '品类销售占比',
left: 'center',
textStyle: { fontSize: 14 }
},
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
top: 'center',
textStyle: { fontSize: 12 }
},
series: [{
type: 'pie',
radius: ['40%', '70%'],
center: ['60%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 8,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 16,
fontWeight: 'bold'
}
},
data: [
{ value: 1048, name: '服装' },
{ value: 735, name: '数码' },
{ value: 580, name: '食品' },
{ value: 484, name: '家居' },
{ value: 300, name: '美妆' }
]
}]
};
},
// 柱状图配置
getBarOption() {
return {
title: {
text: '近7日销售额',
left: 'center',
textStyle: { fontSize: 14 }
},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(255,255,255,0.9)',
borderColor: '#ddd',
textStyle: { color: '#333' }
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
axisLine: { lineStyle: { color: '#ccc' } },
axisTick: { show: false }
},
yAxis: {
type: 'value',
axisLine: { show: false },
splitLine: { lineStyle: { color: '#f0f0f0' } }
},
series: [{
name: '销售额',
type: 'bar',
barWidth: '40%',
data: [12000, 13200, 10100, 13400, 19000, 23000, 21000],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#5470c6' },
{ offset: 1, color: '#91cc75' }
]),
borderRadius: [4, 4, 0, 0]
}
}]
};
},
// 折线图配置
getLineOption() {
return {
title: {
text: '用户增长趋势',
left: 'center',
textStyle: { fontSize: 14 }
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['新增用户', '留存用户'],
top: 30,
textStyle: { fontSize: 12 }
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月'],
axisLine: { lineStyle: { color: '#ccc' } }
},
yAxis: {
type: 'value',
axisLine: { show: false },
splitLine: { lineStyle: { color: '#f0f0f0' } }
},
series: [
{
name: '新增用户',
type: 'line',
smooth: true,
data: [820, 932, 901, 934, 1290, 1330, 1320],
itemStyle: { color: '#5470c6' },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(84,112,198,0.4)' },
{ offset: 1, color: 'rgba(84,112,198,0.05)' }
])
}
},
{
name: '留存用户',
type: 'line',
smooth: true,
data: [620, 732, 701, 734, 1090, 1130, 1120],
itemStyle: { color: '#91cc75' },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(145,204,117,0.4)' },
{ offset: 1, color: 'rgba(145,204,117,0.05)' }
])
}
}
]
};
},
// 加载图表数据(实际项目中应该是接口请求)
loadChartData() {
// 模拟异步加载数据
wx.request({
url: 'https://api.example.com/sales/report',
success: (res) => {
if (res.statusCode === 200 && res.data) {
this.updateCharts(res.data);
}
},
fail: (err) => {
console.error('加载图表数据失败', err);
}
});
},
// 更新图表数据
updateChartData(data) {
// 更新饼图
if (this.charts.pie) {
this.charts.pie.setOption({
series: [{ data: data.pieData }]
});
}
// 更新柱状图
if (this.charts.bar) {
this.charts.bar.setOption({
xAxis: { data: data.xData },
series: [{ data: data.barData }]
});
}
// 更新折线图
if (this.charts.line) {
this.charts.line.setOption({
xAxis: { data: data.xData },
series: [
{ data: data.lineData1 },
{ data: data.lineData2 }
]
});
}
},
// Tab切换处理
onTabChange(e) {
const tab = e.detail.value;
this.setData({ activeTab: tab });
// 根据tab切换重新加载数据
this.loadChartData();
},
onUnload() {
this.disposeCharts();
},
onHide() {
this.disposeCharts();
},
disposeCharts() {
Object.values(this.charts).forEach(chart => {
if (chart) {
chart.dispose();
}
});
this.charts = {};
}
});
对应的 WXML 页面结构:
<view class="page">
<!-- Tab切换 -->
<view class="tabs">
<view class="tab-item {{activeTab === 'week' ? 'active' : ''}}" bindtap="onTabChange" data-tab="week">本周</view>
<view class="tab-item {{activeTab === 'month' ? 'active' : ''}}" bindtap="onTabChange" data-tab="month">本月</view>
<view class="tab-item {{activeTab === 'year' ? 'active' : ''}}" bindtap="onTabChange" data-tab="year">本年</view>
</view>
<!-- 饼图区域 -->
<view class="chart-card">
<view class="card-title">品类销售占比</view>
<ec-canvas id="pie-chart" canvas-id="pie-chart" ec="{{ ecPie }}"></ec-canvas>
</view>
<!-- 柱状图区域 -->
<view class="chart-card">
<view class="card-title">近7日销售额</view>
<ec-canvas id="bar-chart" canvas-id="bar-chart" ec="{{ ecBar }}"></ec-canvas>
</view>
<!-- 折线图区域 -->
<view class="chart-card">
<view class="card-title">用户增长趋势</view>
<ec-canvas id="line-chart" canvas-id="line-chart" ec="{{ ecLine }}"></ec-canvas>
</view>
</view>
对应的样式:
.page {
padding: 20rpx;
background-color: #f5f6f7;
min-height: 100vh;
}
.tabs {
display: flex;
background: #fff;
border-radius: 16rpx;
padding: 10rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
}
.tab-item {
flex: 1;
text-align: center;
padding: 16rpx 0;
font-size: 28rpx;
color: #666;
border-radius: 12rpx;
transition: all 0.3s;
}
.tab-item.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
font-weight: 500;
}
.chart-card {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
}
.card-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
margin-bottom: 16rpx;
padding-left: 16rpx;
border-left: 6rpx solid #667eea;
}
真实项目中经常遇到的报错及解决方法
下面这些坑,我一个个给你讲,都是真实踩过的。
报错一:Canvas is not defined 或 canvas.getContext is not a function
这个错误十有八九是引入路径的问题。你要检查一下 ec-canvas.js 里面引用的 echarts.js 路径是否正确。
打开 ec-canvas/ec-canvas.js,找到这一行:
const echarts = require('./echarts');
确认 echarts.js 文件确实存在于这个目录下。如果找不到,就去GitHub重新下载整个 ec-canvas 文件夹。
报错二:图表显示空白,什么都看不到
这个坑我踩过最多次。最常见的原因是 容器高度没有设置。
小程序里的Canvas默认高度是0,如果你没有在WXML或CSS里给 ec-canvas 的父容器设置明确的高度,图表就会挤成一团或者直接看不到。
解决方式:
/* 在页面样式里确保容器有高度 */
ec-canvas {
width: 100%;
height: 300px; /* 或者 300rpx,取决于你的设计 */
}
或者在WXML里直接设置:
<ec-canvas id="mychart" canvas-id="mychart" ec="{{ ec }}" style="width: 100%; height: 300px;"></ec-canvas>
还有一种情况是 init 回调里的 canvas 参数是 undefined,这通常是因为组件还没有渲染完成你就调用了 init。解决办法是把初始化逻辑放在 onReady 生命周期里:
onReady() {
this.initCharts();
}
报错三:echarts is not defined
这个错误说明你没有正确引入 ECharts 模块。检查一下你的 .js 文件顶部有没有这行:
const echarts = require('../../components/ec-canvas/echarts');
注意路径要相对于当前文件的位置。如果 echarts.js 放在 components/ec-canvas/ 目录下,而你的页面在 pages/index/ 下,那路径就是 ../../components/ec-canvas/echarts。
如果你用的是更老的版本,可能还需要在 ec-canvas.js 里检查是否导出了 echarts:
module.exports = {
init: init,
echarts: echarts // 确认这行存在
};
报错四:图表数据更新了,但视图没有刷新
这是很多新手都会遇到的问题。你以为调用了 setOption 就能更新图表,但实际上可能没有正确获取到图表实例。
关键是要在 init 回调里把 chart 实例保存下来,然后在需要更新的时候用保存的实例去调用 setOption:
// 初始化时保存实例
this.selectComponent('#mychart').init((canvas, width, height) => {
const chart = echarts.init(canvas, null, { width, height });
chart.setOption(option);
// 重要:把 chart 实例保存起来
this.chart = chart;
return chart;
});
// 更新数据时
updateChart(newData) {
if (this.chart) {
this.chart.setOption({
series: [{ data: newData }]
});
}
}
报错五:真机调试时图表不显示,模拟器正常
这个坑真的特别烦人。模拟器里看着好好的,一到真机就出问题了。
最常见的原因是真机上 Canvas 的渲染性能问题。ECharts 在真机上可能会因为内存不足或者渲染超时导致空白。
解决办法有两个:
方法一:降低图表复杂度
// 关闭一些消耗性能的特效
const option = {
animation: false, // 关闭动画
series: [{
// 关闭渐变色,改用纯色
itemStyle: {
color: '#5470c6' // 不用渐变
}
}]
};
方法二:延迟初始化
onReady() {
// 延迟一点时间再初始化,给页面渲染留足时间
setTimeout(() => {
this.initCharts();
}, 300);
}
报错六:微信小程序能用,支付宝小程序不能用
如果你发现同样的代码在微信里跑得挺好,一到支付宝就报错,那很可能是支付宝小程序的 API 差异导致的。
支付宝小程序和微信小程序在以下几个地方有差异:
- 请求API不同:微信用
wx.request,支付宝用my.request - Storage API不同:微信用
wx.setStorage,支付宝用my.setStorage - 生命周期名称略有差异
如果你的代码里混用了不同平台的API,就需要做条件编译:
// 条件编译示例
if (typeof my !== 'undefined') {
// 支付宝小程序
my.request({
url: 'https://api.example.com/data',
success: (res) => {
this.updateChart(res.data);
}
});
} else if (typeof wx !== 'undefined') {
// 微信小程序
wx.request({
url: 'https://api.example.com/data',
success: (res) => {
this.updateChart(res.data);
}
});
}
报错七:图标点击事件不触发
如果你给图表配置了 click 事件,在真机上可能点击没有响应。这是因为小程序的触摸事件和Canvas的事件处理机制不太一样。
解决办法是手动绑定触摸事件:
// 在 init 回调里添加事件绑定
chart.on('click', (params) => {
console.log('点击了', params);
wx.showToast({
title: params.name,
icon: 'none'
});
});
// 如果 click 不生效,尝试用 tap
chart.on('tap', (params) => {
console.log('点击了', params);
});
性能优化建议
图表做多了之后,你会发现页面可能会有卡顿。这里有几个优化技巧:
1. 按需加载 ECharts
ECharts 整个库比较大,如果你只用折线图和柱状图,可以只引入需要的模块:
// 只引入需要的模块,减小体积
const echarts = require('../../components/ec-canvas/echarts');
// 手动注册需要的图表类型
import 'echarts/lib/chart/line';
import 'echarts/lib/chart/bar';
import 'echarts/lib/component/tooltip';
import 'echarts/lib/component/legend';
import 'echarts/lib/component/title';
不过注意,echarts-for-weixin 这个库本身已经做了打包优化,你直接用整个 echarts 也没问题,包体积大约在 300KB 左右。
2. 图表实例复用
如果你的页面有多个图表,尽量复用 chart 实例,不要每次都 init 一个新的:
// 不好的做法:每次 setData 都重新 init
setData({ chartData: newData }) {
this.initCharts(); // 每次都销毁重建,性能差
}
// 好的做法:只更新数据,不重建实例
setData({ chartData: newData }) {
if (this.chart) {
this.chart.setOption({ series: [{ data: newData }] });
}
}
3. 页面隐藏时销毁图表
小程序页面切到后台时,一定要记得销毁图表实例,不然会占用内存:
onHide() {
if (this.chart) {
this.chart.dispose();
this.chart = null;
}
}
onUnload() {
this.onHide();
}
如果你不想用 echarts-for-weixin
还有一个选择是直接用小程序原生的 Canvas API 来画图。不过说实话,这样做的开发成本比较高,尤其是复杂图表。
但如果你只需要简单的图表,比如一个进度环、一个折线图,自己用 Canvas API 写也是可行的。下面是一个简单的进度环示例:
// pages/canvas/canvas.js
Page({
data: {
progress: 75
},
onLoad() {
this.drawProgressRing();
},
drawProgressRing() {
const query = wx.createSelectorQuery();
query.select('#progressCanvas')
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
const dpr = wx.getSystemInfoSync().pixelRatio;
canvas.width = res[0].width * dpr;
canvas.height = res[0].height * dpr;
ctx.scale(dpr, dpr);
const centerX = res[0].width / 2;
const centerY = res[0].height / 2;
const radius = Math.min(centerX, centerY) - 10;
// 绘制背景圆
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
ctx.strokeStyle = '#eee';
ctx.lineWidth = 8;
ctx.stroke();
// 绘制进度圆
const progressAngle = (this.data.progress / 100) * 2 * Math.PI;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + progressAngle);
ctx.strokeStyle = '#667eea';
ctx.lineWidth = 8;
ctx.lineCap = 'round';
ctx.stroke();
// 绘制文字
ctx.fillStyle = '#333';
ctx.font = 'bold 24px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(this.data.progress + '%', centerX, centerY);
});
},
// 更新进度
updateProgress(newProgress) {
this.setData({ progress: newProgress }, () => {
this.drawProgressRing();
});
}
});
不过说实在的,除非你的图表特别简单,否则还是建议用 ECharts,功能丰富太多了。
最后的一些小贴士
开发工具版本:确保你用的是最新版本的支付宝开发者工具,旧版本对 Canvas 的支持可能不完善。
真机调试:开发过程中一定要多切到真机调试模式看看,模拟器的表现和真机有时候差距挺大的。
版本兼容:
echarts-for-weixin这个库更新不是特别频繁,如果遇到问题可以去 GitHub 上提 issue,作者的响应还是挺快的。替代方案:如果你实在搞不定 ECharts,可以考虑其他小程序图表库,比如
wx-charts或者charts-for-weixin,不过说实话,这些库的功能和灵活性都不如 ECharts。
希望这篇文章能帮你顺利在支付宝小程序里跑起 ECharts 图表。如果还有什么问题,欢迎留言讨论。做小程序开发就是不断踩坑不断填坑的过程,多踩几次就熟练了。
