10 советов по разработке Vue, которые помогут вам стать лучшим инженером (2)

Vue.js

Изящно обновляйте реквизит

возобновитьpropЭто очень распространенное требование в бизнесе, но прямая модификация в подкомпонентах запрещена.prop, поскольку такой подход не соответствует принципу одностороннего потока данных, и в режиме разработки будет выдано предупреждение. Так что большинство людей пройдет$emitИнициировать пользовательское событие, получить значение события в родительском компоненте для обновленияprop.

child.vue:

export defalut {
    props: {
        title: String  
    },
    methods: {
        changeTitle(){
            this.$emit('change-title', 'hello')
        }
    }
}

parent.vue:

<child :title="title" @change-title="changeTitle"></child>
export default {
    data(){
        return {
            title: 'title'
        }  
    },
    methods: {
        changeTitle(title){
            this.title = title
        }
    }
}

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

parent.vue:

<child :title.sync="title"></child>

child.vue:

export defalut {
    props: {
        title: String  
    },
    methods: {
        changeTitle(){
            this.$emit('update:title', 'hello')
        }
    }
}

Просто добавьте свойство привязки.sync, который может быть запущен внутри дочернего компонентаupdate:属性名обновитьprop. Видно, что этот метод действительно лаконичен и элегантен, что уменьшает «лишнюю функцию» в коде родительского компонента.

Справочная документация

provide/inject

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

Проще говоря, компонент передает свои свойства черезprovideвыставлены, потомки ниже негоinjectполучить выставленные свойства.

App.vue:

export default {
    provide() {
        return {
            app: this
        }
    } 
}

child.vue:

export default {
    inject: ['app'],
    created() {
        console.log(this.app) // App.vue实例
    }
}

В версии 2.5.0+ это можно сделать необязательным, установив значение по умолчанию:

export default {
    inject: {
        app: {
            default: () => ({})
        }
    },
    created() {
        console.log(this.app) 
    }
}

если вы хотитеinjectимя изменения свойства, вы можете использоватьfromуказать его источник:

export default {
    inject: {
        myApp: {
            // from的值和provide的属性名保持一致
            from: 'app',
            default: () => ({})
        }
    },
    created() {
        console.log(this.myApp) 
    }
}

должен быть в курсеprovideа такжеinjectВ основном используется при разработке высокоуровневых библиотек плагинов/компонентов. Не рекомендуется использовать в обычном коде приложения. Но в какой-то момент, возможно, это может нам помочь.

Справочная документация

Малый государственный менеджер

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

В версии 2.6.0+ новыйVue.observableМожет помочь нам решить эту неловкую проблему, он может превратить объект в реактивные данные:

// store.js
import Vue from 'vue'

export const state = Vue.observable({ 
  count: 0 
})

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

<div @click="setCount">{{ count }}</div>
import {state} from '../store.js'

export default {
    computed: {
        count() {
            return state.count
        }
    },
    methods: {
        setCount() {
            state.count++
        }
    }
}

Конечно, вы также можете настроитьmutationЧтобы повторно использовать методы, которые изменяют состояние:

import Vue from 'vue'

export const state = Vue.observable({ 
  count: 0 
})

export const mutations = {
  SET_COUNT(payload) {
    if (payload > 0) {
        state.count = payload
    } 
  }
}

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

import {state, mutations} from '../store.js'

export default {
    computed: {
        count() {
            return state.count
        }
    },
    methods: {
        setCount() {
            mutations.SET_COUNT(100)
        }
    }
}

Справочная документация

удалить часы

Наблюдения за данными обычно определяются с помощью опций вwatchСредняя конфигурация:

export default {
    data() {
        return {
            count: 1      
        }
    },
    watch: {
        count(newVal) {
            console.log('count 新值:'+newVal)
        }
    }
}

В дополнение к этому существует еще один функциональный способ определения наблюдений за данными:

export default {
    data() {
        return {
            count: 1      
        }
    },
    created() {
        this.$watch('count', function(){
            console.log('count 新值:'+newVal)
        })
    }
}

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

