Краткое изложение разработки мобильного терминала Vue

внешний интерфейс Vue.js

Адаптация мобильного терминала

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

На мобильных устройствах мы часто видим этот код в теге head:

<meta name='viewport' content='width=device-width,initial-scale=1,user-scale=no' />

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

  • layoutviewportШирина макета — это ширина веб-страницы.
  • visualviewportОднако ширина — это ширина окна браузера, и это значение определяет то, что мы можем видеть на одном экране нашего мобильного телефона;visualviewportа такжеlayoutviewportОтношение размера определяет, будет ли отображаться полоса прокрутки.visualviewportбольше или точно равноlayoutviewportполоса прокрутки не появится.
  • idealviewportОбласть просмотра, определенная для браузера, которая может быть идеально адаптирована к мобильному терминалу, является фиксированной и может рассматриваться как ширина области просмотра устройства.device-width.

Настройка мета на самом деле правильнаяlayoutviewportа такжеvisualviewportСделайте настройки.

  • width=device-widthУказывает ширину страницыlayoutviewportс шириной области просмотра устройстваidealviewportпоследовательный
  • initial-scale=1Указывает начальное масштабирование ширины страницы и ширины веб-страницы до ширины области просмотра устройства,visualviewportопределяется этим соотношением, но дляlayoutviewportНапример, на него влияют два свойства одновременно, а затем принимает большее значение.
  • user-scale=noОтключить масштабирование

Итак, теперь мы знаем, что значение этого кода, распространенного на мобильной стороне, вот-вот исчезнет.visualviewportа такжеlayoutviewportУстановить какidealviewportТаким образом, у нас не будет полос прокрутки на мобильном терминале, а содержимое веб-страницы может быть лучше отображено.Исходя из этого, мы будем рассматривать адаптацию страницы.

При отрисовке пользовательского интерфейса, как правило, имеется фиксированная ширина, но ширина наших реальных мобильных устройств не одинакова.Однако, если коэффициент масштабирования элементов страницы совпадает с коэффициентом масштабирования ширины страницы, на устройствах с разными размеры Эффект наших веб-страниц также будет таким же.

Используйте относительные единицы

rem

rem вычисляется относительно размера шрифта корневого элемента html. Обычно это достигается установкой document.documentElement.style.fontSize при инициализации и загрузке страницы. Как правило, мы устанавливаем размер шрифта корневого элемента html равным 1/10 ширины.Ширина разных устройств отличается, но соотношение rem одного и того же значения согласуется с соотношением ширины устройства.

document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';

В реальных проектах нам не нужно конвертировать себя в разработку, мы можем использоватьpxtoremПреобразуйте px в rem на выходе.

Единицы области просмотра

ширина области просмотраwindow.innerWidthи высота области просмотраwindow.innerHeight(которыйlayoutviewport) на 100 равных частей.

vw : 1vw составляет 1% от ширины области просмотра. vh : 1vh составляет 1% от высоты окна просмотра. vmin : меньшее из vw и vh vmax : выберите большее из vw и vh

По сравнению с rem, блоку просмотра не нужно использовать js для установки корневого элемента, а совместимость немного плохая, но большинство устройств уже поддерживает его, точно так же нет необходимости выполнять преобразование единиц во время разработки и использовать соответствующие плагины напрямую.postcss-px-to-viewportКонвертировать на выходе.

Изменить область просмотра

Ранее мы упоминалиlayoutviewportШирина макета на самом деле не является фиксированным значением, а задается через мета-атрибут, черезidealviewportВычисленное значение, мы можем контролировать атрибут мета дляlayoutviewportфиксируется на определенном значении. Ширина чертежа общего дизайна 750px, и теперь наша цельlayoutviewportустановите 750 пикселей;layoutviewportЗатронутые двумя свойствами, мы устанавливаем свойство ширины на 750, а коэффициент масштабирования начального масштаба должен бытьidealviewportширина/750; когда мы не меняли атрибут метатега,layoutviewportЗначение на самом делеidealviewportзначение, поэтому можно пройтиdocument.body.clientWidthилиwindow.innerWidthчтобы получить.

