8 худших вопросов на собеседовании по Vue.js

    Привет, Хабр!

    Вы любите собеседования? И часто проводите их? Если ответ на второй вопрос «Да», то среди кандидатов вам наверняка встречались отличные и умные люди, которые отвечали на все ваши вопросы и приближались к концу зарплатной вилки.

    Но вы, конечно, не хотите платить профессионалам слишком много. И жизненно необходимо казаться умнее них, пускай только на время собеседования.

    Если у вас с этим проблемы, то добро пожаловать под кат. Там вы найдете самые каверзные и извращенные вопросы по Vue, которые поставят любого кандидата на место и заставят сомневаться в своих профессиональных навыках.

    image

    1. Триггер watcher'ов внутри хуков жизненного цикла


    Этот вопрос может показаться легким, но я гарантирую, на него не ответит ни один, даже самый прошареный разработчик. Можете задать его в начале собеседования, чтобы кандидат сразу почувствовал ваше превосходство.

    Вопрос:

    Есть компонент TestComponent, у которого есть переменная amount. Внутри основных хуков жизненного цикла мы задаем ей значение в числовом порядке от 1 до 6. На эту переменную стоит watcher, который выводит ее значение в консоль.

    Мы создаем инстанс TestComponent и через несколько секунд удаляем. Необходимо сказать, что мы увидим в выводе консоли.

    Код:

    /* TestComponent.vue */
    
    <template>
      <span>
        I'm Test component
      </span>
    </template>
    
    <script>
    export default {
      data() {
        return {
          amount: 0,
        };
      },
    
      watch: {
        amount(newVal) {
          console.log(newVal);
        },
      },
    
      beforeCreate() {  this.amount = 1; },
      created() {       this.amount = 2; },
      beforeMount() {   this.amount = 3; },
      mounted() {       this.amount = 4; },
      beforeDestroy() { this.amount = 5; },
      destroyed() {     this.amount = 6; },
    };
    </script>
    

    Дам подсказку: «2345» — неправильный ответ.

    Ответ
    В консоли мы увидим только цифру 4.

    Объяснение
    В хуке beforeCreate еще не создан сам инстанс, watcher здесь работать не будет.

    Watcher срабатывает на изменения в хуке created, beforeMount и mounted. Так как все эти хуки вызываются во время одного тика, Vue вызовет watcher один раз в самом конце, со значением 4.

    Vue отпишется от наблюдения за изменением переменной перед вызовом хуков beforeDestroy и destroyed, поэтому 5 и 6 не попадут в консоль.

    Песочница с примером, чтобы убедиться в ответе

    2. Неявное поведение props


    Этот вопрос основан на редком поведении props во Vue. Все программисты, конечно, просто выставляют нужные валидации для prop'ов и никогда не сталкиваются с таким поведением. Но этого говорить кандидату не нужно. Лучше будет задать этот вопрос, бросить на него осуждающий взгляд после неправильного ответа и перейти к следующему.

    Вопрос:

    Чем поведение prop'а с типом Boolean отличается от остальных?

    /* SomeComponent.vue */
    
    <template>
      <!-- ... -->
    </template>
    
    <script>
    export default {
      /* ... */
      props: {
        testProperty: {
          type: Boolean,
        },
      },
    };
    </script>
    

    Ответ
    Prop с типом Boolean отличается от всех остальных тем, что во Vue для него есть специальное приведение типов.

    Если в качестве параметра будет передана пустая строка или название самого prop'а в kebab-case, то Vue преобразует это в true.

    Пример:

    У нас есть файл с Boolean prop'ом:

    /* TestComponent.vue */
    
    <template>
      <div v-if="canShow">
        I'm TestComponent
      </div>
    </template>
    
    <script>
    export default {
      props: {
        canShow: {
          type: Boolean,
          required: true,
        },
      },
    };
    </script>
    

    Ниже показаны все валидные варианты использования компонента TestComponent.

    /* TestWrapper.vue */
    
    <template>
      <div>
        <!-- В этом случае canShow будет равен true внутри TestComponent -->
        <TestComponent canShow="" />
    
        <!-- Этот пример аналогичен предыдущему, vue-template-compiler выставит пустую строку для нашего prop'а -->
        <TestComponent canShow />
    
        <!-- Тут canShow тоже равен true -->
        <TestComponent canShow="can-show" />
      </div>
    </template>
    
    <script>
    import TestComponent from 'path/to/TestComponent';
    
    export default {
      components: {
        TestComponent,
      },
    };
    </script>
    


    Песочница с примером, чтобы убедиться в ответе

    3. Использование массива в $refs


    Если ваш кандидат знает как работает фреймворк изнутри на уровне Эвана Ю, у вас все еще есть несколько козырей в рукаве: вы можете задать вопрос о незадокументированном и неочевидном поведении фреймворка.

    Вопрос:

    Во Vuex лежит массив объектов files, у каждого из объектов в массиве есть уникальные свойства name и id. Этот массив раз в несколько секунд обновляется, в нем удаляются и добавляются элементы.

    У нас есть компонент, который выводит name каждого объекта массива с кнопкой, по клику на которую в консоль должен выводиться dom-элемент, связанный с текущим файлом:

    /* FileList.vue */
    
    <template>
      <div>
        <div
          v-for="(file, idx) in files"
          :key="file.id"
          ref="files"
        >
          {{ file.name }}
    
          <button @click="logDOMElement(idx)">
            Log DOM element
          </button>
        </div>
      </div>
    </template>
    
    <script>
    import { mapState } from 'vuex';
    
    export default {
      computed: {
        ...mapState('files'),
      },
    
      methods: {
        logDOMElement(idx) {
          console.log(this.$refs.files[idx]);
        },
      },
    };
    </script>
    

    Необходимо сказать, где здесь потенциальная ошибка и как ее исправить.

    Ответ
    Проблема в том, что массив внутри $refs может идти не в том порядке, как и оригинальный массив (ссылка на issue). То есть, может произойти такая ситуация: кликаем на кнопку третьего элемента списка, а на консоль выводится dom-элемент второго.

    Такое происходит только тогда, когда в массиве часто изменяются данные.

    Методы решения написаны в issue на GitHub'е:

    1. Создавать уникальный ref для каждого элемента

    <template>
      <div>
        <div
          v-for="(file, idx) in files"
          :key="file.id"
          :ref="`file_${idx}`"
        >
          {{ file.name }}
    
          <button @click="logDOMElement(idx)">
            Log DOM element
          </button>
        </div>
      </div>
    </template>
    
    <script>
    import { mapState } from 'vuex';
    
    export default {
      computed: {
        ...mapState('files'),
      },
    
      methods: {
        logDOMElement(idx) {
          console.log(this.$refs[`file_{idx}`]);
        },
      },
    };
    </script>
    

    2. Дополнительный аттрибут

    <template>
      <div>
        <div
          v-for="(file, idx) in files"
          :key="file.id"
          :data-file-idx="idx"
        >
          {{ file.name }}
    
          <button @click="logDOMElement(idx)">
            Log DOM element
          </button>
        </div>
      </div>
    </template>
    
    <script>
    import { mapState } from 'vuex';
    
    export default {
      computed: {
        ...mapState('files'),
      },
    
      methods: {
        logDOMElement(idx) {
          const fileEl = this.$el.querySelector(`*[data-file-idx=${idx}]`);
    
          console.log(fileEl);
        },
      },
    };
    </script>
    


    4. Странное пересоздание компонента


    Вопрос:

    У нас есть специальный компонент, который пишет в консоль каждый раз, когда вызывается хук mounted:

    /* TestMount.vue */
    
    <template>
      <div>
        I'm TestMount
      </div>
    </template>
    
    <script>
    export default {
      mounted() {
        console.log('TestMount mounted');
      },
    };
    </script>
    


    Этот компонент используется в компоненте TestComponent. Он имеет кнопку, по нажатию на которую на 1 секунду покажется надпись Top message.
    /* TestComponent.vue */
    
    <template>
      <div>
        <div v-if="canShowTopMessage">
          Top message
        </div>
    
        <div>
          <TestMount />
        </div>
    
        <button
          @click="showTopMessage()"
          v-if="!canShowTopMessage"
        >
          Show top message
        </button>
      </div>
    </template>
    
    <script>
    import TestMount from './TestMount';
    
    export default {
      components: {
        TestMount,
      },
    
      data() {
        return {
          canShowTopMessage: false,
        };
      },
    
      methods: {
        showTopMessage() {
          this.canShowTopMessage = true;
    
          setTimeout(() => {
            this.canShowTopMessage = false;
          }, 1000);
        },
      },
    };
    </script>
    

    Кликнем на кнопку и посмотрим, что будет в консоли:



    Первый маунт был ожидаемый, но откуда еще два? Как это исправить?

    Песочница с примером, чтобы понять ошибку и исправить ее

    Ответ
    Проблема здесь возникает из-за особенностей поиска различий Virtual DOM'ов во Vue.

    В самом начале наш Virtual DOM выглядит так:


    После клика на кнопку он выглядит так:



    Vue пытается сопоставить старый Virtual DOM с новым, чтобы понять, что нужно удалить и добавить:


    Удаленные элементы перечеркнуты красным, созданные — выделены зеленым

    Vue не смог найти компонент TestMount, поэтому пересоздал его.

    Аналогичная ситуация повторится через секунду после нажатия кнопки. В этот момент компонент TestMounted третий раз выведет на консоль информацию о своем создании.

    Чтобы пофиксить проблему, достаточно поставить атрибут key к div'у с компонентом TestMounted:

    /* TestComponent.vue */
    
    <template>
      <div>
        <!-- ... -->
        <div key="container">
          <TestMount />
        </div>
        <!-- ... -->
      </div>
    </template>
    
     /* ... */
    

    Теперь Vue сможет однозначно сопоставить нужные элементы Virtual DOM'ов.

    5. Создание компонента-таблицы


    Задача:

    Необходимо создать компонент, который принимает массив с данными и выводит их в виде таблицы. Необходимо давать возможность задавать колонки и вид ячейки.

    Информация о колонках и виде ячейки должна передаваться через специальный компонент (так же, как и у element-ui):

    /* SomeComponent.vue */
    
    <template>
      <CustomTable :items="items">
    
        <CustomColumn label="Name">
          <template slot-scope="item">
            {{ item.name }}
          </template>
        </CustomColumn>
    
        <CustomColumn label="Element Id">
          <template slot-scope="item">
            {{ item.id }}
          </template>
        </CustomColumn>
    
      </CustomTable>
    </template>
    

    В начале задача не содержала необходимости делать так же, как и у element-ui. Но оказалось, что некоторые люди способны выполнить задачу в первоначальной формулировке. Поэтому и добавилось требование передавать информацию о колонках и виде ячейки с помощью компонентов.

    Уверен, ваши собеседуемые будут все время в ступоре. Можете дать им 30 минут на решение такой задачи.

    Решение
    Основная идея состоит в том, чтобы в компоненте CustomColumn передать все данные компоненту CustomTable, а дальше он уже сам все срендерит.

    Ниже дан пример реализации. Он не учитывает некоторых моментов (как, например, изменение label), но основной принцип должен быть понятен.

    /* CustomColumn.js */
    
    export default {
      render() {
        return null;
      },
    
      props: {
        label: {
          type: String,
          required: true,
        },
      },
    
      mounted() {
        // Передаем в компонент CustomTable необходимые данные
        this.$parent.setColumnData({
          label: this.label,
          createCell: this.$scopedSlots.default,
        });
      },
    };
    

    /* CustomTable.js */
    /* Использется JSX, так как в template не получится использовать метод createCell, переданный из CustomColumn.js */
    
    export default {
      render() {
        const { columnsData, items } = this;
        const { default: defaultSlot } = this.$slots;
    
        return (
          <div>
            // Создаем элементы CustomColumn
            {defaultSlot}
    
            <table>
              // Создаем хедер
              <tr>
                {columnsData.map(columnData => (
                  <td key={columnData.label}>
                    {columnData.label}
                  </td>
                ))}
              </tr>
    
              // Создаем строки таблицы
              {items.map(item => (
                <tr>
                  {columnsData.map(columnData => (
                    <td key={columnData.label}>
                      {columnData.createCell(item)}
                    </td>
                  ))}
                </tr>
              ))}
            </table>
          </div>
        );
      },
    
      props: {
        items: {
          type: Array,
          required: true,
        },
      },
    
      data() {
        return {
          columnsData: [],
        };
      },
    
      methods: {
        setColumnData(columnData) {
          this.columnsData.push(columnData);
        },
      },
    };
    


    6. Создание портала


    Если ваш кандидат не справился с предыдущим заданием, ничего страшного: можете дать ему еще одно, не менее сложное!

    Задача:

    Создать компонент Portal и PortalTarget, как у библиотеки portal-vue:

    /* FirstComponent.vue */
    
    <template>
      <div>
        <Portal to="title">
          Super header
        </Portal>
      </div>
    </template>
    

    /* SecondComponent.vue */
    
    <template>
      <div>
        <PortalTarget name="title" />
      </div>
    </template>
    

    Решение
    Для создания портала нужно реализовать три объекта:

    • Хранилище данных о порталах
    • Компонент Portal, который добавляет данные в хранилище
    • Компонент PortalTarget, который извлекает данные из хранилища и отображает их

    /* dataBus.js */
    /* Файл содержит реактивное хранилище данных */
    
    import Vue from 'vue';
    
    const bus = new Vue({
      data() {
        return {
          portalDatas: [],
        };
      },
    
      methods: {
        setPortalData(portalData) {
          const { portalDatas } = this;
    
          const portalDataIdx = portalDatas.findIndex(
            pd => pd.id === portalData.id,
          );
    
          if (portalDataIdx === -1) {
            portalDatas.push(portalData);
    
            return;
          }
    
          portalDatas.splice(portalDataIdx, 1, portalData);
        },
    
        removePortalData(portalDataId) {
          const { portalDatas } = this;
    
          const portalDataIdx = portalDatas.findIndex(
            pd => pd.id === portalDataId,
          );
    
          if (portalDataIdx === -1) {
            return;
          }
    
          portalDatas.splice(portalDataIdx, 1);
        },
    
        getPortalData(portalName) {
          const { portalDatas } = this;
    
          const portalData = portalDatas.find(pd => pd.to === portalName);
    
          return portalData || null;
        },
      },
    });
    
    export default bus;
    

    /* Portal.vue */
    /* Этот компонент передает данные в dataBus */
    
    import dataBus from './dataBus';
    
    let currentId = 0;
    
    export default {
      props: {
        to: {
          type: String,
          required: true,
        },
      },
    
      computed: {
        // Уникальный id компонента.
        // Нужен для идентификации данных в dataBus
        id() {
          return currentId++;
        },
      },
    
      render() {
        return null;
      },
    
      created() {
        this.setPortalData();
      },
     
      // Подхватываем изменение слотов
      updated() {
        this.setPortalData();
      },
    
      methods: {
        setPortalData() {
          const { to, id } = this;
          const { default: portalEl } = this.$slots;
    
          dataBus.setPortalData({
            to,
            id,
            portalEl,
          });
        },
      },
    
      beforeDestroy() {
        dataBus.removePortalData(this.id);
      },
    };
    

    /* PortalTarget.vue */
    /* Компонент извлекает и отображает данные */
    
    import dataBus from './dataBus';
    
    export default {
      props: {
        name: {
          type: String,
          required: true,
        },
      },
    
      render() {
        const { portalData } = this;
    
        if (!portalData) {
          return null;
        }
    
        return (
          <div class="portal-target">
            {portalData.portalEl}
          </div>
        );
      },
    
      computed: {
        portalData() {
          return dataBus.getPortalData(this.name);
        },
      },
    };
    

    Данное решение не поддерживает изменение атрибута to, не поддерживает анимации через transition и не имеет поддержки дефолтных значений, как portal-vue. Но общая идея должна быть понятна.

    7. Предотвращение создания реактивности


    Вопрос:

    Вы получили от апи большой объект и отобразили его пользователю. Примерно так:

    /* ItemView.vue */
    
    <template>
      <div v-if="item">
        <div> {{ item.name }} </div>
        <div> {{ item.price }} </div>
        <div> {{ item.quality }} </div>
        <!-- И еще много полей -->
      </div>
    </template>
    
    <script>
    import getItemFromApi from 'path/to/getItemFromApi';
    
    export default {
      data() {
        return {
          item: null,
        };
      },
    
      async mounted() {
        this.item = await getItemFromApi();
      },
    };
    </script>
    

    В этом коде есть проблема. У объекта item мы не меняем name, price, quality и остальные свойства. Но Vue об этом не знает и добавляет реактивность в каждое поле.

    Как можно этого избежать?

    Ответ
    Чтобы избежать изменения свойств на реактивные, надо заморозить объект перед добавлением внутрь Vue с помощью метода Object.freeze.

    Vue проверит, заморожен ли объект с помощью метода Object.isFrozen. И если это так, то Vue не станет добавлять реактивные геттеры и сеттеры к свойствам объекта, так как их в любом случае невозможно изменить. При очень больших объектах эта оптимизация помогает сохранить до нескольких десятков миллисекунд.

    Оптимизированный компонент будет выглядеть так:

    /* ItemView.vue */
    
    <template>
      <!-- ... -->
    </template>
    
    <script>
    import getItemFromApi from 'path/to/getItemFromApi';
    
    export default {
      /* .... */
    
      async mounted() {
        const item = await getItemFromApi();
        Object.freeze(item);
    
        this.item = item;
      },
    };
    </script>
    

    Object.freeze замораживает только свойства самого объекта. Так что, если объект содержит в себе вложенные объекты, их тоже необходимо заморозить.

    Обновление от 19.01.2019: По совету Дмитрия Злыгина глянул библиотеку vue-nonreactive и нашел еще один способ. Он отлично подойдет для ситуации, когда у вас много вложенных объектов.

    Vue не станет добавлять реактивность в объект, если увидит, что он уже реактивен. Мы можем обмануть Vue, создав пустой Observer для объекта:

    /* ItemView.vue */
    
    <template>
      <!-- ... -->
    </template>
    
    <script>
    import Vue from 'vue';
    import getItemFromApi from 'path/to/getItemFromApi';
    
    const Observer = new Vue()
      .$data
      .__ob__
      .constructor;
    
    export default {
      /* .... */
    
      async mounted() {
        const item = await getItemFromApi();
    
        // Добавляем пустой Observer для объекта
        item.__ob__ = new Observer({});
    
        this.item = item;
      },
    };
    </script>
    


    8. Ошибки медленных девайсов


    Вопрос:

    Есть компонент с методом, который выводит одно из свойств объекта item в консоль, а затем удаляет объект item:

    /* SomeComponent.vue */
    
    <template>
      <div v-if="item">
        <button @click="logAndClean()">
          Log and clean
        </button>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          item: {
            value: 124,
          },
        };
      },
    
      methods: {
        logAndClean() {
          console.log(this.item.value);
    
          this.item = null;
        },
      },
    };
    </script>
    

    Что здесь может пойти не так?

    Ответ
    Проблема в том, что после первого клика на кнопку Vue требуется некоторое время, чтобы обновить DOM для пользователя и убрать кнопку. Поэтому пользователь иногда может кликнуть два раза. Метод logAndClean отработает первый раз нормально, а второй раз крашнется, так как не сможет получить свойство value.

    Я постоянно вижу такую проблему в трекере ошибок, особенно часто на дешевых мобильниках за 4-5к рублей.

    Чтобы избежать ее, просто добавьте проверку на существование item в начале функции:

    <template>
      <!-- ... -->
    </template>
    
    <script>
    export default {
      /* ... */
    
      methods: {
        logAndClean() {
          const { item } = this;
    
          if (!item) {
            return;
          }
    
          console.log(item.value);
    
          this.item = null;
        },
      },
    };
    </script>
    

    Чтобы воспроизвести баг, можете перейти в песочницу с примером, выставить максимальный троттлинг CPU и быстро-быстро покликать на кнопку. У меня, например, получилось.



    Ссылка на песочницу, чтобы убедиться в ответе

    Спасибо, что дочитали статью до конца! Думаю, теперь вы точно сможете казаться умнее на собеседованиях и у ваших кандидатов сильно упадут зарплатные ожидания!
    Constanta
    22,68
    Разработка букмекерских продуктов
    Поддержать автора
    Поделиться публикацией

    Похожие публикации

    Комментарии 35

      +8
      Опять эти люки… Как и написано в заголовке, эти вопросы действительно ужасны, и совершенно не подходят для собеседования )

      Я возможно ошибаюсь, но мне почему-то кажется, что большинству работодателей, нужны кадры способные решать задачи, а не задачки… Искренне надеюсь, что эти вопросы не были взяты с реальных собеседований.

      А задачки интересные, конечно )
        +8
        Да, абсолютно верно, именно поэтому мы отказались от этих вопросов.

        На текущий момент мы сразу применяем физическое насилие к кандидатам. Обычно используем наручники и батарею.
          +6
          А если кандидат чемпион мира по рукопашному коду?
            +7
            Для таких случаев у нас в офисе есть перцовый балончик.

            Хотя до такого еще не доходило, обычно справляемся своими силами.
              +3
              Для этого собеседующих несколько.
              +3
              На текущий момент мы сразу применяем физическое насилие к кандидатам. Обычно используем наручники и батарею.

              Можно подробнее? Очень интересует практика в данном вопросе.

                +13
                Конечно, приходите к нам на собеседование!
                  +8
                  Нет, уж лучше вы к нам! ©
                +1
                Страшно представить, что у вас делают с бэкендерами… Недельные спринты в темных подвалах?
                  +5
                  Трехдневные спринты с митингами два раза в день, утром и ночью.

                  За баги в проде лишаем еды и будим палкой по ночам.
                    +3
                    Трехдневные спринты

                    Лучше сразу пристрелите.
                  +1
                  Это такой новый тренд moikrug.ru/vacancies/1000032070?
                    +4
                    на первой своей работе коллеги мне рассказали историю (10 лет назад), что за год до моего прихода они наняли человека для кражи кода c прошивки с микроконтроллера и переделки его из ASM в код С++ (тайваньский алгоритм распознавания отпечатков пальцев). Код был скачан, но с конвертацией были проблемы. Чел потихоньку слился. Его нашли, прицепили к батарее пока он не сделал (1 месяц это заняло). Не только вы одни такое практикуете. Сейчас эти деятели пилят оборонные заказы.
                  +2
                  Object.freeze замораживает только свойства самого объекта. Так что, если объект содержит в себе вложенные объекты, их тоже необходимо заморозить.

                  Или чтобы этого не делать достать данные из замыкания. Думаю понятно что флаг dataLoaded нужен только из-за отсутствия реактивности у item.


                  import getItemFromApi from 'path/to/getItemFromApi';
                  
                  let item;
                  
                  export default {
                    data() {
                      return { dataLoaded: false }
                    }
                    async mounted() {
                      item = await getItemFromApi();
                      this.dataLoaded = true;
                    },
                    computed() {
                      return this.dataLoaded && item;
                    }
                  };

                  Статья крутая, спасибо!

                    +1
                    Так у вас тогда будет 1 item на все компоненты.
                    Если мне не изменяет память, (сменил vue на реакт примерно 8 месяцев назад) можно просто не объявлять его в data и таким же способом вернуть через computed. (только как this.item)
                      0
                      Это правда. Можно в data записать, но тогда придётся жить с предупреждениями в консоли, что не очень приятно. Если компонент не один действительно проще рекурсивный фриз сделать. (компоненты с огромными списками обычно одни на всю страницу)
                    +2
                    Этот вопрос может показаться легким, но я гарантирую, на него не ответит ни один, даже самый прошареный разработчик. Можете задать его в начале собеседования, чтобы кандидат сразу почувствовал ваше превосходство.


                    Обычно, в начале собеседования наоборот легкие вопросы. Для человека собес — это стрессовая ситуация, а от простого первого вопроса он становится менее напряженным. Потом по нарастающей.
                      +3
                      кандидат сразу почувствовал ваше превосходство

                      ну вредные советы же, ирония, не?
                        0
                        А чего стрессовать на собеседовании?
                        +1
                        Ох, почувствовал я себя днищем. Ни на один вопрос…
                        Как раз недавно читал тут статью про заниженную самооценку русскоязычных IT-шников.
                        И где вы только умудряетесь такие вопросы находить, изверги! =)
                        Спасибо, интересная и полезная статья.
                          +1
                          К слову говоря — эта песочница тоже написана на Vue.js
                            +1
                            О, так это ваша песочница!

                            Спасибо большое за нее, песочница с лучшей консолью!
                              +1

                              Благодарю!
                              Не могли бы вы рассказать, чем консоль столь примечательна?


                              Возможно у вас есть пожелания?

                                +1
                                Консоль примечательна тем, что:
                                1. Она есть
                                2. Она удобно расположена под preview

                                Единственный аналог с консолью на сайте, который я нашел — это jsbin.com. Вначале я пытался использовать его, но что-то у них не завелась подсветка синтаксиса


                                Из пожеланий — мне показалось неудобным, что нельзя сразу увидеть html, css и js.

                                Проблема возникает тогда, когда ты скидываешь человеку незнакомый ему код. Чтобы понять смысл этого кода, надо прокликать по всем вкладкам и держать в голове предыдущие файлы. В статье это было было не критично, я использовал только один js файл. Но когда мне присылали чуть более сложные демки на вашем сайте, я чувствовал затруднения.
                                  +1

                                  Спасибо Андрей!
                                  Планируется вторая версия, там будет возможность открыть несколько файлов в разных «местах».

                            +1

                            Отличное изложение, спасибо


                            А я то думал: «зачем это подавление справочными вопросами, которые решаются с помощью гугла и исходников того же element-ui?».


                            «… упадут зарплатные ожидания» — теперь понятно. Умно!

                              0
                              Вообще зачем задавать вопросы по фреймворку?
                                +1
                                Обновление: Добавил еще одну фишку в ответе к 7 вопросу.

                                CyberAP Kuorell посмотрите, думаю вам будет интересно.
                                  +1
                                  это уже похоже на хак, с фризом объекта вариант получше. А за статью спасибо, реально полезная статья по Vue за последний год. Пишите еще ;)
                                  +1
                                  Спасибо за пост. Вы здорово повеселили меня и скрасили вечер. :)
                                  Отдельное спасибо Agel_Nash за комментарий.
                                    0
                                    Больше повесили комментарии к посту)
                                      0

                                      В последнем примере почему вы добавили
                                      const { item } = this;
                                      , а не оставили
                                      this.item
                                      ?

                                        0
                                        Внутри методов и computed-функций я использую и деструктуризацию, и обращение через this, это зависит от контекста.

                                        Если функция маленькая и использует переменную только один раз, то я обращаюсь через this (деструктуризация выглядит слишком громоздко для метода из одной строчки):
                                        export default {
                                          /* ... */
                                        
                                          computed: {
                                            formattedValue() {
                                              return this.value.toFixed(2);
                                            },
                                        
                                            isInCart() {
                                              return this.presentInCart(this.item);
                                            },
                                          },
                                        };
                                        


                                        Однако если метод большой и использует переменную несколько раз, то я предпочитаю деструктуризацию:
                                        export default {
                                          /* ... */
                                        
                                          mounted() {
                                            const { field } = this.$refs;
                                        
                                            if (!field) {
                                              return;
                                            }
                                        
                                            this.fieldWidth = field.offsetWidth;
                                            this.doSomething();
                                          },
                                        
                                          computed: {
                                            progress() {
                                              const { start, end } = this;
                                        
                                              if (start === 0 && end === 0) {
                                                return '50%';
                                              }
                                        
                                              return `${(start / (start + end)) * 100}%`;
                                            },
                                          },
                                        };
                                        


                                        Благодаря деструктуризации

                                        • В длинных методах сразу понятно, какие переменные объекта используются, так как использующиеся поля объявляются вначале
                                        • JavaScript после минификации меньше весит


                                        После таких махинаций код выглядит немного почище и попонятней :)
                                        0
                                        Каким образом можно научиться разбирать подобные примеры неочевидного поведение в работе фреймворка? В процессе работы над проектом или в изучении исходников?
                                          0
                                          1, 3, 4, 8 — узнал в процессе работы над проектами. Возникала проблема, я ее решал и запоминал.

                                          2, 5, 6 — смотрел исходники Vue, element-ui и portal-vue.

                                          7 — читал issue на гитхабе, в одной из них увидел совет Евана использовать Object.freeze(). Сейчас, правда, это уже упомянуто в документации.

                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                        Самое читаемое