###全局属性 app.json 配置文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
{
"pages": [
"pages/index/index", // 启动页,进入小程序时首先展示的页面
"pages/logs/logs" // 第二个页面,通常用于展示日志或其他信息
// ......
],
"window": {
"navigationBarBackgroundColor": "#ffffff", // 导航栏背景色,通常设置为白色或主题色
"navigationBarTitleText": "我的小程序", // 导航栏标题文本,通常为小程序的名称
"navigationBarTextStyle": "black", // 导航栏标题文字颜色,设置为黑色
"backgroundColor": "#f0f0f0", // 小程序的背景颜色,设置为浅灰色
"backgroundTextStyle": "dark", // 下拉刷新时的背景文字颜色,可设置为暗色或亮色
"navigationStyle": "default", // 导航栏的样式,'default'为默认样式,'custom'为自定义样式
"statusBarStyle": "dark" // 状态栏文字颜色,'dark'为深色字体,'light'为浅色字体
},
"tabBar": {
"color": "#333333", // TabBar未选中时的文字颜色,设置为深灰色
"selectedColor": "#1a73e8", // TabBar选中时的文字颜色,设置为蓝色
"backgroundColor": "#ffffff", // TabBar背景颜色,设置为白色
"borderStyle": "black", // TabBar上边框的颜色,通常使用黑色
"position": "bottom", // TabBar的位置,设置为底部
"list": [ // 最少2个,最多5个
{
"pagePath": "pages/index/index", // Tab对应的页面路径
"text": "首页", // Tab文字,表示首页
"iconPath": "images/home.png", // 未选中的Tab图标路径
"selectedIconPath": "images/home-active.png" // 选中的Tab图标路径
},
{
"pagePath": "pages/logs/logs", // Tab对应的页面路径
"text": "日志", // Tab文字,表示日志页面
"iconPath": "images/logs.png", // 未选中的Tab图标路径
"selectedIconPath": "images/logs-active.png" // 选中的Tab图标路径
}
]
},
"networkTimeout": {
"request": 10000, // 请求超时时间,单位毫秒,设置为10秒
"connectSocket": 10000, // WebSocket连接超时时间,单位毫秒,设置为10秒
"uploadFile": 10000, // 文件上传超时时间,单位毫秒,设置为10秒
"downloadFile": 10000 // 文件下载超时时间,单位毫秒,设置为10秒
},
"debug": true, // 开启调试模式,输出更多调试信息,便于开发时查看日志
"entryPagePath": "pages/index/index", // 设置小程序启动时显示的页面路径,通常为首页路径

// 使用自定义组件,可以在页面中引入和使用自定义组件
"usingComponents": {
"custom-button": "/components/custom-button/custom-button", // 自定义按钮组件
"custom-modal": "/components/custom-modal/custom-modal" // 自定义模态框组件
},

// 子包配置,适用于大型小程序的分包加载
"subPackages": [
{
"root": "pages/first", // 子包的根目录路径
"pages": [
"index", // 子包内的页面路径
"list"
]
}
],

"theme": "dark", // 设置小程序主题为暗黑模式,可选值为 'light'(亮色模式)或 'dark'(暗色模式)

// 启用下拉刷新功能,允许用户在页面顶部下拉刷新内容
"enablePullDownRefresh": true,

"resizable": true, // 启用页面可调整大小(如适用于桌面端)

// 预加载规则,控制页面加载时的缓存和网络请求策略
"preloadRule": {
"pages/index/index": {
"network": "all", // 预加载页面时加载所有网络请求
"cache": "all" // 预加载时缓存所有资源
}
}
}

详细的知识点解析


pages

  • 说明:定义小程序的所有页面路径,页面的路径是相对 app.json 文件的路径。第一个页面默认是启动页面,除非有”entryPagePath”属性,即小程序加载时显示的页面。

  • 示例:

    1
    2
    3
    4
    "pages": [
    "pages/index/index", // 首页
    "pages/logs/logs" // 日志页面
    ]

window

  • 说明:设置小程序全局窗口样式,包括状态栏、导航栏、背景颜色等。

  • 配置项

    • navigationBarBackgroundColor:导航栏背景色。
    • navigationBarTitleText:导航栏标题文本。
    • navigationBarTextStyle:导航栏标题文字颜色(可选值:black, white)。
    • backgroundColor:小程序的背景色。
    • backgroundTextStyle:下拉刷新的文字颜色(可选值:dark, light)。
    • navigationStyle:导航栏的样式(可选值:default, custom)。
    • statusBarStyle:状态栏文字颜色(可选值:dark, light)。
  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    "window": {
    "navigationBarBackgroundColor": "#ffffff", // 导航栏背景色
    "navigationBarTitleText": "我的小程序", // 导航栏标题
    "navigationBarTextStyle": "black", // 导航栏文字颜色
    "backgroundColor": "#f0f0f0", // 背景颜色
    "backgroundTextStyle": "dark", // 下拉刷新文字颜色
    "navigationStyle": "default", // 默认导航栏样式
    "statusBarStyle": "dark" // 状态栏文字颜色
    }

tabBar

  • 说明:设置小程序底部的 TabBar(标签栏),可以配置底部导航栏的样式、颜色、位置、选中的样式等。

  • 配置项

    • color:TabBar未选中时的文字颜色。
    • selectedColor:TabBar选中时的文字颜色。
    • backgroundColor:TabBar的背景颜色。
    • borderStyle:TabBar上边框的颜色(blackwhite)。
    • position:TabBar的显示位置(bottomtop)。
    • list:一个数组,包含每个 Tab 页的配置项。
      • pagePath:Tab对应的页面路径。
      • text:Tab文字描述。
      • iconPath:未选中的Tab图标路径。
      • selectedIconPath:选中的Tab图标路径。
  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    "tabBar": {
    "color": "#333333", // 未选中Tab文字颜色
    "selectedColor": "#1a73e8", // 选中Tab文字颜色
    "backgroundColor": "#ffffff", // Tab背景颜色
    "borderStyle": "black", // Tab上边框颜色
    "position": "bottom", // Tab位置(bottom 或 top)
    "list": [
    {
    "pagePath": "pages/index/index", // Tab对应的页面
    "text": "首页", // Tab文字
    "iconPath": "images/home.png", // 未选中的图标
    "selectedIconPath": "images/home-active.png" // 选中的图标
    },
    {
    "pagePath": "pages/logs/logs", // Tab对应的页面
    "text": "日志", // Tab文字
    "iconPath": "images/logs.png", // 未选中的图标
    "selectedIconPath": "images/logs-active.png" // 选中的图标
    }
    ]
    }

networkTimeout

  • 说明:配置小程序的网络请求、文件上传、下载、WebSocket连接等操作的超时时间。

  • 配置项

    • request:请求超时时间(单位:毫秒)。
    • connectSocket:WebSocket连接超时时间(单位:毫秒)。
    • uploadFile:文件上传超时时间(单位:毫秒)。
    • downloadFile:文件下载超时时间(单位:毫秒)。
  • 示例

    1
    2
    3
    4
    5
    6
    "networkTimeout": {
    "request": 10000, // 网络请求超时10秒
    "connectSocket": 10000, // WebSocket连接超时10秒
    "uploadFile": 10000, // 文件上传超时10秒
    "downloadFile": 10000 // 文件下载超时10秒
    }

debug

  • 说明:控制小程序是否开启调试模式,调试模式下会输出更多日志。

  • 示例:

    1
    "debug": true  // 开启调试模式

entryPagePath

  • 说明:设置小程序启动时的入口页面路径。如果你的小程序有多个入口页面,可以通过该配置项指定其中一个作为启动页面。

  • 示例:

    1
    "entryPagePath": "pages/index/index"  // 启动时加载的页面路径

usingComponents

  • 说明:用于声明自定义组件,可以在页面中引入和使用自定义组件。

  • 配置项

    • 自定义组件名称:自定义组件的标签名。
    • 路径:自定义组件的路径。
  • 示例

    1
    2
    3
    4
    "usingComponents": {
    "custom-button": "/components/custom-button/custom-button", // 自定义按钮组件
    "custom-modal": "/components/custom-modal/custom-modal" // 自定义模态框组件
    }

subPackages

  • 说明:支持分包加载,可以将小程序拆分成多个包,减小首次加载时的包体积,提高启动速度。

  • 配置项

    • root:子包的根目录。
    • pages:子包中的页面路径数组。
  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    "subPackages": [
    {
    "root": "pages/first", // 子包的根目录
    "pages": [
    "index", // 子包内的页面
    "list"
    ]
    }
    ]

theme

  • 说明:设置小程序的主题,支持亮色和暗色模式。

  • 可选值

    • light:亮色模式
    • dark:暗色模式
  • 示例

    1
    "theme": "dark"  // 启用暗黑模式

enablePullDownRefresh

  • 说明:启用或禁用页面的下拉刷新功能。

