1. რა არის Vue.js ?
Vue.js (იგივე Vue) არის, ჯავასკრიპტის ფრეიმვორკი (და არა ბიბლიოთეკა) ღია წყაროთი, რომელიც გამოიყენება სამომხმარებლო ინტერფეისებისა (UI - User Interface) და ერთგვერდიანი აპლიკაციების (SPA - single page application) შესაქმნელად.
სამომხმარებლო ინტერფეისი (UI - User Interface) არის სივრცე, გარემო, რომლის მეშვეობითაც მომხმარებელი მართავს პროგრამულ მოწყობილობებსა და აპლიკაციებს. ვებ-სივრცის გადმოსახედიდან თუ ვიტყვით, სამომხმარებლო ინტერფეისი არის ყველაფერი რასაც ბრაუზერში ვხედავთ.
ერთგვერდიანი აპლიკაციის (SPA) უკან მოიაზრება ვებ-აპლიკაცია ან ვებ-გვერდი, რომელიც ბრაუზერის განახლების (refresh) გარეშე ახდენს ინფორმაციის ერთ კონკრეტულ გვერდზე დინამიურად გამოტანას, მომხმარებლის ქმედებიდან გამომდინარე.

რა განსხვავებაა ფრეიმვორკსა და ბიბლიოთეკას შორის ?

ბიბლიოთეკა არის კონკრეტული მიზნებისათვის შექმნილი კონკრეტული ფუნქციების ორგანიზებული ნაკრები (მაგ: სტრიქონებთან სამუშო ფუნქციების, ფაილებთან სამუშაო ფუნქციების და ა.შ).
ფრეიმვორკი არის პროექტის აბსტრაქტული, განზოგადებული ფორმა, შაბლონი, ჩონჩხი, კოდირების დიზაინი, რომელშიც სისტემის მართვის მექანიზმი უკვე შექმნილია თუმცა არის სივრცეები სადაც შეგვიძლია ჩვენი კოდი შევიტანოთ, შემდეგ ხდება ზემოთ ნახსენები, უკვე არსებული ფუნდამენტისა და ჩვენი კოდის 'შეზავება' და ასე ყალიბდება პროექტის სრული იერსახე.



Vue გავრცელებულია javascript ფაილის სახით და ვებ-გვერდზე მისი დამატება შესაძლებელია script ტეგის მეშვეობით :

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>  
                

ჩვენი პირველი გვერდი

ბიჯი 1: HTML გვერდი

შევქმნათ მარტივი HTML გვერდი :
<!DOCTYPE html>
<html lang="en">
<head>
    <title>My first Vue page</title>
</head>
<body>

</body>
</html> 
                

ბიჯი 2: დავამატოთ <div> ელემენტი

საჭიროა შევქმნათ რაიმე HTML ელემენტი, რომელსაც დაუკავშირდება Vue. დავამატოთ <div> ელემენტი და მივცეთ მას id :
<!DOCTYPE html>
<html lang="en">
<head>
    <title>My first Vue page</title>
</head>
<body>
    <div id="app"></div>
</body>
</html> 
                

ბიჯი 3: დავკავშირდეთ Vue-სთან

<!DOCTYPE html>
<html lang="en">
<head>
    <title>My first Vue page</title>
</head>
<body>
    <div id="app"></div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</body>
</html> 
                

ბიჯი 4: შევქმნათ Vue-ს ინსტანცია

ავკრიფოთ ჩვენი პირველი Vue კოდი.

კოდის ეს ფრაგმენტი შეიძლება შეიცავდეს სხვადასხვა ინფორმაციებს, მეთოდებს და ა.შ. ამ ეტეპზე უბრალოდ რაიმე შეტყობინება გამოვიტანოთ .

ბოლოს კი ახლადშექმნილი Vue-ს ინსტანცია დავაკავშიროთ მე-2-ე ბიჯში შექმნილ <div> ელემენტთან.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>My first Vue page</title>
</head>
<body>
    <div id="app"></div>

    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

    <script>

        const app = Vue.createApp({
            data() {
                return {
                    message: "Hello World!"
                }
            }
        })

        app.mount('#app')

    </script>
</body>
</html> 
                
createApp ფუნქციის მეშვეობით ხდება Vue-ს ახალი ინსტანციის შექმნა, ეს არის ნებისმიერი Vue აპლიკაციის საწყისი ფაზა.
ინგ: mount - დაყენება, დამონტაჟება, დადგმა

ბიჯი 5: გამოვიტანოთ შეტყობინება ინტერპოლაციის გზით

ახლა შევეცადოთ შეტყობინება გამოვიტანოთ ინტეპოლაციის გზით, Vue-ში ამისათვის გამოიყენება ორმაგი ფიგურული ფრჩხილები: {{ }}
<div id="app"> {{ message }} </div>
                
{{ message }} ჩანაწერს ბრაუზერი ჩაანაცვლებს Vue ინსტანციაში არსებულ 'message' თვისებაში შენახული ტექსტით.
<!DOCTYPE html>
<html lang="en">
<head>
    <title>My first Vue page</title>
</head>
<body>
    
    <div id="app">
        {{ message }}
    </div>

    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

    <script>
        const app = Vue.createApp({
            data() {
                return {
                    message: "Hello World!"
                }
            }
        })

    app.mount('#app')

    </script>
</body>
</html>
                
უცხო სიტყვ. ლექს: ინტეპოლაცია - რაიმე ნაწარმოების ტექსტში გვიანდელი ჩანართი, რომელიც ავტორს არ ეკუთვნის

ჯავასკრიპტი ინტერპოლაციაში

ინტერპოლაციაში აგრეთვე შესაძლებელია ჯავასკრიპტ კოდის გამოყენებაც :
<div id="app">
    {{ message }} <br>
    {{'Random number: ' + Math.ceil(Math.random()*6) }}
</div>
                
2. Vue დირექტივები
Vue-ში დირექტივა ეწოდება სპეციალურ HTML ატრიბუტს, რომელსაც ერთვის პრეფიქსი v- და რომლის მეშვეობითაც HTML ელემენტი ხდება უფრო ფუნქციონალური. დირექტივები უკავშირდებიან Vue-ს ინსტანციას. სწორედ ეს კავშირი უზრუნველყოფს სამომხმარებლო ინტერფეისის რეაქტიულობასა და დინამიურობას.
დირექტივა აღწერა
v-bind HTML ელემენტის ატრიბუტს აკავშირებს Vue-ს ინსტანციაში აღწერილ ცვლადთან.
v-if პირობითი ოპერატორის შედეგიდან გამონდინარე ქმნის HTML ელემენტს. ამ დირექტივასთან ერთად შესაძლებელია v-else-if და v-else დირექტივების გამოყენებაც. თუ პირობითი ოპერატორის შედეგი არის false მაშინ ელემენტი საერთოდ არ შეიქმნება DOM-ში (DOM-ის შესახებ ინფორმაცია შეგიძლიათ იხილოთ ჯავასკრიპტის ცნობარის მე-20-ე თავში).
v-show პირობითი ოპერატორის შედეგიდან გამონდინარე განსაზღვრავს უნდა იყოს თუ არა ხილვადი კონკრეტული HTML ელემენტი. თუ პირობითი ოპერატორის შედეგი არის false, ელემენტი მაინც შეიქმნება DOM-ში, თუმცა მიენიჭება CSS თვისება display: none
v-for ქმნის HTML ელემენტების სიას Vue-ს ინსტანციაში აღწერილი მასივისა და for ციკლის მეშვეობით.
v-on როგორც ვიცით HTML ელემენტთან მუშაობისას შეიძლება შესრულდეს სხვადასხვა მოვლენები (მოვლენების შესახებ ინფორმაცია შეგიძლიათ იხილოთ ჯავასკრიპტის ცნობარის 25-ე თავში). ეს დირექტივა კი ამ მოვლენებს აკავშირებს Vue-ს ინსტანციაში აღწერილ მეთოდებთან.
v-model გამოიყენება HTML ფორმებში არსებულ <form>, <input>, <button> და სხვა ტიპის ელემენტებთან სამუშაოდ. უზრველყოფს ინფორმაციის მიმაგრების ორმხრივ გზას ელემენტებსა და Vue-ს ინსტანციაში აღწერილ თვისებებს შორის.
3. Vue v-bind დირექტივა
v-bind დირექტივის მეშვეობით შესაძლებელია HTML ატრიბუტებს, მათი გადინამიურების მიზნით მივამაგროთ Vue-ს ინსტანციაში აღწერილი მნიშვნელობები (ინგ: bind - მიბმა; დამაგრება). გამოყენების სინტაქსი ასეთია :
<div v-bind:[attribute]="[Vue data]"></div>  
                
კონკრეტული მაგალითი კი ასეთი :
<!DOCTYPE html>
<html>
<head>
    <title>'v-bind' Image Source Example</title>
</head>
<body>

<div id="app">
    <img v-bind:src="url">
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                url: "img_beach3.jpg"
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
ანუ <img> ელემენტის src ატრიბუტის მნიშვნელობად ჩავსვით Vue-ს ინსტანციაში არსებული url თვისების მნიშვნელობა.

სტილების მიმაგრება

v-bind დირექტივის მეშვეობით შესაძლებელია ხაზსშიდა სტილების განსაზღვრაც. ამისათვის შეგვიძლია გამოვიყენოთ ჯავასკრიპტის ობიექტები. მოვიყვანოთ მაგალითი, რომელშიც ფონტის ზომა დამოკიდებული იქნება Vue-ში არწერილ 'size' თვისებაზე :
<!DOCTYPE html>
<html>
<head>
    <title>v-bind font-size</title>
</head>
<body>

<div id="app">
    <div v-bind:style="{ fontSize: size }">Text example</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                size: "28px"
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
შესაძლებელია ტერნარული ჩანაწერის გამოყენებაც, მოვიყვანოთ მაგალითი, რომელშიც ფონის ფერი განისაზღვრება იმის მიხედვით, თუ რა მნიშვნელობა ექნება Vue-ში აღწერილ ლოგიკური ტიპის 'isImportant' თვისებას:
<!DOCTYPE html>
<html>
<head>
    <title>v-bind background-color</title>
</head>
<body>

<div id="app">
    <div v-bind:style="{ backgroundColor: isImportant ? 'lightcoral' : 'lightgray' }">
        Importance based on background color
    </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                isImportant: true
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                

კლასების მიმაგრება

<!DOCTYPE html>
<html>
<head>
    <title>v-bind background-color</title>
    <style>
        .impClass {
            background-color: lightcoral;
        }
    </style>
</head>
<body>

<div id="app">
    <div v-bind:class="className">
        Importance visualized by background color
    </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                className: 'impClass'
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
უნდა აღინიშნოს, რომ შესაძლებელია v-bind დირექტივის უფრო მოკლედ ჩაწერაც, კერძოდ - მის ნაცვლად შეგვიძლია გამოვიყენოთ ორწერტილი :
<div id="app">
    <div :class="className">
        Importance visualized by background color
    </div>
</div>
                
4. Vue v-if დირექტივა
როგორც ადრე აღვნიშნეთ, v-if დირექტივა პირობითი ოპერატორის შედეგიდან გამონდინარე ქმნის HTML ელემენტს. ამ დირექტივასთან ერთად შესაძლებელია v-else-if და v-else დირექტივების გამოყენებაც. თუ პირობითი ოპერატორის შედეგი არის false მაშინ ელემენტი საერთოდ არ შეიქმნება DOM-ში.
<!DOCTYPE html>
<html>
<head>
    <title>Typewriters</title>
</head>
<body>

<div id="app">
    <p v-if="typewritersInStock">
        in stock
    </p>
    <p v-else>
        not in stock
    </p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                typewritersInStock: false
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                

პირობითი ოპერატორები Vue-ში

დირექტივა აღწერა
v-if შეიძლება გამოვიყენოთ, როგორც ცალკე, ასევე v-else-if და/ან v-else დირექტივებთან ერთად. თუ v-if დირექტივაში მოქცეული პირობა ჭეშმარიტია, ანუ არის 'true', v-else-if და v-else დირექტივები აღარ განიხილება.
v-else-if გამოყენებულ უნდა იქნას v-if ან სხვა v-else-if დირექტივასთან ერთად. თუ მიმდინარე v-else-if დირექტივაში არსებული პირობა არის ჭეშმარიტი, ანუ 'true', მაშინ ამ დირექტივის შემდეგ არსებული v-else-if და v-else დირექტივები აღარ განიხილება.
v-else ყოველთვის ჭეშმარიტია, ანუ 'true', მაგრამ განიხილება მხოლოდ იმ შემთხვევაში თუ მანამდე არსებული ყველა პირობითი ოპერატორი მცდარია. დირექტივა იწერება პირობითი ოპერატორის ბოლოში.
<!DOCTYPE html>
<html>
<head>
<title>My first Vue page</title>
</head>
<body>

<div id="app">
    <p v-if="typewriterCount>3">
        პროდუქტი არის მარაგში
    </p>
    <p v-else-if="typewriterCount>0">
        მარაგი იწურება
    </p>
    <p v-else>
        პროდუქტი არ არის მარაგში
    </p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                typewriterCount: 3
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
5. Vue v-show დირექტივა
v-show დირექტივა პირობითი ოპერატორის შედეგიდან გამონდინარე განსაზღვრავს უნდა იყოს თუ არა ხილვადი კონკრეტული HTML ელემენტი. თუ პირობითი ოპერატორის შედეგი არის false, ელემენტი მაინც შეიქმნება DOM-ში, თუმცა მიენიჭება CSS თვისება display: none;
<!DOCTYPE html>
<html>
<head>
    <title>v-show</title>  
</head>
<body>

<div id="app">
    <div v-show="showDiv">This div tag can be hidden</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                showDiv: true
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                

განსხვავება v-show და v-if დირექტივებს შორის

განსხვავება v-show და v-if დირექტივებს შორის არის ის, რომ v-if დირექტივა ელემენტს ქმნის პირობიდან გამომდინარე. v-show დირექტივა კი უკვე შექმნილი ელემენტის ხილვადობას ცვლის, ამიტომ, როდესაც ელემენტის ხილვადობის დინამიურად გადართვა გვჭირდება უმჯობესია გამოვიყენოთ v-show დირექტივა, რადგან ამ შემთხვევაში ბრაუზერი შედარებით უფრო სწრაფად დაამუშავებს მოთხოვნას.

თავის მხრივ v-if დირექტივას ის უპირატესობა აქვს, რომ მისი გამოყენება v-else-if და v-else დირექტივებთან ერთადაა შესაძლებელი.

<!DOCTYPE html>
<html>
<head>
    <title>v-show vs. v-if</title>
</head>
<body>

<div id="app">
    <div v-show="showDiv">Div tag with v-show</div>
    <div v-if="showDiv">Div tag with v-if</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                showDiv: true
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
6. Vue v-for დირექტივა
სტანდარტულ ჯავასკრიპტში მასივის მიხედვით HTML ელემენტების დასაგენერირებლად უნდა გამოვიყენოთ for ციკლი, რომლის თითოეულ იტერაციაზეც შევქმნით ახალ ელემენტს, ამასთანავე თუ მასივის მნიშვნელობა შეიცვლება, ეს ცვლილება რეაქტიულად არ აისახება სამომხმარებლო ინტერფეისში.

Vue-ში კი HTML ელემენტს უბრალოდ ვუწერთ v-for დირექტივას, რომელიც დაკავშირებულია Vue-ს ინსტანციაში აღწერილ მასივთან და დანარჩენს Vue აკეთებს. ამგვარად დაგენერირებული ელემენტების ცვლილებაც ავტომატურად ხდება მასივის ცვლილებისას.

სიის გენერირება მასივების მეშვეობით

<!DOCTYPE html>
<html>
<head>
    <title>My first Vue page</title>  
</head>
<body>

<div id="app">
    <ol>
        <!-- შევქმნათ li ელემენტი manyFoods მასივში მოქცეული, x ცვლადით აღნიშნული თითოეული საკვებისათვის -->
        <li v-for="x in manyFoods">{{ x }}</li>
    </ol>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                manyFoods: [
                    'ბანანი',
                    'პიცა',
                    'ნამცხვარი',
                    'თევზი',
                    'ბრინჯი'
                ]
            }
        }
    })

    app.mount('#app')
</script>

</body>
</html>
                

სიის გენერირება ობიექტთა მასივების მეშვეობით

<!DOCTYPE html>
<html>
<head>
    <title>My first Vue page</title>  
</head>
<body>

<div id="app">
    <figure v-for="x in manyFoods">
        <img v-bind:src="x.url">
        <figcaption>{{ x.name }}</figcaption>
    </figure>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                manyFoods: [
                    {name: 'ბანანი', url: 'img_banana.svg'},
                    {name: 'პიცა', url: 'img_pizza.svg'},
                    {name: 'ნამცხვარი', url: 'img_cake.svg'},
                    {name: 'თევზი', url: 'img_fish.svg'},
                    {name: 'ბრინჯი', url: 'img_rice.svg'}
               ]
            }
        }
    })

    app.mount('#app')
</script>

</body>
</html>
                

ინდექსის დადგენა

იმის დასადგენად რა არის მიმდინარე ინდექსი, ანუ მასივის რომელ ელემენტთან ვმუშაობთ კონკრეტულ იტერაციაზე, გამოიყენება შემდეგი სინტაქსი :
<!DOCTYPE html>
<html>
<head>
    <title>My first Vue page</title>  
</head>
<body>

<div id="app">
    <p v-for="(x, index) in manyFoods">
        {{ index }}: "{{ x }}" 
    </p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                manyFoods: [
                    'ბანანი',
                    'პიცა',
                    'ნამცხვარი',
                    'თევზი',
                    'ბრინჯი'
                ]
            }
        }
    })

    app.mount('#app')
</script>

</body>
</html>
                
