рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЯрд╛рдЗрдкрд┐рдВрдЧ рд╡рд╛рдЙ

рд▓реЛрдЧреЛ


inb4: рдпрд╣ Vue рдФрд░ рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЯреНрдпреВрдЯреЛрд░рд┐рдпрд▓ рдХреЗ рд╕рд╛рде рдПрдХ рдирдИ рдкрд░рд┐рдпреЛрдЬрдирд╛ "рд╕реЗрдЯрд┐рдВрдЧ" рдирд╣реАрдВ рд╣реИред рдЖрдЗрдП рдХреБрдЫ рдЧрд╣рд░реЗ рдЧреЛрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдФрд░ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рд╡рд┐рд╖рдпреЛрдВ рдореЗрдВ!


typescript рдХрдорд╛рд▓ рд╣реИред Vue рдХрдорд╛рд▓ рд╣реИред рдЗрд╕рдореЗрдВ рдХреЛрдИ рд╢рдХ рдирд╣реАрдВ, рдХрд┐ рдмрд╣реБрдд рд╕рд╛рд░реЗ рд▓реЛрдЧ рдЙрдиреНрд╣реЗрдВ рдПрдХ рд╕рд╛рде рдмрд╛рдВрдзрдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рддреЗ рд╣реИрдВ ред рд▓реЗрдХрд┐рди, рдЕрд▓рдЧ-рдЕрд▓рдЧ рдХрд╛рд░рдгреЛрдВ рдХреЗ рдХрд╛рд░рдг, рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЕрдкрдиреЗ Vue рдРрдк рдХреЛ рдЯрд╛рдЗрдк рдХрд░рдирд╛ рдореБрд╢реНрдХрд┐рд▓ рд╣реИред рдЖрдЗрдП рдЬрд╛рдиреЗрдВ рдХрд┐ рдХреНрдпрд╛ рд╕рдорд╕реНрдпрд╛рдПрдВ рд╣реИрдВ рдФрд░ рдЙрдиреНрд╣реЗрдВ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреНрдпрд╛ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ (рдпрд╛ рдХрдо рд╕реЗ рдХрдо рдкреНрд░рднрд╛рд╡ рдХрдо рдХрд░реЗрдВ)ред


TLDR


рд╣рдорд╛рд░реЗ рдкрд╛рд╕ Nuxt , Vue , Vuex , рдФрд░ jest рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЯрд╛рдЗрдк рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдмрд╕ рдЗрд╕реЗ рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВ рдФрд░ рд╕рдм рдХреБрдЫ рдЖрдкрдХреЗ рд▓рд┐рдП рдХрд╡рд░ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдЕрдзрд┐рдХ рдЬрд╛рдирдиреЗ рдХреЗ рд▓рд┐рдП рдбреЙрдХреНрд╕ рдкрд░ рдЬрд╛рдПрдВред


рдФрд░ рдЬреИрд╕рд╛ рдХрд┐ рдореИрдВрдиреЗ рдХрд╣рд╛ рдХрд┐ рдореИрдВ рддреАрди рдХрд╛рд░рдгреЛрдВ рд╕реЗ рдореВрд▓ рд╕реЗрдЯрдЕрдк рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЖрдкрдХрд╛ рдорд╛рд░реНрдЧрджрд░реНрд╢рди рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдирд╣реАрдВ рд╣реВрдВ:


  1. рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╣реБрдд рд╕рд╛рд░реЗ рдореМрдЬреВрджрд╛ рдЯреНрдпреВрдЯреЛрд░рд┐рдпрд▓ рд╣реИрдВ
  2. рдПрдХ рдХреНрд▓рд┐рдХ рдХреЗ рд╕рд╛рде рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд рд╕рд╛рд░реЗ рдЙрдкрдХрд░рдг рд╣реИрдВ рдЬреИрд╕реЗ Nuxt рдкреНрд▓рдЧрдЗрди рдХреЗ рд╕рд╛рде Nuxt рдФрд░ vue-cli
  3. рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА wemake-vue-template рдЬрд╣рд╛рдВ рд╣рд░ рд╕реЗрдЯрдЕрдк рдЬреЛ рдореИрдВ рдмрд╛рдд рдХрд░рдиреЗ рдЬрд╛ рд░рд╣рд╛ рд╣реВрдВ рд╡рд╣ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдХрд╡рд░ рд╣реИ

рдШрдЯрдХ рдЯрд╛рдЗрдкрд┐рдВрдЧ


