表单操作

<body>
  <div id="app">
    <form action="http://itcast.cn">
      <div>
        <span>姓名:</span>
        <span>
          <input type="text" v-model="uname" />
        </span>
      </div>
      <div>
        <span>性别:</span>
        <span>
          <input type="radio" id="male" value="1" v-model="gender" />
          <label for="male"></label>
          <input type="radio" id="female" value="2" v-model="gender" />
          <label for="female"></label>
        </span>
      </div>
      <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="code" value="3" v-model="hobby" />
        <label for="code">写代码</label>
      </div>
      <div>
        <span>职业:</span>
        <select v-model="occupation" multiple>
          <option value="0">请选择职业...</option>
          <option value="1">教师</option>
          <option value="2">软件工程师</option>
          <option value="3">律师</option>
        </select>
      </div>
      <div>
        <span>个人简介:</span>
        <textarea v-model="desc"></textarea>
      </div>
      <div>
        <input type="submit" value="提交" @click.prevent="handle" />
      </div>
    </form>
  </div>
  <script type="text/javascript" src="../js/vue.js"></script>
  <script type="text/javascript">
    /*
          表单基本操作
        */
    var vm = new Vue({
      el: "#app",
      data: {
        uname: "lisi",
        gender: 2,
        hobby: ["2", "3"],
        // occupation: 3
        occupation: ["2", "3"],
        desc: "nihao",
      },
      methods: {
        handle: function () {
          // console.log(this.uname)
          // console.log(this.gender)
          // console.log(this.hobby.toString())
          // console.log(this.occupation)
          console.log(this.desc);
        },
      },
    });
  </script>
</body>

表单域修饰符

  • lazy

    取代 input 监听,change事件。

    <input type="text" v-model.lazy="v1" />

    8ef5108c-402e-499e-baf4-7811499fa017

  • number

    将输入的字符串转为有效数字

  • trim

    输入首尾空格过滤

自定义指令

自定义指令在 VUE 内部提供了两种注册方式:全局指令局部指令

全局指令即全局部分注册即可。

Vue.directive("指令名称", { 指令配置 });

局部指令注册即在某个对象内注册,指令的生效范围为当前对象的。例如在创建 Vue 对象内注册的指令,可以在这个对象的内使用。

new Vue({
  el: "#app",
  directives: {
    指令名称: { 指令配置 },
  },
});

注册命令时不需要使用v-前缀,但使用时需要使用v-前缀

指令生命周期(钩子函数)

指令的运行方式很简单,它提供了一组指令生命周期钩子函数,我们只需要在不同的生命周期钩子函数中进行逻辑处理就可以了

  • bind : 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
  • inserted : 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
  • update : 所在组件更新的时候调用
  • componentUpdated : 所在组件更新完成后调用
  • unbind : 只调用一次,指令与元素解绑时调用

不同的生命周期钩子函数在调用的时候同时会接收到传入的一些不同的参数

  • el : 指令所绑定的元素,可以用来直接操作 DOM
  • binding : 一个对象,包含以下属性:
    • name : 指令名,不包括 v- 前缀
    • value : 指令的绑定值(作为表达式解析后的结果)
    • expression : 指令绑定的表达式(字符串)
    • arg : 传给指令的参数,可选
    • modifiers : 传给指令的修饰符组成的对象,可选,每个修饰符对应一个布尔值
    • oldValue : 指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用,无论值是否改变都可用

f72aa4fc-82c8-4863-a24a-f82786476a5c

当页面刷新时自动将焦点定位到输入框

<body>
  <div id="app">
    <input type="text" v-focus />
  </div>
  <script type="text/javascript" src="../js/vue.js"></script>
  <script type="text/javascript">
    Vue.directive("focus", {
      inserted: function (el) {
        // el表示指令绑定的元素
        el.focus();
      },
    });
    let app = new Vue({
      el: "#app",
      data: {},
      methods: {},
    });
  </script>
</body>

自定义指令如果需要传入参数,那么只需要等于一个值即可。

<input type="text" v-color="msg" />
let app = new Vue({
  el: "#app",
  data: {
    msg: "red",
  },
});