let unwatchFn = this.$watch('count', function(){
    console.log('count 新值:'+newVal)
})
this.count = 2 // log: count 新值:2
unwatchFn()
this.count = 3 // 什么都没有发生...

$watchТретий параметр получает параметр конфигурации:

this.$watch('count', function(){
    console.log('count 新值:'+newVal)
}, {
    immediate: true // 立即执行watch
})

Справочная документация

Используйте шаблон умело

полагатьv-ifЭто наиболее часто используемая инструкция в разработке, поэтому вы, должно быть, сталкивались с такой сценой, нужно переключать несколько элементов, и условия переключения одинаковы, обычно оборачивают элементом, и включают этот элемент.

<div v-if="status==='ok'">
    <h1>Title</h1>
    <p>Paragraph 1</p>
    <p>Paragraph 2</p>
</div>

Если div, подобный приведенному выше, существует только для условий переключения, а также вызывает вложенность элементов еще на один уровень, то он не имеет «смысла существования».

Все мы знаем, что при объявлении шаблона страницы все элементы нужно размещать в<template>внутри элемента. Кроме того, его также можно использовать в шаблонах,<template>Элемент действует как невидимый элемент-обертка, который обрабатывается только во время выполнения и не содержит его в конечном результате рендеринга.

<template>
    <div>
        <template v-if="status==='ok'">
          <h1>Title</h1>
          <p>Paragraph 1</p>
          <p>Paragraph 2</p>
        </template>
    </div>
</template>

Точно так же мы можем также<template>использовать наv-forИнструкция, этот метод также может решитьv-forа такжеv-ifТакже используйте сообщение о проблеме с предупреждением.

<template v-for="item in 10">
    <div v-if="item % 2 == 0" :key="item">{{item}}</div>
</template>

шаблон использует v-if,шаблон использует v-для

мультиплексирование фильтра

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

<div>{{ text | capitalize }}</div>
export default {
    data() {
        return {
            text: 'hello'
        }  
    },
    filters: {
        capitalize: function (value) {
            if (!value) return ''
            value = value.toString()
            return value.charAt(0).toUpperCase() + value.slice(1)
         }
    }
}

Представьте себе сценарий, где эта функция используется не только в шаблоне, но и вmethodВам также нужна функция с той же функцией. но фильтр не проходитthisпрямое цитирование, если оно должно быть вmethodsОпределить ту же функцию еще раз?

Имейте в виду, что конфигурация параметров будет храниться в папке экземпляра.$options, так что просто получитьthis.$options.filtersВы можете получить фильтр в экземпляре.

export default {
    methods: {
        getDetail() {
            this.$api.getDetail({
                id: this.id
            }).then(res => {
                let capitalize = this.$options.filters.capitalize
                this.title = capitalize(res.data.title)
            })
        }
    }
}

Помимо получения фильтров экземпляров, вы также можете получить глобальные фильтры, потому чтоthis.$options.filtersпоследует__proto__Посмотрите вверх, и глобальный фильтр существует в прототипе.

Получить экземпляр пользовательской команды

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

Vue.directive('role', {
    inserted: function (el, binding, vnode) {
      let role = binding.value
      if(role){
        const applist = sessionStorage.getItem("applist")
        const hasPermission = role.some(item => applist.includes(item)) 
        // 是否拥有权限
        if(!hasPermission){
          el.remove() //没有权限则删除模块节点
        }
      }
    }
})

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

Предположим теперь, что бизнес меняется,applistсохранить вvuex, но директива хочет использовать свойство экземпляра или прототипа$store. У нас нет способа получить его, потому что доступ к экземпляру напрямую не предоставляется в функции ловушки.vnodeКак текущий виртуальный дом, он привязан к контексту экземпляра.vnode.contextможно легко решить проблему.

Vue.directive('role', {
    inserted: function (el, binding, vnode) {
      let role = binding.value
      if(role){
        // vnode.context 为当前实例
        const applist = vnode.context.$store.state.applist
        const hasPermission = role.some(item => applist.includes(item)) 
        if(!hasPermission){
          el.remove()
        }
      }
    }
})

