小程序技巧系列——全局组件低成本改造方案

980 阅读3分钟

「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战

背景

业务中,我们有一些组件是全局使用的,也就是每个页面或组件都有可能引入并使用。如果我们每个页面或组件使用一次,都要往对应的页面或组件中引入一次组件,这样做的效率太低,而且会有很多的冗余代码的产生,如何使这种全局性质的组件让使用方无痕接入,只需调用对应的方法就可以。本文提供一种思路,来供大家思考。

核心思路

  • 代码引入方面:其实整体思路就是把我们需要人为写代码的地方,转化为自动插入脚本,让脚本来帮我们做这些重复的工作,也就是将需要在wxml文件中引入的组件,通过脚本来插入,免去人工写入。
  • 如何提供通用的api,来让用户方便使用,这里我们可以将对应的方法挂载在实例上,这里可以参考juejin.cn/post/706568… 这篇文章,将我们的Page函数进行扩展,就可以很好的解决这个问题。这样只要通用的方法扩展到这里,这样只要每个页面引入该扩展方法,则其this上就自动挂载了。
  • 仅插入到page的wxml,自定义组件的wxml则忽略,避免无效的插入,因为在自定义组件内可以直接获取当前页面实例,可以直接使用。

代码实现

以一个全局的自定义toast组件为例。

编写全局组件

这里简单实现下: 最主要的就是实现一个可对外使用的方法,这里不使用传参的方式,而是使用调用实例方法的方式。

Component({
    options: {
        multipleSlots: true,
    },
    properties: {
    },
    data: {

    },
    methods: {
        showToast(options) {
            const {title} = options;
            this.setData({
                display: true,
                title,
            })
        },
        hideToast() {
            this.setData({
                display: false,
            })
        }

    },
});

全局引入插件

在app.json配置文件的usingComponents项中,将组件配置进去:

{
    "usingComponents": {
    "l-toast": "components/toast/index"
  },
}

编写组件插入插件

这里是基于webpack来做这个事情,不再完整的实现一个webpack的配置工具,简单的实现一个插件,同样的原理,也可以用其他方式来实现。 新建parseWxml.js文件,提供插件

// 获取所有的page页面路径
const path = require('path');
const projectRoot = path.join(__dirname, '../');
const appJsonPath = path.join(projectRoot, 'src/app.json');
const appJson = require(appJsonPath);
function getAllPage() {
    const pages = appJson.pages || [];
    const subPages = appJson.subPackages || [];
    let subPagesContent = [];
    if (subPages.length) {
        subPages.forEach(item => {
            item.pages.forEach(page => {
                subPagesContent.push(page);
            })
        })
    }
    return [...pages, ...subPagesContent]
}
class ParseWxml {
    constructor(options = {}) {
    }
    apply(compiler) {
        const pages = getAllPage();
        compiler.hooks.emit.tap('ParseWxml', (compilation) => {
            Object.keys(compilation.assets).forEach((item) => {
                if(item.includes('.wxml')) {
                    if(pages.find(page => item.includes(page))) {
                        const content = compilation.assets[item].source();
                        const newContent = `${content} <l-toast id="l-toast" />`;
                        compilation.assets[item] = {
                            source: () => newContent,
                            size: () => newContent.length
                        }
                    }
                }
            })
        })
    }
}
module.exports = ParseWxml;

代码中已对非页面级的wxml进行了过滤,防止无效插入。

扩展方法

我们基于这篇文章,进行补充:

export default function extendPage(options) {
    Page({
        ...options,
        onLoad(params) {
            // 可以在此写每个页面通用的在onLoad函数中实现的功能,最后调用用户自己的生命周期函数逻辑
            console.log('通用逻辑');
            if (options.onLoad) {
                options.onLoad.call(this, params);
            }
        },
        toast(options) {
           this.toastEle = this.selectComponent('#l-toast');
           if (this.toastEle) {
               this.toastEle.showToast(options);
           }
        },
        hideToast(){
            if(this.toastEle) {
                this.toastEle.hideToast();
            }
        }
    })
}

这样就可以在页面中直接调用,this.toast()方法来进行toast展示。

同理,自定义组件也可以基于这样的封装,来提供通用的方法。只是这里,我们要注意到,由于自定义组件是没有引入自定义toast组件,所以我们要获取页面实例的方法。

getCurPage() {
            const pageId = this.getPageId();
            const pages = getCurrentPages();
            this.curPage = pages.find(item => item.getPageId() === pageId);
            return this.curPage;
        }

总结

很多时候,我们不要只埋头的写代码,业务代码也是有很多值得我们提高的地方,多思考怎么把代码写的好,而且效率高才是我们不断的追求,希望本篇文章对你有所启发。