Բոլոր դասերը
FrontendՄիջին14 րոպե ընթերցումԱրժե իմանալ

Vue 3 և Nuxt

Ռեակտիվ կոմպոնենտներ Composition API-ով, Pinia store-եր և full-stack Nuxt հավելվածներ SSR-ով։

  • Vue 3
  • Composition API
  • Pinia
  • Nuxt
  • SSR
Քայլ 1/5
CSR՝ սերվերն ուղարկում է համարյա դատարկ HTML «կմախք» և JavaScript-ի մեծ փաթեթ։ Բրաուզերը պտտեցնում է spinner-ը և գործի անցնում։

30 վայրկյանում

Vue-ն JavaScript ֆրեյմվորք է կոմպոնենտներից օգտատիրոջ ինտերֆեյսներ կառուցելու համար, որը հայտնի է մեղմ ուսուցման կորով և ճշգրիտ ռեակտիվության համակարգով։ Vue 3-ի Composition API-ն տրամաբանությունը կազմակերպում է վերաօգտագործելի ֆունկցիաներում, իսկ Pinia-ն նրա պաշտոնական state store-ն է։ Nuxt-ը Vue-ի վրա կառուցված full-stack ֆրեյմվորքն է, որը կատարում է նույն դերը, ինչ Next.js-ը React-ի համար՝ ֆայլերով routing, server-side rendering, տվյալների ստացում և սերվերային API route-եր մեկ նախագծում։

Իմ ամենօրյա stack-ում չէ․ գիտեմ հիմնական գաղափարները և մանրամասները կսովորեմ, երբ նախագիծը դա պահանջի։

Ինչու է սա կարևոր

  • Vue-ն ամենատարածված ֆրոնտենդ ֆրեյմվորքներից է, հատկապես ուժեղ Եվրոպայում և Ասիայում, և շատ գործող պրոդուկտներ ու գործակալություններ կառուցված են դրա վրա։
  • Նրա նուրբ ռեակտիվությունը ճշգրիտ հետևում է, թե որ տվյալներն է օգտագործում կոմպոնենտը, ուստի թարմացումները արդյունավետ են՝ առանց ձեռքով memoization-ի։
  • Single-file կոմպոնենտները template-ը, տրամաբանությունը և ոճերը պահում են միասին, ինչը շատ թիմերի համար հեշտ է կարդալ և նոր մարդկանց ներգրավել։
  • Nuxt-ը Vue թիմերին տալիս է SSR, SEO-ի համար հարմար էջեր, սերվերային route-եր և հիբրիդ ռենդերինգ ուժեղ կոնվենցիաներով՝ համեմատելի այն բանի հետ, ինչ Next.js-ը տալիս է React թիմերին։

Ինչպես է աշխատում

01Գաղափարը՝ ռեակտիվ տվյալները ղեկավարում են view-ը

Vue-ում դուք հայտարարում եք state, և template-ը նկարագրում է, թե ինչպիսին պետք է լինի UI-ը այդ state-ի համար։ Երբ state-ը փոխվում է, Vue-ն թարմացնում է DOM-ի միայն այն մասերը, որոնք կախված են դրանից։ Ներքուստ Vue 3-ը ձեր տվյալները փաթաթում է JavaScript Proxy-ներով. երբ կոմպոնենտը ռենդեր է լինում, Vue-ն գրանցում է, թե որ ռեակտիվ հատկություններն է այն կարդացել, և երբ դրանցից մեկը փոխվում է, նորից աշխատեցնում է միայն այդ կոմպոնենտի render-ը։ Պատկերացրեք աղյուսակ (spreadsheet). բանաձևերով բջիջները ավտոմատ վերահաշվարկվում են, երբ փոխվում են այն բջիջները, որոնց հղում են անում։ Vue հավելվածը կոմպոնենտների ծառ է, որոնք սովորաբար գրվում են որպես Single-File Component-ներ (.vue ֆայլեր)՝ template-ով, script-ով և ընտրովի scoped ոճերով։ Vue 3.5-ը ներկայիս կայուն տարբերակն է. Vue 3.6-ը նոր, կոմպիլյատորի վրա հիմնված «Vapor mode»-ով 2026-ի վերջի դրությամբ release-candidate փուլում է։

02Կոմպոնենտ Composition API-ով

script setup-ի ներսում ref()-ը ստեղծում է ռեակտիվ արժեք (script-ում կարդացվում և գրվում է .value-ով, template-ում օգտագործվում է ուղղակիորեն), reactive()-ը ստեղծում է ռեակտիվ օբյեկտ, computed()-ը ստանում է քեշավորված ածանցյալ արժեքներ, իսկ watch()-ը կատարում է կողմնակի ազդեցություններ, երբ աղբյուրները փոխվում են։ Props-ը հայտարարվում են defineProps-ով և, սկսած Vue 3.5-ից, կարող են destructure արվել՝ մնալով ռեակտիվ։ Իրադարձությունները վեր են բարձրանում defineEmits-ով, իսկ defineModel-ը ստեղծում է երկկողմանի կապ v-model-ի համար։ Template-ներն օգտագործում են դիրեկտիվներ՝ v-if, v-for (:key-ով), v-bind (կարճ՝ :) և v-on (կարճ՝ @)։

vue
<script setup lang="ts">
import { ref, computed } from "vue";
 
const { items = [] } = defineProps<{ items?: { id: number; name: string; price: number }[] }>();
const emit = defineEmits<{ select: [id: number] }>();
 
