Как я могу правильно лениво загрузить файл json в Vue.js, чтобы уменьшить размер пакета?

Я использую Vue.js и Laravel для своего проекта и недавно добавил две анимации с помощью плагина Lottie. Каждая анимация является компонентом, и оба они используют отдельный файл JSON для анимации группы изображений PNG (аналогично последовательности PNG). Эти два файла JSON хранятся локально в папке проекта по пути /public/data/.

Во-первых, файлы JSON не читаются, если я не укажу абсолютный путь (/users/username/documents/projectname/public/data/filename.json), я никак не могу получить это работать, просто используя /data/filename.json?

Во-вторых, когда я добавляю приведенный ниже код в свой компонент, мои файлы JS компилируются в отдельные фрагменты, как и ожидалось:

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

Я получаю следующую ошибку, когда анимация пытается запуститься:

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

found in

---> <VueLottie>

Однако, когда я импортирую свой файл json, используя обычный импорт в своем компоненте, как показано ниже, он отлично работает и показывает анимацию:

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

Мои компоненты анимации настроены следующим образом:

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

Я также пытался получить файл JSON с помощью вызова axios при монтировании компонента, но возникает та же ошибка.

Обновить

Я обновил свой код, чтобы каждый компонент загружался лениво вместо файла JSON. Вот так:

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

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

Однако теперь я получаю следующую ошибку:

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

Обновление 2

Я понял, почему я получаю сообщение об ошибке. Правильный способ состоял в том, чтобы добавить следующее вверху моего скрипта внутри родительского файла vue.

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

а затем внутри export default {...} я забыл добавить имена, поэтому:

components: { winAnimation, loseAnimation }

Теперь мой код был разделен, а размер файла app.js уменьшился почти вдвое! :)


person ImranR    schedule 10.02.2020    source источник
comment
Извините, я забыл упомянуть, что пытался axios также получить json при монтировании компонента, но я получил то же сообщение об ошибке, упомянутое выше. @EmielZuurbier   -  person ImranR    schedule 10.02.2020
comment
Что касается вашего обновления: как именно вы используете свой компонент в шаблоне?   -  person Michal Levý    schedule 10.02.2020
comment
Все в порядке, я понял, где ошибся, снова обновил свой вопрос - большое спасибо за вашу помощь!   -  person ImranR    schedule 10.02.2020
comment
Как решить проблему с динамическим импортом? я не могу динамически импортировать файлы .json лотереи. он не отображает анимацию без ошибок...   -  person Jnickz    schedule 24.04.2020


Ответы (2)


Во-первых, не используйте библиотеку vue-lottie. Если вы посмотрите на исходный код, то главное и единственное, что должно быть в этой библиотеке, это компонент src/lottie.vue (+ его зависимость lottie-web), но по какой-то причине пакет NPM также содержит полное демонстрационное приложение, включая демо-файл JSON (src/assets/pinjump.json)

Если вы посмотрите на компонент lottie.vue, то увидите, что это очень маленькая и очень простая оболочка для lottie-web, которая обеспечивает основные функции. Избавившись от vue-lottie, вы получите следующие преимущества:

  1. vue-lottie полностью игнорирует одну из lottie-web опций, которая использует path вместо animationData - документация здесь не очень ясна, но я предполагаю, что, предоставив path, библиотека попытается загрузить данные анимации ad-hoc, поэтому вам не нужно включать их в ваш пакет. Имхо стоит попробовать...

  2. Загрузка данных анимации по запросу

    • why are you using dynamic import on JSON file instead of dynamically importing whole component ? 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. And Vue will handle loading of the component without any additional code changes...
    • если вы по-прежнему хотите загружать по запросу только ваши данные JSON, вы должны понимать, что динамический импорт Webpack (import(".....")) возвращает Promise, а lottie-web (и, в свою очередь, vue-lottie) ожидает объект. Итак, вы должны сделать что-то вроде этого:
<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>

Обновлять

Вы всегда должны быть очень осторожны при добавлении сторонних компонентов в свой проект. Еще одна вещь, которую я заметил, это то, что lottie-web имеет метод destroy() в своем API. Это указывает на то, что он создает некоторые ресурсы (возможно, элементы DOM), которые необходимо очистить. Это то, с чем компонент vue-lottie вообще не справляется и может привести к неприятным утечкам памяти в вашем приложении. Вы можете прочитать о проблеме здесь

person Michal Levý    schedule 10.02.2020
comment
Спасибо за очень информативный ответ, я посмотрю, как перепрофилировать мой проект, чтобы использовать lottie-web вместо любых пакетов-оболочек. Я обновил свой вопрос с учетом вашего отзыва, но я получаю сообщение об ошибке и не знаю, где я ошибаюсь. Это моя первая ленивая загрузка компонента :) - person ImranR; 10.02.2020

Когда установлено свойство animationData, это функция, поэтому строка:

Ожидаемый объект, полученная функция

Ему нужен объект, а не функция.
Функция:

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

При определении свойства animationData вам необходимо установить объект в качестве его значения. Затем при монтировании fetch данных (или используйте Axios, если хотите) обновить свойство animationData компонента.

Примечание. Я никогда не пользовался 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;);
        )
    }
}
person Emiel Zuurbier    schedule 10.02.2020
comment
К сожалению, это не сработает, так как vue-lottie вызовет lottie.loadAnimation с пустым объектом до завершения загрузки... - person Michal Levý; 10.02.2020