uniapp vue3 开发个人小程序记录

项目前端知识点
uniapp特性
1. UniApp 简介
UniApp 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一次代码,可发布到多个平台(如微信小程序、H5、App等),极大地提高了开发效率。
2. uniapp原理
跨平台编译 UniApp 的一大特点是能够将一套代码同时编译成多个平台的应用(如 H5、小程序、App 等)。这种能力主要是通过一套自定义的编译器来实现的,编译器会根据目标平台的不同,将通用的 Vue.js 代码转换成相应的平台代码。例如,H5 平台可能需要生成标准的 HTML、CSS 和 JavaScript 文件,而小程序平台则需要生成符合微信小程序规范的 WXML、WXSS 和 JS 文件。
平台适配层 为了使应用能够在不同的平台上运行,UniApp 引入了一个平台适配层,这个层负责处理不同平台之间的差异。例如,某些 API 可能在微信小程序中可用但在 H5 平台上不可用,适配层会确保在调用这些 API 时能够正确地分发到相应的平台实现。
自定义组件和插件 UniApp 提供了一套丰富的组件库和插件系统,允许开发者使用通用的组件和插件来快速构建应用。这些组件和插件通常都经过优化,可以在多个平台上高效运行。
扩展的生命周期等
这里不得不提到taro Taro 与 UniApp 的比较 虽然 Taro 和 UniApp 都是用于跨平台开发的框架,但它们在设计理念和技术实现上存在一些差异:
基础框架:Taro 基于 React,而 UniApp 基于 Vue.js。 语法和组件化:Taro 使用 JSX 和 React Hooks,而 UniApp 使用 Vue.js 的模板语法和组件化开发。 平台支持:Taro 支持微信小程序、H5、React Native 等平台,而 UniApp 支持微信小程序、H5、App 等平台。 生态和社区:两者都有活跃的社区和丰富的插件生态,但各自的侧重点不同。 选择哪个框架取决于你的个人偏好、团队的技术栈以及具体的项目需求。
3. UniApp中的生命周期与Vue.js生命周期的区别和联系
UniApp 是基于 Vue.js 的一套框架,用于开发跨平台的应用(如微信小程序、H5、App 等)。因此,UniApp 的生命周期钩子在很大程度上继承了 Vue.js 的生命周期概念,但为了适应更多的平台特性,也做了一些扩展。
Vue.js 生命周期 Vue.js 的生命周期钩子包括:
beforeCreate:实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。 created:实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。 beforeMount:在挂载开始之前被调用:相关的 render 函数首次被调用。 mounted:实例被挂载后调用,这时 el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果根实例挂载到了一个文档内的元素上,当 mounted 被调用时 vm.$el 也在文档内。 beforeUpdate:数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。 updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。 beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。 destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
UniApp 生命周期 UniApp 的生命周期钩子与 Vue.js 类似,但也有一些特定于 UniApp 的扩展,例如:
onLaunch:全局生命周期钩子,应用启动时触发一次。 onShow:应用前台显示时触发。 onHide:应用后台隐藏时触发。 onError:全局错误监听函数。 onPageNotFound:页面不存在监听函数。 onPullDownRefresh:下拉刷新事件处理。 onReachBottom:触底事件处理。 onShareAppMessage:用户点击右上角菜单的“转发”按钮时触发此事件。 区别与联系 联系:UniApp 的生命周期钩子大部分都是基于 Vue.js 的生命周期设计的,因此它们之间有很多相似之处。例如,onLaunch 对应于 Vue.js 的 created 阶段,onShow 和 onHide 分别对应于 Vue.js 中的 mounted 和 beforeDestroy 的某种扩展。
区别:UniApp 添加了一些特定于小程序或跨平台开发的功能,比如 onPullDownRefresh 和 onReachBottom,这些是专门为处理用户交互(如下拉刷新、滚动到底部)而设计的。此外,UniApp 还引入了全局的生命周期钩子,如 onLaunch,用于处理应用级别的生命周期事件。
总结来说,UniApp 继承并扩展了 Vue.js 的生命周期机制,以更好地适应跨平台开发的需求。
4. uniapp常用api及组件
常用 API
网络请求
uni.request: 发起网络请求。
uni.uploadFile: 上传文件。
uni.downloadFile: 下载文件。
数据存储
uni.setStorage: 设置本地数据缓存。
uni.getStorage: 获取本地数据缓存。
uni.removeStorage: 删除本地数据缓存。
uni.clearStorage: 清空本地数据缓存。
设备信息
uni.getSystemInfo: 获取系统信息。
uni.getDeviceInfo: 获取设备信息。
界面操作
uni.showToast: 显示消息提示框。
uni.showModal: 显示模态弹窗。
uni.showLoading: 显示 loading 提示框。
uni.hideLoading: 隐藏 loading 提示框。
导航
uni.navigateTo: 保留当前页面,跳转到应用内的某个页面。
uni.redirectTo: 关闭当前页面,跳转到应用内的某个页面。
uni.switchTab: 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。
uni.reLaunch: 关闭所有页面,打开到应用内的某个页面。
媒体
uni.chooseImage: 从本地相册选择图片或使用相机拍照。
uni.previewImage: 预览图片。
uni.saveImageToPhotosAlbum: 保存图片到本地相册。
常用组件
基础组件
<view>: 视图容器。
<text>: 文本。
<image>: 图片。
<button>: 按钮。
<input>: 输入框。
<textarea>: 多行输入框。
布局组件
<scroll-view>: 可滚动视图区域。
<swiper>: 轮播图组件。
<swiper-item>: 轮播图项目。
表单组件
<form>: 表单。
<label>: 标签。
<picker>: 选择器。
<radio>: 单选框。
<checkbox>: 复选框。
导航组件
<navigator>: 导航链接。
<tabbar>: 底部标签栏。
其他组件
<map>: 地图。
<canvas>: 画布。
<rich-text>: 富文本。
<progress>: 进度条。
这些只是 UniApp 中一部分常用 API 和组件,UniApp 还提供了更多丰富的功能和组件,可以根据具体需求查阅官方文档以获取更多信息。
vue3新特性
- 更好的性能 编译优化:Vue 3 的编译器对静态节点进行了更好的优化,减少了不必要的渲染和更新。 更快的虚拟 DOM 更新:Vue 3 使用了新的 Proxy 代理机制来跟踪响应式数据的变化,而不是 Vue 2 中使用的 Object.defineProperty。这使得 Vue 3 在处理复杂数据结构时性能更好。
- 响应式系统 Proxy 代理:Vue 3 使用 Proxy 代理来实现响应式系统,这比 Vue 2 中的 Object.defineProperty 更强大和灵活。Proxy 可以监听到对象的所有属性变化,而不仅仅是属性值的变化。 Composition API:Vue 3 引入了 Composition API,这是一种新的组合式 API,它允许你更灵活地组织和重用逻辑代码。Composition API 通过 setup 函数来定义组件的行为,使得逻辑复用更加直观和自然。
- 新的组件 Teleport:Teleport 允许你将组件的内容渲染到 DOM 中的任何位置,而不仅仅是父组件的子节点。这对于模态框、提示框等场景非常有用。 Fragments:Vue 3 支持多个根节点的组件,即 Fragments。这使得组件的结构更加清晰和灵活。
- Composition API setup 函数:setup 函数是 Composition API 的入口点,你可以在这里定义组件的状态、方法和生命周期钩子。 生命周期钩子:Vue 3 将生命周期钩子改名为更具描述性的名称,如 onMounted、onUnmounted 等。这些钩子可以在 setup 函数中使用。 响应式状态:使用 ref 和 reactive 创建响应式状态。ref 用于基本类型的数据,而 reactive 用于对象和数组。 计算属性和监听器:computed 和 watch 函数用于创建计算属性和监听状态变化。
- 更小的体积 Tree-shaking 支持:Vue 3 的模块化设计使得你可以只导入你需要的部分,从而减小最终打包后的体积。Vue 3 的核心库体积更小,更适合现代前端应用。
- 更好的 TypeScript 支持 内置 TypeScript 支持:Vue 3 对 TypeScript 的支持更为友好,提供了更好的类型推断和类型定义,使得在 TypeScript 项目中使用 Vue 3 更加方便。
- 其他改进 更好的错误处理:Vue 3 改进了错误处理机制,使得调试更加容易。 自定义渲染器:Vue 3 提供了自定义渲染器的能力,这使得开发者可以更容易地将 Vue 3 应用扩展到不同的渲染环境。 这些新特性和改进使得 Vue 3 成为一个更强大、更灵活、更高效的前端框架。
技术栈
uniapp vue3 vant-ui tailwindCSS axios mysql Express
开发工具
微信公众平台:获取开发AppID Hbuilder:用于代码开发以及打包到小程序开发者工具进行测试 微信开发者工具:Hbuilder进行相关配置,指定微信开发者平台在本地的可运行地址,打包后就可以直接唤起微信开发者工具进行测试。
相关配置文件
pages.json
页面配置 包含path、style、globalStyle、tabBar等属性 path:配置页面路由 style:配置导航条、使用的第三方组件或自定义组件等
//使用vant-ui
{
"pages": [
//pages数组中第一项表示应用启动页
{
"path": "pages/index/index",
"style": {}
},
{
"path": "pages/myBill/myBill",
"style": {
"navigationBarTextStyle": "black",
"usingComponents": {
"van-datetime-picker": "/wxcomponents/vant/dist/datetime-picker/index",
"list-item": "/pages/components/listItem"
}
}
},
],
"globalStyle": {
//全局设置自定义导航栏
"navigationStyle": "custom"
},
"tabBar": {
//配置底部tabBar
"color": "#cdcdcd",
"selectedColor": "#6C9EF3",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "static/images/home-icon.png",
"selectedIconPath": "static/images/home-active-icon.png",
"text": "tabBarText"
}, ...]
}
}
manifest.json
配置各端AppID,这边填入自己的微信AppID,在微信开发平台上下载微信开发者工具,后期就可以使用微信开发者工具进行测试。
相关API
获取导航栏高度
//获取屏幕导航栏高度
getHeight() {
if (wx.canIUse('getMenuButtonBoundingClientRect')) {
let sysInfo = wx.getSystemInfoSync(); //状态栏的高度
this.statusBarHeight = sysInfo.statusBarHeight;
// 胶囊位置信息
let rect = wx.getMenuButtonBoundingClientRect();
this.menuButtonRect = JSON.parse(JSON.stringify(rect));
// 导航栏高度
let navBarHeight = (rect.top - sysInfo.statusBarHeight) * 2 + rect.height;
this.navBarHeight = navBarHeight;
// 自定义导航栏的高度
this.height = sysInfo.statusBarHeight + navBarHeight;
} else {
wx.showToast({
title: '您的微信版本过低,界面可能会显示不正常',
icon: 'none',
duration: 4000
});
}
}
判断是否在可视范围
observer = uni.createIntersectionObserver(this);
observer.relativeTo('.mybill-page').observe('.amount-main', (res) => {
// 判断当前元素是否在可视范围,从而显示或隐藏某个元素
if (res.intersectionRatio > 0 && this.showAmountSimple) {
this.showAmountSimple = false;
} else if (!res.intersectionRatio > 0 && !this.showAmountSimple) {
this.showAmountSimple = true;
}
})
数据存储
// 存取
uni.setStorage({
key: 'mini_token',
data: result?.data?.token,
success: function() {
recall.headers.Authorization = `Bearer ${result?.data?.token}`
axios(recall)
}
})
// 读取
uni.getStorageSync('mini_token')
登录
// 下面这段代码是在登录401的时候,重新获取用户token并进行接口的重新请求
// 微信登录方式有静默登录,不需要用户授权,而已获取用户openID
uni.login({
provider: 'weixin',
success: async function(res) {
const result = await wxLogin({
code: res?.code
})
if (result?.data?.token) {
uni.setStorage({
key: 'mini_token',
data: result?.data?.token,
success: function() {
recall.headers.Authorization = `Bearer ${result?.data?.token}`
axios(recall)
}
})
}
}
});
选择本地文件并上传
function chooseMediaHandle() {
uni.chooseMedia({
success: (res) => {
// 返回选定照片的本地文件路径列表 tempFiles
const token = uni.getStorageSync('mini_token');
Promise.all(res.tempFiles?.map(file => {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: server?.apiUrl + '/api/diary/uploadFile',
filePath: file?.tempFilePath,
name: 'media',
header: {
'Authorization': `Bearer ${token}`
},
success: (uploadFileRes) => {
resolve(JSON.parse(uploadFileRes.data)?.data)
},
fail: (err) => {
reject(false)
}
});
})
})).then(res => {
showEditDiary.value = true
mediaList.value = [...mediaList.value, ...res]
}).catch(error => {
// 如果任何一个请求被拒绝,这里的代码会被执行
console.error(error);
})
},
fail: (err) => {
// 选择图片失败的回调
console.log('选择图片失败', err);
}
});
}
路由跳转
//跳转
uni.navigateTo({
url: `路由地址`,
})
//返回到上一页
uni.navigateBack({
delta: 1
})
相关组件
webview
webview只能引入绑定的域名,不能随便引入第三方网站;可通过微信公众平台进行域名授权;微信有很多限制,小程序也不能唤起第三方小程序,除非授权过...
<web-view :src="src"></web-view>
后端知识点
就是一些CRUD的接口,写点sql语句
1. 登录流程
// 1. 根据前端获取到的code,以及开发者AppID、secret获取openID
// 2. 查询数据库中是否存在当前用户,存在则jwt签名获取token,后续请求接口就通过token去请求
router.get('/login', async function (req, res, next) {
let url = `https://api.weixin.qq.com/sns/jscode2session?appid=${appId}&secret=${appSecret}&js_code=${req.query?.code}&grant_type=authorization_code`
const data = await getRequestJSON(url);
if (data?.openid) {
const response = await getRequestJSON('/api/user', { openid: data?.openid }, 'post')
if (response?.wxOpenid) {
// 验证成功
res.send(Result.success({ token: jwt.sign({ wxOpenid: response?.wxOpenid }, secret, { expiresIn: '7200s' }) }))
} else res.send(Result.fail('登录失败,请稍候再试~'))
} else res.send(Result.fail('登录失败,请稍候再试~'))
})