const query = ref("");
const filtered = computed(() =>
  items.filter((i) => i.name.toLowerCase().includes(query.value.toLowerCase()))
);
</script>
 
<template>
  <input v-model="query" placeholder="Search products" />
  <p v-if="!filtered.length">No results</p>
  <ul v-else>
    <li v-for="item in filtered" :key="item.id" @click="emit('select', item.id)">
      {{ item.name }} — {{ item.price.toFixed(2) }}
    </li>
  </ul>
</template>

03Composable-ներ. վերաօգտագործելի տրամաբանություն

Composable-ը սովորական ֆունկցիա է՝ useSomething անունով, որն օգտագործում է Vue-ի ռեակտիվության API-ները և վերադարձնում ռեակտիվ state։ Այն React-ի custom hook-ի Vue համարժեքն է, բայց աշխատում է մեկ անգամ կոմպոնենտի setup-ի ժամանակ, ոչ թե յուրաքանչյուր render-ի, ուստի չկան dependency array-եր կամ stale closure-ի թակարդներ։ Composable-ները փոխարինում են Vue 2-ի mixin-ներին, որոնք ունեին անունների բախումներ և անհասկանալի ծագում։ Օգտագործեք դրանք տվյալների ստացման, ձևերի տրամաբանության, բրաուզերի API-ների (online կարգավիճակ, media query-ներ) և ընդհանուր վարքագծի համար։ VueUse գրադարանն առաջարկում է հարյուրավոր պատրաստի composable-ներ։ Lifecycle hook-երը, ինչպես onMounted-ը և onUnmounted-ը, կարելի է կանչել composable-ների ներսում՝ listener-ներ կարգավորելու և մաքրելու համար։

ts
// composables/useOnline.ts
import { ref, onMounted, onUnmounted } from "vue";
 
export function useOnline() {
  const online = ref(typeof navigator === "undefined" ? true : navigator.onLine);
  const update = () => (online.value = navigator.onLine);
  onMounted(() => {
    window.addEventListener("online", update);
    window.addEventListener("offline", update);
  });
  onUnmounted(() => {
    window.removeEventListener("online", update);
    window.removeEventListener("offline", update);
  });
  return { online };
}

04State-ի կառավարում Pinia-ով

Բազմաթիվ կոմպոնենտների միջև կիսվող state-ի համար, օրինակ՝ մուտք գործած օգտատերը կամ զամբյուղը, Vue-ի պաշտոնական store-ը Pinia-ն է (այն փոխարինեց Vuex-ին)։ Store-ը սահմանվում է defineStore-ով և կարող է օգտագործել նույն setup շարահյուսությունը, ինչ կոմպոնենտները. ref-երը state են, computed-ները՝ getter-ներ, ֆունկցիաները՝ action-ներ։ Pinia-ն type-safe է, մոդուլային (մեկ store յուրաքանչյուր դոմենի համար), աջակցում է Vue DevTools-ը և աշխատում է SSR-ի հետ Nuxt-ում։ State-ը destructure անելիս օգտագործեք storeToRefs, որպեսզի արժեքները մնան ռեակտիվ։ Ամեն ինչ չէ, որ պետք է store-ում լինի. սերվերի տվյալները հաճախ ավելի լավ է պահել տվյալների ստացման շերտում, ինչպես Nuxt-ի useFetch-ը կամ TanStack Query-ն Vue-ի համար, իսկ լոկալ UI state-ը մնում է կոմպոնենտներում։

ts
// stores/cart.ts
import { defineStore } from "pinia";
import { ref, computed } from "vue";
 
export const useCartStore = defineStore("cart", () => {
  const items = ref<{ id: number; qty: number; price: number }[]>([]);
  const total = computed(() => items.value.reduce((s, i) => s + i.qty * i.price, 0));
 
  function add(id: number, price: number) {
    const line = items.value.find((i) => i.id === id);
    line ? line.qty++ : items.value.push({ id, qty: 1, price });
  }
  return { items, total, add };
});

05Nuxt. full-stack Vue ֆրեյմվորքը

Nuxt 4-ը ներկայիս major տարբերակն է (Nuxt 3-ի աջակցությունն ավարտվեց 2026-ի կեսերին)։ Այն տալիս է ֆայլերի վրա հիմնված routing app/pages-ից, layout-ներ, ավտոմատ import արվող կոմպոնենտներ և composable-ներ, լռելյայն server-side rendering և Nitro-ով աշխատող server թղթապանակ API route-երի և middleware-ի համար։ Nitro-ն preset-երի միջոցով կարող է նույն հավելվածը deploy անել Node.js սերվերների, serverless պլատֆորմների կամ edge runtime-երի վրա։ Nuxt 4-ը հավելվածի կոդը լռելյայն տեղափոխեց app/ թղթապանակ՝ առանձնացնելով այն server/-ից, և բարելավեց տվյալների ստացման վարքագիծը և TypeScript նախագծերի բաժանումը։ Մոդուլները (@nuxt/image, @nuxt/content, @pinia/nuxt, @nuxtjs/i18n) հնարավորություններ են ավելացնում կոնֆիգի մեկ տողով։

bash
npx nuxi@latest init my-shop
cd my-shop && npm run dev
 
# Project layout (Nuxt 4)
# app/pages/index.vue           -> /
# app/pages/products/[id].vue   -> /products/:id
# app/components/ProductCard.vue (auto-imported)
# app/composables/useCart.ts     (auto-imported)
# server/api/products/[id].get.ts -> GET /api/products/:id
# nuxt.config.ts

06Տվյալների ստացում և սերվերային route-եր Nuxt-ում

Էջերում և կոմպոնենտներում օգտագործեք useFetch կամ useAsyncData։ Դրանք SSR-ի ժամանակ աշխատում են սերվերում, արդյունքը սերիալիզացնում են HTML payload-ի մեջ և hydration-ի ժամանակ նորից օգտագործում client-ում, ուստի տվյալները երկու անգամ չեն բերվում։ Դրանք վերադարձնում են ռեակտիվ data, status, error և refresh ֆունկցիա, և ընդունում են key, lazy-ի և server-ի նման ընտրանքներ և ռեակտիվ պարամետրեր, որոնց փոփոխության դեպքում տվյալները նորից են բերվում։ Իրադարձությունների handler-ների համար, ինչպես ձևի ուղարկումը, ուղղակիորեն օգտագործեք $fetch։ Սերվերային route-երն ապրում են server/api-ում և export են անում defineEventHandler. դրանք աշխատում են միայն սերվերում, ուստի կարող են գաղտնիքներ կարդալ runtimeConfig-ից և աշխատել բազաների հետ։

ts
// server/api/products/[id].get.ts
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, "id");
  const product = await findProduct(id); // your DB call
  if (!product) throw createError({ statusCode: 404, statusMessage: "Not found" });
  return product;
});
 
<!-- app/pages/products/[id].vue -->
<script setup lang="ts">
const route = useRoute();
const { data: product, status, error } = await useFetch(`/api/products/${route.params.id}`);
useSeoMeta({ title: () => product.value?.name ?? "Product" });
</script>

07Ռենդերինգի ռեժիմներ և deploy

Nuxt-ը աջակցում է universal rendering (SSR գումարած hydration), միայն client-ի SPA ռեժիմ, static generation և հիբրիդ ռենդերինգ, որտեղ յուրաքանչյուր route ստանում է իր կանոնը։ nuxt.config-ի route rules-ը թույլ են տալիս մարքեթինգային էջերը prerender անել build-ի ժամանակ, ապրանքների էջերը քեշավորել stale-while-revalidate-ով, dashboard-ը ռենդեր անել միայն client-ում և ավելացնել redirect-ներ կամ header-ներ՝ ամբողջը մեկ հավելվածում։ Build արեք nuxi build-ով server կամ serverless թիրախի համար, կամ nuxi generate-ով՝ ստատիկ կայքի համար։ .output-ի արդյունքը ինքնաբավ է. VPS-ի վրա գործարկում եք node .output/server/index.mjs nginx-ի կամ process manager-ի հետևում. Vercel-ում, Netlify-ում կամ Cloudflare-ում preset-ը հայտնաբերվում է ավտոմատ։

ts
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ["@pinia/nuxt", "@nuxt/image"],
  runtimeConfig: {
    apiSecret: "",                       // NUXT_API_SECRET, server only
    public: { apiBase: "/api" },         // NUXT_PUBLIC_API_BASE, exposed to client
  },
  routeRules: {
    "/": { prerender: true },
    "/products/**": { swr: 600 },
    "/dashboard/**": { ssr: false },
    "/old-shop/**": { redirect: "/products/**" },
  },
});

Օգտագործեք, երբ

  • Թիմեր, որոնց դուր են գալիս HTML-ի նման template-ները և մեղմ ուսուցման կորը, կամ որոնք արդեն Vue-ի փորձ ունեն։
  • Բովանդակային կայքեր, e-commerce և մարքեթինգային էջեր, որոնց SEO-ի համար պետք է SSR կամ prerendering՝ կառուցված Nuxt-ով։
  • Սերվերում ռենդեր արվող էջերի (Laravel, Django) աստիճանական հարստացում ինտերակտիվ widget-ներով, որտեղ Vue-ն հեշտությամբ ներդրվում է։
  • Ադմին վահանակներ և dashboard-ներ, որտեղ Vue-ի էկոհամակարգը (Vuetify, PrimeVue, Element Plus, Nuxt UI) արագացնում է առաքումը։

Մի՛ օգտագործեք, երբ

  • Երբ թիմը և կոմպոնենտների գրադարաններն արդեն React-ի վրա են. էկոհամակարգերը խառնելը կրկնապատկում է սպասարկումը։
  • Երբ մոբայլի համար React Native-ի հետ կոդի կիսումը պարտադիր պահանջ է. Vue-ի native տարբերակները շատ ավելի քիչ հասուն են։
  • Շատ փոքր ստատիկ էջեր, որտեղ բավական է պարզ HTML կամ Astro-ի նման թեթև գործիք։

Այլընտրանքներ

  • React + Next.js

    Ամենամեծ էկոհամակարգն ու աշխատանքի շուկան. Server Components և JSX-ի վրա հիմնված մոդել։

  • Svelte + SvelteKit

    Compiler-first, շատ փոքր bundle-ներ, rune-երի վրա հիմնված ռեակտիվություն։

  • Angular

    «Batteries included» enterprise ֆրեյմվորք՝ signal-ներով, DI-ով և խիստ կառուցվածքով։

  • Astro

    Բովանդակությունը առաջնային դարձնող կայքեր, որոնք քիչ JavaScript են ուղարկում. կարող է Vue կոմպոնենտներ տեղադրել որպես island-ներ։

