当前位置: 代码网 > 移动>腾讯>微信 > 浅析小程序中如何优雅地进行模块化处理?

浅析小程序中如何优雅地进行模块化处理?

2025年03月31日 微信 我要评论
小程序中如何优雅地进行模块化处理?本篇文章就来手把手教大家如何优雅的在小程序中进行模块化,希望对大家有所帮助!这一篇文章就讲讲在微信小程序中如何优雅地进行模块化处理。通过最近的一些开发经验进行浓缩总结

小程序中如何优雅地进行模块化处理?本篇文章就来手把手教大家如何优雅的在小程序中进行模块化,希望对大家有所帮助!

浅析小程序中如何优雅地进行模块化处理?

这一篇文章就讲讲在微信小程序中如何优雅地进行模块化处理。通过最近的一些开发经验进行浓缩总结,探索一些可以提升微信小程序开发效率和降低心智负担的方法。

es6和commonjs的选择

首先在微信小程序中不论是 es6 或者是 commonjs 模块化语法都是支持的,在传统的web项目中我个人是习惯统一使用 es6 模块化语法进行开发的。

在最初我也是将小程序中所有的通用方法抽离成单独的文件,并使用export 或 export default 导出,使用 import 引入。

注意点

但是!在实际开发中,小程序的js文件是不支持绝对路径引入的!这意味着如果你需要在你的页面中引入一个公用方法,你必须使用 ../../../xxx/xxx.js 的方式,当你同一个页面引入多个模块时,这种写法绝对会极大的打击你的开发热情。

解决方式

那我们该如何解决这么长的引入路径呢,在web项目中,我们常常会使用路径别名的方式,例如 webpackvite 中的 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></import><template><view>
        <!-- 背景蒙版 -->
        <view>
                <image></image>
        </view>
        <view>
        </view>

        <view>
                <!-- 文章标题 -->
                <view>
                        <text>{{topic}}</text>
                        <text>{{title}}</text>
                </view>
                <!-- 文章内容 -->
                <view>{{content}}</view>

                <view>
                        <!-- 文章图片 -->
                        <view>
                                <view>
                                        <image></image>
                                </view>
                        </view>

                        <!-- 浏览量-点赞数 -->
                        <view>
                                <view></view>
                                <view>{{viewnum||0}}</view>
                                <view></view>
                                <view>{{favornum||0}}</view>
                        </view>
                </view>

                <!-- 发布时间 -->
                <view>
                        <view>
                                <template></template>
                                <view>{{user.nickname}}</view>
                        </view>

                        <view>{{createtime}}</view>
                </view>
        </view></view></template>
登录后复制

在页面中使用的时候需要提前引入,由于可以引入多个模板,因此使用时需要使用 is属性 声明使用的是哪一个template,数据的话可以通过data属性传入,这里的示例是我将遍历的item解构后再赋值进去。

<!-- 某个页面 -->
<import></import><template></template>
登录后复制

当然使用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) =&gt; {
  if (array.isarray(options.mixins)) {
    const mixins = options.mixins
    delete options.mixins
    mixins.foreach((mixin) =&gt; {
      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 &amp;&amp; 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) =&gt; {
				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中同样的问题就是变量及方法的来源不好追溯,变量是在那个位置定义的比较难以定位,这时就更加依赖开发者的开发规范以及命名方式了,再不济也可以每一个方法写一个独有的注释嘛~

【相关学习推荐:微信小程序模板文档

以上就是浅析小程序中如何优雅地进行模块化处理?的详细内容,更多请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com