مقدمه
ظهور کتابخانه های مدرن جاوا اسکریپت مانند React.js و Vue.js توسعه وب نرم افزار نهایی را بهتر کرده است. این کتابخانه ها دارای ویژگی هایی از جمله SPA (برنامه های تک صفحه) هستند که بارگذاری دینامیک مطالب در صفحات وب بدون بارگذاری مجدد کامل از مرورگر میباشد.
مفهوم پشت بیشتر برنامه های تک پیج ، ارائه در سمت کلاینت است. در رندر سمت کلاینت ، اکثر مطالب در یک مرورگر با استفاده از JavaScript ارائه می شوند. در لود صفحه ، محتوا در ابتدا لود نمی شود تا جاوا اسکریپت به طور کامل بارگیری شود و بقیه سایت را ارائه دهد.
خاتمه در سمت کلاینت یک مفهوم نسبتاً جدید است و تبعاتی در رابطه با استفاده از آن وجود دارد. نکته منفی قابل توجه این است که ، از آنجایی که محتوا دقیقاً تا زمانی که صفحه با استفاده از JavaScript به روز شود، ارائه نمی گردد ،SEO برای وب سایت دچار مشکل خواهد برد زیرا به سختی می توان اطلاعاتی را برای crawl موتورهای جستجو به دست آورد.
از طرف دیگر ، ارائه سمت سرور مجازی روشی معمول برای بدست آوردن صفحات HTML ارائه شده در یک مرورگر است. در برنامه های قدیمی تر رندر شده توسط سرور مجازی ، برنامه وب با استفاده از یک زبان سمت سرور مجازی مانند PHP ساخته شده است. هنگامی که یک صفحه وب توسط یک مرورگر درخواست می شود ، سرور مجازی از راه دور محتوای (پویا) را اضافه می کند و یک صفحه HTML شلوغ را ارائه می دهد.
دقیقاً همانطور که در رندرگیری سمت کلاینت مشکلاتی وجود دارد ، رندر سمت سرور مجازی باعث می شود مرورگر بیش از حد درخواست های سرور مجازی را ارسال کند و تکرار بارگذاری مجدد صفحه را برای داده های مشابه انجام دهد. چارچوب های JavaScript وجود دارد که در ابتدا می توانند صفحه وب را با راه حل SSR (Server-Send Rendering) بارگذاری کنند ، سپس از یک چارچوب برای اداره مسیریابی پویا استفاده کرده و فقط داده های لازم را دریافت می کنند. برنامه های کاربردی حاصل از آن برنامه های جهانی نامیده می شوند.
به طور خلاصه ، از یک برنامه جهانی برای توصیف کد JavaScript استفاده می شود که می تواند در سمت کلاینت و سرور مجازی اجرا شود. در این مقاله ، ما یک برنامه جهانی دستور العمل پخت با استفاده از Nuxt.js.ایجاد میکنیم.
Nuxt.js یک چارچوب سطح بالاتر برای توسعه برنامه های جهانی Vue.js است. با الهام از React’s Next.js ایجاد شده است و به حل مشکلات (پیکربندی سرور مجازی و توزیع کد مشتری) که در تنظیم برنامه های Vue.js رندر شده در سمت سرور مجازی ایجاد می شود ، کمک می کند. Nuxt.js همچنین دارای ویژگی هایی مانند داده های async ، میان افزار ، طرح بندی و غیره است که به توسعه بین سمت کلاینت و سمت سرور مجازی کمک می کند.
توجه: ما می توانیم به برنامه ای که ایجاد می کنیم به عنوان رندر شده در سمت سرور مجازی (SSR) ارجاع دهیم زیرا Vue.js در هنگام ایجاد یک برنامه تک پیج ، به طور پیش فرض رندرگیری سمت کلاینت را اجرا می کند. این برنامه در واقع یک برنامه جهانی است.

در این مقاله خواهیم دید که چگونه یک برنامه جهانی با استفاده از Django و Nuxt.js. ایجاد کنیم. Django عملیات برگشتی پس زمینه را بر عهده خواهد داشت و API ها را با استفاده از (DRF) Django Rest Framework ارائه می دهد ، در حالی که Nuxt.js ظاهر اصلی را ایجاد می کند.
نسخه نمایشی برنامه نهایی:

می بینیم که برنامه نهایی یک برنامه دستور پخت است که عملیات CRUD را انجام می دهد.
کد منبع این آموزش در اینجا در GitHub موجود است.
پیش نیازها
برای دنبال کردن این آموزش ، به نصب موارد زیر روی دستگاه خود نیاز دارید:
1- Python3
2- Pip
3- Npm
Pipenv ابزاری آماده تولید است که هدف آن آوردن بهترین موارد بسته بندی از همه جا دنیای به دنیای پایتون است. Pipfile ، pip و virtualenv را در یک دستور واحد مهار می کند.
این آموزش فرض می کند که خواننده موارد زیر را دارد:
1- دانش پایه در مورد کار با چارچوب Django و Django Rest.
2- دانش پایه کار با Vue.js.
تنظیم Backend (پشت خط)
در این بخش پشت خط را تنظیم می کنیم و کلیه پوشه های مورد نیاز برای راه اندازی و اجرا را ایجاد می کنیم ، بنابراین نمونه جدیدی از یک ترمینال را راه اندازی کنید و با اجرای این دستور دیرکتوری برنامه را ایجاد کنید:
⦁ $ mkdir recipes_app

در مرحله بعد ، ما به دیرکتوری میرویم:
⦁ $ cd recipes_app

اکنون Pipenv را با استفاده از Pip نصب می کنیم و یک محیط مجازی جدید را فعال می کنیم:
⦁ $ pip install pipenv

⦁ $ pipenv shell

