iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >微信小程序中如何自定义组件
  • 857
分享到

微信小程序中如何自定义组件

2023-06-29 16:06:10 857人浏览 独家记忆
摘要

这篇文章将为大家详细讲解有关微信小程序中如何自定义组件,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。在微信小程序开发过程中,对于一些可能在多个页面都使用的页面模块,可以把它封装成一个组件,以提高开发效率。

这篇文章将为大家详细讲解有关微信小程序中如何自定义组件,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

微信小程序开发过程中,对于一些可能在多个页面都使用的页面模块,可以把它封装成一个组件,以提高开发效率。虽然说我们可以引入整个组件库比如 weui、vant 等,但有时候考虑微信小程序的包体积限制问题,通常封装为自定义的组件更为可控。

并且对于一些业务模块,我们就可以封装为组件复用。本文主要讲述以下两个方面:

  • 组件的声明与使用

  • 组件通信

组件的声明与使用

微信小程序的组件系统底层是通过 Exparser 组件框架实现,它内置在小程序的基础库中,小程序内的所有组件,包括内置组件和自定义组件都由 Exparser 组织管理。

自定义组件和写页面一样包含以下几种文件:

  • index.JSON

  • index.wxml

  • index.wxss

  • index.js

  • index.wxs

以编写一个 tab 组件为例:编写自定义组件时需要在 json 文件中讲 component 字段设为 true

{    "component": true}

js 文件中,基础库提供有 Page 和 Component 两个构造器,Page 对应的页面为页面根组件,Component 则对应:

Component({    options: { // 组件配置        addGlobalClass: true,        // 指定所有 _ 开头的数据字段为纯数据字段        // 纯数据字段是一些不用于界面渲染的 data 字段,可以用于提升页面更新性能        pureDataPattern: /^_/,         multipleSlots: true // 在组件定义时的选项中启用多slot支持    },    properties: {        vtabs: {type: Array, value: []},    },    data: {        currentView: 0,    },    observers: { // 监测        activeTab: function(activeTab) {            this.scrollTabBar(activeTab);        }    },     relations: {  // 关联的子/父组件        '../vtabs-content/index': {            type: 'child', // 关联的目标节点应为子节点            linked: function(target) {                this.calcVtabsCotentHeight(target);            },            unlinked: function(target) {                delete this.data._contentHeight[target.data.tabIndex];            }        }    },    lifetimes: { // 组件声明周期        created: function() {            // 组件实例刚刚被创建好时        },        attached: function() {            // 在组件实例进入页面节点树时执行        },        detached: function() {            // 在组件实例被从页面节点树移除时执行        },    },    methods: { // 组件方法        calcVtabsCotentHeight(target) {}    } });

如果有了解过 Vue2 的小伙伴,会发现这个声明很熟悉。

在小程序启动时,构造器会将开发者设置的properties、data、methods等定义段,

写入Exparser的组件注册表中。这个组件在被其它组件引用时,就可以根据这些注册信息来创建自定义组件的实例。

模版文件 wxml:

<view class='vtabs'>    <slot /></view>

样式文件:

.vtabs {}

外部页面组件使用,只需要在页面的 json 文件中引入

{  "navigationBarTitleText": "商品分类",  "usinGComponents": {    "vtabs": "../../../components/vtabs",  }}

在初始化页面时,Exparser 会创建出页面根组件的一个实例,用到的其他组件也会响应创建组件实例(这是一个递归的过程):

组件创建的过程大致有以下几个要点:

  • 根据组件注册信息,从组件原型上创建出组件节点的 JS 对象,即组件的 this

  • 将组件注册信息中的 data 复制一份,作为组件数据,即 this.data

  • 将这份数据结合组件 WXML,据此创建出 Shadow Tree(组件的节点树),由于 Shadow Tree 中可能引用有其他组件,因而这会递归触发其他组件创建过程;

  • ShadowTree 拼接到 Composed Tree(最终拼接成的页面节点树)上,并生成一些缓存数据用于优化组件更新性能;

  • 触发组件的 created 生命周期函数;

  • 如果不是页面根组件,需要根据组件节点上的属性定义,来设置组件的属性值;

  • 当组件实例被展示在页面上时,触发组件的 attached 生命周期函数,如果 Shadow Tree 中有其他组件,也逐个触发它们的生命周期函数。

组件通信

由于业务的负责度,我们常常需要把一个大型页面拆分为多个组件,多个组件之间需要进行数据通信。

对于跨代组件通信可以考虑全局状态管理,这里只讨论常见的父子组件通信:

方法一 WXML 数据绑定

用于父组件向子组件的指定属性设置数据。

子声明 properties 属性

Component({    properties: {        vtabs: {type: Array, value: []}, // 数据项格式为 `{title}`    }})

父组件调用:

    <vtabs vtabs="{{ vtabs }}"</vtabs>

方法二 事件

用于子组件向父组件传递数据,可以传递任意数据。

子组件派发事件,先在 wxml 结构绑定子组件的点击事件:

   <view bindtap="handleTabClick">

再在 js 文件中进行派发事件,事件名可以自定义填写,第二个参数可以传递数据对象,第三个参数为事件选项。

 handleClick(e) {     this.triggerEvent(         'tabclick',          { index },          {              bubbles: false,  // 事件是否冒泡             // 事件是否可以穿越组件边界,为 false 时,事件只在引用组件的节点树上触发,             // 不进入其他任何组件的内部             composed: false,               capturePhase: false // 事件是否拥有捕获阶段          }     ); }, handleChange(e) {     this.triggerEvent('tabchange', { index }); },

最后,在父组件中监听使用:

<vtabs     vtabs="{{ vtabs }}"    bindtabclick="handleTabClick"     bindtabchange="handleTabChange" >

方法三 selectComponent 获取组件实例对象

通过 selectComponent 方法可以获取子组件的实例,从而调用子组件的方法。

父组件的 wxml

<view>    <vtabs-content="Goods-content{{ index }}"></vtabs-content></view>

父组件的 js

Page({    reCalcContentHeight(index) {        const goodsContent = this.selectComponent(`#goods-content${index}`);    },})

selector类似于 CSS 的选择器,但仅支持下列语法。

  • ID选择器:#the-id(笔者只测试了这个,其他读者可自行测试)

  • class选择器(可以连续指定多个):.a-class.another-class

  • 子元素选择器:.the-parent > .the-child

  • 后代选择器:.the-ancestor .the-descendant

  • 跨自定义组件的后代选择器:.the-ancestor >>> .the-descendant

  • 多选择器的并集:#a-node, .some-other-nodes

方法四  url 参数通信

在电商/物流等微信小程序中,会存在这样的用户故事,有一个「下单页面A」和「货物信息页面B」

  • 在「下单页面 A」填写基本信息,需要下钻到「详细页面B」填写详细信息的情况。比如一个寄快递下单页面,需要下钻到货物信息页面填写更详细的信息,然后返回上一个页面。

  • 在「下单页面 A」下钻到「货物页面B」,需要回显「货物页面B」的数据。

微信小程序由一个 App() 实例和多个 Page() 组成。小程序框架以栈的方式维护页面(最多10个) 提供了以下 api 进行页面跳转,页面路由如下

  • wx.navigateTo(只能跳转位于栈内的页面)

  • wx.redirectTo(可跳转位于栈外的新页面,并替代当前页面)

  • wx.navigateBack(返回上一层页面,不能携带参数)

  • wx.switchTab(切换 Tab 页面,不支持 url 参数)

  • wx.reLaunch(小程序重启)

可以简单封装一个 jumpTo 跳转函数,并传递参数:

export function jumpTo(url, options) {    const baseUrl = url.split('?')[0];    // 如果 url 带了参数,需要把参数也挂载到 options 上    if (url.indexof('?') !== -1) {        const { queries } = resolveUrl(url);        Object.assign(options, queries, options); // options 的优先级最高    }     cosnt queryString = objectEntries(options)        .filter(item => item[1] || item[0] === 0) // 除了数字 0 外,其他非值都过滤        .map(            ([key, value]) => {                if (typeof value === 'object') {                    // 对象转字符串                    value = JSON.stringify(value);                }                if (typeof value === 'string') {                    // 字符串 encode                    value = encodeURIComponent(value);                }                return `${key}=${value}`;            }        ).join('&');    if (queryString) { // 需要组装参数        url = `${baseUrl}?${queryString}`;    }        const pageCount = wx.getCurrentPages().length;    if (jumpType === 'navigateTo' && pageCount < 5) {        wx.navigateTo({             url,            fail: () => {                 wx.switch({ url: baseUrl });            }        });    } else {        wx.navigateTo({             url,            fail: () => {                 wx.switch({ url: baseUrl });            }        });    } }

jumpTo 辅助函数:

export const resolveSearch = search => {    const queries = {};    cosnt paramList = search.split('&');    paramList.forEach(param => {        const [key, value = ''] = param.split('=');        queries[key] = value;    });    return queries;};export const resolveUrl = (url) => {    if (url.indexOf('?') === -1) {        // 不带参数的 url        return {            queries: {},            page: url        }    }    const [page, search] = url.split('?');    const queries = resolveSearch(search);    return {        page,        queries    };};

在「下单页面A」传递数据:

jumpTo({     url: 'pages/consignment/index',     {         sender: { name: 'naluduo233' }    }});

在「货物信息页面B」获得 URL 参数:

const sender = JSON.parse(getParam('sender') || '{}');

url 参数获取辅助函数

// 返回当前页面export function getCurrentPage() {    const pageStack = wx.getCurrentPages();    const lastIndex = pageStack.length - 1;    const currentPage = pageStack[lastIndex];    return currentPage;}// 获取页面 url 参数export function getParams() {    const currentPage = getCurrentPage() || {};    const allParams = {};    const { route, options } = currentPage;    if (options) {        const entries = objectEntries(options);        entries.forEach(            ([key, value]) => {                allParams[key] = decodeURIComponent(value);            }        );    }    return allParams;}// 按字段返回值export function getParam(name) {    const params = getParams() || {};    return params[name];}

参数过长怎么办?路由 api 不支持携带参数呢?

虽然微信小程序官方文档没有说明可以页面携带的参数有多长,但还是可能会有参数过长被截断的风险。

我们可以使用全局数据记录参数值,同时解决 url 参数过长和路由 api 不支持携带参数的问题。

// global-data.js// 由于 switchTab 不支持携带参数,所以需要考虑使用全局数据存储// 这里不管是不是 switchTab,先把数据挂载上去const queryMap = {    page: '',    queries: {}};

更新跳转函数

export function jumpTo(url, options) {    // ...    Object.assign(queryMap, {        page: baseUrl,        queries: options    });    // ...    if (jumpType === 'switchTab') {        wx.switchTab({ url: baseUrl });    } else if (jumpType === 'navigateTo' && pageCount < 5) {        wx.navigateTo({             url,            fail: () => {                 wx.switch({ url: baseUrl });            }        });    } else {        wx.navigateTo({             url,            fail: () => {                 wx.switch({ url: baseUrl });            }        });    }}

url 参数获取辅助函数

// 获取页面 url 参数export function getParams() {    const currentPage = getCurrentPage() || {};    const allParams = {};    const { route, options } = currentPage;    if (options) {        const entries = objectEntries(options);        entries.forEach(            ([key, value]) => {                allParams[key] = decodeURIComponent(value);            }        );+        if (isTabBar(route)) {+           // 是 tab-bar 页面,使用挂载到全局的参数+           const { page, queries } = queryMap; +           if (page === `${route}`) {+               Object.assign(allParams, queries);+           }+        }    }    return allParams;}

辅助函数

// 判断当前路径是否是 tabBarconst { tabBar} = appConfig;export isTabBar = (route) => tabBar.list.some(({ pagePath })) => pagePath === route);

按照这样的逻辑的话,是不是都不用区分是否是 isTabBar 页面了,全部页面都从 queryMap 中获取?这个问题目前后续探究再下结论,因为我目前还没试过从页面实例的 options 中拿到的值是缺少的。所以可以先保留读取 getCurrentPages 的值。

方法五 EventChannel 事件派发通信

前面我谈到从「当前页面A」传递数据到被打开的「页面B」可以通过 url 参数。那么想获取被打开页面传送到当前页面的数据要如何做呢?是否也可以通过 url 参数呢?

答案是可以的,前提是不需要保存「页面A」的状态。如果要保留「页面 A」的状态,就需要使用 navigateBack 返回上一页,而这个 api 是不支持携带 url 参数的。

这样时候可以使用 页面间事件通信通道 EventChannel。

pageA 页面

// wx.navigateTo({    url: 'pageB?id=1',    events: {        // 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据        acceptDataFromOpenedPage: function(data) {          console.log(data)         },    },    success: function(res) {        // 通过eventChannel向被打开页面传送数据        res.eventChannel.emit('acceptDataFromOpenerPage', { data: 'test' })    }});

pageB 页面

Page({    onLoad: function(option){        const eventChannel = this.getOpenerEventChannel()        eventChannel.emit('acceptDataFromOpenedPage', {data: 'test'});           // 监听acceptDataFromOpenerPage事件,获取上一页面通过eventChannel传送到当前页面的数据        eventChannel.on('acceptDataFromOpenerPage', function(data) {          console.log(data)        })      }})

会出现数据无法监听的情况吗?

小程序的栈不超过 10 层,如果当前「页面A」不是第 10 层,那么可以使用 navigateTo 跳转保留当前页面,跳转到「页面B」,这个时候「页面B」填写完毕后传递数据给「页面A」时,「页面A」是可以监听到数据的。

如果当前「页面A」已经是第10个页面,只能使用 redirectTo 跳转「PageB」页面。结果是当前「页面A」出栈,新「页面B」入栈。这个时候将「页面B」传递数据给「页面A」,调用 navigateBack 是无法回到目标「页面A」的,因此数据是无法正常被监听到。

不过我分析做过的小程序中,栈中很少有10层的情况,5 层的也很少。因为调用 wx.navigateBackwx.redirectTo 会关闭当前页面,调用 wx.switchTab 会关闭其他所有非 tabBar 页面。

所以很少会出现这样无法回到上一页面以监听到数据的情况,如果真出现这种情况,首先要考虑的不是数据的监听问题了,而是要保证如何能够返回上一页面。

比如在「PageA」页面中先调用 getCurrentPages 获取页面的数量,再把其他的页面删除,之后在跳转「PageB」页面,这样就避免「PageA」调用 wx.redirectTo导致关闭「PageA」。但是官方是不推荐开发者手动更改页面栈的,需要慎重。

如果有读者遇到这种情况,并知道如何解决这种的话,麻烦告知下,感谢。

使用自定义的事件中心 EventBus

除了使用官方提供的 EventChannel 外,我们也可以自定义一个全局的 EventBus 事件中心。因为这样更加灵活,不需要在调用 wx.navigateTo 等APi里传入参数,多平台的迁移性更强。

export default class EventBus { private defineEvent = {}; // 注册事件 public reGISter(event: string, cb): void {   if(!this.defineEvent[event]) {   (this.defineEvent[event] = [cb]);   }  else {   this.defineEvent[event].push(cb);   }  } // 派遣事件 public dispatch(event: string, arg?: any): void {  if(this.defineEvent[event]) {{            for(let i=0, len = this.defineEvent[event].length; i<len; ++i) {                 this.defineEvent[event][i] && this.defineEvent[event][i](arg);             }        }} } // on 监听 public on(event: string, cb): void {  return this.register(event, cb);  } // off 方法    public off(event: string, cb?): void {        if(this.defineEvent[event]) {            if(typeof(cb) == "undefined") {                 delete this.defineEvent[event]; // 表示全部删除             } else {                // 遍历查找                 for(let i=0, len=this.defineEvent[event].length; i<len; ++i) {                     if(cb == this.defineEvent[event][i]) {                        this.defineEvent[event][i] = null; // 标记为空 - 防止dispath 长度变化                         // 延时删除对应事件                        setTimeout(() => this.defineEvent[event].splice(i, 1), 0);                         break;                     }                }            }        }     }    // once 方法,监听一次    public once(event: string, cb): void {         let onceCb = arg => {         cb && cb(arg);          this.off(event, onceCb);         }        this.register(event, onceCb);     }    // 清空所有事件    public clean(): void {        this.defineEvent = {};     }}export connst eventBus = new EventBus();

在 PageA 页面监听:

eventBus.on('update', (data) => console.log(data));

在 PageB 页面派发

eventBus.dispatch('someEvent', { name: 'naluduo233'});

关于“微信小程序中如何自定义组件”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

--结束END--

本文标题: 微信小程序中如何自定义组件

本文链接: https://www.lsjlt.com/news/325647.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 微信小程序中如何自定义组件
    这篇文章将为大家详细讲解有关微信小程序中如何自定义组件,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。在微信小程序开发过程中,对于一些可能在多个页面都使用的页面模块,可以把它封装成一个组件,以提高开发效率。...
    99+
    2023-06-29
  • 微信小程序如何自定义tabbar组件
    这篇文章主要讲解了“微信小程序如何自定义tabbar组件”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“微信小程序如何自定义tabbar组件”吧!由于项目需求,必须自己写组件:第一步:在App...
    99+
    2023-06-14
  • 【微信小程序】自定义组件(二)
    🎁写在前面: 观众老爷们好呀,这里是前端小刘不怕牛牛频道,小程序系列文章又更新了呀。 上文我们讲解了微信小程序自定义组件的入门知识,那么今天牛牛就来讲讲自定义组件的进阶知识吧,...
    99+
    2023-09-01
    微信小程序 小程序 前端
  • 微信小程序自定义tabbar组件
    本文实例为大家分享了微信小程序自定义tabbar组件的具体代码,供大家参考,具体内容如下 由于项目需求,必须自己写组件: 第一步:在App.json中配置tabBar,自定也组件也必...
    99+
    2024-04-02
  • 【微信小程序】自定义组件(一)
    🎁写在前面: 观众老爷们好呀,这里是前端小刘不怕牛牛频道,小程序系列文章又更新了呀。 今天牛牛带来的是微信小程序的自定义组件入门知识,赶紧拿起小本本做笔记呀! ...
    99+
    2023-09-05
    微信小程序 前端 小程序
  • 微信小程序怎么自定义组件
    这篇文章主要讲解了“微信小程序怎么自定义组件”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“微信小程序怎么自定义组件”吧!组件模版和样式类似于页面,自定义组件拥有自己的 wxml&n...
    99+
    2023-06-26
  • 微信小程序自定义地址组件
    本文实例为大家分享了微信小程序自定义地址组件的具体代码,供大家参考,具体内容如下 项目需求 需要调用后台传过来的地址,存储地址时存的是地址的id,所以市面上的地址组件均不符合我的需求...
    99+
    2024-04-02
  • 微信小程序button组件如何自定义样式
    本篇内容主要讲解“微信小程序button组件如何自定义样式”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“微信小程序button组件如何自定义样式”吧!背景颜色wxml代码<button c...
    99+
    2023-06-26
  • 微信小程序自定义组件(超详细)
    💌写在开头: 哈喽呀,亲爱的宝子们。 今天要介绍的是关于小程序自定义组件的相关内容。 主要分以下几个部分:组件的创建,组件的结构,组件的引用,组件样式,组件的生命周期等。 ...
    99+
    2023-09-09
    微信小程序 小程序 前端
  • 微信小程序如何实现自定义弹窗组件
    本篇内容主要讲解“微信小程序如何实现自定义弹窗组件”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“微信小程序如何实现自定义弹窗组件”吧!首先,放一下,最终的效果图:这是我们最后要实现的效果那么,首...
    99+
    2023-07-02
  • 微信小程序中如何自定义创建和使用组件
    在微信小程序中,可以通过使用自定义组件来实现对页面上重复使用的部分进行封装和复用。下面是在微信小程序中自定义创建和使用组件的步骤: ...
    99+
    2024-04-09
    微信小程序
  • 微信小程序自定义顶部导航组件
    本文实例为大家分享了微信小程序自定义顶部导航组件,供大家参考,具体内容如下 在components中新建文件夹navbar components/navbar.wxml <!-...
    99+
    2024-04-02
  • 微信小程序如何自定义可搜索的picker组件
    本篇内容介绍了“微信小程序如何自定义可搜索的picker组件”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!代码:myPicker.wxml&...
    99+
    2023-07-02
  • 微信小程序自定义组件找不到如何解决
    如果在开发微信小程序时遇到自定义组件找不到的问题,可以按照以下步骤进行排查和解决:1. 确认自定义组件文件夹是否存在于小程序项目的正...
    99+
    2023-09-29
    微信小程序
  • 微信小程序实现自定义拍摄组件
    微信小程序实现自定义摄像头(在摄像头内添加提示信息),供大家参考,具体内容如下 摄像头组件(wxml) <!-- 拍照功能 自定义摄像头           bindtap:t...
    99+
    2024-04-02
  • 微信小程序怎么自定义地址组件
    本文小编为大家详细介绍“微信小程序怎么自定义地址组件”,内容详细,步骤清晰,细节处理妥当,希望这篇“微信小程序怎么自定义地址组件”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。项目需求需要调用后台传过来的地址,存储...
    99+
    2023-07-02
  • 【小程序】微信小程序自定义组件Component详细总结
    1- 前言 在本文中你将收获 小程序如何使用自定义组件自定义组件之间的传值自定义组件中插槽的使用 2- 组件文件新建 2.1 定义组件 在根目录新建components文件夹,建立cell 文件夹,...
    99+
    2023-09-23
    微信小程序 小程序 前端 开发语言 javascript
  • 微信小程序中如何自定义样式
    在微信小程序中,可以通过以下几种方式自定义样式: 使用全局样式:在app.wxss文件中定义全局样式,然后在页面或组件中引用。全...
    99+
    2024-04-02
  • 微信小程序中怎么自定义一个components组件
    本篇文章为大家展示了微信小程序中怎么自定义一个components组件,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。小程序自定义组件找到components目录,没...
    99+
    2024-04-02
  • 微信小程序如何实现自定义模态弹窗组件
    这篇文章主要介绍微信小程序如何实现自定义模态弹窗组件,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!小程序自带一个showModal弹窗,调用很简单,但是限制很多,title有字数限制...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作