ინდექსის გასაგები იგივე სინტაქსი გამოიყენება ობიექტთა მასივთან მუშაობისასაც.
7. Vue მოვლენები
Vue-ში მოვლენათა დამუშავება ხდება v-on დირექტივის მეშვეობით, მაგალითად ჩვენ შეგვიძლია შევასრულოთ რაიმე ინსტრუქცია, როდესაც მომხმარებელი დააწვება ღილაკს.
<!DOCTYPE html>
<html>
<head>
    <title>მთვლელი</title>  
</head>
<body>

<div id="app">
    <p>{{ "მთვლელი: " + count }}</p>
    <button v-on:click="count++">მთვლელის გაზრდა</button>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 0
            }
        }
    })
   app.mount('#app')
</script>

</body>
</html>
                
Vue-ში არსებულ ამდაგვარ მიდგომას ის უპირატესობა აქვს სტანდარტულ ჯავასკრიპთან მიმართებაში, რომ ღილაკზე დაჭერისას <p> ელემენტის შიგთავსი ავტომატურად იცვლება, სტანდარტულ ჯავასკრიპტში კი ამ მნიშვნელობის ხელოვნურად გაზრდა დაგვჭირდებოდა, რაც ზედმეტი კოდის წერას გულისხმობს.

ყველაზე გავრცელებული მოვლენებია: 'click', 'mouseover', 'mouseout', 'keydown' and 'input'.

თუ გვსურს, რომ რომელიმე მოვლენისას უფრო რთული ოპერაცია შევასრულოთ ვიდრე უბრალოდ მთვლელის გაზრდაა, მაშინ შეგვიძლია ეს ოპერაცია Vue მეთოდში აღვწეროთ :
<p v-on:click="changeColor">ფერის შეცვლა</p>
                
ამ მეთოდებს მოვლენათა მოდიფიკატორები ეწოდებათ.
8. Vue v-on დირექტივა
v-on დირექტივის მეშვეობით Vue-ში შეგვიძლია ბრაუზერს გადავცეთ თუ რომელ მოვლენას ('click', 'keydown', 'mouseover' და ა.შ) ვამუშავებთ და რა ოპერაციის შესრულება გვინდა ამ მოვლენის მოხდენისას.

onclick მოვლენა

v-on:click მოვლენა ფიქსირდება რომელიმე ელემენტზე მაუსის დაჭერისას.
<!DOCTYPE html>
<html>
<head>
    <title>Light Switch</title>
</head>
<body>

<div id="app">
    <div id="lightDiv">
        <div v-show="lightOn">გამოჩნდეს/დაიმალოს ღილაკზე დაჭერისას</div>
    </div>
    <button v-on:click=" lightOn =! lightOn ">გამოჩენა/დამალვა</button>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                lightOn: false
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                

oninput მოვლენა

v-on:input მოვლენა ფიქსირდება ტექსტურ ველში რომელიმე კლავიშზე დაჭერისას.
<!DOCTYPE html>
<html>
<head>
    <title>Count Input Events</title>  
</head>
<body>

<div id="app">
    <input type="text" v-on:input="inpCount++" placeholder="აკრიფეთ ტექსტი">
    <p>{{ 'მოვლენა დაფიქსირდა: ' + inpCount + '-ჯერ' }}</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                inpCount: 0
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                

mousemove მოვლენა

v-on:mousemove მოვლენა ფიქსირდება როდესაც მაუსს გადავატარებთ ელემენტზე.
<!DOCTYPE html>
<html>
<head>
<title>Change Color</title>
<style>    
    #app > div {
        width: 200px;
        height: 80px;
    }    
</style>
</head>
<body>

<div id="app">
    <div  v-on:mousemove=" colorVal = Math.floor(Math.random()*360) " v-bind:style=" {backgroundColor: 'hsl('+colorVal+',60%,60%)'} "></div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                colorVal: 50
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
v-on დირექტივის შემოკლებული ვარიანტი შემდეგნაირად იწერება :
<button v-on:click=" lightOn =! lightOn ">გამოჩენა/დამალვა</button>

იგივეა რაც :

<button @:click=" lightOn =! lightOn ">გამოჩენა/დამალვა</button>
                
9. Vue მეთოდები
როგორც ადრე აღვნიშნეთ, კონკრეტულ მოვლენებთან კომპლექსური ინსტრუქციების დასაკავშირებლად Vue-ში გამოიყენება ფუნქციები, რომელთა აღწერაც უნდა მოხდეს Vue-ს ინსტანციაში განსაზღვრულ methods თვისებაში.

აქამდე ჩვენ ვიცნობდით მხოლოდ data თვისებას, სადაც კონკრეტული მნიშვნელობების აღწერას ვაკეთებდით. იმისათვის რათა methods თვისებაში აღწერილ ფუნქციაში გამოვიყენოთ data თვისებაში აღწერილი ცვლადი, ამ ცვლადს უნდა დავურთოთ this. პრეფიქსი.

<!DOCTYPE html>
<html>
<head>
    <title>Click To Run Method</title>  
</head>
<body>

<div id="app">
    <p>{{ text }}</p>
    <button v-on:click="changeText">ტექსი</button>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                text: ''
            }
        },
        methods: {
            changeText() {
                this.text = 'Hello World!'
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                

მეთოდის გამოძახება მოვლენის ობიექტის მეშვეობით

როდესაც კონკრეტული მოვლენა ფიქსირდება და შესაბამისად ხდება რომელიმე მეთოდის გამოძახება, ამ მეთოდში შეგვიძლია მივწვდეთ მოვლენის ობიექტს, რაც საკმაოდ მოსახერხებელია, რადგან ეს ობიექტი საკმაოდ ბევრ სასარგებლო ინფორმაციას ინახავს, მაგალითად ინფორმაციას სამიზნე ობიექტის შესახებ (ანუ იმ ობიექტის, რომელზეც მოვლენა გვაქვს მიმაგრებული), ინფორმაციას მოვლენის ტიპის შესახებ და ა.შ.

მოვიყვანოთ მაგალითი სადაც <div> ელემენტზე მივამაგრებთ 'mousemove' მოვლენას, როდესაც ეს მოვლენა დაფიქსირდება მოვახდინოთ 'mousePos' მეთოდის გამოძახება, ამ მეთოდში კი მოვლენის ობიექტის მეშვეობით მივწვდეთ მაუსის კოორდინატებს :

<!DOCTYPE html>
<html>
<head>
<title>Mouse Pointer Position</title>
<style>
    #app {
        border: black dashed 1px;
        width: 200px;
        padding: 0 20px 20px 20px;
    }
    #app > div {
        width: 160px;
        height: 80px;
        background-color: lightcoral;
        padding: 20px;
    }
    #app span {
        font-weight: bold;
        font-family: 'Courier New', Courier, monospace;
    }
</style>
</head>
<body>

<div id="app">
    <div v-on:mousemove="mousePos">
        <span>xPos: {{ xPos }}</span><br><span>yPos: {{ yPos }}</span>
    </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                xPos: 0,
                yPos: 0
            }
        },
        methods: {
            mousePos(event) {
                this.xPos = event.offsetX
                this.yPos = event.offsetY
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
მოვიყვანოთ კიდევ ერთი მაგალითი, სადაც textarea ელემენტს მივამაგრებთ oninput მოვლენას და შესაბამის მეთოდში კი მივწვდებით იგივე ელემენტს ანუ სამიზნე ობიექტს :
<!DOCTYPE html>
<html>
<head>
    <title>სამიზნე ობიექტი</title>  
</head>
<body>

<div id="app">
    <textarea v-on:input="writeText" rows="5"></textarea>
    <div>
        <span>{{ text }}</span>
    </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                text: ''
            }
        },
        methods: {
            writeText(event) {
                this.text = event.target.value
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                

არგუმენტის გადაცემა მეთოდში

რა თქმა უნდა შესაძლებელია, რომ კონკრეტულ მოვლენაზე მიმაგრებულ მეთოდს გადაეცეს არგუმენტიც. მოვიყვანოთ მაგალითი, რომელშიც გვექნება სამი სხვადასხვა ღილაკი, რომლებიც სხვადასხვა მნიშვნელობით გაზრდიან მთვლელს :
<!DOCTYPE html>
<html>
<head>
    <title>addCounter</title>
</head>
<body>

<div id="app">
    <p>{{ count }}</p>
    <button v-on:click="addCounter(1)">+1</button>
    <button v-on:click="addCounter(5)">+5</button>
    <button v-on:click="addCounter(10)">+10</button>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 0
            }
        },
        methods: {
            addCounter(number) {
                this.count+=number // იგივეა რაც this.count=this.count+number 
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                

არგუმენტისა და მოვლენის ობიექტის ერთდროულად გადაცემა მეთოდში

მოვიყვანოთ მაგალითი, რომელშიც მეთოდს ერთდროულად გადავცემთ მოვლენის ობიექტსა და რიგით არგუმენტს:
<!DOCTYPE html>
<html>
<head>
    <title>არგუმენტი + მოვლენის ობიექტი</title>
</head>
<body>

<div id="app">
    <button id="vaso" v-on:click="myMethod($event,'გამარჯობა')">დააჭირეთ ღილაკს</button>
    <p id="green">{{ msgAndId }}</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                msgAndId: ''
            }
        },
        methods: {
            myMethod(e,msg) {
                this.msgAndId =  msg + ' ' // გადაცემული პარამეტრი: 'გამარჯობა'
                this.msgAndId += e.target.id // სამიზნე ელემენტის ანუ ღილაკის id : vaso
                // შედეგი: გამარჯობა vaso
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
10. Vue მოვლენათა მოდიფიკატორები
Vue-ში მოვლენათა მოდიფიკატორები გამოიყენება მოვლენათა შემდგომი პროცესების დამუშავებისათვის, მოდიფიცირებისათვის. მოდიფიკატორები გამოიყენება v-on დირექტივასთან ერთად. მაგალითად შეგვიძლია :

  • თავიდან ავიცილოთ HTML ფორმის გაგზაცნა ღილაკზე დაჭერისას (v-on:submit.prevent)
  • დავრწმუნდეთ, რომ მოვლენა მხოლოდ ერთხელ დაფიქსირდება მას შემდეგ რაც გვერდი ჩაიტვირთება (v-on:click.once)
  • განვსზაღვროთ თუ რომელ კლავიშზე დაჭერისას უნდა გავუშვათ კონკრეტული მეთოდი (v-on:keyup.enter)

ახლა კი მოვიყვანოთ კონკრეტული მაგალითი, ღილაკს მივამაგროთ ფუნქცია, რომელიც გვაჩვენებს შეტყობინებას, ღილაკზე მაუსის მხოლოდ პირველი დაჭერისას :
<!DOCTYPE html>
<html>
<head>
    <title>შეტყობინება</title>  
</head>
<body>

<div id="app">
    <button v-on:click.once="createAlert">Create Alert</button>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        methods: {
            createAlert() {
                alert("შეტყობინება")
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>  
                

კლავიატურის მოვლენათა მოდიფიკატორები

კლავიატურის მოვლენებია : keydown, keypress და keyup. თითოეულ მათგანთან მიმართებაში შესაძებელია განვსაზღვროთ კონკრეტულად რომელ კლავიშზე დაჭერის დამუშავება გვსურს. მაგალითად : .space, .enter, .w, .up და ა.შ.

მიმდინარე კლავიშის დასადგენად უნდა მივმართოთ მოვლენის ობიექტის key თვისებას :

<!DOCTYPE html>
<html>
<head>
    <title>Key</title>  
</head>
<body>

<div id="app">
    <input type="text" v-on:keydown="getKey">
    <p>{{ keyValue }}</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                keyValue: ''
            }
        },
        methods: {
            getKey(e) {
                this.keyValue = e.key
                console.log(e.key)
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
მოვიყვანოთ .s მოდიფიკატორის გამოყენების მაგალითი, თუ მომხმარებელი დააწვება 's' ღილაკს, გამოვიტანოთ შეტყობინება :
<!DOCTYPE html>
<html>
<head>
    <title>S</title>  
</head>
<body>

<div id="app">
    <textarea v-on:keydown.s="createAlert"></textarea>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        methods: {
            createAlert() {
                alert("თქვენ დააჭირეთ S ღილაკს");
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                

კლავიატურის მოვლენათა კომბინირებული მოდიფიკატორები

შესაძლებელია კლავიშთა კომბინაციების დაფიქსირებაც, მაგალითისათვის გამოვიძახოთ 'createAlert' ფუნქცია, როდესაც მომხმარებელი დააწვება Ctrl და S კლავიშებს :
<!DOCTYPE html>
<html>
<head>
    <title>Ctrl + S</title>  
</head>
<body>

<div id="app">
    <textarea v-on:keydown.ctrl.s="createAlert"></textarea>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        methods: {
            createAlert(evt) {
                alert("თქვენ აკრიფეთ 'Ctrl + S' კომბინაცია")
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                

მაუსის მოვლენათა მოდიფიკატორები

როგორც ვიცით მაუსის მოვლენებთან სამუშაოდ გამოიყენება v-on:click დირექტივა, მოდიფიკატორები : .left, .center და .right კი გამოიყენება იმის მიხედევით თუ მაუსის რომელ ღილაკს დააწვება მომხმარებელი.

მოვიყვანოთ მაგალითი : თუ მომხმარებელი კონკრეტულ ელემენტზე დააწვება მარჯვენა მაუსს, გამოვიტანოთ შეტყობინება :

<!DOCTYPE html>
<html>
<head>
<title>მარჯვენა მაუსი</title>
<style>    
    #app > div {
        width: 160px;
        padding: 20px;
        cursor: default;
        font-weight: bold;
        border: black dashed 1px;
    }
</style>
</head>
<body>

<div id="app">
    <div v-on:click.right="createAlert">
        <p>დააჭირეთ მარჯვენა მაუსს აქ</p>
    </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        methods: {
            createAlert() {
                alert('შეტყობინება')
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                

როგორც ვიცით, მარჯვენა მაუსსე დაჭერისას ოპერაციულ სისტემას, ნაგულისმეობის პრინციპით გამოაქვს ჩამოსაშლელი მენიუ, თუ ამ მენიუს დამალვა გვსურს, უნდა გამოვიყენოთ .prevent მოდიფიკატორი:

<!DOCTYPE html>
<html>
<head>
<title>მარჯვენა მაუსი</title>
<style>    
    #app > div {
        width: 160px;
        padding: 20px;
        cursor: default;
        font-weight: bold;
        border: black dashed 1px;
    }
</style>
</head>
<body>

<div id="app">
    <div v-on:click.right.prevent="createAlert">
        <p>დააჭირეთ მარჯვენა მაუსს აქ</p>
    </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        methods: {
            createAlert() {
                alert('შეტყობინება')
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
მაუსის მოვლენების გამოყენება შესაძლებელია სისტემურ კლავიშებთან ერთადაც. მოვიყვანოთ მაგალითი, რომელშიც გამოვიტანთ შეტყობინებას თუ მომხმარებელი დააწვება Ctrl ღილაკსა და მარჯვენა მაუსს :
<!DOCTYPE html>
<html>
<head>
<title>Ctrl და მარჯვენა მაუსი</title>
<style>    
    #app > div {
        width: 160px;
        padding: 20px;
        cursor: default;
        font-weight: bold;
        border: black dashed 1px;
    }
</style>
</head>
<body>

<div id="app">
    <div v-on:click.right.ctrl="createAlert">
        <p>დააჭირეთ Ctrl ღილაკსა და მარჯვენა მაუსს აქ</p>
    </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        methods: {
            createAlert() {
                alert('შეტყობინება');
            }
       }
    })
    app.mount('#app')
</script>

</body>
</html>
                
11. Vue ფორმები
შევეცადოთ შევქმნათ საყიდლების სიაში პროდუქტის დასამატებელი მარტივი ფორმა. დავიწყოთ სტანდარტული HTML ფორმით :
<form>
    <p>პროდუქტი: <input type="text" required></p>
    <p>რაოდენობა: <input type="number"></p>
    <button type="submit">დამატება</button>
</form>
                
შემდეგ შევქმნათ Vue-ს ინსტანცია, რომელშიც აღვწერთ პროდუქტის დასახელების, პროდუქტის რაოდენობის და სიაში არსებული მიმდინარე პროდუქტის შესაბამის თვისებებს. HTML ფორმის ველებში კი გამოვიყენოთ v-model დირექტივა, ეს საჭიროა იმისათვის, რომ ეს ველები დაკავშირდნენ Vue-ს ინსტანციასთან (v-model დირექტივის შესახებ დაწვრილებით ვისაუბრებთ შემდეგ თავში).
<div id="app">
    <form>
        <p>პროდუქტი: <input type="text" required v-model="itemName"></p>
        <p>რაოდენობა: <input type="number" v-model="itemNumber"></p>
        <button type="submit">დამატება</button>       
    </form>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                itemName: null,
                itemNumber: null,
                shoppingList: [
                    { name: 'პური', number: 5 }
                ]
            }
        }
    })
    app.mount('#app')
</script>
                
ამის შემდეგ მოვახდინოთ სიაში ახალი პროდუქტის დასამატებელი ფუნქციის გამოძახება და ასევე თავიდან ავიცილოთ გვერდის გადატვირთვა ფორმის გაგზავნის შემდეგ :
<form v-on:submit.prevent="addItem">
                
ახლა შევქმნათ მეთოდი, რომელიც სიაში ახალ პროდუქტს დაამატებს და დამატების შემდეგ გაასუფთავებს ფორმის ველებს :
methods: {
    addItem() {
        let item = {
            name: this.itemName,
            number: this.itemNumber
        }
        this.shoppingList.push(item);
        this.itemName = null
        this.itemNumber = null
    }
}
                
ახლა v-for დირექტივის მეშვეობით გამოვიტანოთ საყიდლების სია:
<ul>
    <li v-for="item in shoppingList">{{item.name}}, {{item.number}}</li>
</ul>
                
ყველაფერს თუ გავაერთიანებთ, მივიღებთ შემდეგ სურათს:
<!DOCTYPE html>
<html>
<head>
  <title>საყიდლების სია</title>  
</head>
<body>

<div id="app">
    <form v-on:submit.prevent="addItem">
        <p>პროდუქტი: <input type="text" required v-model="itemName"></p>
        <p>რაოდენობა: <input type="number" v-model="itemNumber"></p>
        <button type="submit">დამატება</button>       
    </form>
    <ul>
        <li v-for="item in shoppingList">{{item.name}}, {{item.number}}</li>
    </ul>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                itemName: null,
                itemNumber: null,
                shoppingList: [
                    { name: 'პური', number: 5 }
                ]
            }
        },
        methods: {
            addItem() {
                let item = {
                    name: this.itemName,
                    number: this.itemNumber
                }
                this.shoppingList.push(item);
                this.itemName = null
                this.itemNumber = null
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>


                
შევნიშნოთ, რომ v-model დირექტივის მეშვეობით HTML ფორმასა და Vue-ს ინსტანციას შორის დამყარდა ორმხრივი კავშირი (two-way binding) : როდესაც ფორმის ველებს ვავსებთ, ინფორმაცია იცვლება Vue-ს ინსტანციშიც და პირიქით - როდესაც Vue-ს ინსტანციში გავასუფთავეთ მონაცემები, გასუფთავდა ფორმის ელემენტებიც.
12. Vue v-model დირექტივა
v-model ამყარებს კავშირს HTML ფორმის ველების value ატრიბუტების მნიშვნელობებსა (ანუ რაც არის აკრეფილი ველებში) და Vue-ს ინსტანციაში აღწერილ ინფორმაციას შორის. როდესაც ველებში აკრეფილი შიგთავსი იცვლება, იცვლება ინსტამციაში აღწერილი მნიშვნელობებიც და პირიქით.
<!DOCTYPE html>
<html>
<head>
    <title>Two-way Binding</title>
</head>
<body>

<div id="app">
    <input type="text" v-model="inpText">
    <p id="text">inpText value: "{{ inpText }}" </p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                inpText: 'საწყისი ტექსტი'
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
თუ ამ კოდის შედეგად გამოტანილ ფორმაში არსებულ ტექსტურ ველს დავაკვირდებით, შევამჩნევთ, რომ მასში ჩაწერილია Vue-ს ინსტანციაში აღწერილი inpText თვისების მნიშვნელობა, ხოლო თუ ამ ველში ახალ ტექსტს ჩავწერთ, შევამჩნევთ, რომ <p id="text"> ელემენტის შიგთავსიც შეიცვლება.

დინამიური checkbox ველი

საყიდლების სიის ფორმას დავამატოთ დინამიური checkbox ველი, რომელიც განსაზღვრავს პრიორიტეტულია თუ არა კონკრეტული პროდუქტის ყიდვა ჩვენი სიიდან.
<!DOCTYPE html>
<html>
<head>
<title>საყიდლების სია</title>  
<style>
    #app {
        border: dashed black 1px;
        display: inline-block;
        padding: 0 20px;
    }
    ul {
        list-style-type: none;
    }
    li {
        border-radius: 5px;
        padding: 5px;
        margin: 2px;
        background-color: rgb(211, 254, 211);
    }
    .impClass {
        background-color: rgb(255, 202, 202);
        font-weight: bold;
    }
</style>
</head>
<body>

<div id="app">
    <form v-on:submit.prevent="addItem">
        <p>პროდუქტი: <input type="text" required v-model="itemName"></p>
        <p>რაოდენობა: <input type="number" v-model="itemNumber"></p>
        <p>
            პრიორიტეტულია ?
            <label>
                <input type="checkbox" v-model="itemImportant">
                {{ itemImportant }}
            </label>
        </p>
        <button type="submit">დამატება</button>       
    </form>
    <ul>
        <li v-for="item in shoppingList" v-bind:class="{ impClass: item.important }">
            {{item.name}}, {{item.number}}
        </li>
    </ul>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                itemName: null,
                itemNumber: null,
                itemImportant: false,
                shoppingList: [
                    { name: 'პური', number: 5, important: false  }
                ]
            }
        },
        methods: {
            addItem() {
                let item = {
                    name: this.itemName,
                    number: this.itemNumber,
                    important: this.itemImportant
                }
                this.shoppingList.push(item)
                this.itemName = null
                this.itemNumber = null
                this.itemImportant = false
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
განვიხილოთ ეს კოდი. პირველ რიგში, <style> ტეგის მეშვეობით ოდნავ შევალამაზეთ ჩვენი ფორმა, ასევე აღვწერეთ კლასი impClass, რომელსაც აქვს მოწითალო ფონი და რომელიც ჩამონათვალში არსებულ ელემენტებს მიენიჭებათ მაშინ, თუ მიმდინარე პროდუქტის important თვისების მნიშვნელობა იქნება 'true'.

ასევე აღვწერეთ დინამიური checkbox ველი, რომელიც დაკავშირებულია Vue-ს ინსტანციაში აღწერილ itemImportant თვისებასთან.

საყიდლების სიას დავამატოთ შემდეგი ფუნქციონალი : თუ მომხმარებელი შეიძენს რომელიმე პროდუქტს, შეეძლოს მისი შეძენილად მონიშვნა და შეძენილების სიაში გადატანა ამ პროდუქტზე მაუსის დაჭერით. შეძენილი პროდუქტები ჩაიყაროს შეძენილების სიაში, ხოლო ის პროდუქტი, რომელიც ჯერ არ შეუძენია დარჩეს საყიდლების სიაში.

ორივე სიაში პროდუქტი გამოჩნდება ისევ v-show დირექტივის მიხედვით, რომელიც დამოკიდებული იქნება პროდუქტის ახალი თვისების - found-ის მნიშვნელობაზე:

<!DOCTYPE html>
<html>
<head>
<title>საყიდლების სია</title>  
<style>
    #app {
        border: dashed black 1px;
        display: inline-block;
        padding: 0 20px;
    }    
    ul {
        list-style-type: none;
    }
    li {
        margin: 2px;
        background-color: rgb(211, 254, 211);
    }
    .impClass {
        background-color: rgb(255, 202, 202);
    }
    #ulFound li {
        text-decoration: line-through;
        background-color: rgb(230,230,230);
    }
