第五章 Vue.js

内容来源于蓝桥杯竞赛,自己根据这个题纲重新巩固下前端的知识

第五章 Vue.js

认识VUE

因为自己开发框架就是vue所以对vue有了一个基本的使用了解,这部分就不详述了,有需要可以到官网看下。看文档才是最高效的学习方法。

模板语法

双大括号表达式
插值

但是通过使用 v-once 指令你也能执行一次性地插值,当数据 改变时,插值处的内容 不会更新。但是你需要注意一下,该元素节点下面其他数据的绑定,数据改变,内容也不会更新,所以,注意代码块的划分。

<p v-once>msg:{{msg}}</p>

那就是因为双大括号表达式会将数据解释为普通文本。有的同学会问,有的需求就是要把标签渲染出来,那么我们就需要认识另外一个指令 v-html,使用它我们就能将它正确渲染。

    <!-- v-html 渲染 html 元素-->
    <div id="app" v-html="msg"></div>
    <script>
      var app = new Vue({
        el: "#app", // el: 挂载点
        data: {
          // data: 数据选项
          msg: "<h1>hello syl</h1>",
        },
      });
    </script>

们只进行了绑定简单的属性键值。但实际上,对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持,感受强大的模板语法力量吧!

    <!-- javascript 表达式-->
    <div id="app">
      <!-- 运算符 -->
      <p>num + 24 = {{num + 24}}</p>
      <!-- 三元表达式 -->
      <p>Are you ok? {{ok ? 'I am ok !':'no'}}</p>
      <!-- 对象方法直接调用 -->
      <p>名字倒过来写:{{name.split('').reverse().join('')}}</p>
      <!-- 属性值运算操作 -->
      <p v-bind:class="'col'+colNum">syl</p>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          num: 20,
          ok: true,
          name: "实验楼",
          colNum: "12",
        },
      });
    </script>
v-bind 指令

值的绑定

v-on 指令

事件的绑定,如下动态操作的方法没有用过

<!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>syl-vue</title>
    <!--引入 vue.js-->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <!-- 指令 动态参数-->
    <div id="app">
      <p>我叫:{{name}}</p>
      <button v-on:[event]="handleClick">点我</button> // 动态参数
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          name: "实验楼",
          event: "click",
        },
        methods: {
          handleClick: function () {
            this.name = this.name.split("").reverse().join("");
          },
        },
      });
    </script>
  </body>
</html>
修饰符

修饰符是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定,大致分为三类,后面课程我们会一一接触到:

  • 事件修饰符
  • 按键修饰符
  • 系统修饰符

例如,事件修饰符中的 .prevent 修饰符和原生 event.preventDefault() 效果一样,可以阻止事件默认行为,在表单中点击提交按钮,就会发生页面跳转,但是使用了 .prevent 就不会发生跳转,例如:

<!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>syl-vue</title>
    <!--引入 vue.js-->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <!-- 指令 修饰符-->
    <div id="app">
      <form action="/" v-on:submit.prevent="submit">
        <button type="submit">提交</button>
      </form>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {},
        methods: {
          submit: function () {
            console.log("成功提交!");
          },
        },
      });
    </script>
  </body>
</html>
指令缩写

v-bind使用:替换,例如v-bind:class="className" 可以简写为 :class="className"v-bind:value="myValue" 可以简写为 :value

v-on使用@替换,例如@click="handleClick"``v-on:click="handleClick"

计算、侦听属性与过滤器

计算属性

例如:购物车,平常开发数据与数据关联计算。在实例的 computed 选项中定义你的计算属性,直接使用 {{}} 向页面输出。计算属性是 惰性的,只有当依赖数据发生改变时,才会触发计算,否则,它的值是上一次触发计算的缓存值。

<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <p>我名字正着写:{{name}}</p>
      <!-- reverseName 计算属性  可以像绑定普通属性一样在模板中绑定计算属性-->
      <p>计算出我名字倒着写:{{reverseName}}</p>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          name: "实验楼",
        },
        computed: {
          // reverseName 是一个计算属性
          reverseName: function () {
            return this.name.split("").reverse().join("");
          },
        },
      });
    </script>
  </body>
</html>
setter 和 gette
<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <p>firstName:{{firstName}}</p>
      <p>lastName:{{lastName}}</p>
      <p>全名是:{{fullName}}</p>
      <button v-on:click="changeName">改姓</button>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          firstName: "王",
          lastName: "花花",
        },
        methods: {
          // changeName 定义一个方法改变 计算属性 fullName 的值
          changeName: function () {
            // 修改计算属性 fullName 等于李花花
            this.fullName = "李花花";
            // 上面一句等于触发了 fullName 属性的 setter
          },
        },
        computed: {
          fullName: {
            // getter
            get: function () {
              return this.firstName + this.lastName;
            },
            // setter  直接改变计算属性 fullName 的值就可以触发 setter this.fullName='XX'
            set: function (newName) {
              var name = newName;
              this.firstName = name.slice(0, 1); // 取新值的第一个字符
              this.lastName = name.slice(1); // 从新值的第二个字符开始取值
            },
          },
        },
      });
    </script>
  </body>
</html>
侦听属性

在开发我们需要监听数据的变化,Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动,侦听属性。在实例 watch 选项中确定监听项。

