成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Vue3自定義指令實踐:使用h函數渲染自定義組件到指令中

開發 前端
為了可以應對更多場景,我們期望可以配置加載中的提示信息,不配置使用默認值,如果是 false ,那么僅展示 loading 圖。

?? 關鍵接口介紹

最近想體驗下自定義指令功能,看了看文檔和 vue2 差異不大,語法如下:

const myDirective = {
// 在綁定元素的 attribute 前 
// 或事件監聽器應用前調用
created(el, binding, vnode, prevVnode) 
{ // 下面會介紹各個參數的細節 }, 
// 在元素被插入到 DOM 前調用
beforeMount(el, binding, vnode, prevVnode) {},
// 在綁定元素的父組件
// 及他自己的所有子節點都掛載完成后調用
mounted(el, binding, vnode, prevVnode) {},
// 綁定元素的父組件更新前調用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在綁定元素的父組件
// 及他自己的所有子節點都更新后調用
updated(el, binding, vnode, prevVnode) {},
// 綁定元素的父組件卸載前調用
beforeUnmount(el, binding, vnode, prevVnode) {},
// 綁定元素的父組件卸載后調用
unmounted(el, binding, vnode, prevVnode) {}
}

起初,最大的痛點是需要手動創建 dom ,然后插入 el 中。

const loadingDom = document.createElement('div', {calss: 'loading'})
el.append(loadingDom)

這樣好難受啊,我不想寫原生 dom ,能不能寫個組件渲染到指令里呢?

我想起了我之前看到的幾個 vue 接口,

  • h函數,也就是 vue 提供的創建 vNode 的函數
  • render函數:將 vNode 渲染到 真實 dom 里的函數

h函數用法如下:

// 完整參數簽名
function h(
    type: string | Component,
    props?: object | null,
    children?: Children | Slot | Slots
): VNode

例如:

import { h } from 'vue'

const vnode = h('div', { class: 'container' }, [
  h('h1', 'Hello, Vue 3'),
  h('p', 'This is a paragraph')
])

我們使用h函數創建了一個 VNode,它表示一個包含 div、h1、p 的 DOM 結構。其中,div 的 class 屬性為 container

?? 自定義 loading 組件

然而,當我使用 props 為組件傳遞值時,發現是徒勞的。

import Loading from './components/Loading.vue';

cconst option = {
    msg: '一大波僵尸來襲',
    loading: true
}

const vnode = h(
    Loading,
    { id: 'loading', ...option}
)

僅僅如下圖一樣,我以為是給組件的 props,實際上是 dom 的 props。

圖片圖片

此時我們的組件只能通過 $attrs 來獲取這些 props 了,如下:

<template>

  <div class="loading">
    <div></div>
    <span v-if="$attrs.msg !== false">{{ $attrs.msg }}</span>
  </div>
  
</template>

接著我們給組件實現 loading 動畫,當然你也可以直接使用組件庫的 loading 組件。

我的實現如下:

<style>
  @keyframes identifier {
    100% {
      -webkit-transform: rotate(360deg);
      transform: rotate(360deg);
    }
  }
  .loading {
    height: 100px;
    width: 100%;
  }
  .loading div {
    width: 50px;
    height: 50px;
    border-radius: 50%;
    border: 2px solid green;
    margin: 25px auto;
    border-top: none;
    border-left: none;
    animation: identifier 1s infinite linear;
  }
</style>

?? 自定義指令

接下來我們封裝自定義指令。

我們的思路是:

  • mounted 階段,如果是 true,那么渲染組件,否則什么都不做。
  • update 階段,如果 true 則重新渲染組件,如果 false 則渲染 vnode

為了可以應對更多場景,我們期望可以配置加載中的提示信息,不配置使用默認值,如果是 false ,那么僅展示 loading 圖。所以參數類型如下:

interface Props  {loading: boolean, msg?: string | false}

v-loading: boolean | Props

由于可能是布爾值,也可能是對象,我們需要初始化配置參數

function formatOption (value: boolean | Props) {
  const loading = typeof value === 'boolean'
      ? value 
      : value.loading
  const option = typeof value !== 'boolean'
    ? Object.assign(defaultOption, value)
    : {
      loading,
      ...defaultOption
    }
  return { loading, option }
}

接著再 mounted 階段獲取格式化后的 loading 和 option,如果為 true 則直接渲染組件。

const vLoading: Directive<HTMLElement, boolean | Props> = {
  mounted(el, binding) {

    const { loading, option } = formatOption(binding.value)

    loading && renderLoading(el, option)

  }
}

function renderLoading (el: HTMLElement, option: Props) {
  const vnode = h(
    Loading,
    { id: 'loading', ...option}
  )
  el.removeChild(el.children[0])
  render(vnode, el)
}

如果進入 update 階段,則根據情況選擇渲染 laoding 組件還是 vnode。

const vLoading: Directive<HTMLElement, boolean | Props> = {
  mounted(el, binding) {

    const { loading, option } = formatOption(binding.value)

    loading && renderLoading(el, option)

  },
  updated(el: HTMLElement, binding, vnode) {

    const { loading, option } = formatOption(binding.value)

    if (loading) {
      renderLoading(el, option)
    } else {
      renderVNode(el, vnode)
    }

  },
}

function renderLoading (el: HTMLElement, option: Props) {
  const vnode = h(
    Loading,
    { id: 'loading', ...option}
  )
  el.removeChild(el.children[0])
  render(vnode, el)
}

function renderVNode (el: HTMLElement, vnode: VNode) {
  el.querySelector('#loading')?.remove()
  render(vnode, el)
}

我們驗證下功能:

  • 默認功能
const loading = ref(true)

setTimeout(() => loading.value = false, 2000)

</script>

<template>
  <div style="width: 300px" v-loading=laoding>
    <h1>加載成功</h1>
  </div>
</template>

圖片圖片

  • 自定義文本
<template>
  <div style="width: 300px" v-loading="{ loading, msg: '一大波僵尸來襲' }">
    <h1>加載成功</h1>
  </div>
</template>

圖片圖片

  • 不展示文本
<template>
  <div style="width: 300px" v-loading="{ loading, msg: false }">
    <h1>加載成功</h1>
  </div>
</template>

圖片圖片

?? 最后

今天的分享就到這了,如果有問題,可以評論區告訴我,我會及時更正。

以下是完整的代碼。

<template>

  <div class="loading">
    <div></div>
    <span v-if="$attrs.msg !== false">{{ $attrs.msg }}</span>
  </div>
  
</template>
  
<script lang="ts">
export default {  
}
</script>
  
<style>
  @keyframes identifier {
    100% {
      -webkit-transform: rotate(360deg);
      transform: rotate(360deg);
    }
  }
  .loading {
    height: 100px;
    width: 100%;
  }
  .loading div {
    width: 50px;
    height: 50px;
    border-radius: 50%;
    border: 2px solid green;
    margin: 25px auto;
    border-top: none;
    border-left: none;
    animation: identifier 1s infinite linear;
  }
</style>
<script setup lang="ts">
import { Directive, VNode, h, ref, render  } from 'vue';
import Loading from './components/Loading.vue';

const defaultOption: {msg?: string | false} = {
  msg: '努力加載中'
}

interface Props  {loading: boolean, msg?: string | false}

function formatOption (value: boolean | Props) {
  const loading = typeof value === 'boolean'
      ? value 
      : value.loading
  const option = typeof value !== 'boolean'
    ? Object.assign(defaultOption, value)
    : {
      loading,
      ...defaultOption
    }
  return { loading, option }
}

function renderLoading (el: HTMLElement, option: Props) {
  const vnode = h(
    Loading,
    { id: 'loading', ...option}
  )
  el.removeChild(el.children[0])
  render(vnode, el)
}

function renderVNode (el: HTMLElement, vnode: VNode) {
  el.querySelector('#loading')?.remove()
  render(vnode, el)
}

const vLoading: Directive<HTMLElement, boolean | Props> = {
  mounted(el, binding) {

    const { loading, option } = formatOption(binding.value)

    loading && renderLoading(el, option)

  },
  updated(el: HTMLElement, binding, vnode) {

    const { loading, option } = formatOption(binding.value)

    if (loading) {
      renderLoading(el, option)
    } else {
      renderVNode(el, vnode)
    }

  },
}

const loading = ref(true)

setTimeout(() => loading.value = false, 2000)

</script>

<template>
  <div style="width: 300px" v-loading="{ loading, msg: '一大波僵尸來襲' }">
    <h1>加載成功</h1>
  </div>
</template>

責任編輯:武曉燕 來源: 萌萌噠草頭將軍
相關推薦

2021-11-30 08:19:43

Vue3 插件Vue應用

2022-07-26 01:06:18

Vue3自定義指令

2022-02-22 13:14:30

Vue自定義指令注冊

2021-07-05 15:35:47

Vue前端代碼

2023-07-21 19:16:59

OpenAIChatGPT

2020-12-28 10:10:04

Vue自定義指令前端

2024-09-26 14:16:07

2022-08-01 11:41:00

Vue插件

2010-10-25 16:05:07

oracle自定義函數

2022-04-24 15:17:56

鴻蒙操作系統

2010-09-14 16:47:23

SQL自定義函數

2010-05-11 13:16:21

Unix awk

2017-05-19 10:03:31

AndroidBaseAdapter實踐

2023-12-21 09:00:21

函數React 組件useEffect

2017-05-18 12:36:16

android萬能適配器列表視圖

2015-02-12 15:33:43

微信SDK

2024-06-03 10:00:51

Vue 3語法插槽

2023-08-11 09:16:14

2023-02-20 15:20:43

啟動頁組件鴻蒙

2009-06-24 15:13:36

自定義JSF組件
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 色资源站 | 九九九久久国产免费 | 国产农村一级国产农村 | 视频一区二区中文字幕 | 综合精品久久久 | 国产成人一区二区三区 | 婷婷综合色 | 天天天操天天天干 | 欧美成人猛片aaaaaaa | 免费毛片www com cn | 成人免费观看男女羞羞视频 | 中文字幕在线观看国产 | 精品1区2区 | 国产精品国产精品国产专区不蜜 | 日本淫视频 | 久久人人爽人人爽人人片av免费 | 黄毛片 | 亚洲成人av在线 | 欧美性高潮 | 蜜桃视频在线观看免费视频网站www | 国产精品成av人在线视午夜片 | 国产xxxx搡xxxxx搡麻豆 | 成人深夜福利 | 国产福利在线播放 | 成人在线观看免费 | 乱码av午夜噜噜噜噜动漫 | 亚洲小说图片 | 国产激情一区二区三区 | www日本高清 | 草久久 | 在线免费观看视频黄 | 成人午夜网站 | 日韩电影免费在线观看中文字幕 | 中文字幕国产精品 | a级片播放| 日本不卡免费新一二三区 | 国产精品毛片 | 狠狠的干狠狠的操 | 亚洲日产精品 | 国产日韩欧美一区 | 91久久精品国产91久久性色tv |