توجه: اگر قبلاً Pipenv را روی رایانه خود نصب کرده اید ، باید از دستور اول عبور کنید.
بیایید با استفاده از Pipenv ، Django و سایر متعلقات را نصب کنیم:
⦁ $ pipenv install django django-rest-framework django-cors-headers

توجه: پس از فعال کردن یک محیط مجازی جدید با استفاده از Pipenv ، هر خط فرمان در ترمینال با نام دایرکتوری کار کنونی پیشوند می یابد. در این حالت ، (recipes_app) است .
اکنون ، یک پروژه جدید Django با نام api و یک برنامه Django به نام core ایجاد خواهیم کرد:
⦁ (recipes_app)$ django-admin startproject api

⦁ (recipes_app)$ cd api

⦁ (recipes_app)$ python manage.py startapp core

بیایید برنامه core را به همراه rest_framework و core-headers ثبت کنیم تا پروژه Django آن را تشخیص دهد. فایل api / settings.py را باز کنید و بر این اساس آن را به روز کنید:
api/settings.py
# Application definition
INSTALLED_APPS = [
‘django.contrib.admin’,
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.messages’,
‘django.contrib.staticfiles’,
‘rest_framework’, # add this
‘corsheaders’, # add this
‘core’ # add this
]

MIDDLEWARE = [
‘corsheaders.middleware.CorsMiddleware’, # add this
‘django.middleware.security.SecurityMiddleware’,
‘django.contrib.sessions.middleware.SessionMiddleware’,
‘django.middleware.common.CommonMiddleware’,
‘django.middleware.csrf.CsrfViewMiddleware’,
‘django.contrib.auth.middleware.AuthenticationMiddleware’,
‘django.contrib.messages.middleware.MessageMiddleware’,
‘django.middleware.clickjacking.XFrameOptionsMiddleware’,
]

# add this block below MIDDLEWARE
CORS_ORIGIN_WHITELIST = (
‘localhost:3000’,
)

# add the following just below STATIC_URL
MEDIA_URL = ‘/media/’ # add this
MEDIA_ROOT = os.path.join(BASE_DIR, ‘media’) # add this

ما localhost: 3000 را به لیست سفید اضافه کردیم زیرا برنامه مشتری در آن پورت ارائه می شود و ما می خواهیم از خطاهای CORS جلوگیری کنیم. همچنین MEDIURL و MEDIAROOT را اضافه کردیم زیرا هنگام ارائه تصاویر در برنامه به آنها احتیاج خواهیم داشت.
تعریف مدل دستور پخت
بیایید مدلی ایجاد کنیم تا نحوه ذخیره اقلام دستور پخت در بانک اطلاعاتی را معرفی کند، فایل core/models.py را باز کرده و قطعه زیر را به طور کامل جایگزین کنید:
core/models.py
from django.db import models
# Create your models here.

class Recipe(models.Model):
DIFFICULTY_LEVELS = (
(‘Easy’, ‘Easy’),
(‘Medium’, ‘Medium’),
(‘Hard’, ‘Hard’),
)
name = models.CharField(maxlength=120)
ingredients = models.CharField(max_length=400)
picture = models.FileField()
difficulty = models.CharField(choices=DIFFICULTY_LEVELS, max_length=10)
prep_time = models.PositiveIntegerField()
prep_guide = models.TextField()

def __str_(self):
return “Recipe for {}”.format(self.name)

قطعه کد بالا شش نوع ویژگی را در مدل دستور پخت ثبت می کند:
⦁ نام
⦁ مواد لازم
⦁ تصویر
⦁ دشواری
⦁ زمان آماده سازی
⦁ راهنمای آماده سازی
ایجاد سریالایزرها (پیاپی ساز) برای مدل دستور پخت
برای تبدیل نمونه های مدل به JSON به سریالایزرها نیاز داریم تا frontend بتواند با داده های دریافت شده کار کند. ما یک فایل core/serializers.py ایجاد خواهیم کرد و آن را با موارد زیر به روز می کنیم:
core/serializers.py
from rest_framework import serializers
from .models import Recipe
class RecipeSerializer(serializers.ModelSerializer):