<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <p>{{msg}}</p>
      <!-- v-on:click 简写为 @click -->
      <button @click="handleClick('hello syl')">改变msg</button>
    </div>

    <script>
      var app = new Vue({
        el: "#app",
        data: {
          msg: "hello",
        },
        methods: {
          // 改变 msg 的值
          handleClick: function (val) {
            this.msg = val;
          },
        },
        // watch 监听属性
        watch: {
          // 监听新旧值  监听属性有两个参数,第一个新值,第二个旧值
          msg: function (newVal, oldVal) {
            alert("新值" + newVal + "----" + "旧值" + oldVal);
          },
        },
      });
    </script>
  </body>
</html>

计算属性和侦听属性两者在很多场景都是共同,都可以实现同样的需求。

过滤器 **

计算属性和侦听属性,在数据处理环节中很有作用,但是用它来处理数据过滤不是最优的

<p>{{msg2|getString}}</p>
<p v-bind:class="msg2|getString"></p>

在双花括号插值和 v-bind 表达式中把需要过滤的数据用 | 与过滤器分割 (data|filter)

<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- toUpperCase   getString  为自定义的过滤器-->
      <p>小写转换大写:过滤前:{{msg}} 过滤后: {{msg|toUpperCase}}</p>
      <p>去除数字:过滤前:{{msg2}} 过滤后: {{msg2|getString}}</p>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          msg: "hello",
          msg2: "1s2y3l",
        },
        // filters 过滤器选项
        filters: {
          // toUpperCase 定义一个字符串转大写的过滤器
          toUpperCase: function (val) {
            return val.toUpperCase();
          },
          // getString 定义一个获取去除数字的过滤器
          getString: function (val) {
            let newVal = "";
            val.split("").map(function (item) {
              if (9 >= item && item >= 0) {
                return;
              } else {
                return (newVal += item);
              }
            });
            return newVal;
          },
        },
      });
    </script>
  </body>
</html>
过滤器应用场景

我们请求到的数据并没有 ¥,在开发中直接去操作数据源是不推荐的,此时我们的过滤器就派上用场,定义一个拼接 ¥ 的过滤器,只是在视图层面实现了效果。

<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- joint 为自定义的过滤器-->
      <p>不要¥899,只要{{price|joint}}</p>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          // 后台价格数据
          price: 199,
        },
        // filters 过滤器选项
        filters: {
          // joint 定义 ¥ 拼接过滤器
          joint: function (price) {
            return "¥" + price;
          },
        },
      });
    </script>
  </body>
</html>
实验:简易购物车
<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      .price {
        font-size: 22px;
        color: brown;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- joint 为自定义的过滤器-->
      <p>单价<span class="price">{{price|joint}}</span></p>
      数量:<input type="number" v-model="goodsNum" />
      <p>总价:<span class="price">{{allPrice|joint}}</span></p>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          goodsNum: 0,
          price: 199,
        },
        computed: {
          allPrice: function () {
            return this.goodsNum * this.price;
          },
        },
        // filters 过滤器选项
        filters: {
          // joint 定义 ¥ 拼接过滤器
          joint: function (price) {
            return "¥" + price;
          },
        },
      });
    </script>
  </body>
</html>

class 与 style 绑定**

元素的 class 绑定

有三种,准确说不能说三种,是三个用法

对象语法

v-bind:class 一个对象,以动态地切换 class,语法表示 active 这个 class 存在与否将取决于数据属性 isActive 的 Boolean 值,大致语法 {className:Boolean}

<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
    <style>
      .active {
        color: pink;
        font-size: 22px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 数组语法绑定 class 当 isActive 为 true 时,active 就成 span 标签的 class -->
      <span v-bind:class="{'active':isActive}">syl</span>
      <!-- isActive 为 true 渲染结果 <span class="active">syl</span> -->
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          isActive: true,
        },
      });
    </script>
  </body>
</html>
多对象语法

你可以在对象中传入更多属性来动态切换多个 class,v-bind:class 指令也可让普通的 class 并存。

<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
    <style>
      .active {
        color: pink;
        font-size: 22px;
      }
      .red-bg {
        background: red;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 数组语法绑定 class 当 isActive 为 true 时,active 就成 span 标签的 class -->
      <span class="static" v-bind:class="{'active':isActive,'red-bg':isRed}"
        >syl</span
      >
      <!-- isActive 为 true 渲染后 <span class="static active red-bg">syl</span> -->
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          isActive: true,
          isRed: true,
        },
      });
    </script>
  </body>
</html>
数组语法
<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
    <style>
      .active {
        color: pink;
        font-size: 22px;
      }
      .red-bg {
        background: red;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 数组语法绑定 class -->
      <span v-bind:class="[activeClass,bgColorClass]">syl</span>
      <!-- 渲染后 <span class="active red-bg">syl</span> -->
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          activeClass: "active",
          bgColorClass: "red-bg",
        },
      });
    </script>
  </body>
</html>
元素 style 绑定

在前端开发中,内联样式经常被使用到,Vue 中内联样式绑定语法灵活。CSS 属性名可以用驼峰式 (camelCase) 或 短横线分隔 (kebab-case,记得用单引号括起来) 来命名。

