移动到其他数组时,Vue保持活动组件

时间:2018-04-09 12:15:58

标签: javascript vue.js vuejs2 vue-component

我在将绑定的项目对象移动到不同的数据数组时尝试保持组件处于活动状态。因为它被移动了,默认的keep-alive标签不起作用。

当我的应用中的动态组件使用外部库时,我需要这样做以改善加载时间。

简化示例:https://jsfiddle.net/eywraw8t/24419/

HTML:

<div id="app">
  <div v-for="list in lists">
    <h1>{{ list.title }}</h1>
    <ul>
      <draggable v-model="list.items" :options="{group: 'list-items'}">
        <list-item 
           v-for="item in list.items" 
           :key="item.key" 
           :content="item.content">
        </list-item>
      </draggable>
    </ul>
  </div>
</div>

JS:

Vue.component('list-item', {
  props: {
    content: {
        required: true
    }
  },
  mounted () {
    document.body.insertAdjacentHTML('beforeend', 'Mounted! ');
  },
  template: '<li>{{ content }}</li>'
})

new Vue({
  el: "#app",
  data: {
    lists: [
        {
        title: 'List 1',
        items: [
            { key: 'item1', content: 'Item 1' },
          { key: 'item2', content: 'Item 2' },
          { key: 'item3', content: 'Item 3' }
        ]
      },
      {
        title: 'List 2',
        items: [
            { key: 'item4', content: 'Item 4' },
          { key: 'item5', content: 'Item 5' },
          { key: 'item6', content: 'Item 6' }
        ]
      }
    ]
  }
})

2 个答案:

答案 0 :(得分:1)

我调查了你的问题,我想我可能会找到一个解决方案,我不能用js小提琴做,但我会试着解释一下:

在你的js小提琴中,mount被挂钩在你的list-item组件中,所以每次状态改变时(拖动时)都会触发事件。

我创建一个带有主模板组件(componentX)的设置,带有挂载的功能,然后创建一个单独的列表项组件

在我的示例中,您将看到在开始时挂载两次,这是正常的,因为我们有2个列表!但是当你开始拖放时,你将无法获得额外的已安装事件

您可以通过以下方式下载解决方案:

http://www.bc3.eu/download/test-vue.zip

这是一个vue cli项目,因此您只需npm run dev即可启动本地服务器

答案 1 :(得分:1)

如果问题只是缓存昂贵的html构建问题,您可以通过从模板中删除list-item组件并在app.mounted()中提前构建它们来实现。

这在现实场景中的效果取决于item.content的性质及其生命周期。

console.clear()
const ListItem = Vue.component('list-item', {
  props: {
    content: {
      required: true
    }
  },
  mounted () {
    document.body.insertAdjacentHTML('beforeend', 'Mounted! ');
  },
  template: '<li>{{ content }}</li>'
})

new Vue({
  el: "#app",
  methods: {
    getHtml(content) {
      const li = new ListItem({propsData: {content}});
      li.$mount()
      return li.$el.outerHTML
    }
  },
  mounted () {
    this.lists.forEach(list => {
      list.items.forEach(item => {
        const cacheHtml = this.getHtml(item.content)
        Vue.set( item, 'cacheHtml', cacheHtml )
      })
    })
  },
  data: {
    lists: [
    	{
      	title: 'List 1',
        items: [
        	{ key: 'item1', content: 'Item 1' },
          { key: 'item2', content: 'Item 2' },
          { key: 'item3', content: 'Item 3' }
        ]
      },
      {
      	title: 'List 2',
        items: [
        	{ key: 'item4', content: 'Item 4' },
          { key: 'item5', content: 'Item 5' },
          { key: 'item6', content: 'Item 6' }
        ]
      }
    ]
  }
})
ul {
  margin-bottom: 20px;
}

li:hover {
  color: blue;
  cursor: move;
}

h1 {
  font-size: 20px;
  font-weight: bold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.6.0/Sortable.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/15.0.0/vuedraggable.min.js"></script>

<div id="app">
  <div v-for="list in lists">
    <h1>{{ list.title }}</h1>
    <ul>
      <draggable v-model="list.items" :options="{group: 'list-items'}">
        <div v-for="item in list.items" :key="item.key">
          <li v-html="item.cacheHtml"></li>   
        </div>
      </draggable>
    </ul>
  </div>
</div>

Reactive item.content

要在item.content更改时保持反应,您需要更多代码。

  • item.content的副本添加到缓存
  • 添加一个方法,如果内容已更改,则使用刷新来获取缓存的html。

(您可以使用参数化计算属性更优雅地执行此操作)。

为了模拟item.content更改,我在mount()中添加了setTimeout。

console.clear()
const ListItem = Vue.component('list-item', {
  props: {
    content: {
      required: true
    }
  },
  mounted () {
    document.body.insertAdjacentHTML('beforeend', 'Mounted! ');
  },
  template: '<li>{{ content }}</li>'
})

new Vue({
  el: "#app",
  methods: {
    getHtml(content) {
      const li = new ListItem({
        propsData: { content }
      });
      li.$mount()
      return li.$el.outerHTML
    },
    cacheHtml(item) {
      if (item.cache && item.cache.content === item.content) {
        return item.cache.html
      } else {
        const html = this.getHtml(item.content)
        const cache = {content: item.content, html} 
        Vue.set(item, 'cache', cache)
      }
    }
  },
  mounted () {
    this.lists.forEach(list => {
      list.items.forEach(item => {
        this.cacheHtml(item)
      })
    })
    setTimeout(() => 
      Vue.set( this.lists[0].items[0], 'content', 'changed' )
    ,2000)      
  },
  data: {
    lists: [
    	{
      	title: 'List 1',
        items: [
        	{ key: 'item1', content: 'Item 1' },
          { key: 'item2', content: 'Item 2' },
          { key: 'item3', content: 'Item 3' }
        ]
      },
      {
      	title: 'List 2',
        items: [
        	{ key: 'item4', content: 'Item 4' },
          { key: 'item5', content: 'Item 5' },
          { key: 'item6', content: 'Item 6' }
        ]
      }
    ]
  }
})
ul {
  margin-bottom: 20px;
}

li:hover {
  color: blue;
  cursor: move;
}

h1 {
  font-size: 20px;
  font-weight: bold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.6.0/Sortable.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/15.0.0/vuedraggable.min.js"></script>

<div id="app">
  <div v-for="list in lists">
    <h1>{{ list.title }}</h1>
    <ul>
      <draggable v-model="list.items" :options="{group: 'list-items'}">
        <div v-for="item in list.items" :key="item.key">
          <li v-html="cacheHtml(item)"></li>   
        </div>
      </draggable>
    </ul>
  </div>
</div>