代码之家  ›  专栏  ›  技术社区  ›  coyotte508

实现v-loading指令,在加载数据时替换内容

  •  0
  • coyotte508  · 技术社区  · 7 年前

    我正在尝试实现 v-loading Vue中的指令,其中元素的内容由微调器替换,而表达式的值为true。

    使用方法如下:

    <div v-loading="loading">
      <p v-if="!data"> No data </p>
      <p v-else>Here is the data: {{data}}</p>
    </div>
    

    下面是我的实现(typescript):

    import Vue from 'vue';
    
    Vue.directive('loading', {
      bind(el: HTMLElement, binding: any, vnode: any) {
        console.log(vnode);
        // console.log(this, arguments);
        vnode.data.html = el.innerHTML;
        vnode.data.loading = binding.value;
    
        if (binding.value) {
          el.innerHTML = `<i class='fa fa-spin fa-spinner'></i>`;
        } else {
          el.innerHTML = vnode.data.html;
        }
      },
    
      update(el: HTMLElement, binding: any, vnode: any, oldVnode: any) {
        console.log(el.innerHTML);
        // console.log("update", this, arguments);
    
        if (binding.value) {
          el.innerHTML = `<i class='fa fa-spin fa-spinner'></i>`;
        } else {
          el.innerHTML = oldVnode.data.html;
        }
    
        vnode.data.html = oldVnode.data.html;
        vnode.data.loading = binding.value;
      }
    });
    

    它可以工作一些,但是呈现的HTML是元素创建时的HTML。所有的动态渲染都将丢失。

    我可以创建一个 V形加载 组件:

    <template>
      <div>
        <slot v-if="!loading"></slot>
        <i v-else class="fa fa-spin fa-spinner"></i>
      </div>
    </template>
    

    但我宁愿接受这个指示。不幸的是,我对Vue框架和Vnodes的了解还不够,来自Vnodes上API的文档重定向到 here 信息不多。

    有什么想法吗?

    1 回复  |  直到 7 年前
        1
  •  1
  •   Sphinx    7 年前

    Vue instance Life cycle el re-mount

    Vue.config.productionTip = false
    let vMyDirective = {}
    vMyDirective.install = function install (_Vue) {
      let _uid = 'vue-directive-loading' + Date.now().toString('16')
      _Vue.directive('loading', {
        inserted: function (el, binding) {
          let spinner = document.createElement('span')
          spinner.id = _uid
          spinner.innerHTML = 'Loading...'
          spinner.style.display = binding.value ? 'block' : 'none'
          spinner.style['background-color'] = 'red'
          spinner.left = 0
          spinner.top = 0
          spinner.style.position = 'absolute'
          el.childNodes.forEach((item) => {
            item.style.display = binding.value ? 'none' : ''
          })
          el.appendChild(spinner)
        },
        update: function (el, binding, vnode) {
          let spinner = document.getElementById(_uid)
          spinner.style.display = binding.value ? 'block' : 'none'
          el.childNodes.forEach((item) => {
            if(item.id === _uid) return
            item.style.display = binding.value ? 'none' : ''
          })
        }
      })
    }
    
    Vue.use(vMyDirective)
    
    new Vue({
      el: '#app',
      data() {
        return {
          loading: true,
          dataset: ''
        }
      },
      methods:{
        toggleLoading: function() {
          this.loading = !this.loading
        },
        AddData: function () {
          this.dataset = this.dataset + 'a'
        }
      }
    })
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
    <div id="app">
      <button v-on:click="toggleLoading()">Toggle Loading {{loading}}</button>
      <button v-on:click="AddData()">Add Data</button>
      <div v-loading="loading">
        <p v-if="!dataset"> No data </p>
        <p v-else>Here is the data: {{dataset}}</p>
      </div>
    </div>

    loading === false

    Vue.config.productionTip = false
    let vMyDirective = {}
    vMyDirective.install = function install (_Vue) {
      _Vue.directive('loading', {
        bind: function (el, binding, vnode) {
          if(binding.value) {
            el.innerHTML = '<span style="background-color:red;top:0;left;0">loading...</span>'
          }
        },
        update: function (el, binding, vnode) {
          if(binding.value) {
            el.innerHTML = '<span style="background-color:red;top:0;left;0">loading...</span>'
          } else {
            vnode.key += '1'
            vnode.context.$forceUpdate()
          }
        }
      })
    }
    
    Vue.use(vMyDirective)
    
    new Vue({
      el: '#app',
      data() {
        return {
          loading: true,
          dataset: ''
        }
      },
      methods:{
        toggleLoading: function() {
          this.loading = !this.loading
        },
        AddData: function () {
          this.dataset = this.dataset + 'a'
        }
      }
    })
    <
    推荐文章