How to modularize elegantly in small programs? This article will teach you how to elegantly modularize small programs. I hope it will be helpful to you!
This article talks about how to elegantly implement modular processing in WeChat mini programs. Through a condensed summary of some recent development experiences, we will explore some methods that can improve the efficiency of WeChat applet development and reduce mental burden.
First of all, both ES6
and commonJS
are supported in the WeChat mini program. In traditional web projects, I am personally accustomed to using ES6
modular syntax for development.
At the beginning, I also extracted all the common methods in the mini program into separate files, and used export
or export default
to export, and used import
Introduction.
Attention
But! In actual development, the js file of the mini program does not support the introduction of absolute paths ! This means that if you need to introduce a public method into your page, you must use the ../../../xxx/xxx.js
method. When you introduce multiple When it comes to modules, this way of writing will definitely dampen your enthusiasm for development.
Solution
So how do we solve such a long import path? In web projects, we often use path aliases Methods, such as webpack or resolve.alias in vite
to shorten the imported path.
alias: {"@src":path.resolve("src"),
But in the native WeChat applet, although some front-end engineering tools such as gulp or webpack can be used to make some modifications to the applet, as a I hope that the startup process of an open source project does not require too much additional configuration. It is best to use native syntax to implement it.
In the end, I chose to add a new require
method in app.js to introduce the module. In this way, when introducing the module into the page, we only need to use the instance of the app to introduce the module. , so that you can use the relative path to the app.js
file to import the file.
// app.js App({ require(path){ return path } })
Usage method
// 使用基于app.js的相对路径来引入文件,这样就避免了写很多"../" const app = getApp() const upload = app.require("lib/upload")
Of course, this is not particularly convenient, first of allThe code prompt is not perfect. If you use the above method, the prompts for parameters or some return values may not be in place, but the impact will not be big. If I find other better implementation methods in the future, I will write an article to analyze it. Secondly, the modular syntax of commonJS must be used globally, but this is not a big problem.
There is no special modularization method provided in the mini program. The more common method is to extract some methods into separate js files, and then Reintroduction. If you want to avoid a page file whose code is too long, the best way is to componentize it. However, in small programs, writing components is really unpleasant.
The mini program component has its own life cycle, and must be defined in advance in the page json when introduced. Since the component is hung on the shadow root node, if If you want to share styles with the page, such as the global style of colorUI, you also need to write the separate configuration item styleIsolation. The overall development experience is relatively fragmented compared to vue.
Based on some of the above personal opinions, I rarely use components when writing small programs. If I need to extract wxml or js, I usually use the following method.
wxml modularization
In small programs, I usually use template
for abstraction and reuse, 微信小programTEMPLATEdoc Compared with components, templates only extract part of the page, and do not include the extraction of functional parts.
The following is a template I extracted. This is a list item of an article. It does not have any independent function, but the code is very longand is was reused in many pages, so I extracted it. Write the styles using inline styles, so that the same styles are introduced wherever they are introduced.
<!-- 文章列表项 --> <import src='./avatar' /> <template name="post-item"> <view class="margin padding-sm bg-white radius flex shadow " style="position: relative;height: 350rpx;border-radius: 10rpx;"> <!-- 背景蒙版 --> <view style="position: absolute;top: 0;left: 0;width: 100%;height: 100%;border-radius: 10rpx;"> <image style="filter:blur(2px) grayscale(80%) opacity(80%)" lazy-load="{{true}}" src="{{imgList[0]}}" mode="aspectFill"></image> </view> <view style="position: absolute;top: 0;left: 0;width: 100%;height: 100%;background-color: rgba(30, 30, 30, 0.8);border-radius: 10rpx;"> </view> <view style="z-index: 10;width: 100%;" class="text-white"> <!-- 文章标题 --> <view class="text-xl "> <text class="cu-tag margin-right-sm bg-color radius">{{topic}}</text> <text class="text-bold">{{title}}</text> </view> <!-- 文章内容 --> <view class="margin-top-xs text-sm text-cut">{{content}}</view> <view class="flex align-end justify-between margin-top"> <!-- 文章图片 --> <view class="flex align-center"> <view class="margin-xs" style="width: 120rpx;height: 120rpx;" wx:for="{{imgList}}" wx:key="{{index}}" wx:if="{{index < 3}}"> <image class="radius" src="{{item}}" mode="aspectFill"></image> </view> </view> <!-- 浏览量-点赞数 --> <view class="bg-color flex align-center text-white text-sm radius" style="padding: 4px 12px;"> <view class="cuIcon-attention "></view> <view class="margin-left-xs">{{viewNum||0}}</view> <view class="cuIcon-like margin-left"></view> <view class="margin-left-xs">{{favorNum||0}}</view> </view> </view> <!-- 发布时间 --> <view class="margin-top-xs flex align-center text-sm text-gray justify-between padding-lr-xs"> <view class="flex align-center"> <template is="avatar" data="{{size:45,avatarUrl:user.avatarUrl}}" /> <view class="margin-left-xs">{{user.nickName}}</view> </view> <view>{{createTime}}</view> </view> </view> </view> </template>
is attribute to declare which template is used. The data can be passed# The ##data
attribute is passed in. The example here is that I deconstruct the traversed item
and then assign it to it. <!-- 某个页面 -->
<import src='../../template/post-item' />
<template data="{{...item}}" is="post-item" />
js modularity 在小程序中最基本的js模块化就是直接抽离js文件,例如一些全局通用的方法,下面展示一个全局上传方法的封装 当然以上的办法对于通用方法来说很方便,但是对于与 页面操作的逻辑耦合性 很高的一些业务代码,这样子抽离并不方便。 在vue2中我们可以使用mixin的方法模块化代码,在vue3中我们可以使用hook的方式模块化代码,但是在小程序中并没有以上两者的支持,最初我想仿照 vue3的hook 方式进行页面js封装改造,但最终实现的效果不理想,于是选择了实现一个模仿vue2 mixin 的方法来实现模块化。 具体代码其他博主有实现过,因此我就直接拿来使用了,具体代码如下。如果不了解vue中mixin的使用方法的可以自行去官网看文档,这里不做过多介绍。 实现的原理是改造小程序中的Page()函数,小程序的每一个页面都是通过调用 我们通过在Page方法的参数 使用的方法是现在app.js中引入mixin.js 然后我们写一个常规页面的js,业务代码大家不用看,主要关注Page的属性中多了一个mixins选项,而 由于 注意点 但是使用 【相关学习推荐:小程序开发教程】 The above is the detailed content of A brief analysis of how to elegantly implement modularization in small programs?. For more information, please follow other related articles on the PHP Chinese website!// lib/upload.js
// 上传方法
module.exports = async function upload(path) {
return await wx.cloud.uploadFile({
cloudPath: new Date().getTime() + path.substring(path.lastIndexOf(".")),
filePath: path,
})
}
// pages/form/form.js
const app = getApp()
const upload = app.require("lib/upload")
Page({
async submit() {
wx.showLoading({
mask: true,
title: "发布中"
})
const imgList = []
for (let img of this.data.form.imgList) {
const uploadRes = await upload(img)
imgList.push(uploadRes.fileID)
}
// ...其他业务代码
}
})
// mixin.js
// 保存原生的 Page 函数
const originPage = Page
// 定义小程序内置的属性/方法
const prop = ['data', 'properties', 'options']
const methods = ['onLoad', 'onReady', 'onShow', 'onHide', 'onUnload', 'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage', 'onPageScroll', 'onTabItemTap']
Page = (options) => {
if (Array.isArray(options.mixins)) {
const mixins = options.mixins
delete options.mixins
mixins.forEach((mixin) => {
for (let [key, value] of Object.entries(mixin)) {
if (prop.includes(key)) {
// 混入属性
options[key] = {
...value,
...options[key]
}
} else if (methods.includes(key)) {
// 混入原生方法
const originFunc = options[key]
options[key] = function (...args) {
value.call(this, ...args)
return originFunc && originFunc.call(this, ...args)
}
} else {
// 混入普通方法
options = {
...mixin,
...options
}
}
}
})
}
originPage(options)
}
Page({option})
方法来实现的,在option
参数中传入页面相关的data和声明周期函数及其他方法。option
中增加一个mixin
属性,这个属性可以传入一个数组,数组即是每一个要混入的模块,每一个模块的结构其实与参数option
是一样的,我们只需要将所有混入的模块与页面自身的option进行一个参数和方法的合并就能实现一个mixin
的功能。// app.js
require("./mixins.js")
App({
// ...其他代码
})
mixins
数组中有一个topic
模块。// pages/form/form.js
const app = getApp()
const upload = app.require("lib/upload")
const to = app.require("lib/awaitTo")
const db = wx.cloud.database()
Page({
mixins: [require("./mixins/topic")],
data: {
user: wx.getStorageSync('user'),
form: {
title: "",
topic: "",
content: "",
imgList: []
}
},
chooseImg() {
wx.chooseImage({
count: 9 - this.data.form.imgList.length,
sizeType: ['original'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], //从相册选择
success: (res) => {
res.tempFilePaths = res.tempFilePaths
if (this.data.form.imgList.length != 0) {
this.setData({ "form.imgList": this.data.form.imgList.concat(res.tempFilePaths) })
} else {
this.setData({ "form.imgList": res.tempFilePaths })
}
}
});
},
async delImg(e) {
const index = e.currentTarget.dataset.index
const temp = this.data.form.imgList
temp.splice(index, 1)
this.setData({ "form.imgList": temp })
}
})
topic
内都是关联性较强的属性与方法,因此就可以抽离出来,这样页面的js就会更加精简啦,如果有更多的代码就根据自己对于功能的判断进行抽离,然后放在页面对于mixin目录中即可!// // pages/form/mixin/topic.js
const db = wx.cloud.database()
module.exports = {
data:{
topic:{
flag:false,
list:[]
},
},
onLoad(options) {
this.getTopic()
},
async getTopic(){
const res = await db.collection("topic").get()
this.setData({"topic.list":res.data})
},
clearTopic(){
this.setData({"form.topic":""})
},
toggleTopic(e){
console.log(e.currentTarget.dataset)
const flag = e.currentTarget.dataset.flag
this.setData({"topic.flag":flag})
},
}
mixin
也有着与vue中同样的问题就是变量及方法的来源不好追溯,变量是在那个位置定义的比较难以定位,这时就更加依赖开发者的开发规范以及命名方式了,再不济也可以每一个方法写一个独有的注释嘛~