1.vue基础语法

一、模板语法

(一) 插值操作

[1]. Mustache语法

也称为双大括号语法。

  • 双大括号中可以使用JS表达式
<div id="app">
    <h2>{{cn}}</h2>
    <h2>{{count * 2}}</h2>
    <h2>{{cn + ' ' + count}}</h2>
    <h2>{{1 + 1}}</h2>
    <h2>{{Date.now()}}</h2>
    <h2>{{message.toUpperCase()}}</h2>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
   //let(变量), const(常量)
   const vm = new Vue({
       el:"#app", //用于挂载要管理的元素
       data:{//定义数据
           message:"hello,world!",
           cn:"你好,世界!",
           count: 100
       }
   });
</script>

[2]. v-once

该指令表示元素和组件只渲染一次,不会随着数据改变而改变。

<h2 v-once>{{message}}</h2>

[3]. v-html

如果我们希望按照HTML格式进行解析数据,并且显示对应的内容,可以使用v-html指令。
会有xss风险,会覆盖子组件。

<div id="app">
  <!-- 输出 <h2><a href="http://www.baidu.com">百度一下</a><h2>  -->
  <h2 v-html="url"></h2>
</div>

<script src="./vue.js"></script>
<script>
  const app = new Vue({
    el: "#app", //用于挂载要管理的元素
    data: { //定义数据
      url: '<a href="http://www.baidu.com">百度一下</a>'
    }
  });
</script>

[4]. v-text

作用和Mustache比较相似:都是用于将数据显示在界面中。
使用这种方法会将标签总原本的文本内容给替换掉。

<h2 v-text="message">这段文字将被替换掉</h2>

[5]. v-pre

  • v-pre用于跳过这个元素和它的子元素的编译过程,用于直接显示原本的Mustache语法。

  • 可利用它跳过:没有使用指令、也没有使用差插值语法的节点,会加快编译。

    <!-- 输出 <h2>{{message}}</h2> -->
    <h2 v-pre>{{message}}</h2>
    

[6]. v-cloak

如果未执行到vue代码时,页面会显示出未编译额Mustache标签。为了避免这种情况可以使用v-cloak

在vue解析之前,如果有属性v-cloak,会添加一个样式[v-cloak]{display:none}
在vue解析之后,将删除v-cloak。

<h2 v-cloak>{{message}}</h2>

[7]. 自定义指令

https://cn.vuejs.org/v2/guide/custom-directive.html

  • 局部自定义指令:directives:{指令名:配置对象}directives:{指令名:回调函数}

  • 全局指令: Vue.directive(指令名, 配置对象)Vue.directive(指令名, 回调函数)

  • 自定义指令提供的钩子函数 (均为可选):

    bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

    inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

    update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。

    componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

    unbind:只调用一次,指令与元素解绑时调用。

  • 钩子函数被赋予了以下参数:

    el: 指令所绑定的元素,可以用来直接操作 DOM 。
    binding: 一个对象,包含以下属性:

    • name: 指令名,不包括 v- 前缀。
      - value: 指令的绑定值, 例如: v-my-directive=“1 + 1”, value 的值是 2。
    • oldValue: 指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
      - expression: 绑定值的字符串形式。 例如 v-my-directive=“1 + 1” , expression 的值是 “1 + 1”。
      - arg: 传给指令的参数。例如 v-my-directive:foo, arg 的值是 “foo”。
      • modifiers: 一个包含修饰符的对象。 例如: v-my-directive.foo.bar, 修饰符对象 modifiers 的值是 { foo: true, bar: true }。
    • vnode: Vue 编译生成的虚拟节点,查阅 VNode API 了解更多详情。
    • oldVnode: 上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
<div id="app">
  <p v-add-number="number"></p>
  <button @click="add">添加number</button>
  <input type="text" v-fbind:value="number" />
</div>
</div>
<script src="./../js/vue.js"></script>
<script>
  const vm = new Vue({
    el: "#app",
    data: {
      number: 0
    },
    methods: {
      add() {
        this.number++;
      }
    },
    directives: {
      'add-number'(element, binding) { //等于 bind()和update()钩子函数的简写
      	console.log(this); //window
        element.innerText = binding.value;
      },
      fbind: {
        bind(element, binding) {
          console.log('指令与元素成功绑定时调用');
          console.log(this); //window
          element.value = binding.value;
        },
        inserted(element, binding) {
          console.log('指令所在元素被插入父元素中时被调用');
          console.log(this); //window
          element.focus();  //元素获得焦点
        },
        update(element, binding){
          console.log('指令所在的模板被重新解析时被调用');
          console.log(this); //window
          element.value = binding.value;
        }
      }
    }
  });
</script>

(二)绑定数据和元素属性 v-bind

v-bind 单向数据绑定:数据只能从data流向页面。

[1].基本使用

  • v-bind指令动态绑定属性值
    <img v-bind:src="imgUrl">
    
  • v-bind语法糖,在属性前添加“:”
    <a :href="url">
    
  • 直接使用js表达式
    <h2 v-bind:data-date="Date.now()">time</h2> 
    <h2 v-bind:data-date="name.toUpperCase()">name</h2> 
    

[2].动态绑定class

  • 通过变量来确定需要绑定的class。

    <div id="root">
    <!-- 通过点击事件,将class中的moon 变为 bad -->
     <p :class="moon" @click="changeMoon"> 点击却换心情 {{moon}}</p>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      const vm = new Vue({
        el: '#root',
        data: {
          moon: 'happy',
        },
        methods: {
          changeMoon(){
            this.moon = 'bad';
          }
        },
      });
    </script>
    
  • 通过数组语法来添加多个样式。如果参数没有使用单引号括住,是会被当成变量解析的。

    <!-- 为h2添加 active和line两个样式 --> 
    <h2 v-bind:class="['active', 'line']">v-bind:class</h2>
    
  • 可以通过对象属性对应的布尔值来判断是否添加该样式。
    可以和普通的类可以同时存在。

    <!-- 当isActive为true时,将增加active和line两个class-->
    <h2 class="title" v-bind:class="{active: isActive, line: isActive}">v-bind:class</h2>
    
    <script>
        const vm = new Vue({
            el:"#app", //用于挂载要管理的元素
            data:{//定义数据
            	isActive: true
            }
        });
    </script>
    
  • 如果过于复杂,可以放在一个methods或者computed中。

    <h2 :class="getclasses()">getclasses</h2>
    <script>
       const app = new Vue({
            el:"#app",
            data:{
                isActive:true,
            },
            methods: {
                getclasses: function(){
                    return {active: this.isActive};
                }
            }
        });
    </script>
    

[3].动态绑定style

利用v-bind:style来绑定css内联样式,在写css属性名的时候,比如font-size,可以使用驼峰法fontSize或短横线分割(记得使用单引号括起来)‘font-size’

绑定class有两种方式:对象语法和数组语法

<div id="app">
	<!--输出:<h2 style="font-size: 100px;">123</h2> -->
	<h2 v-bind:style="{fontSize: finalSize}">123</h2> 
	<!--输出: <h2 style="font-size: 100px;">123</h2> -->
	<h2 v-bind:style="[finalSize2]">123</h2>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
   const vm = new Vue({
        el : '#app',
        data : {
            finalSize: '100px',
            finalSize2: {'font-size': '100px'},
        }
    });
</script>

[4].动态绑定属性

  • 在某些情况下,我们属性的名称可能也不是固定的,可以使用 :[属性名]=“值” 的格式来定义
  <div id="app">
   <~<h2 data-id="123">123</h2>
    <h2 v-bind:[attrname]="attrValue">123</h2> 
  </div>
  
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.13/vue.js"></script>
  <script>
     const vm = new Vue({
          el: '#app',
          data: {
            attrname: 'data-id', // 不支持大写, vue3支持
            attrValue: 123,
          }
      });
  </script>

[4].绑定一个对象中的所有属性

v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值

<div id="app">
    <!-- 最后渲染结果:<h2 name="张三" age="32" height="180">123</h2> -->
    <h2 v-bind="person">123</h2> 
  </div>
  
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.13/vue.js"></script>
  <script>
     const vm = new Vue({
          el: '#app',
          data: {
            person:{
              name: '张三',
              age: 32,
              height: 180
            }
          }
      });
  </script>

(三) 事件监听 v-on

v-on:可以简写为@