Լավագույն պրակտիկաներ

  1. Բոլոր նոր կոդի համար օգտագործեք script setup TypeScript-ով և Composition API-ով։
  2. v-for-ի տարրերին միշտ տվեք կայուն, եզակի :key, և երբեք մի համատեղեք v-if-ը և v-for-ը նույն տարրի վրա։
  3. Վերաօգտագործելի տրամաբանությունը հանեք composable-ների մեջ, իսկ կոմպոնենտները պահեք ռենդերինգի վրա կենտրոնացած։
  4. Nuxt-ում էջի տվյալները բերեք useFetch-ով կամ useAsyncData-ով, ոչ թե setup-ի ներսում $fetch-ով, որպեսզի խուսափեք կրկնակի բեռնումից։
  5. Գաղտնիքները պահեք միայն սերվերի համար նախատեսված runtimeConfig-ում և runtimeConfig.public-ի տակ տրամադրեք միայն հանրային key-երը։
  6. Մեծ անփոփոխ տվյալների համար օգտագործեք shallowRef, իսկ ածանցյալ արժեքների համար՝ computed, state-ը պատճենող watcher-ների փոխարեն։
  7. Lint արեք ESLint-ով գումարած eslint-plugin-vue, և CI-ում template-ների տիպերը ստուգեք vue-tsc-ով։

Տարածված սխալներ

  • reactive() օբյեկտը կամ Pinia store-ը destructure անելը առանց toRefs-ի կամ storeToRefs-ի, ինչը լուռ կոտրում է ռեակտիվությունը։
  • watch-ի օգտագործումը state-ի երկու կտոր համաժամեցված պահելու համար՝ computed արժեքի փոխարեն։
  • SSR setup-ի ժամանակ window-ին կամ localStorage-ին դիմելը, ինչը առաջացնում է սերվերի սխալներ կամ hydration mismatch-եր։
  • $fetch-ը Nuxt էջի վերին մակարդակում կանչելը, ինչը տվյալները բերում է մեկ անգամ սերվերում և նորից client-ում։
  • Հավելվածի ամբողջ state-ը Pinia-ում պահելը, ներառյալ սերվերի քեշը և ժամանակավոր UI state-ը։

Խորը վերլուծություն

08Ռեակտիվության համակարգի ներսում

Յուրաքանչյուր ռեակտիվ օբյեկտ Proxy է, որի get trap-ը կանչում է track(target, key), իսկ set trap-ը՝ trigger(target, key)։ Effect-ները, օրինակ՝ կոմպոնենտի render ֆունկցիան, computed-ը կամ watcher-ը, աշխատում են active-effect կոնտեքստում. այդ աշխատանքի ընթացքում յուրաքանչյուր հետևվող ընթերցում effect-ը գրանցում է որպես այդ key-ի բաժանորդ։ Trigger-ի դեպքում Vue-ն պլանավորում է բաժանորդ effect-ները. կոմպոնենտների թարմացումները խմբավորվում են և ասինխրոն կատարվում microtask-ում, ահա թե ինչու DOM-ը թարմացվում է nextTick()-ից հետո։ Vue 3.4-ը computed արժեքները դարձրեց ծույլ և ծանուցող միայն այն ժամանակ, երբ դրանց արդյունքն իրականում փոխվում է, իսկ Vue 3.5-ը ներքին մասը վերակառուցեց version-counting, doubly-linked-list դիզայնով, որը զգալիորեն կրճատեց հիշողության օգտագործումը։ Օգտագործեք markRaw այն օբյեկտների համար, որոնք երբեք չպետք է ռեակտիվ լինեն, օրինակ՝ քարտեզի կամ գծապատկերների գրադարանների instance-ները։

ts
import { ref, computed, nextTick, markRaw, shallowRef } from "vue";
 
const count = ref(0);
const isEven = computed(() => count.value % 2 === 0); // only notifies on true<->false
 
count.value++;          // DOM not updated yet
await nextTick();       // now it is
 
// Large third-party instance: keep it out of reactivity
const map = shallowRef(markRaw(createMap("#map")));

09Կոմպիլյատորի օպտիմալացումներ և Vapor mode

Vue-ի template կոմպիլյատորն է պատճառը, որ Vue-ի virtual DOM-ն ավելի էժան է, քան պարզունակը։ Այն բարձրացնում (hoist) է ստատիկ node-երը, որպեսզի դրանք ստեղծվեն մեկ անգամ, դինամիկ node-երը նշում է patch flag-երով (փոխվում է միայն տեքստը, միայն class-ը) և դինամիկ երեխաներին խմբավորում է block-երում, որպեսզի diff-ը ամբողջությամբ բաց թողնի ստատիկ կառուցվածքը։ Սա հնարավոր է, որովհետև template-ները ստատիկ վերլուծելի են՝ ի տարբերություն կամայական JSX-ի։ Vapor mode-ը, որը գալիս է Vue 3.6-ում, ավելի հեռու է գնում. script setup vapor-ով ընտրված կոմպոնենտը կոմպիլյացվում է ուղիղ DOM գործողությունների, որոնք կապված են ռեակտիվ effect-ներին, ընդհանրապես առանց virtual DOM-ի՝ Solid-ի կամ Svelte-ի նման։ Այն կարող է համագործակցել սովորական կոմպոնենտների հետ, ուստի թիմերը կայունանալուց հետո կարող են այն ընդունել ըստ կոմպոնենտի՝ արտադրողականության համար կրիտիկական էկրաններում։