</style>
</head>
<body>

<div id="app">
    <form v-on:submit.prevent="addItem">
        <p>პროდუქტი: <input type="text" required v-model="itemName"></p>
        <p>რაოდენობა: <input type="number" v-model="itemNumber"></p>
        <p>
            პრიორიტეტულია ?
            <label>
                <input type="checkbox" v-model="itemImportant">
                {{ itemImportant }}
            </label>
        </p>
        <button type="submit">დამატება</button>       
    </form>
    <ul id="ulToFind">
        <li 
            v-for="item in shoppingList" 
            v-bind:class="{ impClass: item.important }"
            v-on:click="item.found=!item.found"
            v-show="!item.found"
        >
            {{ item.name }}, {{ item.number}}
        </li>
    </ul>
    <ul id="ulFound">
        <li 
            v-for="item in shoppingList" 
            v-bind:class="{ impClass: item.important }"
            v-on:click="item.found=!item.found"
            v-show="item.found"
        >
            {{ item.name }}, {{ item.number}}
        </li>
    </ul>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                itemName: null,
                itemNumber: null,
                itemImportant: false,
                shoppingList: [
                    { name: 'პური', number: 5, important: false, found: true },
                    { name: 'ყველი', number: 5, important: true, found: false  },
                    { name: 'ქარაქი', number: 5, important: false , found: false }
                ]
            }
        },
        methods: {
            addItem() {
                let item = {
                    name: this.itemName,
                    number: this.itemNumber,
                    important: this.itemImportant,
                    found: false
                }
                this.shoppingList.push(item)
                this.itemName = null
                this.itemNumber = null
                this.itemImportant = false
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>          
                
მოვიყვანოთ დინამიური ფორმის მაგალითი, რომლის მეშვეობითაც მომხმარებელს შეეძლება კატეგორიების მიხედვით აირჩიოს და შეუკვეთოს პროდუქტი :
<!DOCTYPE html>
<html>
<head>
  	<title>შეკვეთა მენიუდან</title>  
</head>
<body>

<div id="app">

    <!-- ღილაკზე დაჭერისას არ ვახდენთ გვერდის ხელახლა ჩატვირთვას, ვიძახებთ Vue-ს ინსტანციაში აღწერილ addItem მეთოდს -->
    <form v-on:submit.prevent="addItem">

        <!-- 
            პროდუქტის ტიპები, რომლებიც უკავშირდებიან Vue-ს ინსტანციაში აღწერილ itemType თვისებას,
            რომელიც თავდაპირველად არის ცარიელი, შესაბამისად, გვერდის ჩატვირთვისას select ჩამოსაშლელი მენიუც არ ჩანს
        -->
        <p>
          	<h4>შეუკვეთეთ:</h4>
            <label>
                <input type="radio" required value="Dinner" v-model="itemType">სადილი
            </label><br>
            <label>
                <input type="radio" required value="Drink" v-model="itemType">სასმელი
            </label><br>
            <label>
                <input type="radio" required value="Dessert" v-model="itemType">დესერტი
            </label>
        </p>

        <!-- გამოჩნდება მას შემდეგ რაც მომხმარებელი აირჩევს პროდუქტის ტიპს -->
        <p v-show="itemType">
            <label>
                <!-- პროდუქტის დასახელება, უკავშირდება Vue-ს ინსტანციაში აღწერილ itemName თვისებას -->
                <select required v-model="itemName">
                    <option value="" selected disabled>აირჩიეთ პროდუქტი</option>
                    <!-- 
                        გენერირდება Vue-ს ინსტანციაში აღწერილი, წინასწარგანსაზღვრული preDefItems მასივის მიხედვით.
                        მიმდინარე option ჩნდება იმ შემთხვევაში თუ პროდუქტის ტიპი უდრის მომხმარებლის მიერ არჩეულ პროდუქტის ტიპს
                    -->
                    <option v-for="item in preDefItems" v-bind:value="item.name" v-show="item.type===itemType">
                      	{{ item.name }}
                    </option>
                </select>
            </label>
        </p>

        <!-- 
            ჩნდება იმ შემთხვევაში თუ მომხმარებელი ირჩევს პროდუქტს,  
            უკავშირდება Vue-ს ინსტანციაში აღწერილ itemNumber თვისებას 
        -->
        <p v-show="itemName">
          	<input type="number" placeholder="რაოდენობა" v-model="itemNumber" required>
        </p>

        <button type="submit">შეკვეთა</button>

    </form>

    <br>
    <hr>

    <div>
        <h4>თქვენი შეკვეთა:</h4>
        <!-- 
            შეკვეთილი პროდუქტის სია, გენერირდება იმ შემთხვევაში თუ მომხმარებელი ერთ პროდუქტს მაინც შეუკვეთავს.
            გენერირდება Vue-ს ინსტანციაში აღწერილი order მასივის მიხედვით, რომელიც დავდაპირველად ცარიელია,
            შესაბამისად - გვერდის ჩატვირთვისას ეს სია არ ჩანს
        -->
        <ul>
        	<li v-for="item in order">
                {{ item.name }}, {{ item.number}}
            </li>
        </ul>
    </div>

</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                itemType: '', // პროდუქტის ტიპი
                itemName: '', // პროდუქტის დასახელება
                itemNumber: null, // პროდუქტის პროდუქტის რაოდენობა
                // პროდუქტის სრული სია
                preDefItems: [
                    { name: 'ხაჭაპური', type: 'Dinner' },
                    { name: 'წვნიანი', type: 'Dinner' },
                    { name: 'თევზი', type: 'Dinner' },
                    { name: 'ნამცხვარი', type: 'Dessert' },
                    { name: 'კოკა-კოლა', type: 'Drink' },
                    { name: 'ვისკი', type: 'Drink' },
                    { name: 'ნაყინი', type: 'Dessert' },
                    { name: 'ლიმონათი', type: 'Drink' },
                    { name: 'წყალი', type: 'Drink' }
                ],
                order: [] // შეკვეთილი პროდუქტი
            }
        },
        methods: {
            // კონკრეტული პროდუქტის შეკვეთა
            addItem(){
                let item = {
                    name: this.itemName,
                    number: this.itemNumber,
                }
                this.order.push(item)
                this.itemType = ''
                this.itemName = ''
                this.itemNumber = null  
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
13. Vue CSS მიმაგრება
Vue-ში ელემენტებისათვის სტილების მინიჭებისა და შესაბამისად იერსახის ცვლილების რამოდენიმე გზა არსებობს.

ხაზსშიდა სტილები (inline styles)

ხაზსშიდა სტილების განსასაზღვრავად გამოიყენება v-bind:style დირექტივა :
<!DOCTYPE html>
<html>
<head>
<title>Opacity v-bind</title>
<style>
    #app > div {
        position: relative;
        border: dashed black 1px;
        width: 200px;
        height: 100px;
    }
    #onTop {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        padding: 20px;
        box-sizing: border-box;
        z-index: 2;
        color: white;
    }
</style>
</head>
<body>

<div id="app">
    <p>
        <input type="range" min="0" max="1" step="0.1" v-model="opacityVal"> {{ opacityVal }} (opacity)
    </p>
    <div>
        <!-- შევნიშნოთ რომ background-color თვისება ჩაწერილია ე.წ camelCase სტილში და არა სტანდარტულად -->
        <div id="onTop" v-bind:style="{ backgroundColor: 'rgba(99,0,89,' + opacityVal + ')' }">
        </div>
    </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                opacityVal: 0.8
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                

კლასების მინიჭება

ელემენტებზე კლასების მისანიჭებლად გამოიყენება v-bind:class დირექტივა :
<!DOCTYPE html>
<html>
<head>
<title>Select images</title>
<style>
    #app {
        display: flex;
        flex-wrap: wrap;
        justify-content: space-around;
        border: dashed black 1px;
    }
    #app > div {
        display: block;
        flex-basis: 80px;
        aspect-ratio: 1;
        margin: 5px;
        border: 1px solid grey;
    }
    #app > div > div {
        box-sizing: border-box;
        width: 100%;
        height: 100%;
        padding: 3px;
        border: solid white 4px;
        cursor: pointer;
        display: flex;
        justify-content: center;
        align-items: center;
    }    
    .selClass {
        border: solid brown 4px;
        background-color: lightpink;
    }
</style>
</head>
<body>

<div id="app">
    <div v-for="(box, index) in boxes">
        <!-- selClass მიენიჭება იმ შემთხვევაში თუ მიმდინარე box-ის sale ატრიბუტის მნიშვნელობა იქნება true -->
      	<div v-on:click="select(index)" v-bind:class="{selClass: box.sel}">
            {{ box.title }}
        </div>
    </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                boxes: [
                    {title: '1', sel: false},
                    {title: '2', sel: false},
                    {title: '3', sel: false},
                    {title: '4', sel: false},
                    {title: '5', sel: false},
                    {title: '6', sel: false},
                ]
            }
        },
        methods: {
            select(number) {
                <!-- ინდექტის მიხედვით ნაპოვნ box-ს ელემენტს sel ატრიბუტის მნიშვნელობა შევუცვალოთ ასრსებულის საპირისპიროთი -->
                this.boxes[number].sel = !this.boxes[number].sel
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>    
                

კლასებისა და სტილების მინიჭების სხვა მეთოდები

  1. როდესაც HTML ელემენტზე მიმაგრებულია კლასი class="" ჩანაწერითაც და v-bind:class="" დირექტივითაც, Vue ახდენს ამ კლასების შერწყმას.
  2. v-bind:class="{}" დირექტივის ობიექტში შესაძლებელია რამდენიმე კლასის ერთდროულად მითითება.
  3. ხაზსშიდა სტილებში CSS თვისების მითითებისას ჩვენ გამოვიყენეთ ე.წ 'camelCase', თუმცა შესაძლებელია ე.წ 'kebab-case' სტილის გამოყენებაც თუ თვისების დასახელებას მოვაქცევთ ბრჭყალებში.
  4. კლასების მითითება შესაძლებელია მასივების მეშვეობითაც.

'class' და 'v-bind:class' ჩანაწერების შერწყმა

<div class="impClass" v-bind:class="{yelClass: isYellow}">
    ამ ელემენტს ექნება ორივე კლასი - 'impClass' და 'yelClass' 
</div>
                

რამოდენიმე კლასის ერთდროულად მინიჭება v-bind:class დირექტივით

<div v-bind:class="{yelClass: isYellow, impClass: isImportant}">
    ამ ელემენტს ექნება ორივე კლასი - 'impClass' და 'yelClass'
</div>
                

camelCase vs kebab-case

<div v-bind:style="{ backgroundColor: 'lightpink', 'font-weight': 'bolder' }">
    ელემენტს ექნება ვარდისფერი ფონი და მუქი ტექსტი.
</div>
                

მასივები v-bind:class დირექტივაში

<div v-bind:class="[{ impClass: isImportant }, 'yelClass' ]">
    
</div>
                
14. Vue გამოთვლადი თვისებები
გამოთვლადი თვისებები წააგავნან იმ თვისებებს, რომლებსაც აქამდე Vue-ს ინსტანციის 'data' თვისებაში ვწერდით, განსხვავება ისაა, რომ მათი მნიშვნელობა დამოკიდებულია სხვა თვისებებზე.

გამოთვლადი თვისებების აღწერა ხდება მეთოდების მსგავსად, უბრალოდ მათ არ გადაეცემათ HTML ფორმის ველებიდან მიღებული მნიშვნელობები.

გამოთვლადი თვისების დადებითი მხარე არის ის, რომ იგი დინამიურია ანუ ავტომატურად იცვლება იმ თვისებების ცვლილებისას, რომლებზეც თავად ეს გამოთვლადი თვისებაა დამოკიდებული.

გამოთვლადი თვისებების აღწერა ხდება Vue-ს ინსტანციის computed კონფიგურაციულ პარამეტრში :
const app = Vue.createApp({
    data() {
        ...
    },
    computed: {
        ...
    },
    methods: {
        ...
    }
})
                
მოვიყვანოთ გამოთვლადი თვისების გამოყენების მარტივი მაგალითი : დავუშვათ გვაქვს type="checkbox" ღილაკი, რომელიც პასუხობს კითხვაზე - 'კარგი ამინდია ?', როგორც ვიცით ამდაგვარი ღილაკი ღებულობს ორი შესაძლო მნიშვნელობიდან ერთ-ერთს, ეს მნიშვნელობებია 'true' და 'false', ასევე შევთანხმდეთ იმაზე, რომ მომხმარებლისთვის უფრო კომფორტულია კითხვაზე პასუხად გავცეთ 'დიახ' ან 'არა' ვიდრე 'true' და 'false' :)) ამიტომ აღვწეროთ გამოთვლადი თვისება isGoodWeather, რომელიც დამოკიდებული იქნება, Vue-ს ინსტანციაში აღწერილ და type="checkbox" ღილაკთან დაკავშირებულ chbxVal თვისებასთან :
<!DOCTYPE html>
<html>
<head>
    <title>ამინდი</title>