11. preloadRule

  • 说明:配置小程序的页面预加载规则。

  • 配置项

    • 页面路径:指定需要预加载的页面。
    • network:预加载的网络资源类型(如:all,表示加载所有资源)。
    • cache:预缓存的资源类型。
  • 示例

    1
    2
    3
    4
    5
    6
    "preloadRule": {
    "pages/index/index": {
    "network": "all", // 预加载网络请求
    "cache": "all" // 预缓存页面资源
    }
    }

局部属性page.json 配置文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{
"navigationBarTitleText": "页面标题", // 设置导航栏标题
"navigationBarBackgroundColor": "#ffffff", // 设置导航栏背景色
"navigationBarTextStyle": "black", // 设置导航栏文字颜色
"backgroundColor": "#f0f0f0", // 设置页面的背景色
"backgroundTextStyle": "dark", // 设置下拉刷新时背景文字的颜色
"enablePullDownRefresh": true, // 启用下拉刷新
"usingComponents": { // 使用自定义组件
"custom-button": "/components/custom-button/custom-button"
},
"disableScroll": false, // 禁止页面滚动
"navigationStyle": "default", // 设置导航栏的样式
"scrollIndicator": "auto", // 设置页面滚动条的样式
"pageOrientation": "auto", // 设置页面的方向(竖屏或横屏)
"tabBar": { // 页面独立配置TabBar
"color": "#333333", // TabBar文字颜色
"selectedColor": "#1a73e8", // TabBar选中文字颜色
"backgroundColor": "#ffffff", // TabBar背景颜色
"borderStyle": "black", // TabBar上边框颜色
"list": [ // TabBar项配置
{
"pagePath": "pages/index/index", // Tab对应的页面路径
"text": "首页", // Tab文本
"iconPath": "images/home.png", // Tab未选中的图标路径
"selectedIconPath": "images/home-active.png" // Tab选中的图标路径
}
]
},
"preloadRule": {
"pages/index/index": {
"network": "all", // 预加载网络请求
"cache": "all" // 预加载页面缓存
}
},
"shareAppMessage": { // 页面自定义分享设置
"title": "分享标题", // 分享的标题
"imageUrl": "/images/share-image.jpg", // 分享的封面图
"path": "pages/index/index" // 分享时的路径
},
"shareTimeline": { // 页面自定义分享至朋友圈
"title": "分享朋友圈标题", // 分享至朋友圈的标题
"imageUrl": "/images/share-image.jpg" // 分享封面图
},
"backgroundColorTop": "#ffffff", // 页面顶部背景色
"backgroundColorBottom": "#f0f0f0" // 页面底部背景色
}

详细的知识点解析


  • 说明:设置导航栏文字颜色,可选值为 blackwhite

backgroundTextStyle

  • 说明:设置下拉刷新时的背景文字颜色,可选值为 darklight

enablePullDownRefresh

  • 说明:启用或禁用页面的下拉刷新功能。默认为 false,如果为 true,则页面支持下拉刷新。

** usingComponents**

  • 说明:定义页面中使用的自定义组件,类似于页面导入的 import

disableScroll

  • 说明:是否禁用页面的滚动,默认为 false。如果设置为 true,则该页面无法滚动。

  • 说明:设置导航栏的样式,可以选择 default(默认样式)或 custom(自定义样式)。

scrollIndicator

  • 说明:设置页面滚动条的样式,支持 autoshownone 三种值。
    • auto:自动显示滚动条
    • show:强制显示滚动条
    • none:不显示滚动条

pageOrientation

  • 说明:设置页面的方向,可以选择 auto(自动)、landscape(横屏)、portrait(竖屏)。如果需要强制横屏或竖屏,可以使用该配置。

tabBar

  • 说明:用于配置该页面独立的 TabBar。与全局的 TabBar 配置不同,某些页面可以拥有自己的 TabBar 配置。此项不常用,通常用全局的 tabBar 配置。

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    "tabBar": {
    "color": "#333333", // 未选中Tab的文字颜色
    "selectedColor": "#1a73e8", // 选中Tab的文字颜色
    "backgroundColor": "#ffffff", // TabBar背景颜色
    "borderStyle": "black", // TabBar边框颜色
    "list": [ // TabBar项
    {
    "pagePath": "pages/index/index", // 对应的页面路径
    "text": "首页", // Tab文字
    "iconPath": "images/home.png", // 未选中的图标路径
    "selectedIconPath": "images/home-active.png" // 选中的图标路径
    }
    ]
    }

preloadRule

  • 说明:用于页面资源的预加载规则,控制哪些页面资源需要提前加载以提高用户体验。

  • 示例:

    1
    2
    3
    4
    5
    6
    "preloadRule": {
    "pages/index/index": {
    "network": "all", // 预加载所有网络请求
    "cache": "all" // 预缓存页面资源
    }
    }

shareAppMessage

  • 说明:配置页面自定义分享内容,用于分享页面的标题、图片、路径等。用户点击页面的分享按钮时会触发该配置。

  • 配置项:

    • title:分享标题
    • imageUrl:分享的封面图
    • path:分享时的路径
  • 示例:

    1
    2
    3
    4
    5
    "shareAppMessage": {
    "title": "分享标题",
    "imageUrl": "/images/share-image.jpg",
    "path": "pages/index/index"
    }

shareTimeline

  • 说明:配置分享至朋友圈的内容,包括标题和封面图。此项仅在分享到朋友圈时有效。

  • 示例:

    1
    2
    3
    4
    "shareTimeline": {
    "title": "分享朋友圈标题",
    "imageUrl": "/images/share-image.jpg"
    }
组件/控件作用用途示例代码
<view>容器元素用于布局和包裹其他元素,类似 HTML 的 <div>html <view class="container"> <view class="header">Header</view> <view class="content">Content</view> </view>
<scroll-view>可滚动区域实现滚动视图,当内容超出视口时可以滚动。html <scroll-view scroll-y="true"> <view>Content 1</view> <view>Content 2</view> </scroll-view>
<swiper>轮播图用于实现左右滑动的轮播图效果。html <swiper indicator-dots="true" autoplay="true"> <swiper-item> <image src="image1.jpg"/> </swiper-item> </swiper>
<navigator>页面跳转点击跳转到指定页面,支持内部和外部跳转。html <navigator url="/pages/detail/detail">Go to Detail Page</navigator>
<text>文本显示用于显示文本,类似于 HTML 中的 <span>html <text class="title">This is a title</text>
<image>显示图片用于显示图片,支持本地和网络图片。html <image src="image.jpg" mode="aspectFit"></image>
<button>按钮用于触发事件或跳转。html <button bindtap="onClick">Click Me</button>
<icon>显示图标用于展示内置图标,支持多种常用图标类型。html <icon type="success" size="24" color="#00ff00"></icon>
<form>表单容器用于组织输入控件并提交表单数据。html <form bindsubmit="submitForm"> <input name="username" placeholder="Enter your name"/> <button formType="submit">Submit</button> </form>
<input>单行输入框用于接收用户的文本输入。html <input name="username" placeholder="Enter your username"/>
<textarea>多行输入框用于接收多行文本输入。html <textarea name="content" placeholder="Enter your message" />
<checkbox-group>复选框组用于选择多个选项。html <checkbox-group> <checkbox value="A">Option A</checkbox> <checkbox value="B">Option B</checkbox> </checkbox-group>
<radio-group>单选框组用于选择一个选项。html <radio-group> <radio value="option1">Option 1</radio> <radio value="option2">Option 2</radio> </radio-group>
<picker>选择器用于日期、时间、区域等选择。html <picker mode="date" bindchange="onDateChange"> <view class="picker">Select Date</view> </picker>
<switch>开关控件用于切换状态,开启或关闭。html <switch checked="{{switchChecked}}" bindchange="onSwitchChange"></switch>
<slider>滑块控件用于选择一个连续值。html <slider min="0" max="100" step="1" show-value="true" bindchange="onSliderChange"></slider>
<calendar>日历选择控件用于选择日期。html <calendar bindchange="onDateChange"></calendar>
<rich-text>富文本显示用于显示 HTML 内容,支持文本、图片等。html <rich-text nodes="{{richTextContent}}"></rich-text>
<progress>进度条显示任务或过程的进度。html <progress percent="{{progress}}" show-info="true" stroke-width="6" />

1. <view>

  • 作用<view> 是最常用的容器组件,类似于 HTML 中的 <div>
属性说明示例
class用于设置元素的样式类。<view class="container"></view>
style设置内联样式。<view style="padding: 10px;"></view>
id设置唯一的 id。<view id="header"></view>
hidden是否隐藏该元素,值为 truefalse<view hidden="true"></view>
catchtap阻止冒泡的点击事件。<view catchtap="onClick"></view>
bindtap绑定点击事件的处理函数。<view bindtap="onClick"></view>

