Maison >Applet WeChat >Développement de mini-programmes >Une brève analyse sur la manière d'implémenter avec élégance la modularisation dans les petits programmes ?

Une brève analyse sur la manière d'implémenter avec élégance la modularisation dans les petits programmes ?

青灯夜游
青灯夜游avant
2021-12-29 10:21:432904parcourir

Comment modulariser avec élégance dans des mini programmes ? Cet article vous apprendra comment modulariser avec élégance de petits programmes. J'espère qu'il vous sera utile !

Une brève analyse sur la manière d'implémenter avec élégance la modularisation dans les petits programmes ?

Cet article explique comment implémenter avec élégance le traitement modulaire dans les mini-programmes WeChat. À travers un résumé condensé de quelques expériences de développement récentes, nous explorerons certaines méthodes susceptibles d'améliorer l'efficacité du développement d'applets WeChat et de réduire la charge mentale.

Choix d'ES6 et commonJS

Tout d'abord, dans l'applet WeChat, qu'il s'agisse de ES6 ou de commonJS, la syntaxe modulaire est prise en charge dans les projets Web traditionnels. Personnellement, je suis habitué à utiliser la syntaxe modulaire ES6 pour le développement. 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,数据的话可以通过dataAu début, j'ai également extrait toutes les méthodes courantes de l'applet dans des fichiers séparés, et utilisé export ou export default pour exporter, et utilisé importintroduction.

Soyez prudent

Mais ! En développement actuel, le fichier js du mini programme ne prend pas en charge l'introduction de chemins absolus ! Cela signifie que si vous devez introduire une méthode publique dans votre page, vous devez utiliser ../../../xxx/xxx.js Lorsque vous introduisez plusieurs méthodes dans la même page, Lors de l'écriture d'un module, cette façon d'écrire freinera certainement grandement votre enthousiasme pour le développement.

🎜🎜🎜Solution🎜🎜🎜🎜Alors, comment résoudre un chemin d'importation aussi long ? Dans les projets Web, nous utilisons souvent des alias de chemin, tels que resolve dans 🎜webpack🎜 ou 🎜vite🎜 .alias pour raccourcir le chemin importé. 🎜
<!-- 某个页面 -->
<import src=&#39;../../template/post-item&#39; />

<template data="{{...item}}" is="post-item" />
🎜Mais dans le mini-programme natif WeChat, bien que certains outils d'ingénierie front-end tels que 🎜gulp🎜 ou 🎜webpack🎜 puissent être utilisés pour apporter quelques modifications au mini-programme, en tant que projet open source, j'espère que son processus de démarrage ne nécessite pas trop de configuration supplémentaire. Il est préférable d'utiliser une syntaxe native pour l'implémenter. 🎜🎜Au final, j'ai choisi d'ajouter une méthode require dans app.js pour introduire le module de cette façon, lors de l'introduction du module dans la page, il suffit d'utiliser l'instance du. app pour importer le module, de sorte que l'implémentation utilise le chemin relatif vers le fichier app.js pour importer le fichier.🎜
// lib/upload.js
// 上传方法
module.exports = async function upload(path) {
	return await wx.cloud.uploadFile({
		cloudPath: new Date().getTime() + path.substring(path.lastIndexOf(".")),
		filePath: path,
	})
}
🎜Méthode d'utilisation🎜
// 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)
    }
    // ...其他业务代码
    }
})
🎜Bien sûr, ce n'est pas particulièrement pratique en premier. Surtout, l'invite de code n'est pas parfaite🎜, utilisez la méthode ci-dessus. Si tel est le cas, les invites de paramètres ou certaines valeurs de retour peuvent ne pas être en place, mais l'impact ne sera pas grand. Si je trouve d'autres meilleures méthodes de mise en œuvre à l'avenir, j'écrirai un article pour l'analyser. Deuxièmement, la syntaxe modulaire de 🎜commonJS🎜 doit être utilisée globalement et uniformément, mais ce n'est pas un gros problème. 🎜

🎜Modularisation d'une seule page🎜🎜🎜Le mini programme ne fournit pas de méthode de modularisation spéciale. La méthode la plus courante consiste à extraire certaines méthodes dans des fichiers js séparés, puis à l'introduire. Si vous souhaitez éviter un fichier d'échange dont le code est trop long, le meilleur moyen est de le composer. Cependant, dans les petits programmes, l'écriture de composants est vraiment désagréable. 🎜🎜Le composant applet a son propre cycle de vie, et doit être défini à l'avance dans la 🎜page json lors de son introduction🎜 Puisque le composant est accroché au 🎜nœud racine shadow🎜, si vous souhaitez partager des styles avec la page, tel. en tant que style global de colorUI, vous pouvez également Vous devez écrire un élément de configuration distinct styleIsolation🎜. L'expérience globale de développement est relativement fragmentée par rapport à vue. 🎜🎜Sur la base de certaines des opinions personnelles ci-dessus, j'utilise rarement des composants lors de l'écriture de petits programmes. Si j'ai besoin d'extraire wxml ou js, j'utilise généralement la méthode suivante. 🎜🎜🎜🎜modularité wxml🎜🎜🎜🎜Dans les petits programmes, j'utilise généralement template pour l'abstraction et la réutilisation, WeChat Mini Program Template Document, par rapport au composant, le modèle n'extrait qu'une partie de la page, pas inclure l’extraction des pièces fonctionnelles. 🎜🎜Ce qui suit est un modèle que j'ai extrait. Il s'agit d'un élément de liste d'un article. Il n'a aucune fonction indépendante, mais le code est très long et est réutilisé dans de nombreuses pages, donc il effectue un détachement. Écrivez tous les styles en utilisant des 🎜styles en ligne🎜, afin que les mêmes styles soient introduits partout où ils sont introduits. 🎜
// 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)
}
🎜 doit être introduit à l'avance lorsqu'il est utilisé dans la page. Étant donné que plusieurs modèles peuvent être introduits, vous devez utiliser l'attribut is pour déclarer quel modèle est utilisé pour les données, vous pouvez transmettre <. code>data Les attributs sont transmis. L'exemple ici est que je déconstruis l'élément parcouru🎜 et que je lui attribue ensuite. 🎜
// app.js
require("./mixins.js")
App({
// ...其他代码
})
🎜Bien sûr, le code du modèle qui utilise le modèle pour la modularisation et l'extraction ne peut pas contenir trop de logique fonctionnelle. L'utilisation spécifique doit toujours être basée sur l'entreprise. 🎜🎜🎜🎜js modulaire🎜🎜🎜

在小程序中最基本的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中同样的问题就是变量及方法的来源不好追溯,变量是在那个位置定义的比较难以定位,这时就更加依赖开发者的开发规范以及命名方式了,再不济也可以每一个方法写一个独有的注释嘛~

【相关学习推荐:小程序开发教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer