小程式中如何優雅地進行模組化處理?本篇文章就來手把手教大家如何優雅的在小程式中進行模組化,希望對大家有幫助!
這篇文章就講講在微信小程式中如何優雅地進行模組化處理。透過最近的一些開發經驗進行濃縮總結,探索一些可以提升微信小程式開發效率和降低心智負擔的方法。
首先在微信小程式中不論是ES6
或是commonJS
模組化語法都是支援的,在傳統的web專案中我個人是習慣統一使用ES6
模組化語法進行開發的。
在最初我也是將小程式中所有的通用方法抽離成單獨的文件,並使用export
或export default
匯出,使用import
引入。
注意點
但是!在實際開發中,小程式的js檔案是不支援絕對路徑引入的!這意味著如果你需要在你的頁面中引入一個公用方法,你必須使用../../../xxx/xxx.js
的方式,當你在同一個頁面引入多個模組時,這種寫法絕對會極大的打擊你的開發熱情。
解決方式
那我們該如何解決這麼長的引入路徑呢,在web專案中,我們常常會使用路徑別名的方式,例如webpack或vite 中的resolve.alias
來縮短引入的路徑。
alias: {"@src":path.resolve("src"),
但是在原生微信小程式中,雖然可以透過gulp 或webpack 等一些前端工程化的工具對小程式進行一些改造,但是作為一個開源專案我希望它的啟動過程不需要太多額外配置。最好是能夠使用原生的語法去實作。
最後我選擇了在app.js中新增一個require
方法用於引入模組,這樣在頁面內引入模組時,我們只需要使用app的實例來進行模組引入,這樣可以實作使用與app.js
檔案的相對路徑來引入檔案.
// app.js App({ require(path){ return path } })
使用方式
// 使用基于app.js的相对路径来引入文件,这样就避免了写很多"../" const app = getApp() const upload = app.require("lib/upload")
當然這樣做也不是特別方便,首先是程式碼提示的不健全,使用以上方式的話可能對於參數或一些傳回值的提示不到位,但是影響不大。如果之後我摸索出了其他比較好的實作方式再寫一篇文章解析。其次是必須使用全域統一使用commonJS 的模組化語法啦,不過這一點的話問題不大。
小程式中並沒有提供特殊的模組化方式,比較常用的就是將一些方法抽離為單獨的js文件,然後再引入。想要避免一個頁面檔案程式碼太長的話最好的方式是元件化,但是在小程式中,認為寫入元件真的是一件很不爽的事情。
小程式元件擁有自己的生命週期,而且引入時必須在頁面json中提前定義,由於元件是掛在shadow root節點上,如果想要和頁面共享樣式例如colorUI的全域樣式也需要寫入單獨的設定項styleIsolation。整體開發體驗相比vue而言比較割裂。
基於以上的一些個人看法,我在寫小程式時比較少使用元件,如果是需要抽離wxml或是js我通常使用以下的方法。
wxml模組化
在小程式中我通常使用模板template
進行抽離復用,微信小程式範本文件 ,範本相較於元件抽離的只是部分的頁面,不包含功能部分的抽離。
以下是我抽離的一個模板,這是一個文章的列表項,它並沒有什麼單獨的功能,但是代碼很長並且卻在很多頁面中復用到,於是我將它進行了一個抽離。把樣式都用行內樣式的方式寫上,這樣在哪裡引入都是一樣的樣式。
<!-- 文章列表项 --> <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屬性
聲明使用的是哪一個template,數據的話可以通過data
屬性傳入,這裡的範例是我將遍歷的item解構後再賦值進去。
<!-- 某个页面 --> <import src='../../template/post-item' /> <template data="{{...item}}" is="post-item" />
當然使用template進行模組化進行抽離的模板程式碼可不能包涵太多的功能邏輯,具體的使用還是需要根據業務來噢。
js模組化
#在小程序中最基本的js模块化就是直接抽离js文件,例如一些全局通用的方法,下面展示一个全局上传方法的封装
// 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) } // ...其他业务代码 } })
当然以上的办法对于通用方法来说很方便,但是对于与 页面操作的逻辑耦合性 很高的一些业务代码,这样子抽离并不方便。
在vue2中我们可以使用mixin的方法模块化代码,在vue3中我们可以使用hook的方式模块化代码,但是在小程序中并没有以上两者的支持,最初我想仿照 vue3的hook 方式进行页面js封装改造,但最终实现的效果不理想,于是选择了实现一个模仿vue2 mixin 的方法来实现模块化。
具体代码其他博主有实现过,因此我就直接拿来使用了,具体代码如下。如果不了解vue中mixin的使用方法的可以自行去官网看文档,这里不做过多介绍。
// 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()函数,小程序的每一个页面都是通过调用Page({option})
方法来实现的,在option
参数中传入页面相关的data和声明周期函数及其他方法。
我们通过在Page方法的参数option
中增加一个mixin
属性,这个属性可以传入一个数组,数组即是每一个要混入的模块,每一个模块的结构其实与参数option
是一样的,我们只需要将所有混入的模块与页面自身的option进行一个参数和方法的合并就能实现一个mixin
的功能。
使用的方法是现在app.js中引入mixin.js
// app.js require("./mixins.js") App({ // ...其他代码 })
然后我们写一个常规页面的js,业务代码大家不用看,主要关注Page的属性中多了一个mixins选项,而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中同样的问题就是变量及方法的来源不好追溯,变量是在那个位置定义的比较难以定位,这时就更加依赖开发者的开发规范以及命名方式了,再不济也可以每一个方法写一个独有的注释嘛~
【相关学习推荐:小程序开发教程】
以上是淺析小程式中如何優雅地進行模組化處理?的詳細內容。更多資訊請關注PHP中文網其他相關文章!