2. <scroll-view>

  • 作用:实现可滚动区域,支持垂直或水平滚动。
属性说明示例
scroll-x是否开启水平滚动,值为 truefalse<scroll-view scroll-x="true"></scroll-view>
scroll-y是否开启垂直滚动,值为 truefalse<scroll-view scroll-y="true"></scroll-view>
scroll-top滚动视图的垂直偏移量(像素)。<scroll-view scroll-top="100"></scroll-view>
scroll-left滚动视图的水平偏移量(像素)。<scroll-view scroll-left="50"></scroll-view>
scroll-with-animation是否开启滚动动画,值为 truefalse<scroll-view scroll-with-animation="true"></scroll-view>
bindscroll绑定滚动事件的处理函数。<scroll-view bindscroll="onScroll"></scroll-view>

3. <swiper>

  • 作用:实现轮播图,支持自动播放、手动切换。
属性说明示例
indicator-dots是否显示指示点(小圆点),值为 truefalse<swiper indicator-dots="true"></swiper>
autoplay是否自动播放,值为 truefalse<swiper autoplay="true"></swiper>
interval自动播放间隔时间,单位为毫秒。<swiper interval="3000"></swiper>
circular是否采用循环模式,值为 truefalse<swiper circular="true"></swiper>
current当前显示的轮播项的索引。<swiper current="0"></swiper>
bindchange绑定轮播项变化的事件处理函数。<swiper bindchange="onSwiperChange"></swiper>

4. <navigator>

  • 作用:用于页面跳转。
属性说明示例
url跳转的目标路径,可以是小程序内的页面路径或外部 URL。<navigator url="/pages/detail/detail">Go to Detail</navigator>
open-type操作类型,支持 navigateredirectreLaunchswitchTab<navigator url="/pages/index/index" open-type="redirect">Go to Home</navigator>
hover-class设置点击时的样式。<navigator hover-class="hover">Go to Home</navigator>
target设置目标,_self 为当前窗口,_blank 为新窗口。<navigator url="https://www.example.com" target="_blank">Open in new window</navigator>

以下是 navigator 组件中 open-type 属性值的总结表:

属性值描述使用场景行为
navigate默认值,跳转到指定页面。需要跳转到新页面,但不关闭当前页面时使用。保留当前页面,跳转到目标页面。
redirect跳转到指定页面并关闭当前页面。跳转到新页面后不再需要当前页面时使用。关闭当前页面,跳转到目标页面。
switchTab跳转到 tabBar 页面。跳转到底部导航(tabBar)页面时使用。切换到 tabBar 页面。
reLaunch关闭所有页面并重新打开指定页面。需要重新启动应用或跳转后清空历史页面时使用。关闭所有页面,打开新页面。
navigateBack返回上一页,支持指定返回的层数。想要返回上一层页面时使用。返回上一层或指定层数的页面。
exit退出当前小程序,返回到微信主界面。需要退出当前小程序并返回到微信界面时使用。退出小程序,返回到微信主界面。
contact打开手机的通讯录。需要用户联系或选择联系方式时使用。打开通讯录界面。
miniProgram跳转到指定的另一个小程序。跨小程序跳转,打开其他小程序时使用。跳转到指定的小程序。

5. <text>

  • 作用:显示文本内容,类似于 HTML 中的 <span>
属性说明示例
selectable是否支持文本选择,值为 truefalse<text selectable="true">Selectable Text</text>
decode是否对文本进行 HTML 解码,值为 truefalse<text decode="true">Text with <em>HTML</em></text>

6. <image>

  • 作用:用于显示图片。
属性说明示例
src图片路径,可以是本地路径或网络地址。<image src="/images/pic.jpg"></image>
mode图片裁剪或缩放的模式,常见值有 scaleToFillaspectFitaspectFill 等。<image src="/images/pic.jpg" mode="aspectFit"></image>
lazy-load是否启用图片懒加载,值为 truefalse<image src="/images/pic.jpg" lazy-load="true"></image>
bindload图片加载成功时触发的事件处理函数。<image src="/images/pic.jpg" bindload="onLoad"></image>

7. <button>

  • 作用:用于触发操作或跳转。
属性说明示例
type按钮类型,常见值有 primarywarn<button type="primary">Submit</button>
size按钮的大小,值为 mininormal<button size="mini">Small Button</button>
disabled是否禁用按钮,值为 truefalse<button disabled="true">Disabled</button>
bindtap绑定点击事件的处理函数。<button bindtap="onClick">Click Me</button>

8. <icon>

  • 作用:显示图标,支持多种内置图标类型。
属性说明示例
type图标的类型,常见值有 successinfowarn 等。<icon type="success" size="30" color="#00ff00"></icon>
size图标的大小,单位是 px,如 30<icon type="success" size="30" color="#00ff00"></icon>
color图标的颜色。<icon type="success" size="30" color="#ff0000"></icon>

9. <form>

  • 作用:表单容器。
属性说明示例
bindsubmit提交表单时触发的事件。<form bindsubmit="onSubmit"> <input name="username" placeholder="Enter your name"/> </form>
report-submit是否上报表单的提交事件,默认 false<form report-submit="true"> <input name="username" placeholder="Enter your name"/> </form>

10. <input>

  • 作用:输入框。
属性说明示例
type输入框类型,常见值有 textnumberpassword 等。<input type="text" placeholder="Enter your name"/>
placeholder输入框的占位文本。<input placeholder="Enter text"/>
value输入框的值。<input value="{{inputValue}}"/>
disabled是否禁用输入框,值为 truefalse<input disabled="true"/>

rpx(响应式像素)

rpx 是微信小程序专用的响应式单位,适合多种屏幕尺寸的适配。它使得页面在不同尺寸的设备上显示效果相对一致。

  • 1rpx = 屏幕宽度 / 750。
  • 屏幕宽度:指的是微信小程序屏幕的实际宽度,可以在不同设备上动态适配。

如果屏幕宽度为 375px(iPhone 6的宽度),那么 1rpx = 375px / 750 = 0.5px

  • 适配性强:rpx 可以根据设备屏幕宽度自动缩放,因此不需要单独针对不同的设备进行调整。
  • 页面自适应:使用 rpx 单位时,开发者不需要担心页面在不同屏幕上的显示效果。它根据设备的宽度自动调整元素大小,减少开发者的工作量。

示例

1
2
3
4
5
.container {
width: 750rpx; /* 宽度 750rpx,适应屏幕的 100% */
height: 500rpx; /* 高度 500rpx */
font-size: 30rpx; /* 字体大小 30rpx */
}

wx:for 的基本用法:

基本用法
使用 wx:for="{{array}}" 循环渲染数组数据,默认变量 index(索引)和 item(当前项)。

1
<view wx:for="{{array}}">{{index}}: {{item}}</view>

自定义变量名
通过 wx:for-itemwx:for-index 修改默认变量名。

1
2
3
<view wx:for="{{list}}" wx:for-item="user" wx:for-index="id">
{{id}}: {{user.name}}
</view>

wx:key 的作用
提升渲染性能,为每项指定唯一标识符(如 id*this)。

1
<view wx:for="{{array}}" wx:key="id">{{item.name}}</view>

嵌套循环
多层循环时,自定义变量名以避免冲突。

1
2
3
4
5
6
<view wx:for="{{nestedList}}" wx:for-item="group">
<text>{{group.title}}</text>
<view wx:for="{{group.items}}" wx:for-item="item" wx:for-index="idx">
{{idx}}: {{item}}
</view>
</view>

条件渲染
wx:ifwx:elif 结合使用,进行条件渲染。

1
<view wx:for="{{array}}" wx:if="{{index % 2 === 0}}">偶数项: {{item}}</view>

动态更新数组
使用 this.setData() 更新数据,触发视图更新。

1
2
3
4
updateList() {
const newArray = [...this.data.array, 4];
this.setData({ array: newArray });
}

常见问题

  • Key 冲突:未设置 wx:key 或重复的 key 会导致渲染错误。
  • 空数组处理:通过 wx:if 处理空数组情况。
  • 事件传参:通过 data-* 传递当前项数据。
1
2
3
<view wx:for="{{array}}" bindtap="onItemTap" data-index="{{index}}" data-item="{{item}}">
{{item}}
</view>
1
2
3
4
onItemTap(e) {
const index = e.currentTarget.dataset.index;
const item = e.currentTarget.dataset.item;
}

wx:if 和 hiddlen 二者之间的区别

wx:if : 当条件为 true 的时候,结构会显示出来,但是如果这个为 false 的时候,就不会展示出来结构,通过移除或者新增节点的方式来实现

1
<view wx:if="true"> wx:if 的显示与隐藏 </view>

hidden : 当条件为 true 的时候结构隐藏起来,false的时候会展示出来,无论是true还是false都会有结构渲染出来,因为是通过 css 的 display 来实现的这个功能

1
<view hidden="true"> hidden 的显示与隐藏 </view>

