ホームページ  >  記事  >  WeChat アプレット  >  小さなプログラムでモジュール化をエレガントに実装する方法についての簡単な分析?

小さなプログラムでモジュール化をエレガントに実装する方法についての簡単な分析?

青灯夜游
青灯夜游転載
2021-12-29 10:21:432883ブラウズ

小さなプログラムでエレガントにモジュール化するにはどうすればよいですか?この記事では、小さなプログラムをエレガントにモジュール化する方法を説明します。

小さなプログラムでモジュール化をエレガントに実装する方法についての簡単な分析?

この記事では、WeChat ミニ プログラムでモジュール処理をエレガントに実装する方法について説明します。最近の開発経験を凝縮した要約を通じて、WeChat アプレット開発の効率を向上させ、精神的負担を軽減できるいくつかの方法を探ります。

ES6 と commonJS

の選択

まず第一に、ES6commonJS の両方が WeChat ミニ プログラムでサポートされています。私は個人的に、開発に ES6

モジュール構文を使用することに慣れています。

最初に、ミニ プログラム内のすべての共通メソッドも個別のファイルに抽出し、export または exportdefault を使用してエクスポートし、## を使用しました。 #import はじめに。

#注意

しかし!実際の開発では、ミニプログラム

の js ファイルは絶対パス の導入をサポートしていません。これは、ページにパブリック メソッドを導入する必要がある場合は、../../../xxx/xxx.js メソッドを使用する必要があることを意味します。 , このような書き方をしてしまうと、開発に対する熱意が確実に冷めてしまいます。

解決策

では、このような長いインポート パスを解決するにはどうすればよいでしょうか? Web プロジェクトでは、# などのパス エイリアス メソッドをよく使用します。インポートされたパスを短縮するには、

vite の ##webpack または resolve.alias を使用します。 <pre class="brush:js;toolbar:false;">alias: {&quot;@src&quot;:path.resolve(&quot;src&quot;),</pre>ただし、ネイティブ WeChat アプレットでは、

gulp

webpack などの一部のフロントエンド エンジニアリング ツールを使用して、アプレットにいくつかの変更を加えることができます。 a オープンソース プロジェクトの起動プロセスで追加の構成があまり必要とされないことを願っています。ネイティブ構文を使用して実装するのが最善です。 最終的に、app.js に新しい

require

メソッドを追加してモジュールを導入することにしました。このようにして、モジュールをページに導入するときに必要なのは、モジュールを導入するためのアプリのインスタンス。これにより、app.js ファイルへの相対パスを使用してファイルをインポートできます。<pre class="brush:js;toolbar:false;">// app.js App({ require(path){ return path } })</pre>使用方法

// 使用基于app.js的相对路径来引入文件,这样就避免了写很多"../"
const app = getApp()
const upload = app.require("lib/upload")

もちろん、これは特に便利というわけではありませんが、第一に

コード プロンプトは完璧ではありません

. 上記の方法を使用する場合、パラメーターまたは一部の戻り値のプロンプトが適切に配置されていない可能性があります。しかしその影響は大きくないでしょう。今後、他にもっと良い実装方法が見つかったら、記事を書いて分析していきたいと思います。次に、commonJS のモジュール構文をグローバルに使用する必要がありますが、これは大きな問題ではありません。

単一ページのモジュール化

ミニ プログラムでは特別なモジュール化方法は提供されていません。より一般的な方法は、いくつかのメソッドを個別の js ファイルに抽出してから再導入することです。 。コードが長すぎるページ ファイルを避けたい場合は、ページ ファイルをコンポーネント化するのが最善の方法ですが、小さなプログラムの場合、コンポーネントを記述するのは非常に面倒です。

ミニ プログラム コンポーネントには独自のライフ サイクルがあり、導入時に

ページ json で事前に

定義する必要があります。コンポーネントは シャドウ ルート ノードにハングしているため、, if colorUI のグローバル スタイルなど、ページとスタイルを共有したい場合は、別の設定項目 styleIsolation も記述する必要があります。全体的な開発エクスペリエンスは、vue に比べて比較的細分化されています。 上記の個人的な意見を踏まえると、私は小さなプログラムを書くときにコンポーネントを使用することはほとんどないので、wxml や js を抽出する必要がある場合は、通常次の方法を使用します。

wxml モジュール化小規模なプログラムでは、通常、抽象化と再利用のために

template

を使用します。 微信小ProgramTEMPLATEdoc コンポーネントと比較すると、テンプレートはページの一部を抽出するだけであり、機能部分の抽出は含まれません。 以下は私が抽出したテンプレートです。これは記事のリスト項目です。

独立した機能はありません

が、コードは非常に長いです is は多くのページで再利用されていたので抽出しました。 インライン スタイルを使用してスタイルを記述し、どこに導入されても同じスタイルが導入されるようにします。

<!-- 文章列表项 -->
<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属性

で宣言する必要があります。 be pass

data 属性が渡されます。ここでの例は、 走査された item を分解し、それに割り当てることです。

<!-- 某个页面 -->
<import src=&#39;../../template/post-item&#39; />

<template data="{{...item}}" is="post-item" />
もちろん、モジュール化と抽出にテンプレートを使用するテンプレート コードには、あまり多くの機能ロジックを含めることはできませんが、具体的な使用方法はビジネスに基づいて行う必要があります。

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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。