</head>
<body>
<div id="app">
    <form>
        <p>
            კარგი ამინდია ?
            <label>
                <input type="checkbox" v-model="chbxVal"> 
                {{ isGoodWeather }}
            </label>
        </p>
    </form>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                chbxVal: false
            }
        },
        computed: {
            isGoodWeather() { 
                if(this.chbxVal){
                    return 'დიახ'
                }
                else {
                    return 'არა'
                }
            }            
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
მაშ ასე : ღილაკის გადართვა/გადმორთვა ცვლის chbxVal თვისების მნიშვნელობას (true ან false), chbxVal თვისების მნიშვნელობის მიხედვით კი გენერირდება isGoodWeather გამოთვლადი თვისების მნიშვნელობა ('დიახ' ან 'არა'), რაც ნიშნავს იმას, რომ isGoodWeather თვისების მნიშვნელობა დამოკიდებულია chbxVal თვისების მნიშვნელობაზე.
15. Vue დამკვირვებლები
დამკვირვებლების გამოყენება მოსახერხებელია მაშინ, როდესაც გვსურს თვალი ვადევნოთ Vue-ს ინსტანციაში აღწერილი პარამეტრების ცვლილებას. დამკვირვებლების აღწერა ხდება Vue-ს ინსტანციის watch კონფიგურაციულ პარამეტრში :
const app = Vue.createApp({
    data() {
        ...
    },
    watch: {
        ...
    },
    computed: {
        ...
    },
    methods: {
        ...
    }
})
                
მაგალითად გვაქვს <input type="range"> ველი და იმ შემთხვევაში თუ მისი მნიშვნელობა იქნება 20-ზე მეტი, გვსურს შეტყობინების გამოტანა :
<!DOCTYPE html>
<html>
<head>
    <title>დამკვირვებელი</title>
</head>
<body>

<div id="app">
    <input type="range" min="0" max="100" step="1" v-model="rangeVal">
    <p>{{ rangeVal }}</p> 
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                rangeVal: 0
            }
        },
        watch: {
            rangeVal(val) {
                if( val > 20 ){
                    alert('შეტყობინება')
                }
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                

დამკვირვებლები ძველი და ახალი მნიშვნელობებით

დამკვირვებელის მეშვეობით შესაძლებელია მომხმარებლის მიერ აკრეფილი ახალი და ძველი მნიშვნელობების დაჭერაც:
<!DOCTYPE html>
<html>
<head>
    <title>ძველი და ახალი მნიშვნელობები</title>
</head>
<body>

<div id="app">
    <label>
        <input type="email" v-model="inpAddress">
    </label>
    <p>{{ feedbackText }}</p> 
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                inpAddress: '',
                feedbackText: ''
            }
        },
        watch: {
            inpAddress(newVal,oldVal) {
                if(!newVal.includes('@')){
                    this.feedbackText = 'არასწორი ელ_ფოსტა';
                }
                else if(!oldVal.includes('@') && newVal.includes('@')) {
                    this.feedbackText = 'კიბატონო, ძაღლუკა უკვე გვყავს ^_^';
                }
                else {
                    this.feedbackText = 'ელ_ფოსტის მისამართი სწორია';
                }
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                

დამკვირვებლები vs გამოთვლადი თვისებები

  • მეთოდების გამოძახება ხდება HTML ფორმიდან.
  • მეთოდი შეიძლება გამოძახებულ იქნას მაშინაც როცა დაფიქსირდება კონკრეტული მოვლენა.
  • მეთოდს ავტომატურად გადაეცემა მოვლენის ობიექტი.
  • მეთოდს შეგვიძლია გადავცეთ დამატებითი პარამეტრებიც.
  • დამკვირვებლის გამოძახება ხდება მხოლოდ მაშინ, როდესაც იცვლება ის ინფორმაცია რომელსაც ეს კონკრეტული დამკვირვებელი აკვირდება.
  • დამკვირვებლები ავტომატურად იღებენ დაკვირვების ობიექტის ახალ და ძველ მნიშვნელობებს.
  • ჩვენ არ შეგვიძლია გადავცეთ დაკვირვების ობიექტს რაიმე დამატებითი არგუმენტები გარდა დაკვირვების ობიექტის ახალი და ძველი მნიშვნელობებისა.

დამკვირვებლები vs მეთოდები

  • დამკვირვებლის მუშაობა დამოკიდებულია მხოლოდ ერთ კონკრეტულ თვისებაზე.
  • გამოთვლადი თვისება შეიძლება დამოკიდებული იყოს რამოდენიმე თვისებაზე.
16. Vue შაბლონები
Vue-ში შაბლონი ეწოდება აპლიკაციის იმ ნაწილს, რომელშიც HTML-ია აღწერილი. შემდეგ თავებში ვნახავთ, რომ შაბლონებთან სამუშაოდ გამოიყენება <template> ტეგი. შაბლონი გვეხმარება კოდის სტრუქტურის უკეთესად გამართვაში.

შაბლონის აღწერა ხდება Vue-ს ინსტანციის template კონფიგურაციულ პარამეტრში, შაბლონის შიგთავსი უნდა ჩაისვას `` ბრჭყალებში :

<!DOCTYPE html>
<html>
<head>
    <title>შაბლონი</title>  
</head>
<body>

<div id="app"></div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        template: 
        `<h1>{{ message }}</h1>
        <p>HTML შაბლონის მეორე ხაზი</p>`,
        data() {
            return {
                message: "გამარჯობა!"
            }
        }
    })
    app.mount('#app')
</script>

</body>
</html>
                
17. *.vue გაფართოების ფაილები
*.vue გაფართოების ფაილების გამოყენებას აქვს შემეგი უპირატესობები :
  • დიდ პროექტებზე მუშაობისას, ის სქემა, რომლითაც აქამდე ვმუშაობდით, საკმაოდ მოუხერხებელია, უფრო მეტიც - შეუძლებელია ამგვარად მუშაობა. ასეთ პროეტებში დაგვჭირდება შაბლონებისა და კომპონენტების გამოყენება.
  • ბრაუზერში გვერდის შიგთავსი ავტომატურად განახლდება მას შემდეგ, რაც ედიტორში შევინახავთ ბოლო ცვლილებებს.
  • დღესდღეობით ეს მიდგომაა ყველაზე რეკომენდებული და მიზანშეწონილი, ასე ვთქვათ - თანამედროვე სტანდარტი.
*.vue გაფართოების ფაილებს სხვაგვარად შეიძლება ვუწოდოთ ფაილური კომპონენტები (SFC - Single File Components) და თითოეული მათგანი პასუხისმგებელი იქნება სამომხმარებლო ინტერფეისის კონკრეტული ფრაგმენტის ანუ კომპონენტის გამართულად მუშაობაზე. თუმცა უნდა აღინიშნოს, რომ ამ ფაილების გასაშვებად საჭიროა სამუშაო გარემოს მოწყობა, მათი პირდაპირ ბრაუზერში გახსნა შეუძლებელია. ამ გარემოს მოწყობაში დაგვეხმარება Vite.
Vite არის ერთგვარი ხელსაწყო, ინსტრუმენტი, რომლის მეშვეობითაც ხდება საბაზისო შაბლონისა და სამუშაო გარემოს გამზადება სამომხმარებლო მხარის (front-end) ფრეიმვორკებსა და ბიბლიოთეკებთან მუშაობისას.

1. ედიტორის ინსტალაცია

არსებობს ბევრი სხვადასხვა ედიტორი, თუმცა რეკომენდებულია გამოვიყენოთ VS Code ედიტორი.

2. Volar გაფართოება

VS Code ედიტორთან მუშაობისას სასურველია დავაყენოთ Volar გაფართოება, რომელიც Vue-ში მუშაობას ხდის უფრო კომფორტულს.

3. Node.js

Node ანუ Node.js — არის V8 (ავტ. Google) ძრავზე დაფუძნებული პროგრამული პლატფორმა, რომელიც JavaScript-ს, როგორც სპეციალიზირებულ ენას, გარდაქმნის პროგრამირების სრულფასოვან და საერთო დანიშნულების ენად. ამ პლატფორმაზე დაყრდნობით ჯს შეგვიძლია გამოვიყენოთ სერვერული მხარის სამუშაოებშიც.
Node.js ის გადმოწერა შესაძლებელია ოფიციალური ვებ-გვერდიდან. ინსტალაცია მიმდინარეობს სტანდარტულად, ყოველგვარი სირთულეების გარეშე. იმისათვის რათა დავრწმუნდეთ, რომ ინსტალაციამ წარმატებით ჩაიარა, ბრძანებათა ველიდან (CMD) გავუშვათ შემდეგი ბრძანება :
node -v
                

4. გადავინაცვლოთ სასურველ საქაღალდეში და გავუშვათ შემდეგი ბრძანება :

npm init vue@latest
                
npm - Node.js Package Manager ანუ Node.js პაკეტების მენეჯერი. პაკეტის მიღმა იგულისხმება იმ ფაილების ნაკრებები, რომლებიც გვჭირდება კონკრეტული მოდულის შესაქმნელად.
npm-ის ვერსიის გასაგებად ბრძანებათა ველიდან გავუშვათ შემდეგი ბრძანება :
npm -v
                




დავაჭიროთ 'enter' ღილაკს შემდეგ ავკრიფოთ ჩვენი პირველი პროექტის დასახელება - firstsfc, ისევ დავაჭიროთ 'enter' ღილაკს.

5. ყველგან მივუთითოთ პასუხი 'No' :


ამის შემდეგ თანმიმდევრობით გავუშვათ შემდეგი ბრძანებები :
cd firstsfc
                
npm install
                
npm run dev
                
ბოლოს კი ვესტუმროთ მისამართს, რომელსაც ტერმინალი გამოგვიტანს :

პროექტის სტრუქტურა

პროექტის საწყისი სტრუქტურა იქნება ამდაგვარი :

main.js
main.js ფაილის მეშვეობით Vite გებულობს თუ როგორ დააგენერიროს პროექტი App.vue ფაილზე დაყრდნობით. ეს დაახლოებით იგივეა, როდესაც ჩვენ CDN ბმულით ვატყობინებდით ბრაუზერს თუ როგორ გაეშვა ჩვენი Vue კოდი და თუ როგორ შეექმნა Vue-ს ინსტანცია <div id="app"> ელემენტზე დაფუძნებით.
App.vue
ყველა სხვა *.vue ფაილის მსგავსად ეს ფაილიც შედგება სამი ნაწილისაგან : <script>, <template> და <style>. თითოეულ მათგანს ოდნავ მოგვიანებით განვიხილავთ უფრო დაწვრილებით.
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>

<template>
    <header>
        <img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
        <div class="wrapper">
            <HelloWorld msg="You did it!" />
        </div>
    </header>
    <main>
        <TheWelcome />
    </main>
</template>

<style scoped>
header {
    line-height: 1.5;
}

.logo {
    display: block;
    margin: 0 auto 2rem;
}

@media (min-width: 1024px) {
    header {
        display: flex;
        place-items: center;
        padding-right: calc(var(--section-gap) / 2);
    }

    .logo {
        margin: 0 2rem 0 0;
    }

    header .wrapper {
        display: flex;
        place-items: flex-start;
        flex-wrap: wrap;
    }
}
</style>
                
როგორც ვხედავთ, '<script>' სექციაში დაიმპორტებულია სხვა *.vue ფაილები, ამ ფაილებს კომპონენტები ეწოდებათ და ინახებიან /src/components საქაღალდეში.

'<template>' სექციას თუ დავაკვირდებით შევამჩნევთ <HelloWorld> and <TheWelcome> ტეგებს, რომლებიც სტანდარტული HTML ტეგები არ არიან, სწოერდ ამგვარად ხდება კომპონენტების გამოძახება. მარტივი ენით თუ ვიტყვით - კომპონენტი ეს არის აპლიკაცია აპლიკაციაში, კომპონენტების შესახებ ვისაუბრებთ შემდეგ თავებში.

მაშ ასე, შევუდგეთ საქმეს :) ყველაფერი დავიწყოთ სუფთა ფურცლიდან - პირველ რიგში გავასუფთავოთ main.js ფაილი და მივცეთ ამდაგვარი სახე :
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')
                
ასევე წავშლოთ src საქაღალდეში არსებული assets და components საქაღალდეები. App.vue ფაილში კი შევიტანოთ შემდეგი კოდი :
<template>
    <h1>გამარჯობა !</h1>
</template>

<script></script>
<style></style>
                
ახლა ერთ ფაქტორს დავაკვირდეთ : შევცვალოთ App.vue ფაილის <template> სექციაში არსებული <h1> ელემენტის ტექსტი, თუ ახლა გადავინაცვლებთ ბრაუზერში, შევამჩნევთ, რომ ცვლილება ავტომატურად აისახება.

ოდნავ გავართულოთ ჩვენი კოდი, App.vue :

<template>
    <h1>{{ message }}</h1>
</template>

<script>
export default {
    data() {
        return{
            message: 'ახალი ტექსტი'
        }
    }
}
</script>

<style></style>    
                
export default ჩანაწერია გარანტი იმისა, რომ App.vue ფაილში აღწერილ კოდს ყველგან გამოვიყენებთ სადაც დაგვჭირდება, მაგალითად როგორც ამას main.js ფაილში ვაკეთებთ :
import App from './App.vue'
                
18. Vue კომპონენტები
უვხო სიტყვათა ლექსიკონი : კომპონენტი - რისამე შემადგენელი ნაწილი.
ნებისმიერი ვებ-საიტი შეიძლება განხილულ იქნას, როგორც კონკრეტული ფუნქციონალების, ფრაგმენტების, კომპონენტების ერთობლიობა. კომპონენტის უკან შეიძლება მოიაზრებოდეს მაგალითად საიტის ქუდი (header), პროდუქტის ჩამონათვალის გვერდი და ა.შ. რა თქმა უნდა საჭიროა, რომ სადღაც აღიწეროს კონკრეტული ინსტრუქციები, კონკრეტული ლოგიკა, რომელიც დააგენერირებს საჭირო შიგთავსს, ზემოთ ჩამოთვლილი თითოეული ფრაგმენტისათვის.



კომპონენტები ემსახურებიან სამომხმარებლო ინტერფეისის პატარა ფრაგმენტებად დაყოფას. თითოეულ კომპონენტს აქვს თავისი დამოუკიდებელი თვისებები (props), ინსტრუქციები, შაბლონები და ა,შ. კონკრეტული კომპონენტის ცვლილებისას პროექტის დანარჩენ ნაწილს საერთოდ არ ვეხებით, ეს კი საკმაოდ მოსახერხებელი და კომფორტულია.

src საქაღალდეში შევქმნათ ქვესაქაღალდე - components, მასში კი ფაილი FoodItem.vue შემდეგი კოდით :

<template>
    <div>
        <h2>{{ name }}</h2>
        <p>{{ message }}</p>
    </div>
</template>
  
<script>
export default {
    data() {
        return {
            name: 'ხაჭაპური',
            message: 'მე მიყვარს ხაჭაპური'
        }
    }
};
</script>

<style></style>
                
main.js ფაილში შევიტანოთ შემდეგი კოდი :
import { createApp } from 'vue'
import App from './App.vue'
import FoodItem from './components/FoodItem.vue'

const app = createApp(App)
app.component('food-item', FoodItem)
app.mount('#app')
                
App.vue ფაილში კი შემდეგი კოდი :
<template>
    <h1>საკვები</h1>
    <food-item/>
    <food-item/>
    <food-item/>
</template>

<script></script>

<style>
    #app > div {
        border: dashed black 1px;
        display: inline-block;
        margin: 10px;
        padding: 10px;
        background-color: lightgreen;
    }
</style>  
                

ინდივიდუალური კომპონენტები

Vue-ს კომპონენტებთან მუშაობის ერთ-ერთი მნიშვნელოვანი უპირატესობა და დადებითი მხარე არის ის, რომ ჩვენ შეგვიძლია თითოეულ კომპონენტს მოვექცეთ, როგორც ინდივიდუალურ კომპონენტს და თანაც ისე, რომ, სტანდარტული ჯავასკრიპტისაგან განსხვავებით, კომპონენტებისათვის უნიკალური იდენტიფიკატორის (id) მინიჭება არ დაგვჭირდება, Vue ავტომატურად იზრუნებს ამაზე ^_^

ჩვენს კომპონენტს დავამატოთ მთვლელი, რომელიც დათვლის თუ რამდენჯერ დავაჭერთ მაუსს თითოეულ კომპონენტს, App.vue :

<template>
    <div v-on:click="countClicks">
        <h2>{{ name }}</h2>
        <p>{{ message }}</p>
        <p>დააჭირეთ {{ clicks }} - ჯერ</p>
    </div>
</template>

<script>
export default {
    data() {
        return {
            name: 'ხაჭაპური',
            message: 'მე მიყვარს ხაჭაპური',
            clicks: 0
        }
    },
    methods: {
        countClicks() {
            this.clicks++;
        }
    }
};
</script>