一个小程序的生命周期函数由应用生命周期,页面生命周期,组件生命周期三个部分组成

应用生命周期

应用生命周期函数需要在app.js文件中的APP()方法中进行定义

APP()方法必须要在app.js文件中被执行,主要用来注册微信小程序

应用生命周期函数主要由onLaunch,onShow,onHide 三个函数组成

1.onLaunch

  • 触发时机:小程序初始化时触发,整个小程序只会触发一次。
  • 常用场景:可以在这里做一些初始化的工作,比如获取用户信息、初始化全局数据等。

2.onShow

  • 触发时机:小程序启动或从后台进入前台时触发。
  • 常用场景:当小程序从后台返回时,如果你需要刷新页面数据或做一些更新操作,可以在这里进行。

3.onHide

  • 触发时机:小程序从前台进入后台时触发。
  • 常用场景:可以在这里做一些清理操作或者暂停某些任务,比如停止播放视频、暂停计时等。
1
2
3
4
5
6
7
8
9
10
11
App({
onLaunch: function () {
console.log('小程序初始化');
},
onShow: function () {
console.log('小程序进入前台');
},
onHide: function () {
console.log('小程序进入后台');
}
});

页面生命周期

页面生命周期函数需要在Page()方法中进行定义

微信小程序的页面生命周期函数主要包括:

  1. onLoad
  • 触发时机:页面加载时触发。页面初始化时会调用一次 onLoad,并且页面的 query 参数会作为 onLoad 函数的参数传入。
  • 常用场景:可以在这里获取页面所需的参数,或者执行一些页面初始化的操作,如请求数据、设置页面标题等。
1
2
3
4
5
Page({
onLoad: function (options) {
console.log('页面加载,获取的参数是:', options);
}
});
  1. onShow
  • 触发时机:页面显示时触发。每次页面从后台返回前台时都会触发 onShow
  • 常用场景:可以在这里做页面的刷新操作,或者做一些页面显示前的逻辑处理,比如更新页面数据。
1
2
3
4
5
Page({
onShow: function () {
console.log('页面显示');
}
});
  1. onReady
  • 触发时机:页面初次渲染完成时触发。该函数只会触发一次,页面初次加载完成后会立即触发。
  • 常用场景:可以在这里进行一些只需要执行一次的操作,比如初始化某些复杂的组件、执行动画等。
1
2
3
4
5
Page({
onReady: function () {
console.log('页面渲染完成');
}
});
  1. onHide
  • 触发时机:页面被隐藏时触发。例如,当用户离开当前页面,进入后台或者跳转到其他页面时触发。
  • 常用场景:可以在这里清理一些资源,或者停止一些任务,比如停止播放音视频、暂停定时器等。
1
2
3
4
5
Page({
onHide: function () {
console.log('页面隐藏');
}
});
  1. onUnload
  • 触发时机:页面卸载时触发。页面销毁时会调用一次,比如用户返回上一页或者页面被销毁时触发。
  • 常用场景:可以在这里做一些资源释放的工作,比如清理定时器、关闭连接等。
1
2
3
4
5
Page({
onUnload: function () {
console.log('页面卸载');
}
});
  1. onPullDownRefresh
  • 触发时机:用户下拉刷新时触发。
  • 常用场景:可以在这里请求最新的数据,刷新页面内容。需要调用 wx.stopPullDownRefresh() 来停止刷新动画。
1
2
3
4
5
6
Page({
onPullDownRefresh: function () {
console.log('用户下拉刷新');
wx.stopPullDownRefresh();
}
});
  1. onReachBottom
  • 触发时机:页面触底时触发。当页面滑动到底部时,会触发此函数,适合用来实现分页加载或无限滚动功能。
  • 常用场景:可以用来加载更多数据,通常与分页请求数据配合使用。
1
2
3
4
5
Page({
onReachBottom: function () {
console.log('页面触底');
}
});
  1. onShareAppMessage
  • 触发时机:用户点击右上角分享按钮时触发。
  • 常用场景:可以在这里定制用户分享的信息,比如分享的内容、标题、图片等。
1
2
3
4
5
6
7
8
Page({
onShareAppMessage: function () {
return {
title: '自定义分享标题',
path: '/pages/home/home'
};
}
});

小结

  • onLoad:页面加载时调用,传入参数。
  • onShow:页面显示时调用,每次进入页面都会触发。
  • onReady:页面初次渲染完成时调用。
  • onHide:页面隐藏时调用。
  • onUnload:页面销毁时调用。
  • onPullDownRefresh:用户下拉刷新时调用。
  • onReachBottom:页面触底时调用。
  • onShareAppMessage:用户点击右上角分享时调用。

tabBar 页面之间相互切换,页面不会被销毁

点击左上角的返回的按钮,会返回上一个页面,会销毁当前的页面

小程序AP介绍:

123456

loading提示框;

showLoading 与 hideLoading 必须要一起使用

1
2
3
4
5
6
// 显示提示框
wx.showLoading({
title: '数据加载中...',
mask:true // 当这个mask为 true 的时候,就没有办法进行再次点击这个页面的一切东西,相当于添加了一个膜层
// 想要关闭这个加载的动画 loading 必须要使用 wx.hideLoading()
})

模态提示框:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
wx.showModal({
title: '提示',
content: '是否要删除',
complete: (res) => {
if (res.cancel) {
wx.showToast({
title: '已取消',
icon:'error',
duration:1000
})
}
if (res.confirm) {
// 消息提示框
wx.showToast({
title: '删除成功',
icon:'success',
duration: 2000
})
}
}
})

本地存储信息(不需要进行转换数据的格式,直接就可以使用):

12124124124

异步API:(后面的后缀就是Sync)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
setStrage(){
// 存出本地数据
wx.setStorageSync('appdata',{number:1,addnumber:10})
},
getStrage(){
// 获取相对应的本地数据
const data = wx.getStorageSync('appdata')
console.log(data);
},
removeStrage(){
// 删除指定的存储的数据
wx.removeStorageSync('appdata')
},
clearStrage(){
wx.clearStorageSync() // 删除所有的本地存储的数据
}

在小程序中实现页面之间的跳转,采用的就是有两种方法;

1.声明式导航: navigator 组件

2.编程式导航:使用微信小程序中的提供的API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
wx.navigateTo({
// 跳转的是非tabBar的页面,保留原来的页面,不会销毁原来的页面
url: '/pages/set/set'
})

wx.redirectTo({
// 跳转到非 tabBar 页面,不会保留原来的页面,会销毁原来的页面
url: '/pages/set/set',
})

wx.switchTab({
// 跳转的是tabBar页面,不能跳转到除了tabBar页面以外的其他页面
url: '/pages/index/index',
})

wx.reLaunch({
// 关闭所有的页面,然后跳转到其他页面,所有页面都ok
url: '/pages/set/set'
})


// 关闭当前页面,返回上一个页面或者返回多级页面
// 默认返回的是上一级页面
// data 可以设置返回的是第几级的页面
wx.navigateBack({
data:1
})

跳转到其他页面时若需要传递参数:

1
2
3
onLoad(opoptions){
// options 就是从路由那里传递的对象数据
}

页面向上拉动加载数据:

1
2
3
4
5
6
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {

},

停止刷新操作:

1
wx.stopPullDownRefresh()

组件化开发:

有公共组件和局部组件两种组件形式

公共组件需要再app.json中去注册就可以全局使用

局部组件要到局部页面的.json文件中注册使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Component({

/**
* 组件的属性列表
*/
properties: {

},

/**
* 组件的初始数据
*/
data: {

},

/**
* 组件的方法列表
*/
methods: {

}
})

插槽

当创建一个自定义组件时,可以在 WXML 文件中定义 <slot> 标签来作为占位符。

1
2
3
4
<!-- 子组件的 WXML -->
<view class="container">
<slot></slot>
</view>

在上述例子中,<slot> 标签就是一个默认插槽,它表示一个可以被填充内容的位置。