class Meta:
model = Recipe
fields = (“id”, “name”, “ingredients”, “picture”, “difficulty”, “prep_time”, ”

در قسمت قطعه کد بالا ، ما مدل لازم برای کار و زمینه هایی را که می خواهیم به JSON تبدیل شوند را مشخص کرده ایم.
تنظیم پنل مدیریت
Django رابط ادمین بیرون از باکس در اختیار ما قرار می دهد. این رابط کاربری بررسی عملکرهای CRUD را بر روی مدل دستور پختی که ما ایجاد کرده ایم ، آسان می کند ، اما در ابتدا ، اندکی پیکربندی انجام خواهیم داد.
فایل core/admin.py را باز کنید و قطعه زیر را کامل جایگزین کنید:
core/admin.py
from django.contrib import admin
from .models import Recipe # add this
# Register your models here.

admin.site.register(Recipe) # add this

ایجاد نماها
بیایید یک کلاس RecipeViewSet را در فایل core / views.py ایجاد کنیم ، آن را با قطعه زیر کامل جایگزین کنید:
core/views.py
from rest_framework import viewsets
from .serializers import RecipeSerializer
from .models import Recipe

class RecipeViewSet(viewsets.ModelViewSet):
serializer_class = RecipeSerializer
queryset = Recipe.objects.all()

Viewets.ModelViewSet روشهایی را برای مدیریت عملیات CRUD بطور پیش فرض فراهم می کند. فقط باید کلاس سریالایزر و queryset را مشخص کنیم.
تنظیم URL ها
به فایل api / urls.py بروید و آن را با کد زیر جایگزین کنید. این کد مسیر URL را برای API مشخص می کند:
api/urls.py
from django.contrib import admin
from django.urls import path, include # add this
from django.conf import settings # add this
from django.conf.urls.static import static # add this

urlpatterns = [
path(‘admin/’, admin.site.urls),
path(“api/”, include(‘core.urls’)) # add this
]

# add this
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

اکنون یک فایل urls.py را در دایرکتوری core ایجاد کنید و در قسمت زیر قرار دهید:
core/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import RecipeViewSet

router = DefaultRouter()
router.register(r’recipes’, RecipeViewSet)

urlpatterns = [
path(“”, include(router.urls))
]

در کد بالا ، کلاس router الگوهای URL زیر را تولید می کند:
⦁ /recipes/ – ایجاد و خواندن عملیات را می توان در این مسیر انجام داد.
⦁ /recipes/{id} عملیات خواندن ، بروزرسانی و حذف را می توان در این مسیر انجام داد.
جا به جایی های در حال اجرا
از آنجا که ما اخیراً یک مدل دستور پخت ایجاد کرده ایم و ساختار آن را تعریف کرده ایم ، باید یک فایل جا به جایی تهیه کرده و تغییرات را در مدل در بانک اطلاعاتی اعمال کنیم ، بنابراین دستورات زیر را اجرا می کنیم:
⦁ (recipes_app)$ python manage.py makemigrations

⦁ (recipes_app)$ python manage.py migrate

اکنون ، برای دسترسی به رابط ادمین ، یک اکانت ابرکاربر ایجاد خواهیم کرد:
⦁ (recipes_app)$ python manage.py createsuperuser

از شما خواسته می شود نام کاربری ، ایمیل و رمز عبور را برای این ابرکاربر وارد کنید. حتما جزئیاتی را که می توانید به خاطر بسپارید وارد کنید زیرا به آنها احتیاج خواهید داشت که به زودی وارد داشبورد سرور مجازی شوید.
این تمام پیکربندی است که باید در پس زمینه انجام شود. اکنون می توانیم API های ایجاد شده را تست کنیم ، بنابراین بیایید سرور مجازی Django را شروع کنیم:
⦁ (recipes_app)$ python manage.py runserver

هنگامی که سرور مجازی در حال اجرا است ، برای اطمینان از عملکرد آن ، به آدرس  http://localhost:8000/api/recipes/بروید

می توانیم با استفاده از رابط ، یک دستور پخت جدید ایجاد کنیم:

همچنین می توانیم با استفاده از کلیدهای اولیه id آنها ، عملیات DELETE ، PUT و PATCH را روی موارد خاص دستور پخت انجام دهیم. برای انجام این کار ، به یک آدرس با این ساختار /api/recipe/{id} خواهیم کرد. بیایید با این آدرس امتحان کنیم http://localhost:8000/api/recipes/1:

همه این ها برای پس زمینه برنامه است ، اکنون می توانیم به سراغ ظاهر برنامه برویم.
تنظیم Frontend
در این بخش از آموزش ، front-end برنامه را خواهیم ساخت. می خواهیم پوشه کد front-end را در ریشه دایرکتوری recipes_app قرار دهیم. بنابراین ، قبل از اجرای دستورات در این بخش ، از دیرکتوری api استفاده میکنیم (یا برای پیش رفتن در ترمینال قبلی، یک ترمینال جدید ایجاد میکنیم.)
بیایید با این دستور برنامه nuxt به نام client را ایجاد کنیم:
⦁ $ npx create-nuxt-app client

توجه: create-nuxt-app قبلی با npx بسته را نصب می کند اگر قبلاً در سطح جهانی روی دستگاه شما نصب نشده باشد.

پس از اتمام نصب ، create-nuxt-app چند سؤال درباره اضافه کردن ابزارهای بیشتر می پرسید. ما به آنها به شرح زیر پاسخ خواهیم داد:
⦁ نام پروژه را وارد کنید یا برای قبول نام پیش فرض فقط کلید enter را بزنید
⦁ توضیحات پروژه را وارد کنید یا برای پیش فرض فقط کلید enter را بزنید
⦁ none را برای چارچوب سرور مجازی های سفارشی انتخاب کنید
⦁ PWA support را برای نصب ویژگی ها انتخاب کنید
⦁ Bootstrap را برای چارچوب UI را انتخاب کنید
⦁ none را برای چارچوب آزمون انتخاب کنید
⦁ universal را برای حالت رندر انتخاب کنید
⦁ یک نام نویسنده وارد کنید یا برای قبول نام پیش فرض فقط کلید enter را بزنید
⦁ npm را برای مدیر بسته انتخاب کنید

این امر باعث می شود با استفاده از مدیر بسته انتخابی ، نصب متعلقات انجام شود و در آخر ، صفحه ای مانند این به شما ارائه می شود:

بیایید دستورات زیر را برای شروع برنامه در حالت توسعه اجرا کنیم:
⦁ $ cd client

⦁ $ npm run dev

پس از شروع سرور مجازی توسعه ، برای دیدن برنامه به آدرس http: // localhost: 3000 بروید:

حال اجازه دهید نگاهی به ساختار دیرکتوری پوشه client بیندازیم:
├── client
├── assets/
├── components/
├── layouts/
├── middleware/
├── node_modules/
├── pages/
├── plugins/
├── static/
└── store/

در اینجا بخشی از هدف این دیرکتوری ها آورده شده است:
⦁ Assets  – حاوی فایل های کامپایل نشده مانند تصاویر ، CSS ، SASS و JavaScript می باشد.
⦁ Components  – شامل مؤلفه های Vue.js است.
⦁ Layouts  – حاوی طرح بندی برنامه است. طرح بندی ها برای تغییر ظاهر یک صفحه استفاده می شوند و می توان برای چندین صفحه نیز استفاده کرد.
⦁ Middleware  – حاوی میان افزار برنامه است. میان افزارها توابع سفارشی هستند که قبل از ارائه یک صفحه اجرا می شود.
⦁ Pages  – حاوی نمایش ها و مسیرهای برنامه است. Nuxt.js تمام فایل های .vue را در این دیرکتوری می خواند و از این اطلاعات برای ایجاد روتر برنامه استفاده می کند.
⦁ Plugins  – دارای افزونه های JavaScript است که قبل از شروع برنامه مسیریابی Vue.js اجرا می شود.
⦁ Static  – حاوی فایل های استاتیک است (فایل هایی که بعید به نظر می رسد تغییر پیدا کنند) و تمام این فایل ها روی ریشه برنامه که / است، نگاشت می شوند.
⦁ Store  – اگر قصد داشته باشیم از Vuex با Nuxt.js. استفاده کنیم ، حاوی فایل های فروشگاه است.
همچنین یک فایل nuxt.config.js در پوشه client وجود دارد ، این فایل شامل پیکربندی سفارشی برای برنامه Nuxt.js است. قبل از ادامه ، این فایل zip را دانلود کنید ، آن را اکسترکت کرده و پوشه images/ را در دیرکتوری static/ قرار دهید.
ساختار صفحات
در این بخش چند فایل .vue را به دیرکتوری pages/ اضافه خواهیم کرد تا برنامه ما دارای پنج صفحه باشد:
⦁ صفحه نخست
⦁ صفحه لیست همه دستور پخت ها
⦁ صفحه نمایش دستور پخت تکی
⦁ صفحه ویرایش تک دستور
⦁ صفحه افزودن دستور پخت
بیایید فایل ها و پوشه های .vueزیر را به دیرکتوری pages/ اضافه کنیم ، تا این ساختار دقیق را بدست آوریم:
├── pages/
├── recipes/
├── _id/
└── edit.vue
└── index.vue
└── add.vue
└── index.vue
└── index.vue

ساختار فایل فوق مسیرهای زیر را ایجاد می کند:
/ : توسط pages/index.vue اداره میشود.
/recipes/add: توسط pages/recipes/add.vue اداره میشود.
/recipes/: توسط pages/recipes/index.vue اداره میشود.
/recipes/{id}/: توسط pages/recipes/_id/index.vue اداره میشود.
/recipes/{id}/edit: توسط pages/recipes/_id/edit.vue اداره میشود.
یک فایل یا دایرکتوری .vueکه توسط زیر خط پیشوند دار شده، یک مسیر پویا ایجاد می کند. این در برنامه های ما مفید است زیرا نمایش دستور پخت های مختلف را بر اساس شناسه آنها به عنوان مثال دستور پخت / 1 / ، دستور پخت / 2 / و غیره آسان می کند.
ایجاد صفحه اصلی
در Nuxt.js ، وقتی می خواهید ظاهر و نمای برنامه خود را تغییر دهید ، طرح بندی کمک شایانی می کند. اکنون ، هر نمونه از برنامه های کاربردی Nuxt.js یک چیدمان پیش فرض دارند، می خواهیم همه این سبک ها را حذف کنیم تا در برنامه ما تداخلی نداشته باشند.
فایل layouts/default.vue را باز کنید و آن را با بخش زیر جایگزین کنید:
layouts/default.vue
<template>
<div>
<nuxt/>
</div>
</template>

<style>
</style>

بیایید فایل pages/index.vue را با کد زیر به روز کنیم:
pages/index.vue
<template>
<header>
<div class=”text-box”>
<h1>La Recipes ?</h1>
<p class=”mt-3″>Recipes for the meals we love ❤️ ️</p>
<nuxt-link class=”btn btn-outline btn-large btn-info” to=”/recipes”>
View Recipes <span class=”ml-2″>&rarr;</span>
</nuxt-link>
</div>
</header>
</template>
<script>
export default {
head() {
return {
title: “Home page”
};
},
};
</script>
<style>
header {
min-height: 100vh;
background-image: linear-gradient(
to right,
rgba(0, 0, 0, 0.9),
rgba(0, 0, 0, 0.4)
),
url(“/images/banner.jpg”);
background-position: center;
background-size: cover;
position: relative;
}
.text-box {
position: absolute;
top: 50%;
left: 10%;
transform: translateY(-50%);
color: #fff;
}
.text-box h1 {
font-family: cursive;
font-size: 5rem;
}
.text-box p {
font-size: 2rem;
font-weight: lighter;
}
</style>

از کد بالا ، <nuxt-link> یک مولفه Nuxt.js است که می تواند برای حرکت بین صفحات استفاده شود. بسیار شبیه به مؤلفه <router-link> از Vue Router میباشد.
بیایید سرور مجازی توسعه ظاهری را شروع کنیم (اگر قبلاً در حال اجرا نبوده است) ، به http: // localhost: 3000 / مراجعه کرده و ببینیم که ظاهر صفحه اصلی چگونه است:
⦁ $ npm run dev

همیشه اطمینان حاصل کنید که سرور مجازی پس زمینه Django همواره در نمونه دیگری از ترمینال در حال اجرا باشد زیرا front-end به زودی برای داده ها با آن را شروع به ارتباط می کند.
هر صفحه در این برنامه یک مولفه Vue خواهد بود و Nuxt.js ویژگی ها و کارکردهای خاصی را ارائه می دهد تا توسعه برنامه ها یکپارچه شود. می توانید مطالب مربوط به همه این ویژگی های خاص را در این لینک Nuxt.js پیدا کنید.
به خاطر این آموزش ، ما از دو مورد از این توابع استفاده خواهیم کرد:
⦁ head () – از این روش برای تنظیم برچسبهای خاص <meta> برای صفحه فعلی استفاده می شود.
⦁ asyncData () – از این روش برای دریافت داده ها قبل از بارگذاری مؤلفه صفحه استفاده می شود. سپس شیء برگشت داده شده با داده های مؤلفه صفحه ادغام می شود. بعدا در این آموزش از این مورد استفاده خواهیم کرد.
ایجاد صفحه لیست دستور پخت ها
بیایید یک مؤلفه Vue.js به نام RecipeCard.vue را در مؤلفه ها / دیرکتوری ایجاد کنیم و آن را با بخش زیر به روز کنید:
recipecard.vue
<template>
<div class=”card recipe-card”>
<img :src=”recipe.picture” class=”card-img-top” >
<div class=”card-body”>
<h5 class=”card-title”>{{ recipe.name }}</h5>
<p class=”card-text”>
<strong>Ingredients:</strong> {{ recipe.ingredients }}
</p>
<div class=”action-buttons”>
<nuxt-link :to=”`/recipes/${recipe.id}/`” class=”btn btn-sm btn-success”> View </nuxt-link>
<nuxt-link :to=”`/recipes/${recipe.id}/edit/`” class=”btn btn-sm btn-primary”> Edit </nuxt-link>
<button @click=”onDelete(recipe.id)” class=”btn btn-sm btn-danger”>Delete</button>
</div>
</div>
</div>
</template>
<script>
export default {
props: [“recipe”, “onDelete”]
};
</script>
<style>
.recipe-card {
box-shadow: 0 1rem 1.5rem rgba(0,0,0,.6);
}
</style>

مؤلفه فوق دو پیشنهاد را می پذیرد:
1- یک recipe  که شامل اطلاعات مربوط به یک دستور خاص است.
2- یک روش onDelete که هر زمان کاربر برای حذف دستور پخت روی دکمه کلیک کند، به کار می افتد.
سپس ، pages/recipes/index.vue را باز کنید و آن را با بخش زیر به روز نمایید:
pages/recipes/index.vue
<template>
<main class=”container mt-5″>
<div class=”row”>
<div class=”col-12 text-right mb-4″>
<div class=”d-flex justify-content-between”>
<h3>La Recipes</h3>
<nuxt-link to=”/recipes/add” class=”btn btn-info”>Add Recipe</nuxt-link>
</div>
</div>
<template v-for=”recipe in recipes”>
<div :key=”recipe.id” class=”col-lg-3 col-md-4 col-sm-6 mb-4″>
<recipe-card :onDelete=”deleteRecipe” :recipe=”recipe”></recipe-card>
</div>
</template>
</div>
</main>
</template>
<script>
import RecipeCard from “~/components/RecipeCard.vue”;

const sampleData = [
{
id: 1,
name: “Jollof Rice”,
picture: “/images/food-1.jpeg”,
ingredients: “Beef, Tomato, Spinach”,
difficulty: “easy”,
prep_time: 15,
prep_guide:
“Lorem ipsum dolor sit amet consectetur adipisicing elit. Omnis, porro. Dignissimos ducimus ratione totam fugit officiis blanditiis exercitationem, nisi vero architecto quibusdam impedit, earum ”
},
{
id: 2,
name: “Macaroni”,
picture: “/images/food-2.jpeg”,
ingredients: “Beef, Tomato, Spinach”,
difficulty: “easy”,
prep_time: 15,
prep_guide:
“Lorem ipsum dolor sit amet consectetur adipisicing elit. Omnis, porro. Dignissimos ducimus ratione totam fugit officiis blanditiis exercitationem, nisi vero architecto quibusdam impedit, earum ”
},
{
id: 3,
name: “Fried Rice”,
picture: “/images/banner.jpg”,
ingredients: “Beef, Tomato, Spinach”,
difficulty: “easy”,
prep_time: 15,
prep_guide:
“Lorem ipsum dolor sit amet consectetur adipisicing elit. Omnis, porro. Dignissimos ducimus ratione totam fugit officiis blanditiis exercitationem, nisi vero architecto quibusdam impedit, earum ”
}
];

export default {
head() {
return {
title: “Recipes list”
};
},
components: {
RecipeCard
},
asyncData(context) {
let data = sampleData;
return {
recipes: data
};
},
data() {
return {
recipes: []
};
},
methods: {
deleteRecipe(recipe_id) {
console.log(deleted `${recipe.id}`)
}
}
};
</script>
<style scoped>
</style>

بیایید سرور مجازی توسعه front-end را شروع کنیم (اگر این برنامه در حال اجرا نیست) ، به http://localhost:3000/recipes مراجعه کرده و این صفحه فهرست دستور پخت ها را مشاهده کنید:
⦁ $ npm run dev

از تصویر بالا ، می بینیم که سه کارت دستور پخت ظاهر میشود حتی اگر recipes  را در بخش داده های کامپوننت روی یک آرایه خالی قرار داده باشیم. توضیح این امر این است که روش asyncData قبل از بارگذاری صفحه اجرا می شود و شیء را برمیگرداند که داده های مولفه را به روز می کند.
اکنون ، تمام کاری که ما باید انجام دهیم این است که روش asyncData را اصلاح کنیم تا یک درخواست api به پس زمینه Django انجام دهیم و داده های مولفه را با نتیجه به روز کنیم.
قبل از انجام این کار ، باید Axios را نصب و پیکربندی کنیم:
⦁ $ npm install -s ⦁ @nuxtjs/axios

پس از نصب Axios ، فایل nuxt.config.js را باز کنید و مطابق با آن به روزرسانی انجام دهید:
client/nuxt.config.js
/_
** Nuxt.js modules
_/
modules: [,
// Doc: https://bootstrap-vue.js.org/docs/
‘bootstrap-vue/nuxt’,
‘@nuxtjs/axios’ // add this
],

// add this Axios object
axios: {
baseURL: “http://localhost:8000/api”
},

اکنون فایل pages/recipes/index.vue را باز کنید و بخش <script> را با قسمت زیر جایگزین کنید:
pages/recipes/index.vue
[…]

<script>
import RecipeCard from “~/components/RecipeCard.vue”;

export default {
head() {
return {
title: “Recipes list”
};
},
components: {
RecipeCard
},
async asyncData({ $axios, params }) {
try {
let recipes = await $axios.$get(`/recipes/`);
return { recipes };
} catch (e) {
return { recipes: [] };
}
},
data() {
return {
recipes: []
};
},
methods: {
async deleteRecipe(recipe_id) {
try {
await this.$axios.$delete(`/recipes/${recipe_id}/`); // delete recipe
let newRecipes = await this.$axios.$get(“/recipes/”); // get new list of recipes
this.recipes = newRecipes; // update list of recipes
} catch (e) {
console.log(e);
}
}
}
};
</script>

[…]

در کد بالا ، asyncData () شیئی به نام context دریافت می کند که ما برای بدست آوردن $ axios آن را تجزیه می کنیم. در اینجا می توانید تمام خصوصیات context را بررسی کنید.
ما asyncData () را در یک بلوک آزمایش قرار می دهیم . زیرا می خواهیم مانع از بروز اشکال در صورت عدم اجرای سرور مجازی back-end شویم و axios نتواند داده ها را بازیابی کند. هر وقت این اتفاق بیفتد ، دستور پخت ها فقط روی یک آرایه خالی تنظیم می شوند.
این خط کد – let recipes = await $axios.$get(“/recipes/”)- نسخه کوتاه تر دستور زیر است:
let response = await $axios.get(“/recipes”)
let recipes = response.data

روش DeleteRecipe () یک دستور پخت خاص را حذف می کند ، جدیدترین لیست دستور پخت ها را از پس زمینه Django دریافت می کند و در آخر داده های این مولفه را به روز می کند.
اکنون می توانیم سرور مجازی توسعه front-end را شروع کنیم (اگر قبلاً در حال اجرا نبوده است) و خواهیم دید که کارتهای دستور العمل اکنون با داده های پس زمینه Django پر شده اند.
برای این کار ، سرور مجازی پس زمینه Django باید در حال اجرا باشد و باید برخی از داده ها (وارد شده از رابط ادمین) برای موارد دستور پخت در دسترس باشد.
بیایید به http://localhost:3000/recipesمراجعه کرده و نگاهی بیندازیم:
⦁ $ npm run dev

همچنین می توانید موارد حذف دستور پخت را امتحان کنید و بر این اساس به روز رسانی آن ها را مشاهده کنید.
افزودن دستور پخت های جدید
همینطور که بحث کردیم، می خواهیم قادر به اضافه کردن دستور پخت های جدید از بخش front-end برنامه باشیم ، بنابراین فایل pages/recipes/add/ را باز کنید و آن را با بخش زیر به روز کنید:
<template>
<main class=”container my-5″>
<div class=”row”>
<div class=”col-12 text-center my-3″>
<h2 class=”mb-3 display-4 text-uppercase”>{{ recipe.name }}</h2>
</div>
<div class=”col-md-6 mb-4″>
<img
v-if=”preview”
class=”img-fluid”
style=”width: 400px; border-radius: 10px; box-shadow: 0 1rem 1rem rgba(0,0,0,.7);”
:src=”preview”
alt
>
<img
v-else
class=”img-fluid”
style=”width: 400px; border-radius: 10px; box-shadow: 0 1rem 1rem rgba(0,0,0,.7);”
src=”@/static/images/placeholder.png”
>
</div>
<div class=”col-md-4″>
<form @submit.prevent=”submitRecipe”>
<div class=”form-group”>
<label for>Recipe Name</label>
<input type=”text” class=”form-control” v-model=”recipe.name”>
</div>
<div class=”form-group”>
<label for>Ingredients</label>
<input v-model=”recipe.ingredients” type=”text” class=”form-control”>
</div>
<div class=”form-group”>
<label for>Food picture</label>
<input type=”file” name=”file” @change=”onFileChange”>
</div>
<div class=”row”>
<div class=”col-md-6″>
<div class=”form-group”>
<label for>Difficulty</label>
<select v-model=”recipe.difficulty” class=”form-control”>
<option value=”Easy”>Easy</option>
<option value=”Medium”>Medium</option>
<option value=”Hard”>Hard</option>
</select>
</div>
</div>
<div class=”col-md-6″>
<div class=”form-group”>
<label for>
Prep time
<small>(minutes)</small>
</label>
<input v-model=”recipe.prep_time” type=”number” class=”form-control”>
</div>
</div>
</div>
<div class=”form-group mb-3″>
<label for>Preparation guide</label>
<textarea v-model=”recipe.prep_guide” class=”form-control” rows=”8″></textarea>
</div>
<button type=”submit” class=”btn btn-primary”>Submit</button>
</form>
</div>
</div>
</main>
</template>
<script>
export default {
head() {
return {
title: “Add Recipe”
};
},
data() {
return {
recipe: {
name: “”,
picture: “”,
ingredients: “”,
difficulty: “”,
prep_time: null,
prep_guide: “”
},
preview: “”
};
},
methods: {
onFileChange(e) {
let files = e.target.files || e.dataTransfer.files;
if (!files.length) {
return;
}
this.recipe.picture = files[0];
this.createImage(files[0]);
},
createImage(file) {
// let image = new Image();
let reader = new FileReader();
let vm = this;
reader.onload = e => {
vm.preview = e.target.result;
};
reader.readAsDataURL(file);
},
async submitRecipe() {
const config = {
headers: { “content-type”: “multipart/form-data” }
};
let formData = new FormData();
for (let data in this.recipe) {
formData.append(data, this.recipe[data]);
}
try {
let response = await this.$axios.$post(“/recipes/”, formData, config);
this.$router.push(“/recipes/”);
} catch (e) {
console.log(e);
}
}
}
};
</script>
<style scoped>
</style>

در submitRecipe() ، پس از ارسال اطلاعات فرم و ایجاد موفقیت آمیز دستور پخت ، برنامه با استفاده از this.$router، به /recipes/ هدایت می شود.
ایجاد صفحه نمای تک دستور پخت
بگذارید نمایی ایجاد کنیم که به کاربر اجازه دهد یک مورد تک دستور تهیه را مشاهده کند ، فایل /pages/recipes/_id/index.vue را باز کرده و در قسمت زیر پیست کنید:
/pages/recipes/_id/index.vue
<template>
<main class=”container my-5″>
<div class=”row”>
<div class=”col-12 text-center my-3″>
<h2 class=”mb-3 display-4 text-uppercase”>{{ recipe.name }}</h2>
</div>
<div class=”col-md-6 mb-4″>
<img
class=”img-fluid”
style=”width: 400px; border-radius: 10px; box-shadow: 0 1rem 1rem rgba(0,0,0,.7);”
:src=”recipe.picture”
alt
>
</div>
<div class=”col-md-6″>
<div class=”recipe-details”>
<h4>Ingredients</h4>
<p>{{ recipe.ingredients }}</p>
<h4>Preparation time ⏱</h4>
<p>{{ recipe.prep_time }} mins</p>
<h4>Difficulty</h4>
<p>{{ recipe.difficulty }}</p>
<h4>Preparation guide</h4>
<textarea class=”form-control” rows=”10″ v-html=”recipe.prep_guide” disabled />
</div>
</div>
</div>
</main>
</template>
<script>
export default {
head() {
return {
title: “View Recipe”
};
},
async asyncData({ $axios, params }) {
try {
let recipe = await $axios.$get(`/recipes/${params.id}`);
return { recipe };
} catch (e) {
return { recipe: [] };
}
},
data() {
return {
recipe: {
name: “”,
picture: “”,
ingredients: “”,
difficulty: “”,
prep_time: null,
prep_guide: “”
}
};
}
};
</script>
<style scoped>
</style>

کلید params مشاهده شده در روش asyncData () را معرفی می کنیم. در این حالت ما از params برای دریافت ID دستور پخت مورد نظر خود استفاده می کنیم. params ها را از URL استخراج می کنیم و قبل از نمایش در صفحه ، داده های آن را از قبل واکشی می کنیم.
اکنون می توانیم یک مورد دستور پخت را در مرورگر وب مرور کنیم و یک صفحه مشابه را مشاهده کنیم:

ایجاد صفحه ویرایش دستور پخت تکی
باید نمایی ایجاد کنیم که به کاربر امکان ویرایش و به روزرسانی یک دستور پخت را میدهد ، بنابراین فایل /pages/recipes/_id/edit.vue را باز کرده و در قسمت زیر پیست کنید:
/pages/recipes/_id/edit.vue
<template>
<main class=”container my-5″>
<div class=”row”>
<div class=”col-12 text-center my-3″>
<h2 class=”mb-3 display-4 text-uppercase”>{{ recipe.name }}</h2>
</div>
<div class=”col-md-6 mb-4″>
<img v-if=”!preview” class=”img-fluid” style=”width: 400px; border-radius: 10px; box-shadow: 0 1rem 1rem rgba(0,0,0,.7);” :src=”recipe.picture”>
<img v-else class=”img-fluid” style=”width: 400px; border-radius: 10px; box-shadow: 0 1rem 1rem rgba(0,0,0,.7);” :src=”preview”>
</div>
<div class=”col-md-4″>
<form @submit.prevent=”submitRecipe”>
<div class=”form-group”>
<label for>Recipe Name</label>
<input type=”text” class=”form-control” v-model=”recipe.name” >
</div>
<div class=”form-group”>
<label for>Ingredients</label>
<input type=”text” v-model=”recipe.ingredients” class=”form-control” name=”Ingredients” >
</div>
<div class=”form-group”>
<label for>Food picture</label>
<input type=”file” @change=”onFileChange”>
</div>
<div class=”row”>
<div class=”col-md-6″>
<div class=”form-group”>
<label for>Difficulty</label>
<select v-model=”recipe.difficulty” class=”form-control” >
<option value=”Easy”>Easy</option>
<option value=”Medium”>Medium</option>
<option value=”Hard”>Hard</option>
</select>
</div>
</div>
<div class=”col-md-6″>
<div class=”form-group”>
<label for>
Prep time
<small>(minutes)</small>
</label>
<input type=”text” v-model=”recipe.prep_time” class=”form-control” name=”Ingredients” >
</div>
</div>
</div>
<div class=”form-group mb-3″>
<label for>Preparation guide</label>
<textarea v-model=”recipe.prep_guide” class=”form-control” rows=”8″></textarea>
</div>
<button type=”submit” class=”btn btn-success”>Save</button>
</form>
</div>
</div>
</main>
</template>
<script>
export default {
head(){
return {
title: “Edit Recipe”
}
},
async asyncData({ $axios, params }) {
try {
let recipe = await $axios.$get(`/recipes/${params.id}`);
return { recipe };
} catch (e) {
return { recipe: [] };
}
},
data() {
return {
recipe: {
name: “”,
picture: “”,
ingredients: “”,
difficulty: “”,
prep_time: null,
prep_guide: “”
},
preview: “”
};
},
methods: {
onFileChange(e) {
let files = e.target.files || e.dataTransfer.files;
if (!files.length) {
return;
}
this.recipe.picture = files[0]
this.createImage(files[0]);
},
createImage(file) {
let reader = new FileReader();
let vm = this;
reader.onload = e => {
vm.preview = e.target.result;
};
reader.readAsDataURL(file);
},
async submitRecipe() {
let editedRecipe = this.recipe
if (editedRecipe.picture.indexOf(“http://”) != -1){
delete editedRecipe[“picture”]
}
const config = {
headers: { “content-type”: “multipart/form-data” }
};
let formData = new FormData();
for (let data in editedRecipe) {
formData.append(data, editedRecipe[data]);
}
try {
let response = await this.$axios.$patch(`/recipes/${editedRecipe.id}/`, formData, config);
this.$router.push(“/recipes/”);
} catch (e) {
console.log(e);
}
}
}
};
</script>

<style>
</style>

در کد بالا ، روش submitRecipe() عبارت شرطی دارد که هدف آن حذف تصویر یک آیتم دستور ویرایش شده از داده هایی است که در صورت عدم تغییر تصویر ، باید ارسال شوند.
هنگامی که مورد دستور پخت به روز شد ، برنامه به صفحه لیست دستور پخت ها – – یعنی /recipes/ – هدایت می شود.
تنظیم انتقال ها (تبدیلات)
این نرم افزار کاملاً کاربردی است ، با این حال ، ما می توانیم با اضافه کردن تبدیلات ، به آن ظاهری بهتر بدهیم ، که به ما امکان می دهد تا در مدت زمان معینی ، مقادیر خاصیت CSS (از یک مقدار به مقدار دیگر) را به راحتی تغییر دهیم.
ما انتقال ها را در فایل nuxt.config.js تنظیم خواهیم کرد. به طور پیش فرض ، نام تبدیل روی page تنظیم شده است ، به این معنی که انتقال هایی که ما تعریف می کنیم در همه صفحات فعال خواهد بود.
بیایید یک ظاهر طراحی شده را برای انتقال در نظر بگیریم. پوشه ای به نام css / را در دیرکتوری assets/ ایجاد کنید و یک فایل transitions.css را درون آن اضافه کنید. اکنون فایل transitions.css را باز کرده و در قسمت زیر پیست کنید:
transitions.css
.page-enter-active,
.page-leave-active {
transition: opacity .3s ease;
}
.page-enter,
.page-leave-to {
opacity: 0;
}

فایل nuxt.config.js را باز کنید و مطابق با آن بروزرسانی کنید تا فایل CSS که در حال حاضر ایجاد کردیم لود شود:
nuxt.config.js
module.exports = { /_
** Global CSS
_/
css: [‘~/assets/css/transitions.css’], // update this
}

اکنون برنامه ما فریم های هر ناوبری را به روشی شفاف و با وضوح تغییر می دهد:

نتیجه
در این مقاله ، ما با یادگیری تفاوت های بین برنامه های ارائه شده از سمت کلاینت و سمت سرور مجازی ، شروع به کار کردیم. در ادامه یاد گرفتیم که یک اپلیکیشن جهانی چیست و در آخر ، دیدیم که چگونه می توان با استفاده از Nuxt.js و Django ، یک برنامه جهانی ساخت.
کد منبع این آموزش در اینجا در GitHub موجود است.

 

از این لینک ها زیر می توانید آمورش های بیشتری برای لینوکس پیدا کنید :

استفاده از nsh برای دستورات از راه دور اوبونتو 18 –  میزبانی وب سایت با Caddy اوبونتو 18

تنظیم سرور ذخیره سازی آبجکت با استفاده از Minio در اوبونتو 18  –  ضبط و اشتراک گذاری ترمینال با Terminalizer اوبونتو

تنظیم مسیریابی شرطی و پاسخگو با React Router v4  –  ایجاد یک URL کوتاه کننده با Django و GraphQL

یک برنامه ردیابی سلامت را با React ،GraphQL و Okta –  ساخت برنامه چت زمان حقیقی React و GraphQL

به روزرسانی فیلترهای مرتب سازی Angular (زاویه ای) –  با استفاده از React ، Superagent و API اینستاگرام

نحوه ساختن یک برنامه جهانی با Nuxt.js و Django –  دکمه دانلود با ریزتعاملات با CSS ، anime.js و segment.js

نحوه اضافه کردن عکسهای پیشرفته در Node و Express  –  با Vue ،GraphQL و Apollo Client یک وبلاگ ساخت

یک برنامه SSR با روتر Preact ، Unistore و Preact بسازید  –  ساخت برنامه های وب پیشرونده با Angular

اشکال زدایی JavaScript در تولید با نقشه های منبع  –  می توان با Koa برنامه “سلام جهانی” ساخت

ساختن یک برنامه با Node ، React ، Okta  –

نحوه تنظیم Laravel ، Nginx و MySQL  –

استفاده از ویژوال استودیو از راه دور  –

راه اندازی یک پروژه React با Parcel  –

ساختن یک ربات تلگرام با Laravel و BotMan  –

چگونه می توان موتور جستجوی زمان واقعی را با Vue  –

استفاده از اشتراک زنده با کد ویژوال استودیو  –

شروع عملی GraphQL با Node.js و Express  –

نحوه نصب Discourse روی Ubuntu 18  –

نحوه نصب MySQL در CentOS 8  –

نصب و پیکربندی SNMP Daemon و Client در Ubuntu 18  –

 

 

کلمات کلیدی خرید سرور

خرید vps – خرید سرور مجازی – خرید سرور – سرور هلند – فروش vps – سرور مجازی آمریکا – خریدvps – سرور مجازی هلند – فروش سرور مجازی – سرور آمریکا – vps – سرور مجازی انگلیس – سرور مجازی آلمان – سرور مجازی کانادا – خرید vps آمریکا – خرید وی پی اس – سرور – خرید سرور مجازی هلند – vps خرید – سرور مجازی فرانسه – سرور مجازی هلند – خرید vps آمریکاخرید سرور مجازی ارزان هلندvpsخرید vps هلندخرید سرور مجازی آمریکاخرید vps فرانسهتست vpsسرور مجازی تستسرور مجازی ویندوزارزانترین vpsخرید وی پی اسvps ارزان – 

برچسب‌ها:, , , , , , ,