简体   繁体   English

如何在 Vue.js 中正确延迟加载 json 文件以减少包大小?

[英]How can I correctly lazy load a json file in Vue.js to reduce bundle size?

I am using Vue.js and Laravel for my project and recently added two animations using a plugin named Lottie.我在我的项目中使用 Vue.js 和 Laravel,最近使用名为 Lottie 的插件添加了两个动画。 Each animation is a component, and they both use an individual JSON file to animate a group of PNG images (similar to a PNG sequence).每个动画都是一个组件,它们都使用单独的 JSON 文件为一组 PNG 图像(类似于 PNG 序列)设置动画。 These two JSON files are stored locally in the project folder under the path /public/data/ .这两个 JSON 文件本地存储在路径/public/data/下的项目文件夹中。

Firstly the JSON files are not being read unless I put in the absolute path ( /users/username/documents/projectname/public/data/filename.json ), is there no way I can get this to work just by using /data/filename.json ?首先,除非我放入绝对路径( /users/username/documents/projectname/public/data/filename.json ),否则不会读取 JSON 文件,我是否无法仅通过使用/data/文件名.json ?

Secondly, when I add the code below in my component, my JS files are compiled to separate chunks as expected:其次,当我在我的组件中添加下面的代码时,我的 JS 文件会按预期编译为单独的块:

const animationData = () =>
  import("/users/username/documents/projectname/public/data/filename.json");

I get the following error when the animation tries to run:动画尝试运行时出现以下错误:

Invalid prop: type check failed for prop "data". Expected Object, got Function 

found in

---> <VueLottie>

However when I import my json file using a normal import in my component like below it works fine and shows the animation:但是,当我在组件中使用正常导入来导入我的 json 文件时,如下所示,它可以正常工作并显示动画:

import animationData from "/users/username/documents/projectname/public/data/filename.json";

My animation components are both set up like this:我的动画组件都是这样设置的:

<template>
        <vue-lottie ref="lottie" loop autoplay :data="animationData" :height="400" :width="400"></vue-lottie>
</template>

<script>
    import vueLottie from "vue-lottie-ssr";
    import animationData from '/users/username/documents/projectname/public/data/filename.json'

    export default {
        name: 'animation',
        components: {
            vueLottie
        },
        data () {
            return {
                speed: 1,
                animationData
            }
        },
        computed: {
            lottie () {
                return this.$refs.lottie
            }
        }

    }
</script>

I have also tried getting the JSON file via an axios call when the component mounts, but the same error occurs.我也尝试在组件挂载时通过 axios 调用获取 JSON 文件,但发生了同样的错误。

Update更新

I updated my code so that each component is lazy loaded instead of the JSON file.我更新了我的代码,以便延迟加载每个组件而不是 JSON 文件。 Like so:像这样:

components: {
  WinAnimation: () => import("./WinAnimation.vue");

  LoseAnimation: () => import("./LoseAnimation.vue");
}

However now I'm getting the following error:但是现在我收到以下错误:

Unknown custom element: <win-animation> - did you register the component correctly? For recursive components, make sure to provide the "name" option.

Update 2更新 2

I realised why I was getting an error message.我意识到为什么我会收到一条错误消息。 The correct way was to add the following at the top of my script inside the parent vue file.正确的方法是在父 vue 文件中的脚本顶部添加以下内容。

const winAnimation = () => import("./WinAnimation.vue");
const loseAnimation = () => import("./LoseAnimation.vue");

and then inside export default {...} I forgot to add the names, so:然后在 export default {...} 里面我忘了添加名字,所以:

components: { winAnimation, loseAnimation }

Now my code has been split and my app.js file size has reduced by almost a half!现在我的代码被拆分了,我的 app.js 文件大小减少了近一半! :) :)

