Сравнение синтаксисов Vue JS

от автора

Введение

Экосистема Vue JS развивается с каждым годом. На данный момент существует несколько разных синтаксисов:

Так же встречаются проекты с синтаксисом:

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

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

Используемая документация:

Перед изучением сравнений я рекомендую прочитать статью про отличия script setup.

Определение компонента страницы

Vue Options API

<template>   <!-- ... --> </template>  <script> export default {   name: "ParentComponent", }; </script> 

Vue Composition API

<template>  <!-- ... --> </template>  <script> export default {   // ... } </script> 

Vue Composition API

<template>  <!-- ... --> </template>  <script setup> // ... </script> 

Vue Class API

<script lang="ts"> import { Options, Vue } from "vue-class-component";  @Options({}) export default class ParentComponent extends Vue {   // ... } </script> 

Vue Class API + vue-property-decorator

Синтаксис аналогичен Vue Class API.

Регистрация дочернего компонента

Vue Options API

<template>   <child-component /> </template>  <script> import ChildComponent from "@/components/ChildComponent.vue";  export default {   name: "ParentComponent",   components: {     ChildComponent,   }, }; </script> 

Vue Composition API

<template>   <child-component /> </template>  <script setup> import ChildComponent "@/components/ChildComponent.vue"  // ...  </script> 

Vue Composition API

<template>   <child-component /> </template>  <script> import ChildComponent from "@/components/ChildComponent.vue";  export default {   components: {     ChildComponent,   }, }; </script> 

Vue Class API

<template>   <child-component /> </template>  <script lang="ts"> import { Options, Vue } from "vue-class-component"; import ChildComponent from "@/components/ChildComponent.vue";  @Options({   components: {     ChildComponent,   }, }) export default class ParentComponent extends Vue {} </script> 

Vue Class API + vue-property-decorator

Синтаксис аналогичен Vue Class API.

Определение реактивных данных

Vue Options API

<script> export default {   data() {     return {       someObject: {}     }   } } </script> 

Vue Composition API

<script> import { ref } from 'vue'  export default {   setup() {     const someObject = ref({})          return {       someObject     }   } } </script> 

Vue Composition API

<script setup> import { ref } from 'vue'  const someObject = ref({}) </script>

Vue Class API

<script> import { Options, Vue } from "vue-class-component"; import ChildComponent from "@/components/ChildComponent.vue";  @Options({}) export default class ParentComponent extends Vue {   message = "Hello World!"; } </script> 

Vue Class API + vue-property-decorator

Синтаксис аналогичен Vue Class API.

Определение computed свойства

Vue Options API

<script> export default {   data() {     return {       firstName: 'John',       lastName: 'Doe'     }   },   computed: {     fullName: {       get() {         return this.firstName + ' ' + this.lastName       },       set(newValue) {         [this.firstName, this.lastName] = newValue.split(' ')       }     }   } } </script> 

Vue Composition API

<script> import { reactive, computed } from 'vue'  export default {   setup() {     const author = reactive({       name: 'John Doe',       books: [         'Vue 2 - Advanced Guide',         'Vue 3 - Basic Guide',         'Vue 4 - The Mystery'       ]     })          const publishedBooksMessage = computed(() => {       return author.books.length > 0 ? 'Yes' : 'No'     })          return {       publishedBooksMessage     }   } } </script> 
<script setup> import { ref, computed } from 'vue'  export default {   setup() {     const firstName = ref('John')     const lastName = ref('Doe')      const fullName = computed({       get() {         return firstName.value + ' ' + lastName.value       },       set(newValue) {         [firstName.value, lastName.value] = newValue.split(' ')       }     })          return {       fullName     }   } } </script> 

Vue Composition API

<script setup> import { reactive, computed } from 'vue'  const author = reactive({   name: 'John Doe',   books: [     'Vue 2 - Advanced Guide',     'Vue 3 - Basic Guide',     'Vue 4 - The Mystery'   ] })  const publishedBooksMessage = computed(() => {   return author.books.length > 0 ? 'Yes' : 'No' }) </script> 
<script setup> import { ref, computed } from 'vue'  const firstName = ref('John') const lastName = ref('Doe')  const fullName = computed({   get() {     return firstName.value + ' ' + lastName.value   },   set(newValue) {     [firstName.value, lastName.value] = newValue.split(' ')   } }) </script> 

Vue Class API

<script lang="ts"> import { Options, Vue } from "vue-class-component";  @Options({}) export default class ParentComponent extends Vue {   firstName = "John";   lastName = "Doe";    get name() {     return this.firstName + " " + this.lastName;   }    set name(value) {     const splitted = value.split(" ");     this.firstName = splitted[0];     this.lastName = splitted[1] || "";   } } </script> 

Vue Class API + vue-property-decorator

Синтаксис аналогичен Vue Class API

Определение и вызов emit

Vue Options API

<template>   <button @click="$emit('enlarge-text', 0.1)">     Enlarge text   </button> </template>  <script> export default {   methods: {     myFunction(data) {       this.$emit('emitName', data)     }   } } </script> 

Vue Composition API

<script> export default {   emits: ['submit']   setup() {     const myFunction = (data) => {       $emit('submit', data)     }   } } </script> 
<script> export default {   emits: {     submit(payload) {       // ...     }   } } </script> 
<script> export default {   emits: {     // No validation     click: null,      // Validate submit event     submit: ({ email, password }) => {       if (email && password) {         return true       } else {         console.warn('Invalid submit event payload!')         return false       }     }   },   methods: {     submitForm(email, password) {       this.$emit('submit', { email, password })     }   } } </script> 
<template>   <input       :value="modelValue"       @input="$emit('update:modelValue', $event.target.value)"   /> </template>  <script> export default {   props: ['modelValue'],   emits: ['update:modelValue'] } </script> 
<script> export default {   emits: ['inFocus', 'submit'],   setup(props, ctx) {     ctx.emit('submit')   } } </script> 

Vue Composition API

<script setup> const emit = defineEmits(['inFocus', 'submit'])  function buttonClick() {   emit('submit') } </script> 
<script setup> const emit = defineEmits({   submit(payload) {     // return `true` or `false` to indicate     // validation pass / fail   } }) </script> 
<template>   <input       :value="modelValue"       @input="$emit('update:modelValue', $event.target.value)"   /> </template>  <script setup> defineProps(['modelValue']) defineEmits(['update:modelValue']) </script> 
<template>   <input v-model="value" /> </template>  <script setup> import { computed } from 'vue'  const props = defineProps(['modelValue']) const emit = defineEmits(['update:modelValue'])  const value = computed({   get() {     return props.modelValue   },   set(value) {     emit('update:modelValue', value)   } }) </script> 

Vue Class API

<script lang="ts"> import { Options, Vue } from "vue-class-component";  @Options({   emits: ["submit"], }) export default class ChildComponent extends Vue {   onClick() {     this.$emit("submit");   } } </script> 

Vue Class API + vue-property-decorator

<script lang="ts"> import { Vue, Component, Emit } from 'vue-property-decorator'  @Component export default class YourComponent extends Vue {   count = 0    @Emit()   addToCount(n: number) {     this.count += n   }    @Emit('reset')   resetCount() {     this.count = 0   }    @Emit()   returnValue() {     return 10   }    @Emit()   onInputChange(e) {     return e.target.value   }    @Emit()   promise() {     return new Promise((resolve) => {       setTimeout(() => {         resolve(20)       }, 0)     })   } } </script> 

Код выше эквивалентен:

<script> export default {   data() {     return {       count: 0,     }   },   methods: {     addToCount(n) {       this.count += n       this.$emit('add-to-count', n)     },     resetCount() {       this.count = 0       this.$emit('reset')     },     returnValue() {       this.$emit('return-value', 10)     },     onInputChange(e) {       this.$emit('on-input-change', e.target.value, e)     },     promise() {       const promise = new Promise((resolve) => {         setTimeout(() => {           resolve(20)         }, 0)       })        promise.then((value) => {         this.$emit('promise', value)       })     },   }, } </script> 

Определение lifecycle hooks

Vue Options API

<script> export default {   mounted() {     // ...   } } </script> 

Vue Composition API

<script> import { onMounted } from 'vue'  export default {   onMounted() {     // ...   } } </script> 

Vue Composition API

<script setup> import { onMounted } from 'vue'  const el = ref()  onMounted(() => {   el.value // <div> }) </script> 

Vue Class API

<script lang="ts"> import { Options, Vue } from "vue-class-component";  @Options({}) export default class ChildComponent extends Vue {   mounted() {     console.log("mounted");   } } </script> 

Vue Class API + vue-property-decorator

Синтаксис аналогичен Vue Class API

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

Vue Options API

<script> export default {   data() {     return {       someObject: {         one: "one",         two: "two",       },     };   },   methods: {     funcOne() {       console.log(this.someObject.one);     },   }, } </script> 

Vue Composition API

<script> export default {   import { ref } from 'vue'      setup() {     const someObject = ref({ one: "one", two: "two" })      const funcOne = () => {       console.log(someObject.value.one)     }          return {       someObject,       funcOne     }   } } </script> 

Vue Composition API

<script setup> import { ref } from 'vue'  const someObject = ref({ one: "one", two: "two" })  const funcOne = () => {   console.log(someObject.value.one) } </script> 

Vue Class API

<script lang="ts"> import { Options, Vue } from "vue-class-component"; import ChildComponent from "@/components/ChildComponent.vue";  @Options({   components: {     ChildComponent,   }, }) export default class ParentComponent extends Vue {   someObject = { one: "one", two: "two" };    funcOne() {     console.log(this.someObject.one);   } } </script> 

Vue Class API + vue-property-decorator

Синтаксис аналогичен Vue Class API

Определение props свойства

Vue Options API

Определение props без валидации

<script> export default {   props: ['foo'], } </script> 

Валидация props:

<script> export default {   props: {     propA: Number,     propB: [String, Number],     propC: {       type: String,       required: true     },     propD: {       type: Number,       default: 100     },     propE: {       type: Object,       default(rawProps) {         return { message: 'hello' }       }     },     propF: {       validator(value) {         return ['success', 'warning', 'danger'].includes(value)       }     },     propG: {       type: Function,       default() {         return 'Default function'       }     }   } } </script> 

Vue Composition API

Определение props без валидации

Для того чтобы объявить props с поддержкой вывода полного типа, мы можем использовать
defineProps, которые автоматически доступен внутри <script setup>

<script setup> const props = defineProps(['foo']) </script> 

Валидация props:

<script setup> const props = defineProps({   propA: Number,   propB: [String, Number],   propC: {     type: String,     required: true   },   propD: {     type: Number,     default: 100   },   propE: {     type: Object,     default(rawProps) {       return { message: 'hello' }     }   },   propF: {     validator(value) {       return ['success', 'warning', 'danger'].includes(value)     }   },   propG: {     type: Function,     default() {       return 'Default function'     }   } }) </script> 

Vue Composition API

Определение props без валидации

<script> export default {   props: ['foo'] } </script> 

Валидация props: аналогично синтаксису Vue Options API.

Vue Class API

Определение props без валидации

<script lang="ts"> import { Options, Vue } from "vue-class-component";  @Options({   props: {     msg: String,   }, }) export default class ChildComponent extends Vue {   msg!: string; } </script> 

Валидация props: аналогично другим синтаксисам

Vue Class API + vue-property-decorator

<script lang="ts"> import { Vue, Component, Prop } from 'vue-property-decorator'  @Component export default class YourComponent extends Vue {   @Prop(Number) readonly propA: number | undefined   @Prop({ default: 'default value' }) readonly propB!: string   @Prop([String, Boolean]) readonly propC: string | boolean | undefined } </script> 

Код выше эквивалентен:

<script> export default {   props: {     propA: {       type: Number,     },     propB: {       default: 'default value',     },     propC: {       type: [String, Boolean],     },   }, } </script> 

Определение watch свойства

Vue Options API

Базовое использование

<script> export default {   watch: {     someObject(newValue, oldValue) {       // ...     }   } } </script> 

Глубокое слежение

<script> export default {   watch: {     someObject: {       handler(newValue, oldValue) {         // ...       },       deep: true     }   } } </script> 

Обязательный обратный вызов

<script> export default {   watch: {     question: {       handler(newValue, oldValue) {         // ...       },       immediate: true     }   } } </script> 

Vue Composition API

Базовое использование

<script setup> import { watch } from 'vue'  watch(someObject, async (newValue, oldValue) => {   // ... }) </script> 

Глубокое слежение

Когда вы вызываете watch() непосредственно реактивный объект, он неявно создает глубокий наблюдатель — обратный вызов
будет запускаться для всех вложенных мутаций:

Это следует отличать от геттера, который возвращает реактивный объект — в последнем случае обратный вызов
сработает только в том случае, если геттер вернет другой объект:

<script setup> import { watch } from 'vue'  watch(     () => state.someObject,     () => {       // ...     } ) </script> 

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

<script setup> import { watch } from 'vue'  watch(     () => state.someObject,     (newValue, oldValue) => {       // ...     },     { deep: true } ) </script> 

Vue 2.7 / 3. Composition API

<script setup> export default {   watch: {     someObject(newValue, oldValue) {       // ...     },   },   setup() {     // ...   } }; </script> 

Vue Class API

<script lang="ts"> import { Options, Vue } from "vue-class-component"; import ChildComponent from "@/components/ChildComponent.vue";  @Options({   watch: {     someString(newValue) {       console.log(newValue);     },   }, }) export default class ParentComponent extends Vue {   someString = "old";    mounted() {     this.someString = "new";   } } </script> 

Vue Class API + vue-property-decorator

<script> import { Vue, Component, Watch } from 'vue-property-decorator'  @Component export default class YourComponent extends Vue {   @Watch('child')   onChildChanged(val: string, oldVal: string) {}    @Watch('person', { immediate: true, deep: true })   onPersonChanged1(val: Person, oldVal: Person) {}    @Watch('person')   onPersonChanged2(val: Person, oldVal: Person) {} } </script> 

Код выше эквивалентен:

<script> export default {   watch: {     child: [       {         handler: 'onChildChanged',         immediate: false,         deep: false,       },     ],     person: [       {         handler: 'onPersonChanged1',         immediate: true,         deep: true,       },       {         handler: 'onPersonChanged2',         immediate: false,         deep: false,       },     ],   },   methods: {     onChildChanged(val, oldVal) {},     onPersonChanged1(val, oldVal) {},     onPersonChanged2(val, oldVal) {},   }, } </script> 

Работа с Vuex

Vue Options API

<script> import { mapGetters, mapState } from "vuex";  export default {   computed: {     ...mapGetters(["userId"]),     ...mapState({       pageTitle: (state) => state.pageTitle.toUpperCase(),     })   },   methods: {     getUserId() {       return this.$store.state.userId     },     deleteUser(id) {       this.$store.commit("deleteUser", id);     },   } } </script> 

Vue Composition API

<script> import { mapGetters, mapState, useStore } from "vuex";  export default {   computed: {     ...mapGetters(["userId"]),     ...mapState({       pageTitle: (state) => state.pageTitle.toUpperCase(),     })   },   setup() {     const store = useStore()          const deleteUser = (id) => {       store.commit("deleteUser", id);     }      const getUserId = () => {       return store.state.userId     }      const deleteUser = (id) => {       store.commit('deleteUser', id)     }   } } </script> 

Vue Composition API

<script> const store = useStore()  const deleteUser = (id) => {   store.commit("deleteUser", id); }  const getUserId = () => {   return store.state.userId }  const deleteUser = (id) => {   store.commit('deleteUser', id) }  const userId = computed(() => {   return store.getters.userId })  const count = computed(() => store.getters.count) </script> 

Vue Class API

Vuex не предоставляет типы для свойства this.$store из коробки.
При использовании с TypeScript вы должны объявить расширение собственного модуля.
Подробности.

Vue Class API + vue-property-decorator

Аналогично Vue Class API.


ссылка на оригинал статьи https://habr.com/ru/post/690508/