2021年4月20日编辑 - 两年后,更加睿智
由于这个问题/答案引起了很多关注,在经过所有这些年之后仍然有效,我想提供一些指示。大部分细节仍然有效。不过,我建议在处理过滤结果和复杂对象时使用VueX与Lodash(或现代版本的本地JS函数)。
为了减轻后端压力,您可以保持简单:获取没有相关模型的纯粹对象。这意味着您的主要结果只有相关对象的ID键。使用Axios或类似的库获取所有相关数据,并使用VueX将它们存储在自己的列表属性中。为每个创建getter,例如:
projectsById: state => {
return _.keyBy(state.projects, "id")
},
这样,当需要时,您可以使用相关模型来获取标签和/或完整对象,而且您的后端不需要多次获取相关数据。状态和getter也将在微组件中可用。
基本上:处理大型数据集时,避免获取完整的模型树(即使C# EF或PHP Laravel提供了工具)。使用原子方法:获取20个不同的列表("Axios.all([...])"是您的朋友!),每个列表都有自己的控制器端点,并缓存结果到VueX存储...然后尽情享受 ;)
编辑12.03.2019 - 此答案末尾添加了额外的提示
我问这个问题已经有一段时间了,最终我优化了我的项目的这一部分。我想给那些遇到这些性能和/或内存问题的人提供一些指针。
Vue文档从未真正解释过它,但正如Andrey所指出的,您可以将组件对象用作自定义对象和对象列表的数据存储。毕竟,它只是一个普通的JavaScript对象。
优化后,我的列表组件设置看起来像这样:
module.exports = {
items: [],
mixins: [sharedUtils],
data: function() {
return {
columns: {
all: []
etc... Lot's of data & methods
items-array被填充了数千个复杂对象(大约80MB的数据,6MB压缩),我将其视为非响应式处理。这证明它比我想象的要少成问题 - 我没有直接使用v-for来遍历items,而是使用结构,在其中当用户点击某些过滤按钮和/或输入字符串过滤(例如名称)时触发对该数组的过滤。基本上,“processFilters”方法遍历非响应式items-array并返回filteredItems,它存储在数据上下文中。因此,它会自动变为响应式,因为它被改变了。
<tr v-for="item in filteredItems"
这样,过滤后的所有项目都保持反应性,但在被过滤掉时也失去了反应性,从而节省了大量内存。令人惊讶的是,将1200mb缩减到了400mb,这正是我所期望的。聪明!
还有一些问题需要解决。由于项目不存在于数据上下文中,因此无法直接在模板中使用它。这意味着,你不能直接写...
<div v-if="items.length > 0 && everythingElseIsReady">
我必须将items数组的长度存储到分离的数据属性中。这也可以通过计算值来解决,但我喜欢保留这些属性。
放弃主数据数组的反应性并不是一件坏事 - 最重要的是要理解,直接对基础数组中的项目进行的修改永远不会触发任何UI和/或子组件的更改(当然)。只要您以这样的方式将代码分离开来,即具有“隐藏数据容器”,其中保存来自后端的所有结果,并且您具有该大容器的较小(过滤)表示数组,那么这不应该是一个问题。通过使用良好的REST架构,您应该已经可以使用非响应式数据存储,只要您记得在保存非响应式数据存储中的项目后,也已更新为最新版本。
此外,我惊讶于微型组件数量与数百行相比性能上的影响很小。显然,呈现会受到影响,但即使我要传递大量的props数千次(因为我有数千个输入单元格实例),它似乎也没有影响内存。这种对象之一是我的全局翻译键/值对对象,具有超过20,000行翻译字符串...但它仍然无关紧要。这是有道理的,因为Javascript使用对象引用,Vue Core似乎已经正确编码,因此只要您将这样的配置对象用作props,您就只是从数千个对象到相同数据集的引用。
最后,我想说,开始疯狂地使用复杂的CRUD对象,不必担心内存限制!
非常感谢Andrey Popov给予正确方向的提示!
提示(2019年3月12日)
由于一段时间以来,我一直在使用大型和复杂的数据集构建UI,因此我决定提出一些简短的想法和技巧。
- 考虑如何管理主记录(即人员或产品)与相关记录(子对象/关系对象)。尝试限制注入到子组件中的数据量,因为您可能正在为不同的主记录表示相同的子对象多次。问题在于这些对象可能实际上并不是引用对象!
考虑这样一种情况,您有一个包含城市对象的人员对象。许多人住在同一个城市,但当您从后端获取JSON数据时,您确定那些重复的城市对象实际上是同一个城市(共享/引用人员之间的城市对象),还是类似对象的多个表示(数据完全相同,但在幕后每个对象都是单独的实例/唯一对象)。假设您有50,000个人,每个人都包含相同的子对象/属性“城市”:{id:4,name:“ Megatown”},您刚刚获取了50,000个单独的城市实例而不是一个吗? person1.city === person2.city,还是它们只是看起来相同,仍然是两个不同的对象?
如果您不确定是引用共享城市对象还是使用多个类似子对象的实例,您可以在person-list-component中进行引用。您的person包含city-id,因此使用单独的REST方法(getCities)获取城市列表,并在UI级别上进行配对。这样,您只有一个城市列表,并且可以从该列表中解析城市并将其注入到person中,从而仅引用一个城市。或者,您可以从列表中解析城市并将其作为属性传递给person-component。
还要确保考虑子对象的目的。您需要让它具有反应性吗,还是静态的?为了节省大量内存,您可以简单地告诉“person.city = city”,这将被注入到每个person-component中,但如果它需要具有反应性,则需要使用Vue.set方法...并记住,如果每个城市都需要自己的实例(以便每个人都拥有类似的城市对象,但需要根据每个人的属性进行编辑),则需要确保您没有使用引用对象!因此,您很可能需要克隆城市对象,这将占用浏览器的内存。
- 您的微型组件可能包含分别用于只读状态和编辑器状态的单独视图状态。这是非常常见的。但是,每次都会创建该微型组件的实例,从而初始化该组件数千次。
想象一下,您拥有类似于Excel电子表格的带有表格和表格行的UI。每个单元格都包含自定义“my-input”组件,该组件从布局中获取“readonly”属性。如果UI处于只读状态,则仅显示该my-input组件中的标签部分,但否则将显示具有某些特殊条件(例如具有不同输入的日期时间、数字、文本、文本区域、select标记等)的输入标记。现在假设您有100行20列,因此实际上正在初始化2000个my-input组件。现在的问题是--如何提高性能?
好吧,您可以将只读标签与my-input组件分开到list-view中,以便您仅显示只读版本(标签)或显示可编辑的my-input组件。这样,您就有了v-if条件,确保这些2000个微型组件不会被初始化,除非您已经明确要求初始化它们(由于整个布局或行从只读->可编辑状态移动)...当Vue不需要创建2000个组件时,浏览器的内存影响可能很大。
如果您发现页面加载非常缓慢,可能根本不是VUE的问题。检查呈现到HTML中的HTML标记数量。当您拥有大量标记时,HTML的性能表现相当差。最简单的演示方法之一是重复具有2000个选项的select标记100次,或者拥有一个单独的20000个选项的select标记。同样,您可能通过具有许多不必要的包装div等微型组件来溢出html标记的数量...您拥有的深度和标记越少,浏览器和CPU需要的呈现性能就越少。
尝试通过示例学习良好的HTML标记结构。例如,您可以研究Trello服务仪表板视图的编程方式。这是一个相当简单而美观的半复杂服务表示,具有最少量的子div。
有许多改进内存处理的方法,但我认为最重要的是将“隐藏”的对象与可见对象分开,如我的原始答案所述。第二部分是理解实例化对象与引用对象之间的区别。第三个是限制对象之间不必要的数据传递量。
个人而言,我没有尝试过这一点,但存在一个Vue-virtual-scroller组件,它通过简单地成为似乎无限数量数据的包装器来处理任意数量的数据。查看概念@
https://github.com/Akryum/vue-virtual-scroller,并告诉我是否解决了您的问题。
希望这些指南能够为优化您的组件提供一些思路。永远不要放弃希望,总有改进的空间!