рдЬрдм рдЖрдк Vue рдФрд░ typescript рд╕рд╛рде рдХрд╛рдо рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдЖрдкрдХреЗ рджреНрд╡рд╛рд░рд╛ рдЕрдкрдиреЗ рдХрдХреНрд╖рд╛ рдХреЗ рдШрдЯрдХреЛрдВ рдХреЛ рдЯрд╛рдЗрдк рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж рдкрд╣рд▓реА рдЯреВрдЯреА рд╣реБрдИ рдЕрдкреЗрдХреНрд╖рд╛ рдпрд╣ рд╣реИ рдХрд┐ <template> рдФрд░ <style> рдЯреИрдЧ рдЕрднреА рднреА рдЯрд╛рдЗрдк рдирд╣реАрдВ рдХрд┐рдП рдЧрдП рд╣реИрдВред рдореИрдВ рдЖрдкрдХреЛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рджрд┐рдЦрд╛рддрд╛ рд╣реВрдВ:


 <template> <h1 :class="$style.headr"> Hello, {{ usr }}! </h1> </template> <script lang="ts"> import Vue from 'vue' import Component from 'vue-class-component' import { Prop } from 'vue-property-decorator' @Component({}) export default class HelloComponent extends Vue { @Prop() user!: string } </script> <style module> .header { /* ... */ } </style> 

рдореИрдВрдиреЗ рдпрд╣рд╛рдБ рджреЛ рдЯрд╛рдЗрдкреЛ рдмрдирд╛рдП рд╣реИрдВ: {{ usr }} рдмрдЬрд╛рдп {{ user }} рдФрд░ $style.headr рдмрдЬрд╛рдп $style.header ред рдХреНрдпрд╛ typescript рдореБрдЭреЗ рдЗрди рддреНрд░реБрдЯрд┐рдпреЛрдВ рд╕реЗ рдмрдЪрд╛рдПрдЧрд╛? рдирд╣реАрдВ, рдпрд╣ рдирд╣реАрдВ рд╣реЛрдЧрд╛ред


рдЗрд╕реЗ рдареАрдХ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреНрдпрд╛ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ? рд╡реИрд╕реЗ, рдХрдИ рд╣реИрдХ рд╣реИрдВред


рдЯреЗрдореНрдкрд▓реЗрдЯ рдЯрд╛рдЗрдк рдХрд░рдирд╛


рдЕрдкрдиреЗ рдЯреЗрдореНрдкреНрд▓реЗрдЯ рдЯрд╛рдЗрдк рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП vetur.experimental.templateInterpolationService рд╡рд┐рдХрд▓реНрдк рдХреЗ рд╕рд╛рде vetur.experimental.templateInterpolationService рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рд╣рд╛рдВ, рдпрд╣ рдХреЗрд╡рд▓ рдПрдХ рд╕рдВрдкрд╛рджрдХ-рдЖрдзрд╛рд░рд┐рдд рдЬрд╛рдВрдЪ рд╣реИ рдФрд░ рдЗрд╕реЗ CI рдХреЗ рдЕрдВрджрд░ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рд▓реЗрдХрд┐рди, Vetur рдЯреАрдо рдЗрд╕реЗ рдЕрдиреБрдорддрд┐ рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рд╕реАрдПрд▓рдЖрдИ рдкреНрд░рджрд╛рди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрдбрд╝реА рдореЗрд╣рдирдд рдХрд░ рд░рд╣реА рд╣реИред рдЖрдкрдХреА рд░реБрдЪрд┐ рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ рдореВрд▓ рдореБрджреНрджреЗ рдХреЛ рдЯреНрд░реИрдХ рдХрд░реЗрдВ ред


vetur


рджреВрд╕рд░рд╛ рд╡рд┐рдХрд▓реНрдк рджреЛ рд▓реЗрдЦрди рд╕реНрдиреИрдкрд╢реЙрдЯ рдкрд░реАрдХреНрд╖рдг рд╣реИ рдЬрд┐рд╕рдореЗрдВ jest ред рдпрд╣ рдмрд╣реБрдд рд╕рд╛рд░реЗ рдЯреЗрдореНрдкрд▓реЗрдЯ-рдЖрдзрд╛рд░рд┐рдд рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рдкрдХрдбрд╝ рд▓реЗрдЧрд╛ред рдФрд░ рдпрд╣ рд░рдЦрд░рдЦрд╛рд╡ рдореЗрдВ рдХрд╛рдлреА рд╕рд╕реНрддрд╛ рд╣реИред


рддреЛ, рдЗрди рджреЛ рдЙрдкрдХрд░рдгреЛрдВ рдХрд╛ рд╕рдВрдпреЛрдЬрди рдЖрдкрдХреЛ рддреЗрдЬреА рд╕реЗ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЗ рд╕рд╛рде рдПрдХ рдЕрдЪреНрдЫрд╛ рдбреЗрд╡рд▓рдкрд░ рдЕрдиреБрднрд╡ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ рдФрд░ рд╕реАрдЖрдИ рдХреЗ рдЕрдВрджрд░ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рдкрдХрдбрд╝рдиреЗ рдХрд╛ рдПрдХ рд╡рд┐рд╢реНрд╡рд╕рдиреАрдп рддрд░реАрдХрд╛ рд╣реИред


рдЯрд╛рдЗрдкрд┐рдВрдЧ рд╕реНрдЯрд╛рдЗрд▓


рдЯрд╛рдЗрдкрд┐рдВрдЧ css-module рдПрд╕ рднреА рдХрдИ рдмрд╛рд╣рд░реА рдЙрдкрдХрд░рдгреЛрдВ рджреНрд╡рд╛рд░рд╛ рдХрд╡рд░ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ:



рдЗрди рдЙрдкрдХрд░рдгреЛрдВ рдХрд╛ рдореБрдЦреНрдп рд╡рд┐рдЪрд╛рд░ css-module рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рд╣реИ рдФрд░ рдлрд┐рд░ рдЙрдирдореЗрдВ рд╕реЗ .d.ts рдШреЛрд╖рдгрд╛ рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреЛ рдмрдирд╛рдирд╛ рд╣реИред рдлрд┐рд░ рдЖрдкрдХреА рд╢реИрд▓рд┐рдпреЛрдВ рдХреЛ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЯрд╛рдЗрдк рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдпрд╣ рдЕрднреА рднреА Nuxt рдпрд╛ Vue рд▓рд┐рдП рд▓рд╛рдЧреВ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдЖрдк рдкреНрд░рдЧрддрд┐ рдХреЗ рд▓рд┐рдП рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ Nuxt рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред


рд╣рд╛рд▓рд╛рдБрдХрд┐, рдореИрдВ рд╡реНрдпрдХреНрддрд┐рдЧрдд рд░реВрдк рд╕реЗ рдЕрдкрдиреА рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдореЗрдВ рдЗрдирдореЗрдВ рд╕реЗ рдХрд┐рд╕реА рднреА рдЙрдкрдХрд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реВрдВред рд╡реЗ рдмрдбрд╝реЗ рдХреЛрдб рдмреЗрд╕ рдФрд░ рдмрд╣реБрдд рд╕рд╛рд░реА рд╢реИрд▓рд┐рдпреЛрдВ рд╡рд╛рд▓реА рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧреА рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдореИрдВ рд╕рд┐рд░реНрдл рд╕реНрдиреИрдкрд╢реЙрдЯ рдХреЗ рд╕рд╛рде рдареАрдХ рд╣реВрдВред


рджреГрд╢реНрдп рдкреНрд░рддрд┐рдЧрдорди рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рд╕рд╛рде рд╕реНрдЯрд╛рдЗрд▓рдЧрд╛рдЗрдбреНрд╕ рднреА рдмрд╣реБрдд рдорджрдж рдХрд░рддреЗ рд╣реИрдВред @storybook/addon-storyshots рдЗрд╕ рддрдХрдиреАрдХ рдХрд╛ рдПрдХ рдЕрдЪреНрдЫрд╛ рдЙрджрд╛рд╣рд░рдг рд╣реИред


Vuex


рдЕрдЧрд▓реА рдмрдбрд╝реА рдЪреАрдЬ Vuex ред рдЯрд╛рдЗрдкрд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдЗрд╕рдореЗрдВ рдХреБрдЫ рдмрд┐рд▓реНрдЯ-рдЗрди-рдбрд┐рдЬрд╝рд╛рдЗрди рдЬрдЯрд┐рд▓рддрд╛ рд╣реИ:


 const result: Promise<number> = this.$store.dispatch('action_name', { payload: 1 }) 

рд╕рдорд╕реНрдпрд╛ рдпрд╣ рд╣реИ рдХрд┐ 'action_name' рдореМрдЬреВрдж рдирд╣реАрдВ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рдЕрдиреНрдп рддрд░реНрдХ рд▓реЗ рд╕рдХрддрд╛ рд╣реИ рдпрд╛ рдПрдХ рдЕрд▓рдЧ рдкреНрд░рдХрд╛рд░ рд▓реМрдЯрд╛ рд╕рдХрддрд╛ рд╣реИред рдпрд╣ рд╡рд╣ рдЪреАрдЬ рдирд╣реАрдВ рд╣реИ рдЬрд┐рд╕рдХреА рдЖрдк рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЯрд╛рдЗрдк рдХрд┐рдП рдЧрдП рдРрдк рдХреЗ рд▓рд┐рдП рдЙрдореНрдореАрдж рдХрд░рддреЗ рд╣реИрдВред


рдореМрдЬреВрджрд╛ рд╕рдорд╛рдзрд╛рди рдХреНрдпрд╛ рд╣реИрдВ?


vuex рд╢реНрд░реЗрдгреА


vuex-class рдбреЗрдХреЛрд░реЗрдЯрд░ рдХрд╛ рдПрдХ рд╕реЗрдЯ рд╣реИ рдЬреЛ рдЖрдкрдХреЗ рд╡рд░реНрдЧ-рдЖрдзрд╛рд░рд┐рдд рдШрдЯрдХреЛрдВ рд╕реЗ Vuex рдЗрдВрдЯрд░реНрдирд▓ рддрдХ рдЖрд╕рд╛рди рдкрд╣реБрдБрдЪ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред


рд▓реЗрдХрд┐рди, рдпрд╣ рд╕реБрд░рдХреНрд╖рд┐рдд рдирд╣реАрдВ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рд░рд╛рдЬреНрдп, рдЧреЗрдЯрд░реНрд╕, рдореНрдпреВрдЯреЗрд╢рди рдФрд░ рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рдкреНрд░рдХрд╛рд░реЛрдВ рдореЗрдВ рд╣рд╕реНрддрдХреНрд╖реЗрдк рдирд╣реАрдВ рдХрд░ рд╕рдХрддрд╛ рд╣реИред


vuex рд╢реНрд░реЗрдгреА


рдмреЗрд╢рдХ, рдЖрдк рдореИрдиреНрдпреБрдЕрд▓ рдкреНрд░рдХрд╛рд░ рдХреА рд╕рдВрдкрддреНрддрд┐рдпреЛрдВ рдХреЛ рдПрдиреЛрдЯреЗрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред


vuex-class рдПрдиреЛрдЯреЗрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛


рд▓реЗрдХрд┐рди рдЬрдм рдЖрдк рдЕрдкрдиреЗ рд░рд╛рдЬреНрдп рдХреЗ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдкреНрд░рдХрд╛рд░, рдЧреЗрдЯрд░реНрд╕, рдореНрдпреВрдЯреЗрд╢рди рдпрд╛ рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЛ рдмрджрд▓реЗрдВрдЧреЗ, рддреЛ рдЖрдк рдХреНрдпрд╛ рдХрд░рдиреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВ? рдЖрдкрдХреЗ рдкрд╛рд╕ рдПрдХ рдЫрд┐рдкреЗ рд╣реБрдП рдкреНрд░рдХрд╛рд░ рдХрд╛ рдмреЗрдореЗрд▓ рд╣реЛрдЧрд╛ред


vuex-рд╕рд░рд▓


