Vue.js学习笔记(一)

385 阅读5分钟

1.Vue.js概述

\quad Vue.js是一款用于构件用户界面的渐进式框架,能帮助减少不必要的DOM操作(DOM操作开销比较大)。

  • 渐进式框架:声明式渲染->组件系统->客户端路由->集中式状态管理->项目构件。
  • dom操作:由于HTML文档被浏览器解析后就是一棵DOM树,改变HTML的结构,需要通过js操作DOM。

2.Vue.js使用

2.1 Vue的基本使用步骤

2.2 实例

<body>
    <div id="app">
      <div>{{msg+'-----'}}</div>
    </div>
    //从官网下载的vue.js文件,将其放到项目目录下的js目录下
    <script type="text/javascript" src="js/vue.js"></script>
    <script type="text/javascript">
      var vm = new Vue({
        // el-元素的挂载位置(值可以是CSS选择器或DOM元素)
        el: '#app',
        //模型数据(值是一个对象)
        data: {
          msg: 'Hello World'
        }
      });
    </script>
</body>

2.3 Vue代码运行原理分析

\quad 对于<div>{{msg+'-----'}}</div>这段代码,浏览器并不知道如何去解析。之所以能正常显示在浏览器页面上,是由于Vue框架将Vue代码解析成了原生的js代码。

3.Vue模板语法

3.1 指令

\quad Vue中的指令的格式以v-开始。

3.2 v-cloak指令解决闪动问题

\quad 前面说到,Vue框架会将代码解析成原生的js代码,之后由浏览器渲染结果,所以用户在刷新页面的时候,可能能看到后面的代码。使用v-cloak可避免这种显示效果。
\quad 根据官网上给出的用法:v-cloak指令,在Head中指定样式:

 <style type="text/css">
    [v-cloak]{
      display: none;
    }
 </style>

然后即可在div中添加v-cloak指令:

 <div id="app" v-cloak>
      {{msg+'------'}}
 </div>

\quad 能够实现这种效果的本质在于,先通过样式隐藏内容,然后在内存中进行值的替换,替换好之后再显示最终的结果。

3.3 数据绑定指令之v-text

\quad v-text指令用于插入纯文本,例如:<span v-text="msg"></span>可与前面的{{msg+'------'}}显示同样的效果,并且不用添加v-cloak解决闪动问题,所以更推荐使用v-text绑定数据。

3.4 数据绑定指令之v-html

\quad v-html指令用于插入HTML片段,例如在data中增加

msg1:'<br>123<br><h2>测试内容</h2>'

使用

<span v-html="msg1"></span>

插入html片段。

3.5 数据绑定指令之v-pre

\quad v-pre指令用于显示原始信息,跳过Vue的解析阶段。例如:

<span v-pre>{{这句话不会被编译}}</span>

显示效果:

3.6.数据相应式指令v-once

\quad 响应式指的是数据变化而导致页面内容的变化。也就是打开浏览器后,使用F12进入控制台,可以使用vm.msg=123修改内容。但是使用:

<div id="app" v-once>
      <p>{{msg+'------'}}</p>
       <span v-text="msg"></span>
       <span v-html="msg1"></span>
       <span v-pre>{{这句话不会被编译}}</span>
</div>

v-once指令声明代码块只编译一次,不具有响应式之后,就不能通过控制台修改页面内容了。

3.7 数据的双向绑定v-model

\quad 数据的双向绑定指的是Model与View的内容实现同步更新。例如:

<input type="text" v-model='msg'>

即可实现这种效果:

注意:在View中更改msg的值,Model中的msg也会同样修改,同时通过Console修改Model中的值,View中也会有相应的显示。

3.8 事件的绑定v-on

\quad 举例:

       <p>{{num}}</p>
       <button v-on:click="num++">+1</button>
       <button v-on:click="num--">-1</button>

num在data中的初值为零:

不过num++,num--这样的函数一般会将其写在单独的函数中,提高可读性:

 <button v-on:click="add">+1</button>
 <button @click="minus">-1</button>

可以用@替代v-on:。

methods:{
          add:function () {
            this.num++
          },
          minus:function(){
            this.num--
          }
        }

