前言
四年前,uni-app备受欢迎,多数公司均要求掌握其技能。我曾利用uni-app快速开发公司的微信小程序,自接触小程序至交付仅耗时一个月,效率显著。然而,四年后的今天,uni-app在招聘市场上已鲜少提及。鉴于2023年了,uniapp发展的怎么样了?相关问题的回答及相关生态更新速度,我决定重新回顾下微信小程序的原生开发。
1. 起步
1. 注册账户
登录微信公众平台,进行账户注册
2. 获取APPID
在微信公众平台中,选择 开发 -> 开发管理 -> 开发设置 -> 开发者ID
3. 安装开发者工具
下载地址:developers.weixin.qq.com/miniprogram…
4. 新建小程序项目
打开开发者工具,选择小程序,点击新建
5. 填写项目信息
-
填写项目名称
-
选择目录
-
填写AppID
-
选择不使用云服务
6. 目录结构
pages:描述各自页面,页面由四部分组成 js(逻辑交互)、JSON(配置)、WXML(模板)、WXSS(样式)
app.js:小程序逻辑
app.json:小程序公共配置
app.wxss:小程序公共样式表
2. 全局配置 app.json
1. 全局配置
小程序根目录下的 app.json 文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。
{
"pages": [
"pages/index/index",
"pages/logs/logs"
],
"window": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "Weixin",
"navigationBarBackgroundColor": "#ffffff"
},
"style": "v2",
"componentFramework": "glass-easel",
"sitemapLocation": "sitemap.json",
"lazyCodeLoading": "requiredComponents"
}
pages:配置当前小程序所有页面路径
window:定义小程序所有页面的顶部背景颜色,文字颜色定义等
如果只想用自己定义的样式或者其他组件库,将 app.json 中的 “style”: “v2” 去除,小程序的新版基础组件强行加上了许多样式,难以覆盖
每一个小程序页面也可以使用同名 .json 文件来对本页面的窗口表现进行配置,页面中配置项会覆盖 app.json 的 window 中相同的配置项
2. 配置底部导航
在app.json中添加tabBar选项
"tabBar":{
"color":"#ccc",
"selectedColor": "#f00",
"list":[
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "images/menus.png",
"selectedIconPath": "images/menus-active.png"
},
{
"pagePath": "pages/logs/logs",
"text": "记录",
"iconPath": "images/movie.png",
"selectedIconPath": "images/movie-active.png"
}
]
},
iconPath:icon存放路径,大小限制为 40kb,建议尺寸为 81px * 81px。只能使用本地图片(在项目根目录下新建images文件夹)
3. 入口文件 app.js
app.js是小程序的入口文件,调用 App 方法注册小程序实例
1. 应用生命周期
// app.js
App({
onLaunch (options) {
// 小程序初始化
},
onShow (options) {
// 小程序启动或切前台
},
onHide () {
// 小程序切后台
},
onError (msg) {
console.log(msg)
},
})
onLaunch、onShow函数参数options包含了小程序的启动信息。方便针对不同途径的打开方式,进行不同的业务处理
path:打开小程序的页面路径
query:打开小程序的页面参数
scene:打开小程序的场景值
referrerInfo:当场景为由从另一个小程序或公众号或App打开时,返回此字段
2. 全局变量
在app.js中定义全局变量
App({
globalData: {
global_name: '全局变量'
}
})
在其他页面使用,使用getApp获取全局实例
var app = getApp()
Page({
onLoad(){
console.log(app.globalData.global_name)
},
})
4. WXML 模板语法
WXML模板语法类似于vue的模板语法,从前缀 v- 替换为 wx:
1. 数据绑定
普通写法:
<view>{{ message }}</view>
组件属性:
<view id="item-{{id}}"> </view>
Boolean类型:
<checkbox checked="{{false}}"> </checkbox>
2. 列表渲染
使用wx:for循环,使用item获取子项
<view wx:for="{{array}}">
{{item}}
</view>
3. 条件渲染
- wx:if
类似于v-if,使用wx:作为前缀
<view wx:if="{{length > 5}}"> </view>
<view wx:elif="{{length > 1}}"> </view>
<view wx:else> </view>
- hidden
类似于vue的v-show
<view hidden="{{condition}}"> True </view>
如果频繁切换,hidden更好,如果运行的时候条件不太可能改变,比如登录完之后的状态,那么就用wx:if
4. block
渲染一个包含多节点的结构块 block 最终不会变成真正的 dom 元素
<block wx:for="{{[1, 2, 3]}}">
<view> {{index}}: </view>
<view> {{item}} </view>
</block>
5. WXSS 样式
1. 尺寸单位
rpx:可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有 750 个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。
建议: 开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。
2. 样式导入
使用@import语句可以导入外联样式表,只支持相对路径,用 ; 表示语句结束
/** app.wxss **/
@import "common.wxss";
3. 内联样式
静态的样式统一写到 class 中。style 接收动态的样式,在运行时会进行解析,尽量避免将静态的样式写进 style 中,以免影响渲染速度
<view style="color: {{eleColor}}; font-size: {{eleFontsize}}"></view>
4. 条件样式
使用三元运算符选择类,注意单双引号的使用:
<text class='{{rating > 9 ?"red":"movie-rate"}}'>{{item.rating}}</text>
5. 选择器
类选择器 .class
id选择器 #id
元素选择器 element
伪元素选择器 ::before ::after
6. JavaScript 逻辑
1. 页面数据
页面使用 Page() 进行构造
Page({
data: {
logs: []
},
handleTap() {
this.setData({
logs:[1,2,3]
})
},
})
-
使用this.setData进行数据修改
-
为了提高性能,每次设置的数据不应超过1024kB
2. 页面生命周期
属性 | 说明 |
---|---|
onLoad | 页面创建时执行 |
onShow | 页面出现在前台时执行 |
onReady | 页面首次渲染完毕时执行 |
onHide | 页面从前台变为后台时执行 |
onUnload | 页面销毁时执行 |
onPullDownRefresh | 触发下拉刷新时执行 |
onReachBottom | 页面触底时执行 |
onShareAppMessage | 页面被用户分享时执行 |
onPageScroll | 页面滚动时执行 |
onResize | 页面尺寸变化时执行 |
onTabItemTap | 当前是 tab 页时,点击 tab 时触发 |
3. 路由跳转
// index/index.js
Page({
wxNavAction: function () {
wx.navigateTo({
url: './new-page'
})
}
})
属性 | 说明 |
---|---|
wx.navigateTo | 保留当前页,跳转到新页面,但是不能跳到 tabbar 页面 |
wx.redirectTo | 关闭当前页,跳转到新页面,但是不能跳转到 tabbar 页面 |
wx.navigateBack | 页面返回 |
wx.switchTab | 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面 |
wx.reLaunch | 重启动 |
4. 事件绑定
使用 bind 关键字,如 bind:tap、bind:input、bind:change 等
<view bind:tap="tapName"> Click me! </view>
Page({
tapName: function(event) {
console.log(event)
}
})
bind:tap与bindtap没有本质的区别,只是写法上的不同。bind:tap自基础库版本 2.8.1 起,开始全面支持。可以使用wx.getAppBaseInfo()获取到当前小程序运行的基础库的版本号
如果想阻止事件冒泡,使用catch:tap
进行绑定
5. 双向绑定
使用model: 前缀进行双向绑定。如果输入框的值被改变了, this.data.value 也会同时改变
<input model:value="{{value}}" />
限制:尚不能 data 路径,如
<input model:value="{{ a.b }}" />
6. 事件传参
通过标签自定义属性获取数据
<button bind:tap="handleTap" data-value="100">按钮</button>
handleTap(e){
console.log(e.currentTarget.dataset) //{value:100}
},
7. 模块化
可以将一些公共的代码抽离为一个单独的js文件
模块通过module.exports对外暴露接口,例如utils/util.js
module.exports = {
formatTime
}
通过require引用
// logs.js
const util = require('../../utils/util.js')
7. 常见组件
1. view
代替了div标签,hover-class指定按下去的样式类
<view hover-class="hover">块布局</view>
2. text
文本标签,只能嵌套text。user-select 控制文本是否可选
<text user-select="{{true}}">文本</text>
3. image
image 组件默认宽度 320px、高度 240px。使用的时候需设置图片的宽高
<image src="/images/menus.png"></image>
4. swiper
轮播图组件
<swiper indicator-dots="true" autoplay="true" interval="4000" style="height: 400rpx" indicator-color="#fff" indicator-active-color="#07c160">
<swiper-item wx:for="{{images}}">
<image src="{{item.src}}" bind:tap="show" data-id="{{item.id}}"></image>
<view>{{item.title}}</view>
</swiper-item>
</swiper>
属性 | 说明 |
---|---|
indicator-dots | 是否显示面板指示点 |
autoplay | 是否自动切换 |
interval | 自动切换时间间隔 |
indicator-color | 指示点颜色 |
indicator-active-color | 当前选中的指示点颜色 |
5. navigator
导航组件,类似超链接标签
<navigator
target="self"
url="{{ url }}"
open-type="navigate"
>
<van-icon class="van-notice-bar__right-icon" name="arrow" />
</navigator>
open-type跳转方式
属性 | 说明 |
---|---|
navigate | 对应 wx.navigateTo |
redirect | 对应 wx.redirectTo |
switchTab | 对应 wx.switchTab |
reLaunch | 对应 wx.reLaunch |
navigateBack | 对应 wx.navigateBack |
8. 交互
1. 消息提示框
wx.showToast({
title: '成功',
icon: 'success',
duration: 2000
})
2. 模态对话框
wx.showModal({
title: '提示',
content: '这是一个模态弹窗',
success (res) {
if (res.confirm) {
console.log('用户点击确定')
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
9. 模板
模板与组件都是为了实现代码的复用。模板更轻量级些,只负责值的展示。组件可以有自己的处理逻辑
- 定义模板
使用name属性,作为模板的名字
//texta.wxml
<template name="firstTem">
<view>
<text>{{name}}</text>
<text>{{id}}</text>
</view>
</template>
- 使用模板
使用id属性,声明要使用的模板,data属性传入模板所需要的值
<import src='../texta/texta.wxml'/>
<template id="firstTem" data="{...item}"></template>
Page({
data: {
item: {
id: 0,
name: 'this is a template',
}
}
})
10. 自定义组件
在根目录新建components文件夹,鼠标右击选择新建Component
1. 创建自定义组件
- 在 json 文件中进行自定义组件声明
{
"component": true
}
- 在 wxml 文件中编写组件模板,在 wxss 文件中加入组件样式,在 js 文件中写组件逻辑交互
wxml 文件
<view class="inner">
{{innerText}}
</view>
<slot></slot>
wxss 文件
/* 这里的样式只应用于这个自定义组件 */
.inner {
color: red;
}
js 文件
使用 Component() 来注册组件,并提供组件的属性定义、内部数据和自定义方法
Component({
//类似于mixins
behaviors: [],
//相当于vue的props属性,用来接收父组件传来的属性
properties: {
innerText: {
type: String,
value: 'default value',
}
},
//组件内部数据
data: {
someData: {}
},
//用于监听 properties 和 data 的变化
observers: {},
//组件自定义方法
methods: {
myFunction: function () { }
},
//组件生命周期
lifetimes: {},
//组件所在页面的生命周期
pageLifetimes: {}
})
注意:在组件wxss中不应使用ID选择器、属性选择器和标签名选择器
2. 使用自定义组件
在页面的 json 文件中进行引用声明
{
"usingComponents": {
"tag-title":"/components/tagTitle"
}
}
index.wxml
<tag-title inner-text="some text">solt的内容</tag-title>
注意点:
-
所有组件与属性都是小写,以连字符-连接
-
自定义组件和页面所在项目根目录名不能以”wx-“为前缀,否则会报错
3. 组件生命周期
属性 | 说明 |
---|---|
created | 在组件实例刚刚被创建时执行,此时不能调用 setData |
attached | 在组件实例进入页面节点树时执行 |
ready | 在组件在视图层布局完成后执行 |
moved | 在组件实例被移动到节点树另一个位置时执行 |
detached | 在组件实例被从页面节点树移除时执行 |
error | 每当组件方法抛出错误时执行 |
4. 组件所在页面的生命周期
属性 | 说明 |
---|---|
show | 组件所在的页面被展示时执行 |
hide | 组件所在的页面被隐藏时执行 |
resize | 组件所在的页面尺寸变化时执行 |
routeDone | 组件所在页面路由动画完成时执行 |
5. 组件通信
- 父传子 properties
//父组件
<tag-title inner-text="some text"></tag-title>
//子组件
Component({
properties: {
innerText: {
type: String,
value: 'default value',
}
}
})
- 子传父 triggerEvent
//父组件
<component-tag-name bind:myevent="onMyEvent" />
Page({
onMyEvent: function(e){}
})
//子组件
Component({
methods: {
onTap: function(){
this.triggerEvent('myevent', {name:'1'})
}
}
})
- 父调用子 this.selectComponent
// 父组件
Page({
data: {},
getChildComponent: function () {
const child = this.selectComponent('.my-component');
console.log(child)
}
})
在上例中,父组件将会获取 class 为 my-component 的子组件实例对象,即子组件的 this
11. 本地数据缓存
1. 设置本地缓存
try{
// 同步接口立即写入
wx.setStorageSync('key', 'value2')
}catch (e) {
console.log('写入value2发生错误')
}
2. 读取本地缓存
try{
// 同步接口立即返回值
var value2 = wx.getStorageSync('key2')
}catch (e) {
console.log('读取key2发生错误')
}
3. 删除本地缓存
每个小程序的缓存空间上限为10MB,如果当前缓存已经达到10MB,再通过wx.setStorage写入会触发fail回调
1.清除所有缓存
try {
wx.clearStorageSync()
} catch(e) {
// Do something when catch error
}
- 清除特定缓存
try {
wx.removeStorageSync('key')
} catch (e) {
// Do something when catch error
}
12. 请求网络数据
1. 服务器域名配置
小程序后台-开发管理-服务器域名->request合法域名
提示:
-
域名只支持 https协议;
-
域名不能使用 IP 地址(小程序的局域网 IP 除外)或 localhost
2. DNS预解析域名
在小程序启动时,提前解析业务域名
小程序后台-开发管理-服务器域名->DNS预解析域名
3. 网络请求
wx.request({
url: 'https://test.com/api',
method: 'GET',
header: { 'content-type': 'application/json' },// 默认值
data: {}, //请求的参数
success: function (res) {
console.log(res)// 服务器回包信息
},
fail:function(err){
console.log(err)
},
})
13. 项目发布
- 在开发者工具中选择上传
- 在微信公众平台中选择版本管理 -> 开发版本,微信扫描二维码体验小程序
- 如果想让更多的人体验,选择成员管理 -> 添加项目成员
结尾
如果本篇文章比较受欢迎,我将更新更多的微信小程序相关内容
近期文章推荐:
原文链接:https://juejin.cn/post/7336822951273807898 作者:敲代码的彭于晏