Коллекция общих проблем разработки VueJS

внешний интерфейс модульный тест Vue.js Webpack

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

в основном пишут некоторыеОфициальное руководствоВыше не написано, но проблемы, возникающие в реальной разработке, требуют определенной базы знаний.


Задействованный технологический стек


текст:

полифилл и трансформация во время выполнения

первый,vue-cliавтоматически добавлено для насbabel-plugin-transform-runtimeЭтот плагин, большинство плагинов нормальный, может быть преобразован в большинствоES6грамматика.

Однако есть две проблемы:

  1. Когда компонент загружается асинхронно, он будет производитьpolyfillизбыточность кода
  2. Глобальные функции и методы экземпляра не поддерживаютсяpolyfill

Причины обеих проблем связаны сbabel-plugin-transform-runtimeДля компиляции нашего кода используется механизм песочницы (т. е. без изменения встроенных объектов хост-среды).

Поскольку асинхронные компоненты в конечном итоге компилируются в один файл, даже если одна и та же новая функция используется в нескольких компонентах (например:Object.keys()), то в каждом скомпилированном файле будет копия новой фичиpolyfillкопировать. Если проект небольшой, вы можете не использовать асинхронную загрузку, но нагрузка на первый экран будет относительно большой.

Глобальные функции не поддерживаются(Такие как:Promise,Set,Map),Setа такжеMapЭти две структуры данных не должны широко использоваться всеми, и их влияние невелико. ноPromiseВоздействие может быть больше.

Методы экземпляра не поддерживаются(Такие как:'abc'.includes('b'),['1', '2', '3'].find((n) => n < 2)и т. д.), это ограничение в значительной степени устарело для большинства новых функций строк и половины или около того для массивов.

В основномbabel-plugin-transform-runtimeОн может удовлетворить большинство потребностей.Когда потребности не удовлетворены, рекомендуется использовать полныйbabel-polyfill.

Заменить babel-polyfill

Во-первых, удалить из проектаbabel-plugin-transform-runtime
Удалите эту зависимость:

npm un babel-plugin-transform-runtime -D

Исправлятьbabelконфигурационный файл

// .babelrc
{
  //...
  "plugins": [
    // - "transform-runtime"
  ]
  //...
}

Затем установитеbabel-polyfillполагаться:

npm i babel-polyfill -D

Наконец, импортируйте в файл ввода

// src/main.js
import 'babel-polyfill'

Проблема со ссылкой на импорт ES6

существуетES6, импорт и экспорт модульной системы принимает ссылочный экспорт и импорт (непростой тип данных), то есть, если объект определен в модуле и экспортируется, при импорте и использовании в других модулях импорт фактически Ссылка на переменную (указатель), если свойство в объекте изменено, это повлияет на использование других модулей.

Обычно, когда объем системы невелик, мы можем использоватьJSON.parse(JSON.stringify(str))Просто и грубо создать новую глубокую копиюобъект данных. Однако, когда имеется много компонентов и высокая степень повторного использования объектов данных, очевидно, будут возникать проблемы с производительностью.В настоящее время мы можем рассмотреть возможность использованияImmutable.js.

По этой причине при экспорте сложных типов данных необходимо обращать внимание на проблемы, которые могут возникнуть при изменении данных, когда несколько компонентов импортируют один и тот же объект данных.
Кроме того, когда модуль определяет переменную или функцию, даже если он используетletвместоconst, станет доступным только для чтения при импорте и использовании и не может быть переназначено, эффект эквивалентен использованиюconstутверждение.

Использование Pug и Less с Vue

Установить зависимости

Vueиспользуется вvue-loaderсогласно сlangАтрибуты автоматически определяют, что нужноloader, так что дополнительная настройка не требуетсяLoader, но вам нужно вручную установить связанные зависимости:

npm i pug -D
npm i less-loader -D

Это достаточно удобно, никаких ручных доработок не требуетсяwebpackконфигурационный файл добавитьloaderготов использовать

использоватьpugещеpug-loader?sassоба грамматическихloaderКак это настроить?
--- Пожалуйста, обратитесь кПрепроцессор vue-loader

использовать

<!-- xxx.vue -->
<style lang="less">
  .action {
    color: #ddd;
      ul {
        overflow: hidden;
        li {
          float: left;
        }
      }
  }
</style>
<template lang="pug">
  .action(v-if='hasRight')
    ul
      li 编辑
      li 删除
</template>
<script>
  export default {
    data () {
      return {
        hasRight: true
      }
    }
  }
</script>

Определить глобальную функцию или переменную