10SSR-ի ներքին մանրամասները Nuxt-ում. payload, key-եր և քեշավորում

SSR-ի ժամանակ Nuxt-ը ձեր կոմպոնենտները գործարկում է սերվերում, և յուրաքանչյուր useAsyncData կամ useFetch արդյունք պահվում է իր key-ի տակ payload-ում՝ սերիալիզացված HTML-ի մեջ։ Hydration-ի ժամանակ client-ը տվյալները կարդում է payload-ից՝ նորից բերելու փոխարեն։ Key-երը կարևոր են. նույն key-ով երկու կանչ կիսում են տվյալները, իսկ ֆայլից և տողից ավտոմատ գեներացված key-ն աշխատում է միայն այն դեպքում, երբ կանչը setup-ի վերին մակարդակում է։ Պայմանի կամ ցիկլի ներսում բեռնելը բացահայտ key-եր է պահանջում։ Nitro-ի routeRules-ը swr-ով կամ isr-ով ռենդեր արված HTML-ը քեշավորում է սերվերի կամ CDN-ի շերտում, իսկ defineCachedEventHandler-ը քեշավորում է API պատասխանները։ Հիշեք, որ սերվերի state-ը կիսվում է հարցումների միջև. երբեք մի պահեք կոնկրետ օգտատիրոջ տվյալներ մոդուլի մակարդակի փոփոխականներում սերվերի կոդում կամ composable-ներում։

ts
// Explicit key + transform to keep the payload small
const { data } = await useAsyncData(
  `product-${id}`,
  () => $fetch(`/api/products/${id}`),
  { pick: ["id", "name", "price"] }
);
 
// server/api/rates.get.ts — cached for 10 minutes
export default defineCachedEventHandler(async () => {
  return await $fetch("https://api.example.com/rates");
}, { maxAge: 600 });

11Մեծ Vue կոդային բազայի արտադրողականություն և մասշտաբավորում

Bundle-ի չափի համար օգտագործեք defineAsyncComponent և route-ի մակարդակով lazy loading, որպեսզի յուրաքանչյուր էջ բեռնի միայն այն, ինչ պետք է. Nuxt-ը route splitting-ն անում է ավտոմատ, իսկ Lazy նախածանցը (LazyChart) հետաձգում է կոմպոնենտը մինչև այն ռենդեր լինի։ Nuxt-ն աջակցում է նաև lazy hydration ռազմավարություններ, ինչպես hydrate-on-visible-ը, որպեսզի էկրանից ներքև գտնվող ինտերակտիվ կոմպոնենտները բեռնման ժամանակ main thread-ի ժամանակ չծախսեն։ Երկար ցուցակների համար օգտագործեք virtual scrolling (vue-virtual-scroller կամ TanStack Virtual) և թանկ տողերի համար՝ v-memo։ Մեծ կոդային բազաներում Nuxt layer-ները թույլ են տալիս մի քանի հավելվածի կիսել կոմպոնենտներ, կոնֆիգ և composable-ներ բազային layer-ից, իսկ խիստ TypeScript-ը vue-tsc-ով բռնում է template-ների տիպերի սխալները։ Օպտիմալացնելուց առաջ պրոֆիլավորեք Vue DevTools-ի performance timeline-ով։

Իրական իրավիճակներ

01

SEO խնդիրներ Vue single-page խանութում

Խնդիր

Որպես միայն client-ի Vue SPA կառուցված առցանց խանութը վատ դիրքեր ունի։ Ապրանքների էջերը crawler-ներին ցույց են տալիս դատարկ կմախք, իսկ սոցիալական preview-ներում չկան վերնագրեր կամ նկարներ։

Ինչպես լուծել

Միգրացրեք Nuxt՝ նորից օգտագործելով գոյություն ունեցող կոմպոնենտները և Pinia store-երը։ Route-երը տեղափոխեք app/pages, client-ի բեռնումը փոխարինեք useFetch-ով, որպեսզի ապրանքների տվյալները ռենդեր լինեն սերվերում, և ավելացրեք useSeoMeta վերնագրերի, նկարագրությունների և Open Graph նկարների համար՝ յուրաքանչյուր ապրանքի համար։ Օգտագործեք routeRules՝ կատեգորիաների էջերը prerender անելու և ապրանքների էջերին swr քեշավորում կիրառելու համար, որպեսզի սերվերը չծանրաբեռնվի յուրաքանչյուր դիտման ժամանակ։ Ավելացրեք sitemap մոդուլ և canonical URL-ներ։ view-source-ով և rich results թեստով ստուգեք, որ բովանդակությունը նախնական HTML-ում է։

02

Dashboard-ը սառչում է 10,000 տող ֆիլտրելիս

Խնդիր

Vue-ով կառուցված ադմին աղյուսակը վայրկյաններով դանդաղում է, երբ օգտատերը ֆիլտրի դաշտում տեքստ է մուտքագրում։ Պրոֆիլավորումը ցույց է տալիս ծանր ռեակտիվության և ռենդերինգի աշխատանք յուրաքանչյուր ստեղնսեղմման ժամանակ։

Ինչպես լուծել