局部指令

只需要在组件中定义directives属性即可,使用方式与全局指令一样。

计算属性

<body>
  <div id="app">
    <div>{{msg}}</div>
    <div>{{rmsg}}</div>
  </div>
  <script type="text/javascript" src="../js/vue.js"></script>
  <script type="text/javascript">
    let app = new Vue({
      el: "#app",
      data: {
        msg: "nihao",
      },
      // 计算属性基于data中的数据
      computed: {
        rmsg: function () {
          return this.msg.split("").reverse().join("");
        },
      },
    });
  </script>
</body>

计算属性基于data中的数据,可以直接使用。

image-20201101155424414

计算属性和方法的区别

  • 计算属性时基于他们的依赖(data中的数据)进行缓存的
  • 方法没有缓存

例如定义一个计算属性rmsg调用两次,但是控制台只会出现一次log

<body>
  <div id="app">
    <div>{{rmsg}}</div>
    <div>{{rmsg}}</div>
  </div>
  <script type="text/javascript" src="../js/vue.js"></script>
  <script type="text/javascript">
    let app = new Vue({
      el: "#app",
      data: {
        msg: "nihao",
      },
      // 计算属性基于data中的数据
      computed: {
        rmsg: function () {
          console.log("运行了");
          return this.msg.split("").reverse().join("");
        },
      },
    });
  </script>
</body>

image-20201101155612085

侦听器

8c2942fb-cc57-477c-a49e-b986f85c0e89

<body>
  <div id="app">
    <input type="text" v-model.lazy="msg" />
  </div>
  <script type="text/javascript" src="../js/vue.js"></script>
  <script type="text/javascript">
    let app = new Vue({
      el: "#app",
      data: {
        msg: "",
      },
      methods: {
        getContent: function (val) {
          // 模拟异步
          setTimeout(() => {
            this.msg = "我被修改了哦!!";
          }, 2000);
        },
      },
      watch: {
        msg: function (val) {
          // 调用后台接口
          this.getContent(val);
        },
      },
    });
  </script>
</body>

多层监听

对于多层数据的监听,可以使用字符串+点语法

watch: {
  'a.b.c': function() {
    //...
  }
}

深度监听

默认情况下,watch 只对当前指定的值进行一层监听,如果需要对对象进行深度监听

watch: {
  a: {
    handler() {
      console.log('a deep');
    },
    deep: true
  }
}

过滤器

<body>
  <div id="app">
    <input type="text" v-model="msg" />
    <div>{{msg | upper}}</div>
  </div>
  <script type="text/javascript" src="../js/vue.js"></script>
  <script type="text/javascript">
    Vue.filter("upper", function (val) {
      return val.toUpperCase();
    });
    let app = new Vue({
      el: "#app",
      data: {
        msg: "",
      },
    });
  </script>
</body>

image-20201101162544945

  • | : 管道符,表示数据从左至右通过管道符进行传递
  • 过滤器可以有多个,执行顺序从左至右,过滤器函数第一个参数的值就是其管道符前一个的结果
  • 过滤器可以在插值表达式、绑定属性时使用。

携带参数

<p>{{date|format('abc')}}</p>

接受参数从第二个形参开始,第一个参数是值。

Vue.filter("format", function (val, arg) {
  console.log(arg); // abc
  return val;
});

示例:

<body>
  <div id="app">
    <!-- 过滤器传递的参数通过第二个形参获取 -->
    <p>{{date}}</p>
    <p>{{date|format('yyy-MM-dd')}}</p>
  </div>
  <script type="text/javascript" src="../js/vue.js"></script>
  <script type="text/javascript">
    Vue.filter("format", function (val, arg) {
      console.log(val.getMonth());
      if (arg == "yyy-MM-dd") {
        var ret = "";
        ret +=
          val.getFullYear() + "-" + (val.getMonth() + 1) + "-" + val.getDate();
        return ret;
      }
    });
    let app = new Vue({
      el: "#app",
      data: {
        date: new Date(),
      },
    });
  </script>
</body>

image-20201101164324373

生命周期

生命周期钩子

Vue 实例生命周期

