از طریق منوی جستجو مطلب مورد نظر خود در وبلاگ را به سرعت پیدا کنید
برنامه های تک صفحه ای با Vue.js و Flask: مدیریت ایالت با Vuex. از اینکه برای پست سوم به من پیوستید متشکریم روی استفاده از Vue.js و Flask برای توسعه وب تمام پشته. موضوع اصلی در این پست خواهد بود روی با استفاده از Vuex برای مدیریت وضعیت در برنامه ما. برای معرفی Vuex، روش بازسازی کامپوننت های Home و Survey را از…
سرفصلهای مطلب
مدیریت دولتی با Vuex
از اینکه برای پست سوم به من پیوستید متشکرم روی استفاده کردن Vue.js و Flask برای توسعه وب تمام پشته. موضوع اصلی در این پست خواهد بود روی استفاده کردن Vuex برای مدیریت وضعیت در برنامه ما. معرفی Vuex، من نشان خواهم داد که چگونه می توان مولفه های Home و Survey را از پست قبلی برای استفاده مجدد تغییر داد Vuex، و من همچنین توانایی اضافه کردن نظرسنجی های جدید را با استفاده از Vuex الگو.
کد این پست در یک مخزن است روی من GitHub حساب زیر شعبه پست سوم.
محتوای سری
- راه اندازی و آشنایی با VueJS
- مسیریاب Vue
- مدیریت دولتی با Vuex (تو اینجایی)
- RESTful API با Flask
- ادغام AJAX با REST API
- احراز هویت JWT
- استقرار در یک سرور خصوصی مجازی
معرفی کردن Vuex
Vuex یک کتابخانه مدیریت دولتی متمرکز است که به طور رسمی توسط هسته پشتیبانی می شود Vue.js تیم توسعه. Vuex الف را فراهم می کند شار مانند، جریان داده های یک طرفه، الگویی که در پشتیبانی از متوسط تا بزرگ بسیار قدرتمند است Vue.js برنامه های کاربردی.
پیاده سازی های دیگری از الگوهای مدیریت حالت شار مانند و کتابخانه ها وجود دارد، اما Vuex برای کار به طور خاص طراحی شده است Vue.jsسیستم واکنش سریع و ساده این امر از طریق یک API به خوبی طراحی شده انجام می شود که منبعی از حقیقت را برای داده های یک برنامه کاربردی به عنوان یک شی تک تنی فراهم می کند. علاوه بر اصل منبع واحد حقیقت، Vuex همچنین روشهای صریح و قابل ردیابی را برای عملیات ناهمزمان (عملیات)، دسترسیهای قابل استفاده مجدد راحت (گیرنده)، و قابلیتهای تغییر داده (جهش) ارائه میدهد.
برای استفاده Vuex، ابتدا باید آن را در همان دایرکتوری که حاوی package.json فایل مانند این:
$ npm install --save vuex
بعد من یک دایرکتوری جدید در داخل پروژه اضافه می کنم src/ دایرکتوری فراخوانی شد store و یک را اضافه کنید index.js فایل. این منجر به ساختار پروژه Surve-Spa می شود که اکنون به این شکل است (با نادیده گرفتن دایرکتوری های node_modules، build و config):
├── index.html
├── package-lock.json
├── package.json
├── src
│ ├── App.vue
│ ├── api
│ │ └── index.js
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ ├── Header.vue
│ │ ├── Home.vue
│ │ └── Survey.vue
│ ├── main.js
│ ├── router
│ │ └── index.js
│ └── store
│ └── index.js
└── static
└── .gitkeep
درون store/index.js فایل من با اضافه کردن واردات لازم برای شروع می کنم Vue و Vuex اشیاء سپس متصل می شوند Vuex به Vue استفاده کردن Vue.use(Vuex) مشابه کاری که با آن انجام شد vue-router. پس از این، من چهار شیء جاوا اسکریپت را تعریف می کنم: state، actions، mutations، و getters.
در انتهای فایل یک شی نهایی تعریف می کنم که نمونه ای از آن است Vuex.Store({}) شی، که تمام اشیاء خرد دیگر را به هم میکشد و سپس صادر میشود.
// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
// single source of data
}
const actions = {
// asynchronous operations
}
const mutations = {
// isolated data mutations
}
const getters = {
// reusable data accessors
}
const store = new Vuex.Store({
state,
actions,
mutations,
getters
})
export default store
خوب، چند لحظه به من فرصت دهید تا معنای آن را توضیح دهم state، actions، mutations، و getters اشیاء.
را state شی به عنوان منبع منفرد حقیقت عمل می کند که در آن تمام داده های مهم در سطح برنامه در فروشگاه موجود است. این state شی حاوی داده های نظرسنجی است که می تواند توسط هر مؤلفه ای که به آنها علاقه دارد، مانند مؤلفه Home، به آنها دسترسی داشته باشد و تغییرات را مشاهده کند.
را actions شی جایی است که من آنچه را که به عنوان شناخته می شود تعریف می کنم عمل مواد و روش ها. روشهای اقدام به عنوان «ارسال» نامیده میشوند و برای رسیدگی به عملیات ناهمزمان مانند تماسهای AJAX به یک سرویس خارجی یا API استفاده میشوند.
را mutations شی متدهایی را ارائه می دهد که به آنها “متعهد” می گویند و به عنوان یک و تنها راه برای تغییر وضعیت داده ها در state هدف – شی. هنگامی که یک جهش انجام می شود، هر مؤلفه ای که به داده های واکنشی در حال حاضر ارجاع می دهد state شی با مقادیر جدید به روز می شود و باعث می شود UI عناصر خود را به روز کند و دوباره رندر کند.
را getters شی نیز حاوی متدهایی است، اما در این مورد آنها برای دسترسی به state داده هایی که از منطقی برای بازگرداندن اطلاعات استفاده می کنند. دریافتکنندهها برای کاهش تکرار کد و ارتقای قابلیت استفاده مجدد در بسیاری از مؤلفهها مفید هستند.
آخرین مرحله لازم برای فعال سازی فروشگاه دوباره به داخل انجام می شود src/main.js جایی که من import را store ماژول به تازگی ایجاد شده است. سپس در شی Options که در آن نمونه Vue سطح بالای نمونه است، وارد شده را اضافه می کنم store به عنوان یک ملک این باید به شکل زیر باشد:
// src/main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
انتقال مولفه خانه به Vuex
من می خواهم شروع به استفاده کنم Vuex در برنامه Survey با انتقال روشی که نظرسنجی ها در مؤلفه Home بارگذاری می شوند برای استفاده از Vuex الگو. برای شروع، یک آرایه نظرسنجی خالی را تعریف و مقداردهی اولیه می کنم state شی در داخل store/index.js. این مکانی خواهد بود که پس از دریافت درخواست AJAX، تمام داده های نظرسنجی سطح برنامه در آن قرار می گیرند.
const state = {
// single source of data
surveys: ()
}
اکنون که نظرسنجی ها مکانی برای سکونت دارند، باید یک روش اقدام ایجاد کنم، loadSurveys(...)، که می تواند از مؤلفه Home (یا هر مؤلفه دیگری که به داده های نظرسنجی نیاز دارد) برای رسیدگی به درخواست ناهمزمان به تابع ساختگی AJAX ارسال شود. fetchSurveys(). برای استفاده fetchSurveys() من اول نیاز دارم import آن را از api ماژول را تعریف کنید loadSurveys(...) روش عمل برای رسیدگی به درخواست
کنشها اغلب همراه با جهشهایی در الگوی انجام درخواستهای AJAX ناهمزمان برای دادهها به سرور و بهروزرسانی صریح فروشگاه کار میکنند. state شی با داده های واکشی شده هنگامی که جهش انجام شد، بخشهایی از برنامه که از نظرسنجیها استفاده میکنند، بررسیهای بهروز شده را از طریق سیستم واکنشپذیری Vue تشخیص میدهند. در اینجا جهشی که من تعریف می کنم نامیده می شود setSurveys(...).
import Vue from 'vue'
import Vuex from 'vuex'
// imports of AJAX functions go here
import { fetchSurveys } from '@/api'
Vue.use(Vuex)
const state = {
// single source of data
surveys: ()
}
const actions = {
// asynchronous operations
loadSurveys(context) {
return fetchSurveys()
.then((response) => context.commit('setSurveys', { surveys: response }))
}
}
const mutations = {
// isolated data mutations
setSurveys(state, payload) {
state.surveys = payload.surveys
}
}
با توجه به اینکه فروشگاه اکنون توانایی واکشی نظرسنجی ها را دارد، می توانم مؤلفه Home را به روز کنم و از فروشگاه برای تغذیه داده های نظرسنجی آن استفاده کنم. برگشت داخل src/components/Home.vue، من حذف می کنم import از fetchSurveys تابع:
import { fetchSurveys } from '@/api'
و آن را با یک جایگزین کنید import به Vuex تابع کمکی فراخوانی شد mapState.
import { mapState } from 'vuex'
من استفاده خواهم کرد mapState برای نقشه برداری surveys آرایه ای که در state شی به یک ویژگی محاسبه شده نیز نامیده می شود surveys. mapState به سادگی تابعی است که ارجاع به یک ویژگی خاص از را حفظ می کند state هدف – شی (state.surveys در این مورد)، و اگر آن ویژگی با استفاده از یک جزء جهش یافته باشد mapState به آن تغییر واکنش نشان می دهد و هر رابط کاربری مرتبط با آن داده را تازه می کند.
در کامپوننت Home من جدید را اضافه کرده ام surveys دارایی محاسبه شده علاوه بر این، در beforeMount روش I راه اندازی ارسال از loadSurveys اقدام ذخیره از آنجایی که در حال حاضر یک ویژگی محاسبه شده به نام وجود دارد surveys باید موجود را حذف کنم surveys ویژگی از قسمت داده شی Vue جزء. در واقع، از آنجایی که این تنها ویژگی داده بود، باید کل ویژگی داده را نیز حذف کنم تا همه چیز را مرتب نگه دارم، همانطور که در زیر نشان داده شده است.
<script>
import { mapState } from 'vuex'
export default {
computed: mapState({
surveys: state => state.surveys
}),
beforeMount() {
this.$store.dispatch('loadSurveys')
}
}
</script>
توجه داشته باشید که من می توانم به فروشگاه دسترسی داشته باشم و متد عمل را با سینتکس ارسال کنم this.$store.dispatch(...). این باید شبیه روشی باشد که در مقاله قبلی با استفاده از آن به مسیر دسترسی پیدا کردم this.$route. این به این دلیل است که هر دو vue-router و Vuex کتابخانه این اشیاء را به عنوان ویژگی های راحت به نمونه Vue تزریق می کند. من همچنین می توانستم به فروشگاه دسترسی داشته باشم state.surveys آرایه از درون کامپوننت با استفاده از this.$store.state.surveys بجای استفاده از mapState، و همچنین می توانم با استفاده از جهش انجام دهم this.$store.commit.
در این مرحله باید بتوانم پروژه خود را ذخیره کنم و با درخواست URL، همان عملکرد را در مرورگر خود مشاهده کنم localhost:8080 همانطور که قبلا دیده شد
مهاجرت مؤلفه نظرسنجی
وظیفه بعدی انتقال مولفه Survey برای استفاده است Vuex‘s فروشگاه برای واکشی نظرسنجی خاص برای شرکت در گرفتن. جریان کلی برای مولفه Survey دسترسی به آن خواهد بود :id پایه مسیر و سپس از a استفاده کنید Vuex روش اقدام برای واکشی نظرسنجی توسط آن id. به جای فراخوانی مستقیم تابع ساختگی AJAX fetchSurvey همانطور که قبلاً انجام شد، میخواهم آن را به روش دیگری از فروشگاه واگذار کنم که میتواند نظرسنجی واکشی شده را به یک ذخیره کند (یعنی جهش انجام دهد) state ملکی که نام خواهم برد currentSurvey.
با شروع در ماژول store/index.js این خط را تغییر می دهم:
import { fetchSurveys } from '@/api'
به
import { fetchSurveys, fetchSurvey } from '@/api'
این به من دسترسی می دهد fetchSurvey در ماژول فروشگاه من استفاده می کنم fetchSurvey در یک روش اقدام جدید به نام loadSurvey که سپس یک جهش را در روش جدید دیگری در داخل انجام می دهد mutations شی نامیده می شود setCurrentSurvey.
// src/store/index.js
const actions = {
// asynchronous operations
loadSurveys(context) {
// omitted for brevity
},
loadSurvey(context, { id }) {
return fetchSurvey(id)
.then((response) => context.commit('setSurvey'. { survey: response }))
}
}
در بالا اجرای fetchSurvey روش عمل مشابه قبلی fetchSurveys، به جز اینکه یک پارامتر شی اضافی با ویژگی id برای بررسی واکشی به آن داده می شود. برای ساده کردن دسترسی به شناسه از ES2015 استفاده می کنم تخریب شی. هنگامی که اکشن از یک جزء فراخوانی می شود، نحو به شکل زیر خواهد بود: this.$store.dispatch('loadSurvey', { id: 1 }).
بعد من اضافه می کنم currentSurvey دارایی به state هدف – شی. در نهایت، من یک جهش به نام تعریف می کنم setSurvey در mutations شی، که a اضافه می کند choice برای هر سوال، انتخاب انتخاب شده نظرسنجی را نگه دارید و مقدار آن را تنظیم کنید currentSurvey.
const state = {
// single source of data
surveys: (),
currentSurvey: {}
}
const actions = { // omitted for brevity }
const mutations = {
// isolated data mutations
setSurveys(state, payload) {
state.surveys = payload.surveys
},
setSurvey(state, payload) {
const nQuestions = payload.survey.questions.length
for (let i = 0; i < nQuestions; i++) {
payload.survey.questions(i).choice = null
}
state.currentSurvey = payload.survey
}
}
در Survey.vue فایل کامپوننت، من آن را به روز می کنم beforeMount روش ارسال loadSurvey اقدام و نقشه state.currentSurvey به یک ویژگی محاسبه شده به نام survey. پس از آن می توانم موجود را حذف کنم survey ویژگی داده
<script>
import { saveSurveyResponse } from '@/api'
export default {
data() {
return {
currentQuestion: 0
}
},
beforeMount() {
this.$store.dispatch('loadSurvey', { id: parseInt(this.$route.params.id) })
},
methods: {
// omitted for brevity
},
computed: {
surveyComplete() {
// omitted for brevity
},
survey() {
return this.$store.state.currentSurvey
}
}
}
</script>
ذخیره فایل های پروژه و بازخوانی مرورگر برای درخواست URL localhost:8080/#/surveys/2 دوباره همان UI مانند شکل زیر را به من می دهد.

با این حال، هنوز کمی مشکل وجود دارد. در کد قالب که انتخاب های هر سوالی را که استفاده می کنم نمایش می دهد v-model="question.choice" برای ردیابی تغییرات زمانی که کاربر انتخابی را انتخاب می کند.
<div v-for="choice in question.choices" v-bind:key="choice.id">
<label class="radio">
<input type="radio" v-model="question.choice" :value="choice.id">
{{ choice.text }}
</label>
</div>
این منجر به تغییراتی در question.choice ارزشی که در فروشگاه ذکر شده است state.currentQuestion ویژگی. این نمونه ای از تغییر نادرست داده های ذخیره خارج از یک جهش است. را vuex مستندات توصیه می کنند که هرگونه تغییر در داده های وضعیت فروشگاه منحصراً با استفاده از جهش انجام شود. ممکن است بپرسید چگونه می توانم استفاده کنم؟ v-model در ترکیب با یک عنصر ورودی که توسط داده های منبع از a هدایت می شود Vuex فروشگاه؟
پاسخ به این، استفاده از یک نسخه کمی پیشرفته تر از یک ویژگی محاسبه شده است که شامل یک جفت تعریف شده است get و set روش های درون آن این فراهم می کند v-model مکانیزمی برای استفاده از اتصال داده دو طرفه بین UI و شی Vue جزء. به این ترتیب ویژگی محاسبه شده به صراحت بر تعاملات با داده های فروشگاه کنترل می کند. در کد قالب باید جایگزین کنم v-model="question.choice" با ویژگی جدید محاسبه شده مانند این v-model="selectedChoice". در زیر پیاده سازی ویژگی محاسبه شده است selectedChoice.
computed: {
surveyComplete() {
// omitted for brevity
},
survey() {
return this.$store.state.currentSurvey
},
selectedChoice: {
get() {
const question = this.survey.questions(this.currentQuestion)
return question.choice
},
set(value) {
const question = this.survey.questions(this.currentQuestion)
this.$store.commit('setChoice', { questionId: question.id, choice: value })
}
}
}
توجه داشته باشید که در این پیاده سازی selectedChoice در واقع یک ویژگی شی به جای تابعی مانند بقیه است. را get تابع در ارتباط با currentQuestion ویژگی داده برای برگرداندن choice ارزش سوال در حال مشاهده را set(value) بخش مقدار جدیدی را دریافت می کند که از آن تغذیه می شود v-model‘s داده های 2 طرفه متصل می شود و یک جهش ذخیره ای به نام را مرتکب می شود setChoice. را setChoice جهش به یک بار شیء حاوی id از سوال به همراه جدید به روز شود value.
من اضافه می کنم setChoice جهش به ماژول فروشگاه به این صورت:
const mutations = {
setSurveys(state, payload) {
state.surveys = payload.surveys
},
setSurvey(state, payload) {
// omitted for brevity
},
setChoice(state, payload) {
const { questionId, choice } = payload
const nQuestions = state.currentSurvey.questions.length
for (let i = 0; i < nQuestions; i++) {
if (state.currentSurvey.questions(i).id === questionId) {
state.currentSurvey.questions(i).choice = choice
break
}
}
}
}
آخرین چیزی که باید در مولفه Survey مهاجرت کرد، ذخیره گزینه های پاسخ نظرسنجی است. برای شروع، در Survey.vue، باید آن را حذف کنم import از saveSurveyResponse عملکرد AJAX
import { saveSurveyResponse } from '@/api'
و آن را به عنوان یک اضافه کنید import در ماژول src/store/index.js به این صورت:
import { fetchSurveys, fetchSurvey, saveSurveyResponse } from '@/api'
در حال حاضر پایین در actions متدهای ماژول store/index.js باید روش جدیدی به نام اضافه کنم addSurveyResponse، که تماس خواهد گرفت saveSurveyResponse عملکرد AJAX و در نهایت آن را به سرور ادامه می دهد.
const actions = {
loadSurveys(context) {
// omitted for brevity
},
loadSurvey(context, { id }) {
// omitted for brevity
},
addSurveyResponse(context) {
return saveSurveyResponse(context.state.currentSurvey)
}
}
برگشت در Survey.vue فایل کامپوننت، باید آن را به روز کنم handleSubmit روشی برای ارسال این متد عمل به جای فراخوانی مستقیم saveSurveyResponse مانند:
methods: {
goToNextQuestion() {
// omitted for brevity
},
goToPreviousQuestion() {
// omitted for brevity
},
handleSubmit() {
this.$store.dispatch('addSurveyResponse')
.then(() => this.$router.push('/'))
}
}
افزودن قابلیت ایجاد نظرسنجی جدید
باقیمانده این پست به ایجاد قابلیت ایجاد یک نظرسنجی جدید با نام، سؤالات و انتخابهای هر سؤال اختصاص خواهد یافت.
برای شروع باید یک فایل کامپوننت به نام اضافه کنم NewSurvey.vue داخل دایرکتوری کامپوننت ها بعد من می خواهم import و یک مسیر جدید به آن اضافه کنید router/index.js ماژول مانند این:
// other import omitted for brevity
import NewSurvey from '@/components/NewSurvey'
Vue.use(Router)
export default new Router({
routes: (
{
path: '/',
name: 'Home',
component: Home
}, {
path: '/surveys/:id',
name: 'Survey',
component: Survey
}, {
path: '/surveys',
name: 'NewSurvey',
component: NewSurvey
}
)
})
درون Header.vue فایل، من باید یک پیوند ناو اضافه کنم تا بتوانم به نمای ایجاد حرکت کنم.
<template>
<nav class="navbar is-light" role="navigation" aria-label="main navigation">
<div class="navbar-menu">
<div class="navbar-start">
<router-link to="/" class="navbar-item">
Home
</router-link>
<router-link to="/surveys" class="navbar-item">
Create Survey
</router-link>
</div>
</div>
</nav>
</template>
در حال حاضر در NewSurvey.vue کامپوننت، من ساختار اصلی رابط کاربری ایجاد نظرسنجی را داربست خواهم کرد.
<template>
<div>
<section class="hero is-primary">
<div class="hero-body">
<div class="container has-text-centered">
<h2 class="title">{{ name }}</h2>
</div>
</div>
</section>
<section class="section">
<div class="container">
<div class="tabs is-centered is-fullwidth is-large">
<ul>
<li :class="{'is-active': step == 'name'}" @click="step = 'name'">
<a>Name</a>
</li>
<li :class="{'is-active': step == 'questions'}" @click="step = 'questions'">
<a>Questions</a>
</li>
<li :class="{'is-active': step == 'review'}" @click="step = 'review'">
<a>Review</a>
</li>
</ul>
</div>
<div class="columns">
<div class="column is-half is-offset-one-quarter">
<div class="name" v-show="step === 'name'">
<h2 class='is-large'>Add name</h2>
</div>
<div class="questions" v-show="step === 'questions'">
<h2>Add Questions</h2>
</div>
<div class="review" v-show="step === 'review'">
<h2>Review and Submit</h2>
</div>
</div>
</div>
</div>
</section>
</div>
</template>
<script>
export default {
data() {
return {
step: 'name'
}
}
}
</script>
<style></style>

همانطور که در تصویر بالا می بینید، سه تب وجود دارد که نمایش اجزای رابط کاربری را برای اضافه کردن نام، سؤالات و بررسی قبل از ذخیره کردن فعال می کند.
عملکردی که باعث تعامل این می شود page بر اساس دیکته شده است روی ارزش a step ویژگی داده که تعیین می کند کدام برگه باید فعال باشد. step به صورت پیشفرض روی برگه «نام» قرار میگیرد، اما با کلیک کاربر بهروزرسانی میشود روی یکی از برگه های دیگر نه تنها ارزش step تعیین کنید کدام برگه باید داشته باشد is-active کلاس، اما نمایش و پنهان کردن آن را نیز هدایت می کند divs که رابط کاربری را برای افزودن نام، سؤال و بررسی قبل از ارسال ارائه می دهد.
من با نام UI شروع می کنم div که به سادگی حاوی یک ورودی متنی است که به a گره خورده است name ویژگی داده از طریق v-model، مانند:
بخش قالب
<div class="name" v-show="step === 'name'">
<div class="field">
<label class="label" for="name">Survey name:</label>
<div class="control">
<input type="text" class="input is-large" id="name" v-model="name">
</div>
</div>
</div>
بخش اسکریپت
data() {
return {
step: 'name',
name: ''
}
}
UI سوالات و پاسخ ها کمی بیشتر درگیر خواهد شد. برای نگه داشتن NewSurvey جزء سازماندهی شده و کاهش پیچیدگی را اضافه می کنم NewQuestion.vue جزء فایل برای رسیدگی به رابط کاربری و رفتار لازم برای افزودن سؤالات جدید به همراه تعداد متغیری از پاسخ ها.
همچنین باید توجه داشته باشم که برای مؤلفههای NewSurvey و NewQuestion از حالت سطح مؤلفه برای جدا کردن فروشگاه از دادههای نظرسنجی جدید واسطه استفاده میکنم تا زمانی که کاربر نظرسنجی جدید را ارسال کند. پس از ارسال، من شرکت خواهم کرد Vuexذخیره و الگوی مربوط به ارسال یک اقدام برای ارسال نظرسنجی جدید به سرور و سپس به مؤلفه Home تغییر مسیر دهید. سپس مؤلفه Home میتواند همه نظرسنجیها از جمله نظرسنجی جدید را واکشی کند.
در NewQuestion.vue فایل، من اکنون کد زیر را دارم:
<template>
<div>
<div class="field">
<label class="label is-large">Question</label>
<div class="control">
<input type="text" class="input is-large" v-model="question">
</div>
</div>
<div class="field">
<div class="control">
<a class="button is-large is-info" @click="addChoice">
<span class="icon is-small">
<i class="fa fa-plus-square-o fa-align-left" aria-hidden="true"></i>
</span>
<span>Add choice</span>
</a>
<a class="button is-large is-primary @click="saveQuestion">
<span class="icon is-small">
<i class="fa fa-check"></i>
</span>
<span>Save</span>
</a>
</div>
</div>
<h2 class="label is-large" v-show="choices.length > 0">Question Choices</h2>
<div class="field has-addons" v-for="(choice, idx) in choices" v-bind:key="idx">
<div class="control choice">
<input type="text" class="input is-large" v-model="choices(idx)">
</div>
<div class="control">
<a class="button is-large">
<span class="icon is-small" @click.stop="removeChoice(choice)">
<i class="fa fa-times" aria-hidden="true"></i>
</span>
</a>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
question: '',
choices: ()
}
},
methods: {
removeChoice(choice) {
const idx = this.choices.findIndex(c => c === choice)
this.choices.splice(idx, 1)
},
saveQuestion() {
this.$emit('questionComplete', {
question: this.question,
choices: this.choices.filter(c => !!c)
})
this.question = ''
this.choices = ()
},
addChoice() {
this.choices.push('')
}
}
}
</script>
<style>
.choice {
width: 90%;
}
</style>
بیشتر ویژگی ها قبلاً مورد بحث قرار گرفته اند، بنابراین من فقط به طور خلاصه آنها را بررسی می کنم. برای شروع، من یک question ویژگی داده که از طریق به ورودی متنی متصل می شود v-model="question" ارائه اتصال داده دو طرفه بین ویژگی داده question و عنصر ورودی UI.
در زیر ورودی متن سوال دو دکمه وجود دارد. یکی از دکمه ها برای افزودن یک انتخاب است و شامل شنونده رویداد است @click="addChoice" که یک رشته خالی را روی choices آرایه. را choices آرایه برای هدایت نمایش ورودی های متن انتخابی استفاده می شود که هر کدام به عنصر مربوطه خود گره خورده اند choices آرایه از طریق v-model="choices(idx)". هر ورودی متن انتخابی با یک دکمه جفت می شود که به کاربر اجازه می دهد آن را به دلیل حضور شنونده رویداد کلیک حذف کند. @click="removeChoice(choice)".
آخرین قطعه UI در مؤلفه NewQuestion که باید مورد بحث قرار گیرد، دکمه ذخیره است. هنگامی که کاربر سؤال خود را اضافه کرد و تعداد گزینه های مورد نظر خود را اضافه کرد، می تواند برای ذخیره سؤال روی آن کلیک کند. این کار از طریق شنونده کلیک انجام می شود @click="saveQuestion".
با این حال، در داخل saveQuestion روش من موضوع جدیدی را معرفی کرده ام. توجه داشته باشید که من از روش دیگری متصل به نمونه Vue کامپوننت استفاده می کنم. این است this.$emit(...) روش ساطع کننده رویداد در فراخوانی این، من در حال پخش به مؤلفه اصلی، NewSurvey، رویداد نامیده می شود questionComplete و همراه با آن یک شی محموله با question و choices.
برگشت در NewSurvey.vue فایل، من می خواهم import این NewQuestion کامپوننت و آن را در نمونه Vue کامپوننت به این صورت ثبت کنید:
<script>
import NewQuestion from '@/components/NewQuestion'
export default {
components: { NewQuestion },
data() {
return {
step: 'name',
name: ''
}
}
}
</script>
سپس می توانم آن را به عنوان یک عنصر مؤلفه مانند این در قالب قرار دهم:
<div class="questions" v-show="step === 'questions'">
<new-question v-روی:questionComplete="appendQuestion"/>
</div>
توجه داشته باشید که من از آن استفاده کرده ام v-روی دستورالعمل گوش دادن به questionComplete رویدادی که باید از مؤلفه NewQuestion ساطع شود و یک فراخوان از آن ثبت شود appendQuestion. این همان مفهومی است که ما با آن دیدیم @click="someCallbackFunction" شنونده رویداد، اما این بار برای یک رویداد سفارشی است. اتفاقا من میتونستم از کوتاهتر استفاده کنم @questionComplete="appendQuestion" نحو، اما من فکر کردم که تنوعی در آن ایجاد کنم، و همچنین به این صورت واضح تر است.
نکته منطقی بعدی اضافه کردن آن خواهد بود appendQuestion روش به NewSurvey جزء همراه با الف questions ویژگی داده برای حفظ مجموعه سوالات و پاسخ های تولید شده در NewQuestion جزء و ساطع شده به NewSurvey.
export default {
components: { NewQuestion },
data() {
return {
step: 'name',
name: '',
question: ()
}
},
methods: {
appendQuestion(newQuestion) {
this.questions.push(newQuestion)
}
}
}
اکنون می توانم با مرورگر در URL ذخیره و بازخوانی کنم localhost:8080/#/surveys سپس کلیک کنید روی برگه سؤالات، متن سؤال و چند گزینه را مانند شکل زیر اضافه کنید.

