前端高手特训 从0到1带你手写一个微信小程序底层框架,小程序是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或者搜一下即可打开应用。也体现了“用完即走”的理念,用户不用关心是否安装太多应用的问题。应用将无处不在,随时可用,但又无需安装卸载。
框架 管理了整个小程序的页面路由,可以做到页面间的无缝切换,并给以页面完整的生命周期。开发者需要做的只是将页面的数据、方法、生命周期函数注册到 框架 中,其他的一切复杂的操作都交由 框架 处理。
wepy支持类似Vue的组件化开发,可以将页面拆分成多个独立的组件,提高代码复用性和开发效率。下面我们通过一个实际的案例来说明组件化开发在wepy中的应用。假设我们有一个小程序项目,其中包含一个商品列表页面和一个商品详情页面。我们可以将商品列表和商品详情抽象成两个组件,并在需要的地方引用它们。首先,我们创建一个名为GoodsList的组件。在src/components目录下创建GoodsList.wpy文件,并编写如下代码:
import axios from 'axios'
const defaultConfig = {
timeout: 5000,
baseURL: '/release/'
}
const axiosInstance = axios.create(defaultConfig)
// 添加请求拦截器
axiosInstance.interceptors.request.use(config => {
return config
}, (err) => {
// 对请求错误做些什么
return Promise.reject(err)
})
// 请求拦截器,内部根据返回值,重新组装,统一管理。
axiosInstance.interceptors.response.use(res => {
console.log('接口详情:',res)
return res
})
export default {
// 封装get
httpGet(url: any, params = {}) {
return axiosInstance.get(url, { params }).then(res => res.data).catch()
},
// 封装post
httpPost(url: any, params = {}) {
return axiosInstance.get(url, { params }).then(res => res.data).catch()
}
}
封装一个openStore(),使用indexedDB.open()方法返回一个 IDBRequest对象,接着将这个对象上的三个事件分别放置进入:onsuccess、onerror、onupgradeneeded。
onsuccess表示打开数据库成功的事件。
onerror表示打开数据库失败的事件。
onupgradeneeded是数据库升级事件,如果版本号更新,并且大于之前的版本号则进行数据库升级,该事件回调里面,会创建我们所需要的对象仓库,类似于关系型数据库中的表的概念。
export default class DB {
private dbName: string // 数据库名称
constructor(dbName: string) {
this.dbName = dbName
}
// 打开数据库
public openStore(storeName: string, keyPath: string, indexs?: Array<string>) {
const request = window.indexedDB.open(this.dbName, 2)
request.onsuccess = (event) => {
console.log('数据库打开成功')
console.log(event)
}
request.onerror = (event) => {
console.log('数据库打开失败')
console.log(event)
}
request.onupgradeneeded = (event) => {
console.log('数据库升级成功')
const { result }: any = event.target
const store = result.createObjectStore(storeName, { autoIncrement: true, keyPath })
if (indexs && indexs.length > 0) {
indexs.map((v: string) => {
store.createIndex(v, v, { unique: true })
})
}
store.transaction.oncomplete = (event: any) => {
console.log('创建对象仓库成功')
}
console.log(event)
}
}
}
在子组件 headerCommon.vue中切换语言时,调用 saveLanguageApi接口,保存当前语言环境到 indexedDB中,并将当前语言包 zhCn或者 en作为参数传递给父组件 App.vue,代码片段如下:
// commonHeader.vue
function handleSelect(e: any) {
if (e === 'zh') {
emit('changeLang', zhCn)
saveLanguage('zh')
} else if (e === 'en') {
emit('changeLang', en)
saveLanguage('en')
}
console.log(e)
}
// Mock接口:保存当前语言环境
function saveLanguage(language: any) {
saveLanguageApi(language).then(res => {
const { success } = res
if (success) {
console.log('保存当前语言包成功')
}
})
}
通过调用 getLanguage接口获取到之前调用存储在 indexedDB中的语言环境,然后赋值给全局组件,代码片段如下:
// headerCommon.vue
// Mock接口:保存当前语言环境
function getLanguage() {
fetchLanguageApi().then(res => {
const { success, result } = res
const { name } = result
if (success) {
if (name === 'zh') {
emit('changeLang', zhCn)
} else if (name === 'en') {
emit('changeLang', en)
}
console.log('获取当前语言环境成功')
}
})
}
getLanguage()
使用@include 指令来引入定义好的样式函数,该函数的三个参数可以根据传入的值来对 flex布局进行自定义,默认值为:column、center、right,在 footerCommon.scss中我们重新自定义了该样式函数,分别传入 row、space-between、flex-start,代码片段如下:
// footerCommon.scss
.common-footer{
border-top: 1px solid rgb(235, 235, 235);
.footer{
@include main-wrapper;
@include layout(row,space-between,flex-start);
padding: 20px 0;
li{
@include layout;
h4{
font-weight: bold;
}
a{
margin-bottom: 10px;
color: rgb(72, 72, 72);
text-decoration: none;
&:hover{
text-decoration:underline;
}
}
}
}
}
订单中心模块会使用到的两个 Mock接口为 saveOrderApi、fetchOrderApi,一个是立即预定,另一个是查询订单列表,具体代码片段如下
// src/api/order/index.ts
const storeName = Object.keys(airbnb.orderObjectStore)[0]
// Mock接口:立即预定
export async function saveOrderApi(params: any) {
const loading = ElLoading.service({
lock: true,
background: 'rgba(0, 0, 0, 0.1)'
})
// 是否存在相同订单Id
const hasOrderId = await new Promise((resolve, reject) => {
airbnb.airbnbDB.getList(storeName).then((res: any) => {
setTimeout(() => {
loading.close()
}, 200)
res && res.filter((item: any) => {
if (item.orderId === params.orderId) { // 存在相同订单Id
resolve(true)
}
})
resolve(false)
})
})
let result: IResultOr
if (hasOrderId) {
result = await new Promise((resolve, reject) => {
resolve({ code: '000001', success: false, message: '数据已存在', result: null })
})
} else {
result = await new Promise((resolve, reject) => {
airbnb.airbnbDB.updateItem(storeName, params).then(res => {
setTimeout(() => {
loading.close()
}, 200)
resolve({ code: '000000', success: true, message: '操作成功', result: null })
})
})
}
return result
}
通过应用这些进阶技巧和最佳实践,可以进一步提升小程序的性能和开发效率,同时优化代码质量,为用户提供更好的使用体验。
![1.png](http://static.itsharecircle.com/231218/8a25ee4c256b2635f57d9933d74f8c23.png)
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传