[1]. 基本使用

  • 点击按钮实现counter加和减
    <div id="app">
      <h2>{{counter}}</h2>
       <button v-on:click="counter++">+</button>
       <button v-on:click="counter--">-</button>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
       const vm = new Vue({
           el : '#app',
           data : {
               counter: 0
           }
       });
    </script>
    
  • 监听图片加载
    <!-- 图片加载完成触发自定义方法imgLoadfuinish() -->
    <img src="../img.png" @load="imgLoadFinish()">
    

[2]. 事件监听传参 v-on

  1. methods中定义的方法不需要额外的参数,那么v-on:方法名后可以不添加()

  2. 如果在组件中使用v-on绑定方法而没有传递参数,那么会默认将原生事件event当作参数传递进去。

  3. 如果需要传递某个参数的同时也需要event时,可以通过$event传入事件。
    1). vue的event是原生的
    2). 事件被挂载到当前元素

  4. methods中的方法中的this为vue实例。

  5. $event是一个占位符,可以在参数的任意位置。

    <div id="app">
       <h2>{{counter}}</h2>
        <button @click="add">+1</button>
        <button @click="addNum($event, 10)">+10</button>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const vm = new Vue({
            el : '#app',
            data : {
                counter: 0
            },
            methods :{
                add(event){
                	//event 是原生的
                    console.log(event.target);
                    this.counter++; 
                },
                addNum(event, num){
                    console.log(event);
                    this.counter += num;
                }
            }
        });
    </script>
    

[3]. v-on事件修饰符

  • 注:修饰符可以多个连续使用:
    <div @click="divClick">
    	<button @click.stop.prevent="btnClick">按钮</button>
    </div>
    
  1. .stop阻止事件冒泡

    <!--触发btnClick事件,不会触发divClick事件 -->
    <div @click="divClick">
    	<button @click.stop="btnClick">按钮</button>
    </div>
    
  2. .prevent 阻止元素的默认事件

    <!--触发preventClick事件的同时,页面不会跳转 -->
    <a href="http://www.baidu.com" @click.prevent="preventClick">百度一下 你就知道</a>
    
  3. .{keyCode | keyAlias} 按下指定按键时触发

    1. 常用的按键别名:
      回车 (enter),删除(delete),退出(esc),空格(space),换行(tab,必须配合keydown事件使用),上(up),下(down),左(left),右(right)
       
    2. 系统修饰键:ctrl、alt、shift、meta
      (1). 配合keyup事件使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才会被触发
      (2). 配合keydown事件使用:正常触发事件
       
    3. 可以使用keyCode去指定具体的按键(不推荐)
       
    4. Vue.config.keyCodes.自定义键名 = 键码; 自定义自己的按键别名。
    <!-- 松开键盘任意按键时触发 -->
    <input type="text" @keyup="keyClick">
    <!-- 松开回车键时触发 -->
    <input type="text" @keyup.enter="enter">
    <!-- 松开字母a键时触发(不区分大小写) -->
    <input type="text" @keyup.65="aClick">
    <!-- 组合键ctrl + y 时触发 -->
    <input type="text" @keyup.ctrl.y ="aClick">
    
  4. .once事件只触发一次事件

    <button @click.once="once">once</button>
    
  5. .native 监听子组件事件
    当子组件cpn发生点击事件时触发父组件的parentReceive方法

    <!-- 1.父组件 -->
    <div id="app">
        <!-- 2.子组件中 -->
        <cpn @click.native="parentReceive"></cpn>
    </div>
    

    尝试监听一个类似 <input> 的非常特定的元素时,有时会监听不到。因为<base-input> 组件的根元素实际上是一个 <label> 元素,父级的 .native 监听器将静默失败。它不会产生任何报错,但是 onFocus 处理函数不会如你预期地被调用。

    解决方案:https://cn.vuejs.org/v2/guide/components-custom-events.html#%E5%B0%86%E5%8E%9F%E7%94%9F%E4%BA%8B%E4%BB%B6%E7%BB%91%E5%AE%9A%E5%88%B0%E7%BB%84%E4%BB%B6

    <div id="app">
        <base-input v-on:focus.native="onFocus"></base-input>
    </div>
    
    <template id="base-input">
    	<!--  添加上label标签后将监听不到input标签的focus事件 -->
        <label for="">{{info}}<input type="text" v-model="info"></label>
    </template>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const baseInput = {
            template:"#base-input",
            data () {
                return {
                    info: ''
                }
            },
        }
    
        const app = new Vue({
            el: "#app",
            
            methods: {
                onFocus(){
                    console.log('获得焦点');
                }
            },
            components: {
                baseInput
            }
        });
    </script>
    
  6. .capture 内部元素触发的事件先在此处理,然后再交由内部元素处理

    <div v-on:click.capture="doThis">...</div>
    
  7. .self 只当在 event.target 是当前元素自身时触发处理函数,即事件不是从内部元素触发的

    <div v-on:click.self="doThat">...</div>
    
  8. .passive 事件的默认行为立即执行,无需等待事件回调函数执行完毕。

    <!-- 当滚动滚轮时,先执行滚轮行为,再执行doSome函数 -->
    <div @wheel.passive="doSome">...</div>
    

(四) 条件判断 v-if

  • 当 v-if 的条件为false时,v-if 包含的元素,不会存在于DOM中
    <p v-if="score >= 90">
        优秀
    </p>
    <p v-else-if="score >= 60">
        及格
    </p>
    <p v-else>
        不及格
    </p>
    

(五) 显示元素 v-show

  • 当需要在显示与隐藏之间切换很频繁时,使用v-show
  • 当只有一次切换时,通常使用v-if
  • 当isShow为false时,给元素的样式display改为none
    <div v-show="isShow">显示元素</div>
    

(六) 遍历数组(对象) v-for

  • 遍历数组
    <li v-for="(value, index) in arr" :key="(value + index)">{{index + '-' + value}}</li>
    
  • 遍历对象
    key和index可以省略
    <li v-for="(val, key, index) in obj" :key="val.id">{{index + '-' + key + ':' + val}}</li>
    
  • 在使用v-for时,给对应的元素或组件添加上一个:key属性(默认使用序号index作为:key的值),key的主要作用是更高效的更新虚拟dom (使用了虚拟DOM对比算法)。
  • 使用key来给每个节点做一个唯一标识符,key不推荐使用random或者index,可以使用id或对象的key
  • v-for 和 v-if 不能一起使用, v-for 比 v-if 的计算优先级高

(八) 表单绑定v-model

v-model 双向绑定:数据可以从data流向页面,也可以从页面流向data。

[1].v-model基础绑定

  1. v-model其实是一个语法糖,背后的本质是包含两个操作:

    • v-bind绑定一个value属性
    • v-on指令给当前元素绑定input事件
    <input type="text" v-model="message">
    <!-- 等同于 -->
    <input type="text" v-model:value="message">
    <!-- 等同于 -->
    <input type="text" v-bind:value="messge" v-on:input="message = $event.target.value">
    
  2. v-model 绑定在模板上

    • 为了让它正常工作,这个组件内的 必须:
      • 将其 value attribute 绑定到一个名叫 value 的 prop 上
      • 在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出
    <custom-input v-modal="searchText"></custom-input>
    <!-- 等价于 -->
    <custom-input v-bind:value="searchText" v-on:input="searchText = $event"></custom-input>
    
    Vue.component('custom-input', {
      props: ['value'],
      template: `
        <input
          v-bind:value="value"
          v-on:input="$emit('input', $event.target.value)"
        >
      `
    })
    

[2]. v-model结合radio

  • 即使radio标签中的name属性省略,也能互斥同一组radio标签。

  • 如果给变量sex了一个默认值(如“男”),vue会自动选中和value属性默认值一样标签

    <div id="app">
       <label for="man">
       		<!-- 默认选中该选项 -->
           <input type="radio" name="" id="man" v-model="sex" value=""></label>
       <label for="woman">
           <input type="radio" name="" id="woman" v-model="sex" value=""></label>
       <p>你选择的性别是:{{sex}}</p>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
       const vm = new Vue({
           el:"#app",
           data:{
               sex:'男'
           },
       });
    </script>
    

[3]. v-model结合checkbox

  • 没有配置checkbox的value属性,那么收集的就是checked属性(勾选 or 未勾选,是布尔值)

    <div id="app">
      <label>
      	<!-- 如果勾选则 agree的值为true -->
        <input type="checkbox" v-model="agree">是否同意协议
      </label>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      const vm = new Vue({
        el: "#app",
        data: {
          agree: '', //选中时,值为true(也可以给一个默认值false,表示刚开始不选择)
        },
      });
    </script>
    
  • 配置input的value属性

    • (1). v-model的初始值为非数组,那么收集的就是checked属性(勾选 or 未勾选,是布尔值)
    • (2). v-model的初始值为数组,则收集的就是value属性值组成的数组
    <div id="app">
        <input type="checkbox" name="" id="" v-model="hobbies" value="篮球">篮球
        <input type="checkbox" name="" id="" v-model="hobbies" value="足球">足球
        <input type="checkbox" name="" id="" v-model="hobbies" value="羽毛球">羽毛球
        <p>爱好为:{{hobbies}}</p>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const vm = new Vue({
            el:"#app", 
            data:{
                hobbies: ['篮球'] //hobbies需为数组,默认选中篮球
            },
        });
    </script>
    

[4]. v-model结合select

<div id="app">
  <!-- 1.单选下拉框,默认选中香蕉 -->
    <select name="" id="" v-model="fruit">
        <option value="香蕉">香蕉</option>
        <option value="苹果">苹果</option>
        <option value="葡萄">葡萄</option>
    </select>
    <p>你选择的水果是:{{fruit}}</p>
    <!-- 2. 多选下拉框 -->
    <select name="" id="" v-model="fruits" multiple>
        <option value="香蕉">香蕉</option>
        <option value="苹果">苹果</option>
        <option value="葡萄">葡萄</option>
    </select>
    <p>你选择的水果是:{{fruits}}</p>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            fruit:'香蕉',
            fruits: []
        },
    });
</script>

[5]. v-model修饰符

  1. .lazy修饰符
  • 默认情况下,v-model默认是在input事件中同步输入的数据的。也就是说,一旦数据发生改变对应的data中的数据就会自动发生改变。

  • lazy修饰符可以让数据在失去焦点或者回车时才更新。

    <input type="text" v-model.lazy="text">
    <p>文本框内容:{{text}}</p>
    
  1. number修饰符
  • 默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理,但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。

  • .number修饰符可以在输入框中输入的内容自动转换为数字类型

    <input type="number" v-model.number="number">
    <p>文本框内容:{{number}} - {{typeof number}}</p>
    
  1. trim修饰符
  • trim修饰符可以过滤输入内容左右两边的空格。

    <input type="tex" v-model.trim="trim">
    <p>文本框内容:{{trim}}</p>
    

(九) 自定义v-module双向绑定数据

https://cn.vuejs.org/v2/guide/components-custom-events.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6%E7%9A%84-v-model

v-model 双向绑定实际上就是通过子组件中的 $emit 方法派发 input 事件,父组件监听 input 事件中传递的 value 值,并存储在父组件 data 中;然后父组件再通过 prop 的形式传递给子组件 value 值,在子组件中绑定 input 的 value 属性即可。

[1]. 自定义v-model 1

<div id="app">
    <p>{{content}}</p>
    <cpn v-model="content"></cpn>
</div>

<template id="cpn">
    <input type="text" :value="content" @input="$emit('change1', $event.target.value)">
</template>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    const cpn = {
        template: '#cpn',
        model: {
            prop: 'content',//props中的content与该名字要一致
            event: 'change1'//触发父组件的事件
        },
        props: {
            content: {
                type: String,
                default () {
                    return ''
                }
            }
        }
    };

    const vm = new Vue({
        el: "#app",
        data:{
            content:''
        },
        components: {
            cpn,
        }
    });
</script>

[2]. 自定义v-model 2

<div id="app">
       <p>{{content}}</p>
       <cpn @change1="value=>content = value" :message="content"></cpn>
   </div>

   <template id="cpn">
       <input type="text"  @input="$emit('change1', $event.target.value)" :value="message">
   </template>

   <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
   <script>
       const cpn = {
           template:'#cpn',
           props:{
               message:{
                   type:String,
               }
           }
       }
       const app = new Vue({
           el: "#app",
           data: {
               content: ''
           },
           components: {
               cpn,
           },
       });
   </script>

[3]. v-module和.sync

从 2.3.0 起我们重新引入了 .sync 修饰符,但是这次它只是作为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on 监听器。

<div id="app">
  <p>{{bar}}</p>
    <!-- <cpn :content="bar" @update:content="val=> bar = val"></cpn> -->
    <!-- 等同于 -->
    <cpn :content.sync="bar"></cpn>
</div>

<template id="cpn">
    <input type="text" :value="content" @input="$emit('update:content', $event.target.value)">
</template>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    const cpn = {
        template: '#cpn',
        props: {
            content: {
                type: String,
                default () {
                    return ''
                }
            }
        }
    };

    const app = new Vue({
        el: "#app",
        data: {
            bar: ''
        },
        components: {
            cpn,
        },
    });
</script>

二、 计算属性computed

  • 计算属性有缓存,计算属性会把函数执行一次,把结果存起来,依赖的data改变,会重新计算。

  • 原理:底层截住了Object.defineproperty方法提供的getter和setter

  • methods和computed有时都可以实现我们的功能,但是计算属性会进行缓存,如果多次使用时,计算属性只会调用一次。

    <div id="app">
        <h2>{{fullName}}</h2>
        <h2>{{fullName2}}</h2>
        <button @click="updateFullName('张 三')">点击修改</button>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
    //在浏览器控制台运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。
    
    const vm = new Vue({
      el: '#app',
      data: {
        firstName: 'Kobe',
        lastName: 'Bryant',
      },
      computed: {
        //计算属性方法一:完整版
        fullName: {
          set: function (newValue) {
            //有set方法而方法体为空时表示是只读属性
            console.log('fullName setter方法被调用');
            let arr = newValue.split(' ');
            this.firstName = arr[0];
            this.lastName = arr[1];
          },
          get: function () {
            //get被调用:1.初次读取fullName时执行 2.所依赖的数据发生变化时
            return this.firstName + ' ' + this.lastName;
          }
        },
        //方法二:简写版 (只读不改时),相当于只有getter函数
        fullName2: function () {
          return this.firstName + ' ' + this.lastName;
        }
      },
      methods: {
        updateFullName(val) {
          this.fullName = val;
        }
      },
    });
    </script>
    

三、过滤器 filters

  • 过滤器就是一个函数。格式{{要过滤的值 | 过滤器函数 [| 过滤器函数2] }}
  • v-bind也可以使用过滤器。
<div id="app">
   <h2>{{price | showPrice('我是参数')}}</h2>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app", 
        data:{
            price: 88
        },
        filters:{
            showPrice(val, str){  //str非必有
            	//str 就是 '我是参数'
                return '¥' + val.toFixed(2);
            }
        }
    });
</script>
//定义全局过滤器capitalize
Vue.filter('capitalize', function (value) {
  if (!value) return ''
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

四、对象、数组添加和删除属性并实现响应式

https://cn.vuejs.org/v2/guide/reactivity.html#%E5%A6%82%E4%BD%95%E8%BF%BD%E8%B8%AA%E5%8F%98%E5%8C%96

由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。

(一)对象添加和删除属性的响应式

  • Vue 无法检测 property 的添加或移除。(由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。)
  • 使用 Vue.set(target, property, value)或vm.$set(target, property, value)新增属性,并且为新添加的属性实现响应式。
  • 使用 Vue.delete(target, property)或vm.$delete(target, property)删除属性,删除时也能实现响应式。
    <div id="app">
        <ul>
            <li v-for="(item,index) in obj" :key="index">{{item}}</li>
        </ul>
        <button @click="setObj">更改姓名</button>
        <button @click="addAttr">添加属性</button>
        <button @click="delAttr">删除属性</button>
        <button @click="addAttr2">添加属性2</button>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const vm = new Vue({
            el: "#app", 
            data: { 
                obj: {
                    name: '张三',
                    age: 123
                }
            },
            watch: {
                obj(newValue, oldValue) {
                    console.log(newValue);
                    console.log(oldValue);
                }
            },
            methods: {
                setObj() {
                    //更改属性值,是响应式的
                    this.obj.name = '李四'
                },
                addAttr() {
                    //添加属性,不是响应式的
                    this.obj.sex = '男'
                },
                delAttr() {
                    //删除属性,不是响应式的
                   delete this.obj.age;
                },
                addAttr2(){
                    //方式一:添加单个属性,是响应式的
                    Vue.set(this.obj, 'school', '阳光小学');
                    //方式二:添加单个属性,是响应式的,this就是vm
                    this.$set(this.obj, 'class', '五年级三班');
                    //方式三:添加多个属性,是响应式的
                    this.obj = Object.assign({}, this.obj,{a:1,b:2})
                    //方式四:删除属性,是响应式的,this就是vm
                   this.$delete(this.obj, 'sex')
                }
            },
        });
    </script>
    

(二)数组添加和删除的响应式

  • 对数组进行push()、pop()、shift()、unshift()、splice()、sort()、reverse()操作可以触发vue的响应式。(vue重写了这七个方法,实现响应式)

  • Vue.set(target,index, value), 对数组也管用。

  • Vue 不能检测以下数组的变动:

    • 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
    • 当你修改数组的长度时,例如:vm.items.length = newLength
    <div id="app">
      <ul>
          <li v-for="item in arr">
            {{item}}
          </li>
          <button @click="setArr">修改年龄</button>
          <button @click="addAttr">添加学校</button>
          <button @click="setLength">改变数组长度</button>
          <button @click="setAttr2">更改数组</button>
          <button @click="setLength2">改变数组长度</button>
          <button @click="arrPush">使用push</button>
      </ul>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const vm = new Vue({
            el: "#app", 
            data: { 
              arr:['张三','12岁']
            },
            methods: {
                setArr(){
                    //非响应式
                    this.arr[1] = '11岁';
                },
                addAttr(){
                    //非响应式
                    this.arr[2] = '阳光小学';
                },
                setLength(){
                    //非响应式
                    this.arr.length = 1;
                },
                setAttr2(){
                    //响应式
                    //方式一:Vue.set(vm.items, indexOfItem, newValue)
                    Vue.set(this.arr, 0, '李四');
                    //方式二: Array.prototype.splice
                    //删除年龄,并用新年龄代替删除的
                    this.arr.splice(1,1,'13岁')
                    //方式三:是方式一的另一种写法,this就是vm
                    this.$set(this.arr, 2, '阳光小学')
                },
                setLength2(){
                    this.arr.splice(1);
                },
                arrPush(){
                    //响应式
                    this.arr.push('三班')
                }
            }
           
        });
    </script>
    

(三)$nextTick

  • 语法:this.$nextTick(回调函数)

  • 作用:在下一次DOM更新结束后执行其指定的回调。

  • 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作

  • Vue是异步渲染,data改变之后,DOM不会立刻渲染。

  • 因为 $nextTick() 返回一个 Promise 对象,所以你可以使用 async/await 语法,参考:https://v2.cn.vuejs.org/v2/guide/reactivity.html

    <div id="app">
        <div id="example" ref="msg">{{message}}</div>
        <button @click="updateMessage">更新msg</button>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
    const vm = new Vue({
      el: "#app",
      data: {
        message: "未更新!",
      },
      methods: {
        updateMessage() {
          this.message = '已更新'
          console.log(this.$el);//<div id="app">...</div>
          console.log(this.$refs.msg);//<div id="example">...</div>
    
          console.log('结果1', this.$refs.msg.textContent) // => '未更新'
          this.$nextTick(function () { //异步调用
            console.log('结果2', this.$refs.msg.textContent) // => '已更新'
          })
          console.log('结果3', this.$refs.msg.textContent) // => '未更新'
        }
      }
    });
    </script>
    

五、监听属性 watch

https://cn.vuejs.org/v2/api/#vm-watch

(一)watch

  • Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据(data中的属性和计算属性)变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。

  • 注意:在变更 (不是替换) 对象或数组时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变更之前值的副本。

    <div id="app">
    <p>{{message}}</p>
    <p>所在城市:{{info.city}}, 温度:{{info.weather}}</p>
    <p>numbers.a:{{numbers.a}}</p>
    <button @click="setMessage">修改message</button>
    <button @click="setInfo">修改城市</button>
    <button @click="changeWeather()">修改天气</button>
    <button @click="numbersItemAAdd()">numbers.a+1</button>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
    const vm = new Vue({
      el: "#app",
      data: {
        message: "hello,world!",
        isFlag: true,
        info: {
          city: '北京'
        },
        numbers: {
          a: 1,
          b: 2
        }
      },
      computed: {
        weather() {
          return this.isFlag ? '炎热' : '寒冷';
        }
      },
      methods: {
        setMessage() {
          this.message = '你好'
        },
        setInfo() {
          this.info.city = '上海'
        },
        numbersItemAAdd() {
          this.numbers.a++;
        },
        changeWeather() {
          this.isFlag = !this.isFlag;
        }
      },
      watch: {
        //方法名称要与data中的变量一致,方法有两个参数一个是新值,一个是旧值(newValue [, oldValue])
        message(newValue, oldValue) {
          console.log(newValue); // 你好
          console.log(oldValue); // hello,world!
        },
        info: {
          //引用类型需要深度监听
          handler(newVal, oldVal) {
            // 引用类型,拿不到oldVal,因为指针相同,此时已经指向了新的val
            console.dir(newVal); // {city:"上海"}
            console.dir(oldVal); // {city:"上海"}
          },
          deep: true //深度监听,如果不添加则监听不到info的变化
        },
        weather(newVal, oldVal) {
          //监听计算属性
          console.log('weather发生变化:', newVal, oldVal);
        },
        "numbers.a"(newVal, oldVal) {
          //监听多层级结构中的某个属性变化
          console.log('numbers.a发生变化:', newVal, oldVal);
        }
      }
    });
    </script>
    

(二)handler方法和immediate属性

watch 的一个特点是,最初绑定的时候是不会执行的,要等到 firstName 改变时才执行监听计算。如果需要最初绑定的时候就执行该需要添加immediate属性

watch: {
  firstName: {
    handler(newName, oldName) {
       console.log(this.fullName = newName + ' ' + this.lastName);
    },
    // 代表在wacth里声明了firstName这个方法之后立即先去执行handler方法
    immediate: true
  }
}

(三) deep属性

为了发现对象内部值的变化(默认只能监听一层),可以在选项参数中指定 deep: true。注意监听数组的变更不需要这么做。

(四) vm.$watch

使用实例的vm.$watch(监听的属性名,配置项)方法,也可以实现监听。

vm.$watch('message', {
  //方法名称要与data中的变量一致,方法有两个参数一个是新值,一个是旧值(newValue [, oldValue])
  handler(newValue, oldValue) {
    console.log(newValue); // 你好
    console.log(oldValue); // hello,world!
  },
  deep: true 
})
vm.$watch('message', function(newValue, oldValue) {
  console.log(newValue); // 你好
  console.log(oldValue); // hello,world!
})

六、数据代理

数据代理:通过一个对象代理对另一个对象属性的操作

(一) Object.defineProperty()

在这里插入图片描述

var person = {
    name: '张三',
    age: 13,
}

var gender = '男';

Object.defineProperty(person, 'sex', {
    // value: '男',
    // enumerable: true, //是否可以枚举,默认为false
    // writable: true, //是否可以修改,默认为false
    // configurable: true, //是否可以删除,默认为false

    get: function () { //读取person的sex属性时触发,get函数整体被称为(getter)
        console.log('person的sex属性被读取');
        return gender;
    },
    set: function (val) { //设置person的sex属性时触发,set函数整体被称为(setter)
        console.log('person的sex属性被设置,设置的值为', val);
        gender = val;
    }

});

(二) Vue中的数据代理

vue通过Object.defineProperty()把data对象中的所有属性添加到vm实例上。

//vue通过使用vm._data代理data中的数据
const vm = new Vue({
   el: "#app",
   data: {  //data对象
       message: "hello,world!",
       cn: "你好,世界!",
       count: 100
   }
});

(三)简单实现vue监听对象

  • 简单实现vue监听对象数据(不能实现深度监听)
    var data = {
     name: '张三',
      age: 15,
      sex: '男'
    }
    
    function Obsever(data) {
      var keys = Object.keys(data);
    
      keys.forEach((item) => {
        // this为Obsever实例
        Object.defineProperty(this, item, {
          get() {
            console.log(item + '被读');
            return data[item];
          },
          set(val) {
            console.log(item + '被写');
            data[item] = val;
          }
        })
      });
    }
    
    
    var obs = new Obsever(data);
    
    var vm = {};
    vm._data = data = obs;
    console.log(vm._data)
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值