方案一:
<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <p v-bind:style="{fontSize:size,backgroundColor:bgColor}">你好,实验楼</p>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          size: "26px",
          bgColor: "pink",
        },
      });
    </script>
  </body>
</html>
方案二:
<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <p v-bind:style="styleObject">你好,实验楼</p>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          styleObject: {
            fontSize: "26px",
            backgroundColor: "pink",
          },
        },
      });
    </script>
  </body>
</html>
方案三:
<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <p v-bind:style="[styleObject1,styleObject2]">你好,实验楼</p>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          //样式一
          styleObject1: {
            fontSize: "26px",
            backgroundColor: "pink",
          },
          //样式二
          styleObject2: {
            marginTop: "200px",
            textAlign: "center",
          },
        },
      });
    </script>
  </body>
</html>

条件与循环渲染

条件渲染
v-if

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回除了 false0""nullundefinedNaN 外的值的时候被渲染。

v-else

使用 v-else 指令来表示 v-if 的“else 块”,当 v-if 返回除了 false0""nullundefinedNaN 外的值的时候被渲染,否则,就渲染 v-else 块元素。注意:v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别

v-show

另一个用于根据条件展示元素的选项是 v-show 指令。不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。而 v-if 是从虚拟 DOM 的层面操作。

一般来说,v-if 会牵涉到虚拟 DOM diff 算法,有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

循环渲染

循环渲染经常会使用到,在开发中经常会遇到 DOM 结构一样的块代码,那么我们就可以使用循环渲染来一步到位。

v-for 将数组数据渲染成元素
<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      ul {
        width: 100%;
        height: 40px;
        list-style: none;
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: center;
        background: yellowgreen;
      }
      ul li {
        width: 20%;
        height: 100%;
        color: white;
        line-height: 40px;
        text-align: center;
        text-transform: uppercase; /*大写转换*/
      }
    </style>
  </head>
  <body>
    <div id="app">
      <ul class="nav">
        <li v-for="navItem in nav">{{navItem}}</li>
      </ul>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          nav: ["home", "shop", "contact", "about", "name", "mroe", "histroy"],
        },
      });
    </script>
  </body>
</html>
key 属性
<div v-for="item in items" :key="item.id">
  <!-- 内容 -->
</div>
数组更新检测

对数组的有些操作不会直接被页面响应,此时如果需求为响应式需要使用vue方法。

app.items[1] = "x"; // 不是响应性的
app.items.length = 2; // 不是响应性的

//以下为响应式
// Vue.set
Vue.set(vm.items, indexOfItem, newValue);
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue);
显示过滤

显示一个数组的过滤或排序副本,而不实际改变或重置原始数据。在这种情况下,可以创建返回过滤或排序数组的计算属性。

方案一:
<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <span v-for="number in oddNumber">{{number}}</span>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          numberArray: [1, 2, 3, 4, 5, 6, 7, 8],
        },
        computed: {
          // 计算 numberArray 中为奇数的 oddNumber 奇数数组
          oddNumber: function () {
            return this.numberArray.filter(function (number) {
              return number % 2 === 1;
            });
          },
        },
      });
    </script>
  </body>
</html>
方案二:
<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- v-for 内直接调用方法 -->
      <span v-for="number in getOddNumber()">{{number}}</span>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          numberArray: [1, 2, 3, 4, 5, 6, 7, 8],
        },
        methods: {
          // 定一个一个获取数组内奇数的方法 filter 数组对象的过滤方法
          getOddNumber: function () {
            return this.numberArray.filter(function (number) {
              return number % 2 === 1;
            });
          },
        },
      });
    </script>
  </body>
</html>
v-for 循环一段值
<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- :style 绑定样式 -->
      <span v-for="number in 10" :style="styleObject">{{number}}</span>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          // 指定分页样式
          styleObject: {
            fontSize: "14px",
            color: "#fff",
            background: "green",
            padding: "5px 10px",
            border: "1px solid #fff",
          },
        },
      });
    </script>
  </body>
</html>
综合实验
<!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>syl-vue-test</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      a {
        text-decoration: none;
        color: #fff;
      }
      ul {
        list-style: none;
      }
      nav,
      ul {
        width: 100%;
        display: flex; /* 开启弹性盒模型 布局方式*/
        flex-direction: row;
        justify-content: center;
        background: yellowgreen;
      }
      nav > ul > li {
        width: 20%;
        height: 100%;
        text-align: center;
        line-height: 50px;
      }
      nav > ul > li:hover {
        box-shadow: 1px 0px 10px #fff;
      }
      nav > ul > li > ul {
        display: flex;
        flex-direction: column;
      }
      nav > ul > li > ul > li {
        box-shadow: 1px 0px 10px #fff;
      }
      nav > ul > li > a {
        text-transform: uppercase;
      }
    </style>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <nav>
        <ul>
          <!-- 循环渲染一级菜单 -->
          <!-- 鼠标移入触发 currentIndex(index) 函数,更正 current 是当前菜单的 index,鼠标移出重置 current 为空,事件回调方法在 methods 中实现-->
          <li
            v-for="(nav,index) in navbar"
            :key="index"
            @mouseover="currentIndex(index)"
            @mouseout="changeIndex"
          >
            <!-- nav.name 一级菜单名字 -->
            <a href="javascript:;">{{nav.name}}</a>
            <!-- 如果 nav.child 存在,说明有子菜单,再次循环渲染子菜单 -->
            <!-- 子菜单 v-show  如果当前菜单的 index 等于 鼠标移入那个菜单的下标我们就展示出子菜单-->
            <ul v-if="nav.child" v-show="current===index">
              <li v-for="item in nav.child">
                <a href="javascript:;">{{item}}</a>
              </li>
            </ul>
          </li>
        </ul>
      </nav>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          // navbar 模拟后台获取到的菜单列表
          navbar: [
            {
              name: "home",
              child: ["homeItem", "homeItem"],
            },
            {
              name: "contact",
              child: ["contactItem", "contactItem"],
            },
            {
              name: "about",
            },
          ],
          // current 当前鼠标在那个菜单上,初始时没有值
          current: null,
        },
        methods: {
          // 更正 当前鼠标移入的是哪个菜单的 index
          currentIndex: function (index) {
            this.current = index;
          },
          // 鼠标移出 重置 current 值
          changeIndex: function () {
            this.current = null;
          },
        },
      });
    </script>
  </body>
