最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 你欠我一个全局组件,咋办?

    正文概述 掘金(muchan92)   2021-01-15   572

    众所周知,小程序不是一个单页应用,它的每一个页面都是一个独立的 WebView,又因为没有提供可以跨越多页面的渲染能力,所以没有办法实现全局的可以跨越多页面的组件。

    但是群众们有智慧啊,那我们就麻烦点,每个页面都手动引入一次组件好了,反正不管咋样,总算能实现了,算是填了一个坑。
    这其中具体的实现方案多种多样,各有千秋,无论是渲染体验,还是 coding 调用体验,都千差万别,难以详述。

    这篇来描述一种还不错的实现方案,以 uni-app 为例(原生,及其他框架类同),来一步步实现全局组件的效果。

    在开始之前,先重新简单梳理下小程序的页面栈。

    • 每个页面都是一个独立的 WebView
    • 底部的 Tab 页面是固定的,一旦打开就只会创建一次并且不会销毁,当切换回该页面时会重新位于页面栈的顶部
    • 其他非 Tab 页面当打开时会新建一次,并置于页面栈顶部;当打开新页面时,新页面置顶,旧页面被压在了下面;当返回上一页时,当前页面销毁,上一页重新置于页面栈顶部

    正是由于页面栈的存在,以及每个页面的独立性、隔离性,导致实现可以跨越多页面的全局组件复杂重重。

    由上可知,理论上来讲,没有办法可以真正实现覆盖在所有页面之上的全局组件。
    不过我们可以模拟来实现,模拟的恰到好处,就是个中差别了,可以模拟一个体验不错的全局组件出来。

    首先,我们定几个目标:

    • 要少写代码
    • 要简单易用
    • 支持多个组件

    一. 封装一个组件容器

    封装一个组件容器,将多个组件全部引入这个容器,这样一来,我们就可以在 Page 上只引入一次这个组件容器,就相当于引入了全部的全局组件。

    为了降低实现和使用的复杂性,这个组件容器就仅仅只是一个组件容器而已。
    另外,我们也额外在这里封装下简单的组件注册函数。

    <!-- leaf.vue -->
    <template>
      <view>
        <image-edit />
        <notification />
        <alert />
      </view>
    </template>
    
    <script>
    // 全局 API
    uni.$leaf = {
      $register(vm, name) { // 注册组件
        if (!uni.$leaf[name]) {
          // 根据 name 注册组件 API
          uni.$leaf[name] = (data) => {
            // 为了“简单易用”,所以返回 Promise
            return Promise((resolve, reject) => {
              const pages = getCurrentPages()
              let vm = pages[pages.length - 1]
              // #ifdef MP-WEIXIN
              vm = vm.$vm
              // #endif
              uni.$emit('leaf/' + name, {
                $route: vm.$route, // 用于判断区分当前的页面栈
                data,
                resolve,
                reject,
              })
            })
          }
        }
    
        // 封装事件,以简化自定义组件
        const handler = (e) => {
          if (e.$route !== vm.$route || e.$stop) {
            return
          }
          // 只有栈顶的页面才能获得事件,
          // 这样就能保证,无论 API 调用是由哪个页面或页面内的子组件发起,
          // 都能立刻显示在用户面前
          e.$stop = true
          // 调用组件上的事件侦听
          vm.leafHandler(e)
        }
        uni.$on('leaf/' + name, handler)
        vm.$on('hook:beforeDestroy', () => {
          uni.$off('leaf/' + name, handler)
        })
      },
    }
    
    import ImageEdit from './image-edit'
    import notification from './notification'
    import alert from './alert'
    
    export default {
      components: {
        ImageEdit,
        notification,
        alert,
      },
    }
    </script>
    

    我们可以直接在 Page 中引入这个组件容器。

    <!-- index.vue -->
    <template>
      <view>
        <leaf />
      </view>
    </template>
    
    <script>
    import leaf from '@/components/leaf'
    
    export default {
      components: {
        leaf,
      },
    }
    </script>
    

    二. 定义组件

    定义组件时,需要注册一下,并提供一个事件侦听函数来处理 API 的调用。
    我们努力保证简单性。

    <!-- alert.vue -->
    <template>
      <uni-popup ref="popup">
        <view>{{content}}</view>
        <button @click="confirm">确定</button>
        <button @click="cancel">取消</button>
      </uni-popup>
    </template>
    
    <script>
    export default {
      data() {
        return {
          content: '',
        }
      },
      created() {
        // 注册组件
        uni.$leaf.$register(this, 'alert')
      },
      methods: {
        leafHandler(e) {
          const { data } = e
          this.content = data.content
          this.$e = e // 缓存事件
          this.$refs.popup.open()
        },
        confirm() {
          this.$refs.popup.close()
          // 啊哈,我们可以使用 Promise 来管理组件的状态,可以为所欲为,
          // 这个 API 的实现方案很人性化吧。
          this.$e.resolve()
        },
        cancel() {
          this.$refs.popup.close()
          this.$e.reject()
        },
      },
    }
    </script>
    

    三. 使用

    现在我们可以很简单、很方便、很人性化的来使用全局组件了。
    前面之所以用 Promise 来封装,就是为了这里能人性化的使用,简单易用才令人愉快。

    <!-- index.vue -->
    <template>
      <view>
        <button @click="testAlert">测试下弹框</button>
        
        <leaf />
      </view>
    </template>
    
    <script>
    import leaf from '@/components/leaf'
    
    export default {
      components: {
        leaf,
      },
      methods: {
        async testAlert() {
          try {
            // 用最少的代码,简单测试下效果
            await uni.$leaf.alert({ content: '你觉得这个方案还可吗?' })
          } catch (error) {
            uni.showToast({ title: '有啥好建议么?' })
          }
        },
      },
    }
    </script>
    

    总结

    前面定义的几个目标应该、也许、大概,算是实现了吧。

    关注一下

    下一篇介绍一下小程序的 只有在需要时才获取用户授权 的简单易用、人性化的实现方案。
    这是一个很普通、很合理的可以提高用户体验的需求,但是一般实现起来却并不简单,真正手写实践起来有点令人不愉快。
    那么就等着一个好方案来实现吧,求关注一下哈。


    下载网 » 你欠我一个全局组件,咋办?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元