单个插槽与多个插槽

  • 单个插槽:每个自定义组件默认情况下支持一个未命名的插槽。如果需要使用多个插槽,则需使用具名插槽。

  • 多个插槽:通过给 <slot> 标签添加 name 属性,可以定义多个具名插槽。这样,父组件可以根据插槽的名字向不同的位置传递内容。

  •  options:{
        // 启用多slot支持
        multipleSlots:true
     }
    
    1
    2
    3
    4
    5
    6
    7
    8

    ```html
    <!-- 子组件的 WXML -->
    <view class="container">
    <slot name="header"></slot>
    <slot></slot> <!-- 默认插槽 -->
    <slot name="footer"></slot>
    </view>

使用插槽

在父组件中引用自定义组件时,可以直接在自定义组件标签内放置内容,这些内容将被插入到子组件的 <slot> 标签所在的位置。

1
2
3
4
5
6
<!-- 父组件的 WXML -->
<custom-component>
<view slot="header">这是头部内容</view>
<view>这是默认插槽内容</view>
<view slot="footer">这是尾部内容</view>
</custom-component>

插槽的作用域

插槽的内容是在父组件的作用域内渲染的,这意味着插槽内的数据绑定表达式会访问父组件的数据。而子组件内部的数据则不会影响到插槽内容的渲染。

插槽的默认内容

1
2
3
4
5
6
<!-- 子组件的 WXML -->
<view class="container">
<slot>
这是默认内容,当父组件没有提供内容时显示。
</slot>
</view>
1
2
3
4
5
6
7
8
<!-- card.wxml -->
<view class="card">
<slot name="header"></slot>
<view class="content">
<slot></slot>
</view>
<slot name="footer"></slot>
</view>
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- page.wxml -->
<card>
<view slot="header">
<image class="avatar" src="https://example.com/avatar.png"></image>
<view class="username">张三</view>
</view>
<view>
<text>这里是卡片的内容</text>
</view>
<view slot="footer">
<button>点击按钮</button>
</view>
</card>

在组件中设置样式时只能使用class类选择器来使用

在组件化开发中,父级文件中的样式不会影响到子级文件组件中的样式,除了如果父级文件中使用到标签选择器以及全局样式中设置到了标签选择器的情况下,会影响到组件中的样式

在组件的js文件中,还可以设置组件样式隔离效果

1
2
3
4
5
6
7
8
9
options:{
// 组件样式隔离
styleIsolation:'apply-shared',
styleIsolation:'isolated',
styleIsolation:'page-apply-shared',
styleIsolation:'page-isolated',
styleIsolation:'page-shared',
styleIsolation:'shared',
}

使用observers监听数据变化

当你需要监听一个或多个数据字段的变化时,可以在组件的定义中添加observers字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
Component({
properties: {
num1: Number,
num2: Number,
},
observers: {
'num1, num2': function(newNum1, newNum2) {
this.setData({
sum: newNum1 + newNum2,
});
}
},
});

在这个例子中,当num1或者num2的数据发生变化时,观察器(observer)会自动计算它们的和并将结果存储到sum中。

1
2
3
4
5
6
7
8
9
10
11
12
Component({
data: {
someObject: {
subfield: '',
},
},
observers: {
'someObject.subfield': function(subfield) {
console.log('subfield changed:', subfield);
}
},
});

这里,每当someObject.subfield被更新时,观察器就会触发并打印新的值。

深度监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Component({
data: {
someObject: {
nestedField: {
deeperField: '',
},
},
},
observers: {
'someObject.**': function(newVal) {
console.log('someObject 内部发生了变化', newVal);
}
},
});

注意事项与最佳实践

  • 性能问题:避免创建过多不必要的监听器,因为每个监听器都会增加额外的计算开销。只监听那些真正需要监控的数据。
  • 引用类型数据监听:当监听的对象是引用类型(如对象或数组)时,要注意对象的引用是否发生了变化。observers监听的是对象的引用,而不是对象内部属性的变化。如果需要监听对象内部属性的变化,可以使用深度监听或手动触发。
  • 避免循环引用:在监听器回调函数中不要再次修改被监听的数据,以防止循环引用和无限循环触发监听器。
  • 生命周期管理:确保在页面或组件销毁时取消监听以防止内存泄漏。可以在onUnload生命周期方法中取消监听。
  • 兼容性observers从基础库版本2.6.1开始支持。因此,在使用前请确认你的小程序基础库版本是否符合要求。

进一步扩展

除了上述基本的监听方式,你还可以结合其他技术手段来增强数据监听的能力。例如,利用Object.defineProperty方法实现类似Vue的watch功能,这可以提供更加灵活的数据监听方案

子组件传递数据到父组件:

子组件:

1
2
3
4
5
6
methods: {
load_data_father() {
// 使用下面的形式传递数据到父组件中
this.triggerEvent('data',this.data.name)
}
}

父组件:(自定义事件.bind:data)(data是和上面传递过来的一样)

1
2
3
data( event ) {
// 通过event.detail 就是传递到父组件的数据
},

在父组件中对子组件的上面添加id选择器或者类选择器来绑定子组件,之后使用:

1
2
3
const data =  this.selectComponent('.class')
console.log(data.data);
// 可以获取到子组件中的数据以及方法

可以获取到子组件的全部数据以及方法

组件的生命周期:

  1. created:组件实例刚刚被创建好时触发。此时还不能调用setData方法,通常只应该用于给组件的this添加一些自定义属性字段
  2. attached:在组件实例进入页面节点树时触发。此时,this.data已被初始化为组件的当前值,可以进行绝大多数初始化工作,例如发起异步请求获取初始数据
  3. ready:在渲染线程被初始化完成之后触发。这表明组件已经在视图层布局完成后准备就绪,可以进行涉及视图的操作
  4. moved:当组件实例被移动到节点树的另一个位置时触发。这个生命周期不太常用,因为它通常只会在非常具体的情况下被触发
  5. detached:在组件实例被从页面节点树移除时触发。这是清理资源的好时机,比如取消定时器或者关闭网络连接
  6. error:每当组件方法抛出错误时触发。这对于捕获和处理运行时异常非常有用

除了上述基本的生命周期函数外,还有一些与页面状态相关的生命周期函数,它们可以在pageLifetimes字段中定义:

  • show:当组件所在的页面被展示时触发。
  • hide:当组件所在的页面被隐藏时触发。
  • resize:当组件所在的页面尺寸发生变化时触发。

这些生命周期函数可以直接定义在Component构造器的第一级参数中,也可以在lifetimes字段内进行声明(推荐的方式,其优先级最高)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Component({
lifetimes: {
created: function() {
// 在组件实例刚刚被创建时执行
},
attached: function() {
// 在组件实例进入页面节点树时执行
},
ready: function() {
// 在渲染线程被初始化已经完成
},
moved: function() {
// 在组件实例被移动到节点树另一个位置时执行
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
},
error: function(error) {
// 每当组件方法抛出错误时执行
}
},
pageLifetimes: {
show: function() {
// 页面被展示时执行
},
hide: function() {
// 页面被隐藏时执行
},
resize: function(size) {
// 页面尺寸变化时执行
}
}
});

小程序中的behaviors可以存储重复的一些代码逻辑,对于组件

首先要创建一个文件在components文件夹下面,把一些重复的逻辑放入其中,接着需要使用时在其他组件中导入,列如:

1
behaviors:[behavior]

如果组件内部方法与储存重复文件中的方法相同,就近原则,采用组件中的代码

在父组件中传递样式给子组件,可以这样使用:

父组件:

1
<view extend_classname="my-class"></view>

子组件:

1
<view class="extend_classname"></view>
1
externalClasses:['extend_classname'],// 接收的是样式类

如果在使用外部样式类的情况下,子组件中的一些样式可能会影响到预期的效果,建议使用css的!important来提高权重

小程序分包:

1341415234523532

yyyysdgsd

主包不用进行配置,需要配置分包:

12412412412412

1
2
3
4
5
6
7
8
9
10
"subPackages": [
{
"root": "",
"name": "",
"pages": [
""
],
"independent": true // 独立分包,可以独立与主包进行运行
}
]

分包规则:分包预下载处理:

1
2
3
4
5
6
"preloadRule": {
"pages/index/index":{
"network": "all",
"packages": [""] // 里面写的就是分包的名字
}
}

999999999

微信小程序中获取微信的头像操作:

需要在按钮处添加open-type="chooseAvatar" 这个属性,接着绑定bindchooseavatar这个点击事件,就可以实现这个功能

微信小程序中获取微信用户名字的操作:

12412412412412412

微信小程序转发功能的实现:

1242143256766435

微信小程序分享到朋友圈的操作;

在js文件中添加onShareTimeline事件进行监听操作,必须要设置这个才能进行分享到朋友圈

在这个监听事件中,可以自定义设置分享时的标题,图片等一些数据上去

微信小程序中对于获取用户的手机号的方法:

1111111111111111111111111111111111111111111111111111

微信小程序客服功能的实现:

3333333333333333333333333

框架接口:getApp()

在小程序中,app.js文件中可以通过保存全局数据,全局方法等操作.其中需要注意的就是在其他页面获取全局数据,方法的时候需要调用getApp()的方法,还要进行接收放回的数据进行使用

1
2
3
4
5
6
7
8
9
10
App({
globalData:{
data:'数据',
token:''
},

settoken(newtoken){
this.globalData.token = newtoken
}
})
1
2
3
4
5
6
7
8
const data = getApp()

Page({

changetoken(){
data.settoken('evunirevernivenfvjev')
}
})

页面间进行通信:

999999999999999999999

不同页面间进行传递数据,可以使用一个包来使用:

124712947124124

设置自定义导航栏:

在app.json或者page.json文件中,配置naxigationStyle属性为custom,即为自定义导航栏,这样的话就会移除默认的导航栏,只会保留右上角上面的胶囊按钮


3. 小程序生命周期

小程序的生命周期分为两类:小程序生命周期页面生命周期

3.1 小程序生命周期

小程序的生命周期函数包括:

  • **onLaunch()**:小程序初始化时触发(全局)。
  • **onShow()**:小程序每次显示时触发。
  • **onHide()**:小程序每次隐藏时触发。
  • **onError()**:小程序发生错误时触发。

示例:

1
2
3
4
5
6
7
8
9
10
11
App({
onLaunch() {
console.log('小程序启动');
},
onShow() {
console.log('小程序显示');
},
onHide() {
console.log('小程序隐藏');
}
});

3.2 页面生命周期

页面的生命周期函数包括:

  • **onLoad()**:页面加载时触发。
  • **onShow()**:页面显示时触发。
  • **onHide()**:页面隐藏时触发。
  • **onUnload()**:页面卸载时触发。

示例:

1
2
3
4
5
6
7
8
9
10
11
Page({
onLoad() {
console.log('页面加载');
},
onShow() {
console.log('页面显示');
},
onHide() {
console.log('页面隐藏');
}
});

javascript4. 小程序路由与导航

小程序中的页面跳转使用不同的 API 来完成。小程序页面没有传统的 URL 路由,而是通过页面路径来进行导航。

4.1 页面跳转

  • **wx.navigateTo()**:跳转到一个新的页面,保留当前页面。
  • **wx.redirectTo()**:关闭当前页面并跳转到目标页面。
  • **wx.switchTab()**:切换到 TabBar 页面,关闭其他非 TabBar 页面。

示例:

1
2
3
4
5
6
7
8
9
// 跳转到新的页面
wx.navigateTo({
url: '/pages/detail/detail?id=1'
});

// 跳转到 TabBar 页面
wx.switchTab({
url: '/pages/home/home'
});

4.2 返回上一页

  • **wx.navigateBack()**:返回上一页。
  • **wx.reLaunch()**:关闭所有页面并打开指定页面。

5. 小程序API

微信小程序提供了大量的 API,涵盖了网络请求、设备信息、存储、媒体等功能。

5.1 网络请求

  • **wx.request()**:用于发送 HTTP 请求。

示例:

1
2
3
4
5
6
wx.request({
url: 'https://api.example.com/data',
success: function(res) {
console.log(res.data);
}
});

5.2 本地存储

  • **wx.setStorageSync()**:同步设置本地存储。
  • **wx.getStorageSync()**:同步获取本地存储。
  • **wx.removeStorageSync()**:同步删除本地存储。

示例:

1
2
3
wx.setStorageSync('key', 'value');
const value = wx.getStorageSync('key');
console.log(value);

5.3 地理位置

  • **wx.getLocation()**:获取用户的地理位置。

示例:

1
2
3
4
5
6
wx.getLocation({
type: 'wgs84',
success: function(res) {
console.log(res.latitude, res.longitude);
}
});

5.4 图片选择

  • **wx.chooseImage()**:用户从相册或相机选择图片。

示例:

1
2
3
4
5
wx.chooseImage({
success: function(res) {
console.log(res.tempFilePaths);
}
});

5.5 支付功能

  • **wx.requestPayment()**:发起支付请求。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
wx.requestPayment({
timeStamp: '1234567890',
nonceStr: 'abcdefg',
package: 'prepay_id=wx201411101639507cbf6ff8f6f8bcd019d6c',
signType: 'MD5',
paySign: 'abcdefg',
success(res) {
console.log('支付成功', res);
},
fail(err) {
console.log('支付失败', err);
}
});

6. 小程序权限管理

小程序需要访问设备的一些敏感数据或功能,如位置、相机、麦克风等,需要请求用户授权。

6.1 请求授权

使用 wx.authorize() 来请求用户授权。

示例:

1
2
3
4
5
6
7
8
9
wx.authorize({
scope: 'scope.userInfo',
success() {
console.log('授权成功');
},
fail() {
console.log('授权失败');
}
});

6.2 查看授权状态

使用 wx.getSetting() 来获取用户的授权状态。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
wx.getSetting({
success(res) {
if (!res.authSetting['scope.userInfo']) {
wx.authorize({
scope: 'scope.userInfo',
success() {
// 用户同意授权
}
});
}
}
});

微信小程序创建过程:

一.项目基本配置

重新调整目录:在project.config.json文件中配置

1
"miniprogramRoot": "src/",

调整目录为像vue项目一样的形式,有助于管理项目

在根目录下进行终端配置

1
npm init -y

接着配置"setting"里面的

1
2
3
4
5
6
7
"packNpmManually": true,
"packNpmRelationList": [
{
"packageJsonPath": "/package.json",
"miniprogramNpmDistDir": "/src"
}
],

接着如果要使用组件库:(终端操作)

1
npm i @vant/weapp

接着就是点击工具中的构建npm,如果报错,重启项目

如果要配置sass,接着在project.config.json文件夹中"setting"的配置

1
2
3
"useCompilerPlugins": [
"sass"
],

在项目中的css文件夹名字的后缀改为scss

对于项目中可以设置一些代码格式统一化,可以添加下面的配置:

配置详细插件:

  1. 在【项目的根目录】下创建 .vscode 文件夹,注意:文件夹名字前面带 . 点❗

  2. .vscode 文件夹下,创建 settings.json,用来对安装的插件属性进行设置,具体属性设置从下面复制即可

    • 注意:.vscode 文件夹下的 settings.json 文件只对当前一个项目生效
  3. 在【项目的根目录】下创建 .prettierrc 文件,进行 Prettier 代码规则的配置,规则从下面复制即可

  4. 为了让 Prettier 配置项在微信开发者工具生效,需要在微信开发者工具中也安装 Prettier 扩展插件。

➡️ .vscode/settings.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
{
// 保存文件时是否自动格式化
"editor.formatOnSave": true,

// ---------------- 以下是 [ prettier ] 插件配置 ----------------

// 指定 javascript、wxss、scss、less、json、jsonc 等类型文件使用 prettier 进行格式化
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},

"[wxss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},

"[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},

"[less]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},

"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},

"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},

// Prettier 的一个配置项,用于指定哪些文件类型需要使用 Prettier 进行格式化
"prettier.documentSelectors": ["**/*.wxml", "**/*.wxss", "**/*.wxs"],

// ---------------- 以下是 [ WXML - Language Service ] 插件配置 ----------------

// wxml 文件使用 prettier 进行格式化
"[wxml]": {
// "qiu8310.minapp-vscode" 是 WXML - Language Service 插件提供的配置项
// 此插件主要是针对小程序的 wxml 模板语言,可以自动补全所有的组件、组件属性、组件属性值等等

// 如果是 VsCode 需要开启这个配置
"editor.defaultFormatter": "qiu8310.minapp-vscode"

// 如果是微信小程序,需要开启这个配置,通过 esbenp.prettier-vscode 对代码进行格式化
// "editor.defaultFormatter": "esbenp.prettier-vscode"
},

// 创建组件时使用的 css 后缀
"minapp-vscode.cssExtname": "scss", // 默认 wxss,支持 styl sass scss less css

// 指定 WXML 格式化工具
"minapp-vscode.wxmlFormatter": "prettier",
// 配置 prettier 代码规范
"minapp-vscode.prettier": {
"useTabs": false,
"tabWidth": 2,
"printWidth": 80
},

// ---------------- 以下是 [ 微信小程序助手-Y ] 插件配置 ----------------

// 新增、删除小程序页面时,是否自动同步 app.json pages 路径配置,默认为 false
"wechat-miniapp.sync.delete": true,
// 设置小程序页面 wxss 样式文件的扩展名
"wechat-miniapp.ext.style": "scss",

// ---------------- 其他配置项 ----------------

// 配置语言的文件关联,运行 .json 文件时写注释
// 但在 app.json 和 page.json 中无法使用
"files.associations": {
"*.json": "jsonc"
}
}

➡️ .prettierrc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"semi": false,
"singleQuote": true,
"useTabs": false,
"tabWidth": 2,
"printWidth": 180,
"trailingComma": "none",
"overrides": [
{
"files": "*.wxml",
"options": { "parser": "html" }
},
{
"files": "*.wxss",
"options": { "parser": "css" }
},
{
"files": "*.wxs",
"options": { "parser": "babel" }
}
]
}
配置项配置项含义
“semi”: false不要有分号
“singleQuote”: true使用单引号
“useTabs”: false缩进不使用 tab,使用空格
“tabWidth”: 2tab缩进为4个空格字符
“printWidth”: 80一行的字符数,如果超过会进行换行,默认为80
“trailingComma”: “none”尾随逗号问题,设置为none 不显示 逗号
“overrides”: []overrides 解析器:默认情况下,Prettier 会根据文件文件拓展名推断要使用的解析器

二.项目组件模块化

把一些消息提示框的代码进行封装,便于维护以及减少代码的冗余,如果其他的文件中需要这个方法,必须要导入这个包装好的js文件(在别的文件中需要import导入这个)

原来的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
wx.showToast({
title: '消息提示框', // 提示的内容
icon: 'success', // 提示图标
duration: 2000, // 提示的延迟时间
mask: true // 是否显示透明蒙层,防止触摸穿透
})

wx.showModal({
title: '提示', // 提示的标题
content: '您确定执行该操作吗?', // 提示的内容
confirmColor: '#f3514f', // 确定按钮的样式
// 接口调用结束的回调函数(调用成功、失败都会执行)
complete({ confirm, cancel }) {
if (confirm) {
console.log('用户点击了确定')
return
}

if (cancel) {
console.log('用户点击了取消')
}
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
export const toast = ({ title = '加载中...', icon = 'none', duration = 2000, mask = true }={}) => {
wx.showToast({
title,
icon,
duration,
mask
})
}

const model = (option = {}) => {
return new Promise((resolve) => {
const basicoption = {
title: '提示',
content: '您确定执行该操作吗?',
confirmColor: '#f3514f'
}
const totaloption = Object.assign({}, basicoption, option)
wx.showModal({
...totaloption,
complete({ confirm, cancel }) {
confirm && resolve(true)
cancel && resolve(false)
}
})
})
}

export { toast, model }

还可以把把这个方法挂载在全局上面进行操作(全局import一下就OK了,后面就可以不再导入了)

1
2
3
4
5
6
7
8
9
10
11
12
const toast = ({ title = '加载中...', icon = 'none', duration = 2000, mask = true }={}) => {
wx.showToast({
title,
icon,
duration,
mask
})
}
wx.toast = toast
wx.model = model

export { toast, model }

对同步存储的数据进行封装:(大概的封装框架)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//同步存储
export const UseStorage = {
setStorage: (kay, value) => {
try {
wx.setStorage(kay, value)
} catch (error) {
console.error(`存储指定 ${key} 数据发生错误:`, error)
}
},
getStorage: (key) => {
try {
const value = wx.getStorage(key)
if (value) {
return value
}
} catch (error) {
console.error(`获取指定 ${key} 数据发生错误:`, error)
}
},
removeStorage: (key) => {
try {
wx.removeStorage(key)
} catch (error) {
console.error(`移除指定 ${key} 数据发生错误:`, error)
}
},
clearStorage: () => {
try {
wx.clearStorage()
} catch (error) {
console.error(`清空本地存储时发生错误:`, error)
}
}
}

异步存储的数据进行封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 异步存储数据
export const setStorageSync = (key, data) => {
return new Promise((resolve) => {
wx.setStorageSync({
key,
data,
complete(response) {
resolve(response)
}
})
})
}
export const getStorageSync = (key) => {
return new Promise((resolve) => {
wx.getStorageSync({
key,
complete(response) {
resolve(response)
}
})
})
}
export const removeStorageSync = (key) => {
return new Promise((resolve) => {
wx.removeStorageSync({
key,
complete(response) {
resolve(response)
}
})
})
}
export const clearStorageSync = () => {
return new Promise((resolve) => {
wx.clearStorageSync({
complete(response) {
resolve(response)
}
})
})
}

三.请求封装- request 方法

对于使用 wx.request()有注意点:(有坑)

在使用 wx.request 发送网络请求时。

只要成功接收到服务器返回,无论statusCode是多少,都会进入 success 回调

开发者根据业务逻辑对返回值进行判断。

什么时候会有 fail 回调函数 ?

一般只有网络出现异常、请求超时等时候,才会走 fail 回调

基础配置形式1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 封装request请求方法

// 创建一个 WxRequest 类
// 通过类来封装

class WxRequest {
basic_option = {
baseURL: '',
url: '',
method: 'GET',
// 请求头
header: {
'Content-type': 'application/json' // 设置数据的交互格式
},
timeout: 60000 // 小程序默认超时时间是 60000,一分钟
}
constructor(Option) {
Object.assign(this.basic_option, Option)
}

request(option) {
option.url = option.baseURL + option.url
// 合并请求参数
option = { ...this.basic_option, ...option }
return new Promise((resolve, reject) => {
wx.request({
...option,
success: (result) => {
resolve(result)
},
fail: (err) => {
reject(err)
}
})
})
}
}

const instance = new WxRequest({
// 基础配置
})

export default instance

基础配置形式2:

1
2
3
4
instance.get('请求地址', '请求参数', '请求配置')
instance.delete('请求地址', '请求参数', '请求配置')
instance.post('请求地址', '请求参数', '请求配置')
instance.put('请求地址', '请求参数', '请求配置')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 封装request请求方法

// 创建一个 WxRequest 类
// 通过类来封装

class WxRequest {
basic_option = {
baseURL: '',
url: '',
method: 'GET',
// 请求头
header: {
'Content-type': 'application/json' // 设置数据的交互格式
},
timeout: 60000 // 小程序默认超时时间是 60000,一分钟
}
constructor(Option) {
Object.assign(this.basic_option, Option)
}
request(option) {
option.url = option.baseURL + option.url
// 合并请求参数
option = { ...this.basic_option, ...option }
return new Promise((resolve, reject) => {
wx.request({
...option,
success: (result) => {
resolve(result)
},
fail: (err) => {
reject(err)
}
})
})
}
// 封装get,post,put,delete等方法
get(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'get' }, config))
}
post(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'post' }, config))
}
put(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'put' }, config))
}
delete(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'delete' }, config))
}
}

const instance = new WxRequest({
// 基础配置
})

export default instance

基础配置形式3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// 封装request请求方法

// 创建一个 WxRequest 类
// 通过类来封装

class WxRequest {
// 定义相应拦截器与请求拦截器
interceptors = {
// 请求拦截器
request: (config) => {
return config
},
// 响应拦截器
response: (response) => {
return response
}
}
basic_option = {
baseURL: '',
url: '',
method: 'GET',
// 请求头
header: {
'Content-type': 'application/json' // 设置数据的交互格式
},
timeout: 60000 // 小程序默认超时时间是 60000,一分钟
}
constructor(Option) {
Object.assign(this.basic_option, Option)
}
request(option) {
option.url = option.baseURL + option.url
// 合并请求参数
option = { ...this.basic_option, ...option }
// 请求拦截器
option = this.interceptors.request(option)
return new Promise((resolve, reject) => {
wx.request({
...option,
success: (result) => {
// 响应拦截器
const message_result = Object.assign({}, result, { config: option })
resolve(this.interceptors.response(message_result))
},
fail: (err) => {
// 响应拦截器
const message_err = Object.assign({}, err, { config: option })
reject(this.interceptors.response(message_err))
}
})
})
}
// 封装get,post,put,delete等方法
get(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'GET' }, config))
}
post(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'POST' }, config))
}
put(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'PUT' }, config))
}
delete(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'DELETE' }, config))
}
}

const instance = new WxRequest({
// 基础配置
})

export default instance

基础配置形式4:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import { UseStorage } from './storage.js'

class WxRequest {
constructor(Option) {
Object.assign(this.basic_option, Option)
}

interceptors = {
request: (config) => {
const { token } = UseStorage.getStorage('token')
if (token) {
config.header.Authorization = `Bearer ${token}`
}
return config
},
response: async (response) => {
const { issuccess, data } = response
if (!issuccess) {
wx.showToast({
title: '网络异常请重试',
icon: 'error'
})
return response
}
switch (data.code) {
case 200:
return data
case 401:
await wx.showModal({
content: '鉴权失败,请重新登录',
showCancel: false,
title: '提示'
})
UseStorage.clearStorage()
wx.navigateTo({
url: '/pages/login/index'
})
return Promise.reject(response)
default:
wx.showToast({
title: '网络异常请重试',
icon: 'error'
})
return Promise.reject(response)
}
}
}

basic_option = {
baseURL: '',
url: '',
method: 'GET',
header: {
'Content-type': 'application/json'
},
timeout: 60000
}

request(option) {
option.url = option.baseURL + option.url
option = { ...this.basic_option, ...option }
option = this.interceptors.request(option)
return new Promise((resolve, reject) => {
wx.request({
...option,
success: (result) => {
const message_result = Object.assign({}, result, { config: option, issuccess: true })
resolve(this.interceptors.response(message_result))
},
fail: (err) => {
const message_err = Object.assign({}, err, { config: option, issuccess: false })
reject(this.interceptors.response(message_err))
}
})
})
}

get(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'GET' }, config))
}
post(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'POST' }, config))
}
put(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'PUT' }, config))
}
delete(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'DELETE' }, config))
}
}

const instance = new WxRequest({
baseURL: '',
timeout: 2000
})

export default instance

基础配置形式5:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { UseStorage } from './storage.js'

class WxRequest {
constructor(Option) {
Object.assign(this.basic_option, Option)
}

queue = []
timeid

interceptors = {
request: (config) => {
const { token } = UseStorage.getStorage('token')
if (token) {
config.header.Authorization = `Bearer ${token}`
}
return config
},
response: async (response) => {
const { issuccess, data } = response
if (!issuccess) {
wx.showToast({
title: '网络异常请重试',
icon: 'error'
})
return response
}
switch (data.code) {
case 200:
return data
case 401:
await wx.showModal({
content: '鉴权失败,请重新登录',
showCancel: false,
title: '提示'
})
UseStorage.clearStorage()
wx.navigateTo({
url: '/pages/login/index'
})
return Promise.reject(response)
default:
wx.showToast({
title: '网络异常请重试',
icon: 'error'
})
return Promise.reject(response)
}
}
}

basic_option = {
baseURL: '',
url: '',
method: 'GET',
header: {
'Content-type': 'application/json'
},
timeout: 60000
}

request(option) {
this.timeid && clearTimeout(this.timeid)
option.url = option.baseURL + option.url
option = { ...this.basic_option, ...option }
this.queue.length === 0 && wx.showLoading()
this.queue.push('request')
option = this.interceptors.request(option)
return new Promise((resolve, reject) => {
wx.request({
...option,
success: (result) => {
const message_result = Object.assign({}, result, { config: option, issuccess: true })
resolve(this.interceptors.response(message_result))
},
fail: (err) => {
const message_err = Object.assign({}, err, { config: option, issuccess: false })
reject(this.interceptors.response(message_err))
},
complete: () => {
this.queue.pop()
this.queue.length === 0 && this.queue.push('request')
timeid = setTimeout(() => {
this.queue.pop()
this.queue.length === 0 && wx.hideLoading()
}, 1000)
clearTimeout(timeid)
}
})
})
}

get(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'GET' }, config))
}
post(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'POST' }, config))
}
put(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'PUT' }, config))
}
delete(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'DELETE' }, config))
}

// 用来处理并发请求
all(...request_list) {
return Promise.all(request_list)
}
}

const instance = new WxRequest({
baseURL: '',
timeout: 2000
})

export default instance

基础配置形式6:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import { UseStorage } from './storage.js'

class WxRequest {
constructor(Option) {
Object.assign(this.basic_option, Option)
}

queue = []
timeid

interceptors = {
request: (config) => {
const { token } = UseStorage.getStorage('token')
if (token) {
config.header.Authorization = `Bearer ${token}`
}
return config
},
response: async (response) => {
const { issuccess, data } = response
if (!issuccess) {
wx.showToast({
title: '网络异常请重试',
icon: 'error'
})
return response
}
switch (data.code) {
case 200:
return data
case 401:
await wx.showModal({
content: '鉴权失败,请重新登录',
showCancel: false,
title: '提示'
})
UseStorage.clearStorage()
wx.navigateTo({
url: '/pages/login/index'
})
return Promise.reject(response)
default:
wx.showToast({
title: '网络异常请重试',
icon: 'error'
})
return Promise.reject(response)
}
}
}

basic_option = {
baseURL: '',
url: '',
method: 'GET',
header: {
'Content-type': 'application/json'
},
timeout: 60000,
isloading: true // 是否显示loading动画
}

request(option) {
this.timeid && clearTimeout(this.timeid)
option.url = option.baseURL + option.url
option = { ...this.basic_option, ...option }
if (option.isloading && option.method !== 'upload') {
this.queue.length === 0 && wx.showLoading()
this.queue.push('request')
}
option = this.interceptors.request(option)
return new Promise((resolve, reject) => {
if (option.method === 'upload') {
wx.uploadFile({
...option,
success: (result) => {
result.data = JSON.parse(rsult.data)
const message_result = Object.assign({}, result, { config: option, issuccess: true })
resolve(this.interceptors.response(message_result))
},
fail: (err) => {
const message_err = Object.assign({}, err, { config: option, issuccess: false })
reject(this.interceptors.response(message_err))
}
})
} else {
wx.request({
...option,
success: (result) => {
const message_result = Object.assign({}, result, { config: option, issuccess: true })
resolve(this.interceptors.response(message_result))
},
fail: (err) => {
const message_err = Object.assign({}, err, { config: option, issuccess: false })
reject(this.interceptors.response(message_err))
},
complete: () => {
if (option.isloading) {
this.queue.pop()
this.queue.length === 0 && this.queue.push('request')
timeid = setTimeout(() => {
this.queue.pop()
this.queue.length === 0 && wx.hideLoading()
}, 1000)
clearTimeout(timeid)
}
}
})
}
})
}

get(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'GET' }, config))
}
post(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'POST' }, config))
}
put(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'PUT' }, config))
}
delete(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'DELETE' }, config))
}
upload(url, filePath, name = 'file', config = {}) {
return this.request(Object.assign(Object.assign({ url, filePath, name, method: 'upload' }, config)))
}

// 用来处理并发请求
all(...request_list) {
return Promise.all(request_list)
}
}

const instance = new WxRequest({
baseURL: '',
timeout: 2000
})

export default instance

四.配置环境变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 获取小程序帐号信息
const { miniProgram } = wx.getAccountInfoSync()

// 获取小程序当前开发环境
// develop 开发版, trial 体验版, release 正式版
const { envVersion } = miniProgram

let env = {
baseURL: 'https://gmall-prod.atguigu.cn'
}

switch (envVersion) {
case 'develop':
env.baseURL = 'https://gmall-prod.atguigu.cn'
break

case 'trial':
env.baseURL = 'https://gmall-prod.atguigu.cn'
break

case 'release':
env.baseURL = 'https://gmall-prod.atguigu.cn'
break

default:
env.baseURL = 'https://gmall-prod.atguigu.cn'
break
}

export { env }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import { UseStorage } from './storage.js';
import { env } from './environment.js';

class WxRequest {
constructor(Option) {
Object.assign(this.basic_option, Option);
}

queue = [];
timeid;

interceptors = {
request: (config) => {
const { token } = UseStorage.getStorage('token');
if (token) {
config.header.Authorization = `Bearer ${token}`;
}
return config;
},
response: async (response) => {
const { issuccess, data } = response;
if (!issuccess) {
wx.showToast({
title: '网络异常请重试',
icon: 'error'
});
return Promise.reject(response); // 修改:返回被拒绝的Promise
}
switch (data.code) {
case 200:
return data;
case 401:
await wx.showModal({
content: '鉴权失败,请重新登录',
showCancel: false,
title: '提示'
});
UseStorage.clearStorage();
wx.navigateTo({
url: '/pages/login/index'
});
return Promise.reject(response); // 修改:返回被拒绝的Promise
default:
wx.showToast({
title: '网络异常请重试',
icon: 'error'
});
return Promise.reject(response); // 修改:返回被拒绝的Promise
}
}
};

basic_option = {
baseURL: '',
url: '',
method: 'GET',
header: {
'Content-Type': 'application/json' // 修改:正确的内容类型名称
},
timeout: 60000,
isloading: true // 是否显示loading动画
};

request(option) {
this.timeid && clearTimeout(this.timeid);
option.url = option.baseURL + option.url;
option = { ...this.basic_option, ...option };
if (option.isloading && option.method !== 'upload') {
this.queue.length === 0 && wx.showLoading();
this.queue.push('request');
}
option = this.interceptors.request(option);
return new Promise((resolve, reject) => {
if (option.method === 'upload') {
wx.uploadFile({
...option,
success: (result) => {
result.data = JSON.parse(result.data); // 修改:拼写错误
const message_result = Object.assign({}, result, { config: option, issuccess: true });
resolve(this.interceptors.response(message_result));
},
fail: (err) => {
const message_err = Object.assign({}, err, { config: option, issuccess: false });
reject(this.interceptors.response(message_err));
}
});
} else {
wx.request({
...option,
success: (result) => {
const message_result = Object.assign({}, result, { config: option, issuccess: true });
resolve(this.interceptors.response(message_result));
},
fail: (err) => {
const message_err = Object.assign({}, err, { config: option, issuccess: false });
reject(this.interceptors.response(message_err));
},
complete: () => {
if (option.isloading) {
this.queue.pop();
this.queue.length === 0 && (this.timeid = setTimeout(() => { // 修改:修复逻辑错误
wx.hideLoading();
}, 1000));
}
}
});
}
});
}

get(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'GET' }, config));
}
post(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'POST' }, config));
}
put(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'PUT' }, config));
}
delete(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'DELETE' }, config));
}
upload(url, filePath, name = 'file', config = {}) {
return this.request(Object.assign({ url, filePath, name, method: 'upload' }, config));
}

// 用来处理并发请求
all(...request_list) {
return Promise.all(request_list);
}
}

const instance = new WxRequest({
baseURL: env.baseURL,
timeout: 2000
});

export default instance;