;(function () {
    const width = document.body.clientWidth || window.innerWidth
    const scale = width / 750
    const content = 'width=750, initial-scale=' + scale + ', minimum-scale=' + scale + ', maximum-scale=' + scale + ', viewport-fit=cover'
    document.querySelector('meta[name="viewport"]').content = content
})()

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

стиль макета

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

исправлено, к которому нужно относиться с осторожностью

position:fixedОн очень часто используется в ежедневных макетах страниц и играет ключевую роль во многих макетах. Что он делает:position:fixedЭлемент будет указывать свое положение относительно положения окна просмотра экрана. И положение элемента не меняется при прокрутке экрана. Однако во многих конкретных ситуацияхposition:fixedСпектакль сильно отличается от того, что мы себе представляли.

  1. iOS выводит клавиатуру; после вызова программной клавиатуры фиксированный элемент страницы будет недействителен (iOS считает, что пользователь предпочитает, чтобы элемент перемещался при прокрутке, то есть абсолютное позиционирование), так как он стал абсолютным, поэтому, когда страница превышает единицу. Когда экран прокручивается, недопустимый фиксированный элемент будет следовать за прокруткой.
  2. Когда свойство преобразования предка элемента не равно none, контейнер позиционирования изменяется с области просмотра на этого предка. Проще говоря, этоposition:fixedЭлемент будет расположен относительно ближайшего предка, к которому было применено преобразование, а не окна. Причина этого в том, что элементы, использующие преобразование, создадут новый контекст стека. Контекст стекирования: контекст стекирования — это трехмерная концепция HTML-элементов, которые простираются по воображаемой оси Z по отношению к пользователю, обращенному к окну (экрану компьютера) или веб-странице. HTML-элементы имеют приоритет в соответствии с их собственными атрибутами. порядок занимает место в контексте стека. Порядок показан на рисунке ниже.Короче говоря, контекст стека будет влиять на отношение позиционирования. Хотите увидеть дальшеНеуправляемое положение: фиксированное.

Всплывающие окна клавиатуры и использование атрибута преобразования очень распространены на мобильных устройствах, поэтому их следует использовать с осторожностью.position:fixed.

рекомендуется гибкий

flex, то есть гибкая компоновка, имеет хорошую совместимость с мобильными терминалами и может удовлетворить большинству требований к компоновке. Теперь мы используем flex для реализации общего макета верхней строки заголовка + среднего прокручиваемого содержимого + нижней панели навигации в h5; эффект выглядит следующим образом:
Во-первых, давайте реализуем общий макет. Общий макет должен быть направлениемflex-direction: column;И эластичный бокс, занимающий все окно, и тогда макет внутри должен быть фиксированной высоты в начале и в конце, а область содержимого посередине —flex: 1;.

html, body {
  padding: 0;
  margin: 0;
}
.page {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  .page-content {
    flex: 1;
    overflow-y: auto;
  }
}

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

<template>
  <div class="header">
    <div class="header__left">
      左
    </div>
    <div class="header__main">
      <slot>Title</slot>
    </div>
    <div class="header__right">
      <div>右</div>
    </div>
  </div>
</template>
<script>
export default {
  name: 'HeadBar'
}
</script>
<style lang="scss" scoped>
.header {
  display: flex;
  width: 100%;
  line-height: 88px;
  height: 88px;
  font-size: 36px;
  background-color: #42b983;
  z-index: 999;
  color: #fff;
  transition: background-color .5s ease;
  .header__main {
    flex: 1;
    display: flex;
    justify-content: center;
    align-items: center;
    text-align: center;
  }
  .header__left, .header__right {
    padding: 0 16px;
    width: 120px;
  }
  .header__left {
    text-align: left;
  }
  .header__right {
    text-align: right;
  }
}
</style>

Основная часть нижней панели навигации фактически представляет собой один параметр навигации, который делит панель навигации пополам, а каждый параметр навигации является направлением движения.flex-direction: column;Горизонтальное расположение естьalign-items: center;, вертикаль естьjustify-content: space-around;

<template>
  <div class="taber">
    <div class="taber-item">
      <div class="icon"></div>
      <span>选项1</span>
    </div>
    <div class="taber-item">
      <div class="icon"></div>
      <span>选项2</span>
    </div>
    <div class="taber-item">
      <div class="icon"></div>
      <span>选项3</span>
    </div>
    <div class="taber-item">
      <div class="icon"></div>
      <span>选项4</span>
    </div>
  </div>