vuex-simple рд╣рдорд╛рд░реА рдорджрдж рдХрд░рддрд╛ рд╣реИред рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЖрдкрдХреЗ Vuex рдХреЛрдб рдХреЛ рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЕрд▓рдЧ рддрд░реАрдХрд╛ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ рдФрд░ рдпрд╣реА рдЗрд╕реЗ рд╕реБрд░рдХреНрд╖рд┐рдд рдмрдирд╛рддрд╛ рд╣реИред рдЖрдЗрдП рдирдЬрд░ рдбрд╛рд▓рддреЗ рд╣реИрдВ:


 import { Action, Mutation, State, Getter } from 'vuex-simple' class MyStore { // State @State() public comments: CommentType[] = [] // Getters @Getter() public get hasComments (): boolean { return Boolean(this.comments && this.comments.length > 0) } // Mutations @Mutation() public setComments (payload: CommentType[]): void { this.comments = updatedComments } // Actions @Action() public async fetchComments (): Promise<CommentType[]> { // Calling some API: const commentsList = await api.fetchComments() this.setComments(commentsList) // typed mutation return commentsList } } 

рдмрд╛рдж рдореЗрдВ рдЗрд╕ рдЯрд╛рдЗрдк рдХрд┐рдП рдЧрдП рдореЙрдбреНрдпреВрд▓ рдХреЛ рдЖрдкрдХреЗ Vuex рдЕрдВрджрд░ рдкрдВрдЬреАрдХреГрдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдЬреИрд╕реЗ:


 import Vue from 'vue' import Vuex from 'vuex' import { createVuexStore } from 'vuex-simple' import { MyStore } from './store' Vue.use(Vuex) // Creates our typed module instance: const instance = new MyStore() // Returns valid Vuex.Store instance: export default createVuexStore(instance) 

рдЕрдм рд╣рдорд╛рд░реЗ рдкрд╛рд╕ 100% рджреЗрд╢реА Vuex.Store рдЙрджрд╛рд╣рд░рдг рдФрд░ рдЗрд╕рдХреЗ рд╕рд╛рде рдмрдВрдбрд▓ рдХреА рдЧрдИ рд╕рднреА рдкреНрд░рдХрд╛рд░ рдХреА рдЬрд╛рдирдХрд╛рд░реАред рдШрдЯрдХ рдореЗрдВ рдЗрд╕ рдЯрд╛рдЗрдк рдХрд┐рдП рдЧрдП рд╕реНрдЯреЛрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣рдо рдХреЛрдб рдХреА рд╕рд┐рд░реНрдл рдПрдХ рдкрдВрдХреНрддрд┐ рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВ:


 import Vue from 'vue' import Component from 'nuxt-class-component' import { useStore } from 'vuex-simple' import MyStore from './store' @Component({}) export default class MyComponent extends Vue { // That's all we need! typedStore: MyStore = useStore(this.$store) // Demo: will be typed as `Comment[]`: comments = typedStore.comments } 

рдЕрдм рд╣рдордиреЗ Vuex рдЯрд╛рдЗрдк рдХрд┐рдпрд╛ рд╣реИ Vuex рд╣рдорд╛рд░реЗ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдХреЗ рдЕрдВрджрд░ рд╕реБрд░рдХреНрд╖рд┐рдд рд░реВрдк рд╕реЗ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред
рдЬрдм рд╣рдо рдЕрдкрдиреЗ рд╕реНрдЯреЛрд░ рдХреА рдкрд░рд┐рднрд╛рд╖рд╛ рдХреЗ рдЕрдВрджрд░ рдХреБрдЫ рдмрджрд▓рддреЗ рд╣реИрдВ рддреЛ рдпрд╣ рдЙрди рдШрдЯрдХреЛрдВ рдХреЗ рд▓рд┐рдП рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рдкрд░рд┐рд▓рдХреНрд╖рд┐рдд рд╣реЛрддрд╛ рд╣реИ рдЬреЛ рдЗрд╕ рд╕реНрдЯреЛрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред рдпрджрд┐ рдХреБрдЫ рд╡рд┐рдлрд▓ рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ - рд╣рдо рдЗрд╕реЗ рдЬрд▓реНрдж рд╕реЗ рдЬрд▓реНрдж рдЬрд╛рдирддреЗ рд╣реИрдВред


рдЕрд▓рдЧ-рдЕрд▓рдЧ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рднреА рд╣реИрдВ рдЬреЛ рд╕рдорд╛рди рд╣реИрдВ рд▓реЗрдХрд┐рди рдЙрдирдХреЗ рдкрд╛рд╕ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдПрдкреАрдЖрдИ рд╣реИрдВред рдЪреБрдиреЗрдВ рдХрд┐ рдЖрдкрдХреЛ рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ рдХреНрдпрд╛ рд╕реВрдЯ рдХрд░рддрд╛ рд╣реИред


рдПрдкреАрдЖрдИ рдХреЙрд▓


рдЬрдм рд╣рдорд╛рд░реЗ рдкрд╛рд╕ Vuex рд╕рд╣реА рдврдВрдЧ рд╕реЗ рд╕реЗрдЯрдЕрдк рд╣реЛрддрд╛ рд╣реИ, рддреЛ рд╣рдореЗрдВ рдЗрд╕реЗ рдбреЗрдЯрд╛ рд╕реЗ рднрд░рдирд╛ рд╣реЛрдЧрд╛ред
рдЖрдЗрдП рдПрдХ рдмрд╛рд░ рдлрд┐рд░ рд╣рдорд╛рд░реА рдХреНрд░рд┐рдпрд╛ рдкрд░рд┐рднрд╛рд╖рд╛ рдкрд░ рдирдЬрд╝рд░ рдбрд╛рд▓реЗрдВ:


 @Action() public async fetchComments (): Promise<CommentType[]> { // Calling some API: const commentsList = await api.fetchComments() // ... return commentsList } 

рд╣рдо рдпрд╣ рдХреИрд╕реЗ рдЬрд╛рди рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ CommentType рд╕реВрдЪреА CommentType рдФрд░ рдПрдХ рднреА number рдпрд╛ AuthorType рдЗрдВрд╕реНрдЯреЗрдВрд╕ рдХрд╛ рдПрдХ рдЧреБрдЪреНрдЫрд╛ рдирд╣реАрдВ рд╣реЛрдЧрд╛?


рд╣рдо рд╕рд░реНрд╡рд░ рдХреЛ рдирд┐рдпрдВрддреНрд░рд┐рдд рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗред рдФрд░ рд╕рд░реНрд╡рд░ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЕрдиреБрдмрдВрдз рдХреЛ рддреЛрдбрд╝ рд╕рдХрддрд╛ рд╣реИред рдпрд╛ рд╣рдо рдмрд╕ рдЧрд▓рдд api рдЙрджрд╛рд╣рд░рдг рдХреЛ рдкрд╛рд░рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, URL рдореЗрдВ рдЯрд╛рдЗрдкреЛ рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВ рдпрд╛ рдЬреЛ рднреА рд╣реЛред


рд╣рдо рдХреИрд╕реЗ рд╕реБрд░рдХреНрд╖рд┐рдд рд░рд╣ рд╕рдХрддреЗ рд╣реИрдВ? рд╣рдо рд░рдирдЯрд╛рдЗрдо рдЯрд╛рдЗрдкрд┐рдВрдЧ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ! рдореБрдЭреЗ рдЖрдк рдХреЗ рд▓рд┐рдП io-ts рдкрд░рд┐рдЪрдп:


 import * as ts from 'io-ts' export const Comment = ts.type({ 'id': ts.number, 'body': ts.string, 'email': ts.string, }) // Static TypeScript type, that can be used as a regular `type`: export type CommentType = ts.TypeOf<typeof Comment> 

рд╣рдо рдпрд╣рд╛рдБ рдХреНрдпрд╛ рдХрд░рддреЗ рд╣реИрдВ?


  1. рдЬрдм рд╣рдо рд╕рд░реНрд╡рд░ рд╕реЗ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рд╣рдо рдЙрди рдХреНрд╖реЗрддреНрд░реЛрдВ рдХреЗ рд╕рд╛рде ts.type рдХреА рдПрдХ рдЖрд╡реГрддреНрддрд┐ рдХреЛ ts.type рдХрд░рддреЗ рд╣реИрдВ, рдЬрд┐рдиреНрд╣реЗрдВ рд╣рдореЗрдВ рд░рдирдЯрд╛рдЗрдо рдореЗрдВ ts.type рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ
  2. рд╣рдо рдХрд┐рд╕реА рднреА рдЕрддрд┐рд░рд┐рдХреНрдд рдмреЙрдпрд▓рд░рдкреНрд▓реЗрдЯ рдХреЗ рдмрд┐рдирд╛ рдПрдиреЛрдЯреЗрд╢рди рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рдПрдХ рд╕реНрдерд┐рд░ рдкреНрд░рдХрд╛рд░ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддреЗ рд╣реИрдВ

рдФрд░ рдмрд╛рдж рдореЗрдВ рд╣рдо рдЗрд╕реЗ рдЕрдкрдиреЗ api рдХреЙрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:


 import * as ts from 'io-ts' import * as tPromise from 'io-ts-promise' public async fetchComments (): Promise<CommentType[]> { const response = await axios.get('comments') return tPromise.decode(ts.array(Comment), response.data) } 

io-ts-promise рдХреА рдорджрдж рд╕реЗ, рд╣рдо рдПрдХ рд╡рд┐рдлрд▓ рд╕реНрдерд┐рддрд┐ рдореЗрдВ рдПрдХ Promise рд╡рд╛рдкрд╕ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдпрджрд┐ рд╕рд░реНрд╡рд░ рд╕реЗ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдПрдХ ts.array(Comment) рдкреНрд░рдХрд╛рд░ рд╕реЗ рдореЗрд▓ рдирд╣реАрдВ рдЦрд╛рддреА рд╣реИред рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдПрдХ рд╕рддреНрдпрд╛рдкрди рдХреА рддрд░рд╣ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред


 fetchComments() .then((data) => /* ... */ .catch(/* Happens with both request failure and incorrect response type */) 

рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд╡рд╛рдкрд╕реА рдкреНрд░рдХрд╛рд░ рдПрдиреЛрдЯреЗрд╢рди .decode рд╡рд┐рдзрд┐ рдХреЗ рд╕рд╛рде рд╕рд┐рдВрдХ рдореЗрдВ рд╣реИред рдФрд░ рдЖрдк рд╡рд╣рд╛рдБ рдпрд╛рджреГрдЪреНрдЫрд┐рдХ рдмрдХрд╡рд╛рд╕ рдирд╣реАрдВ рдбрд╛рд▓ рд╕рдХрддреЗ рд╣реИрдВ:


рдХрдм-ts


рд░рдирдЯрд╛рдЗрдо рдФрд░ рд╕реНрдЯреИрдЯрд┐рдХ рдЪреЗрдХ рдХреЗ рд╕рдВрдпреЛрдЬрди рд╕реЗ, рд╣рдо рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдЯрд╛рдЗрдк рдмреЗрдореЗрд▓ рдХреЗ рдХрд╛рд░рдг рд╣рдорд╛рд░реЗ рдЕрдиреБрд░реЛрдз рд╡рд┐рдлрд▓ рдирд╣реАрдВ рд╣реЛрдВрдЧреЗред
рд▓реЗрдХрд┐рди, 100% рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рд╕рдм рдХреБрдЫ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, рдореИрдВ рдЕрдиреБрдмрдВрдз-рдЖрдзрд╛рд░рд┐рдд рдкрд░реАрдХреНрд╖рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рд╕рд┐рдлрд╛рд░рд┐рд╢ рдХрд░реВрдВрдЧрд╛: рдПрдХ рдЙрджрд╛рд╣рд░рдг рдХреЗ рд░реВрдк рдореЗрдВ pact рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓реЗрдВред рдФрд░ Sentry рд╕рд╛рде рдЕрдкрдиреЗ рдРрдк рдХреЛ рдореЙрдирд┐рдЯрд░ рдХрд░реЗрдВред


рд╡рд╛рдЙ рд░рд╛рдЙрдЯрд░


рдЕрдЧрд▓реА рд╕рдорд╕реНрдпрд╛ рдпрд╣ рд╣реИ рдХрд┐ this.$router.push({ name: 'wrong!' }) рдЙрд╕ рддрд░реАрдХреЗ рд╕реЗ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ рдЬреИрд╕рд╛ рд╣рдо рдЪрд╛рд╣рддреЗ рд╣реИрдВред


рдореИрдВ рдХрд╣реВрдВрдЧрд╛ рдХрд┐ рд╕рдВрдХрд▓рдХ рджреНрд╡рд╛рд░рд╛ рдЪреЗрддрд╛рд╡рдиреА рджреА рдЬрд╛рдирд╛ рдЖрджрд░реНрд╢ рд╣реЛрдЧрд╛ рдХрд┐ рд╣рдо рдЧрд▓рдд рджрд┐рд╢рд╛ рдореЗрдВ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВ рдФрд░ рдпрд╣ рдорд╛рд░реНрдЧ рдореМрдЬреВрдж рдирд╣реАрдВ рд╣реИред
рд▓реЗрдХрд┐рди, рдпрд╣ рд╕рдВрднрд╡ рдирд╣реАрдВ рд╣реИред рдФрд░ рдмрд╣реБрдд рдХреБрдЫ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ: рдмрд╣реБрдд рд╕рд╛рд░реЗ рдЧрддрд┐рд╢реАрд▓ рдорд╛рд░реНрдЧ рд╣реИрдВ, рд░реЗрдЧреЗрдХреНрд╕, рдлрд╛рд▓рдмреИрдХ, рдЕрдиреБрдорддрд┐рдпрд╛рдВ, рдЖрджрд┐ рдЬреЛ рдЕрдВрддрддрдГ рдЯреВрдЯ рд╕рдХрддреЗ рд╣реИрдВред рдПрдХрдорд╛рддреНрд░ рд╡рд┐рдХрд▓реНрдк рдпрд╣ рд╣реИ рдХрд┐ рдкреНрд░рддреНрдпреЗрдХ рдЖрдкрдХреЗ рдРрдк рдореЗрдВ this.$router рдХреЙрд▓ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░реЗрдВред


Vue рдкрд░реАрдХреНрд╖рдг-utils


рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмреЛрд▓рддреЗ рд╣реБрдП, рдореЗрд░реЗ рдкрд╛рд╕ @vue/test-utils рдХрд╛ рдЙрд▓реНрд▓реЗрдЦ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреЛрдИ рдмрд╣рд╛рдирд╛ рдирд╣реАрдВ рд╣реИ рдЬрд┐рд╕рдореЗрдВ рдЯрд╛рдЗрдкрд┐рдВрдЧ рдХреЗ рд╕рд╛рде рдХреБрдЫ рд╕рдорд╕реНрдпрд╛рдПрдВ рднреА рд╣реИрдВред


рдЬрдм рд╣рдо typedStore рд╕рдВрдкрддреНрддрд┐ рдХреЗ рд╕рд╛рде рдЕрдкрдиреЗ рдирдП рдЪрдордХрджрд╛рд░ рдШрдЯрдХ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░реЗрдВрдЧреЗ, рддреЛ рд╣рдо рдкрд╛рдПрдВрдЧреЗ рдХрд┐ рд╣рдо рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ typescript рдЕрдиреБрд╕рд╛рд░ рдРрд╕рд╛ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:


Vue рдкрд░реАрдХреНрд╖рдг-utils


рдРрд╕рд╛ рдХреНрдпреЛрдВ рд╣реЛрддрд╛ рд╣реИ? рдРрд╕рд╛ рдЗрд╕рд▓рд┐рдП рд╣реЛрддрд╛ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ mount() рдХреЙрд▓ рдЖрдкрдХреЗ рдШрдЯрдХ рдХреЗ рдкреНрд░рдХрд╛рд░ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХреБрдЫ рднреА рдирд╣реАрдВ рдЬрд╛рдирддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╕рднреА рдШрдЯрдХреЛрдВ рдореЗрдВ рдПрдХ VueConstructor<Vue> рдкреНрд░рдХрд╛рд░ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рд╣реЛрддрд╛ рд╣реИ:


Vue-рдирд┐рд░реНрдорд╛рддрд╛


рдпрд╣реАрдВ рд╕реЗ рд╕рд╛рд░реА рд╕рдорд╕реНрдпрд╛рдПрдВ рдЖрддреА рд╣реИрдВред рдХреНрдпрд╛ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ?
рдЖрдк vuetype рдХрд╛ рдЙрддреНрдкрд╛рджрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП YouComponent.vue.d.ts рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ рдЖрдкрдХреЗ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЛ рдорд╛рдЙрдВрдЯ рдХрд┐рдП рдЧрдП рдШрдЯрдХ рдХреЗ рд╕рдЯреАрдХ рдкреНрд░рдХрд╛рд░ рдХреЛ рдмрддрд╛рдПрдЧрд╛ред


рдЖрдк рдкреНрд░рдЧрддрд┐ рдХреЗ рд▓рд┐рдП рдЗрд╕ рдореБрджреНрджреЗ рдХреЛ рднреА рдЯреНрд░реИрдХ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред


рд▓реЗрдХрд┐рди, рдореБрдЭреЗ рдпрд╣ рд╡рд┐рдЪрд╛рд░ рдкрд╕рдВрдж рдирд╣реАрдВ рд╣реИред рдпреЗ рдкрд░реАрдХреНрд╖рдг рд╣реИрдВ, рд╡реЗ рд╡рд┐рдлрд▓ рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВред рдХреЛрдИ рдмрдбрд╝реА рдмрд╛рдд рдирд╣реАрдВред
рдпрд╣реА рдХрд╛рд░рдг рд╣реИ рдХрд┐ рдореИрдВ (wrapper.vm as any).whatever рд▓рд┐рдП рдЪрд┐рдкрдХреЗ рд░рд╣рддреЗ рд╣реИрдВред (wrapper.vm as any).whatever рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╣реИред рдпрд╣ рдореБрдЭреЗ рдкрд░реАрдХреНрд╖рдг рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╛рдлреА рд╕рдордп рдмрдЪрд╛рддрд╛ рд╣реИред рд▓реЗрдХрд┐рди рдбреЗрд╡рд▓рдкрд░ рдХрд╛ рдЕрдиреБрднрд╡ рдереЛрдбрд╝рд╛ рдЦрд░рд╛рдм рд╣реЛрддрд╛ рд╣реИред


рдЕрдкрдирд╛ рдирд┐рд░реНрдгрдп рдпрд╣рд╛рдБ рд▓реЗрдВ:


  • рд╕рднреА рддрд░рд╣ рд╕реЗ vuetype рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ
  • рдЖрдВрд╢рд┐рдХ рд░реВрдк рд╕реЗ рдЗрд╕реЗ рд╕рдмрд╕реЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдШрдЯрдХреЛрдВ рдХреЗ рд▓рд┐рдП рд╕рдмрд╕реЗ рдмрдбрд╝реА рдорд╛рддреНрд░рд╛ рдореЗрдВ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рд╕рд╛рде рд▓рд╛рдЧреВ рдХрд░реЗрдВ рдФрд░ рдЗрд╕реЗ рдирд┐рдпрдорд┐рдд рд░реВрдк рд╕реЗ рдЕрдкрдбреЗрдЯ рдХрд░реЗрдВ
  • any рдПрдХ рд╡рд╛рдкрд╕реА рдХреЗ рд░реВрдк рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ

рдирд┐рд╖реНрдХрд░реНрд╖


рдкрд┐рдЫрд▓реЗ рдХреБрдЫ рд╡рд░реНрд╖реЛрдВ рдореЗрдВ Vue рдкрд╛рд░рд┐рд╕реНрдерд┐рддрд┐рдХ рддрдВрддреНрд░ рдореЗрдВ typescript рд╕рдорд░реНрдерди рдХрд╛ рдФрд╕рдд рд╕реНрддрд░ рдмрдврд╝рд╛:


  • Nuxt рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ nuxt-ts рдкреЗрд╢ рдХрд┐рдпрд╛ рдФрд░ рдЕрдм рдЬрд╣рд╛рдЬ ts рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рдмрдирд╛рддрд╛ рд╣реИ
  • Vue@3 typescript рд╕рдорд░реНрдерди рдореЗрдВ рд╕реБрдзрд╛рд░ рд╣реЛрдЧрд╛
  • рдЕрдзрд┐рдХ 3-рдкрдХреНрд╖ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдФрд░ рдкреНрд▓рдЧрдЗрдиреНрд╕ рдкреНрд░рдХрд╛рд░ рдХреА рдкрд░рд┐рднрд╛рд╖рд╛рдПрдВ рдкреНрд░рджрд╛рди рдХрд░реЗрдВрдЧреЗ

рд▓реЗрдХрд┐рди, рдлрд┐рд▓рд╣рд╛рд▓ рдпрд╣ рддреИрдпрд╛рд░ рд╣реИред рдпреЗ рд╕рд┐рд░реНрдл рд╕реБрдзрд╛рд░рдиреЗ рдХреА рдмрд╛рддреЗрдВ рд╣реИрдВ! рдЯрд╛рдЗрдк-рд╕реЗрдл Vue рдХреЛрдб рд▓рд┐рдЦрдирд╛ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЖрдкрдХреЗ рдбреЗрд╡рд▓рдкрд░ рдПрдХреНрд╕рдкреАрд░рд┐рдпрдВрд╕ рдХреЛ рдмреЗрд╣рддрд░ рдмрдирд╛рддрд╛ рд╣реИ рдФрд░ рдЖрдкрдХреЛ рдХрдВрдкрд╛рдЗрд▓рд░ рдХреЛ рд╣реИрд╡реА-рд▓рд┐рдлреНрдЯрд┐рдВрдЧ рдЫреЛрдбрд╝рдиреЗ рдХреЗ рджреМрд░рд╛рди рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╕рд╛рдорд╛рди рдкрд░ рдзреНрдпрд╛рди рдХреЗрдВрджреНрд░рд┐рдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред


Vue рдРрдкреНрд╕ рдЯрд╛рдЗрдк рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдкрдХреЗ рдкрд╕рдВрджреАрджрд╛ рд╣реИрдХ рдФрд░ рдЯреВрд▓ рдХреНрдпрд╛ рд╣реИрдВ? рдЖрдЗрдП рдЯрд┐рдкреНрдкрдгреА рдЕрдиреБрднрд╛рдЧ рдореЗрдВ рдЗрд╕рдХреА рдЪрд░реНрдЪрд╛ рдХрд░реЗрдВред

Source: https://habr.com/ru/post/hi458632/


All Articles