小程序开发-自定义组件的扩展

时间:2024-01-25 11:46:02

一个开始

由于本人喜欢封装组件做到高内聚,这样的好处是,拿来就用,如果封装一个组件,需要外部耦合,那么将没法做到很好的复用,因为耦合的部分需要每次重新开发。

最近遇到了一个业务场景是这样,如图:

1. 页面展示主页,主页可以浏览,也可以点击去其他页面,主页有登录按钮,登录按钮点击显示登录view(注意:登录不是跳去登录页,而是在当前页做view切换)。

2. 登录view中可以填写用户名、密码,可以登录、可以返回(返回到主页view)。

愉快的开发起开

1. 首先将主页是小程序的一个页面,由于我希望登录模块是通用的,既然不是新的页面,就做成一个组件。

2. 登录组件命名为login-view,愉快的开发完毕。

3. 接下来页面引用组件,并在wxml中使用

<view class="home-container" wx:if="{{!showLogin}}"></view>

<login-view wx:else></login-view>

4. 就这样,定义data:showLogin, 用其控制是否切换到登录view。

5. 当主页中点击登录时,将showLogin = true。

6. 很棒,展示了登录界面。填写用户名、密码、点击登录,成功!。

7. 很开心,第一次测试成功了,接着第二次测试。

8. 这是第二次测试,点击登录,到登录界面,输入用户名,这时候点击了返回。

9. 再次点击登录来到了登录界面,发现刚刚输入的用户名不见了

10. 留下了没有技术的泪水,emmm...

寻找失踪的用户名

接下来展示思考,终于想到了为什么:

wx:if是dom的移除与添加,并不是显示与隐藏,因此,在切换showLogin = false时,login-view组件被移除了,当再次点击登录,showLogin = true时,login-view组件又重新挂在。

好,失踪的用户名找到了,是wx:if将它夺走了。那么该怎么解决?

两个过程

我解决此问题用了两个过程,分别是:完成与完善。

过程一(完成):如何解决组件data丢失问题

分析:信息丢失是因为组件的注销与重新挂载,这是不可避免的,组件注销后,内容永远也保存不了。要解决的就是把组件内容保存起来

解决办法: 信息存储在page中,因为page是一直生存的,组件是会注销的。因此page中的data是不会随着组件注销而消失的。那么如何将信息存放在page中?

如下:

1. 将userName和password直接放在page的data中,并且接收input事件去改变值

<login-view userName="{{userName}}" password="{{password}}" bindinput="onInput"></login-view>

2. 在page中声明onInput,当组件中触发此函数,则改变userName和password的值

Page({
    data: {
        userName: \'\',
        password: \'\',
    },
    // 组件中触发事件,修改输入框的值
    onInput(e) {
        const {key, value} = e.detail;
        this.setData({
            [key]: value,
        });
    },
});

3. 在login-view组件中,接收userName和password值,并渲染到login-view.wxml中去,这里就不再贴代码了。

思考:这样修改有什么问题?我认为这样的组件不能称之为一个优秀的组件,为什么?

答:我希望我的组件是拿来就用的,那么这样修改,我怎么做到拿来就用?这样的组件已与page高耦合,没有page中的逻辑,此组件无法运行,因此我不能这样该。

该怎样:那么我该怎样该达到我缓存数据的目的呢?

过程二(完善):开发低耦合高内聚的灵活性组件

首先看完善后的业务上该怎么用

<login-view></login-view>

wxml里直接这样使用,那么page的js里呢?答案:不需要任何代码。这么神奇么?就是这么神奇。

思路: 保存原来代码不变,login-view做了任何login-view该做的事情,数据存储、数据更新都在组件内完成。我要做的是做到组件重新挂在,数据缓存。该怎样做?

同样,利用page data缓存数据,但是不需要主动去定义,在login-view组件里做手脚:

const Base = require(\'./wx-component.js\');
Component(Base({
    name: \'login\',
    $$data: {
        userName: \'\',
        password: \'\',
    },
    methods: {
        onInput(e) {
            const key = e.currentTarget.dataset.key;
            const value = e.detail.value;
            this.setData({
                [key]: value,
            });
        },
    },
}));

以上代码,引用了wx-component.js,暴露一个方法,将原本传递给Component的options对象传递给他,Base(options),然后再将其返回值传递给Component,Component(Base(options))。做了一层包装。就这样,就完成了数据缓存,再次切换login-view时,数据依旧保存着。

那么看到这里读者要问:wx-component是什么鬼?$$data又是什么?

三个BUFF

wx-component.js是自己封装的一个组件增强的扩展函数,他可以轻松赋予你三个特殊能力。

1. 逆向数据绑定

什么意思?我们都知道,page中的data可以传递给组件使用,并且page中data更新,组件中的view页同步更新,但是组件中的data更新不会反射到page中。

那么这第一个buff就是:组件data更新引发page中data的更新,换句话说,page的data中会时时存储组件data的一个副本。因此我叫他逆向数据绑定。

代码如下:

const Base = require(\'./wx-component.js\');
Component(Base({
    // 声明增强组件的name
    name: \'login\',
    // 声明逆向数据绑定的data
    $$data: {
        userName: \'\',
        password: \'\',
    },
}));

name是必须声明的,使用增强组件功能,需要声明唯一name值,比如‘login’,$$data属性是逆向数据绑定的所有数据,其他不需要逆向数据绑定的数据依旧放在data中。

这样就完成了component.$$data -> page.data的功能,在组件setData改变userName和password的值会同步到page.data中去。

那你怎么可以在page中获取到他呢?在page中用this.data.$[name]格式,此例子中为:this.data.$login就获取到了逆向数据的对象。

2. 组件数据缓存

有了逆向数据绑定,你可能猜到了,就用这种方法可以把数据存储到page中了,那么在组件注销后重新挂载,我们可以拿到此份数据来进行渲染到组件。

这部分代码在wx-component.js中做体现,业务中并不需要做额外的事情。

3. 方法暴露

很多时候,组件内部的一些方法,page中其实也需要用到的, 比如:tab组件,点击某个tab,则变为激活状态,在page中可能也是需要主动去改变某个tab的激活状态,因此需要用到组件内部方法。

当然组件内部方法是有办法获取的,官方文档有介绍如何获取组件实例,获取到组件实例,当然就可以使用其方法,但这样很麻烦的,我可以提供更好的方法。

const Base = require(\'./wx-component.js\');
Component(Base({
    name: \'login\',
    $$data: {
        userName: \'\',
        password: \'\',
    },
    // 定义向page暴露的方法
    $parentMethods: {
        reset() {
            this.setData({
                userName: \'\',
                password: \'\',
            });
        },
    },
}));

如上用$parentMethods对象来暴露给page方法,在page中可以直接调用

Page({
  onReset() {
    // $login来获取暴露的方法
    this.$login.reset();
  }
})

最后的最后

这就是wx-component的功能,功能并不是通用的,只适合部分业务场景。如果你的业务放好需要这样做,你就可以用到它。

github点这里

如果对你有帮助,点一个star吧~~~

如果有错误的地方,请提issue吧~~~

如果有什么建议的,欢迎留言哈~~~

如果要转载,请附上原文链接哈~~~