3.9 事件参数的传递

\quad 在前面的例子中,我们将num++,num--这样的操作抽取到函数中,那么对于函数中需要传递参数的情况:

<button v-on:click="add(num,$event)">+1</button>
<button @click="minus">-1</button>
methods:{ 
          add:function (num,event) {
            if(num<10){
              ++this.num
            }
            console.log(event.target.innerHTML)
            console.log("还有"+(10-num)+"次机会")
          },
          minus:function(event){
            console.log(event.target.tagName)
            this.num--
          }
          }

效果:

tagName为BUTTON,innerHTML为+1;

结论:

  • 1.如果事件直接绑定函数名称,那么默认会传递事件对象作为事件函数的第一个参数。
  • 2.如果事件绑定函数调用,那么事件对象必须作为最后一个参数显示传递,并且事件对象的名称必须是$event。

3.10 v.stop阻止冒泡

假设将代码改成这样:

<div id="app" v-on:click="add(num,$event)">
       <p>{{num}}</p>
       <button>+1</button>
       <button @click="minus">-1</button>
</div>

\quad 可以发现,点击+1会进行+1操作,但是点击-1数字却不会变化。这是由于在执行button中的操作时,同样会执行div中的操作,一减一加,所以看起来数字不发生改变。

\quad 所以需要在minus中阻止冒泡:

 minus:function(event){
            event.stopPropagation();
            console.log(event.target.tagName)
            this.num--
          }

这种方式也可以简化为:

<button v-on:click.stop="minus">-1</button>

3.11 v.prevent阻止默认操作

\quad 存在如下a标签:

<a href="http://www.zhihu.com" target="_blank" v-on:click="another($event)">知乎</a>

我们知道,点击“知乎”即可默认跳转到知乎的首页,并且绑定了another函数。但是在another中加上:

event.preventDefault();

即可阻止这种默认行为。同时这种方式也可以简化为:

v-on:click.prevent

3.12 v.self只有自身可以触发事件

\quad 即排除冒泡所触发的情形,对于这种情况:

<div id="app" v-on:click.self="add(num,$event)">
       <button v-on:click="add(num,$event)">+1</button>
       <button v-on:click="minus">-1</button>
</div>

只有在点击“+1”时,才会调用add()方法,由于点击“-1”而冒泡调用的add()方法不会执行。

3.13 修饰符之间的串联

\quad 当然,修饰符之间可以串联,例如:v.on:click:prevent.self会阻止自身以及冒泡调用方法。

3.14 v.enter/v.delete 按键修饰符

\quad 对于用户提交表单的情况,我们需要实现用户点击enter也可以进行提交,这时候就需要用到v-on:keyup指令。例如:

<body>
    <div id="app">
      <form action="">
        <div>
          用户名:
          <input v-model="username" type="text" />
        </div>
        <div>
          密码:
          <input v-model="password" v-on:keyup.enter="handel" type="text" />
        </div>
        <div>
          <input type="button" v-on:click="handel" value="提交" />
        </div>
      </form>
    </div>

    <script type="text/javascript" src="js/vue.js"></script>
    <script type="text/javascript">
      var vm = new Vue({
        el: '#app',
        data: {
          username: '',
          password: ''
        },
        methods: {
          handel: function(username) {
            console.log(this.username, this.password);
          }
        }
      });
    </script>
  </body>

\quad 即可实现,键盘输入enter也可提交表单。注意:v-on:keyup后需要加.enter,否则按任何键都可触发提交操作。v.delete同理。

3.15 自定义按键修饰符

\quad 首先我们知道,键盘上的每一个按键都有一个唯一的数字标识,称之为keyCode。可使用:

<div>
     <input type="text" v-on:keyup="number" />
</div>
number: function(event) {
            console.log(event.keyCode);
          }

查看对应的值。
\quad 知道键盘上每个按键对应的数值后,可以使用: v-on:keyup.65来定义a键的操作,使用v-on:keyup.32来定义空格键的操作。

\quad 根据官网提示,可以通过配置别名来给对应ASCII码的键盘数字取别名。例如: Vue.config.keyCodes.show = 65;

3.16 v-bind属性绑定

\quad 我们知道,在<a>标签中可以将标签名与链接相绑定,但是需要使用某个变量代替url时:

<a href="url" target="_blank">知乎</a>
data: {
          url: 'http://www.zhihu.com'
        },

这时候之间点击知乎是会报错的,这时候需要用到v-bind指令:

 <a v-bind:href="url" target="_blank">知乎</a>

或者可以简写为:

 <a :href="url" target="_blank">知乎</a>

那么其实前面用到的双向数据绑定v-model,本质上也用到了v-bind。例如:

<input type="text" v-bind:value="url" v-on:input="write" />
write: function(event) {
            this.url = event.target.value;
          }

也可以实现v-model的相同效果。或者,可以直接将其简写为:

v-on:input="url=$event.target.value"

3.17 v-bind的class样式处理

\quad 可以使用v-bind:class动态的切换class:

<style>
      .boder {
        border: 4px solid red;
        width: 80px;
        height: 100px;
      }
      .background {
        background: blue;
      }
      .font {
        color: white;
      }
      .circle {
        width: 100px;
        height: 100px;
        border-radius: 50px;
      }
    </style>
  <body>
    <div id="app">
      <div v-bind:class="{active:isActive,backgroundColor:isBack}"></div>
      <button v-on:click="change">显示/隐藏边框</button>
      <button v-on:click="change1">显示/隐藏底色</button>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>
    <script type="text/javascript">
      var vm = new Vue({
        el: '#app',
        data: {
          isActive: 'true',
          isBack: 'true'
        },
        methods: {
          change: function() {
            this.isActive = !this.isActive;
          },
          change1: function() {
            this.isBack = !this.isBack;
          }
        }
      });
    </script>
  </body>
</html>

v-class的数组绑定:

 <body>
    <div id="app">
      <div v-bind:class="[boderClass,backgroundClass]"></div>
      <button v-on:click="change">隐藏</button>
      <button v-on:click="return1">显示</button>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>
    <script type="text/javascript">
      var vm = new Vue({
        el: '#app',
        data: {
          boderClass: 'boder',
          backgroundClass: 'background'
        },
        methods: {
          change: function() {
            (this.boderClass = ''), (this.backgroundClass = '');
          },
          return1: function() {
            (this.boderClass = 'boder'), (this.backgroundClass = 'background');
          }
        }
      });
    </script>
  </body>

显示效果:

其他注意事项:

  • 1.对象绑定和数组绑定可以结合使用,例如:
 <div v-bind:class="[boderClass,backgroundClass,{font:isWhite}]">
  • 2.class绑定的值可以简化处理,例如:
arrayClass: ['circle', 'background', 'boder']

一个属性名可以绑定多个属性。

  • 3.默认的class会被保留。例如:
<div class="font" v-bind:class="arrayClass">测试用文字</div>

font属性不会被覆盖,而是会跟arrayClass中的属性一同显示。

3.18 style样式处理(内联样式处理)

\quad 在前面我们介绍了通过v-bind:class=''控制对象的样式,那么同样可以使用v-bind:style=''修改样式。举例:

<div v-bind:style="{border:borderStyle,background:backgroundStyle,width:widthStyle,height:heightStyle}"></div>
data: {
          borderStyle: '2px solid red',
          backgroundStyle: 'blue',
          widthStyle: '80px',
          heightStyle: '100px',
          circlestyle: 'circle'
        },

上面这种书写方式可以简写为:

<div v-bind:style="circlestyle"></div>
circlestyle: {
            width: '100px',
            height: '100px',
            border: '2px solid blue',
            background: 'red'
          }

同样v-bind:style中也可以使用数组例如:

 <div v-bind:style="[circlestyle,circlestyle1]"></div>

注意:如果circlestyle中的属性与circlestyle1中的属性有重复的,那么后者会覆盖前者,不重复的则会相互补充。

4.分支循环结构

分支结构包括:

  • v-if
  • v-else
  • v-else-if
  • v-show(控制元素是否显示)

4.1 分支

\quad 首先我们知道,在methods中可以使用if/else实现相应的逻辑:

