در طی ۹ جلسهی گذشته آموزشهای مقدماتی و متوسط فریمورک قدرتمند جاوا اسکریپت Vue.js در اختیار شما قرار گرفت. در این بخش با آخرین جلسهی آموزشی Vue.js در خدمت شما هستیم. البته ناگفته نماند که این آخرین نوشته ما درباره Vue.js نیست بلکه جلسات متعدد دیگری در اختیار شما قرار میگیرد تا بتوانید پروژههای کاربردیتر و اصولیتر را با این فریم ورک زیبا و بینظیر پیادهسازی کنید.
کامپوننت vue.jsها از بزرگترین تواناییهای فریمورک Vue.js هستند. آنها به شما کمک میکنند تا المانهای HTML را داخل یک بستهبندی مناسب و قابل استفاده، توسعه دهید. در سطوح بالاتر، کامپوننت vue.jsها المانهای تولیدی خواهند بود که کامپایلر Vue رفتارهای این المانها را به آن متصل میکند. در برخی موارد، میتوان از کامپوننت vue.jsها به عنوان یک مجموعهی HTML کاملا مجزا استفاده کرد که با استفاده از صفت is قابل توسعه است.
شما در جلسههای گذشته آموختید که میتوان یک نمونه جدید از Vue را ایجاد کرد. بنابراین داریم:
new Vue({ el: "#some-element", // options })
برای ثبت یک کامپوننت گلوبال، باید از دستور Vue.component(tagName, options) استفاده کرد. به عنوان مثال:
Vue.component('roxo-component',{ // options })
توجه داشته باشید که Vue قوانین W3C را برای تگهای دلخواه کامپوننتها اجرا نمیکند. بر اساس قانون اجرایی این فریم ورک معمولا نامهای کامپوننتها با حروف کوچک و فاصلهی بین کلمات با خط تیره (-) مشخص میشود.
پس از ثبت نام، کامپوننت میتواند به عنوان یک نمونه قالب با المانهای دلخواه مورد استفاده قرار بگیرد. مثلا به صورت قالب <roxo-component></roxo-component>. درنظر داشته باشید که کامپوننتهاحتما باید قبل از مقداردهی نمونه اصلی Vue، تعریف و ثبت شوند. در اینجا یک مثال کامل برای روشنتر شدن مفهوم کامپوننتها شرح میدهیم:
<div id="roxoApp"> <roxo-component></roxo-component> </div> // ثبت و تعریف کامپوننت Vue.component('roxo-component', { template: '<div> یک کامپوننت دلخواه تعریف کردم </div>' }) // ساخت یک نمونه اصلی از Vue new Vue({ el: '#roxoApp' })
در نهایت خروجی شما به صورت کد زیر خواهد بود:
<div id="roxoApp"> <div> یک کامپوننت دلخواه تعریف کردم </div> </div>
و چیزی که شما در صفحه خروجی خودتان میبینید:
یک کامپوننت دلخواه تعریف کردم
نیازی نیست که تمام کامپوننتها به صورت گلوبال تعریف شوند. بلکه میتوان کامپوننتی ایجاد کرد که تنها برای یک محدود مشخص از نمونهها یا کامپوننتها قابل دسترسی باشد. تنها کافیست آن را به صورت نمونه components تعریف کنید. مثال زیر را با هم بررسی میکنیم:
var Child = { template: '<div> یک کامپوننت دلخواه تعریف میکنم</div>' } new Vue({ //... components: { 'roxo-component' : Child } })
همانطوری که مشاهده میکنید کامپوننت roxo-component تنها برای قالبهای والدش در دسترس است.
هنگامیکه از DOM برای قالب خود استفاده میکنید ( به عبارت دیگر از گزینه el برای فعالسازی المانها و محتوای درون آنها استفاده میکنید)، شما میتوانید محدودیهایی که به صورت ذاتی در کارایی HTML وجود دارند را نشانهگذاری کنید. زیرا Vue تنها میتواند محتوای قالب را پس از پردازش و بهینهسازی توسط مرورگر، بازیابی کند. توجه کنید، برخی از المانها مانند <ul>, <ol> یا <table> و <select> محدودیتهایی را برای محتوایی که درون آنها قرار میگیرند، دارند یا بعضی از المانها مانند <option> حتما باید درون یک المان دیگری قرار بگیرند.
این مشکل هنگام استفاده از یک کامپوننت دلخواه به همراه المانها وجود داره که محدودیتهایی را شامل میشود به عنوان مثال:
<table> <roxo-row> ... </roxo-row> </table>
در این مثال کامپوننت دلخواه roxo-row درون یک المان قرار داده شده که محتوای غلط را تولید میکند. یک راه حل استفاده از صف ویژه is درون این المانهاست که به صورت زیر میباشد:
<table> <tr is="roxo-row"></tr> </table>
بسیاری از گزینههایی که در سازندهی Vue مشاهده میشوند، داخل کامپوننتها نیز قابل استفاده هستند ولی یک موضوع را باید خیلی جدی بگیرید و به آن توجه کنید: data باید یک تابع باشد. در واقع اگر شما دستور زیر را اجرا کنید:
Vue.component('roxo-component', { template: <span>{{ message }}</span>, data:{ message: 'hello' } })
قطعا کنسول به شما اخطار میدهد و برنامهی شما متوقف میشود و پیامی که برای شما صادر خواهد شد: data برای نمونههای کامپوننت باید یک تابع باشد. چه بهتر است که بدانید دلیل این امر چیست؟ بنابراین اجازه دهید یک کد را اجرا کنیم:
<div id="roxoApp"> <roxo-component></roxo-component> <roxo-component></roxo-component> <roxo-component></roxo-component> </div> var data = { counter: 0 } Vue.component('roxo-component', { template: '<button v-on:click="counter +=1">{{ counter }}</button>', data: function(){ return data } }) new Vue({ el: '#roxoApp' })
خروجی کد بالا را اینجا مشاهده کنید.
همانگونه که مشاهده میکنید هر سه نمونهی کامپوننت یک مقدار مشابه برای شیء data درنظر گرفته اند که به اشتراک گذاشته است. با افزایش هر بلوک تمام بلوک ها افزایش پیدا میکنند. برای حل این مشکل باید هر بار یک دادهی جدید را برگرداند. به کد زیر دقت کنید:
<div id="roxoApp"> <roxo-component></roxo-component> <roxo-component></roxo-component> <roxo-component></roxo-component> </div> var data = { counter: 0 } Vue.component('roxo-component', { template: '<button v-on:click="counter +=1">{{ counter }}</button>', data: function(){ return { counter: 0 } } }) new Vue({ el: '#roxoApp' })
بنابراین با اجرای مجموعه کد بالا هر ستون از ابتدا مقدار دهی شده و مقادیر هر بلوک متفاوت خواهد بود. برای مشاهده خروجی کد بالا اینجا کلیک کنید.
کامپوننتها اغلب موارد باهمدیگر مورد استفاده قرار میگیرند که معمولترین آنها رابطه والد-فرزند است. مثلا کامپوننت A ممکن است از کامپوننت B در المان template خود استفاده کند. بنابراین هر دو آنها نیاز دارند که به ناچار با هم در ارتباط باشند. کامپوننت والد ممکن است نیاز داشته باشد تا data را به طبقه پایینتر (فرزند) ارسال کند و همچنین کامپوننت فرزند ممکن است نیاز داشته باشد تا تغییرات و اتفاقهای رخ داده درون خودش را به طبقه بالاتر (والد) اطلاع دهد.
در فریمورک Vue.js، کامپوننت والد-فرزند (parent-child component) به صورت props down و events up خلاصهنویسی میشوند. کامپوننت والد data را از طریق props به کامپوننت فرزند ارسال میکند و کامپوننت فرزند پیامها را از طریق events به کامپوننت والد انتقال میدهد. توجه داشته باشید در اینجا منظور از کامپوننت والد یک کامپوننت مجزا نیست بلکه یک دستور از بالا صادر شده و به پایین ارسال میشود و در نهایت از والد (بالا) به فرزند (پایین) دادهها تحت عنوان props ارسال شده و نتیجه از فرزند (پایین) به والد (بالا) تحت عنوان events ارسال میشود. برای فهم بیشتر این موضوع به تصویر ذیل دقت کنید:
هر نمونهی (Instance) کامپوننت یک محدوده و دامنهی محدود به خود را دارد. این بدین معنی است که شما نمیتوانید (و نباید) به صورت مستقیم به دادههای والد در یک template کامپوننت ارجاع بدهید. داده میتواند تنها با استفاده از props از کامپوننت والد به کامپوننت فرزند انتقال پیدا کند.
prop به عنوان یک صف دلخواه برای ارسال اطلاعات از کامپوننت والد مورد استفاده قرار میگیرد. کامپوننت فرزند باید به صورت صریح و مشخص props را برای دریافت اطلاعات، تعریف کند به مثال زیر توجه کنید:
Vue.component('child',{ //تعریف props برای دریافت اطلاعات props=['message'], // دقیقا مشابه data // props تنها داخل تمپلت ها استفاده میشوند // و همواره درون vm تحت عنوان this.message در دسترس هستند template: '<span>{{ message }}</span>' })
در نتیجه میتوان یک رشته خالی را به کامپوننت فرزند ارسال کرد:
<child message="hello!"></child>
بنابراین خروجی کد بالا به صورت ذیل خواهد بود:
hello!
صفات HTML به بزرگ و کوچک بودن حروف حساس هستند، بنابراین هنگامیکه از ساختار غیررشتهای templateها استفاده میکنید، ساختار نوشتاری camelcase برای اسامی propها باید به صورت kebab-case از کامپوننتها ارسال شوند. درست شبیه به مثال ذیل:
<child my-message="hello!"></child> Vue.component('child',{ //camelCase in JavaScript props=['myMessage'], template: '<span>{{ myMessage }}</span>' })
مشابه مبحث اتصال و ارسال دادهها (Binding)، میتوان از v-bind برای ارسال دادههای props به صورت کاملا پویا و دینامیکی به کامپوننت والد استفاده کرد. منطق به این صورت است که هرگاه دادهای در کامپوننت والد آپدیت و بروزرسانی شود، آن داده در کامپوننت فرزند نیز بروزرسانی خواهد شد:
<div> <input v-model="parentMessage"> <br> <child v-bind:my-message="parentMessage"></child> </div>
دستور سادهتر برای نمایش این روش به صورت زیر میباشد:
<child :my-message="parentMessage"></child>
اشتباه رایجی که در افراد مبتدی وجود دارد، تمایل به ارسال اطلاعات با سختار لیترال است، مانند مثال زیر:
// ارسال یک رشته خالص با مقدار "1" به پایین <comp some-prop="1"><comp>
بنابراین دستور بالا یک prop ثابت است اما مقداری که به کامپوننت فرزند ارسال میشود عدد واقعی 1 نمیباشد بلکه مقدار رشتهی "1" است. درصورتیکه نیاز داشتید یک عدد جاوا اسکریپتی واقعی را به کامپوننت فرزند ارسال کنید، بهتر است از دستور v-bind استفاده نمایید. زیرا این دستور یک عبارت جاوا اسکریپتی را ارسال میکند. بنابراین کد زیر یک روش صحیح برای ارسال اعداد به طبقه پایینتر (فرزند) است:
// ارسال یک عدد واقعی به پایین تر <comp v-bind:some-prop="1"></comp>
تمام Propها از یک راه ( بالا به پایین) بین ویژگیهای فرزند و والد انتقال پیدا میکنند. هر گاه دادهی والد آپدیت و بروزرسانی شود، به دادهی فرزند ارسال شده و آن نیست بروزرسانی خواهد شد و هیچ راه دیگری برای این انتقال وجود ندارد. این امر باعث میشود کامپوننتهای فرزند به صورت اتفاقی و تصادفی وضعیت والد خود را تغییر ندهند.
علاوه بر این، هر وقت کامپوننت والد آپدیت شود، همهی propهای کامپوننتهای فرزند نیز متناسب با آخرین وضعیت مقادیر و دادههای خود، بروزرسانی و نوسازی (refresh) میشوند. این بدین معنیست که شما نباید propsهای موجود در کامپوننت فرزند را تغییر بدهید. اگر شما اینکار را انجام بدهید، کنسول Vue به شما اخطار خواهد داد.
هم اکنون دو وضعیت وجود دارد که شمارا برای تغییر prop وسوسه میکند:
۱) یک Prop تنها برای ارسال یک مقدار اولیه استفاده میشود، کامپوننت فرزند به سادگی میخواهد بعدا از این Prop برای ویژگیهای دادهها به صورت محلی استفاده کند.
۲) یک Prop به عنوان یک مقدار خام ارسال شود.
در این حالت پاسخ به دو وضعیت بالا به صورت زیر خواهد بود:
۱) تعریف یک ویژگی داده محلی که مقدار اولیه prop در آن ذخیره میشود:
props: ['initialCounter'], data: function(){ return { counter: this.initialCounter } }
۲) تغریف یک ویژگی Computed که وظیفه محاسبات مقادیر دریافتی از prop را به عهده داشته باشد:
props: ['size'], computed:{ nomalizedSize: function(){ return this.size.trim().toLowerCase() } }
برای یک کامپوننت میتوان روی Propهای دریافتیاش، یک سری قید و بند تعریف کرد. اگر این قید و بند برقرار نباشد، Vue یک اخطاری را نمایش نمیدهد. این ویژگی زمانی ارزشمند میباشد که شما بخواهید تصدیق هویت یک کامپوننت که داخل سایر کامپوننتها استفاده میشود، بررسی کنید.
به جای تعریف Propها به عنوان رشتهای از آرایهها، میتوان از یک شیء با بررسی تصدیق آن استفاده کرد:
Vue.component('example', { props: { // مقدار برابر عدد propA: Number, // تعریف چندین نوع برای پراپ ها propB: [String, Number], //اجباری کردن یک رشته propC: { type: String, required: true }, // قراردادن مقدار پیش فرض برای یک عدد propD:{ type: Number, default: 100 }, //شیء و آرایه به صورت پیش فرض توسط یک تابع بازگردانده میشود propE{ type: Object, default: function(){ return { message: 'hello' } } }, // اجرای یک تصدیق کننده خودکار propF{ validator: function(value){ return value > 10 } } } })
type هایی که میتوان داخل یک prop استفاده کرد به شرح زیر میباشد:
تا به این جای کار باید آموخته باشید که اطلاعات توسط والد به پایینتر (فرزند) ارسال میشود که این ارسال با استفاده از Propها صورت میپذیرد، اما اگر اتفاقی در فرزند (پایین) به وقوع بپیوندد ما چگونه با والد در ارتباط باشیم؟ پاسخ به این سوال استفاده از سیستم رویداد دلخواه (Custom Event System)، میباشد.
هر نمونه Vue تحت عنوان یک رابط رویداد اجرا و پیادهسازی میشود، بدین معنی:
علاوهبر این، کامپوننت والد میتواند ارسال اطلاعات متناسب با رویدادهایی را انچام بدهد که توسط کامپوننت فرزند حذف شدهاند. این کار با استفاده از دستور v-on در template های فرزند، امکان پذیر است. به مثال زیر توجه کنید:
<div id="counter-event-example"> <p>{{ total }} </p> <button-counter v-on:increment="incrementTotal"></button-counter> <button-counter v-on:increment="incrementTotal"></button-counter> </div> Vue.component('button-counter', { template: '<button v-on:click="increment">{{ counter }}</button>', data: function(){ return{ counter: 0 } }, methods: { increment: function(){ this.counter += 1 this.$emit('increment') } } }) new Vue({ el: '#counter-event-example', data: { total: 0 }, methods: { incrementTotal: function(){ this.total +=1 } } })
خروجی کد بالا را اینجا مشاهده کنید.
جامع ترین دوره آموزشی Vue.js 2.0 در ایران را می توانید از طریق اینجا مشاهده کنید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
Vue.component('child',{ //تعریف props برای دریافت اطلاعات props=['message'], // دقیقا مشابه data // props تنها داخل تمپلت ها استفاده میشوند // و همواره درون vm تحت عنوان this.message در دسترس هستند template: '{{ message }}' })
این دقیقا کدی هست که توی کامپوننت والد نوشته میشه در واقع شما دارید با این کد یک پروپرتی از نوع message رو به کامپوننت child یا فرزند ارسال می کنید.در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.