Элегантный плагин регистрации

Плагины часто используются дляVueДобавьте глобальную функциональность. как обычно используетсяvue-router,vuexиспользуются черезVue.useзарегистрироваться.Vue.useавтоматически найдетinstallМетод вызывается, и первый параметр, который он принимает,VueКонструктор.

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

вант.config.js:

import {
  Toast,
  Button
} from 'vant'

const components = {
  Toast,
  Button
}

const componentsHandler = {
  install(Vue){
    Object.keys(components).forEach(key => Vue.use(components[key]))
  }
}

export default componentsHandler

main.js:

import Vue from 'vue'
import vantCompoents from '@/config/vant.config'

Vue.config.productionTip = false

Vue.use(vantCompoents)

new Vue({
  render: h => h(App)
}).$mount('#app')

Справочная документация

Автоматически импортировать модули

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

Разделите модули по функциям, например, файлы API, которые можно использовать при объединении.require.contextИмпортируйте все файлы модулей папки одновременно, вместо того, чтобы импортировать файлы модулей по одному. Всякий раз, когда добавляется файл модуля, вам нужно сосредоточиться только на написании логики и демонстрации модулей.require.contextпоможет нам импортировать автоматически.

требует вниманияrequire.contextне от природы, а отwebpackпоставка. При строительстве,webpackРазберите его в коде.

let importAll = require.context('./modules', false, /\.js$/)

class Api extends Request{
    constructor(){
        super()
        //importAll.keys()为模块路径数组
        importAll.keys().map(path =>{
            //兼容处理:.default获取ES6规范暴露的内容; 后者获取commonJS规范暴露的内容
            let api = importAll(path).default || importAll(path)
            Object.keys(api).forEach(key => this[key] = api[key])
        })
    }
}

export default new Api()

require.contextпараметр:

  1. Путь к папке
  2. Нужно ли рекурсивно находить модули в подпапках
  3. Правила сопоставления модулей, обычно совпадающие суффиксы файлов

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

Справочная документация

Отложенная загрузка маршрута (динамическое имя блока)

В качестве средства оптимизации производительности отложенная загрузка маршрутизации позволяет лениво загружать компоненты маршрутизации. Обычно мы также добавляем «магический комментарий» (webpackChunkName) к лениво загруженному маршруту, чтобы настроить имя пакета.При упаковке компонент маршрутизации будет упакован отдельно.

let router = new Router({
  routes: [
    {
      path:'/login',
      name:'login',
      component: import(/* webpackChunkName: "login" */ `@/views/login.vue`)
    },
    {
      path:'/index',
      name:'index',
      component: import(/* webpackChunkName: "index" */ `@/views/index.vue`)
    },
    {
      path:'/detail',
      name:'detail',
      component: import(/* webpackChunkName: "detail" */ `@/views/detail.vue`)
    }
  ]
})

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

const routeOptions = [
  {
    path:'/login',
    name:'login',
  },
  {
    path:'/index',
    name:'index',
  },
  {
    path:'/detail',
    name:'detail',
  },
]

const routes = routeOptions.map(route => {
  if (!route.component) {
    route = {
      ...route,
      component: () => import(`@/views/${route.name}.vue`)
    }
  }
  return route
})

let router = new Router({
  routes
})

Пишем меньше кода, но также жертвуем «магическими комментариями». Как мы все знаем, в коде нельзя писать динамические комментарии. Этот вопрос смущает, разве нет способа получить лучшее из обоих миров?

мощныйwebpackК счастью, начиная с версии webpack 2.6.0 заполнители [index] и [request] поддерживаются как увеличивающиеся числа или фактические проанализированные имена файлов. Мы можем использовать «магические аннотации» следующим образом:

const routes = routeOptions.map(route => {
  if (!route.component) {
    route = {
      ...route,
      component: () => import(/* webpackChunkName: "[request]" */ `@/views/${route.name}.vue`)
    }
  }
  return route
})

Справочная документация,Справочная статья

наконец

Предыдущие статьи по теме:

10 советов по разработке Vue, чтобы стать лучшим инженером