methods: {
          calculate: function() {
            if (this.score > 90) {
              this.result = '优秀';
            } else {
              this.result = '良好';
            }
          }
        }

那么也可以采用这种方式:

 <div v-if="100>=score&&score>=90">优秀</div>
 <div v-else-if="score<90&&score>=80">良好</div>

4.2 循环

对于for循环,使用v-for指令,举例说明:

data: {
          weeks: ['monday','tuesday','wednesday','thrusday','friday','saturday','sunday']
        },

遍历:

<ul>
        <li v-for="week in weeks">{{week}}</li>
</ul>

注意:

  • 在这里week是别名,用于代表数组中的一个示例。
  • in是关键字

如果需要索引的话,可以这样使用:

<li :key='index' v-for="(week,index) in weeks">{{week}}-星期{{index+1}}</li>

index也是关键字。

4.3 v-for循环遍历对象

例如: data中存在这样一个对象:

 aMan: {
            username: 'zhangsan',
            password: 'password',
            timestamp: '2020-02-20'
          }

通过循环将对象中的属性逐一显示出来:

 <div v-for="(v,k,i) in aMan">{{v+'---'+k+'---'+i}}</div>

v代表value, k代表key,i代表index。

5.Vue常用特性

概览: 1.表单操作 2.自定义指令 3.计算属性 4.过滤器 5.侦听器 6.生命周期

5.1 基于Vue的表单操作

  • input 单行文本
  • textarea 多行文本
  • select 下拉多选
  • radio 单选框
  • checkbox 多选框

示例:

  • 1.input单行文本
<input type="text" v-model="uname" />
  • 2.select 下拉多选
<div>
            <span>掌握语言:</span>
            <select v-model="langunage" multiple="true">
            <option value="1">Java</option>
            <option value="2">c</option>
            <option value="3">c++</option>
            <option value="4">python</option>
            </select>
</div>
  • 3.radio单选框
<div>
            <span>性别:</span>
            <!-- label的name设置为一致,则只能选中一个 -->
            <input
              type="radio"
              name="sex"
              id="male"
              value="1"
              v-model="gender"
            />
            <label for="male">男</label>
            <input
              type="radio"
              name="sex"
              id="female"
              value="2"
              v-model="gender"
            />
            <label for="female">女</label>
          </div>
  • 4.checkbox多选框
<div>
            <span>爱好:</span>
            <input type="checkbox" id="ball" value="1" v-model="hobby" />
            <label for="ball">篮球</label>
            <input type="checkbox" id="sing" value="2" v-model="hobby" />
            <label for="sing">唱</label>
            <input type="checkbox" id="jump" value="3" v-model="hobby" />
            <label for="jump">跳</label>
            <input type="checkbox" id="rap" value="4" v-model="hobby" />
            <label for="rap">rap</label>
 </div>
  • 5.textarea多行文本
<div>
              <span>自我介绍:</span>
              <textarea v-model="resume" cols="30" rows="2"></textarea>
 </div>

5.2 表单域修饰符

  • number:转化为数值 示例:
<input type="text" v-model.number="len" size="2" disabled="disabled" />
  • trim:去掉开始和结尾的空格
<input type="text" v-model.trim="message" size="10" />
  • lazy:将input事件切换为change事件
 <div v-text="message"></div>
        <input type="text" v-model.lazy="message" />

\quad 改变input中内容不会马上引起div显示内容的变化,当输入框失去焦点时,才会引起change的变化。

5.3 自定义指令

\quad 官网中给出的例子:

// Vue.directive-Vue指令中的API,定义指令函数
      //  函数的第一各参数为指令的名称,第二个参数实现对应名称的逻辑
      Vue.directive('focus', {
        inserted: function(el) {
          // el表示指令所绑定的元素
          el.focus();
        }
      });

实现效果在于:加上v-focus标识后,每次打开页面时,焦点自动对应到选定的区域。

5.4 带参数的自定义指令

根据官网的描述,示例:

Vue.directive('color', {
        bind: function(el, binding) {
            // 将元素的背景色指定为所绑定的对象属性中的color
          el.style.backgroundColor = binding.value.color;
        }
      });