آخرین برگه ای که باید تکمیل شود، تب Review است. این page سوالات و گزینه ها را لیست می کند و همچنین امکان حذف آنها را به کاربر ارائه می دهد. اگر کاربر راضی باشد، میتواند نظرسنجی را ارسال کند و برنامه به مؤلفه Home هدایت میشود.
بخش قالب کد برای UI بررسی به شرح زیر است:
<div class="review" v-show="step === 'review'">
<ul>
<li class="question" v-for="(question, qIdx) in questions" :key="`question-${qIdx}`">
<div class="title">
{{ question.question }}
<span class="icon is-medium is-pulled-right delete-question"
@click.stop="removeQuestion(question)">
<i class="fa fa-times" aria-hidden="true"></i>
</span>
</div>
<ul>
<li v-for="(choice , cIdx) in question.choices" :key="`choice-${cIdx}`">
{{ cIdx + 1 }}. {{ choice }}
</li>
</ul>
</li>
</ul>
<div class="control">
<a class="button is-large is-primary" @click="submitSurvey">Submit</a>
</div>
</div>
بخش اسکریپت اکنون فقط باید با افزودن آن به روز شود removeQuestion و submitSurvey روش هایی برای رسیدگی به شنوندگان رویداد کلیک مربوطه خود.
methods: {
appendQuestion(newQuestion) {
this.questions.push(newQuestion)
},
removeQuestion(question) {
const idx = this.questions.findIndex(q => q.question === question.question)
this.questions.splice(idx, 1)
},
submitSurvey() {
this.$store.dispatch('submitNewSurvey', {
name: this.name,
questions: this.questions
}).then(() => this.$router.push('/'))
}
}
را removeQuestion(question) روش سوال را از روی حذف می کند questions آرایه ای در ویژگی داده که به صورت واکنشی لیست سوالات تشکیل دهنده رابط کاربری بالا را به روز می کند. را submitSurvey متد یک روش اقدام به زودی اضافه می کند submitNewSurvey و محتوای نظرسنجی جدید را به آن ارسال می کند و سپس از کامپوننت استفاده می کند this.$router.push(...) برای تغییر مسیر برنامه به مؤلفه Home.
اکنون تنها کاری که باید انجام داد ایجاد آن است submitNewSurvey روش عمل و تابع ساختگی AJAX مربوط به ارسال جعلی به سرور. در فروشگاه actions شی من موارد زیر را اضافه می کنم.
const actions = {
// asynchronous operations
loadSurveys(context) {
return fetchSurveys()
.then((response) => context.commit('setSurveys', { surveys: response }))
},
loadSurvey(context, { id }) {
return fetchSurvey(id)
.then((response) => context.commit('setSurvey', { survey: response }))
},
addSurveyResponse(context) {
return saveSurveyResponse(context.state.currentSurvey)
},
submitNewSurvey(context, survey) {
return postNewSurvey(survey)
}
}
در نهایت، در api/index.js ماژول را اضافه می کنم postNewSurvey(survey) عملکرد AJAX برای تمسخر POST به سرور.
export function postNewSurvey(survey) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Saving survey ...', survey)
resolve()
}, 300)
})
}
من تمام فایل های پروژه ام را ذخیره می کنم و URL را درخواست می کنم localhost:8080/#/surveys. سپس یک نام، چند سوال با انتخاب اضافه کنید و مکث کنید روی در برگه بررسی، رابط کاربری زیر را می بینم:

منابع
می خواهید در مورد آن بیشتر بدانید Vue.js و ساختن برنامه های وب جلویی؟ سعی کنید برخی از منابع زیر را برای بررسی عمیقتر در این چارچوب ظاهری بررسی کنید:
نتیجه
در طول این پست سعی کردم آنچه را که فکر می کنم مهمترین جنبه های یک موضوع نسبتاً بزرگ است پوشش دهم. Vuex. Vuex یک افزودنی بسیار قدرتمند به a است Vue.js پروژه ای که به توسعه دهنده الگویی بصری می دهد که سازماندهی و استحکام تک تک داده محور متوسط تا بزرگ را بهبود می بخشد. page برنامه های کاربردی.
مثل همیشه، ممنون که خواندید و از نظر دادن یا انتقاد در زیر خجالت نکشید.
منتشر شده در 1403-01-27 03:49:03