<style>
#red {
    font-weight: bold;
    color: rgb(144, 12, 12);
}
</style>        
                
როგორც ვხედავთ <div> ელემენტისათვის, რომელზეც v-on:click" დირექტივა გვაქვს მიმაგრებული, უნიკალური იდენტიფიკატორი არ მიგვიცია, თუმცა უყველაფერი სწორად მუშაობს.
19. Vue რეკვიზიტები (props)
ინგ : props - რეკვიზიტი

უვხ. სიტყ. ლექს. : რეკვიზიტი - თეატრ. ნივთები, რომლებიც სჭირდებათ მსახიობებს წარმოდგენის მსვლელობის დროს :))
რეკვიზიტების მეშვეობით შესაძლებელია კომპონენტების ტეგებს მივამაგროთ ინფორაცია სამომხმარებლო, ანუ არასტანდარტული ატრიბუტებით (custom attributes).

როგორც გვახსოვს შვქმენით <food-item/> კომპონენტი, რომელსაც სამჯერ ვიყენებდით App.vue ფაილში, შესაბამისად - ბრაუზერშიც გენერირდებოდა სამი ელემენტი, სამივე მათგანში ხაჭაპურს მივირთმევდით :)) მოდით გავამრავალფეროვნოთ მენიუ :))

შევქმნათ საკუთარი food-name ატრიბუტი, რომლის მეშვეობითაც მივამაგრებთ ინფორმაციას <food-item/> კომპონენტს, App.vue :

<template>
    <h1>Food</h1>
    <food-item food-name="ხაჭაპური"/>
    <food-item food-name="ნამცხვარი"/>
    <food-item food-name="პიცა"/>
</template>

<script></script>

<style>
    #app > div {
        border: dashed black 1px;
        display: inline-block;
        width: 120px;
        margin: 10px;
        padding: 10px;
        background-color: lightgreen;
    }
</style>       
                

რეკვიზიტების მიღება კომპონენტში

იმისათვის რათა კომპონენტში დავამუშავოთ რეკვიზიტით გამოგზავნილი ინფორმაცია, უნდა გამოვიყენოთ props კონფიგურაციული თვისება, FoodItem.vue :
<template>
    <div>
        <h2>{{ foodName }}</h2>
    </div>
</template>

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

<style></style>  
                
როგორც ზემოთ ვნახეთ food-name რეკვიზიტი ჩაიწერა ტირით, ანუ ე.წ 'kebab-case' სტილით, ეს სტილი კი ჯავასკრიპტში მიუღებელია, სწორედ ამიტომ გამოვიყენეთ ე.წ 'camelCase' სტილი FoodItem.vue ფაილში.

ლოგიკური ტიპის რეკვიზიტები

ჩვენს კომპონენტს დავამატოთ ახალი რეკვიზიტი isFavorite, რომელიც იქნება ლოგიკური ტიპის (true ან false).

როდესაც ვქმნით ისეთ რეკვიზიტს, რომელიც არასტრიქონული ტიპისაა, რეკვიზიტის ატრიბუტთან ერთად უნდა გამოვიყენოთ v-bind დირექტივა, App.vue :

<template>
    <food-item food-name="ხაჭაპური" v-bind:is-favorite="true"/>
    <food-item food-name="ნამცხვარი" v-bind:is-favorite="false"/>
    <food-item food-name="პიცა" v-bind:is-favorite="true"/>
</template>

<script></script>

<style>
    #app > div {
        border: dashed black 1px;
        display: inline-block;
        width: 120px;
        margin: 10px;
        padding: 10px;
        background-color: lightgreen;
    }
</style>    
                
FoodItem.vue :
<template>
    <div>
        <h2>{{ foodName }}</h2>
        <h3 v-show="isFavorite">ფავორიტია</h3>
    </div>
</template>

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

<style></style> 
                
მოყვანილ მაგალითში ჩვენ არ შეგვიძლია კომპონენტში, ანუ FoodItem.vue ფაილში ვაკონტროლოთ თუ რომელი რეკვიზიტი რა ტიპით გამოგვეგზავნება App.vue ფაილიდან. ვერ ვაკონტროლებთ სრიქონული ტიპისაა თუ ლოგიკური ტიპის ესა თუ ის რეკვიზიტი, რამაც შესაძლოა გაუგებრობები გამოიწვიოს. ამიტომ საჭიროა შემოვიღოთ ვალიდაციის რაიმე მექანიზმი. მოვიქცეთ ასე : რეკვიზიტების კონპიგურაციული თვისევა props ჩავწეროთ ობიექტის სახით და ამ ობიექტში განვსაზღვროთ ვალიდაციის წესებიც, FoodItem.vue :
<template>
    <div>
        <h2>{{ foodName }}</h2>
        <h3 v-show="isFavorite">ფავორიტია</h3>
    </div>
</template>

<script>
    export default {
        // props: ['foodName','isFavorite']
        props: {
            foodName: String,
            isFavorite: Boolean
        }
    }
</script>

<style></style> 
                

აუცილებელი რეკვიზიტები

იმისათვის რათა Vue-მ გაიგოს კონკრეტული რეკვიზიტი არის თუ არა აუცილებელი, მოვიქცეთ ასე, FoodItem.vue
<template>
    <div>
        <h2>{{ foodName }}</h2>
        <h3 v-show="isFavorite">ფავორიტია</h3>
    </div>
</template>

<script>
    export default {
        // props: ['foodName','isFavorite']
        props: {
            foodName: {
                type: String,
                required: true
            },
            isFavorite: Boolean
        }
    }
</script>

<style></style> 
                
ანუ კონკრეტული რეკვიზიტიც (foodName) ობიექტის სახით ჩავწერეთ.

რეკვიზიტების ნაგულისმები მნიშვნელობები

App.vue ფაილში გამოძახებულ ერთ-ერთ კომპონენტს, მაგილთად ნამცხვარს, წავუშალოთ is-favorite რეკვიზიტი :
<template>
    <food-item food-name="ხაჭაპური"v-bind:is-favorite="true"/>
    <food-item food-name="ნამცხვარი"/>
    <food-item food-name="პიცა" v-bind:is-favorite="true"/>
</template>

<script></script>

<style>
    #app > div {
        border: dashed black 1px;
        display: inline-block;
        width: 120px;
        margin: 10px;
        padding: 10px;
        background-color: lightgreen;
    }
</style>    
                
კომპონენტში კი განვსაზღვროთ რეკვიზიტების ნაგულისმები მნიშვნელობა, FoodItem.vue :
<template>
    <div>
        <h2>{{ foodName }}</h2>
        <h3 v-show="isFavorite">ფავორიტია</h3>
    </div>
</template>

<script>
    export default {
        // props: ['foodName','isFavorite']
        props: {
            foodName: {
                type: String,
                required: true
            },
            isFavorite: {
                type: Boolean,
                required: false,
                default: true
            }
        }
    }
</script>

<style></style> 
                

რეკვიზიტთა ვალიდაციის ფუნქცია

შესაძლებელია რეკვიზიტთა ვალიდაციის ფუნქციის აღწერაც, რომელიც დაადგენს ვალიდურია თუ არა ესა თუ ის რეკვიზიტი. ასეთმა ფუნქციამ უნდა დააბრუნოს 'true' ან 'false' მნიშვნელობებიდან ერთ-ერთი. FoodItem.vue :
<template>
    <div>
        <h2>{{ foodName }}</h2>
        <h3 v-show="isFavorite">ფავორიტია</h3>
    </div>
</template>

<script>
    export default {
        // props: ['foodName','isFavorite']
        props: {
            foodName: {
                type: String,
                required: true,
                validator: function(value) {
                    if(value.length < 3) {
                        return false;
                    }
                    else {
                        return true;
                    }
                }
            },
            isFavorite: {
                type: Boolean,
                required: false,
                default: true
            }
        }
    }
</script>

<style></style> 
                
ასეთი ვალიდაციის დარღვევის შემთხვეაში ('პიცა'-ს მაგივრად გამოვგზავნოთ 'პი') ბრაუზერის კონსოლში ვიხილავთ ამდაგვარ შეტყობინებას :



რეკვიზიტთა მოდიფიცირება

როდესაც კომპონენტის გამოძახება ხდება მშობელ ელემენტში, ჩვენ არ შეგვიძლია მშობლიდან გამოგზავნილი რეკვიზიტის ცვლილება შვილობილ კომპონენტში. მაგალითად FoodItem.vue კომპონენტში ვერ შევცვლით isFavorite რეკვიზიტის იმ მნიშვნელობას, რომელიც App.vue-დან მივიღეთ. ანუ მშობლიდან გამოგზავნილი რეკვიზიტი არის მხოლოდ წაკითხვადი (read-only).

დავუშვათ გვინდა, რომ მოხმარებელს მიეცეს საშუალება ღილაკის მეშვეობით გადაწყვიტოს რომელი პროდუქტია მისთვის ფავორიტი და რომელი არა. პირველი, რაც თავში გაგვიელვებს, ალბათ არის რაღაც ამდაგვარი :

methods: {
    toggleFavorite() { 
        this.isFavorite = !this.isFavorite;
    }
}
                
მაგრამ ეს არ იმუშავებს, რადგან, როგორც ვთქვით, მშობლიდან მიღებული რეკვიზიტის მხოლოდ წაკითხვა შეგვიძლია. ნაცვლად ამისა, კომპონენტში უნდა აღვწეროთ ახალი ინფორმაცია :
data() {
    return { 
        foodIsFavorite: this.isFavorite
    }
}
                
შემდეგ კი მეთოდი :
methods: {
    toggleFavorite() { 
        this.foodIsFavorite = !this.foodIsFavorite;
    }
}
                
FoodItem.vue :
<template>
    <div>
        <h2>{{ foodName }}</h2>
        <h3 v-show="foodIsFavorite">ფავორიტია</h3>
        <button v-on:click="toggleFavorite">ფავორიტი</button>
    </div>
</template>

<script>
    export default {
        props: ['foodName','isFavorite'],
        data() {
            return {
                foodIsFavorite: this.isFavorite // foodIsFavorite ადგილობრივია, isFavorite მშობლიდან მოვიდა
            }
        },
        methods: {
            toggleFavorite() {
                this.foodIsFavorite = !this.foodIsFavorite;
            }
        }
    }
</script>

<style></style>  
                
20. Vue v-for კომპონენტები
v-for დირექტივის მეშვეობით შესაძლებელია კომპონენტების მრავალჯერადად გამოყენება, კომპონენტების ამ გზით გენერირება საკმაოდ მოსახერხებელია, რადგან შესაძლებლობა გვექნება თითოეულ მათგანს დინამიურად გადავცეთ რეკვიზიტები.

App.vue :

<template>
    <div id="wrapper">
        <food-item v-for="x in foods" food-name="x" />
    </div> 
</template>

<script>
export default {
    data() {
        return {
            foods: ['ხაჭაპური','ნამცხვარი','პიცა']
        };
    }
}
</script>

<style>
    #wrapper {
        display: flex;
        flex-wrap: wrap;
    }
    #wrapper > div {
        border: dashed black 1px;
        margin: 10px;
        padding: 0 20px;
        background-color: lightgreen;
    }
</style> 
                
FoodItem.vue
<template>
    <div>
        <h2>{{ foodName }}</h2>
    </div>
</template>

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

<style></style>     
                
21. Vue $emit()
რეკვიზიტები გამოიყენება ინფორმაციის გასაგზავნად მშობელი ელემენტიდან შვილობილ კომპონენტში, Vue-ში ჩადგმული $emit() მეთოდი კი პირიქით - შვილობილი კომპონენტიდან გზავნის ინფორმაციას მშობელ ელემენტში.

შემდეგი ბიჯები რასაც ახლა გავაკეთებთ, ემსახურება იმას, რომ საკვების 'favorite' სტატუსი შეიცვალოს მშობელ ელემენტ App.vue-ში და არა FoodItem.vue კომპონენტში.

ამის მიზეზი კი გახლავთ ის, რომ სწორედ App.vue ფაილია ის ადგილი სადაც 'favorite' სტატუსი პირველად ჩნდება. გასაგებია, რომ ამ ეტაპზე საკმაოდ მარტივი სქემით ვმუშაობთ, მაგრამ დიდ პროექტში შესაძლებელია App.vue-ში ინფორმაცია მონაცემთა ბაზიდან მოგვქონდეს და მოხდეს ისე, რომ დაგვჭირდეს შვილობილ კომპონენტში ჩატარებული ოპერაციების საფუძველზე ამ ინფორმაციის ბაზაში განახლებაც, რაც ბუნებრივია მოითხოვს შეგვეძლოს კომპონენტიდან მშობელ ელემენტში ინფორმაციის გაგზავნა/განახლება.

FoodItem.vue-ში არსებული toggleFavorite მეთოდი გადავაკეთოთ შემდეგნაირად :
methods: {
    toggleFavorite() {
        // toggle-favorite არის სამომხმარებლო მოვლენის სახელი, რომელიც უნდა აღვწეროთ App.vue-ში
        this.$emit('toggle-Favorite'); 
    }
}  
                
ანუ მოვახდინეთ შვილობილი კომპონენტიდან მშობელ ელემენტში მოვლენის გაგზავნა, ახლა საჭიროა მშობელ ელემენტში დავამუშავოთ ეს მოვლენა და ჩავატაროთ საჭირო ოპერაციები.

დავაფიქსიროთ toggle-favorite მოვლენა App.vue-ში :

<food-item v-for="x in foods" :food-name="x.name"  :is-favorite="x.favorite" @toggle-favorite="receiveEmit"/>
                
ახლა კი App.vue ფაილში აღვწეროთ receiveEmit მეთოდი, რომელიც შესრულდება მას შემდეგ, რაც შვილობილი კომპონენტიდან გამოიგზავნება მოვლენა :
methods: {
    receiveEmit() {
        alert('გამარჯობა !');
    }
}
                
ახლა დროა მოვახდინოთ ინფორმაციის განახლება App.vue-ში. უნდა განვაახლოთ 'foods' მასივში აღწერილი პროდუქტებიდან უშუალოდ იმ პროდუქტის 'favorite' თვისების განახლება, რომელსაც მომხმარებელი დააწვება FoodItem.vue კომპონენტში. ამისათვის გვჭირდება რაიმე იდენტიფიკატორი და ამ ეტაპზე გამოვიყენოთ პროდუქტის დასახელება (შემდგომში სხვაგვარად მოვიქცევით) : FoodItem.vue :
methods: {
    toggleFavorite() {
        this.$emit('toggle-favorite', this.foodName);
    }
}
                
არგუმენტად გადაცემული დასახელება დავაფიქსიროთ App.vue-ში :
methods: {
    receiveEmit(foodName) {  
        alert( 'თქვენ აირჩიეთ: ' + foodName );
    }
}
                
ახლა უკვე ვიცით თუ რომელი პროდუქტის სტატუსის ცვლილება გვსურს, App.vue :
methods: {
    receiveEmit(foodName) {
        // მასივში ვიპოვოთ ის ობიექტი, რომლის name ატრიბუტიც არის foodName
        const foundFood = this.foods.find(food => food.name === foodName); 
        // ნაპოვნი ობიექტის favorite სტატუსი შევცვალოთ არსებულის საპირისპიროთი
        foundFood.favorite = !foundFood.favorite; 
    }
}
                
FoodItem.vue კომპონენტში შევიტანოთ შემდეგი ცვლილება :
<template>
    <div>
        <h2>
            {{ foodName }}
        </h2>
        <p v-show="isFavorite">ფავორიტია</p>
        <button v-on:click="toggleFavorite">ფავორიტი</button>
    </div>
</template>

<script>
export default {
    props: ['foodName','isFavorite'],  
    emits: ['toggle-favorite'], // ამ კონფიგურაციული პარამეტრის დამატება ნებაყოფლობითია, უბრალოდ აადვილებს კოდის აღქმას 
    methods: {
        toggleFavorite() {
            this.$emit('toggle-favorite', this.foodName);
        }
    }
};
</script>

<style>
    img {
        height: 1.5em;
        float: right;
    }
</style>       
                
ასევე შეგვიძლია წავშალოთ foodIsFavorite თვისებაც.
22. Vue ლოკალური სტილები
კომპონენტებში ან App.vue ფაილში <style> ტეგით აღწერილი სტილები გლობალურად ვრცელდება, ანუ მოქმედებს ყველა კომპონენტში. იმისათვის რათა კომპონენტში აღწერილმა სტილებმა მხოლოდ ლოკალურად იმუშავონ, <style> ტეგი უნდა გამოვიყენოთ scoped ატრიბუტთან ერთად.
ინგ: scoped - გამოყენების სფერო, თვალთახედვის არე, ფარგლები
TestComponent.vue
<template>
    <p>ეს p ტეგი ეკუთვნის 'TestComponent.vue' კომპონენტს</p>
</template>

<script></script>

<style scoped>
    p {
        background-color: pink;
        width: 150px;
    }
</style>  
                
23. Vue ლოკალური კომპონენტები
კომპონენტების გამოძახების ის გზა, რომელსაც აქამდე ვიყენებდით, გულისხმობს იმას, რომ ეს კომპონენტები ხელმისაწვდომია ყველა*.vue ფაილში. ანუ შეგვიძლია თითოეული მათგანი განვიხილოთ, როგორც გლობალური კომპონენტი.

/src/components საქაღალდეში შევქმნათ სატესტო კომპონენტები CompOne.vue და CompTwo.vue

CompOne.vue

<template>
    <div class="compOneDiv">
        <p>CompOne.vue</p>
    </div>
</template>

<script></script>

<style></style>    
                

CompTwo.vue

<template>
    <div class="compTwoDiv">
        <p>CompTwo.vue</p>
        <comp-one />
    </div>
</template>

<script></script>

<style></style>   
                

App.vue

<template>
    <div>
        <h3>გლობალური კომპონენტები</h3>
        <p>App.vue</p>
        <p>CompOne.vue კომპონენტი გამოყენებულია App.vue-შიც და CompTwo.vue-შიც</p>
        <comp-one />
        <comp-two />
    </div>