在input中指定参数:

<input type="text" v-color="msg" />

msg内容:

msg: {
            color: 'red'
          },

5.5 局部指令

\quad 可通过在new Vue()中添加directives的方式注册局部指令:

directives: {
          color: {
            bind: function(el, binding) {
              // 将元素的背景色指定为所绑定的对象属性中的color
              el.style.backgroundColor = binding.value.color;
            }
          }
        }

与全局指令不同,局部指令只能在本组件中使用。

5.6 计算属性

\quad 使用计算属性可以使得模板内容更加简洁。

computed: {
          reverseMessage: function() {
            return this.message
              .split('')
              .reverse()
              .join('');
          }
        }

可以通过直接调用属性名获取属性:

<span v-text="reverseMessage"></span>

计算属性与方法的区别:

  • 1.计算属性是基于他们的依赖进行缓存的。
  • 2.函数每调用一次就会执行一次,不存在缓存。

5.7 侦听器

  • 1.侦听器用于数据变化时执行异步或开销较大的操作。 举例,输入姓和名,自动拼接姓名:
watch: {
          firstname: function(val) {
            this.fullname = val + ' ' + this.lastname;
          },
          lastname: function(val) {
           this.fullname = this.firstname + ' ' + val;
          }
        }

调用监听器的名称,获取监听器的属性:

<input type="text" v-model="fullname" size="5" />

效果:

实例:验证用户名是否存在

<div>
        <span>名称:</span>
        <input type="text" v-model.lazy="uname" />
        <span>{{message}}</span>
</div>
methods: {
          checkname: function(uname) {
            //在此处调用接口,但是可以使用定时任务的方式模拟接口调用
            //在setTimeout函数中的this指的是windows,所以需要在外面缓存this
            var that = this;
            setTimeout(function() {
              if (uname == 'admin') {
                that.message = '用户名已被使用,请选择其他用户名...';
              } else {
                that.message = '用户名可以被使用...';
              }
            }, 3000);
          }
        },
        watch: {
          uname: function(val) {
            this.checkname(val);
            this.message = '用户名正在核查...';
          }
        }

5.8 Vue过滤器

自定义过滤器:

Vue.filter('过滤器名称',function(value){
    //过滤器业务逻辑
})

按照官网的提示定义一个过滤器:

Vue.filter('upper1', function (val) {
      return val.charAt(0).toUpperCase() + val.slice(1);
    });

调用过滤器:

    <div>
      {{msg|upper1}}
    </div>

过滤器之间可以串联:

    Vue.filter('lower', function (val) {
      return val.charAt(0).toLowerCase() + val.slice(1);
    });

val默认是需要处理的数据,既lower,调用方法:

    <div>
      {{msg|upper |lower}}
    </div>

另一种调用方法:

    Vue.filter('formate', function (val) {
      return val.slice(1);
    });
    <div>
      <a v-bind:href="url | formate" target="_blank">测试跳转</a>
    </div>

带参数的过滤函数:

      Vue.filter('add', function(val, formateType) {
        if (formateType == 'yyyy-mm-dd') {
          var formateDate = '';
          formateDate +=
            val.getFullYear() +
            '-' +
            (val.getMonth() + 1) +
            '-' +
            val.getDate();
        }
        return formateDate;
      });
        data: {
          formateType: 'yyyy-mm-dd',
          date: new Date()
        },

显示效果:

5.9 生命周期

主要阶段:

  • 1.挂载(初始化相关属性)
  • 2.更新(元素或组件的变更操作)
  • 3.销毁(销毁相关属性)

在Vue中用方法表示为以下几个阶段:

  • beforeCreate:在实例初始化之后,数据观测和事件配置之前被调用。
  • created:在实例创建完成后被立即调用。
  • beforeMount:在挂载开始之前被调用。
  • mounted:el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。
  • beforeUpdate:数据更新时调用,发生在虚拟DOM打补丁之前。
  • Updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。
  • beforeDestroy:实例销毁之前调用。
  • Destroy:实例销毁之后调用。