</template>
<script>
export default {
  name: 'BottomTaber',
  data () {
    return {
    }
  }
}
</script>
<style lang="scss" scoped>
.taber {
  background-color: #42b983;
  color: #fff;
  height: 88px;
  display: flex;
  .taber-item {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    align-items: center;
  }
  .icon {
    width: 36px;
    height: 36px;
    background-color: #fff;
  }
}
</style>

прыжок страницы

анимация перехода

В vue мы управляем маршрутизацией через vue-router.Каждый переход маршрутизации аналогичен переключению между разными страницами.С точки зрения удобства пользователя лучше всего добавлять эффект перехода каждый раз при переключении страниц. Если анимация перехода не различает, открывает ли маршрут новую страницу или возвращается на предыдущую страницу, нам нужно только<router-view>внешнее использование<transition>Достаточно добавить анимационный эффект, но обычно к открытию и возврату применяются разные анимационные эффекты, поэтому при переключении маршрутов нам нужно различать, идет ли маршрут вперед или назад. Чтобы различать действия маршрутизации, мы устанавливаем мета как число в файле маршрутизации, мета представляет глубину его маршрутизации, а затем слушаем $route и устанавливаем различные анимации перехода в соответствии с мета-значениями «куда» и «из». Если он применяется к нескольким анимационным прыжкам, его можно применять в соответствии с деталями и конкретными обстоятельствами.

<template>
  <transition :name="transitionName">
    <router-view></router-view>
  </transition>
</template>

<script>
export default {
  name: 'app',
  data () {
    return {
      transitionName: 'fade'
    }
  },
  watch: {
    '$route' (to, from) {
      let toDepth = to.meta
      let fromDepth = from.meta
      if (fromDepth > toDepth) {
        this.transitionName = 'fade-left'
      } else if (fromDepth < toDepth) {
        this.transitionName = 'fade-right'
      } else {
        this.transitionName = 'fade'
      }
    }
  }
}
</script>

登录跳转
Хоть этим и можно добиться эффекта прыжка, но добавить настройки при написании роутера хлопотнее, можно использовать проекты с открытым исходным кодомvue-navigationДобиться этого удобнее, и нет необходимости выполнять избыточные настройки на роутере.npm i -S vue-navigationУстановить, импортировать в main.js:

import Navigation from 'vue-navigation'
Vue.use(Navigation, {router}) // router为路由文件

Установите в App.vue:

this.$navigation.on('forward', (to, from) => {
    this.transitionName = 'fade-right'
 })
 this.$navigation.on('back', (to, from) => {
    this.transitionName = 'fade-left'
 })
 this.$navigation.on('replace', (to, from) => {
    this.transitionName = 'fade'
 })

Другой важной функцией плагина vue-navigation является сохранение состояния страницы, аналогичное сохранению активности, но состояние сохранения активности не может идентифицировать прямой и обратный маршрут.В практических приложениях наше требование состоит в возврате на страницу и надеюсь, что состояние страницы Сохранить, когда вы входите на страницу, вы хотите получить новые данные.Использование vue-навигации может хорошо добиться этого эффекта. Для конкретного использования см.vue-navigationЕсть подробные инструкции и кейсы. Также попробуйтеvue-page-stack, оба проекта могут добиться нужного нам эффекта,vue-page-stackзаимствованныйvue-navigation, также реализовало больше функций и недавно было обновлено.

PS: Эффект анимации здесь взят из animate.scss;

Нижняя панель навигации

Мы реализовали базовый стиль нижней панели навигации ранее, здесь мы сделаем некоторые пояснения. Если путь маршрутизации страницы совпадает сrouter-linkКогда маршрут совпадает,router-linkбудет установлен в активное состояние, мы можем установитьactive-classЧтобы установить имя класса, применяемое при активации пути, по умолчанию используетсяrouter-link-active, а имя активированного класса также имеетrouter-link-exact-active, имя класса задаетсяexact-active-classЧтобы установить, это также имя класса, применяемое при активации пути настройки;active-classа такжеexact-active-classФактически он определяется методом сопоставления маршрута.