Много раз нам нужно определить некоторые глобальные функции или переменные для обработки некоторых частых операций (здесь возьмемAJAXпример обработки исключений). Но когдаVue, каждый однофайловый компонент имеет отдельный контекст (this). Обычно при обработке исключений это должно быть отражено в представлении.На данный момент нам нужно получить доступthisобъект, но контекст глобальной функции обычноwindow, то требуется особая обработка.

просто и грубо

Самый простой способ — напрямуюwindowДля объекта определен глобальный метод, который используется при использовании в компоненте.bind,callилиapplyизменить контекст.

Определите глобальный метод обработки исключений:

// errHandler.js
window.errHandler = function () { // 不能使用箭头函数
  if (err.code && err.code !== 200) {
    this.$store.commit('err', true)
  } else {
    // ...
  }
}

Импорт в файл записи:

// src/main.js
import 'errHandler.js'

В компоненте используйте:

// xxx.vue
export default {
  created () {
    this.errHandler = window.errHandler.bind(this)
  },
  method: {
    getXXX () {
      this.$http.get('xxx/xx').then(({ body: result }) => {
        if (result.code === 200) {
          // ...
        } else {
          this.errHandler(result)
        }
      }).catch(this.errHandler)
    }
  }
}

Элегантный и безопасный

В крупных совместных проектах с участием нескольких человек загрязнениеwindowОбъект по-прежнему не подходит. В частности, некоторые глобальные методы, которые являются более личными (могут использоваться почти везде в компонентах, которые вы пишете, но могут не понадобиться для других). В настоящее время рекомендуется написать модуль, который является более элегантным, безопасным и естественным.Единственным недостатком является то, что каждый компонент, который должен использовать эту функцию или метод, необходимо импортировать.

Метод использования аналогичен предыдущему, поэтому я не буду вводить его дальше.  ̄ω ̄=

Момент.JS и Webpack

В использованииMoment.jsЯ столкнулся с некоторыми проблемами и обнаружил, что окончательный упакованный файл будетMoment.jsВсе языковые пакеты для . Я проверил и обнаружил, что это может бытьwebpackупаковать илиMoment.jsПроблема ссылки на ресурс (?), эта проблема в настоящее время не решена должным образомtrickДля решения этой проблемы.

существуетwebpackв производственном профилеpluginsДобавьте плагин в поле, используя встроенный метод классаContextReplacementPluginОтфильтрованоMoment.jsЯзыковые пакеты, которые не используются в:

// build/webpack.prod.conf.js
new webpack.ContextReplacementPlugin(/moment[\\/]locale$/, /^\.\/(zh-cn)$/)

Решения взяты изoleg-nogin@webpack/webpack#3128.
См. GitHub Issue для обсуждения проблемы:moment/moment#2373,webpack/webpack#3128.

пользовательский псевдоним пути

Некоторые люди могли заметить, что вvue-cliСгенерированный шаблон использует этот синтаксис при импорте компонентов:

import Index from '@/components/Index'

это@Что это такое? Позже, когда я изменил файл конфигурации, я обнаружил, что этоwebpackОдин из вариантов конфигурации: псевдонимы путей.

Мы также можем добавить наши собственные псевдонимы пути в базовый файл конфигурации, такие как следующие~установить путьsrc/componentsпсевдоним:

// build/webpack.base.js
{
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      '~': resolve('src/components')
    }
  }
}

Затем мы можем написать это при импорте компонента:

// import YourComponent from 'YourComponent'
// import YourComponent from './YourComponent'
// import YourComponent from '../YourComponent'
// import YourComponent from '/src/components/YourComponent'
import YourComponent from '~/YourComponent'

Это не только решает проблему слишком длинных путей, но и решает проблемы относительных путей, что намного удобнее!ヾ(゚∀゚ゞ)

CSS-области и модули

Компонентные стили

Как правило, компонент<style></style>Стили в тегах являются глобальными и могут использоваться при использовании сторонних UI-библиотек (таких как:Element), глобальные стили, скорее всего, повлияют на стили библиотеки пользовательского интерфейса.

Мы можем добавитьscopedсвойство делатьstyleСтили применяются только к текущему компоненту:

<style lang="less" scoped>
  @import 'other.less';
  .title {
    font-size: 1.2rem;
  }
</style>

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

Кроме того, избегайте использования селекторов элементов во встроенных стилях, поскольку селекторы элементов могут значительно снизить производительность в сочетании с селекторами атрибутов.

--- Тесты для двух комбинаций селекторов:classes selector,elements selector

стиль импорта

в сравнении сstyleиспользоватьscopedСтиль в компоненте, когда атрибут используется, и иногда нам также нужно добавить некоторые глобальные стили. Конечно, мы можем использоватьscopedатрибутstyleписать глобальные стили.

