Ответы пользователя по тегу Vue.js
  • После отключения правила eslint ругается prettier, как предотвартить?

    Aetae
    @Aetae Куратор тега JavaScript
    Тлен
    prettier - это одно, eslint - это другое, если у тебя prettier подключается через eslint, то настройки для него указываются скопом, условно так:
    rules: {
        'prettier/prettier': ['warn', {
          semi: false,
          ...
          singleQuote: true,
          vueIndentScriptAndStyle: true,
          arrowParens: 'avoid',
          trailingComma: 'none',
          quoteProps: 'consistent',
        }],
    Ответ написан
    Комментировать
  • Как не дублировать компонент с похожими данными?

    Aetae
    @Aetae Куратор тега Vue.js
    Тлен
    const options = computed(() => {
      if (props.secondType) 
        return props.secondTypeItems.map(({ id, value}) => ({
          label: value,
          value: id 
        }));
    
      if (props.thirdType) 
        return props.thirdTypeEvents.map(({ desc, name }) => ({
          label: name,
          value: desc 
        }));
    
      // ...
    
      return [];
    });


    <el-select
          v-model="modelText"
          placeholder="Выберите предмет"
          @change="changeVal"
        >
          <el-option
            v-for="{ label, value} in options"
            :key="value"
            :label="label"
            :value="value"
          >
            {{ label }}
          </el-option>
        </el-select>
    Ответ написан
    Комментировать
  • Почему во Vue3 не срабатывает computed, когда меняется ref?

    Aetae
    @Aetae Куратор тега Vue.js
    Тлен
    Во первых:
    import { computed, ref } from "vue";

    Во-вторых:
    <Input type="number" :value="count" @change="changeCount"/>


    Дальше при нормальных данных и компонентах должно работать, однако возможно энтот твой Input в change возвращает не значение, а event, тут хз сам смотри.
    Ответ написан
    3 комментария
  • Почему сервер получает пустые данные?

    Aetae
    @Aetae Куратор тега Vue.js
    Тлен
    Откуда ты эту хрень взял?
    axios.post('http://localhost:5000/api/create-post', () => {
      body: data
    })
    Во-первых: axios принимает параметром объект, а не функцию возвращающую объект.
    Во-вторых: axios.post вообще сразу принимает body:
    axios.post('http://localhost:5000/api/create-post', data)

    В-третьих: даже если бы оно принимало функцию(что не так) - конструкция
    () => {
      body: data
    }
    на самом деле расшифровывается так:
    () => { // начало блока кода
      // метка body указывающий на висящую в воздухе переменную data
      body: data
      // никакого возврата из функции
    } // конец блока кода
    чтобы оно воспринималось как объект, можно, например, заключать в скобки:
    () => ({
      body: data
    })
    Ответ написан
    5 комментариев
  • Можно ли программно отрендерить vue компонент передав шаблон и параметры?

    Aetae
    @Aetae Куратор тега Vue.js
    Тлен
    Vue работает с DOM, а не со строковым представлением.
    Тупо смонтируй компонент в скрытый div и забери из него innerHTML.

    Условно:
    // версия с рантайм компилятором шаблонов
    import { createApp } from 'vue/dist/vue.esm-bundler.js'; 
    
    function renderVueComponent(template, data) {
      const div = document.createElement('div');
      
      createApp({
        template,
        data: () => data
      }).mount(div);
    
      return div.innerHTML;
    }
    Ответ написан
    Комментировать
  • Как подсветить все ошибки typescript в webstorm?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    На самом деле твой вопрос: "Как подсветить ошибки типов typescript внутри шаблонов vue в webstorm".

    Ответ: никак.

    Они завязали всё на стороннюю тулзу и плагин к vscode, а не на языковой сервис typescript, потому любые ide кроме vscode в пролёте. Это очень расстраивает, да.

    У меня есть личные намётки на эту тему, но оно движется медленно и постоянно откладывается, т.к. есть задачи по-актуальней, так что в ближайшее время можно не ждать.)

    P.S. Могу пока порекомендовать плагин awesome console - он сделает кликабеьными все ссылки в консоли, включая переход к конкретной строчке.
    Ответ написан
    1 комментарий
  • Как типизировать в ts?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Вопрос конечно в духе "я его того, а он мне нихрна, чаво это он". Где конкретика блин? Что именно за ошибка? Любая ошибка TS - это "ошибка типов", он для этого и существует. Что у тебя в route?

    Но окей, предположим, что route у тебя - это текущий роут vue-рутера, тогда ошибка у тебя скорее всего выглядит как-то так:
    TS7053: Element implicitly has an 'any' type because expression of type 'RouteRecordName' can't be used to index type '{ first: string; second: string; }'.   No index signature with a parameter of type 'string' was found on type '{ first: string; second: string; }'.

    Собственно прочитав текст ошибки можно уже догадаться, в чём проблема: у (неявно выведенного за отсутствием явного объявления) типа объекта pages есть ключи типа 'first' | 'second' | ..., но нет index signature типа string, т.е. не указано, что ключом может быть любая строка, а не только конкретные'first' | 'second' | ....
    route.name же после проверки на пустоту имеет тип string | symbol. Ты не можешь у объекта с чётко ограниченным набором ключей брать значение по произвольному строковому/символьному.

    Прямое решение в лоб:
    Задать тип pages позволяющий рандомные ключи, например
    const pages: Record<PropertyKey, string> = {
      first: 'Первая',
      second: 'Вторая',
      ...
    }

    Всё сразу заработает, но это не спасёт тебя от ошибок(например опечаток).


    Не менее прямой вариант
    (но с другой стороны):
    Кастануть нужный тип руками: ... = pages[route.name as keyof typeof pages]
    Ведь мы уверены, что name в route всегда будет одним из ключей pages. Уверены же?..

    Энтерпрайз решение(ничем не лучше предыдущих, зато выглядит солидно):
    Твой файл routes.ts:
    export enum ERoutes {
      FIRST = 'first',
      SECOND = 'second'
    }
    
    const routes = [
      {
         name: ERoutes.FIRST,
         ...
      },
      {
         name: ERoutes.SECOND,
         ...
      },
      ...
    ]

    В коде:
    const pages: Record<ERoutes, string> = {
      [ERoutes.FIRST]: 'Первая',
      [ERoutes.SECOND]: 'Вторая',
      ...
    }
    
    ... = pages[route.name as ERoutes]


    Надмозговое решение("как батька"):
    Твой файл routes.ts:
    const routes = [
      {
         name: 'first',
         ...
      },
      {
         name: 'second',
         ...
      },
      ...
    ] as const satisfies ReadonlyArray<ReadonlyRouteRecordRaw>;
    
    type ReadonlyRouteRecordRaw = Omit<RouteRecordRaw, 'children'> & {
      children?: ReadonlyArray<ReadonlyRouteRecordRaw>;
    };
    
    type ExtractNames<Route> = Route extends { name: infer Name } ? Name : never;
    type FlattenChildren<Route> = Route extends { children: ReadonlyArray<infer Children> }
      ? FlattenChildren<Children> | Route
      : Route;
    
    // с помощью магии ts вытаскиваем в тип RouteNames все заданные у нас имена маршрутов
    export type RouteNames = ExtractNames<FlattenChildren<typeof routes[number]>>;
    
    // с помощью магии же прокидывем их прямо в декларацию vue-router
    declare module 'vue-router' {
      export interface RouteLocationNormalizedLoaded {
        name: RouteNames | null | undefined;
      }
    }

    satisfies
    satisfies - новая фича ts 4.9, в предыдущих версиях того же можно добиться сделав обёртку вида:
    const narrowRoutesTypeWrapper = <T extends ReadonlyArray<ReadonlyRouteRecordRaw>>(routes: T) => routes;
    const routes = narrowRoutesTypeWrapper([ ... ] as const);

    И твой код заработает вообще без изменений (если в pages есть все нужные ключи).)
    Однако для удобства можно написать так:
    const pages: Record<RouteNames, string> = {
      first: 'Первая',
      second: 'Вторая',
      ...
    }
    Ответ написан
    Комментировать
  • Как использовать useSlots().default() vue3 внутри script setup?

    Aetae
    @Aetae Куратор тега JavaScript
    Тлен
    о какой рендер функции идет речь?

    render-function

    Это более низкоуровневый вариант работы с vue, вместо использования <template>.(под капотом <template> компилируется в render-функцию)

    Render-функцию можно возвращать из setup, можно класть в свойство render.
    При script setup, можно сделать так:
    <template>
      <div>
        <render/>
      </div>
    </template>
    
    <script setup lang="ts">
      import { h, useSlots } from 'vue'
    
      const slots = useSlots();
      const render = () => {
        return h('div', slots.default());
      };
    </script>

    Но не нужно в подавляющем большинстве случаев. Мешать всё в одну кучу - весьма опасно.
    Ответ написан
    Комментировать
  • Как организовать работу с одними стилями на разных проектах?

    Aetae
    @Aetae Куратор тега JavaScript
    Тлен
    Одинаковая структура при разных реализациях - это больно и малореально. Существуют css\html-only дизайн системы, которые можно брать и реализовывать на любом фреймворке, но это тот ещё геморрой без особого выхлопа.

    Мне кажется вам стоит решить чего вы будете-придерживаться впредь, а что легаси, и вынести таки все компоненты одной актуальной версии(vue2 или vue3) в библиотеку, скомпилировав её в режиме web-components. Веб компоненты вы сможете подключать куда угодно, ценой лишних 100кб на непрофильный vue-runtime в случае несовпадения.
    Ответ написан
    1 комментарий
  • Как разработать компонент– кнопку счётчик, значение которой увеличивается на 1 по клику?

    Aetae
    @Aetae Куратор тега Vue.js
    Тлен
    export default defineComponent({
      name: 'CounterButton',
      props: {
        count: {
          type: Number,
          default: 0
        }
      },
      template: `<button type="button" @click="counter++">{{counter}}</button>`,
      emits: ['update:count'],
      setup(props, context) {
        const innerCounter = ref(props.count);
        watch(() => props.count, (value) => innerCounter.value = value);
        return {
          counter: computed({
            get: () => innerCounter.value,
            set: (value) => context.emit('update:count', innerCounter.value = value)
          })
        }
      }
    })
    Ответ написан
  • Почему не удаляется последнее значение?

    Aetae
    @Aetae Куратор тега Vue.js
    Тлен
    На каждое срабатывание nextStep ты вешаешь новый слушатель keydown.
    10 буковок ввёл - 10 слушателей весит. Работать это говно, соответственно, может как угодно.

    И вообще нефиг смешивать vue и работу с сырым dom.
    Ответ написан
  • Как сделать focus для input text при клике на кнопку?

    Aetae
    @Aetae Куратор тега Vue.js
    Тлен
    (this.$refs.text as HTMLInputElement).focus();
    TypeScript, увы, не знает типа твоего ref.
    Ответ написан
  • Объясните смысл vue + vuex?

    Aetae
    @Aetae Куратор тега Vue.js
    Тлен
    Компоненты могут быть переиспользованы. Но помимо переиспользуемых компонентов, которые действительно не должны лезть ни в какой vuex, есть собственно и страницы самого приложения\сайта. Они никогда не будут переиспользованы, кроме как в том же самом приложении и у них должно быть общее состояние(не может не быть).
    Ответ написан
    Комментировать
  • Vue3, как сделать задержку внутри computed?

    Aetae
    @Aetae Куратор тега JavaScript
    Тлен
    Условно как-то так:
    const betsArr = ref();
    watch(<то что было в computed>, debounce((value) => betsArr.value = value, 1000));
    Ответ написан
    Комментировать
  • Почему vue watch выдает обратное значение?

    Aetae
    @Aetae Куратор тега Vue.js
    Тлен
    Потому что не isDropped(oldVal, newVal), а isDropped(newVal, oldVal), лол.
    Ответ написан
    Комментировать
  • Как выводить имя полученных файлов в input type=file?

    Aetae
    @Aetae Куратор тега Vue.js
    Тлен
    Так делать нельзя: нельзя писать напрямую в стор, без коммита - что у тебя происходит с помощью v-model. У тебя сейчас должна быть полна консоль ворнингов.

    Меняешь v-model на :value и @input="...." и работаешь с этим. Имя файла лежит в $event.target.files[0].name если что.
    Ответ написан
  • Vue как отобразить сообщения ровно один раз?

    Aetae
    @Aetae Куратор тега Vue.js
    Тлен
    Очевидный ответ - не удалять из store пока они остаются на экране.)
    Если юзер может их "закрыть" - удалять по событию закрытия.
    Если они должны пропадать по таймауту - удалять по таймауту.
    Иначе - удалять по хуку onUnmounted компонента.
    Ответ написан
  • Ошибка при создании роута?

    Aetae
    @Aetae Куратор тега Vue.js
    Тлен
    Router 4 - для Vue 3, для 2 нужен 3.
    Ответ написан
    Комментировать
  • Как сделать прелоадер для компонента с резервированием места?

    Aetae
    @Aetae Куратор тега Vue.js
    Тлен
    А откуда тебе знать сколько он займёт, если данные произвольны?

    Если в receivedData может быть фиксированное количество записей фиксированных размеров, то условно как-то так:
    <div v-if="loading">
      <div v-for="foo in 10" class="fixed-size loading"></div>
      Loading
    </div>
    <div v-else>
      <div v-for="foo in receivedData.foo" class="fixed-size">{{ foo }}</div>
      {{ receivedData.bar }}
    </div>
    Ответ написан
  • Почему не отображается value реактивного(ref) массива?

    Aetae
    @Aetae Куратор тега Vue.js
    Тлен
    const products = {
      value: false
    };
    console.log(products)
    console.log(products.value)
    products.value = true;

    Разницы с ref в данном случае никакой.

    spoiler
    Получив ref.value ты уже получил текущее значение, если оно изменилось позже, то ты об этом никак не узнаешь, не поучив снова ref.value. Собсно потому и нужно это уродское .value, т.к. в js (пока?) нет нативных ref-ссылок приходится везде тащить объект-обёртку.
    Ответ написан
    3 комментария