举例说明:

    <div id="app">
      <input type="text" v-model="msg" />
      <div>
        <button v-on:click="update">更新内容</button>
        <button v-on:click="destroy">删除内容</button>
      </div>
    </div>
        methods: {
          update: function() {
            this.msg = '456';
          },
          destroy: function() {
            this.$destroy();
          }
        },
        beforeCreate: function() {
          console.log('beforeCreate');
        },
        created: function() {
          console.log('created');
        },
        beforeMount: function() {
          console.log('beforMount');
        },
        mounted: function() {
          console.log('mounted');
        },
        beforeUpdate: function() {
          console.log('beforeUpdate');
        },
        updated: function() {
          console.log('updated');
        },
        beforeDestroy: function() {
          console.log('beforeDestroy');
        },
        destroyed: function() {
          console.log('destroyed');
        }

定义两个按钮,分别表示更新和删除操作,可以看到每个阶段所调用的函数:

6.MVVM开发思想

\quad MVVM是Model-View-ViewModel的缩写,Model实现模型控制,View控制页面展示,VM控制逻辑。

  • DOM Listeners-DOM监听
  • Data Bindings-数据绑定

7. script的type类型

\quad type用以属性规定脚本的 MIME 类型。MIME 类型包括两部分:media type 和 subtype。对于 JavaScript,MIME 类型是 "text/javascript"。在HTML5中type类型默认为"text/javascript"。

一些常用的值:

  • text/javascript (默认)
  • text/ecmascript
  • application/ecmascript
  • application/javascript
  • text/vbscript

8.项目实战-图书管理系统

数组相关API:

1.变异方法:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

2.替换数组(生成新的数组)

  • filter()
  • concat()
  • slice

3.响应式更改数组数据:

Vue.set(vm.bookName, 2, '兄弟');
vm.$set(vm.page1, 1, '朝花夕拾');
//数组响应式变化
vm.$set(vm.user, 'age', 18);

4.常用特性应用场景

  • 过滤器(格式化日期)
  • 自定义指令(获取表单焦点)
  • 计算属性(统计图书数量)
  • 侦听器(验证图书存在性)
  • 声明周期(图书数据处理)