Общий метод сопоставления маршрутов — сопоставление включений. Например, если текущий путь начинается с /a, также будет установлено имя класса CSS. Согласно этому правилу каждый маршрут будет активирован, при использованииexactАтрибуты могут использовать «шаблон точного соответствия». Точное сопоставление активируется только в том случае, если маршруты идентичны.

охранник маршрута

Защита маршрутизации на мобильном терминале, как правило, не слишком сложна, в основном это оценка разрешений на вход.Мы настраиваем белый список маршрутизации и помещаем в него все маршруты, не требующие разрешений на вход, для входа в систему без входа в систему. Страница требует, чтобы пользователь вошел в систему, а затем получил к ней доступ. Если пользователю необходимо вернуться к исходному маршруту после входа в систему, передайте маршрут целевой страницы в качестве параметра на страницу входа, а затем примите решение после входа в систему. Если есть параметр целевой страницы, перейдите на целевую страницу. Просто перейдите на домашнюю страницу.

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

const whiteList = ['/login']
router.beforeEach((to, from, next) => {
  const hasToken = store.getters.auth
  if (hasToken) {
    if (to.path === '/login') {
      next({ path: '/' })
    } else {
      const needRoles = to.meta && to.meta.roles && to.meta.roles.length > 0
      if (needRoles) {
        const hasRoles = store.state.user.roles.some(role => to.meta.roles.includes(role))
        if (hasRoles) {
          next()
        } else {
          next('/403')
        }
      } else {
        next()
      }
    }
  } else {
    if (whiteList.includes(to.path)) {
      next()
    } else {
      next('/login')
    }
  }
})

компоненты

автозагрузка

В нашем проекте часто используется много компонентов, как правило, компоненты с высокой частотой используются в проекте как глобальные компоненты, чтобы избежать утомительного повторного импорта. Чтобы зарегистрировать глобальные компоненты, нам сначала нужно ввести компоненты, а затем использоватьVue.componentЗарегистрируйтесь; это повторяющаяся работа, которую мы делаем каждый раз, когда создаем компонент, если наш проект собран с помощью веб-пакета (vue-cli также использует веб-пакет), мы можем передатьrequire.contextАвтоматически регистрировать компоненты в файле global. Создайтеcomponents/index.jsдокумент:

export default function registerComponent (Vue) {
  /**
   * 参数说明:
   * 1. 其组件目录的相对路径
   * 2. 是否查询其子目录
   * 3. 匹配基础组件文件名的正则表达式
   **/
  const modules = require.context('./', false, /\w+.vue$/)
  modules.keys().forEach(fileName => {
    // 获取组件配置
    const component = modules(fileName)
    // 获取组件名称,去除文件名开头的 `./` 和结尾的扩展名
    const name = fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
    // 注册全局组件
    // 如果这个组件选项是通过 `export default` 导出的,
    // 那么就会优先使用 `.default`,
    // 否则回退到使用模块的根。
    Vue.component(name, component.default || component)
  })
}

позжеmain.jsИмпортируйте модуль регистрации для регистрации, используйтеrequire.contextМы также можем реализовать импорт плагинов vue и глобальных фильтров.

import registerComponent from './components'
registerComponent(Vue)

Связывание данных через v-модель

v-modelЭто синтаксический сахар, суть его в отслеживании событий компонентов и обновлении данных, это аббревиатура от props и $on Monitoring events.v-modelдоставка по умолчаниюvalue, мониторinputмероприятие. Теперь мы используемv-modelЧтобы реализовать поле ввода числа, это поле ввода может вводить только числа.В компоненте нам нужно только определить значение, чтобы принять переданное значение, а затем использовать его, когда входное значение соответствует нашим условиям ввода (ввод - это число) .$emitвызыватьinputмероприятие.

<template>
  <div>
    <input type="text" :value="value" @input="onInput">
  </div>
</template>
<script>
export default {
  name: 'NumberInput',
  props: {
    value: String
  },
  methods: {
    onInput (event) {
      if (/^\d+$/.test(event.target.value)) {
        this.$emit('input', event.target.value)
      } else {
        event.target.value = this.value
      }
    }
  }
}
</script>

