【问题标题】:Vue keep alive component when moving to other arrayVue在移动到其他数组时保持活动组件
【发布时间】:2018-09-18 20:12:51
【问题描述】:

在将绑定的项目对象移动到不同的数据数组时,我试图让组件保持活动状态。因为它被移动了,所以默认的 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' }
        ]
      }
    ]
  }
})

【问题讨论】:

  • 我没有看到任何组件破坏,也没有看到任何库。你的问题很不清楚。我还看到了一些道具,但没有父组件和一些 Vanilla JS:document.body.insertAdjacentHTML('beforeend', 'Mounted!') 我相信即使我不知道你是什么,也有更好的方法来做到这一点正在努力实现。
  • 这只是一个复制问题的快速模型,而不是我的实际代码。在此示例中,当您将项目拖动到不同的列表时,它会重新渲染组件,从而再次安装它。 (因此肮脏的 js 代码,以显示它确实如此)
  • 似乎draggable 暴露的事件对于您想要做的事情来说太早或太晚了。使用滴答声可能会做一些事情,但在我想知道为什么要让组件保持活动之前。因为如果是为了保存数据,我觉得可以使用draggablecomponentData选项。
  • 这是为了保存被 WYSIWYG 和图像编辑器等外部库更改的 DOM,因为这些可能需要一些时间来加载。数据没有问题,因为我在我的应用程序中使用 Vuex :)
  • 似乎 Vue.Draggable 在拖动时会在“放置区”中添加一个新组件并删除之前的组件。看来你需要修改他们的代码才能得到你需要的东西。

标签: javascript vue.js vuejs2 vue-component


【解决方案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>

反应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>

【讨论】:

  • 似乎在我的情况下加载模板之外的组件是唯一的方法。哦好的,谢谢!我会试一试,否则创建我自己对 Vue.Draggable 的看法,它只会在视觉上改变,数据中保存的顺序。
  • 可能不是唯一的方法,但我之前看过 vue-draggable 和 sortable 代码,它有点耗时。我目前在一个 Vue 项目中有 dragula - 打算通过添加一个内部组件(目前 v-for 在 &lt;li&gt; 上)来适应你的场景。如有不同,将发布。
  • 找到了一种方法来完成它,远非理想,但它有效: - 从可拖动中删除 v-model,使更改仅可视化 - 克隆初始列表,使用 v-for 的克隆- 手动捕获@sort,根据事件startIndex、endIndex、from element和to element手动更改真实列表。 - 如果您从列表中添加或删除项目,请为两个列表执行此操作。
  • 这听起来是个好主意 - 但你必须克隆列表吗?
  • 顺便说一句,dragula 似乎没有重新创建从一个列表拖到另一个列表的内部 vue 组件。
【解决方案2】:

我调查了你的问题,我想我可能找到了解决方案,我不能在 js fiddle 中做到这一点,但我会尝试解释它:

在你的 js fiddle 中,mounted 挂在你的 list-item 组件中,所以每次状态改变(拖动时)时,都会触发事件。

我用一个主要的模板化组件 (componentX) 创建了一个设置,并带有一个挂载的函数,然后创建了一个单独的列表项组件

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

您可以从以下网址以 zip 格式下载解决方案:

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

它是一个vue cli项目,所以你可以npm run dev来启动一个本地服务器

【讨论】:

  • 你能重新上传一下吗? 404 未找到。
  • @Stanniebeer,你的问题有什么进展吗,因为赏金明天就结束了?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-06
  • 1970-01-01
  • 2017-08-20
  • 1970-01-01
  • 2022-01-10
  • 2017-12-23
  • 1970-01-01
相关资源
最近更新 更多