5.代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style type="text/css">
      [v-cloak] {
        display: none;
      }

      .gridTable {
        /* 设置外边距属性 */
        margin: auto;
        width: 500px;
        text-align: center;
      }

      .gridTable table {
        width: 100%;
        /* 表格属性,表示两个表格的边框合并为一个,默认为separate表示不合并,collapse表示合并边框 */
        border-collapse: collapse;
      }

      .gridTable th,
      td {
        padding: 10;
        /* dashed表示虚线 */
        border: 1px solid rgb(15, 15, 15);
        height: 35px;
        line-height: 35px;
      }

      .gridTable th {
        /* background-color: rgb(93, 205, 240); */
      }

      .gridTable total {
        /* height: 300px;
        line-height: 300px;
        background-color: orange;
        border-top: 1px orange solid; */
      }

      .center {
        text-align: center;
      }
    </style>
  </head>

  <body>
    <div id="app" v-cloak>
      <div class="gridTable">
        <table>
          <tr>
            <th>
              <div class="center">
                <p v-html="message"></p>
              </div>
            </th>
          </tr>
        </table>

        <table>
          <tbody>
            <tr>
              <th>
                <label for="id">编号:</label>
                <input
                  type="text"
                  id="id"
                  size="3"
                  v-model="newBookId"
                  v-bind:disabled="noEditBookId"
                  v-focus
                />
              </th>
              <th>
                <label for="name">书名:</label>
                <input type="text" id="name" size="12" v-model="newBookName" />
              </th>
              <th>
                <label for="name">姓名:</label>
                <input
                  type="text"
                  id="userName"
                  size="8"
                  v-model="newUserName"
                />
              </th>
              <th>
                <button v-on:click="addItem" v-bind:disabled="submitFlag">
                  提交
                </button>
              </th>
            </tr>
          </tbody>
        </table>
        <table>
          <tr>
            <th>
              <div class="total">
                <span>图书总数:</span>
                <span>{{total}}</span>
              </div>
            </th>
          </tr>
        </table>

        <table>
          <thead>
            <tr>
              <th>编号</th>
              <th>名称</th>
              <th>借出时间</th>
              <th>借阅人</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>
            <div>
              <tr :key="index" v-for="(book,index) in books">
                <td>{{book.id}}</td>
                <td>{{book.name}}</td>
                <td>{{book.lendTime |formate}}</td>
                <td>{{book.userName}}</td>
                <td>
                  <a href="" v-on:click.prevent="editBook(book.id)">编辑</a>
                  <span>|</span>
                  <a href="" v-on:click.prevent="deleteItem(book.id)">删除</a>
                </td>
              </tr>
            </div>
          </tbody>
        </table>
      </div>
      <p class="center">
        <span style="color: red;font-weight: bold;">{{tips}}</span>
      </p>
    </div>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
      Vue.filter('formate', function(val) {
        return (
          val.getFullYear() +
          '-' +
          val.getMonth() +
          '-' +
          val.getDate() +
          ' ' +
          val.getHours() +
          ':' +
          val.getMinutes() +
          ':' +
          val.getSeconds()
        );
      });
      Vue.directive('focus', {
        inserted: function(parm) {
          parm.focus();
        }
      });
      var vm = new Vue({
        el: '#app',
        data: {
          noEditBookId: false,
          submitFlag: false,
          newBookName: '',
          newBookId: '',
          newUserName: '',
          message: '<h2>---欢迎使用图书管理系统V1.0---<h2>',
          tips: '',
          books: []
        },
        methods: {
          addItem: function() {
            if (this.noEditBookId == false) {
              if (this.newBookName != '') {
                var el = {};
                el.name = this.newBookName;
                el.id = this.newBookId;
                el.lendTime = new Date();
                el.userName = this.newUserName;
                this.books.push(el);
              } else {
                this.tips = '书名不能为空!';
              }
              this.noEditBookId = false;
            } else {
              //根据当前的ID去更新数组中对应的数据
              //使用数组的遍历,定位到需要修改的数组对象
              // this.books.some(function(val){
              //   this(some是Vue中自带的函数,所以在这里的this指的是windows)
              // });
              this.books.some(parm => {
                //在这里的this同样指的是windows
                if (parm.id == this.newBookId) {
                  parm.name = this.newBookName;
                  parm.userName = this.newUserName;
                  return true;
                }
              });
              this.noEditBookId = false;
            }
            // 修改或者增加书籍后,将输入框置空
            (this.newBookId = ''),
              (this.newBookName = ''),
              (this.newUserName = '');
          },
          deleteItem: function(id) {
            // // 根据id从数组中查找元素的索引
            // var index = this.books.findIndex(function(parm) {
            //   return parm.id == id;
            // });
            // this.books.splice(index, 1);
            // 方法2:使用数组的filter方法
            this.books = this.books.filter(function(parm) {
              return parm.id != id;
            });
          },
          editBook: function(id) {
            // 定位对应ID的Book
            // 返回结果是包含对应ID的对象的数组
            var readToEdit = this.books.filter(function(item) {
              return item.id == id;
            });
            this.newBookId = readToEdit[0].id;
            this.newBookName = readToEdit[0].name;
            this.newUserName = readToEdit[0].userName;
            this.noEditBookId = true;
          }
        },
        computed: {
          total: function() {
            return this.books.length;
          }
        },
        watch: {
          newBookName: function(parm) {
            var flag = this.books.some(function(item) {
              return item.name == parm;
            });
            if (flag) {
              this.submitFlag = true;
            } else {
              this.submitFlag = false;
            }
          }
        },
        mounted: function() {
          // 该声明周期钩子函数被触发的时候,模板已经可以使用
          // 此时一般用于获取后台数据,然后把数据填充到模板
          var data = [
            {
              id: 1,
              name: '许三观卖血记',
              lendTime: new Date(),
              userName: '张三'
            },
            {
              id: 2,
              name: '活着',
              lendTime: new Date(),
              userName: '李四'
            },
            {
              id: 3,
              name: '在细雨中呐喊',
              lendTime: new Date(),
              userName: '王五'
            }
          ];
          this.books = data;
        }
      });
    </script>
  </body>
</html>

页面:

9.参考文档