При использовании нам нужно использовать толькоv-modelПросто привяжите значение.v-modelПо умолчанию будет использоватьсяvalueизpropи назвалinputсобытие, но много раз мы хотим использовать другойpropи прослушивание различных событий, мы можем использоватьmodelопции для модификации.

Vue.component('my-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    // this allows using the `value` prop for a different purpose
    value: String,
    // use `checked` as the prop which take the place of `value`
    checked: {
      type: Number,
      default: 0
    }
  },
  // ...
})
<my-checkbox v-model="foo" value="some value"></my-checkbox>

Приведенный выше код эквивалентен:

<my-checkbox
  :checked="foo"
  @change="val => { foo = val }"
  value="some value">
</my-checkbox>

Используйте компоненты в качестве плагинов

Во многих сторонних библиотеках компонентов мы часто видим способ прямого вызова компонентов с помощью плагинов, таких какVantUIКомпонент диалогового всплывающего окна, мы можем не только использовать его в виде компонента, но и вызывать в виде плагина.

this.$dialog.alert({
  message: '弹窗内容'
});

Принцип использования компонентов в качестве плагинов на самом деле не сложен, то есть использовать ручное монтирование экземпляров компонента Vue.

import Vue from 'vue';
export default function create(Component, props) {
    // 先创建实例
    const vm = new Vue({
        render(h) {
            // h就是createElement,它返回VNode
            return h(Component, {props})
        }
    }).$mount();
    // 手动挂载
    document.body.appendChild(vm.$el);
    // 销毁方法
    const comp = vm.$children[0];
    comp.remove = function() {
        document.body.removeChild(vm.$el);
        vm.$destroy();
    }
    return comp;
}

передачаcreateвходящий компонент иpropsПараметр может получить экземпляр компонента, и через экземпляр компонента мы можем вызывать различные функции компонента.

<template>
  <div class="loading-wrapper" v-show="visible">
    加载中
  </div>
</template>
<script>
export default {
  name: 'Loading',
  data () {
    return {
      visible: false
    }
  },
  methods: {
    show () {
      this.visible = true
    },
    hide () {
      this.visible = false
    }
  }
}
</script>
<style lang="css" scoped>
.loading-wrapper {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100%;
  background-color: rgba(0, 0, 0, .4);
  z-index: 999;
}
</style>
<!--使用-->
const loading = create(Loading, {})
loading.show() // 显示
loading.hide() // 关闭

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

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

Общая библиотека компонентов

VantUIЭто набор легкой и надежной библиотеки мобильных компонентов Vue с открытым исходным кодом Youzan; он поддерживает импорт по запросу, настройку темы и SSR.Помимо обычных компонентов, существуют специальные бизнес-компоненты для сценариев электронной коммерции. разработка проекта электронной коммерции. Если это так, рекомендуется использовать его. Официальная документация по настройке темы находится по адресуwebpack.config.jsустановить в:

// webpack.config.js
module.exports = {
  rules: [
    {
      test: /\.less$/,
      use: [
        // ...其他 loader 配置
        {
          loader: 'less-loader',
          options: {
            modifyVars: {
              // 直接覆盖变量
              'text-color': '#111',
              'border-color': '#eee'
              // 或者可以通过 less 文件覆盖(文件路径为绝对路径)
              'hack': `true; @import "your-less-file-path.less";`
            }
          }
        }
      ]
    }
  ]
};

Но наш проект может быть собран с использованием vue-cli, тогда нам нужноvue.config.jsустановить в:

module.exports = {
  css: {
    loaderOptions: {
      less: {
        modifyVars: {
          'hack': `true; @import "~@/assets/less/vars.less";`
        }
      }
    }
  }
}

Кроме тогоvux,mint-uiТоже хороший выбор.

Общие плагины

better-scrollЭто плагин, который обеспечивает эффекты плавной прокрутки для различных сценариев прокрутки на мобильной стороне.Если вы используете его в vue, вы можете обратиться к статье автораКогда лучшая прокрутка встречается с Vue.

swiperЭто карусельный плагин, если он используется в vue, его можно использовать напрямую.vue-awesome-swiper,vue-awesome-swiperна основеSwiper4и поддерживает SSR.