Հում տվյալները պահեք shallowRef-ում, որպեսզի Vue-ն խորությամբ proxy չանի 10,000 օբյեկտ։ Որոնման մուտքը debounce արեք VueUse-ի refDebounced-ով, իսկ ֆիլտրված արդյունքները հաշվարկեք computed-ով, որպեսզի դրանք վերահաշվարկվեն միայն մուտքերի փոփոխության դեպքում։ Ռենդեր արեք virtual scroller-ով, որպեսզի DOM-ում լինեն միայն տեսանելի տողերը։ Եթե տողերը ծանր կոմպոնենտներ են, օգտագործեք v-memo՝ տողի տվյալներով key-ով։ Սերվերի մասշտաբի տվյալների համար ֆիլտրումը և էջավորումը տեղափոխեք API։ Կրկին պրոֆիլավորեք DevTools-ում՝ հաստատելու, որ main thread-ը մնում է արձագանքող։

03

Օգտատիրոջ տվյալները արտահոսում են այցելուների միջև SSR-ից հետո

Խնդիր

SSR-ը միացնելուց հետո օգտատերը header-ում կարճ ժամանակով տեսավ մեկ այլ օգտատիրոջ անունը։ Հավելվածը ընթացիկ օգտատիրոջը պահում է composable-ում՝ մոդուլի մակարդակի ref-ով։

Ինչպես լուծել

Սերվերում մոդուլի մակարդակի state-ը կիսվում է բոլոր հարցումների միջև, ուստի մի հարցման օգտատերը արտահոսում է մյուսի render-ի մեջ։ Օգտատիրոջը տեղափոխեք Pinia store կամ Nuxt-ի useState, որոնք երկուսն էլ սերվերում state են ստեղծում յուրաքանչյուր հարցման համար և hydrate անում client-ում։ Բեռնեք այն session cookie-ից սերվերային plugin-ում կամ route middleware-ում, և երբեք մի քեշավորեք անհատականացված էջերը swr-ով կամ CDN կանոններով։ Ստուգեք մյուս composable-ները մոդուլի մակարդակի ref-երի համար և ավելացրեք թեստ, որը ռենդեր է անում երկու զուգահեռ հարցում տարբեր օգտատերերով։

Արագ ուղեցույց

Նոր Vue հավելված (Vite)
npm create vue@latest
Նոր Nuxt հավելված
npx nuxi@latest init my-app
Երկկողմանի կապված prop
const model = defineModel<string>()
Template ref (3.5+)
const input = useTemplateRef<HTMLInputElement>("input")
Store-ի արժեքները ռեակտիվ պահել
const { items, total } = storeToRefs(useCartStore())
Nuxt SSR տվյալներ
const { data, status, refresh } = await useFetch("/api/items")
Միայն client-ի widget
<ClientOnly><MapView /></ClientOnly>
Քեշավորում ըստ route-ի
routeRules: { "/blog/**": { swr: 3600 } }
Template-ների տիպերի ստուգում
npx vue-tsc --noEmit
Nuxt-ի build Node-ի համար
npx nuxi build && node .output/server/index.mjs

Հարցեր և պատասխաններ

Q1Ինչո՞վ է Vue-ի ռեակտիվությունը տարբերվում React-ի մոդելից։

React-ը նորից աշխատեցնում է կոմպոնենտի ֆունկցիան, երբ նրա state-ը կամ props-ը փոխվում են, և համեմատում ստացված virtual DOM-ը. ավելորդ աշխատանքը վերահսկում եք memoization-ով և dependency array-երով։ Vue-ն ավտոմատ հետևում է կախվածություններին. ռեակտիվ օբյեկտները Proxy-ներ են, և render-ի ժամանակ Vue-ն գրանցում է, թե որ հատկություններն են կարդացվել։ Երբ դրանցից մեկը փոխվում է, նորից ռենդեր են լինում միայն այն կոմպոնենտները, որոնք օգտագործել են այն։ Setup կոդն աշխատում է մեկ անգամ, ոչ թե յուրաքանչյուր render-ի, ուստի stale closure-ներ չկան։ Արդյունքում Vue հավելվածներին հազվադեպ է պետք ձեռքով memoization։ React-ի մոդելն ավելի բացահայտ և ֆունկցիոնալ է. Vue-ինը՝ ավելի ավտոմատ և մոտ signal-ներին, որոնք այդ ժամանակից ի վեր ընդունել են նաև այլ ֆրեյմվորքներ։

Q2ref, թե՞ reactive. որն օգտագործել։

Vue թիմը խորհուրդ է տալիս ref-ը որպես հիմնական API։ ref-ն աշխատում է ցանկացած տիպի արժեքի հետ, ներառյալ պրիմիտիվները, և կարող եք փոխարինել ամբողջ արժեքը կամ այն փոխանցել՝ առանց ռեակտիվությունը կորցնելու։ reactive-ն աշխատում է միայն օբյեկտների հետ և կորցնում է ռեակտիվությունը, եթե այն destructure եք անում կամ փոփոխականը վերագրում, ինչը բագերի տարածված աղբյուր է։ Template-ներում ref-երը ավտոմատ «բացվում» են, ուստի .value-ի գինը երևում է միայն script-ում։ reactive-ը երբեմն օգտագործում եմ լոկալ ձևի օբյեկտի համար, որը երբեք չի վերագրվում, իսկ ref-ը՝ մնացած ամեն ինչի համար, գումարած shallowRef մեծ տվյալների համար, որոնք փոխարինվում են, ոչ թե մուտացիայի ենթարկվում։