</template>

<script></script>

<style>
    p {
        width: 200px;
    }
    #app div {
        border: dashed black 1px;
        margin: 10px;
        padding: 10px;
        display: inline-block;
    }
    .compOneDiv {
        background-color: lightgreen;
    }
    .compTwoDiv {
        background-color: lightcoral;
    }
</style>          
                

main.js

import { createApp } from 'vue'

import App from './App.vue'
import CompOne from './components/CompOne.vue'
import CompTwo from './components/CompTwo.vue'

const app = createApp(App)
app.component('comp-one', CompOne)
app.component('comp-two', CompTwo)
app.mount('#app')
                
ეს არის გლობალური კომპონენტების მაგალითი.

ლოკალური კომპონენტები

შეგვიძლია კომპონენტი გამოვიძახოთ კონკრეტული *.vue ფაილის <script> სექციაში და არა main.js ფაილში. ასეთ შემთხვევაში კომპონენტი ხელმისაწვდომი იქნება მხოლოდ ამ *.vue ფაილში და შესაბამისად იგი შეგვიძლია განვიხილოთ, როგორც ლოკალური კომპონენტი.

CompOne.vue კომპონენტი ხელმისაწვდომი გავხადოთ მხოლოდ App.vue ფაილში. წავშალოთ ეს კომპონენტი main.js ფაილში :

import { createApp } from 'vue'
 
import App from './App.vue'
import CompOne from './components/CompOne.vue'
import CompTwo from './components/CompTwo.vue'
 
const app = createApp(App)
app.component('comp-one', CompOne)
app.component('comp-two', CompTwo)
app.mount('#app')
                
ასევე CompOne.vue კომპონენტი გამოვიძახოთ უშუალოდ App.vue ფაილის<script> სექციაში :
<template>
    <h3>ლოკალური კომპონენტი</h3>
    <p>CompOne.vue ლოკალური კომპონენტია და მისი გამოყენება შეიძლება მხოლოდ App.vue-ში</p>
    <comp-one />
    <comp-two />
</template>

<script> 
    import CompOne from './components/CompOne.vue';

    export default {
        components: {
            'comp-one': CompOne
        }
    }
</script>  
                
ასეთ შემთხვევაში ვიხილავთ ამდაგვარ სურათს :



24. Vue Slots
Vue-ში ე.წ სლოტი არის ინსტრუმენტი, რომლის მეშვეობითაც შეგვიძლია მშობელი ელემენტიდან გადავცეთ შიგთავსი შვილობილი კომპონენტის <template> სექციას. აქამდე კომპონენტებს <template> სექციაში ვიყენებდით თვითდამხურავი ტეგების მეშვეობით, App.vue :
<template>
    <slot-comp />
</template>
                
ნაცვლად ამისა, შეგვიძლია გამოვიყენოთ ტეგთა წყვილები და მათ შორის ჩავსვათ სასურველი შიგთავსი : App.vue :
<template>
    <slot-comp>გამარჯობა!</slot-comp>
</template>
                
მაგრამ იმისათვის რომ 'გამარჯობა!' ტექსტი გამოვიყენოთ, შვილობილ კომპონენტში სასურველ ადგილას უნდა ჩავსვათ <slot> ტეგი, ერთგვარი წინასწარგამზადებული ცარიელი სივრცე, რომელიც საჭირო მომენტში შეივსება გამოგზავნილი შიგთავსით.

App.vue

<template>
    <div>
        <h3>Slot</h3>
        <p>App.vue-დან ვაგზავნით ტექსტ 'გამარჯობა!'-ს, რომელიც შეავსებს SlotComp.vue კომპონენტში არსებული slot ტეგის მიერ შექმნილ სივრცეს</p>
        <slot-comp>გამარჯობა!</slot-comp>
    </div>
</template>

<script></script>

<style>
    p {
        width: 200px;
    }
    #app div {
        border: dashed black 1px;
        margin: 10px;
        padding: 10px;
        display: inline-block;
    }
</style>        
                
SlotComp.vue
<template>
    <div>  
        <p>SlotComp.vue</p>
        <slot></slot>
    </div>
</template>    
                
main.js
import { createApp } from 'vue'

import App from './App.vue'
import SlotComp from './components/SlotComp.vue'

const app = createApp(App)
app.component('slot-comp', SlotComp)
app.mount('#app')   
                
სლოტების მეშვეობით შესაძლებელი უფრო ვრცელი HTML შიგთავსების დაგენერირებაც ვიდრე უბრალოდ ტექსტებია. აქამდე კომპონენტებში ინფორმაციის გასაგზავნად რეკვიზიტებს ვიყენებდით, ახლა შევეცადოთ გავგზავნოთ HTML შიგთავსი, რომელიც სლოტის სივრცეს შეავსებს კომპონენტში.

App.vue

<template>
    <div id="wrapper">
        <slot-comp v-for="x in foods">
            <h4>{{x.name}}</h4>
            <p>{{x.desc}}</p>
        </slot-comp>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                foods: [
                    { 
                        name: 'ხაჭაპური', 
                        desc: 'იმერული ხაჭაპური'
                    },
                    { 
                        name: 'პიცა', 
                        desc: 'იტალიური პიცა', 
                    }                  
                ]
            }
        }
    }
</script>

<style>
    #wrapper {
        display: flex;
        flex-wrap: wrap;
    }
    #wrapper img {
        display: block; 
        margin: auto; 
        width: 60%;
    }
</style>           
                
SlotComp.vue
<template>
    <div>
        <slot></slot>
    </div>
</template>

<script></script>

<style scoped>
    div {
        background-color: lightgreen;
        box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
        transition: 0.3s;
        flex-basis: 150px;
        border-radius: 10px;
        border: solid black 2px;
        margin: 10px;
        padding: 20px 10px 0 10px;
    }
    div:hover {
        box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
    }
</style>      
                




ამ ყველაფრის შედეგს თუ დავაკვირდებით, შევამჩნევთ, რომ SlotComp.vue-ში აღწერილმა კოდმა მოგვცა ბარათის ტიპის (card) ელემენტი კონკრეტული მახასიათებლებით : ჩარჩოს სისქე და ფერი, მომრგვალებული ჩარჩო, შუქჩრდილი და ა.შ. ახლა შეგვიძლია ეს ბარათი გამოვიყენოთ იმდენჯერ და იმდენგან რამდენჯერაც დაგვჭირდება და სადაც დაგვჭირდება, ამასთანავე შესაძლებელია ბარათის შიგთავსის შეცვლა, რაც საკმაოდ მოსახერხებელია. მაგალითად დავამატოთ საიტის ძირი, რომელიც ასეთივე მონაცემების ბარათზე იქნება დაფუძნებული.

App.vue

<template>
    <div id="wrapper">
        <slot-comp v-for="x in foods">
            <h4>{{x.name}}</h4>
            <p>{{x.desc}}</p>
        </slot-comp>
    </div>
    <footer>
        <slot-comp>
            <!- შეცვლილი შიგთავსი -->
            <h3>Footer</h3>
        </slot-comp>
    </footer>
</template>

<script>
    export default {
        data() {
            return {
                foods: [
                    { 
                        name: 'ხაჭაპური', 
                        desc: 'იმერული ხაჭაპური'
                    },
                    { 
                        name: 'პიცა', 
                        desc: 'იტალიური პიცა', 
                    },
                    { 
                        name: 'ბრინჯი', 
                        desc: 'ჩინური ბრინჯი', 
                    } ,
                    { 
                        name: 'სუში', 
                        desc: 'იაპონური სუში', 
                    },
                    { 
                        name: 'ბეკონი', 
                        desc: 'ამერიკული ბეკონი', 
                    }                     
                ]
            }
        }
    }
</script>

<style>
    #wrapper {
        display: flex;
        flex-wrap: wrap;
        justify-content: space-around;
    }
    #wrapper > div {
        background-color: lightgreen;
    }
    footer > div {
        background-color: lightpink;
    }
    #wrapper img {
        display: block; 
        margin: 20% auto 0; 
        width: 60%;
    }
    h3, h4 {
        text-align: center;
    }
</style>       
                
SlotComp.vue
<template>
    <div>
        <slot></slot>
    </div>
</template>

<script></script>

<style scoped>
    div {
        box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
        transition: 0.3s;
        flex-basis: 150px;
        border-radius: 10px;
        border: solid black 2px;
        margin: 10px;
        padding: 0 10px 0;
    }
    div:hover {
        box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
    }
</style>      
                




25. Vue დინამიური კომპონენტები
წარმოვიდგინოთ, რომ გვაქვს რამდენიმე კომპონენტი და გვჭირდება მათი გამოჩენა მონაცვლეობით, საჭიროებისამებრ. ამაში დაგვეხმარება <component> ტეგი is ატრიბუტთან ერთად.

App.vue

 <template>
    <h1>დინამიური კომპონენტები</h1>
    <button @click="toggleValue = !toggleValue">გადართეთ კომპონენტი</button>
    <component :is="activeComp"></component>
</template>
  
<script>
    export default {
        data () {
            return {
                toggleValue: true
            }
        },
        computed: {
            activeComp() {
                if(this.toggleValue) {
                    return 'comp-one'
                }
                else {
                    return 'comp-two'
                }
            }
        }
    }
</script>

<style>
    #app {
        width: 350px;
        margin: 10px;
    }
    #app > div {
        border: solid black 2px;
        padding: 10px;
        margin-top: 10px;
    }
</style>        
                
გვაქვს <component> ტეგის მიერ შექმნილი ერთგვარი წინასწარგანსაზღვრული სივრცე, რომელსაც შეავსებს ორი კომპონენტიდან ერთ-ერთი, ეს კომპონენტება comp-one და comp-two. <component> ტეგის is მეთოდი აკვირდება გამოთვლად თვისება activeComp-ს, რომელიც აბრუნებს 'comp-one' ან 'comp-two' მნიშვნელობებს. ასევე გვაქვს ღილაკი, რომელიც data პარამეტრში აღწერილ toggleValue თვისებას ანიჭებს მნიშვნელობებს 'true' ან 'false', ამ მნიშვნელობიდან გამომდინარე კი გამოთვლად თვისებაში ბრუნდება აქტიური კომპონენტი

CompOne.vue

<template>
    <div>
        <h2>პირველი!</h2>
        <p>ეს არის პირველი კომპონენტი</p>
    </div>
</template>

<script></script>

<style scoped>
    div {
        background-color: lightgreen;
    }
</style> 
                
CompTwo.vue
<template>
    <div>
        <h2>მეორე!</h2>
        <p>ეს არის მეორე კომპონენტი</p>
    </div>
</template>

<script></script>

<style scoped>
    div {
        background-color: lightpink;
    }
</style>  
                
main.js
import { createApp } from 'vue'

import App from './App.vue'
import CompOne from './components/CompOne.vue'
import CompTwo from './components/CompTwo.vue'

const app = createApp(App)
app.component('comp-one', CompOne)
app.component('comp-two', CompTwo)
app.mount('#app')
                

KeepAlive ტეგი

ზემოთ მოყვანილ ორივე კომპონენტში უბრალოდ ტექსტები გამოგვქონდა. წარმოვიდგინოთ, რომ ორივე მათგანში რაიმე ფორმები გვქონოდა, თუ მომხმარებელი შეავსებდა პირველ კომპონენტში არსებულ ფორმას და გადართავდა კომპონენტს, ამის შემდეგ კი ისევ პირველ კომპონენტზე დაბრუნდებოდა, შევსებული ფორმა ცარიელი დახვდებოდა. ამის თავიდან ასაცილებლად <component> ტეგი უნდა მოვაქციოთ <KeepAlive> ტეგში :
<!-- დამახსოვრებულ იქნება ორივე კომპონენტის მდგომარეობა -->
<KeepAlive>
    <component :is="activeComp"></component>
</KeepAlive>

<!-- დამახსოვრებულ იქნება მხოლოდ CompOne კომპონენტის მდგომარეობა -->
<KeepAlive include="CompOne">
    <component :is="activeComp"></component>
</KeepAlive>

<!-- დამახსოვრებულ იქნება მხოლოდ CompTwo კომპონენტის მდგომარეობა -->
<KeepAlive exclude="CompOne">
    <component :is="activeComp"></component>
</KeepAlive>

<!-- 
    დამახსოვრებულ იქნება ბოლოს ნანახი ორი კომპონენტის მდგომარეობა,
    ჩვენს შემთხვევაში ორივე კომპონენტს დაიმახსოვრებდა ბრაუზერი,
    რადგან სულ ორი კომპონენტი გვაქვს
-->
<KeepAlive :max="2">
    <component :is="activeComp"></component>
</KeepAlive>
                
26. Vue HTTP მოთხოვნა
HTTP ანუ Hyper Text Transfer Protocol არის სერვერსა და მომხმარებლეს შორის კომუნიკაციის გზა, მექანიზმი, პროტოკოლი. მომხმარებელი სერვერზე აგზავნის მოთხოვნას (HTTP request), სერვერიდან კი ბრუნდება შესაბამისი პასუხი (HTTP response). HTTP მოთხოვნის ყველაზე გავრცელებული ტიპებია POST, GET, PUT, PATCH და DELETE.

fetch მეთოდი

სერვერიდან ინფორმაციის მისაღებად Vue-ში გამოიყებენა ჯავასკრიპტის ფუნქცია fetch(), რომელსაც ხშირად გამოვიყენებთ ამ თავში, თუმცა არ განვსაზღვრავთ HTTP მოთხოვნის ტიპს, რაც იმას ნიშნავს, რომ ეს მოთხოვნები ნაგულისხმეობის პრინციპით იქნება GET ტიპის.

fetch() მეთოდს არგუმენტად უნდა გადაეცეს ის მისამართი, რომლიდანაც გვსურს ინფორმაციის წამოღება. მოვიყვანოთ მარტივი მაგალითი, პროექტის ძირ საქაღალდეში შევქმნათ ტექსტური ფაილი file.txt და მასში შევიტანოთ რაიმე ტექსტი :

Lorem Ipsum საბეჭდი და ტიპოგრაფიული ინდუსტრიის უშინაარსო ტექსტია. იგი სტანდარტად 1500-იანი წლებიდან იქცა.
                
App.vue
<template>
    <div>
        <button @click="fetchData">ინფორმაციის ამოღება</button>
        <p v-if="data">{{ data }}</p>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                data: null,
            };
        },
        methods: {
            fetchData() {
                const response = fetch("file.txt");
                this.data = response;
                console.log(response);
            }
        }
    };
</script> 
                
ასეთ შემთხვევაში პასუხად მივიღებთ ამდაგვარ რამეს : "[object Promise]", კონსოლში კი ვიხილავთ ასეთ სურათს :



მაგრამ ეს ის არ არის რაც ჩვენ გვჭირდება.

რა არის 'Promise' ?

მანამ, სანამ იმას გავარკვევთ, თუ რა არის promise ჯავასკრიპტში, გავიხსენოთ თუ რას ნიშნავს სინქრონული პროგრამირება და ასინქრონული პროგრამირება.

წარმოვიდგინოთ, რომ გვაქვს ასეთი ამოცანა : თავდაპირველად უნდა გავაგზავნოთ მოთხოვნა სერვერზე, შემდეგ დავაგენერიროთ სამომხმარებლო ინტერფეისი და ბოლოს ეს ინტერფეისი შევავსოთ სერვერიდან მიღებული ინფორმაციით.

სინქრონული პროგრამირების შემთხვევაში ინტერფეისის გენერირება არ მოხდება მანამ, სანამ სერვერიდან არ მოვა პასუხი, ანუ ოპერაციათა ჯაჭვის ყოველი რგოლი ელოდება წინას დასრულებას, მაშინ, როდესაც ასინქრონული პროგრამირება გვაძლევს საშუალებას არ დაველოდოთ წინა ამოცანას და გადავიდეთ შემდეგ ეტაპზე, შემდეგი ნაბიჯებიდან გავაკეთოთ ის რისი გაკეთებაც შეგვიძლია, ამასობაში კი წინა ოპერაციების შედეგებიც დაგვეწევა, საბოლოოდ ყველაფერი გაერთიანდება და მივიღებთ შემაჯამებელ სურათს ანუ სასურველ შედეგს.







***

ინგ: Promise - დაპირება; სახელშეკრულებო ვალდებულება (დავალებული პირის შემდეგი მოქმედებების); სიტყვის მიცემა; ვალდებულების აღება.
ასინქრონული პროგრამირება სწორედ promise-ზეა დაფუძნებული თანამედროვე ჯავასკრიპტში. promise არის ასინქრონული ფუნქციის მიერ დაბრუნებული ობიექტი, რომელიც ასახავს მოთხოვნის მიმდინარე მდგომარეობას, სტატუსს. promise-ს დაბრუნების მომენტში ოპერაცია ხშირად დასრულებული არ არის, თუმცა აღნიშნული ობიექტი გვაძლევს საშუალებას განვსაზღვროთ წარმატებულია თუ არა კონკრეტული ოპერაცია და მოვიქცეთ ამ პასუხის მიხედვით.

promise-ს შეიძლება გააჩნდეს ამ სტატუსებიდან ერთ-ერთი :

  • pending - promise-ს ობიექტი შეიქმნა თუმცა ასინქრონული ფუნქცია ჯერ არ შესრულებულა და არ ვიცით წარმატებით დასრულდება თუ არა იგი.
  • fulfilled - ასინქრონული ფუნქცია წარმატებით დასრულდა. ასეთ შემთხვევაში ხდება then() დამმუშავებლის (handler) გამოძახება.
  • rejected - ასინქრონული ფუნქცია წარუმატებლად დასრულდა. ასეთ შემთხვევაში ხდება catch() დამმუშავებლის გამოძახება.

const fetchPromise = fetch("https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json");

console.log(fetchPromise);

fetchPromise.then((response) => {
    console.log(`მიღებული პასუხი: ${response.status}`);
});