</html>

事件处理

事件处理方法
<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
    <style>
      button {
        width: 150px;
        height: 40px;
        border-radius: 10px;
        background: green;
        outline: none;
        color: #fff;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 绑定点击监听 -->
      <button v-on:click="say">点击</button>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          counter: 0,
        },
        methods: {
          // 声明事件点击监听 say 方法
          say: function (event) {
            // 监听事件回调处理 event.type 触发事件类型 说明:`${}` 为 es6 模板字符串,拼接字符串的
            alert(`小楼提醒:你触发了${event.type}事件`);
          },
        },
      });
    </script>
  </body>
</html>
修饰符

在事件处理程序中调用 event.preventDefault()event.stopPropagation() 是非常常见的需求,阻止事件冒泡或捕获或者事件默认行为。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。

  • .stop:阻止单击事件继续传播。

.stop 修饰符的应用。

未添加 .stop 修饰符,事件会触发冒泡行为,点击子元素也会触发父元素的相同事件。

<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
    <style>
      /* 居中 */
      .super,
      .child {
        position: absolute;
        top: 0;
        left: 0;
        bottom: 0;
        right: 0;
        margin: auto;
      }
      .super {
        width: 300px;
        height: 300px;
        background: pink;
      }
      .super .child {
        width: 100px;
        height: 100px;
        background: green;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <div class="super" v-on:click="handleClick('super')">
        父
        <div class="child" v-on:click="handleClick('child')">子</div>
      </div>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {},
        methods: {
          // 声明事件点击监听 handleClick
          handleClick: function (name) {
            alert(`我是${name}`);
          },
        },
      });
    </script>
  </body>
</html>

添加后
<div id="app">
  <div class="super" v-on:click.stop="handleClick('super')">
    父
    <div class="child" v-on:click.stop="handleClick('child')">子</div>
  </div>
</div>
  • .prevent:阻止事件默认行为。
  • .capture:添加事件监听器时使用事件捕获模式。
  • .self:只当在 event.target 是当前元素自身时触发处理函数。
  • .once:点击事件将只会触发一次。
  • .passive:滚动事件的默认行为 (即滚动行为) 将会立即触发。
按键修饰符
  • .enter
  • `.tab``
  • ``.delete`
  • .esc
  • ``.space`
  • .up.down.left.right`
<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <input
        type="text"
        v-on:keyup.enter="alert('你按了enter,确定输入完毕?')"
      />
    </div>
    <script>
      var app = new Vue({
        el: "#app",
      });
    </script>
  </body>
</html>
按键键码
Enter13
Shift16
Alt18
Spacebar32
Page Up33
Page Down34
系统修饰符
  • .ctrl
  • .alt
  • .shift
  • .meta
<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 同时鼠标左击和按 ctrl 弹出提示 -->
      <div @click.ctrl="alert('你同时按了鼠标点击和ctrl')">Do something</div>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
      });
    </script>
  </body>
</html>
精确按钮修饰符

.exact 精确按键修饰符,允许你控制由精确的系统修饰符组合触发的事件。

<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
      <button
        @click.ctrl="alert('你不单单只按了鼠标左键和 Ctrl键,同时按其他键我也可以触发')"
      >
        A
      </button>

      <!-- 有且只有 ctrl 键 + 鼠标左键 被按下的时候才触发 -->
      <button @click.ctrl.exact="alert('你只按ctrl键+鼠标左键,才能触发我')">
        A
      </button>

      <!-- 没有任何系统修饰符被按下的时候才触发 -->
      <button @click.exact="alert('没有按任何系统修饰符')">A</button>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
      });
    </script>
  </body>
</html>
鼠标按钮修饰符
  • .left
  • .right
  • .middle
<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
  </head>
  <body>
    <div id="app">
      <button @click.left="alert('你按了鼠标左击键')">按钮</button>
      <button @click.middle="alert('你按了鼠标滚轮')">按钮</button>
      <button @click.right="alert('你按了鼠标右击键')">按钮</button>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
      });
    </script>
  </body>
</html>
综合小练习

小游戏(500px),一直鼠标左键,将小球向右移 500px,拼手速的哦:

<!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>syl-vue-test</title>
    <!-- 引入 vue.js -->
    <script src="vue.min.js"></script>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      #app {
        position: relative;
        width: 500px;
        height: 100px;
        background: black;
      }
      .box {
        position: absolute;
        left: 0;
        top: 0;
        width: 100px;
        height: 100px;
        border-radius: 50%;
        background: red;
        transition: all 0.3s ease-in;
      }
    </style>
  </head>
  <body>
    <div id="app" @click.left="changePosition">
      <div class="box" :style="{left:left+'px'}"></div>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          left: 0,
        },
        methods: {
          changePosition: function () {
            this.left += 25;
            if (this.left >= 500) {
              alert("走了500px了");
              this.left = 0;
            }
          },
        },
      });
    </script>
  </body>
</html>

表单处理

复选框用发:当选中值位yes未选中值位false。

在日常开发中,复选框的值很多情况是特定的值,那么我们可以这样做,在标签中声明 true-value="yes"false-value="no" 这两个属性,当选中时就是 true-value 属性指定的值,当未选中时就是 false-value 属性值。

<!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>vue</title>
    <script src="vue.min.js"></script>
  </head>

  <body>
    <div id="app">
      <input
        type="checkbox"
        v-model="toggle"
        true-value="yes"
        false-value="no"
      />
      <p>toggle:{{toggle}}</p>
    </div>
    <script>
      // 通过 true-value="yes" false-value="no" 属性控制,选中时 toggle 值为 yes,未选中时为 no
      var vue = new Vue({
        el: "#app",
        data() {
          return {
            toggle: "",
          };
        },
      });
    </script>
  </body>
</html>

如果你只想要选中有值,你可以这样做,true-value 属性指定值,false-value 属性设为空值。
<input type="checkbox" v-model="toggle" true-value="name" false-value="" />
修饰符
.lazy

开始介绍表单处理时,我们说了几点注意,不同的元素,使用的值不同,抛出的事件也不同。可能开发中,我们不需要数据实时更新,那么,我们怎么将 input 事件与 change 事件替换,可以使用 .lazy 修饰符,可以将抛出事件由 input 改为 change,使表单元素惰性更新,不实时更新。

<!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>vue</title>
    <script src="vue.min.js"></script>
  </head>

  <body>
    <div id="app">
      <!--使用 .lazy 修饰符将文本框 抛出的事件改为 change 事件,不再实时更新,只有文本框失去焦点才更新数据 惰性更新 -->
      <input v-model.lazy="msg" />
      <p>{{msg}}</p>
    </div>
    <script>
      var vue = new Vue({
        el: "#app",
        data: {
          msg: "hello",
        },
      });
    </script>
  </body>
</html>
.number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值返回字符串(默认),需要自己进行类型转换。如果这个值无法被 parseFloat() 解析,则会返回原始的值。 给 v-model 添加 number 修饰符,用户即使输入的是非数值类型,也会进行转换,无法转换时,会返回原始的。

<!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>vue</title>
    <script src="vue.min.js"></script>
  </head>

  <body>
    <div id="app">
      <p>没有使用 .number 修饰符</p>
      <input v-model="number1" type="number" />
      <!-- 使用 typeof 对值类型检测 -->
      <p>{{typeof(number1)}}</p>
      <p>使用 .number 修饰符</p>
      <input v-model.number="number2" type="number" />
      <!-- 使用 typeof 对值类型检测 -->
      <p>{{typeof(number2)}}</p>
    </div>
    <script>
      var vue = new Vue({
        el: "#app",
        data: {
          number1: "",
          number2: "",
        },
      });
    </script>
  </body>
</html>
.trim

表单元素值首尾空格,自动过滤。

<!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>vue</title>
    <script src="vue.min.js"></script>
  </head>

  <body>
    <div id="app">
      <input v-model.trim="msg" type="text" />
      <p>首尾空格被过滤了:{{msg}}</p>
    </div>
    <script>
      var vue = new Vue({
        el: "#app",
        data: {
          msg: "",
        },
      });
    </script>
  </body>
</html>
综合小练习
<!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>vue</title>
    <script src="vue.min.js"></script>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      html,
      body {
        width: 100%;
        height: 100%;
        overflow: hidden;
      }
      #app {
        position: absolute;
        top: 0;
        left: 0;
        bottom: 0;
        right: 0;
        margin: auto;
        width: 400px;
        height: 400px;
      }
    </style>
  </head>

  <body>
    <div id="app">
      <form class="app-form">
        <span>name:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
        <input type="text" v-model="username" />
        <br />
        <span>password:</span><input type="password" v-model="password" />
        <br />
        <span
          >sex:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span
        >
        <input type="radio" id="man" value="man" v-model="sex" />
        <label for="man">man</label>
        <input type="radio" id="woman" value="woman" v-model="sex" />
        <label for="woman">women</label>
        <br />
        <span>hobby:</span>
        <input type="checkbox" id="game" value="game" v-model="hobby" />
        <label for="game">game</label>
        <input
          type="checkbox"
          id="basketball"
          value="basketball"
          v-model="hobby"
        />
        <label for="basketball">basketball</label>
        <input type="checkbox" id="study" value="study" v-model="hobby" />
        <label for="study">study</label>
        <br />
        <br />
        <p>名字:{{username}}</p>
        <p>密码:{{password}}</p>
        <p>性别:{{sex}}</p>
        <p>爱好:{{hobby}}</p>
      </form>
    </div>
    <script>
      var vue = new Vue({
        el: "#app",
        data() {
          return {
            username: "",
            password: "",
            sex: "man", // 性别单选默认勾选男
            hobby: [],
          };
        },
      });
    </script>
  </body>
</html>

组件

组件注册
全局组件
<!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>vue</title>
    <script src="vue.min.js"></script>
  </head>

  <body>
    <div id="app">
      <syl></syl>
      <syl></syl>
      <syl></syl>
    </div>
    <script>
      // Vue.component(组件名字,template:{元素标签})
      Vue.component("syl", {
        template: "<h1>实验楼全局组件</h1>",
      });
      var app = new Vue({
        el: "#app",
      });
    </script>
  </body>
</html>
局部组件
<!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>vue</title>
    <script src="vue.min.js"></script>
  </head>

  <body>
    <div id="header">
      <syl-header></syl-header>
    </div>
    <div id="mid">
      <syl-mid></syl-mid>
    </div>
    <script>
      // 头部组件
      var childComponent = {
        template: "<h2>我是实验楼局部组件header,只有我们父级才能调用</h2>",
      };
      // 中间部分组件
      var childComponent2 = {
        template: "<h2>我是实验楼局部组件mid,只有我们父级才能调用</h2>",
      };
      // header vm
      var header = new Vue({
        el: "#header",
        // 子组件必须声明后使用,不然不能起效
        components: {
          "syl-header": childComponent,
        },
      });
      var mid = new Vue({
        el: "#mid",
        // 子组件必须声明后使用,不然不能起效
        components: {
          "syl-mid": childComponent2,
        },
      });
    </script>
  </body>
</html>
组件复用

复用组件内的 data 必须是一个函数,如果是一个对象(引用类型),组件与组件间会相互影响,组件数据不能独立管理。

<!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>vue</title>
    <script src="vue.min.js"></script>
  </head>

  <body>
    <div id="app">
      <button-counter></button-counter>
      <button-counter></button-counter>
      <button-counter></button-counter>
    </div>
    <script>
      // 注册一个全局可复用组件
      Vue.component("button-counter", {
        // data 必须是一个函数不然会影响其他组件
        data() {
          return {
            counter: 0,
          };
        },
        template: '<button @click="counter++">{{counter}}</button>',
      });
      var app = new Vue({
        el: "#app",
      });
    </script>
  </body>
</html>
组件间通信
父子组件之 props

props 是一个单向的数据流,只允许父组件向子组件传值,值类型可以是一个数值、字符、布尔值、数值、对象,子组件需要显式地用 props 选项声明 “prop”。

注意:HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 需要使用其等价的 kebab-case (短横线分隔命名) 命名,

通常你希望每个 prop 都有指定的值类型。这时,你可以以对象形式列出 prop,这些属性的名称和值分别是 prop 各自的名称和类型:

<!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>vue</title>
    <script src="vue.min.js"></script>
  </head>

  <body>
    <div id="app">
      <title-component post-title="syl1"></title-component>
      <title-component post-title="syl2"></title-component>
      <title-component post-title="syl3"></title-component>
    </div>
    <script>
      // 注册一个 title 组件,通过传入不同的 title 值,渲染不同的东西
      // 组件上 传递的 props 属性名为 kebab-case(短横线分隔命名)的要转换为驼峰命名
      Vue.component("title-component", {
        props: ["postTitle"], // post-title 转换为驼峰命名
        template: "<p>{{postTitle}}</p>",
      });
      var app = new Vue({
        el: "#app",
      });
    </script>
  </body>
</html>
子父组件通信之 emit

上面提到 props 实现父向子组件传递数据是单向流的,那么,如何实现子组件向父组件通信呢?这里要使用自定义事件 emit 方法,通过自定义事件来由下到上的数据流动。

<!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>vue</title>
    <script src="vue.min.js"></script>
  </head>

  <body>
    <div id="app">
      <child-component v-on:send-msg="getMsg"></child-component>
    </div>
    <script>
      // 定义一个子组件,template 绑定 click 事件
      // 当 click 事件触发就使用 emit 自定义一个事件 send-msg,传入参数 “我是子组件请求与你通信”
      // $emit('send-msg','我是子组件请求与你通信')
      // 子组件标签上绑定自定义事件 send-msg,并绑定上父级的方法 getMsg,即可完成了子父组件通信
      // <child-component v-on:send-msg="getMsg"></child-component>
      Vue.component("child-component", {
        template: `
                <button v-on:click="$emit('send-msg','我是子组件请求与你通信')">
                Click me
                </button>
                `,
      });
      var app = new Vue({
        el: "#app",
        methods: {
          getMsg: function (msg) {
            // 弹出子组件传递的信息
            alert(msg);
          },
        },
      });
    </script>
  </body>
</html>

子组件向父组件数据传递套路:

第一步:子组件绑定事件。

第二步:子组件绑定事件触发,使用 $emit 创建自定义事件并传入需要传值给父组件的数据。

第三步:在子组件标签上 用 v-on 绑定自定义事件,在父组件中声明自定义事件处理的方法。

第四步:父组件方法,接受自定义事件传的参数,就完成了整个由下到上的数据流。

动态组件

上面例子我们传值都是直接传的固定值,其实动态传值我们也支持,生成动态组件,使用 v-bind 动态绑定 props 值。

<!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>vue</title>
    <script src="vue.min.js"></script>
  </head>

  <body>
    <div id="app">
      <!-- 使用 v-bind 简写模式 动态绑定 props 值 -->
      <child-component
        :name="name"
        :age="age"
        :height="height"
      ></child-component>
      <child-component
        :name="name+'2'"
        :age="age+1"
        :height="height"
      ></child-component>
    </div>
    <script>
      // 定义一个子组件
      Vue.component("child-component", {
        // 使用属性类型检测
        props: {
          name: String,
          age: Number,
          height: String,
        },
        template: `
            <ul>
                <li>{{name}}</li>
                <li>{{age}}</li>
                <li>{{height}}</li>
            </ul>
            `,
      });
      var app = new Vue({
        el: "#app",
        data() {
          return {
            name: "syl",
            age: 20,
            height: "180cm",
          };
        },
      });
    </script>
  </body>
</html>
生命周期函数
<!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>vue</title>
    <script src="vue.min.js"></script>
  </head>

  <body>
    <div id="app">
      <button @click="handleClick">{{name}}</button>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data() {
          return {
            name: "syl",
          };
        },
        methods: {
          handleClick: function () {
            this.name = "syl syl";
          },
        },
        beforeCreate() {
          alert(
            "在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用"
          );
        },
        created() {
            "created 钩子函数内我们可以进行异步数据请求。"
          alert(
            "在实例创建完成后被立即调用,挂载阶段还没开始,$el 属性目前不可见"
          );
        },
        beforeMount() {
          alert("在挂载开始之前被调用:相关的 render 函数首次被调用");
        },
        mounted() {
            "mounted 我们可以直接操作元素 DOM 了,但是并不推荐这样做,不利于性能提升“
          alert("el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子");
        },
        beforeUpdate() {
          alert("数据更新时调用");
        },
        updated() {
          alert("组件 DOM 已经更新");
        },
        beforeDestroy() {},
        destroyed() {},
      });
    </script>
  </body>
</html>

Axios 的使用

  1. 可以从浏览器中创建 XMLHttpRequest。
  2. 能从 Node.js 创建 HTTP 请求。
  3. 支持 Promise API。
  4. 能够拦截请求和响应。
  5. 可以转换请求和响应数据。
  6. 也可取消请求。
  7. 可以自动转换 JSON 数据。
  8. 在客户端支持防止 CSRF/XSRF 攻击。
Axios简介

其中需要注意的是,getpost 请求中向后端传递参数的配置项名字不同:get 请求的需要使用 paramspost 请求用于发送数据的为 data

axios.get('url',{
  params:{
    id:'接口配置参数(相当于url?id=xxxx)',
  },
}).then(function(res){
  console.log(res); // 处理成功的函数 相当于 success
}).catch(function(error){
  console.log(error) // 错误处理 相当于 error
})

axios
  .post(
    "url",
    { data: {} },
    {
      headers: "xxxx", // 头部配置
    }
  )
  .then(function (res) {
    console.log(res); // 处理成功的函数 相当于 success
  })
  .catch(function (error) {
    console.log(error); // 错误处理 相当于 error
  });
Axios的使用
<!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>syl-vue-test</title>
    <script src="vue.min.js"></script>
    <script src="axios.min.js"></script>
  </head>
  <body>
    <div id="app">
      <ul>
        <li v-for="item in dataList">{{item}}</li>
      </ul>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          dataList: [],
        },
      });
        // 可以放在外边,但为了方便理解一般会放在自定义函数中
      axios.get("dataList.json").then((res) => {
        console.log(res.data);
        app.dataList = res.data;
      });
    </script>
  </body>
</html>

Vue-Router

关于前端路由的 hash 和 history 两种模式的实现,感兴趣的同学可以通过这篇《前端路由实现方法》文章去了解。

前端路由的优点如下:

  1. 页面刷新速度快:由于不需要向服务器发送请求,所以这个过程不会受到网络延迟的影响,实际上只是完成部分组件间的切换,因此页面的刷新速度会比较快,用户体验也更好些。
  2. 复用性强:由于使用前端路由的应用为单页面应用,所以代码中很多 CSS、JS 都可以共用,避免了过多的重复加载,大大提升了性能。
  3. 页面状态可记录:如果不使用前端路由,仅通过 Ajax 在页面进行局部切换的应用,由于页面 URL 始终保持不变,因此页面的状态是无法记录的,而前端路由很好的解决了这个问题。例如,使用了前端路由的应用中访问 https://www.lanqiao.cn/a 这个链接,再打开后会直接触发 /a 匹配的路由页面中的事件。

当然,前端路由也有一些缺点,比如使用浏览器的前进、后退键的时候,会重新发送请求,没有合理地利用缓存。

功能
  • 嵌套路由映射
  • 动态路由选择
  • 模块化、基于组件的路由配置
  • 路由参数、查询、通配符
  • 展示由 Vue 的过渡系统提供的过渡效果
  • 细致的导航控制
  • 自动激活 CSS 类的链接
  • HTML5 history 模式或 hash 模式
  • 可定制的滚动行为
  • URL 的正确编码