Q3Ի՞նչ է Nuxt-ն ավելացնում Vue-ի վրա։

Vue-ն կոմպոնենտների գրադարանն է. Nuxt-ը՝ հավելվածի ֆրեյմվորքը։ Nuxt-ն ավելացնում է ֆայլերի վրա հիմնված routing և layout-ներ, server-side rendering hydration-ով, prerendering և ըստ route-ի քեշավորման կանոններ, տվյալների ստացման composable-ներ, որոնք սերվերի տվյալները փոխանցում են client-ին, Nitro սերվեր API route-երի և middleware-ի համար, SEO օգնականներ, ավտոմատ import-ներ, runtime կոնֆիգուրացիա և մոդուլների էկոհամակարգ նկարների, բովանդակության, i18n-ի և auth-ի համար։ Այն նաև preset-երի միջոցով նույն նախագիծը deploy է անում Node.js, serverless կամ edge պլատֆորմների վրա։ Այն համեմատելի է Next.js-ի հետ React-ի համար. այս մասերը կարող էիք ինքներդ հավաքել Vite-ով և Vue Router-ով, բայց Nuxt-ը դրանք դարձնում է համահունչ։

Q4Ի՞նչ է hydration mismatch-ը և ինչպե՞ս խուսափել դրանից։

SSR-ի դեպքում սերվերը ռենդեր է անում HTML, իսկ client-ը հետո hydrate է անում այն՝ Vue-ի ռեակտիվությունը կցելով գոյություն ունեցող DOM-ին։ Եթե client-ի առաջին render-ը տալիս է սերվերից տարբեր markup, Vue-ն զգուշացնում է mismatch-ի մասին և կարող է նորից ռենդեր անել էջի մասեր՝ առաջացնելով թարթում կամ բագեր։ Տիպիկ պատճառներն են setup-ի ժամանակ window-ի, localStorage-ի կամ ընթացիկ ժամանակի ընթերցումը, պատահական ID-ները և locale-ից կախված ֆորմատավորումը։ Լուծումներ. միայն բրաուզերի կոդը տեղափոխեք onMounted, միայն բրաուզերի widget-ները փաթաթեք ClientOnly-ով, կայուն ID-ների համար օգտագործեք useId, իսկ ժամանակը կամ locale-ը սերվերից փոխանցեք useState-ի կամ payload-ի միջոցով։

Q5Pinia, թե՞ composable-ներ ընդհանուր state-ի համար։

Composable-ը, որը ref-երը հայտարարում է ֆունկցիայի ներսում, յուրաքանչյուր կոմպոնենտի համար նոր state է ստեղծում. ref-երը մոդուլի մակարդակում հայտարարելը կիսում է state-ը, բայց սերվերում այդ state-ը SSR-ի ժամանակ արտահոսում է տարբեր օգտատերերի հարցումների միջև, ինչը անվտանգության խնդիր է։ Pinia-ն լուծում է սա. store-երը ստեղծվում են յուրաքանչյուր app instance-ի համար, ուստի SSR հարցումները մեկուսացված են, և ստանում եք DevTools-ի աջակցություն, plugin-ներ և store state-ի hydration։ Իմ կանոնը. լոկալ վերաօգտագործելի տրամաբանությունը՝ composable-ներում, գլոբալ client state-ը՝ Pinia store-երում, իսկ սերվերի տվյալները՝ useFetch-ում կամ query գրադարանում։ Nuxt-ում useState-ը թեթև, SSR-ի համար անվտանգ տարբերակ է փոքր ընդհանուր արժեքների համար։

Q6Ինչպե՞ս կմիգրացնեիք մեծ Vue 2 հավելվածը Vue 3։

Vue 2-ի աջակցությունն ավարտվեց 2023-ի վերջին, ուստի միգրացիան անվտանգության հարց է։ Նախ կթարմացնեի Vue 2.7-ի, որը backport է անում Composition API-ն և script setup-ը, և նոր կոդը կսկսեի գրել այդպես։ Հետո կփոխարինեի Vue 3-ը չաջակցող գրադարանները. Vuex-ը՝ Pinia-ով, հին router-ը՝ Vue Router 4-ով, իսկ UI kit-երը՝ դրանց Vue 3 տարբերակներով։ Migration build-ը (@vue/compat) կարող է հավելվածը գործարկել Vue 3-ի վրա Vue 2-ի վարքագծով և զգուշացումներով, ուստի deprecation-ները ուղղում եք աստիճանաբար։ Webpack-ից Vite անցումը սովորաբար կատարվում է միաժամանակ։ Լավ end-to-end թեստերն են, որ սա դարձնում են անվտանգ։

Սենիոր մակարդակի հարցեր

S1Ինչպե՞ս կկառուցեիք մեծ Nuxt հավելված, որը կիսում են մի քանի թիմեր։

