本文概述
- 什么是SSR?
- 构建书店应用
CRUD –创建, 读取, 更新, 删除
我假设你已经了解Vue JS的基础知识和/或你对该框架有点熟悉。 Nuxt JS是基于Vue JS构建的强大框架。它与Vue JS基本相同。那为什么, Nuxt?
对于大多数人来说, 决定使用Nuxt JS通常是因为其SSR功能。
什么是SSR? SSR是服务器端渲染的缩写。
通常, 对于大多数单页应用程序(SPA), 在页面加载后, 呈现的文件会自动注入DOM中。因此, 机器人, SEO搜寻器会在页面加载时找到一个空白页面。但是, 对于SSR, 由于其能够在页面之前预渲染服务器上的应用程序, 因此SEO搜寻器可以轻松地为该页面编制索引。而且, 它可能使该应用程序比常规SPA的性能更高。
Nuxt JS使开发人员能够轻松创建SSR应用程序。常规的Vue JS SPA应用程序也可以配置为使用SSR, 但是过程有些繁琐, Nuxt JS提供了一个包装器来处理所有这些配置。除了SSR, Nuxt还提供了一种简便的方法来更高效地设置VueJS项目。
尽管Nuxt JS仍然是Vue JS, 但是在其文件夹体系结构的结构上有一些根本的区别。
文章图片
本文的重点是使你能够使用Nuxt构建应用程序。因此, 我们不会深入研究Nuxt的文件夹架构, 但是, 我将在这里快速解释一些我们可能需要的重要架构。
页数
pages文件夹是与常规Vue SPA的根本区别之一。它代表常规Vue体系结构中的Views文件夹, 此外, 在Nuxt中, 在Pages文件夹中创建的文件会自动设置为路由。这意味着, 当你在pages文件夹中创建index.vue文件时, 该文件将自动成为你的根路由, 即localhost:3000 /。
同样, 当你创建任何其他filename.vue时, 它也会变成一条路由-创建about.vue允许你访问localhost:3000 / about。
【创建你的第一个Nuxt应用程序– CRUD书店】你也可以在Pages文件夹中创建一个文件夹。如果你创建一个名为” contact” 的文件夹, 并且在该文件夹中有email.vue, 则可以访问localhost:3000 / contact / email。就这么简单。这样, 你无需像通常使用Vue JS那样手动创建router.js文件来创建路由。
组件
与Vue JS几乎相同, 创建的组件不会自动设置为路由。
static
静态文件夹取代常规Vue JS应用程序中的公用文件夹, 功能几乎相同。这里的文件不会被编译;它们的存储方式与存储方式相同。
你可以在Nuxt JS文档页面上阅读有关体系结构的所有内容。
现在, 让我们来构建一些有趣的东西…
构建书店应用 我们将构建一个书店应用, 用户可以在其中将已阅读的书添加到自己喜欢的特定类别。它看起来像这样。
文章图片
因此, 我们将有一个如上所述的简单布局, 只有3列包含书籍的不同部分。最近读过的书, 最喜欢的书, 是的, 最好的书中最好的(我承认, 我不知道该叫什么, , )
因此, 此处的目标是能够在任何部分的卡片上添加书名, 作者和说明, 编辑已添加的书并删除现有的书。我们不会使用任何数据库, 因此一切都在状态中发生。
首先, 我们安装Nuxt:
npm install create-nuxt-app
其次, 在安装Nuxt之后, 你现在可以使用以下命令创建项目:
create-nuxt-app bookStore
我选择将应用命名为” bookStore” ;你可以命名更酷的名字^ _ ^
然后, 浏览其余的提示, 输入说明,
文章图片
作者名称, 键入名称或按Enter键保留默认值
文章图片
选择一个包管理器, 无论你满意还是满意
文章图片
选择一个UI框架。对于这个项目, 我将使用Vuetify, 然后再次使用任何你喜欢的UI框架都可以。
文章图片
选择一个自定义服务器框架;我们不需要任何东西, 我不会选择
文章图片
多余的模块, 选择你想要的, 或者同时选择两者, 我们将不会在此应用中使用它们。
文章图片
整理很重要。让我们来看一下ESLint。
文章图片
尽管测试很重要, 但我们今天将不再关注它, 因此没有一个
文章图片
渲染模式, 是的, SSR。
注意:选择SSR并不意味着我们没有获得SPA的好处, 该应用程序仍然是SPA, 但具有SSR。另一个选项仅意味着SPA而没有SSR。
文章图片
点击Enter并继续前进,
文章图片
我们的项目正在创建,
文章图片
创建之后, 我们现在可以进入目录并运行
yarn dev
如果你使用npm作为包管理器, 请使用,
npm run dev
文章图片
默认情况下, 该应用程序在localhost:3000上运行。访问浏览器中的链接, 你应该看到默认的Nuxt页面。
现在开始创建所需的组件。我们将拥有显示每本书信息的卡片, 并且将有一个模式, 其中包含用于输入新书信息或编辑现有书信息的表格。
要创建组件, 只需在components文件夹中创建一个新文件。这是我的卡组件的代码。
// BookCard.vue<
template>
<
v-card class="mx-auto" max-width="400">
<
v-img src="http://img.readke.com/220515/11364SN5-14.jpg" height="200px">
<
/v-img>
<
v-card-title>
{{bookTitle}}<
/v-card-title>
<
v-card-subtitle>
{{bookAuthor}}<
/v-card-subtitle>
<
v-card-text>
{{bookDescription}}<
/v-card-text>
<
v-card-actions>
<
v-spacer>
<
/v-spacer>
<
slot name="button">
<
/slot>
<
/v-card-actions>
<
/v-card>
<
/template>
<
script>
export default {
props: ["bookTitle", "bookAuthor", "bookDescription"]
};
<
/script>
以上内容的简要说明。图像是硬编码的;我们暂时不会为此烦恼。书籍标题, 书籍作者和书籍说明作为道具从父页面传递到此组件。如果你不熟悉道具, 可以想象一下它们是可以通过数据填充此组件的入口点。
现在到下一个组件, 模态。
//BookModal.vue<
template>
<
v-dialog max-width="500px" v-model="open">
<
v-card class="p-5">
<
v-card-title>
Add Books<
/v-card-title>
<
v-form>
<
v-select v-model="category" :items="categories" label="Select A Category">
<
/v-select>
<
v-text-field v-model="title" label="Enter Book Title">
<
/v-text-field>
<
v-text-field v-model="author" label="Enter Book Author">
<
/v-text-field>
<
v-textarea v-model="description" label="Enter Book Description">
<
/v-textarea>
<
/v-form>
<
v-card-actions>
<
v-spacer>
<
/v-spacer>
<
v-btn @click.stop="saveBook" color="green">
Add<
/v-btn>
<
/v-card-actions>
<
/v-card>
<
/v-dialog>
<
/template>
现在, 这是模态的标记;我们需要创建v模型作为数据属性;因此, 我们将在< template> 标签下面添加一个script标签。
<
script>
export default {
data() {
return {
category: "", title: "", author: "", description: "", };
}, }
<
/script>
此外, 还有一个” 选择类别” 下拉列表, 该下拉列表需要” 类别” 数据。我们将其添加到数据中。
<
script>
export default {
data() {
return {
open: false, category: "", title: "", author: "", description: "", categories: ["Recently read books", "Favourite books", "Best of the best"]
};
}, }
<
/script>
现在, 我们需要一种方式来切换模式打开和关闭, 现在, 我们将只有上面的” 打开” 数据属性。接下来, 我们将仔细研究。
让我们快速创建我们的视图页面, 在该页面中, 我们将有三个网格/列, 每本书一个部分。我们将其称为页面index.vue, 请参见下面的代码。
//index.vue
<
template>
<
div>
<
v-row>
<
v-col md="4">
<
h2 class="text-center mb-5">
Recently Read Books<
/h2>
<
/v-col>
<
v-col md="4">
<
h2 class="text-center mb-5">
Favourite Books<
/h2>
<
/v-col>
<
v-col md="4">
<
h2 class="text-center mb-5">
Best of the Best<
/h2>
<
/v-col>
<
/v-row>
<
BookModal />
<
/div>
<
/template>
文章图片
现在我们有了网格, 我们需要为添加的每本书添加卡片组件到每个网格。因此, 我们将导入BookCard.vue组件。
<
template>
<
div>
<
v-row>
<
v-col md="4">
<
h2 class="text-center mb-5">
Recently Read Books<
/h2>
<
v-row v-for="(item, index) in recentBooks" :key="index">
<
BookCard
class="mb-5"
:bookTitle="item.title"
:bookAuthor="item.author"
:bookDescription="item.description"
>
<
template v-slot:button>
<
v-btn @click.stop="edit(item, index)">
Edit<
/v-btn>
<
v-btn @click.stop="remove(item.category, index)">
Remove<
/v-btn>
<
/template>
<
/BookCard>
<
/v-row>
<
/v-col>
<
v-col md="4">
<
h2 class="text-center mb-5">
Favourite Books<
/h2>
<
v-row v-for="(item, index) in favouriteBooks" :key="index">
<
BookCard
class="mb-5"
:bookTitle="item.title"
:bookAuthor="item.author"
:bookDescription="item.description"
>
<
template v-slot:button>
<
v-btn @click.stop="edit(item, index)">
Edit<
/v-btn>
<
v-btn @click.stop="remove(item.category, index)">
Remove<
/v-btn>
<
/template>
<
/BookCard>
<
/v-row>
<
/v-col>
<
v-col md="4">
<
h2 class="text-center mb-5">
Best of the Best<
/h2>
<
v-row v-for="(item, index) in bestOfTheBest" :key="index">
<
BookCard
class="mb-5"
:bookTitle="item.title"
:bookAuthor="item.author"
:bookDescription="item.description"
>
<
template v-slot:button>
<
v-btn @click.stop="edit(item, index)">
Edit<
/v-btn>
<
v-btn @click.stop="remove(item.category, index)">
Remove<
/v-btn>
<
/template>
<
/BookCard>
<
/v-row>
<
/v-col>
<
/v-row>
<
/div>
<
/template>
现在, 我们导入了BookCard组件, 并将其道具绑定到循环的结果中。这样可以确保为添加到任何部分的每个条目创建一张卡片。此外, 在每张卡上, 我们都将包含用于编辑或删除卡的按钮。
现在, 我们需要从脚本中导入卡, 并定义将保存每个类别记录的数组。
<
script>
import BookCard from "@/components/BookCard";
export default {
components: {
BookCard, }, data() {
return {
recentBooks: [], favouriteBooks: [], bestOfTheBest: []
};
}, };
<
/script>
接下来, 我们需要在标题中有一个按钮, 该按钮将在需要添加书籍时打开模式。我们将在” default.vue” 文件中执行此操作。我们会将按钮添加到默认的应用栏标题。
<
v-btn color="green" @click.stop="openModal">
Add Books<
/v-btn>
接下来, 我们需要在脚本部分中创建openModal方法。在常规的Vue JS应用程序中, 有一个事件总线, 它使你可以与另一个组件进行通信, 甚至可以在Nuxt JS中传递数据, 甚至还有一个事件总线, 你仍然可以用相同的方式创建它。因此, 我们将使用事件总线传递数据, 以从layout / default.vue文件在index.vue页(我们尚未导入)中打开一个模式。
让我们看看它是如何完成的。
要创建全局事件总线, 请在项目的根目录中打开一个文件, 将其命名为eventBus.js并将其下面的代码粘贴到其中。
import Vue from 'vue'export const eventBus = new Vue()
是的, 全部。现在我们可以使用它了。
<
script>
import { eventBus } from "@/eventBus";
methods: {
openModal() {
eventBus.$emit("open-add-book-modal");
}
}
<
/script>
接下来, 我们将返回BookModal组件, 并聆听eventBus何时发出” open-add-book-modal” 。我们将其添加到脚本部分。
import { eventBus } from "@/eventBus";
created() {
eventBus.$on("open-add-book-modal", this.open = true);
},
现在, 我们可以打开和关闭模式, 但是它还没有添加任何书籍。让我们在Modal中添加一种方法, 使其保存添加到状态中的内容(请记住, 我们没有使用任何数据库或本地存储)。我们将其添加到” created()” 旁边
methods: {
saveBook() {
let cardData = http://www.srcmini.com/{
title: this.title, author: this.author, description: this.description, category: this.category
};
eventBus.$emit("save-book", cardData);
this.open = false;
}
}
接下来, 当我们编辑任何卡中的数据时, 我们需要一种重新填充模态的方法。因此, 让我们对” created()” 进行一些调整
created() {
eventBus.$on("open-add-book-modal", data =http://www.srcmini.com/>
{
if (data) {
this.category = data.category;
this.title = data.title;
this.author = data.author;
this.description = data.description;
}
this.open = true;
});
},
现在, BookModal整体看起来像这样,
//BookModal.vue<
template>
<
v-dialog max-width="500px" v-model="open">
<
v-card class="p-5">
<
v-card-title>
Add Books<
/v-card-title>
<
v-form>
<
v-select v-model="category" :items="categories" label="Select A Category">
<
/v-select>
<
v-text-field v-model="title" label="Enter Book Title">
<
/v-text-field>
<
v-text-field v-model="author" label="Enter Book Author">
<
/v-text-field>
<
v-textarea v-model="description" label="Enter Book Description">
<
/v-textarea>
<
/v-form>
<
v-card-actions>
<
v-spacer>
<
/v-spacer>
<
v-btn @click.stop="saveBook" color="green">
Add<
/v-btn>
<
/v-card-actions>
<
/v-card>
<
/v-dialog>
<
/template>
<
script>
import { eventBus } from "@/eventBus";
export default {
data() {
return {
open: false, category: "", title: "", author: "", description: "", categories: ["Recently read books", "Favourite books", "Best of the best"]
};
}, created() {
eventBus.$on("open-add-book-modal", data =>
{
if (data) {
this.category = data.category;
this.title = data.title;
this.author = data.author;
this.description = data.description;
}
this.open = true;
});
}, methods: {
saveBook() {
let cardData = http://www.srcmini.com/{
title: this.title, author: this.author, description: this.description, category: this.category
};
eventBus.$emit("save-book", cardData);
this.open = false;
}
}
};
<
/script>
接下来, 我们现在可以返回index.vue页面以导入BookModal组件。我们将其添加到脚本部分。
<
script>
import BookCard from "@/components/BookCard";
import BookModal from "@/components/BookModal";
import { eventBus } from "@/eventBus";
export default {
components: {
BookCard, BookModal
}, data() {
return {
recentBooks: [], favouriteBooks: [], bestOfTheBest: []
};
}, <
/script>
另外, 在正文中, 我们将添加
<
BookModal/>
我们需要用于编辑和删除卡的方法。在前面的模板中, 我已经将edit和remove方法传递给按钮, 如下所示, 同样, 我传递了每种方法所需的参数。
<
template v-slot:button>
<
v-btn @click.stop="edit(item, index)">
Edit<
/v-btn>
<
v-btn @click.stop="remove(item.category, index)">
Remove<
/v-btn>
<
/template>
让我们创建方法。
methods: {
remove(category, index) {
if (category === "Recently read books") {
this.recentBooks.splice(index, 1);
}
if (category === "Favourite books") {
this.favouriteBooks.splice(index, 1);
}
if (category === "Best of the best") {
this.bestOfTheBest.splice(index, 1);
}
}, edit(item, index) {
if (item.category === "Recently read books") {
eventBus.$emit("open-add-book-modal", item);
this.recentBooks.splice(index, 1);
}
if (item.category === "Favourite books") {
eventBus.$emit("open-add-book-modal", item);
this.favouriteBooks.splice(index, 1);
}
if (item.category === "Best of the best") {
eventBus.$emit("open-add-book-modal", item);
this.bestOfTheBest.splice(index, 1);
}
}
}
请记住, BookModal正在发出, 并且有一个名为save-book的事件, 我们在这里需要一个用于该事件的侦听器。
created() {
eventBus.$on("save-book", cardData =http://www.srcmini.com/>
{
if (cardData.category ==="Recently read books") {
this.recentBooks.push(cardData);
}
if (cardData.category === "Favourite books") {
this.favouriteBooks.push(cardData);
}
if (cardData.category === "Best of the best") {
this.bestOfTheBest.push(cardData);
}
});
},
现在, 从整体上看, 我们的index.vue页面看起来像这样
<
template>
<
div>
<
v-row>
<
v-col md="4">
<
h2 class="text-center mb-5">
Recently Read Books<
/h2>
<
v-row v-for="(item, index) in recentBooks" :key="index">
<
BookCard
class="mb-5"
:bookTitle="item.title"
:bookAuthor="item.author"
:bookDescription="item.description"
>
<
template v-slot:button>
<
nuxt-link :to="`/books/${item.title}`">
<
v-btn>
View<
/v-btn>
<
/nuxt-link>
<
v-btn @click.stop="edit(item, index)">
Edit<
/v-btn>
<
v-btn @click.stop="remove(item.category, index)">
Remove<
/v-btn>
<
/template>
<
/BookCard>
<
/v-row>
<
/v-col>
<
v-col md="4">
<
h2 class="text-center mb-5">
Favourite Books<
/h2>
<
v-row v-for="(item, index) in favouriteBooks" :key="index">
<
BookCard
class="mb-5"
:bookTitle="item.title"
:bookAuthor="item.author"
:bookDescription="item.description"
>
<
template v-slot:button>
<
v-btn @click.stop="edit(item, index)">
Edit<
/v-btn>
<
v-btn @click.stop="remove(item.category, index)">
Remove<
/v-btn>
<
/template>
<
/BookCard>
<
/v-row>
<
/v-col>
<
v-col md="4">
<
h2 class="text-center mb-5">
Best of the Best<
/h2>
<
v-row v-for="(item, index) in bestOfTheBest" :key="index">
<
BookCard
class="mb-5"
:bookTitle="item.title"
:bookAuthor="item.author"
:bookDescription="item.description"
>
<
template v-slot:button>
<
v-btn @click.stop="edit(item, index)">
Edit<
/v-btn>
<
v-btn @click.stop="remove(item.category, index)">
Remove<
/v-btn>
<
/template>
<
/BookCard>
<
/v-row>
<
/v-col>
<
/v-row>
<
BookModal />
<
/div>
<
/template>
<
script>
import BookCard from "@/components/BookCard";
import BookModal from "@/components/BookModal";
import { eventBus } from "@/eventBus";
export default {
components: {
BookCard, BookModal
}, data() {
return {
recentBooks: [], favouriteBooks: [], bestOfTheBest: []
};
}, created() {
eventBus.$on("save-book", cardData =http://www.srcmini.com/>
{
if (cardData.category ==="Recently read books") {
this.recentBooks.push(cardData);
this.recentBooks.sort((a, b) =>
b - a);
}
if (cardData.category === "Favourite books") {
this.favouriteBooks.push(cardData);
this.favouriteBooks.sort((a, b) =>
b - a);
}
if (cardData.category === "Best of the best") {
this.bestOfTheBest.push(cardData);
this.bestOfTheBest.sort((a, b) =>
b - a);
}
});
}, methods: {
remove(category, index) {
if (category === "Recently read books") {
this.recentBooks.splice(index, 1);
}
if (category === "Favourite books") {
this.favouriteBooks.splice(index, 1);
}
if (category === "Best of the best") {
this.bestOfTheBest.splice(index, 1);
}
}, edit(item, index) {
if (item.category === "Recently read books") {
eventBus.$emit("open-add-book-modal", item);
this.recentBooks.splice(index, 1);
}
if (item.category === "Favourite books") {
eventBus.$emit("open-add-book-modal", item);
this.favouriteBooks.splice(index, 1);
}
if (item.category === "Best of the best") {
eventBus.$emit("open-add-book-modal", item);
this.bestOfTheBest.splice(index, 1);
}
}
}
};
<
/script>
如果你走得这么远, 那就好!!!你真棒!
文章图片
如前所述, 页面文件夹中创建的每个文件夹的创建都将自动将页面文件夹中创建的每个.vue文件设置为路由。这不仅适用于静态页面, 还可以通过这种方式创建动态页面!
让我们看看如何。
假设我们要使用当前项目, 要为所有图书卡片添加一个动态页面, 并带有查看按钮以查看有关图书的更多详细信息。
让我们快速添加一个查看按钮, 然后使用< nuxt-link> 访问该页面。是的, < nuxt-link> 替换了< router-link> , 并且可以正常工作。
文章图片
<
nuxt-link :to="`/${item.title}`">
<
v-btn>
View<
/v-btn>
<
/nuxt-link>
接下来, 我们通过在名称前面加上下划线来创建动态文件夹。即_title并在该文件夹中, 我们将有一个index.vue文件, 当我们访问该路由时会对其进行渲染。
仅出于演示目的, 我们将仅访问文件中的params属性。
// _title/index.vue<
template>
<
h1>
{{$route.params.title}}<
/h1>
<
/template>
现在, 当我们单击视图时, 它会打开另一个页面, 在这里我们可以看到通过路线的标题。就动态页面而言, 可以将其开发为做我们想做的任何事情。
文章图片
本课就是这样!
完整的代码可以在此存储库中找到。欢迎你为代码做出贡献。如果你有兴趣掌握框架, 那么我建议你参加Udemy课程。
推荐阅读
- 如何创建你的第一个Jenkins管道()
- centos 遇到/dev/mapper/cl-root 100% 解决方法
- Android 开发环境搭建及程序打包方法
- kotlin android中Recyclerview的适配器类
- Android OS的image文件组成
- Android-原笔迹手写的探索与开发
- 当前Android开发人员如何提升岗位竞争力
- mybatis使用Dao和Mapper方式
- [Android] Android 支持下拉刷新上拉加载更多 的 XRecyclerview