Vuex یک افزونه Vue است که به عنوان یک ذخیرهساز مرکزی داده برای تمام بخشهای برنامه front-end شما عمل میکند. یک برنامه Vue از تعداد زیادی کامپوننت مجزا اما هماهنگ تشکیل شده است که وقتی کنار هم قرار می گیرند، یک برنامه کاربردی front-end را ایجاد می کنند. شما ممکن است یک صفحه برای ویرایش کاربران، یک صفحه برای نمایش کاربران و یا یک صفحه برای نشان دادن یک کاربر خاص داشته باشید. همه این صفحه ها و کامپوننت ها باید با هم کار کنند و با یکدیگر ارتباط داشته باشند تا بتوانیم یک برنامه کامل و کاربردی داشته باشیم.
Vue.js یک کتابخانه برای توسعه frontend است که برای طراحی رابط کاربری به کار می رود و امروزه محبوبیت زیادی پیدا کرده است. Vuex یکی از پیادهسازیهای مدل Vue js است، یا به عبارت دیگر میتوان گفت یک State برای نمایش دادهها است.
Vuex یک الگو یا کتابخانه برای مدیریت state برنامه است و به عنوان یک ذخیره ساز داده های برنامه کار می کند. ایده اصلی برای ساخت آن از کتابخانه Redux و Flux React الهام گرفته شده است. استفاده از Vuex زمانی مطرح می شود که برنامه سمت کلاینت پیچیده و پیچیده تر می شود. اگر برنامه پیچیده نباشد، نباید از Vuex استفاده کرد زیرا اگر به درستی با این کتابخانه کار نشود، استفاده از آن پیچیده و گیج کننده خواهد بود.
یک برنامه ساخته شده با Vuex از بخش های زیر تشکیل شده است:
شکل زیر یک نمایش از مفهوم "جریان داده یک طرفه" است:
action ها mutation ها را فراخوانی می کنند و mutation ها شی را تغییر می دهند. بنابراین state با توجه به یک action فعال تغییر می کند. state برنامه تغییر می کند و با توجه به آن کامپوننت تغییرات را مشخص می کند و براساس آن واکنش می دهد.
کامپوننت Vue در واکنش به یک رویداد یک action را آغاز می کند. با توجه به نوع action، mutation رخ می دهد و سپس state تغییر می کند و سایر اجزای Vue از این تغییرات مطلع می شوند.
اگر از Vuex استفاده کنید، آنگاه برای هر برنامه ساخته شده با VueJS فقط یک store خواهید داشت. استور Vuex یک منبع یکتا ساخته شده از state است. store دارای یک نمونه از state برنامه است. مدیریت state در VueJS با استفاده از Vuex ساده است. باید state را از طریق فرستادن action تغییر دهید و نه به طور مستقیم، زیرا میخواهیم state های آینده را با استفاده از آن پیشبینی کنیم.
تنها راه برای تغییر state در استور Vuex، انجام یک mutation است. میتوانیم به طور مستقیم state را تغییر دهیم، اما این کار را انجام نمیدهیم زیرا برای هر مرحله از پروژه خود به مقدار state نیاز داریم. برای جلوگیری از بروز اشکال، state را به طور مستقیم تغییر نمی دهیم، بلکه این کار را از طریق mutation ها انجام می دهیم.
action ها مشابه mutation ها هستند ولی تفاوت هایی نیز دارند که در زیر آمده است:
همان طور که در بالا گفته شد یک برنامه Vue از بخش های state، view و actions تشکیل شده است.به شکل زیر نگاه کنید:
شکل بالا جریان داده یک طرفه داده در یک برنامه Vue را نشان می دهد.
وقتی چندین کامپوننت داریم که state مشترکی دارند، سادگی از بین می رود:
برای مشکل اول، فرستادن prop ها می تواند برای کامپوننت های تو در تو خسته کننده باشد و برای کامپوننت هایی که پدر مشترکی دارند کار نمی کند.
برای مشکل دوم دو راه حل داریم:
هر دو این راهحلها شکننده هستند و به کدهای غیرقابل نگهداری منجر می شوند.
پرسش: چرا state اشتراکی را از کامپوننت استخراج نکنیم و آن را به صورت سراسری (global) مدیریت نکنیم؟
با این کار، درخت کامپوننت به یک «View» بزرگ تبدیل میشود و هر کامپوننتی در هر جایی از درخت که باشد به state دسترسی پیدا می کند و ممکن است action لازم را راهاندازی کند.
با تعریف و جداسازی مفاهیم دخیل در مدیریت state و اجرای قوانینی که استقلال بین view ها و state ها را حفظ می کند، به کد خود قابلیت نگهداری بیشتری می دهیم.
نمودار جریان داده که تصویر آن در پایین آمده است به توسعه دهندگان کمک می کند تا بفهمند که آیا برای برنامه خود به Vuex نیاز دارند یا خیر:
همچنین در اسناد Vuex بخشی وجود دارد که به طور خلاصه توضیح می دهد چه زمانی باید از Vuex استفاده کنید.زمانی که چندین کامپوننت داشته باشیم که یک state مشترک دارند جریان داده قبلی به سرعت از بین می رود:
این زمانی است که Vuex به کمک می آید.
Vuex یک سیستم مدیریت state است. بنابراین، برای درک آنچه Vuex انجام می دهد، ابتدا باید درک خوبی از مفهوم state در یک SPA و نحوه تعامل آن با سایر عناصر داشته باشید.
state دادههایی است که کامپوننت های برنامه به آن وابسته هستند. به عنوان مثال، کارهای موجود در لیست کارهای روزانه، یا پست های یک وبلاگ. هر کامپوننت Vue تنها می تواند یک state داشته باشد.
همان طور که برنامه رشد می کند، ممکن است تعداد کامپوننت ها نیز افزایش یابند. اگر از کتابخانه مدیریت state مانند Vuex استفاده نکنید، هر یک از این کامپوننت ها با استفاده از API های Vue.js که برای این منظور طراحی شده اند، state خود را مدیریت می کنند.
به خاطر داشته باشید که Vue یک معماری کامپوننت درختی را همانطور که در بالا نشان داده شده است حفظ می کند و همچنین ارتباط پدر-فرزند را از طریق prop ها و ارتباط فرزند-پدر را از طریق رویدادها امکان پذیر می کند. می توان تصور کرد دو state که پدر یکسانی دارند برای واکنش نشان دادن به تغییر دادههای یکسان چه مراحل پیچیده ای را باید پست سر بگذارند. باید دادهها را از فرزند به پدر منتقل کنیم تا از آن جا به نزدیکترین جد و سپس از آن جد به یک فرزند خاص فرستاده شود.(نگاه کنید به اجزای برجسته شده در شکل بالا).
این کار بسیار پر زحمت و دارای خطاهای احتمالی زیادی است.
این دقیقا همان جایی است که Vuex وارد میشود. Vuex کار نگهداری state یک ماژول و نگهداری آن را در یک store متمرکز را انجام میدهد، که از الگوی مدیریت استیت Vuex پیروی میکند و بر این اساس با کامپوننت های شما ارتباط برقرار میکند.
به همین دلیل است که اغلب از آن به عنوان «the single source of truth یا منبع واحد حقیقت» یاد می شود. اگرچه Vuex به این شکل نامیده می شود، اما این واقعیت را تغییر نمی دهد که کامپوننت ها همچنان می توانند state های خود را داشته باشند.
اکنون که فهمیدید چرا ممکن است به یک سیستم مدیریت state مانند Vuex نیاز داشته باشیم، میتوانیم دو دلیل اصلی که آیا state را باید با استفاده از Vue مدیریت کنیم یا نه را بررسی کنیم.
اغلب گفته می شود که «با رشد برنامه، به Vuex نیاز خواهید داشت». در واقع این بستگی به نحوه رشد آن دارد. برنامه شما ممکن است رشد کند اما جریان دادههای شما هستهای باقی میماند (یعنی پدر-فرزند و خواهر و برادر نزدیک).
در نهایت، میتوانید از ابزارها و رویدادها برای به اشتراک گذاشتن این دادهها استفاده کنید، بدون اینکه نیازی به اضافه کردن Vuex داشته باشید.
از طرف دیگر، به محض اینکه متوجه شدید که کامپوننت شما نیاز به یک منبع داده انتزاعی (یعنی یک منبع داده مشترک که قابلیت دسترسی به آن توسط سایر کامپوننت ها وجود داشته باشد) دارند، عاقلانه است که استفاده از prop ها و رویدادها را متوقف کنید و از Vuex کمک بگیرید. این باعث صرفه جویی در زمان می شود، زیرا هر چه دیرتر از Vuex استفاده کنید، نیاز بیشتری به دوباره نویسی کد دارید.
گاهی اوقات لازم است اجرای یک منطق پیچیده خاص را از کامپوننت خود جدا کنیم، به خصوص اگر این منطق با سایر کامپوننت های سیستم ما مرتبط باشد. یک مثال عالی از این مورد استفاده، احراز هویت یا Authentication است.
Vuex یک روش محبوب برای کنترل احراز هویت یک برنامه پیچیده در Vue است. با Vuex، میتوانید در دسترس بودن توکن و مجوز ها و مسیرها را در سراسر برنامه خود مدیریت کنید. mutation ها، getter ها و setter ها به این کار کمک می کنند.
این الگو به شما کمک می کند منطق احراز هویت را به طور کامل از منطق برنامه جدا کنید. دارد.
Vuex به ما کمک می کند تا با مدیریت state مشترک، دامنه مفاهیمی که باید بدانیم را کمتر کنیم. در واقع یک مبادله بین بهره وری کوتاه مدت و بلند مدت است.
اگر هرگز یک SPA در مقیاس بزرگ ایجاد نکرده اید و به طور مستقیم از Vuex استفاده نکرده اید، ممکن است ترسناک به نظر برسد. این کاملا طبیعی است. اگر برنامه شما ساده باشد، به احتمال زیاد بدون Vuex عملکرد خوبی خواهد داشت. یک الگوی store ساده ممکن است تمام چیزی باشد که برای ساخت برنامه به آن نیاز داشته باشید.
اما اگر در حال ساخت یک SPA در مقیاس متوسط تا بزرگ هستید، به احتمال زیاد با موقعیتهایی روبه رو شدهاید که باعث شده است به مدیریت بهتر state کامپوننت های Vue فکر کرده باشید. Vuex انتخاب بعدی شما برای مدیریت state خواهد بود. نقل قول خوبی از دن آبراموف، سازنده Redux وجود دارد:
«کتابخانه های Flux مانند عینک هستند زمانی که به آن ها نیاز دارید، بسیار ضروری است که آن را پیاده سازی کنید.»
همچنین می گوید: "اگر برنامه شما دارای state های بسیار زیادی است و نمی توانید یک کتابخانه باز را مدیریت کنید، پس باید از Redux استفاده کنید." در مورد Vue باید از Vuex استفاده کرد.
به چند نکته زیر دقت کنید:
دلیل اصلی دوری برنامه نویسان از Vuex این است که کدهای زیادی دارد. باید این طور باشد. برای اطمینان از این که تغییرها در state شما اتفاق میافتد، باید از یک الگو پیروی کند. در زیر دو کامپوننت todo آمده است که یکی از آن ها از Vuex استفاده می کند و دیگری از Vuex استفاده نمی کند:
تصویر بالا نشان می دهد که هنگام استفاده از Vuex برای مدیریت state خود در یک برنامه بسیار ساده، کد شما چقدر زیاد می شود. در مواردی مانند این، بهتر است از Vuex صرف نظر کنید و از prop ها و رویدادها یا یک الگوی ساده مدیریت state کمک بگیرید.
روش های نصب Vuex را در زیر بررسی می کنیم.
Unpkg.com لینکهای CDN مبتنی بر NPM را ارائه میدهد. این لینک همیشه به آخرین نسخه در NPM اشاره می کند. همچنین میتوانید از طریق آدرسهایی مانند https://unpkg.com/vuex@4.0.0/dist/vuex.global.js از یک ورژن/تگ خاص استفاده کنید.
vuex را بعد از Vue قرار دهید تا به طور خودکار نصب شود:
npm install vuex@next --save
yarn add vuex@next --save
اگر میخواهید از آخرین نسخه dev استفاده کنید، باید به طور مستقیم آن را از GitHub کلون کنید و خودتان vuex را بسازید.
git clone https://github.com/vuejs/vuex.git node_modules/vuex cd node_modules/vuex yarn yarn build
همه آموخته های خود را تا این جا در قالب یک پروژه پیاده خواهیم کرد. با این کار یادگیری ما کامل می شود و دیرتر از یاد می رود.
پروژه ای که این جا می سازیم یک برنامه todo ساده است.آموزش زیر را گام به گام دنبال کنید تا در پایان یک برنامه کارهای روزانه یا todo app داشته باشید.تصویر پروژه در زیر آمده است:
Node.js
ابتدا باید Vue CLI را نصب کنید. اگر Node.js را نصب نکرده اید، آن را از نشانی دانلود و نصب کنید.
اگر Node.js را نصب کرده اید، با خط node --version می توانید ورژن آن را بررسی کنید. همچنین با دستور npm install -g npm@latest می توانید آن را به روزرسانی کنید.
از Vue CLI 3 برای ساخت استخوان بندی برنامه استفاده خواهیم کرد. اگر قبلا Vue CLI را نصب کرده اید، با دستور vue --version می توانید ورژن آن را بررسی کنید. اگر آن را نصب نکرده اید با دستور npm install -g @vue/cli آن را نصب کنید.
اکنون که از ورژن های مناسب Node و Vue CLI استفاده می کنیم، می توانیم پروژه خود را ایجاد کنیم.
در ترمینال خود، به دایرکتوری که می خواهید پوشه پروژه شما در آن قرار گیرد، بروید. یا می توانید یک پوشه ایجاد کنید و آن را با ویرایشگر کد خود باز کنید. من نام این پوشه را todo-app گذاشته ام و در Desktop من قرار دارد.سپس ترمینال vscode را باز کنید و دستور . vue create را اجرا می کنیم.
پس از این کار منویی برای پیکربندی پروژه به شما نمایش داده می شود. در نخست از شما خواسته می شود که آیا پروژه در این همین مسیر ایجاد شود یا نه. برای تایید y را ترمینال می نویسیم و سپس Enter را می زنیم:
پس از این کار باید گزینه Manually select features را انتخاب کنیم و Enter را بزنیم:
بعد از بین گزینه های ظاهر شده Vuex را انتخاب می کنیم و دوباره Enter را می زنیم:
بعد از گزینه های ظاهر شده ESLint + Standard config را انتخاب می کنیم:
پس از همه کارهای بالا منتظر می شویم تا پروژه ساخته شود. این کار ممکن است کمی طول بکشد. پس از نصب پروژه پیام زیر باید در ترمینال ظاهر شود:
پیش از این که به کدنویسی را شروع کنیم، اجازه دهید Vuex را یکبار دیگر مرور کنیم:
در Vuex، چیزی به نام store داریم که یک شی global یا سراسی است که خود شامل چهار چیز اصلی است که برای مدیریت state در برنامه Vue به آن ها نیاز دارد. آن چیزها عبارتند از:
یک کاربر با برنامه Vue تعامل خواهد داشت و این تعاملات باعث ایجاد action هایی می شود که باعث تغییر در state می شود. پس از تغییر state ،Vue کامپوننت های تغییر کرده را بر اساس این state جدید دوباره رندر میکند.
اولین قدم در استفاده از Vuex این است که تصمیم بگیریم برنامه ما به چه داده هایی در state نیاز دارد. این برنامه مجموعهای از کارهای روزانه (todo ها) را ردیابی میکند، بنابراین تنها چیزی که در state خود به آن نیاز داریم todo ها هستند.
هر آیتم در آرایه باید چند تا چیز داشته باشد:
در پوشه src پروژه، یک پوشه جدید به نام store ایجاد کنید.یک فایل به نام store.js را در پوشه store ایجاد کنید.
در پوشه store، یک پوشه جدید به نام modules ایجاد کنید. سپس یک فایل جدید در پوشه modules به نام todos.js ایجاد کنید:
ابتدا یک آرایه خالی برای todo ها در فایل todos.js ایجاد می کنیم:
export const state = { todos: [] };
بعد یک تابع Getter خواهیم ساخت که آرایه todos را برمی گرداند.
export const getters = { getTodos: state => state.todos};
اکنون باید تصمیم بگیریم که state در کدام mutation قرار بگیرد. سه تغییر برای state وجود دارد که در نظر خواهیم گرفت:
export const mutations = { ADD_TODO: (state, payload) => { const newTask = { id: payload.newId, task: payload.task, completed: false }; state.todos.unshift(newTask); }, TOGGLE_TODO: (state, payload) => { const item = state.todos.find(todo => todo.id === payload); item.completed = !item.completed; }, DELETE_TODO: (state, payload) => { const index = state.todos.findIndex(todo => todo.id === payload); state.todos.splice(index, 1); } };
mutation ها همچنین یک payload دریافت می کنند که اطلاعات مورد نیاز برای ایجاد تغییرات است. برای ADD_TODO، payload یک شی است که شامل id بعدی و شرح کار (description) است. TOGGLE_TODO و DELETE_TODO فقط به id کار نیاز دارند، بنابراین تنها چیزی است که به mutation ها می فرستیم.
کاری که در این سه تابع انجام می شود در زیر آمده است:
در نهایت میتوانیم توابع Action را ایجاد کنیم، که برای فراخوانی توابع mutation هستند. گاهی اوقات آن ها می توانند چندین mutation یا Action دیگر را فراخوانی کنند، اما برای این برنامه فقط یک mutation فراخوانی می شود.
export const actions = { addTodo: (context, payload) => { context.commit("ADD_TODO", payload); }, toggleTodo: (context, payload) => { context.commit("TOGGLE_TODO", payload); }, deleteTodo: (context, payload) => { context.commit("DELETE_TODO", payload); } };
تابع mutation را با استفاده از تابع context.commit فراخوانی می کنیم. اولین پارامتر commit نام mutation و دومین پارامتر payload است. payload داده ای خواهد بود که به خود توابع action فرستاده داده می شود.
کد کامل فایل todos.js:
export const state = { todos: [] }; export const getters = { getTodos: state => state.todos }; export const mutations = { ADD_TODO: (state, payload) => { const newTask = { id: payload.newId, task: payload.task, completed: false }; state.todos.unshift(newTask); }, TOGGLE_TODO: (state, payload) => { const item = state.todos.find(todo => todo.id === payload); item.completed = !item.completed; }, DELETE_TODO: (state, payload) => { const index = state.todos.findIndex(todo => todo.id === payload); state.todos.splice(index, 1); } }; export const actions = { addTodo: (context, payload) => { context.commit("ADD_TODO", payload); }, toggleTodo: (context, payload) => { context.commit("TOGGLE_TODO", payload); }, deleteTodo: (context, payload) => { context.commit("DELETE_TODO", payload); } };
کد زیر را در فایل store.js قرار می دهیم:
import Vue from "vue"; import Vuex from "vuex"; import * as todos from "@/store/modules/todos.js"; Vue.use(Vuex); export default new Vuex.Store({ state: todos.state, getters: todos.getters, mutations: todos.mutations, actions: todos.actions });
ماژول todos.js وارد فایل می شود و استور Vuex با استفاده از آن تعریف می شود.
کد زیر را در فایل main.js قرار می دهیم:
import Vue from "vue"; import store from "./store/store"; import App from "./App.vue"; Vue.config.productionTip = false; new Vue({ store, render: h => h(App) }).$mount("#app");
store بزرگترین بخش برنامه است. اکنون به ساخت رابط کاربری می پردازیم. دو کامپوننت (برای فهرست کردن کارها یا todo ها و افزودن یک todo جدید) و کامپوننت اصلی را خواهیم داشت که شامل کل برنامه است.
ابتدا در پوشه components، فایل HelloWorld.vue را که Vue CLI به طور پیش فرض در آن جا ایجاد کرده است را حذف کنید.
به جای آن یک فایل جدید به نام TodoList.vue ایجاد کنید. ابتدا بیایید اسکلت را برنامه بنویسیم:
<template> </template> <script></script> <style></style>
بین تگهای <script> کد زیر را اضافه میکنیم:
<script> export default { computed: { todos() { return this.$store.getters.getTodos; } }, methods: { toggleTodo: function(id) { this.$store.dispatch("toggleTodo", id); }, deleteTodo: function(id) { this.$store.dispatch("deleteTodo", id); } } }; </script>
یک تابع ()todos به عنوان یک ویژگی محاسبه شده ایجاد می شود و تنها کاری که انجام می دهد این است که آیتم های موجود را از store بازگرداند.
همچنین متدهایی برای جابجایی و حذف آیتم ها تعریف می کنیم. آن ها متد dispatch() را برای فرستادن یک action فراخوانی میکنند.
در تگ template کد زیر را قرار می دهیم:
<template> <ul class="tasks"> <li v-for="todo in todos" :key="todo.id" :class="{ completed: todo.completed }" class="task" @click="toggleTodo(todo.id)" > {{ todo.task }} <span class="delete" @click="deleteTodo(todo.id)">ⓧ</span> </li> </ul> </template>
لیست توسط دستور v-for با تگ های li ایجاد می شود و در آرایه todos حلقه می زند. متدهای خود را به ترتیب برای تغییر و حذف ایجاد میکنیم. همچنین یک کلاس completed نیز برای نشان دادن کامل شدن کار اضافه می کنی.
برای استایل می توانیم کدهای زیر را بین تگ های style قرار دهیم:
<style> .tasks { padding: 0; list-style-type: none; } .task { padding: 10px; margin-bottom: 0.5rem; border: 0.5px solid #999; border-radius: 5px; color: #34495e; font-weight: bold; } .task:before { content: "\2002"; } .task:hover { cursor: pointer; } .completed { text-decoration: line-through; color: #41b883; } .completed:before { content: "\2714"; } .delete { display: block; float: right; color: #d22; width: 1.25rem; height: 1.25rem; text-align: center; } </style>
یک فایل جدید در پوشه components ها به نام TodoNew.vue ایجاد کنید. همان کارهایی که برای فایل TodoList.vue انجام دادیم را برای TodoNew.vue انجام می دهیم:
<template> </template> <script></script> <style></style>
بین تگ script کد های زیر را قرار می دهیم:
<script> export default { data() { return { task: "", newId: 0 }; }, methods: { addTodo: function() { this.$store.dispatch("addTodo", this); this.newId++; this.task = ""; } } }; </script>
داده های کامپوننت یک شی را با مقادیر پیش فرض یک آیتم To-Do جدید برمی گرداند. همچنین یک متد addTodo وجود دارد که اکشن addTodo را ارسال می کند و سپس newId را افزایش می دهد و description را بازنشانی یا reset می کند که با این کار فیلد متنی را پاک می کند.
بین تگ های template کد زیر را قرار می دهیم:
<template> <form @submit.prevent="addTodo"> <input class="todo-input" type="text" placeholder="Enter a new task" v-model="task" /> </form> </template>
این کامپوننت فقط یک فرم با یک تگ input دارد. یک description برای انجام کار در ورودی تایپ می شود و هنگامی که کلید Enter فشار داده می شود، متد addTodo فراخوانی می شود و آیتم To-Do را در Store ایجاد می کند. همچنین از طریق v-model به ویژگی task متصل میشود، بنابراین وقتی متن توسط کاربر یا متد اصلی تغییر میکند، تغییرات آن در هر دو مکان منعکس میشود.
استایل برای فایل TodoNew.vue را بین تگهای style قرار می دهیم:
<style> .todo-input { font-family: "Open Sans", sans-serif; width: 100%; padding: 0.5rem; font-size: 1rem; outline: none; border-radius: 0.25rem; border-style: none; border: solid 1px lightgray; box-sizing: border-box; } </style>
در پوشه src، فایل App.vue را باز کرده و محتویات آن ویرایش می کنیم. در تگهای script دو کامپوننتی را که ساختهایم وارد می کنیم:
<script> import TodoNew from "@/components/TodoNew.vue"; import TodoList from "@/components/TodoList.vue"; export default { components: { TodoNew, TodoList } }; </script>
در تگ template کد زیر را قرار می دهیم:
<template> <div class="container"> <h1>To-Do List</h1> <div id="app"> <div> <TodoNew /> <TodoList /> </div> </div> <img src="@/assets/logo.png" class="vue-logo" alt="Vue.js Logo" /> </div> </template>
در تگ style کد زیر را اضافه می کنیم:
<style> @import url("https://fonts.googleapis.com/css?family=Open+Sans"); html { font-family: "Open Sans", sans-serif; background: linear-gradient(45deg, #5189c1 25%, #41b883); height: 100%; color: #333; } body { display: flex; height: 100%; margin: 0; } .container { width: 24rem; margin: auto; background-color: white; border-radius: 1rem; padding: 1rem; box-shadow: 0 0 1rem rgba(0, 0, 0, 0.5); } h1 { text-align: center; margin-top: 0; } .vue-logo { display: block; width: 50px; margin: 0 auto; } </style>
کد کامل برنامه در این نشانی قرار دارد.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.