基本使用

我们通过一个单页面应用来看看 Vue-Router 的使用,其基本步骤如下所示:

  • 使用 router-link 组件来导航,其通过 to 属性来指定跳转链接(这相当于 HTML 中的 a 标签)。
  • 使用 router-view 组件定义路由出口,路由匹配到的组件将会渲染到此处。
  • 使用 const routes = [{ path, component }] 来定义路由(路径和组件名)。
  • 使用 const router = new VueRouter({}) 来创建路由实例,在其中传入上一步定义的路由配置 routes
  • 创建和挂载根实例,在 new Vue 中挂载上一步创建的路由实例 router
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="vue.min.js"></script>
    <script src="vue-router.js"></script>
  </head>

  <body>
    <div id="app">
      <h1>路由的使用</h1>
      <p>
        <!-- 使用 router-link 组件来导航 -->
        <router-link to="/home">首页</router-link>
        <router-link to="/hot">热门</router-link>
        <router-link to="/class">分类</router-link>
      </p>
      <!-- 路由出口 -->
      <!-- 路由匹配到的组件将渲染在这里 -->
      <router-view></router-view>
    </div>
    <script>
      const Home = { template: "<div>首页</div>" };
      const Hot = { template: "<div>热门</div>" };
      const Class = { template: "<div>分类</div>" };

      // 定义路由
      const routes = [
        { path: "/home", component: Home },
        { path: "/hot", component: Hot },
        { path: "/class", component: Class },
      ];

      // 创建 router 实例,然后传 routes 配置
      const router = new VueRouter({
        routes,
      });

      // 创建和挂载根实例
      const app = new Vue({
        router,
      }).$mount("#app");
    </script>
  </body>
</html>

Vuex 状态管理器

Vuex 的简介

Vuex 是一个专门为 Vue 应用程序开发的状态管理模式。通过它可以将 Vue 应用中所有组件的共享状态储存管理起来,并以一定的规则保证这些状态以一种可预测的方式发生变化。

在这里插入图片描述

Vuex 的核心概念

在 Vuex 中有五个核心概念,它们分别是 StateGettersMutationsActionsModules

State
import Vue from "vue";
import App from "./App.vue";
import Vuex from "vuex"; // 导入 Vuex

Vue.use(Vuex); // 使用 Vuex,让 Vuex 可以访问到 Vue
Vue.config.productionTip = false;

// 创建 Store 实例
const store = new Vuex.Store({
  state: {
    count: 0, // 计数器的初始值
  },
});

new Vue({
  store, // 注入 Store
  render: (h) => h(App),
}).$mount("#app");
Mutations
// 在 App.vue 文件中定义一个按钮,新增代码如下:
<!--绑定一个点击事件,用 increment 来执行 count++ 的逻辑-->
<button @click="$store.commit('increment')">++</button>

// 我们在 main.js 文件中增加 mutations,代码如下:
const store = new Vuex.Store({
  // 此处省略 ...
  mutations: {
    increment(state) {
      state.count++; // 执行 count++ 的操作
    },
  },
});
Actions

有时候我们需要向后台发出一些异步请求,我们不能直接在 mutations 里进行操作,这时就可以在 actions 中定义一些异步操作。

在页面上新增一个按钮,触发 count-- 的操作。在 App.vue 中新增以下代码:注意哦!!! Actions 是通过 store.dispatch 方法来触发 actions 更新 state 状态。

<button @click="$store.dispatch('decrement')">--</button>

main.js 文件中新增以下内容。

const store = new Vuex.Store({
  mutations: {
    decrement(state) {
      state.count--;
    },
  },
  actions: {
    decrement({ commit }) {
      setTimeout(() => {
        // 通过 commit 交给 mutations 去处理
        commit("decrement");
      }, 500);
    },
  },
});
actions 与 mutations 的区别。

actions 类似于 mutations,不同的是:

  • actions 中的更新函数最终仍是通过调用 mutations 中的函数来更新状态的,不能通过它直接变更状态。
  • mutations 不同,actions 中可以包含任意异步操作。
Getters

getters 可以帮助我们缓存数据。

// main.js
getters: {
    doubleCount(state) {
      return state.count * 2
    }
}
// app文件
{{$store.getters.doubleCount}}
Vuex规则

在实际开发中 Vuex 并不会限制我们的代码结构。但是,它规定了一些需要遵守的规则:

  1. 应用层级的状态应该集中到单个 store 对象中。
  2. 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
  3. 异步逻辑都应该封装到 action 里面。

只要你遵守以上规则,如何组织代码随你便。

不过随着业务的增多,我们可能会面临这样一个问题:由于 statemutationsgettersactions 存储的内容越来越多,会导致 store 文件及其庞大,开发和维护起来变得困难。没关系,学习过 ES6+ 的同学都知道模块化的概念,这里我们可以将它们作为单独的文件从 store 中分割出去。这样做对于大型应用开发来说在合适不过。😊

我们可以将 store 分割为如下所示的结构:

└── store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── state.js          # 根级别的 state
    ├── getters.js        # 根级别的 getters
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
Module
const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
└── store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── state.js          # 根级别的 state
    ├── getters.js        # 根级别的 getters
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    └── modules
        ├── cart.js       # 购物车模块
        └── products.js   # 产品模块
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值