Vue.js 是一个进步的、多功能的前端 JavaScript 框架。由于其简单性和小包大小,它具有很高的可采用性。本地存储是现代浏览器中的一种 Web 存储 API,它允许我们将数据作为字符串的键值对存储在用户浏览器上。
有了这个,我们可以在不与后端应用程序通信的情况下处理数据,从而增强数据持久性。与使用 cookie 时不同,后者在客户端上最多存储 4kb 的数据。它们在发出 HTTP 请求时发送到服务器,并且可以由服务器修改。
先决条件
要跟随本教程,读者将需要:
- Node.js 6.x 或更高版本
- Npm 5.10 或更新版本
- CLI 视图
- 一些 JavaScript、CSS 和 HTML 的知识
使用 Vue CLI 创建项目
要创建 Vue.js 项目,首先检查 Vue CLI 是否全局安装在您的计算机中。
使用终端运行:
$ vue –version
如果未安装,请运行以下命令进行安装。
$ npm install -g @vue/cli
转到您的工作区文件夹并运行以下命令以创建一个新的 Vue.js 应用程序。
$ vue create books-app
使用箭头键选择:
❯ Default ([Vue 2] babel, eslint)
然后点击进入。创建后,导航到创建的文件夹books-app
并通过运行以下命令为应用程序提供服务:
$ cd books-app
$ npm run serve
然后,在浏览器中打开 URL http://localhost:8080 以查看应用程序。
安装 Vue.js 开发工具
这是一个用于调试 Vue.js 应用程序的浏览器扩展。它检查组件、道具、路由、vuex 等。
打开您的浏览器,并安装 Vue.js DevTools Mozilla或Chrome扩展。
SHIFT + CTRL + J
在 Windows/Linux 或Command + Option + j
MacOS上打开浏览器的 DevTools 。
创建书籍组件
此应用程序将管理要阅读的书籍列表。使用您选择的代码编辑器打开应用程序。我们浏览器上显示的HelloWorld
组件是位于src/components
文件夹中的组件。
我们将删除它及其在App.vue
包含图像徽标时的引用。现在在文件夹中创建一个Books.vue
文件components
。将以下代码添加到此文件中。
<template>
<div>
<h2>My Books List</h2>
</div>
</template>
<script>
export default {
name: "Books"
}
</script>
<style scoped>
</style>
请注意,在 Vue 2 中,组件模板应该只包含一个根元素。否则会抛出错误。然后我们可以将Books
组件导入到根组件中。该App.vue
组件现在应该如下所示。
data 函数返回一个空的书籍数组,我们稍后会添加这些书籍来填充数组。
<template>
<div id="app">
<Books/>
</div>
</template>
<script>
import Books from "./components/Books";
export default {
name: 'App',
components: {
Books
},
data () {
return {
books: []
}
}
}
</script>
<style>
#app {
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
v-bind
是一个 Vue 指令,用于将数据附加到 Vue 组件。这将帮助我们绑定传递给Books
组件的数据。
App.vue
如下所示进行更改。
<Books v-bind:books="books"/>
props
用于将数据从父组件传递到子组件。在这种情况下,App.vue
是父组件,Books.vue
而是子组件。
要使用道具,请编辑<script>
子组件的 ,如下所示。
<script>
export default {
name: "Books",
props: ["books"]
}
</script>
创建“BookItem”组件
在 components 文件夹中创建一个文件BookItem.vue
. 该组件将代表一本书。的代码BookItem.vue
应如下所示。
<template>
<div>
<p></p>
</div>
</template>
<script>
export default {
name: "BookItem"
}
</script>
现在,作为子组件导入组件并在组件对象中声明它BookItem
。Books
在这里,我们将遍历数据并BookItems
使用 Vue 指令向用户显示v-for
。
的代码Books.vue
现在应该如下所示。
<template>
<div>
<h2>My Books List</h2>
<div v-bind:key="book.id" v-for="book in books"> <BookItem v-bind:book="book"></BookItem>
</div>
</div>
</template>
<script>
import BookItem from "./BookItem";
export default {
name: "Books",
props: ["books"],
components: {
BookItem
}
}
</script>
注意v-bind:key
. 这很重要,因为它为 Vue 提供了跟踪每个节点身份的提示。将v-bind:book
数据绑定到 Vue 组件。
要显示一本书,请编辑BookItem.vue
如下所示。
<template>
<div>
<p>{{book.title}}</p>
</div>
</template>
<script>
export default {
name: "BookItem",
props: ["book"]
}
</script>
您可以将自己的数据添加到 books 数组中App.vue
,如下所示,以将数据显示到 UI。
books: [
{
id:1,
title: "1000 Leagues Under the Sea"
},
{
id:2,
title: "The Scorpion"
},
]
创建“AddBookItem”组件
您现在可以删除上面的JSON 测试数据。在该components
文件夹下,创建一个名为AddBook.vue
. 将其导入App.vue
并在脚本内的 components 对象中声明,如下所示。
import Books from "./components/Books";
import AddBookItem from "./components/AddBookItem";
export default {
name: 'App',
components: {
Books,
AddBookItem
},
}
现在将以下代码添加到AddBookItem.vue
.
<template>
<div>
<form @submit="addBook">
<input type="text" name="title" v-model="title" placeholder="Add Book"> <button type="submit">Add Book</button>
</form>
</div>
</template>
<script>
export default {
name: "AddBookItem",
data () {
return {
title: ''
}
},
methods: {
addBook(e){
e.preventDefault();
const newBook = {
title: this.title,
id: Math.floor(Math.random() * 100)
};
if (newBook.title !== ''){
this.$emit('add-book-event', newBook);
}
this.title = ''
}
}
}
</script>
此代码有一个表单,您可以使用它来添加一本书。它还有一个方法addBook()
和一个 vue 指令v-model
,用于在用户输入和 Vue.js 组件之间创建一个双向绑定。对输入值的任何更改都会更改绑定数据,反之亦然。在这种情况下title
。您将看到一个用于添加书籍的表单。
每本书都需要一个唯一的 ID。我们将使用 JavaScript 的Math.random()
方法来生成唯一的 id。
该$emit()
方法发出一个事件add-book-event
,用于根据用户的操作将数据从子组件传递到父组件。当用户添加一本书并提交时,此事件将发送给父级。
为了让父级 ( App.vue
) 监听add-book-event
来自子级 ( AddBookItem.vue
) 的事件,我们创建了一个方法addBook()
并将其分配给发出的事件。
对 进行更改,App.vue
如下所示。让我们在模板中AddBookItem()
的组件之上。Book
<template>
<div id="app">
<AddBookItem v-on:add-book-event="addBook" />
<Books v-bind:books="books"/>
</div>
</template>
紧随其后,使用下面的代码data()
添加一个方法。addBook()
methods: {
addBookItem(newBook){
this.books = [...this.books, newBook]
},
}
该方法将新书添加到书籍数组,我们使用扩展运算符,这会将新书添加到数组的末尾,而不创建新数组。
将数据保存到本地存储
我们将使用 Vue.js 的内置方法watch()。此方法自动监视书籍数组中的更改并将数据保存到本地存储。
该watch()
方法有一个名为的属性,该属性deep
设置为 true 以通知 Vue 实例始终监视书籍数组中的更改。
watch()
用于处理组件外部的数据,例如浏览器 API 或获取数据。
将以下代码添加到<script>
in App.vue
。
watch: {
books: {
handler() {
localStorage.setItem('books',JSON.stringify(this.books))
},
deep: true
}
}
本地存储使用该setItem()
方法将数据保存为键值对,数据必须是字符串,因此我们将 JSON 转换为字符串,以便使用JSON.stringify()方法将其保存。
从本地存储加载数据
我们需要显示保存的数据,从本地存储到用户。我们将使用一个名为mounted()的生命周期钩子,它在Vue 实例创建后执行。
在循环钩子中,我们使用该方法localStorage.getItem('key')
从本地存储中检索数据。我们用来存储的密钥与我们将用来检索数据的密钥相同。
将下面的代码添加watch()
到App.vue
.
mounted() {
if (localStorage.getItem("books")){
this.books = JSON.parse(localStorage.getItem("books"))
}
}
该JSON.parse()
方法将字符串转换为 JavaScript 对象,因为数据仅作为字符串存储在本地存储中。
然后将数据设置为显示给用户的 books 数组。
添加后,您现在可以看到图书列表。
从本地存储中删除数据
BookItem.vue
使用以下代码更新:
<div class="float-left">
<span class="float-right">
{{book.title}}
<button>
<i class="glyphicon glyphicon-trash" @click="$emit('del-book-item', book.id)">delete</i>
</button>
</span>
</div>
index.html
我在文件中添加了一个引导 CSS CDN 链接。你可以设计你的样式,让它看起来更好。
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
当您单击删除按钮时,会发出一个事件以将书籍 ID 传递给 parent( Books.vue
)。$emit()
是 Vue 将数据从子组件传递到父组件的方式。
在Books.vue
更新<BookItem/>
中,如下所示。
<BookItem v-bind:book="book" v-on:del-book-item="delBookMethod" />
将来自孩子的事件分配给一个名为 的方法delBookMethod()
。将它添加到方法对象中并向其父级 ( App.vue
) 发出一个事件,并与它一起传递书籍 ID。
methods: {
delBookMethod(id){
//send to parent this.$emit('del-book-event', id);
},
}
在父组件(App.vue
)中,让我们进行一些更改。
<Books v-bind:books="books" v-on:del-book-event="deleteBookItem" />
子事件被捕获并分配给一个方法deleteBookItem()
。这个方法将帮助我们删除被点击的图书项目。还记得我们从BookItem.vue
to Books.vue
up to传递的 IDApp.vue
吗?
它将用于删除书籍,使用 JavaScriptfilter()
方法创建书籍数组,不包括具有传递 id 的书籍。我们将使用如下所示的 ES6 箭头函数,这将返回除传递了 id 的书籍之外的所有书籍。
将此方法添加到methods
in AppVue
。
deleteBookItem(id){
this.books = this.books.filter(book => book.id !== id);
}
编辑数据
就像我们在删除数据时所做的那样,BookItem.vue
对添加编辑按钮进行更改,代码应该如下所示。
<template>
<div class="float-left">
<span class="float-right">
{{book.title}}
<button>
<i class="glyphicon glyphicon-pencil" @click="$emit('edit-book-item', book.id)">edit</i>
</button>
<button>
<i class="glyphicon glyphicon-trash" @click="$emit('del-book-item', book.id)">delete</i>
</button>
</span>
</div>
</template>
一个被调用的事件edit-book-item
被发出,并与它一起将书籍 ID 传递给它的 parent( Books.vue
)。在Books.vue
监听事件并将其分配给editBookMethod()
如下所示调用的方法。
<BookItem v-bind:book="book" v-on:del-book-item="delBookMethod" v-on:edit-book-item="editBookMethod" />
使用该方法,向其父级 ( App.vue
) 发送一个事件,并将图书 ID 连同它一起传递。
将此方法添加到methods
in Books.vue
。
editBookMethod(id){
//send to parent (App.vue) this.$emit('edit-book-event', id)
}
在父级中,进行更改以edit-book-event
从Books
组件中捕获事件,并将其分配给方法editBookItem()
。在data
创建editBook
将保存正在编辑的数据的新对象。
对象应该有一个标题和一个 ID。
两者都应该是空字符串。
data () {
return {
books: [],
editBook: {
title: '',
id: ''
}
}
}
现在在editBookItem
方法中,我们需要找到对象 id 的索引。我们使用 JavaScript 的findIndex()
方法来做到这一点。我们通过书籍数组找到与从子组件传递的 ID 匹配的书籍对象,并将其分配给一个变量objIndex
。
这个变量帮助我们从 books 数组中访问书名,并将其与editBook
对象的 id 一起分配给对象中的书名,如下所示。
editBookItem(id){
//find the index of the book's id var objIndex = this.books.findIndex(obj=> obj.id === id);
this.editBook.title = this.books[objIndex].title;
this.editBook.id = id;
},
我们仍然不能编辑一本书。我们捕获组件中较早的事件edit-book-event
并将AddBookItem
其分配给方法editBookItemEvent()
。
然后,我们使用指令将属性绑定editBook
到组件,v-bind
并将其作为道具传递给子 ( AddBookItem
),如下所示。
<AddBookItem v-model="editBook.title" v-on:add-book-event="addBookItem" v-bind:editBook="editBook"/>
让我们打开AddBookItem.vue
。我们editBook
从父级接收数据对象作为道具。然后在 data 函数中添加id
为空字符串和edit
false。
name: "AddBookItem",
props: ['editBook'],
data () {
return {
title: '',
id: '',
edit: false
}
}
我们将使用此edit
属性来决定是编辑还是添加新书。我们首先检查用户是否没有在编辑。我们保存数据,否则我们将编辑数据。
如果我们正在编辑,我们发出一个edit-book-event
并将保存编辑数据的变量bookItem
与事件一起传递给父级。
我们还清除输入字段。现在更新addBookItem()
方法如下所示。
addBook(e){
e.preventDefault();
if (this.edit === false){
// add new book const newBook = {
title: this.title,
id: Math.floor(Math.random() * 100)
};
if (newBook.title !== ''){
this.$emit('add-book-event', newBook);
}
this.title = ''
}else{
//edit book const bookItem = {
title: this.title,
id: this.id
}
//send to parent (App.vue) this.$emit('edit-book-event', bookItem)
// clear input field this.title = '';
this.edit = false;
}
}
现在您可以单击编辑按钮,输入字段将填充书名。该watch()
方法再次派上用场,帮助我们观察 editBook 数据中的任何变化。
我们设置deep:true
属性让 Vue 实例持续观察变化。因此,在编辑一本书时,该edit
属性将始终为真。
它还监视 title 属性,如果它为空,则将edit
属性设置为 false。
在这里,我们不需要该deep
属性。
watch: {
editBook: {
handler() {
this.title = this.editBook.title;
this.id = this.editBook.id;
this.edit = true
},
deep: true
},
title: {
handler() {
if (this.title === ''){
this.edit = false;
}
}
}
}
回到App.vue
,编辑标题后,将事件edit-book-event
发送到App.vue
。我们将事件分配给一个方法,以便将更改保存到本地存储。更新您的代码,如下所示。
<AddBookItem v-model="editBook.title" v-on:add-book-event="addBookItem" v-bind:editBook="editBook" v-on:edit-book-event="editBookItemEvent" />
现在我们创建一个editBookItemEvent()
方法来处理数据的保存。在该方法中,我们找到了 id 对象的索引。
该索引将用于重新分配正在编辑的图书的标题。如果您已经达到了这一步,您可以编辑书名。
将下面的代码添加到methods
in App.vue
。
editBookItemEvent(bookItem){
//find the index of this id's object let objIndex = this.books.findIndex(obj => obj.id === bookItem.id)
//update the item this.books[objIndex].title = bookItem.title;
}
现在,您App.vue
应该看起来像下面显示的代码。
<template>
<div id="app">
<AddBookItem v-on:add-book-event="addBookItem" v-on:edit-book-event="editBookItemEvent" v-bind:editBook="editBook"/>
<div>
<Books v-bind:books="books" v-on:del-book-event="deleteBookItem" v-on:edit-book-event="editBookItem" />
</div>
</div>
</template>
<script>
import Books from "./components/Books";
import AddBookItem from "./components/AddBookItem";
export default {
name: 'App',
components: {
Books,
AddBookItem
},
data () {
return {
books: [],
editBook: {
title: '',
id: ''
}
}
},
methods: {
addBookItem(newBook){
// console.log('newbook', newBook.title); this.books = [...this.books, newBook];
// this.books.unshift(newBook) },
deleteBookItem(id){
this.books = this.books.filter(book => book.id !== id);
},
editBookItem(id){
//find the index of the book's id let objIndex = this.books.findIndex(obj=> obj.id === id);
this.editBook.title = this.books[objIndex].title;
this.editBook.id = id;
},
editBookItemEvent(bookItem){
//find the index of this id's object let objIndex = this.books.findIndex(obj => obj.id === bookItem.id)
//update the item this.books[objIndex].title = bookItem.title;
}
},
watch: {
books: {
handler() {
localStorage.setItem('books',JSON.stringify(this.books))
},
deep: true
}
},
mounted() {
if (localStorage.getItem("books")){
this.books = JSON.parse(localStorage.getItem("books"))
}
}
}
</script>
<style>
#app {
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
结论
我们刚刚创建了一个带有本地存储的 CRUD Vue2 应用程序。您可以使用物化组件或其他 UI 设计材料来改进应用程序的用户界面。
如果你问我,Vue 是一件相当不错的艺术品。它更干净,场景下具有令人敬畏的功能。如果您遇到困难,这里是我的GitHub 存储库中代码的链接。