目录
ES6和commonJS的选择
单页面模块化
首页 微信小程序 小程序开发 浅析小程序中如何优雅地进行模块化处理?

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

Dec 29, 2021 am 10:21 AM
小程序

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

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

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

ES6和commonJS的选择

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

在最初我也是将小程序中所有的通用方法抽离成单独的文件,并使用exportexport 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 src=&#39;./avatar&#39; />
<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=&#39;../../template/post-item&#39; />

<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 = [&#39;data&#39;, &#39;properties&#39;, &#39;options&#39;]
const methods = [&#39;onLoad&#39;, &#39;onReady&#39;, &#39;onShow&#39;, &#39;onHide&#39;, &#39;onUnload&#39;, &#39;onPullDownRefresh&#39;, &#39;onReachBottom&#39;, &#39;onShareAppMessage&#39;, &#39;onPageScroll&#39;, &#39;onTabItemTap&#39;]

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(&#39;user&#39;),
		form: {
			title: "",
			topic: "",
			content: "",
			imgList: []
		}
	},
	chooseImg() {
		wx.chooseImage({
			count: 9 - this.data.form.imgList.length,
			sizeType: [&#39;original&#39;], //可以指定是原图还是压缩图,默认二者都有
			sourceType: [&#39;album&#39;, &#39;camera&#39;], //从相册选择
			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中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

PHP教程
1596
276
使用Python开发微信小程序 使用Python开发微信小程序 Jun 17, 2023 pm 06:34 PM

随着移动互联网技术和智能手机的普及,微信成为了人们生活中不可或缺的一个应用。而微信小程序则让人们可以在不需要下载安装应用的情况下,直接使用小程序来解决一些简单的需求。本文将介绍如何使用Python来开发微信小程序。一、准备工作在使用Python开发微信小程序之前,需要安装相关的Python库。这里推荐使用wxpy和itchat这两个库。wxpy是一个微信机器

实现微信小程序中的卡片翻转特效 实现微信小程序中的卡片翻转特效 Nov 21, 2023 am 10:55 AM

实现微信小程序中的卡片翻转特效在微信小程序中,实现卡片翻转特效是一种常见的动画效果,可以提升用户体验和界面交互的吸引力。下面将具体介绍如何在微信小程序中实现卡片翻转的特效,并提供相关代码示例。首先,需要在小程序的页面布局文件中定义两个卡片元素,一个用于显示正面内容,一个用于显示背面内容,具体示例代码如下:&lt;!--index.wxml--&gt;&l

支付宝上线'汉字拾光-生僻字”小程序,用于征集、补充生僻字库 支付宝上线'汉字拾光-生僻字”小程序,用于征集、补充生僻字库 Oct 31, 2023 pm 09:25 PM

本站10月31日消息,今年5月27日,蚂蚁集团宣布启动“汉字拾光计划”,最近又迎来新进展:支付宝上线“汉字拾光-生僻字”小程序,用于向社会征集生僻字,补充生僻字库,同时提供不同的生僻字输入体验,以帮助完善支付宝内的生僻字输入方法。目前,用户搜索“汉字拾光”、“生僻字”等关键词就可以进入“生僻字”小程序。在小程序里,用户可以提交尚未被系统识别录入的生僻字图片,支付宝工程师在确认后,将会对字库进行补录入。本站注意到,用户还可以在小程序体验最新的拆字输入法,这一输入法针对读音不明确的生僻字设计。用户拆

小程序能用react吗 小程序能用react吗 Dec 29, 2022 am 11:06 AM

小程序能用react,其使用方法:1、基于“react-reconciler”实现一个渲染器,生成一个DSL;2、创建一个小程序组件,去解析和渲染DSL;3、安装npm,并执行开发者工具中的构建npm;4、在自己的页面中引入包,再利用api即可完成开发。

uniapp如何实现小程序和H5的快速转换 uniapp如何实现小程序和H5的快速转换 Oct 20, 2023 pm 02:12 PM

uniapp如何实现小程序和H5的快速转换,需要具体代码示例近年来,随着移动互联网的发展和智能手机的普及,小程序和H5成为了不可或缺的应用形式。而uniapp作为一个跨平台的开发框架,可以在一套代码的基础上,快速实现小程序和H5的转换,大大提高了开发效率。本文将介绍uniapp如何实现小程序和H5的快速转换,并给出具体的代码示例。一、uniapp简介unia

教你如何在小程序中用公众号模板消息(附详细思路) 教你如何在小程序中用公众号模板消息(附详细思路) Nov 04, 2022 pm 04:53 PM

本篇文章给大家带来了关于微信小程序的相关问题,其中主要介绍了如何在小程序中用公众号模板消息,下面一起来看一下,希望对大家有帮助。

用Python编写简单的聊天程序教程 用Python编写简单的聊天程序教程 May 08, 2023 pm 06:37 PM

实现思路x01服务端的建立首先,在服务端,使用socket进行消息的接受,每接受一个socket的请求,就开启一个新的线程来管理消息的分发与接受,同时,又存在一个handler来管理所有的线程,从而实现对聊天室的各种功能的处理x02客户端的建立客户端的建立就要比服务端简单多了,客户端的作用只是对消息的发送以及接受,以及按照特定的规则去输入特定的字符从而实现不同的功能的使用,因此,在客户端这里,只需要去使用两个线程,一个是专门用于接受消息,一个是专门用于发送消息的至于为什么不用一个呢,那是因为,只

PHP与小程序的地理位置定位与地图显示 PHP与小程序的地理位置定位与地图显示 Jul 04, 2023 pm 04:01 PM

PHP与小程序的地理位置定位与地图显示地理位置定位与地图显示在现代科技中已经成为了必备的功能之一。随着移动设备的普及,人们对于定位和地图显示的需求也越来越高。在开发过程中,PHP和小程序是常见的两种技术选择。本文将为大家介绍PHP与小程序中的地理位置定位与地图显示的实现方法,并附上相应的代码示例。一、PHP中的地理位置定位在PHP中,我们可以使用第三方地理位

See all articles