Однако для сравнения более рекомендуется следующее:

/* 单独的全局样式文件 */
/* style-global.less */
body {
  font-size: 10px;
}
.title {
  font-size: 1.4rem;
  font-weight: bolder;
}

Затем импортируйте глобальный стиль в файл записи:

// src/main.js
import 'style-global.less'

Получить значение элемента управления формы

Обычно мы можем напрямую использоватьv-modelПривяжите элементы управления формы к данным, но иногда нам также необходимо получить текущее значение при вводе данных пользователем (например: проверить достоверность текущего содержимого элемента управления вводом в режиме реального времени).

В этот момент мы можем использовать@inputили@changeСобытия связывают наши собственные обработчики и передаются$eventобъект для получения входного значения текущего элемента управления:

<input type='text' @change='change($event)'>
change (e) {
  let curVal = e.target.value
  if (/^\d+$/.test(curVal)) {
    this.num = +curVal
  } else {
    console.error('%s is not a number!', curVal)
  }
}

Конечно, если UI-фреймворк приметElementЭто будет проще, и его обратный вызов события будет напрямую передавать текущее значение.

Советы по использованию v-for

v-forДирективы мощные, их можно использовать не только для обхода массивов, объектов, но даже числа или строки.

Не буду рассказывать об основах грамматики, вот небольшой совет:

значение индекса

В использованииv-forСоздать из объекта или массиваDOMИногда вам нужно знать текущий индекс. Мы можем это сделать:

<ul>
  <li v-for='(item, key) in items' :key='key'> {{ key }} - {{ item }}
</ul>

но, вам нужно обратить внимание при обходе чисел, количествоvalueначинается с 1 иkeyот 0:

<ul>
  <li v-for='(v, k) in 3' :key='k'> {{ k }}-{{ v }} 
  <!-- output to be 0-1, 1-2, 2-3 -->
</ul>

2.2.0+версия, при использовании в компонентеv-forчас,keyЭто обязательно сейчас.

Уникальный корневой узел шаблона

а такжеJSXТочно так же шаблон в компоненте может иметь только один корневой узел, то есть следующая записьошибкаиз:

<template>
  <h1>Title</h1>
  <article>Balabala...</article>
</template>

Нам нужно обернуть его в элемент блочного уровня:

<template>
  <div>
    <h1>Title</h1>
    <article>Balabala...</article>
  </div>
</template>

Ссылка на причину:React-Notes: Заметки о разработке компонентов#Уникальный корневой узел

конфигурация пути к проекту

из-заvue-cliСконфигурированный проект предоставляет встроенный статический сервер, что в принципе не представляет проблемы на этапе разработки. Однако когда мы размещаем код на сервере, мы часто сталкиваемся с ошибками ссылок на статические ресурсы, что приводит к пустому интерфейсу.

Это потому чтоvue-cliнастроен по умолчаниюwebpackЭто файл, указанный в корневом каталоге сайта, однако иногда нам может потребоваться развернуть проект в подкаталоге.

мы можем пройтиconfig/index.jsЧтобы изменить относительный путь ссылки на файл:

  build.assetsSubDirectory: 'static'
  build.assetsPublicPath: '/'

  dev.assetsSubDirectory: 'static'
  dev.assetsPublicPath: '/'

Мы видим, что в объекте экспортаbuildа такжеdevобеassetsSubDirectory,assetsPublicPathэти два свойства.

вassetsSubDirectoryОтносится к папке статического ресурса, т. е. упакованномуjs,css, картинки и другие файлы помещаются в папку, по умолчанию это вообще не проблема.

assetsPublicPathОтносится к эталонному пути статических ресурсов, конфигурация по умолчанию/, который является корневым каталогом веб-сайта, сassetsSubDirectoryКомбинация представляет собой полный путь ссылки на статический ресурс./static.

Написанное здесь решение очевидно, просто измените корневой каталог на относительный:

  build.assetsSubDirectory: 'static'
  build.assetsPublicPath: './'

Точно! только один.Эта проблема. ㄟ( ▔, ▔ )ㄏ

Меньшие накладные расходы полифилла

во введенииPolyfillПосле этого вы можете.babelrcоткрыть в файлеuseBulitInsАтрибуты. Когда это свойство включено, компиляция проекта завершитpolyfillРазделить на последовательность независимых модулей.
включитьuseBuiltInsАтрибуты:

  // .babelrc
  {
    "presets": [
      ["env", {
        "modules": false,
        "useBuiltIns": true
      }],
      "es2015",
      "stage-2"
    ]
    // ...
  }

Импорт после установкиbabel-polyfill:

  // src/main.js
  import 'babel-polyfill'

  [1, 2, 3].find((v => v > 2))