console.log("მოთხოვნა იგზავნება …");
                
ასინქრონული ჯავასკიპტის ეს კოდი ასეთ შედეგს მოგვცემს :
Promise { <state>: "pending" }
მოთხოვნა იგზავნება…
მიღებული პასუხი: 200
                
ანუ, როგორც ვხედავთ, console.log("მოთხოვნა იგზავნება …") ინსტრუქცია ასინქრონული ფუნქციის დასრულებამდე შესულდა.

იმისათვის რათა კონკრეტული ფუნქცია ასინქრონულად ვაქციოთ, გამოიყენება async სიტყვაგასაღები, რომელიც ბრაუზერს ეუბნება, რომ ფუნქცია ასინქრონულია, შესაბამისად ელოდება რაღაცის დასრულებას და ამასობაში ბრაუზერს შეუძლია შეასრულოს სხვა ამოცანები. ამ ლოდინის ასაღწერად გამოიყენება await სიტყვაგასაღები :

async function myFunction() {
    const response = await fetch("something.txt"); 
}
                
ასინქრონულ ფუნქციაში შეგვიძლია გამოვიყენოთ await სიტყვაგასაღები, რომელიც კოდის მუშაობას აყოვნებს promise-ს ობიექტი შექმნამდე (ინგ: Await - ცდა, იცდის, ლოდინი).

***

დავუბრუნდეთ App.vue ფაილს :
<template>
    <div>
        <button @click="fetchData">ინფორმაციის ამოღება</button>
        <pre v-if="data">{{ data }}</pre>
    </div>
</template>

<script>
export default {
    data() {
        return {
            data: null,
        };
    },
    methods: {
        async fetchData() {
            const response = await fetch("file.txt");
            this.data = await response.text();
            console.log(this.data);
        }
    }
};
</script>   
                
იმისათვის რათა წაგვეკითხა ფაილის შიგთავსი, გამოვიყენეთ პასუხის ობიექტის text() მეთოდი.

ინფორმაციის ამოღება JSON ფაილიდან

წინა მაგალითში ინფორმაცია ამოვიღეთ .txt ფაილიდან, ახლა ვნახოთ თუ როგორ შეიძლება იგივეს გაკეთება .json ფაილიდან.

პროექტის ძირ საქაღალდეში შევქმნათ ფაილი animals.json და მასში შევიტანოთ შემდეგი კოდი :

{
    "results": [
        {
            "name": "African elephant",
            "maxWeight": 10000,
            "carnivore": false,
            "countries": [
                "Namibia",
                "Angola",
                "Tanzania",
                "Kenya",
                "Mozambique",
                "Botswana",
                "South-Africa"
            ]
        },
        {
            "name": "Siberian tiger",
            "maxWeight": 300,
            "carnivore": true,
            "countries": [
                "Russia",
                "North Korea",
                "China"
            ]
        },
        {
            "name": "American bison",
            "maxWeight": 1200,
            "carnivore": false,
            "countries": [
                "USA",
                "Canada"
            ]
        },
        {
            "name": "Polar bear",
            "maxWeight": 1000,
            "carnivore": true,
            "countries": [
                "USA",
                "Canada",
                "Norway",
                "Russia",
                "Greenland"
            ]
        },
        {
            "name": "Gaur",
            "maxWeight": 1500,
            "carnivore": false,
            "countries": [
                "India",
                "Thailand",
                "Laos",
                "Cambodia",
                "Vietnam",
                "Myanmar",
                "Malaysia",
                "China",
                "Bhutan",
                "Nepal"
            ]
        }
    ]
} 
                
.json ფაილიდან ინფორმაციის წასაკითხად უნდა გამოვიყენოთ json() მეთოდი. App.vue :
<template>
    <div>
        <button @click="fetchData">ინფორმაციის ამოღება</button>
        <pre v-if="data">{{ data }}</pre>
    </div>
</template>
  
<script>
    export default {
        data() {
            return {
                data: null,
            };
        },
        methods: {
            async fetchData() {
                const response = await fetch("animals.json");
                this.data = await response.json();
            }
        }
    };
</script>      
                
main.js :
import { createApp } from 'vue'

import App from './App.vue'

const app = createApp(App)
app.mount('#app')  
                

ინფორმაციის ამორება API-დან

API ანუ Application Programming Interface. დაწვრილებითი ინფორმაცია შეგიძლიათ იხილოთ აქ. სადემონსტრაციოდ გამოვიყენოთ random-data-api.com.

App.vue :

<template>
    <button @click="fetchData">ინფორმაციის ამოღება</button>
    <pre v-if="data">{{ data }}</pre>
</template>
  
<script>
    export default {
        data() {
            return {
                data: null,
            };
        },
        methods: {
            async fetchData() {      
                const response = await fetch("https://random-data-api.com/api/v2/users"); 
                this.data = await response.json();
            }   
        }
    };
</script>    
                
ინფორმაციის დაბრუნება სხვადასხვა ფორმატითაა შესაძლებელი API-ში, მაგრამ ყველაზე გავრცელებული ფორმატი json-ია, ამ ფორმატით ბრუნდება აღნიშნულ API-შიც და სწორედ ამიტომ განოვიყენეთ json() მეთოდი.

მოთხოვნის გაგზავნა 'axios' ბიბლიოთეკის მეშვეობით

axios არის ჯავასკრიპტის ბიბლიოთეკა, რომლის მეშვეობითაც შესაძლებელია სხვადასხვა ტიპის მოთხოვნების გაგზავნა სასურველ მისამართზე. როგორც ადრე აღვნიშნეთ, მოთხოვნის ყველაზე გავრცელებული ტიპებია GET, POST, PUT/PATCH და DELETE.

იმისათვის რათა შევძლოთ axios-ის გამოყენება, პირველ რიგში უნდა დავაინსტალიროთ ბიბლიოთეკა. ტერმინალიდან გადავინაცვლოთ პროექტის საქაღალდეში და გავუშვათ შემდეგი ბრძანება :

npm install axios
                

App.vue :

<template>
    <p>
        თითოეული დაჭერა აგენერირებს შემთხვევით მომხამერებელს <a href="https://random-data-api.com/" target="_blank">https://random-data-api.com/</a>-დან.
    </p>
    <button @click="fetchData">ინფორმაციის ამოღება</button>
    <div v-if="data" id="dataDiv">
        <img :src="data.data.avatar" alt="avatar">
        <pre>{{ data.data.first_name + " " + data.data.last_name }}</pre>
        <p>"{{ data.data.employment.title }}"</p>
    </div>
</template>
  
<script>
    import axios from 'axios'
  
    export default {
        data() {
            return {
                data: null,
            };
        },
        methods: {
            async fetchData() {  
                // get ტიპის მოთხოვნა, ანალოგიურად ხდება სხვა ტიპის მოთხოვნების გაგზავნაც.
                this.data = await axios.get("https://random-data-api.com/api/v2/users");
            }
        }
    };
</script>
  
<style>
    #dataDiv {
        width: 240px;
        background-color: aquamarine;
        border: solid black 1px;
        margin-top: 10px;
        padding: 10px;
    }
    #dataDiv > img {
        width: 100%;
    }
    pre {
        font-size: larger;
        font-weight: bold;
    }
</style>                                  
                
27. Vue $refs ობიექტი
თუ რომელიმე ელემენტს მიმაგრებული აქვს ref ატრიბუტი, ეს იმას ნიშნავს, რომ ეს ელემენტი შენახულია Vue-ში ჩადგმულ $refs ობიექტში, ეს ობიექტი კი გამოიყენება ელემენტთა იდენტიფიკაციისათვის, ელემენტთა ამორჩევისათვის. აღნიშნული ატრიბუტისა და ობიექტის გამოყენება არის ჯავასკრიპტის ამორჩევითი ფუნქციების : getElementById(), querySelector() და ა.შ ალტერნატივა.

App.vue :

<template>
    <p ref="p1">დააჭირეთ ღილაკს რათა მოხდეს ამ ტექსტის ტრანსფერი ქვედა პარაგრაფში.</p>
    <button @click="transferText">Transfer text</button>
    <p ref="p2">...</p>
</template>

<script>
    export default {
        methods: {
            transferText() { 
                this.$refs.p2.innerHTML = this.$refs.p1.innerHTML;
            }
        }
    };
</script>    
                
წავიკითხოთ ტექსტური ველის მნიშვნელობა :
<template>
    <p>აკრიფეთ ტექსტი ტექსტურ ველში და ეს ტექსტი ავტომატურად ჩაჯდება '$refs' ობიექტში შენახულ პარაგრაფში.</p>
    <input ref="inputEl" @input="getRefs" placeholder="აკრიფეთ ტექსტი">
    <p ref="pEl"></p>
</template>
  
<script>
    export default {
        methods: {
            getRefs() { 
                this.$refs.pEl.innerHTML = this.$refs.inputEl.value;
            }
        }
    };
</script>     
                
28. Vue კომპონენტის სასიცოცხლო ციკლი
Vue-ში კომპონენტის სასიცოცხლო ციკლის ყოველ ფაზაზე შესაძლებელია კონკრეტული ინსტრუქციების შესრულება, კონკრეტული ფუნქციების გაშვება. თითო ფაზას უკავშირდება ერთი კონკრეტული ფუნქცია, რომელსაც სასიცოცხლო ციკლის ჰუკი ეწოდება. Vue-ში ჩაშენებულია შემდეგი ჰუკები :
  1. beforeCreate
  2. created
  3. beforeMount
  4. mounted
  5. beforeUpdate
  6. updated
  7. beforeUnmount
  8. unmounted
  9. errorCaptured
  10. renderTracked
  11. renderTriggered
  12. activated
  13. deactivated
  14. serverPrefetch

beforeCreate ჰუკი

beforeCreate ჰუკი ფიქსირდება კომპონენტის ინიციალიზაციამდე, ანუ მანამ, სანამ Vue მოახდენს ინსტანციაში აღწერილი მონაცემების, გამოთვლადი მეთოდების, სხვა მეთოდებისა და მოვლენათა დამმუშავებლების ჩატვირთვას. სხვა სიტყვებით თუ ვიტყვით ეს არის კომპონენტის სასიცოცხლო ციკლის საწყისი ფაზა.

App.vue :

<template>
    <button @click="this.activeComp = !this.activeComp">კომპონენტის გამოჩენა/დამალვა</button>
    <div>
        <comp-one v-if="activeComp"></comp-one>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                activeComp: false
            }
        }
    }
</script>
                
CompOne.vue :
<template>
    <p id="pResult">{{ text }}</p>
</template>

<script>
    export default {
        data() {
            return {
                text: '...'
            }
        },
        beforeCreate() {
            this.text = 'განახლებული ტექსტი'; // ეს ინსტრუქცია არ შესრულდება
            console.log("beforeCreate: კომპონენტი ჯერ არ შექმნილა.");
        }
    }
</script>   
                
main.js :
import { createApp } from 'vue'

import App from './App.vue'
import CompOne from './components/CompOne.vue'

const app = createApp(App)
app.component('comp-one', CompOne)
app.mount('#app')              

                
დავაკვირდეთ CompOne.vue კომპონენტში აღწერილ beforeCreate ჰუკს, მის პირველ ხაზში ვცდილობთ data პარამეტრში აღწერილი text პარამეტრის ცვლილებას, მაგრამ ეს ინსტრუქცია არ შესრულდება, ვინაიდან Vue-ს ჯერ არ მოუხდენია data პარამეტრის ჩატვირთვა. სამაგიეროდ, შესრულდება ამავე ჰუკში აღწერილი console.log ინსტრუქცია.

created ჰუკი

created ჰუკი ფიქსირდება მას შემდეგ რაც მოხდება კომპონენტის ინიციალზაცია, ამ დროს Vue-ს უკვე ჩატვირთული აქვს ინსტანციაში აღწერილი მონაცემები, გამოთვლადი მეთოდები და ა.შ.

created ჰუკიდან DOM ელემენტებთან წვდომა შეუძლებელია რადგან ამ დროს DOM ჯერ კიდევ არ არის დაგენერირებული.

created ჰუკი ხელსაყრელი ადგილია HTTP მოთხოვნების გასაგზავნად ან საინიციალიზაციო ინფორმაციების ასაღწერად.

CompOne.vue :
<template>
    <p id="pResult">{{ text }}</p>
</template>

<script>
    export default {
        data() {
            return {
                text: '...'
            }
        },
        created() {
            this.text = 'საინიციალიზაციო ტექსტი';
            console.log("created: კომპონენტი ახლახანს შეიქმნა.");
        }
    }
</script>   
                

beforeMount ჰუკი

beforeMount ჰუკი ფიქსირდება მანამ სანამ მოხდება კომპონენტის DOM-ში შეტანა, უხეშად თუ ვიტყვით 'დამონტაჟება'. ეს არის mounted ფაზის წინა ფაზა.

DOM ელემენტებთან წვდომა beforeMount ჰუკიდანაც შეუძლებელია. ქვემოთ მოყვანილი მაგალითი, სადაც DOM ელემენტის ტექსტის ცვლილებას ვცდილობთ, შეცდომას გამოიწვევს ბრაუზერის კონსოლში,

CompOne.vue :
<template>
    <p ref="pEl" id="pEl">შევეცადოთ მივწვდეთ ამ ტექსტს 'beforeMount' ჰუკიდან</p>
</template>

<script>
    export default {
        beforeMount() {
            console.log("beforeMount: კომპონენტის დამონტაჟებამდე დარჩენილია წამის მეასედები :))");
            this.$refs.pEl.innerHTML = "გამარჯობა!"; // <-- ჩვენ არ შეგვიძლია მივწვდეთ 'pEl' DOM ელემენტს ამ ფაზაზე
        }
    }
</script>    
                

mounted ჰუკი

mounted ჰუკი ფიქსირდება მას შემდეგ, რაც კომპონენტი ჩაჯდება DOM-ში. ამ დროს უკვე შესაძლებელია DOM ელემენტებით მანიპულირება.

CompOne.vue :
<template>
    <p>კომპონენტი</p>
</template>

<script>
    export default {
        mounted() {
            alert("კომპონენტი დამონტაჟდა DOM-ში!");
        }
    }
</script>
                

beforeUpdate ჰუკი

beforeUpdate ჰუკი ფიქსირდება მას შემდეგ, რაც ვახდენთ კომპონენტში აღწერილი ინფორმაციის ცვლილებებს თუმცა ეს ცვლილებები ჯერ კიდევ არ არის ასახული ეკრანზე.

App.vue :
<template>
    <button @click="this.activeComp = !this.activeComp">კომპონენტის გამოჩენა/დამალვა</button>
    <div>
      <comp-one v-if="activeComp"></comp-one>
    </div>
    <ol ref="divLog"></ol>
</template>
  
<script>
    export default {
        data() {
            return {
                activeComp: true
            }
        },
        beforeUpdate() {
            this.$refs.divLog.innerHTML += "<li>beforeUpdate: ეს მოხდება updated ჰუკამდე</li>";
        }
    }
</script>
                
CompOne.vue :
<template>
    <h2>კომპონენტი</h2>
</template> 
                
beforeUpdate ჰუკის განსაკუთრებულობა არის ის, რომ ამ ფაზაზე მშვიდად შეგვიძლია ცვლილებების გაკეთება აპლიკაციაში :)), მშვიდად გაკეთება კი გულისხმობს შემდეგს : შეტანილი ცვლილება არ გამოიწვევს კიდევ ერთ ცვლილებას, რომელიც, თავის მხრივ, კიდევ ერთს გამოიწვევს და ა.შ. უფრო დეტალურად ოდნავ ქვემოთ ვისაუბრებთ ამ უსასრულო ციკლზე და ყველაფერს ნათელი მოეფინება.

updated ჰუკი

updated ჰუკი ფიქსირდება მას შემდეგ, კომპონენტი განახლდება DOM-ში.

App.vue :
<template>
    <button @click="this.activeComp = !this.activeComp">კომპონენტის გამოჩენა/დამალვა</button>
    <div>
        <comp-one v-if="activeComp"></comp-one>
    </div>
</template>
  
<script>
    export default {
        data() {
            return {
                activeComp: true
            }
        },
        updated() {
            console.log("კომპონენტი განახლდა!");
        }
    }
</script>  
                

beforeUnmount ჰუკი

beforeUnmount ჰუკი ფიქსირდება კომპონენტის DOM-იდან წაშლამდე.

App.vue :
<template>
    <button @click="this.activeComp = !this.activeComp">{{ btnText }}</button>
    <div>
        <comp-one v-if="activeComp"></comp-one>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                activeComp: true
            }
        },
        computed: {
            btnText() {
                if(this.activeComp) {
                    return 'კომპონენტის წაშლა'
                }
                else {
                    return 'კომპონენტის გამოჩენა'
                }
            }
        }
    }
</script>
                
CompOne.vue :
<template>
    <p ref="pEl">გამარჯობა!</p>
</template>
    
<script>
    export default {
        beforeUnmount() {
            alert("beforeUnmount: p ტეგის ტექსტი არის: " + this.$refs.pEl.innerHTML);
        }
    }
</script> 
                
როგორც ვხედავთ beforeUnmount ჰუკის დროს ჯერ კიდევ ხელმისაწვდომია კომპონენტის ელემენტები - innerHTML ფუნქციის მეშვეობით მოვახერხეთ კომპონენტში არსებული p ელემნტის შიგთავსის წაკითხვა.

unmounted ჰუკი

unmounted ჰუკი ფიქსირდება კომპონენტის DOM-იდან წაშლის შემდეგ.

App.vue :
<template>
    <button @click="this.activeComp = !this.activeComp">{{ btnText }}</button>
    <div>
        <comp-one v-if="activeComp"></comp-one>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                activeComp: true
            }
        },
        computed: {
            btnText() {
                if(this.activeComp) {
                    return 'კომპონენტის წაშლა'
                }
                else {
                    return 'კომპონენტის გამოჩენა'
                }
            }
        }
    }
