如何使用 Vue 2 制作带有本地存储的 CRUD 应用程序

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 MozillaChrome扩展。

SHIFT + CTRL + J在 Windows/Linux 或Command + Option + jMacOS上打开浏览器的 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>

现在,作为子组件导入组件并在组件对象中声明它BookItemBooks在这里,我们将遍历数据并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.vueto Books.vueup to传递的 IDApp.vue吗?

它将用于删除书籍,使用 JavaScriptfilter()方法创建书籍数组,不包括具有传递 id 的书籍。我们将使用如下所示的 ES6 箭头函数,这将返回除传递了 id 的书籍之外的所有书籍。

将此方法添加到methodsin 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 连同它一起传递。

将此方法添加到methodsin Books.vue

editBookMethod(id){
    //send to parent (App.vue)     this.$emit('edit-book-event', id)
}

在父级中,进行更改以edit-book-eventBooks组件中捕获事件,并将其分配给方法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为空字符串和editfalse。

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 对象的索引。

该索引将用于重新分配正在编辑的图书的标题。如果您已经达到了这一步,您可以编辑书名。

将下面的代码添加到methodsin 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 存储库中代码的链接。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值