1st - don't use vue-lottie library.第一 - 不要使用vue-lottie库。 If you take a look at the source code, the main and only thing which should be provided by this library is component src/lottie.vue (+ it's dependency lottie-web ) but for some reason, NPM package also contains whole demo app including the demo JSON file ( src/assets/pinjump.json )如果你看一下源代码,这个库应该提供的主要也是唯一的东西是组件src/lottie.vue (+它的依赖lottie-web )但出于某种原因,NPM 包还包含整个演示应用程序,包括演示 JSON 文件( src/assets/pinjump.json

If you take a look at lottie.vue component, its just very little and very simple wrapper for lottie-web which provides main functionality.如果你看一下lottie.vue组件,它只是非常小而且非常简单的lottie-web包装器,它提供了主要功能。 By getting rid of vue-lottie you will get following benefits:通过摆脱vue-lottie您将获得以下好处:

  1. vue-lottie completely ignores one of the lottie-web options which is using path instead of animationData - documentation is not very clear here but I would guess that by providing path , the library will try download the animation data ad-hoc so you don't need to include it in your bundle. vue-lottie完全忽略了使用path而不是animationDatalottie-web选项之一- 此处的文档不是很清楚,但我猜想通过提供path ,库将尝试临时下载动画数据,因此您不会“不需要将它包含在您的捆绑包中。 Worth trying imho...值得一试恕我直言...

  2. Loading animation data on demand按需加载动画数据

    • why are you using dynamic import on JSON file instead of dynamically importing whole component ?为什么要对 JSON 文件使用动态导入而不是动态导入整个组件? By making separate chunk on component level, dynamic chunk will include not only your json data but also lottie-web which is also not small.通过在组件级别制作单独的块,动态块将不仅包括您的 json 数据,还lottie-web同样不小的lottie-web And Vue will handle loading of the component without any additional code changes... Vue 将处理组件的加载,无需任何额外的代码更改...
    • if you still want to load on demand only your JSON data, you must understand that Webpack dynamic import ( import(".....") ) is returning Promise and lottie-web (and in turn vue-lottie ) is expecting object.如果您仍然只想按需加载您的 JSON 数据,您必须了解 Webpack 动态导入( import(".....") )正在返回 Promise 并且lottie-web (以及vue-lottie )正在vue-lottie对象. So you must do something like this:所以你必须做这样的事情:
<script>
    import lottie from 'lottie-web';

    const animationData = () =>
       import("/users/username/documents/projectname/public/data/filename.json");

    export default {
      mounted () {
        animationData().then(function(data) {
          this.anim = lottie.loadAnimation({
            // other options 
            animationData: data
          })
        });
      }
    }
</script>

Update更新

You should be always very careful when considering adding 3rd party components into your project.在考虑将 3rd 方组件添加到您的项目时,您应该始终非常小心。 One more thing I'v noticed is that lottie-web has destroy() method in it's API.我注意到的另一件事是lottie-web在它的 API 中有destroy()方法。 This indicates that it is creating some resources (DOM elements probably) which needs to be cleaned up.这表明它正在创建一些需要清理的资源(可能是 DOM 元素)。 This is something vue-lottie component is not handling at all and can lead to nasty memory leaks in your app.这是vue-lottie组件根本无法处理的事情,可能会导致您的应用程序中出现严重的内存泄漏。 You can read about the problem here您可以在此处阅读有关该问题的信息

When the animationData property is set it is a function, hence the line:当设置animationData属性时,它是一个函数,因此该行:

Expected Object, got Function预期对象,得到函数

It needs an object, not a function.它需要一个对象,而不是一个函数。
The function being:功能是:

const animationData = () =>
  import("/users/username/documents/projectname/public/data/filename.json");

When defining the animationData property you need to set an object as its value.定义animationData属性时,您需要将一个对象设置为其值。 Then when mounting fetch the data (or use Axios if you prefer that) to update the animationData property on the component.然后在挂载时fetch数据(如果您愿意,也可以使用 Axios)来更新组件上的animationData属性。

NB I have never used Vue, so I hope that what I am saying is correct. NB我从来没有用过Vue,所以我希望我说的是正确的。

export default {
    name: 'animation',
    components: {
        vueLottie
    },
    data () {
        return {
           speed: 1,
           animationData: {}
        }
    },
    computed: {
        lottie () {
            return this.$refs.lottie
        }
    },
    mounted() {
        fetch('/users/username/documents/projectname/public/data/filename.json')
            .then(response => response.json())
            .then(json => this.animationData = json;);
        )
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM