【Web前端】Vue核心基础

文章目录

  • 1. Vue简介
  • 2. Vue官网使用指南
  • 3. 初识Vue
    • 3.1 搭建Vue开发环境
    • 3.2 HelloWorld案例
    • 3.3 el与data的两种写法
    • 3.4 MVVM模型
    • 3.5 模板语法
  • 4. 数据绑定
    • 4.1 v-bind单向数据绑定
    • 4.2 v-model双向数据绑定
  • 5. 事件处理
    • 5.1 v-on绑定事件
    • 5.2 事件修饰符
    • 5.3 键盘事件
  • 6. 计算属性
    • 6.1 姓名案例
    • 6.2 computed计算属性
  • 7. 监视属性
    • 7.1 天气案例
    • 7.2 watch监视属性
  • 8. 绑定样式
    • 8.1 绑定class样式
    • 8.2 绑定style样式
  • 9. 条件渲染
    • 9.1 v-show
    • 9.2 v-if、v-else与v-else-if
  • 10. 列表渲染
    • 10.1 v-for渲染列表数据
    • 10.2 key的作用与原理
    • 10.3 列表过滤
    • 10.4 列表排序
  • 11. 收集表单数据
  • 12. 内置指令
    • 12.1 v-text 解析普通文本
    • 12.2 v-html 解析html标签
    • 12.3 v-cloak 隐藏模板
    • 12.4 v-once 仅渲染一次
    • 12.5 v-pre 跳过元素编译
  • 13. 自定义指令
    • 13.1 函数式和对象式
    • 13.2 局部指令和全局指令
  • 14. Vue的生命周期
    • 14.1 生命周期概念
    • 14.2 生命周期演示
  • 15. Vue的组件化编程
    • 15.1 模块与组件
    • 15.2 非单文件组件
      • 15.2.1 组件的基本使用
      • 15.2.2 组件的注意事项
      • 15.2.3 组件的嵌套
      • 15.2.4 VueComponent
      • 15.2.5 一个重要的内置关系
    • 15.3 单文件组件
      • 15.3.1 单文件组件的组成
      • 15.3.2 单文件组件的示例

1. Vue简介

Vue的概念:Vue是一套用于构建用户界面的渐进式JavaScript框架。

渐进式:表示Vue可以自底向上逐层的应用,从简单应用逐渐递进到复杂应用。

Vue的特点:

  • 采用组件化模式,提高代码复用率,且让代码更好维护。
  • 声明式编码,让编码人员无需直接操作DOM,提高开发效率。
  • 使用虚拟DOM和Diff算法,尽量复用DOM节点。

学习Vue之前要掌握的js基础知识:

  • ES6语法规范
  • ES6模块化
  • 包管理器
  • 原型和原型链
  • 数组常用方法
  • axios
  • promise

2. Vue官网使用指南

Vue官网:

  • Vue3文档:https://cn.vuejs.org/
  • Vue2文档:https://v2.cn.vuejs.org/

以Vue2文档为例,对该文档顶部的导航栏做详细的介绍。

在这里插入图片描述

1.学习:其中最重要的是教程和API,贯穿vue学习的始终。

  • 教程:入门vue的教程
  • API:vue的属性、方法和指令等
  • 风格指南:官方推荐的vue代码风格指南
  • 示例:官方展示的一些vue案例
  • Cookbook:展示一些vue案例,教你如何优化vue代码
    在这里插入图片描述

2.生态系统:其中工具和插件很重要,用于搭建Vue工程。

在这里插入图片描述

3.资源列表:其中Awesome Vue、浏览和Vue相关的包,这两个包含了官方整理的一些优秀的vue周边库。
在这里插入图片描述

3. 初识Vue

3.1 搭建Vue开发环境

1.首先需要下载Vue

进入Vue官网的导航列表:学习 -> 教程 -> 安装,也可点击如下地址直接跳转:https://v2.cn.vuejs.org/v2/guide/installation.html

有开发和生产两种版本,这里我们使用开发版本,其中包含了完整的警告和调试模式,适用于学习阶段。

  • 开发环境版本:安装包名称为 vue.js,包含了有帮助的命令行警告,体积较大。
  • 生产环境版本:安装包名称为 vue.min.js,优化了尺寸和速度,体积较小。
    在这里插入图片描述

2.新建一个js文件夹,将下载好的vue.js放入js文件夹中,然后使用 <script>标签引入 vue.js,就可以使用Vue了

目录结构如下:

在这里插入图片描述

index.html内容如下:

<!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>
    <!-- 引入Vue -->
    <script src="../js/vue.js"></script>
  </head>
  <body></body>
</html>

打开页面后,再打开控制台,发现有两个提示:

在这里插入图片描述

3.消除开发环境的提示

添加vue开发者工具,消除第一个提示,步骤如下:打开Chrome扩展程序 -> 打开开发者模式 ->拖入vue_dev_tools.crx 文件到浏览器并松开,点击添加程序即可

在这里插入图片描述
script标签中写入以下vue配置,消除第二个提示:

Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

3.2 HelloWorld案例

<!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>
    <!-- 引入Vue -->
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 准备好一个容器 -->
    <div id="app">
      <h1>{{message}}</h1>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      // 创建Vue实例
      new Vue({
      	// 配置
        el: "#app", // el用于指定当前Vue实例为哪个容器服务
        data: { // data中用于存储数据,数据供el所指定的容器去使用
          message: "Hello World!"
        }
      });
    </script>
  </body>
</html>

代码解释:
1.el表示元素(element),值通常为css选择器,此处#app为id选择器,对应<div id="app">,此时当前vue实例挂载到了div容器上,为该容器服务。
2.data表示数据,数据供挂载的容器去使用,而容器中使用了模板语法 {{message}},引用了vue实例中的data数据。
3.vue实例中的message属性值Hello World!替换了{{message}},因此<h1>{{message}}</h1>最终解析成了<h1>Hello World!</h1>

运行结果如下:

在这里插入图片描述

注:
1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象
2.容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法
3.容器里的代码被称为Vue模板
4.Vue实例和容器是一一对应的
5.真实开发中只有一个Vue实例,并且会配合着组件一起使用
6.{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性
7.一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新

3.3 el与data的两种写法

1.eldata的第一种写法是对象式写法,就是上述HelloWorld案例中的写法,即通过配置对象属性和属性值。

// 创建Vue实例
new Vue({
  // 配置
  el: "#app", // 将Vue实例挂载到 id=app的容器上
  data: {  // data中用于存储数据,数据供挂载的容器去使用
    message: "Hello World!"
  }
});

2.eldata的第二种写法是函数式写法

// 创建Vue实例
const vm = new Vue({
  // data的函数式写法,组件中必须使用该写法
  data() {
    return {
      message: "Hello World!"
    };
  }
});

vm.$mount("#app"); // 等价于 el: "#app"

注意:vue实例中的函数不能使用箭头函数的写法,原本函数中的this指向的是vue实例对象,使用箭头函数后,函数中的this指向的就是window对象。

3.4 MVVM模型

Vue采用了MVVM架构模型,其对应关系如下:

  • M:模型(Model) ,对应 data 中的数据
  • V:视图(View) ,对应模板
  • VM:视图模型(ViewModel) , 对应 Vue 实例对象

在这里插入图片描述

在这里插入图片描述

3.5 模板语法

模板语法:Vue实例挂载的容器里中的代码被称为模板,模板语法也就是这个容器中代码的语法规范,模板语法分为两大类(插值语法和指令语法)。

插值语法:

  • 功能:用于解析标签体内容
  • 写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性

指令语法:

  • 功能:用于解析标签(包括标签属性、标签体内容、绑定事件等)
  • 写法:v-xxx,如v-bindv-modelv-on
<!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>
    <!-- 引入Vue -->
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 插值语法,{{js表达式}} -->
      <h3>{{message.toUpperCase()}}</h3>
      <!-- 指令语法,v-xxx -->
      <a v-bind:href="url">百度链接</a>
    </div>

    <script>
      new Vue({
        el: "#app",
        data: {
          message: "hello world",
          url: "https://www.baidu.com/"
        }
      });
    </script>
  </body>
</html>

运行结果:
在这里插入图片描述

4. 数据绑定

Vue中有两种数据绑定的方式:

  • 单向绑定(v-bind):数据只能从data流向页面
  • 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data

4.1 v-bind单向数据绑定

v-bind:用于单向数据绑定,该指令也可以简写为 :

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 普通写法 -->
      单向数据绑定:<input type="text" v-bind:value="name" /> <br />
      <!-- 简写 -->
      单向数据绑定:<input type="text" :value="name" />
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          name: "hello"
        }
      });
    </script>
  </body>
</html>

验证数据能从data流向页面:打开控制台,输入 vm.name="hello123"并回车,可以发现页面中的数据也显示成了hello123

在这里插入图片描述

验证页面的数据无法流向data:在文本框中输入hello1234,随后在控制台输入vm.name,得到的结果依然是hello123,说明vue实例中的data数据未发生改变

在这里插入图片描述

4.2 v-model双向数据绑定

v-model:用于双向数据绑定,双向绑定只能应用在表单类元素(输入类元素)上,如:input、select等

注:v-model:value 可以简写为 v-model,因为v-model默认收集的就是value值。

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 普通写法 -->
      双向数据绑定:<input type="text" v-model:value="name" /> <br />
      <!-- 简写 -->
      双向数据绑定:<input type="text" v-model="name" />
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          name: "hello"
        }
      });
    </script>
  </body>
</html>

验证数据能从data流向页面:打开控制台,输入 vm.name="hello123"并回车,可以发现页面中的数据也显示成了hello123

在这里插入图片描述

验证页面的数据也能流向data:在文本框中输入hello1234,随后在控制台输入vm.name,得到的结果是hello1234,说明vue实例中的data数据随页面数据的改变而改变

在这里插入图片描述

5. 事件处理

5.1 v-on绑定事件

事件的基本使用:

  • 使用v-on:xxx="yyy" 指令用于绑定事件,其中v-on:可以简写为@xxx是事件名,yyy是回调函数名(也可以是简单的语句,如isShow=!isShow
  • methods对象用于配置vm的方法,事件的回调函数需要配置在methods对象中
  • methods中配置的函数,this指向的是vm或组件实例对象,且配置的函数不能使用箭头函数,否则this指向的是window对象
  • @click="demo"@click="demo($event)" 效果一致,但后者可以传参

1.绑定事件,且回调函数不传参

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 普通写法 -->
      <button v-on:click="showInfo">按钮1</button>
      <!-- 简写 -->
      <button @click="showInfo">按钮2</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        // 配置方法
        methods: {
          // 事件回调函数
          showInfo() {
            alert("Hello World!");
          }
        }
      });
    </script>
  </body>
</html>

2.绑定事件,且给回调函数传参

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <button @click="showInfo">按钮1</button>
      <!-- $event为占位符,表示此时第一个参数为事件对象 -->
      <button @click="showInfo1($event,'Jack')">按钮2</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        // 配置方法
        methods: {
          // 不传参时,函数的第一个参数代表事件对象
          showInfo(e) {
            console.log(e.target); // 触发事件的对象
            console.log(e.type); // 事件的类型
            console.log(this); // this指向vm
          },
          // 传参时,根据$event占位符的位置确定哪个参数代表事件对象
          showInfo1(e, name) {
            console.log(e.target);
            console.log(e.type);
            console.log(`Hello ${name}`);
          }
        }
      });
    </script>
  </body>
</html>

运行结果:

在这里插入图片描述

5.2 事件修饰符

Vue中的事件修饰符:

  • prevent:阻止默认事件(常用)
  • stop:阻止事件冒泡(常用)
  • once:事件只触发一次(常用)
  • capture:使用事件的捕获模式
  • self:只有event.target是当前操作的元素时才触发事件
  • passive:事件的默认行为立即执行,无需等待事件回调执行完毕

注:修饰符可以连续写,如@click.prevent.stop="showInfo"

1.prevent:阻止事件的默认行为(常用)

同js事件对象中的e.preventDefault()方法,用于阻止事件的默认行为

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 阻止链接跳转 -->
      <a href="https://www.baidu.com/" @click.prevent="showInfo">百度链接</a>
      <!-- 阻止表单提交 -->
      <form action="https://www.baidu.com/">
        <input type="submit" value="提交" @click.prevent="showInfo" />
      </form>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        methods: {
          showInfo() {
            console.log("已阻止事件的默认行为");
          }
        }
      });
    </script>
  </body>
</html>

运行结果:点击百度链接和表单提交按钮,都不发生页面跳转,因为跳转的默认行为被阻止了

2.stop:阻止事件冒泡(常用)

同js事件对象中的e.stopPropagation()方法,用于阻止事件冒泡

<!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="../js/vue.js"></script>
    <style>
      .father {
        position: relative;
        width: 200px;
        height: 200px;
        background-color: #666;
        margin: 30px;
      }

      .son {
        position: absolute;
        width: 100px;
        height: 100px;
        background-color: orange;
        margin: auto;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
      }
    </style>
  </head>
  <body>
    <div id="app" class="father" @click="showInfo1">
      <!-- 阻止事件冒泡 -->
      <div class="son" @click.stop="showInfo2"></div>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        methods: {
          showInfo1() {
            alert("father");
          },
          showInfo2() {
            alert("son");
          }
        }
      });
    </script>
  </body>
</html>

运行结果:点击子元素 ,只弹出警示框“son”,没有后续弹出“father”
(释义:点击子元素son以后,阻止冒泡,因此没有冒泡到父元素,父元素的事件行为不执行)

在这里插入图片描述

3.once:事件只触发一次(常用)

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 此按钮只在第一次点击时,触发事件 -->
      <button @click.once="showInfo">点击按钮</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        methods: {
          showInfo() {
            alert("Hello World!");
          }
        }
      });
    </script>
  </body>
</html>

运行结果:第一次点击按钮时,弹出警示框,后续再点击无法弹出

4.capture:使用事件的捕获模式

注:js中addEventListener(type, listener, useCapture)useCapturefalse或者忽略时,事件处于冒泡阶段;为true时,事件处于捕获阶段

<!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="../js/vue.js"></script>
    <style>
      .father {
        position: relative;
        width: 200px;
        height: 200px;
        background-color: #666;
        margin: 30px;
      }

      .son {
        position: absolute;
        width: 100px;
        height: 100px;
        background-color: orange;
        margin: auto;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
      }
    </style>
  </head>
  <body>
    <!-- 绑定事件到父元素father,为捕获阶段 -->
    <div id="app" class="father" @click.capture="showInfo1">
      <div class="son" @click="showInfo2"></div>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        methods: {
          showInfo1() {
            alert("father");
          },
          showInfo2() {
            alert("son");
          }
        }
      });
    </script>
  </body>
</html>

运行结果:点击橘色的子元素div,先弹出警示框 “father”,后弹出警示框 “son”
(释义:捕获从上往下传递,点击子元素son以后,会先找到父元素并执行它的事件,再往下找到子元素后,执行子元素的事件)

在这里插入图片描述

5.self:只有event.target是当前操作的元素时才触发事件

注:通过这种方式,也能阻止事件冒泡

<!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="../js/vue.js"></script>
    <style>
      .father {
        position: relative;
        width: 200px;
        height: 200px;
        background-color: #666;
        margin: 30px;
      }

      .son {
        position: absolute;
        width: 100px;
        height: 100px;
        background-color: orange;
        margin: auto;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
      }
    </style>
  </head>
  <body>
    <!-- 只有当e.target是当前操作的元素时才触发事件,即只有点击该父元素才触发父元素的事件 -->
    <div id="app" class="father" @click.self="showInfo1">
      <!-- <div id="app" class="father" @click="showInfo1"> -->
      <div class="son" @click="showInfo2"></div>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        methods: {
          showInfo1() {
            alert("father");
          },
          showInfo2() {
            alert("son");
          }
        }
      });
    </script>
  </body>
</html>

运行结果:只有点击灰色的父元素div时,才弹出警示框 “father”

在这里插入图片描述

5.3 键盘事件

Vue中常用的按键别名:

  • 回车 => enter
  • 删除 => delete (捕获“删除”和“退格”键)
  • 退出 => esc
  • 空格 => space
  • 换行 => tab (特殊,必须配合keydown去使用)
  • 上 => up
  • 下 => down
  • 左 => left
  • 右 => right

注:
1.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名,如caps-lock)
2.可以使用keyCode去指定具体的按键(不推荐),如e.keyCode===13代表回车键
3.Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名,如Vue.config.keyCodes.huiche=13,此时别名huiche代表回车键
4.系统修饰键(用法特殊):ctrl、alt、shift、meta,

  • 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
  • 配合keydown使用:正常触发事件。
<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 按下回车键松开时触发事件 -->
      <input type="text" placeholder="输入完成后请按回车" @keyup.enter="showInfo" />
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        methods: {
          showInfo(e) {
            alert(e.target.value);
          }
        }
      });
    </script>
  </body>
</html>

运行结果:输入“123456”,再按下回车后松开

在这里插入图片描述

6. 计算属性

6.1 姓名案例

要求:一个input框输入“姓”,另一个input框输入“名”,生成“姓-名”

解题方法有三种:

  • 使用插值语法{{}}实现
  • 使用methods方法实现
  • 使用computed计算属性实现(推荐)

1.使用插值语法{{}}实现姓名案例,通过直接在模板里进行字符串拼接

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      姓:<input type="text" v-model="firstName" /> <br />
      名:<input type="text" v-model="lastName" /> <br />
      全名:<span>{{firstName}}-{{lastName}}</span>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          firstName: "张",
          lastName: "三"
        }
      });
    </script>
  </body>
</html>

运行结果如下:在两个输入框中分别输入“姓”和“名”,得到全名为“姓-名”

在这里插入图片描述

2.使用methods方法实现姓名案例,通过调用方法,将方法的返回值渲染到模板中

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      姓:<input type="text" v-model="firstName" /> <br />
      名:<input type="text" v-model="lastName" /> <br />
      全名:<span>{{fullName()}}</span>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          firstName: "张",
          lastName: "三"
        },
        methods: {
          fullName() {
            return this.firstName + "-" + this.lastName;
          }
        }
      });
    </script>
  </body>
</html>

3.使用computed计算属性实现,通过计算原有的属性,得到新的属性,并将其渲染到模板中

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      姓:<input type="text" v-model="firstName" /> <br />
      名:<input type="text" v-model="lastName" /> <br />
      全名:<span>{{fullName}}</span>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          firstName: "张",
          lastName: "三"
        },
        computed: {
          fullName: {
            get() {
              return this.firstName + "-" + this.lastName;
            }
          }
        }
      });
    </script>
  </body>
</html>

6.2 computed计算属性

计算属性:

  • 定义:要用的属性不存在,要通过已有属性计算得来,计算属性最终会出现在vm上,直接读取使用即可
  • 原理:底层借助了Objcet.defineproperty方法提供的gettersetter
  • 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便
  • 计算属性中的getset函数:
    • 使用计算属性时,初次读取会执行一次get函数,当依赖的数据发生改变时也会再次调用get函数
    • 修改计算属性时,会调用set函数
  • 当计算属性中,只有get函数时,可以简写计算属性

1.计算属性中的getset函数的使用

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      姓:<input type="text" v-model="firstName" /> <br />
      名:<input type="text" v-model="lastName" /> <br />
      全名:<span>{{fullName}}</span>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          firstName: "张",
          lastName: "三"
        },
        computed: {
          fullName: {
            // 使用fullName时,返回全名
            get() {
              console.log("get被调用了");
              return this.firstName + "-" + this.lastName;
            },
            // 修改fullName时,修改firstName和lastName属性
            set(value) {
              console.log("set被调用了");
              arr = value.split("-");
              this.firstName = arr[0];
              this.lastName = arr[1];
            }
          }
        }
      });
    </script>
  </body>
</html>

运行结果:
由于模板中使用了fullName属性,先调用了一次get,然后在输入修改名后,fullName依赖的lastName属性发生了改变,再次调用了get;

在这里插入图片描述
控制台中输入vm.fullName='张-三丰',fullName属性被修改,调用了set,又因为set函数修改了fullName所依赖的firstName 和lastName,所以再次触发调用了get

在这里插入图片描述

2.简写计算属性

当计算属性中,只有get函数时,代码可以进行如下简写:

// 普通写法
computed: {
  fullName: {
    get() {
      return this.firstName + "-" + this.lastName;
    }
  }
}
// 简写
computed: {
  fullName() {
    return this.firstName + "-" + this.lastName;
  }
}

7. 监视属性

7.1 天气案例

要求:点击按钮时,天气在炎热和凉爽之间切换,初始值默认为炎热。

实现如下:

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <h1>今天天气很{{info}}</h1>
      <button @click="changeWeather">点击切换天气</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          isHot: true // 默认天气为炎热
        },
        computed: {
          info() {
            return this.isHot ? "炎热" : "凉爽";
          }
        },
        methods: {
          changeWeather() {
            this.isHot = !this.isHot; // 每次调用方法时,isHot取反
          }
        }
      });
    </script>
  </body>
</html>

注:由于methods中的changeWeather方法逻辑简单,因此也可以将methods去掉,并把@click="changeWeather"改为@click="isHot = !isHot",这样就简化了代码。

运行结果:点击按钮,天气在炎热和凉爽之间切换

在这里插入图片描述

7.2 watch监视属性

监视属性watch

  • 监视的属性必须存在,才能进行监视
  • 当被监视的属性变化时, handler回调函数自动调用
  • 监视属性有两种写法,一是通过watch属性配置,二是通过vm.$watch()方法
  • 深度监视属性:
    • 当被监视的属性有多层结构时,默认不监测对象内部值的改变(一层)
    • 通过配置deep:true可以监测对象内部值改变(多层)
  • 当监视属性中,只有handler函数时,可以监视计算属性

1.监视属性的基本用法(通过watch属性配置)

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <h1>今天天气很{{info}}</h1>
      <button @click="isHot = !isHot">点击切换天气</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          isHot: true // 默认天气为炎热
        },
        computed: {
          info() {
            return this.isHot ? "炎热" : "凉爽";
          }
        },
        methods: {
          changeWeather() {
            this.isHot = !this.isHot; // 每次调用方法时,isHot取反
          }
        },
        watch: {
          // 当isHot属性发生变化时,会调用handler函数
          isHot: {
            immediate: true, //初始化时会调用一下handler函数
            handler(newValue, oldValue) {
              console.log(`isHot被修改,原来的值是${oldValue},现在是${newValue}`);
            }
          }
        }
      });
    </script>
  </body>
</html>

点击按钮,运行结果如下:

在这里插入图片描述

2.可以通过vm.$watch()方法,进行属性监视

// watch属性配置的写法
watch: {
  isHot: {
    immediate: true, 
    handler(newValue, oldValue) {
      console.log(`isHot被修改,原来的值是${oldValue},现在是${newValue}`);
    }
  }
}
// vm.$watch()的写法
vm.$watch("isHot", {
  immediate: true, 
  handler(newValue, oldValue) {
    console.log(`isHot被修改,原来的值是${oldValue},现在是${newValue}`);
  }
});

3.deep:true 用于深度监视属性

  • 深度监视单个属性:监视的属性写为"numbers.a",表示监视numbers属性中的a属性
  • 深度监视所有属性: 监视的属性写为numbers,再配置deep:true,表示监视numbers属性中的所有属性
<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <h3>a的值是:{{numbers.a}}</h3>
      <button @click="numbers.a++">点我让a+1</button>
      <h3>b的值是:{{numbers.b}}</h3>
      <button @click="numbers.b++">点我让b+1</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          // numbers属性有多层结构
          numbers: {
            a: 1,
            b: 1,
            c: {
              d: {
                e: 100
              }
            }
          }
        },
        watch: {
          //监视多级结构中某个属性的变化
          "numbers.a": {
            handler() {
              console.log("a发生了改变");
            }
          },
          //监视多级结构中所有属性的变化
          numbers: {
            deep: true,
            handler() {
              console.log("numbers发生了改变");
            }
          }
        }
      });
    </script>
  </body>
</html>

分别点击上下两个按钮,使a、b值增加,运行结果如下:

在这里插入图片描述

4.简写监视属性

当监视属性中,只有handler函数时,代码可以进行如下简写:

// 普通写法
watch: {
  isHot: {
    handler(newValue, oldValue) {
      console.log(`isHot被修改,原来的值是${oldValue},现在是${newValue}`);
    }
  }
}
// watch属性配置简写
watch: {
  isHot(newValue, oldValue) {
    console.log(`isHot被修改,原来的值是${oldValue},现在是${newValue}`);
  }
}

// vm.$watch()方法简写
vm.$watch("isHot", function (newValue, oldValue) {
  console.log(`isHot被修改,原来的值是${oldValue},现在是${newValue}`);
});

5.计算属性与监视属性的区别

computedwatch之间的区别:

  • computed能完成的功能,watch都可以完成
  • watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作
  • computed的写法更加的简洁,因此能用 computed完成的功能,不要用watch完成

注:Vue中有关函数的两个重要的小原则:
1.所有被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象
2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。

8. 绑定样式

8.1 绑定class样式

绑定class样式有三种方式,分别适用于不同的场景:写法为:class="xxx"

  • 字符串写法:适用于样式的类名不确定,需要动态指定
  • 对象写法:适用于要绑定的样式个数确定、名字也确定,但要动态决定用不用
  • 数组写法:适用于要绑定的样式个数不确定、名字也不确定

1.绑定class样式,字符串写法:适用于样式的类名不确定,需要动态指定

<!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="../js/vue.js"></script>
    <style>
      /* basic为固定样式,happy和sad为需要动态绑定的样式 */
      .basic {
        width: 200px;
        height: 100px;
        border: 1px solid black;
      }

      /* 快乐情绪 */
      .happy {
        border: 4px solid red;
        background-color: orangered;
        background: linear-gradient(30deg, yellow, pink, orange, yellow);
      }

      /* 悲伤情绪 */
      .sad {
        border: 4px dashed rgb(2, 197, 2);
        background-color: gray;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 字符串写法,适用于:样式的类名不确定,需要动态指定 -->
      <div class="basic" :class="mood"></div>
      <br />
      <button @click="changeMood">点击切换情绪</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          mood: "happy" // 属性值为字符串
        },
        methods: {
          // 改变情绪
          changeMood() {
            if (this.mood == "happy") {
              this.mood = "sad";
            } else {
              this.mood = "happy";
            }
          }
        }
      });
    </script>
  </body>
</html>

运行结果:点击按钮,盒子样式发生改变(盒子的样式是动态的)

在这里插入图片描述在这里插入图片描述

2.绑定class样式,对象写法:适用于要绑定的样式个数确定、名字也确定,但要动态决定用不用

动态切换单个 class样式:

<!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="../js/vue.js"></script>
    <style>
      .basic {
        width: 200px;
        height: 100px;
        border: 1px solid black;
      }

      .a1 {
        background-color: yellowgreen;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
      <div class="basic" :class="{a1:isActive}">归海一刀</div>
      <button @click="isActive=!isActive">添加/删除样式</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          isActive: true
        }
      });
    </script>
  </body>
</html>

运行结果:点击按钮,选择添加或者删除样式.

在这里插入图片描述

动态切换多个 class样式:

<!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="../js/vue.js"></script>
    <style>
      .basic {
        width: 200px;
        height: 100px;
        border: 1px solid black;
      }

      .a1 {
        background-color: yellowgreen;
      }

      .a2 {
        font-size: 30px;
        text-shadow: 2px 2px 10px red;
      }

      .a3 {
        border-radius: 20px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
      <div class="basic" :class="classObj">归海一刀</div>
      <button @click="classObj.a1=!classObj.a1">添加/删除样式a1</button>
      <button @click="classObj.a2=!classObj.a2">添加/删除样式a2</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          classObj: {
            a1: false,
            a2: false
          }
        }
      });
    </script>
  </body>
</html>

运行结果:点击按钮,选择添加或者删除样式

在这里插入图片描述

3.绑定class样式,数组写法:适用于要绑定的样式个数不确定、名字也不确定

<!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="../js/vue.js"></script>
    <style>
      .basic {
        width: 200px;
        height: 100px;
        border: 1px solid black;
      }

      .a1 {
        background-color: yellowgreen;
      }

      .a2 {
        font-size: 30px;
        text-shadow: 2px 2px 10px red;
      }

      .a3 {
        border-radius: 20px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
      <div class="basic" :class="classArr">归海一刀</div>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          classArr: ["a1", "a2"] // 属性值为数组
        }
      });
    </script>
  </body>
</html>

运行结果:可以通过对数组进行操作,从而对样式进行添加和删除

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

8.2 绑定style样式

绑定style样式有两种方式:

  • 对象写法::style="{fontSize: xxx}"其中xxx是动态值
  • 数组写法::style="[a,b]"其中a、b是样式对象

1.绑定style样式,对象写法

注:在style样式绑定中,CSS中的样式名要改为小驼峰写法,如:font-size 要改为 fontSize

<!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="../js/vue.js"></script>
    <style>
      .basic {
        width: 200px;
        height: 100px;
        border: 1px solid black;
        margin-bottom: 20px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 绑定style样式,对象写法 -->
      <div class="basic" :style="{fontSize: `${fsize}px`}">归海一刀</div>
      <div class="basic" :style="styleObj">归海一刀</div>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          fsize: 30,
          styleObj: {
            // 小驼峰写法
            fontSize: "30px",
            color: "red",
            backgroundColor: "orange"
          }
        }
      });
    </script>
  </body>
</html>

运行结果:

在这里插入图片描述

2.绑定style样式,数组写法

<!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="../js/vue.js"></script>
    <style>
      .basic {
        width: 200px;
        height: 100px;
        border: 1px solid black;
        margin-bottom: 20px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 绑定style样式,数组写法 -->
      <div class="basic" :style="styleArr">归海一刀</div>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          styleArr: [
            {
              fontSize: "30px",
              color: "red"
            },
            {
              backgroundColor: "orange"
            }
          ]
        }
      });
    </script>
  </body>
</html>

运行结果:

在这里插入图片描述

9. 条件渲染

9.1 v-show

v-show

  • 写法:v-show="表达式"
  • 适用于:切换频率较高的场景
  • 特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉(设置为display:none
<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <h1 v-show="isActive">Hello World</h1>
      <button @click="isActive=!isActive">显示/隐藏元素</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          isActive: true
        }
      });
    </script>
  </body>
</html>

运行结果:点击按钮之后,元素被隐藏,再查看DOM结构,发现元素样式上多了个style="display: none;"

在这里插入图片描述
在这里插入图片描述

9.2 v-if、v-else与v-else-if

v-if

  • 写法:v-if="表达式"v-else="表达式"v-else-if="表达式"
  • 适用于:切换频率较低的场景
  • 特点:不展示的DOM元素直接被移除

1.使用v-if会把DOM元素直接被移除

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <h1 v-if="isActive">Hello World</h1>
      <button @click="isActive=!isActive">显示/隐藏元素</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          isActive: true
        }
      });
    </script>
  </body>
</html>

运行结果:点击按钮之后,元素被隐藏,再查看DOM结构,发现元素直接被移除了

在这里插入图片描述

2.v-ifv-elsev-else-if配合使用

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <h2>当前n的值是{{n}}</h2>
      <button @click="n++">点击n+1</button>

      <h3 v-if="n===1">Angular</h3>
      <h3 v-else-if="n===2">React</h3>
      <h3 v-else-if="n===3">Vue</h3>
      <h3 v-else>不是前端三大框架</h3>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          n: 0
        }
      });
    </script>
  </body>
</html>

运行结果:点击按钮增加n的值,当n达到对应值时,分别展示对应的元素
在这里插入图片描述

注:v-if可以和v-else-ifv-else一起使用,但要求结构不能被“打断”

打断的结构示例如下:

<h3 v-if="n===1">Angular</h3>
<h3 v-else-if="n===2">React</h3>
<h3>打断结构</h3> 
<h3 v-else-if="n===3">Vue</h3>
<h3 v-else>不是前端三大框架</h3>

3.v-iftemplate的配合使用:当几个v-if的条件一样时,可以配合template,将多个条件简写为一个

注:使用template标签,不会影响结构,而且只能和v-if配合使用,不能和v-show配合

<!-- 简写前 -->
<h3 v-if="n===1">Angular</h3>
<h3 v-if="n===1">React</h3>
<h3 v-if="n===1">Vue</h3>
<!-- 简写后 -->
<template v-if="n===1">
  <h3>Angular</h3>
  <h3>React</h3>
  <h3>Vue</h3>
</template>

10. 列表渲染

10.1 v-for渲染列表数据

v-for指令:用于展示列表数据

语法:v-for="(item, index) in xxx" :key="yyy"

  • xxx为可遍历对象,包括数组、对象、字符串等
  • item为可遍历对象的每一项数据,index为每一项数据的索引值
  • key属性为每项数据的唯一标识

1.使用v-for遍历数组(常用)

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 遍历数组 -->
      <ul>
        <!-- p为数组成员,index为数组下标 -->
        <li v-for="(p,index) in persons" :key="index">{{index}}:{{p.name}}-{{p.age}}</li>
      </ul>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          // id为每项数据的唯一标识
          persons: [
            { id: "001", name: "张三", age: 18 },
            { id: "002", name: "李四", age: 19 },
            { id: "003", name: "王五", age: 20 }
          ]
        }
      });
    </script>
  </body>
</html>

注:由于数组下标indexid都是唯一标识,而key只要作为唯一标识就可以,因此以上代码中的:key="index"也可以改为:key="p.id"

运行结果:

在这里插入图片描述

2.使用v-for遍历对象(常用)

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 遍历对象 -->
      <ul>
        <!-- v为对象的属性值(value),k为对象的属性名(key) -->
        <li v-for="(v,k) in car" :key="k">{{k}}-{{v}}</li>
      </ul>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          // 对象是一个键值对,属性名就是键(key),可以作为唯一标识
          car: {
            name: "奥迪A6L",
            price: "70万",
            color: "黑色"
          }
        }
      });
    </script>
  </body>
</html>

运行结果:

在这里插入图片描述

3.使用v-for遍历字符串(用的少)

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 遍历字符串 -->
      <ul>
        <!-- item为每个字符,index为字符串下标 -->
        <li v-for="(item,index) in str" :key="index">{{index}}-{{item}}</li>
      </ul>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          str: "Hello"
        }
      });
    </script>
  </body>
</html>

运行结果:

在这里插入图片描述

4.使用v-for遍历指定的次数(用的少)

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 遍历指定的次数 -->
      <ul>
        <!-- 遍历5次 -->
        <li v-for="(number,index) in 5" :key="index">{{number}}-{{str}}</li>
      </ul>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          str: "Hello World"
        }
      });
    </script>
  </body>
</html>

运行结果:

在这里插入图片描述

10.2 key的作用与原理

1.key作为数据的唯一标志,选择原则遵循如下:

  • 最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
  • 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

2.用index作为key可能会引发的问题:

  • 若对数据进行:逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
  • 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 遍历数组 -->
      <button @click.once="add">添加一个老刘</button>
      <ul>
        <!-- p为数组成员,index为数组下标 -->
        <li v-for="(p,index) in persons" :key="index">
          {{p.name}}-{{p.age}} <input type="text" />
        </li>
      </ul>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          // id为每项数据的唯一标识
          persons: [
            { id: "001", name: "张三", age: 18 },
            { id: "002", name: "李四", age: 19 },
            { id: "003", name: "王五", age: 20 }
          ]
        },
        methods: {
          // 对数据进行逆序添加,破坏原有顺序
          add() {
            const p = { id: "004", name: "老刘", age: 40 };
            this.persons.unshift(p);
          }
        }
      });
    </script>
  </body>
</html>

运行结果如下:在输入框中输入内容,然后再点击按钮,发现输入框中的数据显示有问题

在这里插入图片描述

在这里插入图片描述

当把以上代码的:key="index"改为:key="p.id",数据则显示正常了

在这里插入图片描述

原理如下:

  • 虚拟DOM中key的作用:key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】
  • 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
    • 旧虚拟DOM中找到了与新虚拟DOM相同的key:若虚拟DOM中内容没变, 直接使用之前的真实DOM;若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
    • 旧虚拟DOM中未找到与新虚拟DOM相同的key:创建新的真实DOM,随后渲染到到页面

在这里插入图片描述

在这里插入图片描述

10.3 列表过滤

列表过滤:对列表的数据进行筛选,筛选后的数据再渲染到页面中。

1.使用监视属性,实现列表过滤

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      人员列表:<input type="text" placeholder="请输入名字" v-model="keyWord" />
      <ul>
        <!-- 展示过滤后的数组 -->
        <li v-for="(p,index) of filPerons" :key="index">
          {{p.name}}-{{p.age}}-{{p.sex}}
        </li>
      </ul>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          keyWord: "", // 关键词,用于模糊搜索
          persons: [
            { id: "001", name: "马冬梅", age: 19, sex: "女" },
            { id: "002", name: "周冬雨", age: 20, sex: "女" },
            { id: "003", name: "周杰伦", age: 21, sex: "男" },
            { id: "004", name: "温兆伦", age: 22, sex: "男" }
          ],
          filPerons: [] // 用于存放过滤后的数组,避免改动原数组
        },
        watch: {
          // 监视keyWord属性,当keyWord属性值发生变化时,调用handler函数进行模糊搜索
          keyWord: {
            immediate: true, // 初始化时调用handler,空字符串会匹配所有数据,首次展示为完整数据
            handler(val) {
              this.filPerons = this.persons.filter(p => {
                return p.name.includes(val); // 判断输入框中的值是否出现在姓名中
              });
            }
          }
        }
      });
    </script>
  </body>
</html>

运行结果:

在这里插入图片描述

在这里插入图片描述

2.使用计算属性,实现列表过滤

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      人员列表:<input type="text" placeholder="请输入名字" v-model="keyWord" />
      <ul>
        <!-- 展示过滤后的数组 -->
        <li v-for="(p,index) of filPerons" :key="index">
          {{p.name}}-{{p.age}}-{{p.sex}}
        </li>
      </ul>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          keyWord: "", // 关键词,用于模糊搜索
          persons: [
            { id: "001", name: "马冬梅", age: 19, sex: "女" },
            { id: "002", name: "周冬雨", age: 20, sex: "女" },
            { id: "003", name: "周杰伦", age: 21, sex: "男" },
            { id: "004", name: "温兆伦", age: 22, sex: "男" }
          ]
        },
        computed: {
          // 计算属性filPerons依赖于keyWord属性,当keyWord发生改变时,会重新计算属性
          filPerons() {
            return this.persons.filter(p => {
              return p.name.includes(this.keyWord);
            });
          }
        }
      });
    </script>
  </body>
</html>

注:使用computed还是watch的原则
1.computed 的写法更加的简洁(推荐使用),能用 computed 完成的功能,不要用 watch 完成
2.computed 无法完成的功能才用 watch 实现,例如:watch 可以进行异步操作

10.4 列表排序

列表排序:对列表数据进行排序,排序后的数据再渲染到页面中

  • 可以对完整的列表数据进行排序
  • 也可以配合列表过滤,对筛选后的列表数据进行排序。
<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      人员列表:<input type="text" placeholder="请输入名字" v-model="keyWord" />
      <button @click="sortType = 2">年龄升序</button>
      <button @click="sortType = 1">年龄降序</button>
      <button @click="sortType = 0">原顺序</button>
      <ul>
        <!-- 展示过滤后的数组 -->
        <li v-for="(p,index) of filPerons" :key="index">
          {{p.name}}-{{p.age}}-{{p.sex}}
        </li>
      </ul>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          keyWord: "",
          sortType: 0, // 0原顺序、1降序、2升序
          persons: [
            { id: "001", name: "马冬梅", age: 30, sex: "女" },
            { id: "002", name: "周冬雨", age: 31, sex: "女" },
            { id: "003", name: "周杰伦", age: 18, sex: "男" },
            { id: "004", name: "温兆伦", age: 19, sex: "男" }
          ]
        },
        computed: {
          // 计算属性filPerons依赖于keyWord和sortType属性,依赖的这两个属性任何一个发生变化,都会引起重新计算属性
          filPerons() {
            // 对列表数据进行筛选
            const arr = this.persons.filter(p => {
              return p.name.includes(this.keyWord);
            });
            // 判断一下是否需要排序
            if (this.sortType) {
              arr.sort((p1, p2) => {
                return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age;
              });
            }
            return arr;
          }
        }
      });
    </script>
  </body>
</html>

运行结果:在输入框中输入关键词,可以对数据进行筛选,再点击按钮可以对筛选后的数据再进行排序

在这里插入图片描述

11. 收集表单数据

v-model用于收集表单数据,且在收集表单数据时,根据表单控件类型的不同,默认收集的值也不同。

v-model需要注意的点如下:

  • 若表单控件为文本框或密码框,即 <input type="text"/><input type="password"/>,则v-model收集的是value值,用户输入的就是value值。
  • 若表单控件为单选框,即<input type="radio"/>,则v-model收集的是value值,需要给标签配置value值。
  • 若表单控件为复选框,即<input type="checkbox"/>,则有以下两种情况:
    • 没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
    • 配置了input的value属性,当v-model的初始值是非数组时,那么收集的就是checked;是数组时,那么收集的的就是value组成的数组

注:v-model的三个修饰符
1.lazy:失去焦点时,再收集数据
2.number:输入字符串转为有效的数字
3.trim:清除文本框首尾空格

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 阻止表单默认跳转行为 -->
      <form @submit.prevent="demo">
        账号:<input type="text" v-model.trim="userInfo.account" /> <br /><br />
        密码:<input type="password" v-model="userInfo.password" /> <br /><br />
        年龄:<input type="number" v-model.number="userInfo.age" /> <br /><br />
        性别: 男<input type="radio" name="sex" v-model="userInfo.sex" value="male" /><input type="radio" name="sex" v-model="userInfo.sex" value="female" /> <br /><br />
        爱好: 学习<input type="checkbox" v-model="userInfo.hobby" value="study" /> 打游戏<input type="checkbox" v-model="userInfo.hobby" value="game" /> 吃饭<input
          type="checkbox"
          v-model="userInfo.hobby"
          value="eat"
        />
        <br /><br />
        所属校区
        <select v-model="userInfo.city">
          <option value="">请选择校区</option>
          <option value="beijing">北京</option>
          <option value="shanghai">上海</option>
          <option value="shenzhen">深圳</option>
          <option value="wuhan">武汉</option>
        </select>
        <br /><br />
        其他信息:
        <textarea v-model.lazy="userInfo.other"></textarea> <br /><br />
        <input type="checkbox" v-model="userInfo.agree" />阅读并接受<a href="http://www.baidu.com">《用户协议》</a>
        <button>提交</button>
      </form>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          userInfo: {
            account: "",
            password: "",
            age: 18,
            sex: "female",
            hobby: [],
            city: "beijing",
            other: "",
            agree: ""
          }
        },
        methods: {
          demo() {
            console.log(JSON.stringify(this.userInfo));
          }
        }
      });
    </script>
  </body>
</html>

运行结果如下:输入内容,勾选单选和复选框等控件,再点击提交按钮,可以看到控制台打印了表单提交的信息。

在这里插入图片描述

12. 内置指令

目前已经学过的内置指令有:v-bindv-modelv-onv-showv-ifv-elsev-for

12.1 v-text 解析普通文本

v-text指令:用于解析普通文本,向其所在的节点中渲染文本内容。

v-text与插值语法的区别:v-text会替换掉节点中的内容,而{{xxx}}则不会,因此{{xxx}}更加灵活,用的更多。

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 插值语法相对v-text更加灵活 -->
      <div>Hello, {{name}}</div>
      <!-- v-text会替换掉节点中的内容 -->
      <div v-text="name">Hello</div>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          name: "Jack"
        }
      });
    </script>
  </body>
</html>

运行结果:

在这里插入图片描述

12.2 v-html 解析html标签

v-html指令:用于解析html标签,向其所在的节点中渲染包含html结构的内容。

v-html与插值语法的区别:v-html会替换掉节点中的内容,而{{xxx}}则不会。

注:v-html有安全性问题
1.在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击
2.一定要在可信的内容上使用v-html,永不要用在用户提交的内容上(如表单的输入框等)

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- v-text只能解析普通文本,无法解析html标签 -->
      <div v-text="str"></div>
      <!-- v-html可以解析html标签 -->
      <div v-html="str"></div>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          str: `<h3>Hello World</h3>`
        }
      });
    </script>
  </body>
</html>

运行结果:

在这里插入图片描述

12.3 v-cloak 隐藏模板

v-cloak指令:

  • 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
  • 该指令可以配合css属性选择器,解决网速慢时页面展示出{{xxx}}的问题
<!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="../js/vue.js"></script>
    <style>
      /* 使用该属性的元素被隐藏 */
      [v-cloak] {
        display: none;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 
        初始时,v-cloak属性存在,但由于属性选择器的作用,会将h2标签隐藏;
        等到vue接管容器后,会移除掉h2上的v-cloak属性,此时会显示h2标签
       -->
      <h2 v-cloak>{{name}}</h2>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          name: "Jack"
        }
      });
    </script>
  </body>
</html>

12.4 v-once 仅渲染一次

v-once指令:

  • v-once所在节点在初次动态渲染后,就视为静态内容
  • 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <h2 v-once>初始化的n值是:{{n}}</h2>
      <h2>当前的n值是:{{n}}</h2>
      <button @click="n++">点我n+1</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          n: 1
        }
      });
    </script>
  </body>
</html>

运行结果:点击按钮,只有下面的n值发生变化,而上面的n值不变化

在这里插入图片描述

12.5 v-pre 跳过元素编译

v-pre指令:

  • 跳过其所在节点的编译过程
  • 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 该标签未用到指令语法和插值语法,可以使用v-pre加快编译 -->
      <h2 v-pre>Vue其实很简单</h2>
      <h2>当前的n值是:{{n}}</h2>
      <button @click="n++">点我n+1</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      const vm = new Vue({
        el: "#app",
        data: {
          n: 1
        }
      });
    </script>
  </body>
</html>

13. 自定义指令

13.1 函数式和对象式

自定义指令有两种定义方式:

  • 函数式定义
  • 对象式定义

1.函数式定义

例:定义一个 v-big 指令,和 v-text 功能类似,但会把绑定的数值放大10倍

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <h2>当前的n值是:<span v-text="n"></span></h2>
      <h2>放大10倍后的n值是:<span v-big="n"></span></h2>
      <button @click="n++">点我n+1</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      new Vue({
        el: "#app",
        data: {
          n: 1,
        },
        // 自定义指令
        directives: {
          // element表示真实的dom元素, binding表示绑定的对象
          big(element, binding) {
            element.innerText = binding.value * 10;
          },
        },
      });
    </script>
  </body>
</html>

big函数被调用的条件:
1.指令与元素成功绑定时(一上来)
2.指令所在的模板被重新解析时

运行结果:点击按钮,上面的n值+1,下面的n值+1后乘以10倍

在这里插入图片描述

2.对象式定义

配置对象中常用的三个回调:

  • bind:指令与元素成功绑定时调用
  • inserted:指令所在元素被插入页面时调用
  • update:指令所在模板结构被重新解析时调用

对象式定义与函数式定义的区别:函数式定义写法等于对象式定义中的 bind + update 回调函数,而对象式定义还有inserted回调函数,可以实现函数式定义无法实现的细节操控。

例:定义一个 v-fbind 指令,和 v-bind 功能类似,但可以让其所绑定的input元素默认获取焦点

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <h2>当前的n值是:<span v-text="n"></span></h2>
      <button @click="n++">点我n+1</button>
      <br />
      <input type="text" v-fbind:value="n" />
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      new Vue({
        el: "#app",
        data: {
          n: 1,
        },
        // 自定义指令
        directives: {
          fbind: {
            //指令与元素成功绑定时(一上来)
            bind(element, binding) {
              element.value = binding.value;
            },
            //指令所在元素被插入页面时
            inserted(element, binding) {
              element.focus();
            },
            //指令所在的模板被重新解析时
            update(element, binding) {
              element.value = binding.value;
            },
          },
        },
      });
    </script>
  </body>
</html>

运行结果:刷新页面后,文本框默认获取焦点,点击按钮后失去焦点

在这里插入图片描述

13.2 局部指令和全局指令

自定义指令分为两类:

  • 局部指令
  • 全局指令

1.局部指令写法

// 函数式定义
 new Vue({															
 	directives:{指令名:回调函数}   
 }) 	
 
// 对象式定义	
 new Vue({															
 	directives:{指令名:配置对象}   
 }) 		
new Vue({
  // 局部自定义指令
  directives: {
    // 函数式定义
    big(element, binding) {
      element.innerText = binding.value * 10;
    },
    // 对象式定义
    fbind: {
      bind(element, binding) {
        element.value = binding.value;
      },
      inserted(element, binding) {
        element.focus();
      },
      update(element, binding) {
        element.value = binding.value;
      },
    },
  },
});

2.全局指令写法

// 函数式定义
Vue.directive(指令名, 回调函数)
// 对象式定义
Vue.directive(指令名, 配置对象)
 // 函数式定义
 Vue.directive("big", function (element, binding) {
   element.value = binding.value;
 });

 // 对象式定义
 Vue.directive("fbind", {
   bind(element, binding) {
     element.value = binding.value;
   },
   inserted(element, binding) {
     element.focus();
   },
   update(element, binding) {
     element.value = binding.value;
   },
 });

自定义指令注意事项:
1.指令定义时不加v-,但使用时要加v-;
2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。

new Vue({
  directives: {
    "big-number"(element, binding) {
      element.innerText = binding.value * 10;
    },
  },
});

14. Vue的生命周期

14.1 生命周期概念

Vue的生命周期:又称生命周期回调函数、生命周期函数、生命周期钩子,是Vue在关键时刻帮我们调用的一些特殊名称的函数。

生命周期函数中的this指向是vm 或 组件实例对象

vue的生命周期分为以下四个流程:每个流程对应两个函数,其中最重要的是mountedbeforeDestroy

  • 创建流程:
    • beforeCreate:此时无法通过vm访问到data中的数据和methods中的方法。
    • created:此时可以通过vm访问到data中的数据和methods中配置的方法。
  • 挂载流程:
    • beforeMount:此时页面呈现的是未经Vue编译的DOM结构,所有对DOM的操作,最终都不奏效。
    • mounted:此时页面呈现的是经过Vue编译的DOM,对DOM的操作均有效(尽可能避免)。至此初始化过程结束,一般在此阶段:开启定时器、发送网络请求、订阅消息以及绑定自定义事件等初始化操作
  • 更新流程:
    • beforeUpdate:此时数据是新的,但页面是旧的,即页面尚未和数据保持同步。
    • updated:此时数据是新的,页面也是新的,即页面尚未和数据保持同步。
  • 销毁流程
    • beforeDestroy:此时vm中所有的data、methods和指令等等,都处于可用状态,马上要执行销毁过程。一般在此阶段:关闭定时器、取消订阅消息以及解绑自定义事件等收尾操作
    • destroyed:此时vm已完成销毁。

Vue的生命周期流程图:
在这里插入图片描述

14.2 生命周期演示

1.生命周期钩子函数的调用顺序

<!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="../js/vue.js"></script>
  </head>
  <body>
    <!-- 准备好一个容器-->
    <div id="app">
      <h2>当前的n值是:{{n}}</h2>
      <button @click="add">点我n+1</button>
      <button @click="bye">点我销毁vm</button>
    </div>

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示

      new Vue({
        el: "#app",
        data: {
          n: 1,
        },
        methods: {
          add() {
            this.n++;
          },
          bye() {
            this.$destroy();
          },
        },
        beforeCreate() {
          console.log("beforeCreate");
        },
        created() {
          console.log("created");
        },
        beforeMount() {
          console.log("beforeMount");
        },
        mounted() {
          console.log("mounted");
        },
        beforeUpdate() {
          console.log("beforeUpdate");
        },
        updated() {
          console.log("updated");
        },
        beforeDestroy() {
          console.log("beforeDestroy");
        },
        destroyed() {
          console.log("destroyed");
        },
      });
    </script>
  </body>
</html>

在这里插入图片描述

2.生命周期的常用应用场景

常用的生命周期钩子:

  • mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等(初始化操作)
  • beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等(收尾操作)

关于销毁Vue实例:

  • 销毁后借助Vue开发者工具看不到任何信息
  • 销毁后自定义事件会失效,但原生DOM事件依然有效
  • 一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了
<!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="../js/vue.js"></script>
  </head>
  <body>
    <!-- 准备好一个容器-->
    <div id="app">
      <h2 :style="{opacity}">欢迎学习Vue</h2>
      <button @click="opacity = 1">透明度设置为1</button>
      <button @click="stop">点我停止变换</button>
    </div>

    <script>
      Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。

      new Vue({
        el: "#app",
        data: {
          opacity: 1,
        },
        methods: {
          stop() {
            this.$destroy(); // 销毁vm
          },
        },
        //Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
        mounted() {
          // 初始化操作:启动定时器
          console.log("mounted", this);
          this.timer = setInterval(() => {
            console.log("setInterval");
            this.opacity -= 0.01;
            if (this.opacity <= 0) this.opacity = 1;
          }, 16);
        },
        beforeDestroy() {
          // 收尾操作:清除定时器
          clearInterval(this.timer);
        },
      });
    </script>
  </body>
</html>

运行结果:
未点击按钮停止变换前,点击设置透明度有效;点击按钮停止变换后,再点击设置透明度则无效,因为vm被销毁了。
在这里插入图片描述

15. Vue的组件化编程

15.1 模块与组件

1.模块与模块化:

  • 模块:一个模块就是一个js文件,向外提供特定功能的 js 程序,作用是可以复用 js、简化 js 的编写、提高 js 运行效率。
  • 模块化:当应用中的 js 都以模块来编写的, 那这个应用就是一个模块化的应用。

2.组件与组件化:

  • 组件:用来实现局部(特定)功能效果的代码集合(html/css/js/image……),作用是复用编码、简化项目编码、提高运行效率。
  • 组件化:当应用中的功能都是多组件的方式来编写的, 那这个应用就是一个组件化的应用。

传统方式编写应用:

在这里插入图片描述

组件化方式编写应用:

在这里插入图片描述

15.2 非单文件组件

组件的分类:

  • 非单文件组件:模板编写没有提示, 没有构建过程,无法将 ES6 转换,不支持组件的 CSS,真正开发中几乎不用。
  • 单文件组件:单文件组件为 .vue 文件,由<template> 模板页面、<script> JS模块对象和 <style> 样式组成。

虽然非单文件组件在开发中几乎不用,但是要想学会单文件组件( .vue) ,必须先从非单文件组件开始入手。

15.2.1 组件的基本使用

Vue中使用组件的三大步骤:

  • 创建组件
  • 注册组件
  • 使用组件

1.创建组件:

  • 使用 Vue.extend(options) 创建组件
  • Vue.extend(options)new Vue(options) 时传入的 options 几乎一样,但有区别如下:
    • el 不能写,因为最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器
    • data 必须写成函数,为了避免组件被复用时,数据存在引用关系

注:使用template可以配置组件结构

2.注册组件

  • 局部注册:靠 new Vue 的时候传入 components 配置项
  • 全局注册:靠 Vue.component('组件名',组件)

3.使用组件:编写组件标签,使用组件

组件示例如下:

<!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="../js/vue.js"></script>
  </head>
  <body>
    <!-- 准备好一个容器-->
    <div id="app">
      <!-- 第三步:使用student组件 -->
      <student></student>
    </div>

    <script>
      Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。

      // 第一步:创建student组件
      const student = Vue.extend({
        template: `
          <div>
            <h2>学生姓名:{{name}}</h2>
            <h2>学生年龄:{{age}}</h2>
            <button @click="showName">点我提示姓名</button>	
          </div>
        `,
        // 组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器
        data() {
          return {
            name: "张三",
            age: 18,
          };
        },
        methods: {
          showName() {
            alert(this.name);
          },
        },
      });

      // 第二步:注册student组件(局部注册)
      new Vue({
        el: "#app",
        components: {
          student,
        },
      });
    </script>
  </body>
</html>

以上局部注册也可改为全局注册:

// 第二步:注册student组件
Vue.component("student", student);

new Vue({
  el: "#app",
});

15.2.2 组件的注意事项

1.组件名的取名:

  • 组件名由一个单词组成:
    • 第一种写法(首字母小写):school
    • 第二种写法(首字母大写):School(推荐)
  • 组件名由多个单词组成:
    • 第一种写法(kebab-case命名):my-school
    • 第二种写法(CamelCase命名):MySchool (推荐,且该写法需要Vue脚手架支持)

2.组件标签的两种写法

  • 双标签:<school></school>
  • 单标签:school/>

备注:不用使用脚手架时,<school/> 会导致后续组件不能渲染

3.Vue.extend()可以简写:const school = Vue.extend(options) ,可简写为 const school = options

<!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="../js/vue.js"></script>
  </head>
  <body>
    <!-- 准备好一个容器-->
    <div id="app">
      <!-- 第三步:使用student组件 -->
      <Student />
    </div>

    <script>
      Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。

      // 第一步:创建student组件,简写省略了Vue.extend()
      const Student = {
        template: `
        <div>
          <h2>学生姓名:{{name}}</h2>
          <h2>学生年龄:{{age}}</h2>
          <button @click="showName">点我提示姓名</button>	
        </div>
        `,
        // 组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器
        data() {
          return {
            name: "张三",
            age: 18,
          };
        },
        methods: {
          showName() {
            alert(this.name);
          },
        },
      };

      // 第二步:注册student组件
      new Vue({
        el: "#app",
        components: {
          Student,
        },
      });
    </script>
  </body>
</html>

15.2.3 组件的嵌套

组件的嵌套:
在这里插入图片描述

<!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="../js/vue.js"></script>
  </head>
  <body>
    <!-- 准备好一个容器-->
    <div id="root"></div>

    <script>
      Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。

      //定义student组件
      const student = Vue.extend({
        name: "student",
        template: `
        <div>
          <h2>学生姓名:{{name}}</h2>	
          <h2>学生年龄:{{age}}</h2>	
        </div>
        `,
        data() {
          return {
            name: "张三",
            age: 18,
          };
        },
      });

      //定义school组件
      const school = Vue.extend({
        name: "school",
        template: `
		<div>
			<h2>学校名称:{{name}}</h2>	
			<h2>学校地址:{{address}}</h2>	
			<student></student>
		</div>
		`,
        data() {
          return {
            name: "北京大学",
            address: "北京",
          };
        },
        //注册组件(局部),school中嵌套student
        components: {
          student,
        },
      });

      //定义hello组件
      const hello = Vue.extend({
        template: `<h1>{{msg}}</h1>`,
        data() {
          return {
            msg: "欢迎来到北京!",
          };
        },
      });

      //创建vm
      new Vue({
        template: `
        <div>
            <hello></hello>	
            <school></school>	
        </div>
        `,
        el: "#root",
        //注册组件(局部)
        components: {
          hello,
          school,
        },
      });
    </script>
  </body>
</html>

组件之间结构如下:

在这里插入图片描述

15.2.4 VueComponent

关于VueComponent:

1.school 组件本质是一个名为 VueComponent 的构造函数,且不是程序员定义的,是 Vue.extend 生成的

2.我们只需要编写 <school/><school></school>,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行了 new VueComponent(options)

3.特别注意,每次调用 Vue.extend,返回的都是一个全新的 VueComponent

4.关于this指向:

  • 组件配置中的this指向:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是 VueComponent 实例对象
  • new Vue(options)配置中的this指向:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是 Vue 实例对象

15.2.5 一个重要的内置关系

一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype,这个关系可以让VueComponent组件实例对象访问到 Vue原型上的属性、方法。

在这里插入图片描述

<!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="../js/vue.js"></script>
  </head>
  <body>
    <div id="root"></div>

    <script>
      Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。
      Vue.prototype.x = 99;

      //定义school组件
      const school = Vue.extend({
        name: "school",
        template: `
		<div>
			<h2>学校名称:{{name}}</h2>	
			<h2>学校地址:{{address}}</h2>	
			<button @click="showX">点我输出x</button>
		</div>
		`,
        data() {
          return {
            name: "北京大学",
            address: "北京",
          };
        },
        methods: {
          showX() {
            alert(this.x);
          },
        },
      });

      new Vue({
        template: `<school></school>`,
        el: "#root",
        components: { school },
      });
    </script>
  </body>
</html>

15.3 单文件组件

15.3.1 单文件组件的组成

单文件组件:即一个 .vue 文件,由以下三部分组成

  • <template> : 模板页面
  • <script> :JS模块对象
  • <style> : 样式

1.模板页面

<template>
页面模板
</template>

2.JS模块对象

<script>
  export default {
    data() {
      return {};
    },
    methods: {},
    computed: {},
    components: {},
  };
</script>

3.样式

<style>
样式定义
</style>

15.3.2 单文件组件的示例

本节仅展示代码和文件之间的关联逻辑,此处的代码无法直接运行,需要搭建脚手架环境以后才能运行。

示例:有以下文件结构,作为单文件组件的实际开发使用

在这里插入图片描述

文件之间关系如下:

  • index.html :主页面
  • main.js :程序入口
  • App.vue :汇总所有组件,此处包括School和Student组件
  • School.vue :学校组件
  • Student.vue :学生组件

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
  </head>
  <body>
    <!-- 准备一个容器 -->
    <div id="root"></div>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript" src="./main.js"></script>
  </body>
</html>

main.js

import App from './App.vue'

new Vue({
	el:'#root',
	template:`<App></App>`,
	components:{App},
})

App.vue

<template>
  <div>
    <School></School>
    <Student></Student>
  </div>
</template>

<script>
//引入组件
import School from "./School.vue";
import Student from "./Student.vue";

export default {
  name: "App",
  components: {
    School,
    Student,
  },
};
</script>

School.vue

<template>
	<div class="demo">
		<h2>学校名称:{{name}}</h2>
		<h2>学校地址:{{address}}</h2>
		<button @click="showName">点我提示学校名</button>	
	</div>
</template>

<script>
	 export default {
		name:'School',
		data(){
			return {
				name:'北京大学',
				address:'北京'
			}
		},
		methods: {
			showName(){
				alert(this.name)
			}
		},
	}
</script>

<style>
	.demo{
		background-color: orange;
	}
</style>

Student.vue

<template>
	<div>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生年龄:{{age}}</h2>
	</div>
</template>

<script>
	 export default {
		name:'Student',
		data(){
			return {
				name:'张三',
				age:18
			}
		}
	}
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/440688.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024最新版短剧小程序

仿抖音滑动小短剧影视微信小程序源码&#xff0c;带支付收益等模式、支持无限滑动&#xff1b;高性能滑动、预加载、视频预览&#xff0c;支持剧情介绍&#xff0c;集合壁纸另外仿抖音滑动效果&#xff1b;支持会员模式&#xff0c;支持用户单独购买等等多功能。 丰富的后台设…

【C++】C++11---右值引用和移动语义

目录 1、什么是左值引用和右值引用2、左值引用与右值引用比较3、右值引用使用场景和意义4、右值引用引用左值的分析5、完美转发 1、什么是左值引用和右值引用 传统的C语法中就有引用的语法&#xff0c;而C11中新增了的右值引用语法特性&#xff0c;所以从现在开始我们之前学习…

读《文明之光》第1册总结

人类几千年的文明史和地球的历史相比&#xff0c;实在是太短暂了&#xff0c;大约相当于几分钟和一年的关系。人类已经走过的路&#xff0c;相比今后要走的漫漫长路&#xff0c;只能算是刚刚起步。如果跳出一个个具体事件&#xff0c;站在历史的高度去看&#xff0c;我们会发现…

3DES算法的起源与演进:保障信息安全的重要里程碑

title: 3DES算法的起源与演进&#xff1a;保障信息安全的重要里程碑 date: 2024/3/8 21:25:19 updated: 2024/3/8 21:25:19 tags: 3DES算法起源安全性增强三次迭代加密密钥管理复杂效率对比AES应用场景广泛Python实现示例 一、3DES算法的起源与演进 3DES算法是DES算法的增强版…

JAVA缓存:小工具

一、google.guava 用到的包 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>20.0</version><scope>compile</scope></dependency>写法 单位 代码 Component publi…

‘ jupyter ‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。

安装anaconda后&#xff0c;在 Dos黑窗口 运行 jupyter notebook 的两个问题 原因&#xff1a;没配置环境变量 解决方法&#xff1a; 在 系统环境变量Path 中 添加两个地址 这里以anaconda安装在 D:\anaconda\install 下为例 &#xff08;根据个人安装具体位置而定&#xff…

【OJ比赛日历】快周末了,不来一场比赛吗? #03.09-03.15 #13场

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 2024-03-09&#xff08;周六&#xff09; #6场比赛2024-03-10…

docker部署若依项目

目录 目录 一、搭建局域 二、redis安装 1.创建目录 2. redis.conf修改 三、MySQL安装 1. 安装 2. 设置远程连接 3. 创建数据库 四、若依后端项目搭建 1. 切换到家目录 2. 上传jar包 3. 上传Dockerfile文件 4. 构建镜像 5. 运行容器 6. 查看运行情况 7. 测试(自己…

day17_订单(结算,提交订单,支付页,立即购买,我的订单)

文章目录 订单模块1 结算1.1 需求说明1.2 获取用户地址1.2.1 UserAddress1.2.2 UserAddressController1.2.3 UserAddressService1.2.4 UserAddressMapper1.2.5 UserAddressMapper.xml 1.3 获取购物项数据1.3.1 CartController1.3.2 CartService1.3.3 openFeign接口定义 1.4 环境…

LVS集群(Linux Virtual server)介绍----及LVS的NAT模式部署(一)

群集的含义 ●Cluster&#xff0c;集群、群集由多台主机构成&#xff0c;但对外只表现为一个整体&#xff0c;只提供访问入口(域名或IP地址)&#xff0c;相当于一台大型计算机 问题&#xff1a; 互联网应用中&#xff0c;随着站点对硬件性能、响应速度、服务稳定性、数据可靠…

15. C++泛型与符号重载

【泛型编程】 若多组类型不同的数据需要使用相同的代码处理&#xff0c;在C语言中需要编写多组代码分别处理&#xff0c;这样做显然太过繁琐&#xff0c;C增加了虚拟类型&#xff0c;使用虚拟类型可以实现一组代码处理多种类型的数据。 虚拟类型是暂时不确定的数据类型&#…

uniapp在页面中中获取pages.json下pages设置navigationBarTitleText这个值?uniapp获取页面标题

一、问题描述 有个需求就是,在app.vue页面中首先会隐藏所有页面的title,然后在相应的页面会判断当前环境是否是在微信浏览器内&#xff0c;如果不是&#xff0c;则还原标题。 二、解决方法 在 pages.json 文件中设置 navigationBarTitleText&#xff0c;例如&#xff1a; {&qu…

OpenCascade源码剖析:Handle类

Handle其实就是智能指针的上古版本&#xff0c;了解一点C11的应该对shared_ptr非常熟悉&#xff0c;那么你就把Handle当做shared_ptr来理解就没有任何问题了。 不过OCCT的Handles是侵入式的实现&#xff0c;前面讲过Standard_Transient类提供了引用计数机制&#xff0c;这个就…

Missing type map configuration or unsupported mapping

今天开发的时候突然遇到这么一个问题&#xff0c;可以确定的是不是AutoMap的问题&#xff0c;因为项目中其他接口都是好好的&#xff0c;只有新加的这个控制器不行&#xff0c;排查了一下&#xff0c;少了映射配置&#xff0c;在这里加上映射关系即可&#xff0c;大意了。

egg如何写单元测试

优秀的代码需要有单元测试进行质量保证&#xff0c;每个测试用例都给应用的稳定性提供了一层保障。 测试目录结构 我们约定 test 目录为存放所有测试脚本的目录&#xff0c;测试所使用到的 fixtures 和相关辅助脚本都应该放在此目录下。 测试文件的目录和我们需要测试的文件目…

HTML—常用标签

常用标签&#xff1a; 标题标签&#xff1a;<h1></h1>......<h6></h6>段落标签&#xff1a;<p></p>换行标签&#xff1a;<br/>列表&#xff1a;无序列表<ul><li></li></ul> 有序列表<ol>&…

Python的数据库编程基础知识

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd;如果停止&#xff0c;就是低谷&#xf…

[HackMyVM]靶场 Zeug

kali:192.168.56.104 主机发现 arp-scan -l # arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:d2:e0:49, IPv4: 192.168.56.104 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.56.1 0a:00:27:00:00:05 (Un…

力扣hot100:240.搜索二维矩阵II(脑子)

吉大21级算法分析与设计的一道大题&#xff0c;由于每一行都是排好序的直接逐行二分 可以达到&#xff1a;O(mlogn)。但是这里追求更广的思路可以使用其他方法。 矩阵四分&#xff1a; 在矩阵中用中心点比较&#xff0c;如果target大于中心点的值&#xff0c;则由于升序排列&am…

排序二叉树

参考 Binary Search Tree Visualization (usfca.edu) 一、构建排序二叉树 注意引用 tree /*** 构造二叉排序树* param tree* param x*/ void buildTree(BSTree &tree,ElementType &x){if (treeNULL){tree(BSTree) calloc(1, sizeof(BSTNode));tree->datax;//注意ret…