</script>
                
CompOne.vue :
<template>
    <h2>კომპონენტი</h2>
</template>

<script>
    export default {
        unmounted() {
            alert("კომპონენტი წაიშალა (unmounted)!");
        }
    }
</script> 
                
29. Vue მარშრუტიზაცია
Vue-ში მარშრუტიზაცია გამოიყენება აპლიკაციაში ნავიგაციისათვის, თანაც ეს ხდება კლიენტის მხარეს (ბრაუზერში), გვერდის ხელახალი ჩატვირთვის (refresh) გარეშე, ეს კი განაპრობებს სამომხმარებლო ინტერფეისის სისწრაფესა და კომფორტულობას. ჩვენ უკვე შევეხეთ ამდაგვარ საკითხს დინამიური კომპონენტების განხილვისას, მაგრამ მარშრუტიზცია გაცილებით დახვეწილი და მასშტაბური სისტემაა, რომელსაც ამ თავში განვიხილავთ.

როგორც ადრე აღვნიშნეთ Vue-ს მეშვეობით იქმნება ერთგვერდიანი აპლიკაციები (SPA) რომლებშიც მხოლოდ ერთი .html ფაილი გვაქვს, რაც იმას ნიშნავს, რომ შეუძლებელია მომხმარებელს გავუხსნათ სხვა .html ფაილი განსხვავებული შიგთავსის სანახავად.

Vue Router ბიბლიოთეკა

პირველ რიგში უნდა დავაინსტალიროთ Vue Router ბიბლიოთეკა, ამისათვის გადავინაცვლოთ პროექტის საქაღალდეში და გავუშვათ შემდეგი ბრძანება :
npm install vue-router@4
                
ახლა განვაახლოთ main.js ფაილი :
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'

import App from './App.vue'
import CompOne from './components/CompOne.vue'
import CompTwo from './components/CompTwo.vue'

const router = createRouter({
    history: createWebHistory(),
    routes: [
        { 
            path: '/one', 
            component: CompOne
        },
        { 
            path: '/two', 
            component: CompTwo
        }
    ]
});

const app = createApp(App)
app.use(router);
app.mount('#app')
                
App.vue
<template>
    <p>
        <!-- router-link კომპონენტი გამოიყენება სანავიგაციო ბმულების დასაგენერირებლად -->
        <!-- ბმულის გზის განსაზღვრა ხდება `to` ატრიბუტის მეშვეობით -->
        <!-- <router-link> კომპონენტი დააგენერირებს `<a>` ტეგს კორექტული `href` ატრიბუტითურთ -->
        <router-link to="/one">პირველი</router-link>
        <router-link to="/two">მეორე</router-link>
    </p>
    <!-- აქ ჩაიტვირტება კომპონენტი, რომლის შესაბამის ბმულსაც დავაწვებით -->
    <router-view></router-view>
</template>  
                
CompOne.vue
<template>
    <h2>პირველი კომპონენტი</h2>
</template>
                
CompTwo.vue
<template>
    <h2>მეორე კომპონენტი</h2>
</template>
                
ამ ყველაფერის შედეგი იქნება შემდეგი :



ბმულებს თუ გადავამოწმებთ ვიხილავთ შემდეგ სურათს :

router-link

როგორც ზემოთ ვნახეთ, სტანდარტული <a> ტეგის ნაცვლად გამოვიყენეთ <router-link> კომპონენტი, ეს Vue-ს საშუალებას აძლევს შეცვალოს მისამართი ბრაუზერის სამისამართო ველში, გვერდის ხელახალი ჩატვირთვის გარეშე (refresh).

router-view

<router-view> დირექტივა ქმნის წინასწარგამზადებულ სივრცეს, რომლის შევსებაც მოხდება იმ კომპონენტით, რომლის შესაბამის ბმულსაც დავაწვებით. ამ დირექტივის განთავსება შეგვიძლია ნებისმიერ ადგილას.

დინამიური მარშრუტები პარამეტრებით

ძალიან ხშირად საჭიროა, რომ მარშრუტს გადაეცეს დამატებითი პარამეტრები. წარმოვიდგინოთ ასეთი სიტუაცია: დავუშვათ გვინდა, რომ ჩვენს CompOne.vue კომპონენტში გამოვიტანოთ ინფორმაცია კონკრეტული მომხმარებლის შესახებ, ბუნებრივია სისტემას რამენაირად უნდა მივაწოთ საჭირო პარამეტრები იმის დასადგენად, თუ კონკრეტულად რომელი მომხმარებელი გვესაჭიროება. ამისათვის მარშრუტს უნდა გადავცეთ დამატებითი პარამეტრი. main.js :
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'

import App from './App.vue'
import CompOne from './components/CompOne.vue'
import CompTwo from './components/CompTwo.vue'

const router = createRouter({
    history: createWebHistory(),
    routes: [
        { 
            path: '/one/:id', 
            component: CompOne
        },
        { 
            path: '/two', 
            component: CompTwo
        }
    ]
});

const app = createApp(App)
app.use(router);
app.mount('#app')
                
როგორც ვხედავთ, დამატებითი პარამეტრის მითითება ხდება ორწერტილის მეშვეობით.

ამის შემდეგ /one/vaso და /one/giorgi და მათნაირ ბმულებს ერთი და იგივე მარშრუტი დაამუშავებს ასევე მოხდება ერთი და იგივე კომპონენტის ჩატვირთვა.

კომპონენტში მარშრუტის პარამეტრების გასაგებად გამოიყენება this.$route.params ჩანაწერი:

<template>
    <h2>პირველი კომპონენტი</h2>
    <div>{{ id }}</div>
</template>

<script>
    export default {
        data() {
            return {
                id: this.$route.params.id
            }
        }
    }
</script>
                
შაბლონი გზაბმული $route.params
/users/:username /users/vaso { username: 'vaso' }
/users/:username/posts/:postId /users/vaso/posts/123 { username: 'vaso', postId: '123' }
30. Vue ფორმის ველები

რადიო ღილაკები

რადიო ღილაკებს v-model ატრიბუტთან ერთად უნდა განესაზღვროს value ატრიბუტიც, App.vue :
<template>
    <form @submit.prevent="registerAnswer">
        <label>
            <input type="radio" name="favFood" v-model="inpVal" value="ხაჭაპური"> ხაჭაპური
        </label>
        <label>
            <input type="radio" name="favFood" v-model="inpVal" value="პიცა"> პიცა
        </label>
        <label>
            <input type="radio" name="favFood" v-model="inpVal" value="ლობიანი"> ლობიანი
        </label>    
        <button type="submit">არჩევა</button>
    </form>
    <div>
        <h3>არჩეული საკვები:</h3>
        <p >{{ inpValSubmitted }}</p>
    </div>
</template>

<script>
export default {
    data() {
        return {
            inpVal: '',
            inpValSubmitted: 'ჯერ არ არის არჩეული'
        }
    },
    methods: {
        registerAnswer() {
            if(this.inpVal) {
                this.inpValSubmitted = this.inpVal;
            }
        }
    }
}
</script>
                
main.js
import { createApp } from 'vue'

import App from './App.vue'

const app = createApp(App)
app.mount('#app')
                

ჩეკბოქსები

როდესაც <input type="checkbox"> ველების არჩეული მნიშვნელობები ინახება მასივში, App.vue :
<template>
    <form @submit.prevent="registerAnswer">
        <label>
            <input type="checkbox" v-model="likeFoods" value="ხაჭაპური"> ხაჭაპური
        </label>
        <label>
            <input type="checkbox" v-model="likeFoods" value="ლობიანი"> ლობიანი
        </label>
        <label>
            <input type="checkbox" v-model="likeFoods" value="პიცა"> პიცა
        </label>    
        <button type="submit">არჩევა</button>
      </form>
      <div>
            <h3>არჩეული საკვები:</h3>
            <p id="pAnswer">{{ inpValSubmitted }}</p>
      </div>
</template>

<script>
    export default {
        data() {
            return {
                likeFoods: [],
                inpValSubmitted: 'არ არის არჩეული'
            }
        },
        methods: {
            registerAnswer() {
                this.inpValSubmitted = this.likeFoods;
            }
        }
    }
</script>
                

ჩამოსაშლელი სიები

App.vue :
<template>
    <form @submit.prevent="registerAnswer">
        <label for="cars">აირჩიეთ მანქანა:</label>
        <select  v-model="carSelected">
            <option disabled value="">აირჩიეთ მანქანა</option>
            <option>Volvo</option>
            <option>Saab</option>
            <option>Opel</option>
            <option>Audi</option>
        </select>
        <br><br>
        <input type="submit" value="არჩევა">
    </form>
    <div>
        <h3>არჩეული მანქანა:</h3>
        <p id="pAnswer">{{ inpValSubmitted }}</p>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                carSelected: '',
                inpValSubmitted: 'არ არის არჩეული'
            }
        },
        methods: {
            registerAnswer() {
                if(this.carSelected) {
                    this.inpValSubmitted = this.carSelected;
                }
            }
        }
    }
</script>
                

<select multiple>

App.vue :
<template>
    <form @submit.prevent="registerAnswer">
        <select  v-model="carsSelected" multiple>
            <option>Volvo</option>
            <option>Saab</option>
            <option>Opel</option>
            <option>Audi</option>
            <option>Kia</option>
        </select>
        <button type="submit">არჩევა</button>
    </form>
    <div>
        <h3>არჩეული მანქანები:</h3>
        <p>{{ inpValSubmitted }}</p>
    </div>
</template>
  
<script>
    export default {
        data() {
            return {
                carsSelected: [],
                inpValSubmitted: 'არ არის არჩეული'
            }
        },
        methods: {
            registerAnswer() {
                if(this.carsSelected) {
                    this.inpValSubmitted = this.carsSelected;
                }
            }
        }
    }
</script>
                
31. Vue 'build' რეჟიმი
პროექტზე მუშაობისას, ანუ 'დამზადების რეჟიმში' (development mode), ხელსაწყო Vite ახდენს ამ რეჟიმის შესაბამისი სერვერის ამუშავებას. როდესაც პროექტში შეგვაქვს ცვლილებები Vite დაუყოვნებლივ ახდენს ბრაუზერის განახლებას. ეს კი დიდ რესურსებს მოითხოვს კომპიუტერისგან.

საჭიროა, რომ პროექტის დასრულების შემდეგ მოვახდინოთ პროექტის საბოლოო გენერირება, აგება, 'დაბილდვა' build ბრძანების დახმარებით. ბრძანება შექმნის .html, .js და .css ფაილებს, რომლებსაც ბრაუზერი 'დამზადების რეჟიმში' გაშვებული Vite ხელსაწყოს გარეშეც აღიქვამს, რაც შეამცირებს დატვირთვას სერვერზე.

პირველ რიგში უნდა გამოვრთოთ 'დამზადების რეჟიმი', ბრძანებათა ველიდან ავკრიფოთ 'Q' ან 'ctrl'+'C', შემდეგ კი გავუშვათ შემდეგი ბრძანება :

npm run build
                
დაბილდვის შემდეგ Vite შექმნის საქაღალდე dist-ს, რომელშიც შეინახება, პროექტის საჯარო სერვერზე გასაშვებად საჭირო .html, .js და .css ფაილები, რომელთა აღქმაც ბრაუზერს შეუძლია, *.vue გაფართოება მისთვის უცნობია.

დაბილდული პროექტის სანახავად უნდა გავუშვათ შემდეგი ბრძანება :

npm run preview
                
ბრაუზერში გაიხსნება dist საქაღალდეში არსებული ფაილების მიხედვით დაგენერირებული პროექტი.
32. Vue Options vs Composition
როგორც გვახსოვს, კურსის მსვლელობისას არაერთხელ ვახსენეთ ტერმინი 'კონფიგურაციული პარამეტრი', ამ პარამეტრების უკან მოიაზრდებოდა Vue-ს ინსტანციაში აღწერილი, ერთგბარი ბლოკები, კოდის ფრაგმენტები, რომლებიც გვიმარტივებდნენ კოდის საერთო იერსახის ჩამოყალიბებისა და სტრუქტურიზების პროცესს :
const app = Vue.createApp({
    data() {
        ...
    },
    watch: {
        ...
    },
    computed: {
        ...
    },
    methods: {
        ...
    }
    
    ...
    
})
                
ასეთ მიდგომას ეწოდება პარამეტრული მიდგომა (Options Api). ერთი შეხედვით, ამ მიდგომით საკმაოდ მარტივად იწერება კოდი, რაც მოსახერხებელია დამწყებთათვის. არსებობს სხვა მიდგომაც, რომელსაც თავისი უპირატესობები გააჩნია თუმცა მოითხოვს რიგი საკითხების უფრო სიღრმისეულ ცოდნას, ესაა კომპოზიციური მიდგომა (Composition Api). ამ მიდგომას აქვს ორი ძირითადი უპირატესობა :

  • შესაძლებელია რელევანტური ანუ ერთი და იმავე საკითხთან დაკავშირებული კოდის ფრაგმენტების დაჯგუფება, რის შედეგადაც იწერება უფრო ორგანიზებული და ლამაზი კოდი.
  • მარტივადაა შესაძლებელი კოდის განმეორებითად (reuse) გამოყენება კომპონენტებში.

ლამაზი და ორგანიზებული კოდი

წარმოვიდგინოთ, რომ გვაქვს ორი მეთოდი - ერთი უკავშირდება პროდუქტს, მეორე კი მომხმარებლებს. პარამეტრული მიდგომის (Options Api) შემთხვევაში, მიუხედავად იმისა, რომ ამ მეთოდებს საერთო არაფერი აქვთ, ორივე მათგანის აღწერა უნდა მოხდეს methods პარამეტრში.

ასევე თუ გვაქვს მომხმარებლებთან და პროდუქტთან დაკავშირებული ორი გამოთვლადი თვისება, ორივეს აღწერა უნდა მოხდეს computed პარამეტრში.

მომხმარებელთან დაკავშირებული თვისება და გამოთვლადი პარამეტრი, ნაცვლად იმისა, რომ ერთ სივრცეში, ერთ სექციაში მოექცეს, კოდის ორ სხვადასხვა ადგილას 'გაიფანტება', ანალოგიურად მოხდება პროდუქტისთვისაც. არადა უფრო ლოგიკურია პროდუქტთან დაკავშირებული კოდი გვქონდეს ცალკე, მომხმარებლებთან დაკავშირებული კოდი ცალკე და ა.შ

ამ ყველაფრის კიდევ უფრო კარგად გასაგებად დავაკვირდეთ ფოტოს :


ფოტოზე აღწერილ კოდში მანიპულირება ხდება სამი ძირითად საკითხით, ესენია :

  • პერსონალური ინფორმაცია
  • მომხმარებელთა სია
  • სასიცოცხლო ციკლის ჰუკები
ფოტოს მარცხენა მხარეს თუ დავაკვირდებით, ფერთა ცვალებადობიდან გამომდინარე, ადვილად გავიგებთ თუ რა იგულისხმება რელევანტური კოდის გაფანტულობაში პარამეტრული მიდგომისას (Options Api). საპირისპირო სიტუაციაა მარჯვენა მხარეს - კომპოზიციური მიდგომის (Composition Api) შემთხვევაში.

კოდის მრავალგამოყენებადობა

მოვიყვანოთ კომპოზიციური მიდგომით (Composition Api) დაწერილი კოდის მაგალითი, დავუშვათ რომელიმე კომპონენტში აღწერილი გვაქვს useUserList() ფუნქცია, რომელიც აბრუნებს users, usersList, addUser() და removeUser() მონაცემებსა და მეთოდებს :
// src/composables/user-list.js

import { computed, ref } from 'vue';

export const useUserList = () => {
    const users = ref(['Jane', 'Mark', 'Bob']);
    const usersList = computed(() => users.value.join(', '));
    const addUser = (user) => {
        users.value.push(user);
    };
    const removeUser = (username) => {
        users.value = users.value.filter((user) => user !== username);
    };

    return {
        users,
        usersList,
        addUser,
        removeUser,
    };
};
                
ამ ინფორმაციის გამოყენება მარტივადაა შესაძლებელი სხვა კომპონენტებში :
<script setup>
import { useUserList } from '@/composables/user-list';

const { users, usersList, addUser, removeUser } = useUserList();

</script>
                
useUserList() ფუნქციას ეწოდება კომპოზიციური ფუნქცია, სწორედ კომპოზიციური ფუნქციების მეშვეობითაა შესაძლებელი მრავალჯერადად გამოყენებადი კოდის წერა. გამომდინარე იქიდან, რომ useUserList() ფუნქციაში ვიყენებთ ref() და computed() ფუნქციებს, თუ composables/user-list.js ფაილში რაიმე ინფორმაცია შეიცვლება, ეს ცვლილება აისახება ყველა იმ კომპონენტში, რომელშიც useUserList() ფუქნცია გვექნება შეიმპორტებული.

***

ვთანხმდებით იმაზე, რომ კომპოზიციური მიდგომისას (Composition Api) არ ვიყენებთ ისეთ კონფიგურაციულ ბლოკებს როგორებიცაა data(), computed, methods, watch ... კომპოზიციური მიდგომაში მათ ნაცვლად გამოიყენება Vue-ში ჩაშენებული ფუნქციები, რომელთა იმპორტირებასაც ვახდენთ კომპონენტებში :

<template>
    <p>საწყისი რაოდენობა: {{ number }}</p>
    <button @click="remove">ერთის მოკლება</button>
    <p>"{{ storageComment }}"</p>
</template>
  
<script setup>
    import { ref, computed } from 'vue'
  
    const number = ref(10);
  
    function remove(){
        if(number.value>0){
            number.value--;
        }
    }
  
    const storageComment = computed(
        function(){
            if(number.value > 5) {
                return "ჯერ კიდევ არის მარაგი"
            }
            else if(number.value > 0){
                return "მარაგი იწურება"
            }
            else {
                return "მარაგი ამოიწურა"
            }
        }
    )
</script>