数组更新检测

  1. 变更方法:会影响原始数据

    • push()

      向数组末尾添加数据

    • pop()

      去除

    • shift()

      去掉第一个元素

    • unshift()

      向第一个元素添加数据

    • splice()

      删除指定的元素

    • sort()

      排序

    • reverse()

      反转

  2. 替换方法:不会影响原始数据

    • filter()
    • concat()
    • slice()

通过索引修改列表的数据不是响应式的,需要通过set/$set方法进行修改。

let app = new Vue({
  el: "#app",
  data: {
    list: ["apple", "orange", "banana"],
  },
});
Vue.set(app.list, 2, "lemon");
// 或者如下写法
// app.$set(app.list, 2, 'lemon')

image-20201101170722239

如果修改的是对象,那么第二个参数传入键即可。

app.$set(app.obj, "age", 14);

综合案例:图书管理系统

列表开发

<body>
  <div id="app">
    <div class="grid">
      <table>
        <thead>
          <tr>
            <th>编号</th>
            <th>名称</th>
            <th>时间</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <tr :key="item.id" v-for="item in books">
            <td>{{item.id}}</td>
            <td>{{item.name}}</td>
            <td>{{item.date}}</td>
            <td>
              <a href="" @click.prevent>修改</a>
              <span>|</span>
              <a href="" @click.prevent>删除</a>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
  <script type="text/javascript" src="../js/vue.js"></script>
  <script type="text/javascript">
    var vm = new Vue({
      el: "#app",
      data: {
        books: [
          {
            id: 1,
            name: "三国演义",
            date: "",
          },
          {
            id: 2,
            name: "水浒传",
            date: "",
          },
          {
            id: 3,
            name: "红楼梦",
            date: "",
          },
          {
            id: 4,
            name: "西游记",
            date: "",
          },
        ],
      },
    });
  </script>
</body>

添加图书

添加图书的逻辑,在 data 中定义两个属性,用于与输入框进行双向数据绑定。通过点击事件将这些数据组合成一个对象添加到数据数组中。

<div>
  <h1>图书管理</h1>
  <div class="book">
    <div>
      <label for="id"> 编号: </label>
      <input type="text" id="id" v-model="id" />
      <label for="name"> 名称: </label>
      <input type="text" id="name" v-model="name" />
      <button @click="handle">提交</button>
    </div>
  </div>
</div>
var vm = new Vue({
  el: "#app",
  data: {
    id: "",
    name: "",
    books: [
      {
        id: 1,
        name: "三国演义",
        date: "",
      },
      {
        id: 2,
        name: "水浒传",
        date: "",
      },
      {
        id: 3,
        name: "红楼梦",
        date: "",
      },
      {
        id: 4,
        name: "西游记",
        date: "",
      },
    ],
  },
  methods: {
    handle() {
      let book = {};
      book.id = this.id;
      book.name = this.name;
      book.date = "";
      this.books.push(book);
      this.id = "";
      this.name = "";
    },
  },
});

修改图书

  1. 点击修改后,将信息填充到顶部编辑栏
  2. 修改与新增共用一个函数
<a href="" @click.prevent="toEdit(item.id)">修改</a>
methods: {
    handle() {
        if (this.flag) {
            // 编辑
            this.books.some((item) => {
                if (item.id == this.id) {
                    item.name = this.name
                    // 终止循环
                    return true
                }
            })
            this.flag = true
        } else {
            // 添加
            let book = {}
            book.id = this.id
            book.name = this.name
            book.date = ''
            this.books.push(book)

        }
        this.id = ''
        this.name = ''
    },
        toEdit(id) {
            this.flag = true
            // 根据ID查询出要编辑的数据
            let book = this.books.filter(function (item) {
                return item.id == id
            })
            // 把获取到的数据填充到表单
            this.id = book[0].id
            this.name = book[0].name
        }
},

删除

两种删除方法

// 根据ID查询出要编辑的数据
let book = this.books.findIndex(function (item) {
  return item.id == id;
});
// 根据索引删除数组元素
this.books.splice(index, 1);
// 另一种方法
this.books = this.books.filter(function (item) {
  return item.id != id;
});