включитьuseBulitInsавтоматическое разделениеbabel-polyfill

  import 'core-js/modules/es6.array.find'

  [1, 2, 3].find((v => v > 2))

Протестировано, чтобы уменьшить максимум примерно наполовинуpolyfillобъем
Я не изучал его глубоко, думаю, это могло быть добавленоcore-jsС некоторыми основнымиpolyfill

Использование функции класса ESnext

В сравнении

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

  const App = {
    // initialized data
    data () {
      return {
        init: false
      }
    }
    // lifecycle hook
    created () {}
    mounted () {}
    // ...
  }

  export default App

Мы можем поддерживать последнюю версию, установив некоторые зависимостиclassПишу:

  import Vue from 'vue'
  import Component from 'vue-class-component'

  @Component
  class App extends Vue {
    init = false;
    created () {}
    mounted () {}
  }

  export default App

Бесспорно, кода действительно больше, но я все же больше склоняюсь к написанию новых синтаксических возможностей, ведь стандарт — это маяк
P.S используется здесь все еще вStage 3изField declarationsобъявить первоначальныйdata

использовать

Давайте посмотрим, какие изменения необходимо внести для поддержки использованияclassзаписывается как:

  1. Во-первых, и это наиболее очевидно, нам нужноvue-class-componentЭто зависимо.
  2. Тогда эта зависимость требуетbabelизtransform-decorators-legacyПоддержка плагинов.
  3. Наконец, если вы также хотите использовать объявления полей объявлений полей, добавьтеtransform-class-propertiesДостаточно.

Установите зависимости:

  npm i vue-class-component -D
  npm i babel-plugin-transform-decorators-legacy -D
  npm i babel-plugin-transform-class-properties -D

конфигурацияbabel

  // .babelrc
  {
    // ...
    "plugins": [
      "transform-runtime",
      "transform-decorators-legacy",
      "transform-class-properties"
    ]
    // ...
  }

Уведомление:transform-decorators-legacyбыть размещеннымtransform-class-propertiesДо

Недействительность адаптивных данных

множество

из-заVue.jsРеактивные данные зависят отметод объекта Object.defineProperty. Но очевидно, что у специального "объекта" массива нет этого метода, и, естественно, он не может задавать свойства объекта.descriptor, чтобы не былоgetter()а такжеsetter()метод. Таким образом, при изменении данных элемента в виде индекса массива index (arr[index] = newVal), представления, как правило, не обновляются в ответ.
Для решения этой проблемы,Vue.jsпредоставлено в$set()метод:

vm.arr.$set(0, 'newVal')
// vm.arr[0] = 'newVal'

объект

с учетом современныхJavaScriptограничения (и устаревшиеObject.observe),Vue Не удается обнаружить добавление или удаление свойств объекта. из-заVueбудет выполняться для свойств при инициализации экземпляраgetter/setterпроцесс преобразования, поэтому атрибут должен быть вdataсуществуют на объекте, чтобы позволитьVueПреобразуйте его, чтобы он был отзывчивым.
Ref: Углубленные принципы реактивного взаимодействия — Vue.js

var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` 是响应的
vm.b = 2
// `vm.b` 是非响应的

Выявление статического типа

Рекомендуется при разработке более сложных компонентовpropsСтатическое обнаружение типов повышает надежность компонентов.В большинстве случаев ошибки можно обнаружить заранее на этапе перекодирования.

// before
prop: [
  'id',
  'multiple',
  'callback',
]
// after
props: {
  id: {
    type: [ Number, Array ],
    required: true,
  },
  multiple: {
    type: Boolean,
    default: false,
  },
  callback : Function,
}

Асинхронные компоненты

использовать вStage.3Динамически импортируемые функции для этаповimport(), при использованииwebpackФункция сегментации кода, вVue.jsМы можем легко реализовать асинхронный компонент.

Компонент асинхронной маршрутизации

const AsyncComponent = () => import('./AsyncComponent')

Фабрика асинхронных компонентов

Vue.component(
  'async-webpack-example',
  () => import('./my-async-component')
)

По сравнению с асинхронными компонентами маршрутизации, асинхронные фабрики компонентов обычно подходят для дальнейшей мелкозернистой раздельной обработки в компонентах, таких как: второстепенные компоненты (всплывающие компоненты, вложенные в компоненты илиPopoverкомпоненты и др.).


To be continue...

Статья еще дорабатывается, приглашаю всех к обсуждению некоторых проблем, возникших при разработке Vue.JS (゚▽゚)/

Взгляните на свой список избранного, уверены ли вы, что не забудете посмотреть его после того, как соберете?

оригинал:Коллекция общих проблем разработки VueJS