Կօգտագործեի Nuxt layer-ներ. բազային layer՝ դիզայն համակարգով, ընդհանուր composable-ներով, auth-ով և API client-ով, և feature կամ պրոդուկտային layer-ներ, որոնք ընդլայնում են այն։ Յուրաքանչյուր թիմ տիրապետում է իր layer-ին monorepo-ում, որը կառավարվում է pnpm workspaces-ով և Turborepo-ով կամ Nx-ով, lint կանոններով պարտադրված խիստ սահմաններով, որպեսզի feature-ները չimport անեն միմյանց ներքին մասերը։ Տվյալների հասանելիությունն անցնում է OpenAPI-ից գեներացված տիպավորված API client-ներով, ոչ թե պատահական $fetch կանչերով։ Ընդհանուր state-ը նվազագույն է և ապրում է հստակ սեփականատեր ունեցող Pinia store-երում։ CI-ը յուրաքանչյուր ազդված layer-ի համար գործարկում է vue-tsc, unit թեստեր Vitest-ով և end-to-end թեստեր Playwright-ով։ Սա պահում է մեկ deploy արվող հավելված՝ հստակ սեփականությամբ, ինչը սովորաբար ավելի լավ է, քան անմիջապես micro-frontend-ների անցնելը։

S2Համեմատեք Nuxt-ը և Next.js-ը նոր պրոդուկտի համար։ Ի՞նչը կորոշի ընտրությունը։

Տեխնիկապես դրանք մոտ են. երկուսն էլ առաջարկում են ֆայլային routing, SSR, ստատիկ և հիբրիդ ռենդերինգ, սերվերային route-եր և edge deploy։ Որոշումը հիմնականում մարդկանց և էկոհամակարգի մասին է։ Next.js-ն ունի ավելի մեծ աշխատանքի շուկա, React Server Components՝ նուրբ սերվերային ռենդերինգի համար, և React Native՝ մոբայլի հետ կոդ կիսելու համար։ Nuxt-ն առաջարկում է Vue-ի ավելի պարզ ռեակտիվությունը, ավելի քիչ boilerplate ավտոմատ import-ների և կոնվենցիաների շնորհիվ, Nitro-ի տեղափոխելիությունը hosting-երի միջև՝ առանց vendor-ին հատուկ հնարավորությունների, և ուժեղ մոդուլների էկոհամակարգ։ Եթե թիմը գիտի React կամ պետք է կոդ կիսել մոբայլի հետ, ընտրում եմ Next.js։ Եթե թիմը Vue-ի վրա է, կամ պրոդուկտը բովանդակությամբ և SEO-ով ծանր է՝ փոքր թիմով, որը գնահատում է կոնվենցիաները, Nuxt-ը հիանալի ընտրություն է։ Ֆրեյմվորքը փոխելու համար վերագրելը հազվադեպ է արդարանում։

S3Ինչպե՞ս եք անվտանգ կազմակերպում authentication-ը Nuxt-ում SSR-ով։

Session-ը պահում եմ HttpOnly, Secure, SameSite=Lax cookie-ում, որը սահմանում է սերվերը, երբեք token localStorage-ում, որպեսզի այն հասանելի լինի SSR-ի ժամանակ և չկարդացվի script-երով։ Սերվերային route-երը և server middleware-ը վալիդացնում են session-ը յուրաքանչյուր հարցման ժամանակ. client-ի route middleware-ը միայն բարելավում է UX-ը՝ շուտ redirect անելով, քանի որ այն կարելի է շրջանցել։ SSR-ի ժամանակ ձեր սեփական API-ն կանչելիս փոխանցեք մուտքային cookie-ն՝ օգտագործելով useRequestFetch կամ useRequestHeaders։ Օգտատիրոջ state-ը գնում է useState կամ Pinia, որպեսզի լինի ըստ հարցման։ Անհատականացված էջերը բացառվում են swr և CDN քեշավորումից։ Երրորդ կողմի login-ի համար nuxt-auth-utils-ը կամ provider-ի SDK-ն մշակում են OAuth հոսքերը, իսկ cookie-ով authenticated փոփոխող հարցումների համար CSRF պաշտպանությունը պարտադիր է։

Բառարան

Single-File Component
.vue ֆայլ, որը մեկ տեղում միավորում է կոմպոնենտի template-ը, script-ը և ոճերը։
Composition API
Vue 3-ի ֆունկցիաների վրա հիմնված եղանակը կոմպոնենտի տրամաբանություն գրելու համար՝ ref-ով, computed-ով, watch-ով և lifecycle hook-երով։
ref
Ռեակտիվ կոնտեյներ ցանկացած արժեքի համար. script-ում այն կարդացվում և գրվում է .value-ի միջոցով։
Composable
Վերաօգտագործելի ֆունկցիա, որն օգտագործում է Vue-ի ռեակտիվության API-ները և ըստ կոնվենցիայի կոչվում է useSomething։
Pinia
Vue-ի պաշտոնական state management գրադարանը՝ type-safe, մոդուլային store-երով և SSR աջակցությամբ։
Hydration
Client-ի կողմից ինտերակտիվության կցումը HTML-ին, որը սերվերն արդեն ռենդեր է արել։
Nitro
Nuxt-ի սերվերային engine-ը, որը գործարկում է API route-երը և deploy անում Node, serverless կամ edge պլատֆորմների վրա։
Route rules
Nuxt կոնֆիգ, որը URL pattern-ները համապատասխանեցնում է ռենդերինգի, քեշավորման, redirect-ի և header-ների վարքագծին։
Vapor mode
Vue-ի առաջիկա կոմպիլյացիայի ռեժիմ, որը DOM-ը թարմացնում է ուղղակիորեն՝ առանց virtual DOM-ի։

Պրոդուկտի գաղափար ունե՞ք։Եկեք ստեղծենք այն։

Պատմեք ձեր գաղափարի, պրոդուկտի կամ թիմի մասին։ Սովորաբար պատասխանում եմ մեկ աշխատանքային օրվա ընթացքում։