01.【Vue】Vue2基础操作

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

一、Vue

Vue (读音 /vjuː/类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层不仅易于上手还便于与第三方库或既有项目整合。另一方面当与现代化的工具链以及各种支持类库结合使用时Vue 也完全能够为复杂的单页应用提供驱动。

二、Vue实例创建

2.1 Vue.js代码引用

Vue的官网提供了开发版本生产版本两个版本的源码下载这里需要注意的是生产版本去掉了相关错误的警告信息对于调试程序会造成不便。所以在开发环境中需要引入开发版本的Vue.js。

同时也可以使用CDN引用Vue.js

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

2.2 Vue Devtools调试工具

为了能够更好的观察Vue在页面中的作用Vue官方推荐了Vue Devtools的调试工具。该工具为Chrome插件可以在Chrome官方插件平台下载也可以下载GitHub源码通过编译添加至Chrome浏览器中。

2.2.1 下载

在本地下载Vue Devtools的源码

git clone https://github.com/vuejs/devtools.git

2.2.2 编译

进入项目路径打开cmd执行npm install进行安装。完成后执行npm run bulid命令编译项目。完成后进入./shells/chrome/打开文件mainifest.json修改persistent属性为true

"background": {
  "scripts": [
    "build/background.js"
  ],
  "persistent": true
}

2.2.3 安装

打开谷歌浏览器打开【扩展程序】打开【开发者模式】点击【加载已解压的扩展程序】选择Vue Devtools项目中的shells/chrome文件夹点击添加即可将Vue Devtools插件部署至Chrome浏览器中。


使用node编译项目比较费时也可以从网上获取编译好的内容例如其他博主提供的资源https://github.com/arcliang/Vue-Devtools-

2.3 创建实例

按照官网提供的示例编写一个简单的Vue示例

<div id="root">
  <!-- 插值语法用{{}}获取Vue对象中的数据可以使用JS表达式作为值 -->
  <h1>Hello,{{name}}</h1>
</div>
<script src="../js/vue.js"></script>
<script type="text/javascript">
  // 设置Vue全局属性禁用生产环境提示信息
  Vue.config.productionTip = false;

  // 创建Vue对象Vue对象与容器一一对应不能一对多或多对一
  // 在真实开发过程中只有一个Vue对象并且会会伴随着多组件使用
  new Vue({
    // 为当前Vue对象绑定对应的容器值通常使用选择器
    el: '#root',
    // 为该容器设置所需要的数据值一般使用对象存储数据
    data: {
      name: 'Tom'
    }
  });
</script>

三、Vue基础语法

3.1 模板语法

根据2.3的示例我们可以看出Vue可以通过特定的语法将Vue实例中的数据代入到页面中这种操作符合HTML的模板基础可以在Vue渲染中对特别的语法位置进行解析在Vue中被称为模板语法。

3.1.1 插值

使用{{ propertyName }}的形式可以将Vue实例中的data的属性直接注入到HTML中。例如2.3中的示例。

在Vue中对于插值语法插入的数据是可以动态刷新的当data中的数据发生变化时可以进行自动刷新。

可以在Vue Devtools中进行验证。

示例

在插件中修改属性的值可以立刻反馈到页面上。

3.1.2 指令

指令语法通常用于为DOM绑定事件或赋予属性。示例

    <div id="root">
        <h1>插值语法</h1>
        <p>hello, {{name}}</p>
        <hr>
        <h1>指令语法</h1>
        <a v-bind:href=urlTemp.url>链接1</a>
        <a :href=urlTemp.url>{{urlTemp.name}}</a>
    </div>
    <script src="../js/vue.js"></script>
    <script type="text/javascript">
        Vue.config.productionTip = false;

        new Vue({
            el:'#root',
            data:{
                name:'Jack',
                urlTemp:{
                    url:'http://www.baidu.com',
                    name:'百度'
                }
            }
        })
    </script>

在Vue中的指令语法通常都以v-开头常用的指令有v-bindv-on等也可以使用简写方式。v-bind可以直接省略在属性前使用:即可v-on可以简写为@

插值语法主要作用于标签体的内容填充使用js表达式

指令语法主要作用于标签标签属性标签体内容绑定事件可以调用Vue中的众多指令

3.2 数据绑定

在Vue实例中创建的数据可以通过插值语法与v-bind在页面进行渲染这里需要注意的是v-bind只能将data中的数据赋予页面中。如果需要实现页面上修改的值同步至Vue实例的data中可以使用v-model指令因此v-model是可以为DOM的数据进行双向绑定。

    <div id="root">
        单向绑定<input type="text" :value=name /><br>
        双向绑定<input type="text" v-model=name />
    </div>
    <script src="../js/vue.js"></script>
    <script type="text/javascript">
        Vue.config.productionTip = false

        new Vue({
            el:'#root',
            data:{
                name:'Tom'
            }
        })
    </script>

只有带有value值的表单元素才能够使用双向绑定

3.3 el和data的两种写法

在Vue实例中示例

<div id="root1">
        <h1>Hello, {{name}}</h1>
    </div>
    
    <div id="root2">
        <h1>Hello, {{name}}</h1>
    </div>

    <div id="root3">
        <h1>Hello, {{name}}</h1>
    </div>

    <script src="../js/vue.js"></script>
    <script>
        Vue.config.productionTip = false;

        // 绑定容器
        // 1. el直接在创建Vue实例时绑定
        new Vue({
            el: '#root1',
            data: {
                name: 'Tom'
            }
        });

        // 2. 将Vue实例挂载在容器上
        const vm = new Vue({
            data: {
                name: 'Jack'
            }
        });
        vm.$mount('#root2');

        // 绑定数据
        // 1. 对象式直接使用对象示例略

        /* 2. 函数式(只能使用普通函数形式箭头式函数形式获取的对象是全局对象不能够使用
            data: () => {
                console.log(this)
                return {
                    name: 'Mary'
                }
            }
        */
        new Vue({
            el: '#root3',
            data: function () {
                console.log(this)
                return {
                    name: 'Mary'
                }
            }
        })

    </script>

在Vue实例中this是指定的当前Vue对象如果使用箭头函数此时的this为全局对象不再特指当前Vue对象

四、数据代理

Vue对数据的绑定核心就是数据代理我们虽然在Vue对象中创建了属性data但是在页面获取data中的属性值时是以代理的形式获取。

    <div id="root">
        <h1>Hello, {{ name }}</h1>
        <h1>Hello, {{ address }}</h1>
    </div>
    <script src="../js/vue.js"></script>
    <script>
        /**
         * 1. Vue实例中的data属性会处理为_data使用数据劫持实现当数据发生改变时动态刷新页面的功能
         * 2. Vue实例中的data属性Vue会自动对其进行代理当调用vm.name时则调用_data的getter方法
         */
        const vm = new Vue({
            el: '#root',
            data: {
                name:'Tom',
                address:'三里屯'
            }
        })
    </script>

可以在页面控制台中输出vm对象可以直接看到data中的nameaddress并不是直接显示值而是需要点击省略号才能获取它的值这就是数据代理。只有在需要时才会去获取属性值。

vm对象的下方也可以看到对nameaddress两个属性的gettersetter方法。

五、事件

5.1 事件

对DOM元素绑定事件时一般使用v-on:[event]='fun'也可以简写为@[event]='fun'对DOM中绑定的事件方法可以写在Vue的methods属性中。

示例

    <div id="root">
        <!-- 
            1. 点击事件使用v-on绑定事件v-on:click="show"也可以简写为@click="show"
            2. show1为Vue实例中的方法这里注意做调用的函数需要写在methods属性中否则无效
            3. 当函数不存在参数时默认拥有一个事件参数(Event)
            4. methods中的函数要使用普通函数的写法不能写成箭头函数否则this的值则会指向windows而不是vm
         -->
        <button @click="show1">Click me 1</button>
        <button @click="show2($event, 'Jack')">Click me 2</button>
    </div>

    <script src="../js/vue.js"></script>
    <script>
        const vm = new Vue({
            el:'#root',
            data: {
                name:'Tom'
            },
            methods:{
                show1(event) {
                    console.log(event)
                    alert('Hello, Tom')
                },
                show2(event, name) {
                    console.log(event)
                    alert('Hello ' + name)
                }
            }
        })
    </script>

这里需要注意的是调用函数的参数如果函数没有参数可以直接在事件绑定中直接写函数名即可。如果存在参数那么默认约定第一个参数为当前事件对象。

5.2 修饰符

修饰符描述
prevent在事件中添加修饰符prevent可以阻止默认事件对于链接则阻止链接的跳转
stop当嵌套结构都有事件时可以通过添加stop属性阻止事件的冒泡
once当只想当前事件执行一次时可以添加once属性当执行一次后就不会再执行
capture在嵌套结构中每层的事件会由外而内依次捕获之后再由内而外的依次冒泡使用capture可在捕获时执行事件
self在嵌套结构中使用self作用于事件上只有操作当前元素时才会执行该元素上的事件

事件的修饰符可以叠加使用

stop和self都可以有效阻住嵌套结构中的事件冒泡

示例

    <div id="root">
        <h1>事件修饰符</h1>
        <hr>

        <!-- 01.在事件中添加修饰符prevent可以阻止默认事件对于链接则阻止链接的跳转 -->
        <a :href=url @click.prevent="showInfo">点我跳转至百度</a>
        <hr>

        <!-- 02.当嵌套结构都有事件时可以通过添加stop属性阻止事件的冒泡 -->
        <div class="demo" @click="showMsg(1)">
            <button @click.stop="showMsg(2)">点击我实现冒泡</button>
        </div>
        <hr>

        <!-- 03.当只想当前事件执行一次时可以添加once属性当执行一次后就不会再执行 -->
        <button @click.once="showInfo">点我打开弹窗</button>
        <hr>

        <!-- 04.在嵌套结构中每层的事件会由外而内依次捕获之后再由内而外的依次冒泡使用capture可在捕获时执行事件 -->
        <div class="box1" @click.capture="showMsg(1)">
            div1
            <div class="box2" @click="showMsg(2)">
                div2
            </div>
        </div>
        <hr>

        <!-- 05.在嵌套结构中使用self作用于事件上只有操作当前元素时才会执行该元素上的事件 -->
        <!-- 注意stop和self都可以有效阻住嵌套结构中的事件冒泡 -->
        <div class="box1" @click.self="showMsg(1)">
            div1
            <div class="box2" @click="showMsg(2)">
                div2
            </div>
        </div>
        <hr>

        <!-- 06. passive属性事件默认执行无需等待事件的回调执行完毕已失效 -->
        <div class="scroll" @scroll="scroll">
            <ul>
                <li>1</li>
                <li>1</li>
                <li>1</li>
                <li>1</li>
                <li>1</li>
                <li>1</li>
                <li>1</li>
                <li>1</li>
                <li>1</li>
                <li>1</li>
            </ul>
        </div>

        <!-- 注意事件修饰符可以叠加使用 -->
    </div>

    <script src="../js/vue.js"></script>
    <script>
        const vm = new Vue({
            el:'#root',
            data:{
                url:'http://www.baidu.com'
            },
            methods:{
                showInfo(event) {
                    alert('正在打开百度搜索')
                },
                showMsg(msg) {
                    console.log(msg)
                    alert('正在执行点击操作')
                },
                scroll() {
                    for(let i = 1; i < 10000; i++) {
                        console.log('#')
                    }
                    console.log("over")
                }
            }
        })
    </script>

5.3 键盘事件

示例

    <!-- 
        1. 键盘捕获事件主要分为两类keyup和keydown分别为键盘弹起事件和键盘按下事件
        2. 如果需要通过某个按键触发事件可以使用特定的键值修饰符
            回车=>enter
            删除=>delete(包括delete键和Backspace键)
            退出=>esc
            空格=>space
            制表符=>tab
            上=>up
            下=>down
            左=>left
            右=>right
        3. 对于Vue未提供别名的按键可以使用键的原始Key值进行绑定需要注意多单词需要使用kebab-case
        4. 系统修饰键ctrl,shift,alt,meta这些系统按键需要特殊使用
            4.1 使用keyup时系统修饰键可以搭配其他按键触发
            4.2 使用keydown可以直接触发
        5. 也可以直接使用键值替代不推荐
        6. 使用Vue.config.keyCodes.自定义键名 = 键值可以实现自定义键值

        注意可以使用组合键限制条件
     -->
    <div id="root">
        <h1>键盘捕获事件</h1>
        <hr>
        <input type="text" name="" id="" @keyup.enter="outputInfo"/>
    </div>

    <script src="../js/vue.js"></script>
    <script>
        Vue.config.keyCodes.huiche = 13
        const vm = new Vue({
            el:'#root',
            methods:{
                outputInfo(event) {
                    console.log(event.target.value)
                }
            }
        })
    </script>

六、计算属性与侦听

6.1 计算属性

如果需要根据其他数据经过逻辑处理后获取最后的结果一是可以使用插值语法使用JS表达式这样做会使得页面臃肿不好维护与复用。二是使用方法在每次渲染页面的时候就会加载该方法如果处理逻辑复杂的话会影响加载速度。以上两种方法虽然可以实现预期但是还并不简洁。

Vue提供了computed属性用于对复杂的属性进行计算。例如

<div id="example">
  <p>
    <input type="text" v-model=message />
  </p>
  <p>
    result:{{ reversedMessage }}
  </p>
</div>
const vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    reversedMessage: {
      get() {
        console.log('调用getter方法')
        return this.message.split('').reverse().join('')
      },
      set(val) {
        console.log('调用setter方法', val)
        this.message = val
      }
    }
  }
})

通过附加reversedMessage属性并添加该属性的gettersetter方法在属性被调用或修改时进行调用。

getter方法在属性调用时执行执行后的结果会被保留在缓存中再次调用时并不会再访问该方法除了初次访问会调用外当该属性所依赖的数据发生变化时也会触发该方法。

setter方法在该属性发生变化时执行。

在控制台输出vm.reversedMessage时可以看到并没有再调用getter方法。

一般情况下用到计算属性时更多的使用获取该属性的结果此时就可以省略setter方法作一种简写模式例如

const vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    reversedMessage: function() {
      console.log('调用getter方法')
      return this.message.split('').reverse().join('')
    }
  }
})

简写模式要在不需要setter方法的前提下使用而且要注意reversedMessagevm对象的一个属性而不是方法

6.2 侦听

Vue提供了watch属性用于侦听数据的变化例如

<div id="example">
  <h1>天气{{ info }}</h1>
  <button @click="changeWeather">切换天气</button>
</div>
const vm = new Vue({
  el: '#example',
  data: {
    isHot: true
  },
  methods: {
    changeWeather() {
      console.log("切换isHot的值")
      return this.isHot = !this.isHot
    }
  },
  computed: {
    info: function() {
      return this.isHot ? '晴天' : '阴天'
    }
  },
  watch: {
    isHot: {
      // 页面渲染时加载handler的方法
      immediate: true,
      // 当被监视的属性发生变化时就会调用该函数计算属性也可被监视
      handler(newValue, oldValue) {
        console.log('切换isHot的值', newValue, oldValue)
      }
    }
  },
})

通过点击按钮调用changeWeather函数改变isHot的值。当isHot的值发生改变时就会触发watch中的逻辑操作。其中watch属性中还拥有许多属性例如immediate当页面渲染时就加载handler方法。

6.2.1 深度侦听

对于多级结构数据如果需要实现对结构中的数据进行监听可以对该数据对象添加deep属性开启深度侦听例如

<div id="example">
  <h1>a:{{nums.a}}</h1>
  <button @click="nums.a++">a+1</button>
  <h1>b:{{nums.b}}</h1>
  <button @click="nums.b++">b+1</button>
</div>
const vm = new Vue({
  el: '#example',
  data: {
    nums: {
      a: 1,
      b: 2
    }
  },
  watch: {
    // 监听nums中多级属性的变化
    // 若只需要监听多级数据中的某个属性的变化可以使用'.'连接例如'nums.a'需要加引号
    nums: {
      // deep属性可以开启Vue的watch属性对数据中多级属性变化的监听
      deep: true,
      handler() {
        console.log('nums发生变化')
      }
    }
  },
})

6.2.2 侦听的简写

如果在watch中没有多余的配置时可以通过直接构建函数简写侦听过程例如

<div id="example">
  <h1>天气{{ info }}</h1>
  <button @click="changeWeather">切换天气</button>
</div>
const vm = new Vue({
  el: '#example',
  data: {
    isHot: true
  },
  methods: {
    changeWeather() {
      return this.isHot = !this.isHot
    }
  },
  computed: {
    info: function() {
      return this.isHot ? '晴天' : '阴天'
    }
  },
  watch: {
    // 如果被监听的对象没有其他的属性附加可以使用简写的方法即将处理逻辑写成一个函数
    isHot: function(newValue, oldValue) {
      console.log('切换isHot的值', newValue, oldValue, this)
    }
  }
})

6.2.3 绑定侦听

除了在Vue实例中创建watch属性外还可以使用$watch为Vue对象绑定侦听。

vm.$watch('isHot', {
  // 页面渲染时加载handler的方法
  immediate: true,
  // 当被监视的属性发生变化时就会调用该函数
  handler(newValue, oldValue) {
    console.log('切换isHot的值', newValue, oldValue)
  }
})

在不需要其他配置时可以使用简写

vm.$watch('isHot', function(newValue, oldValue) {
  console.log('切换isHot的值', newValue, oldValue, this)
})

6.3 计算属性与侦听

计算属性能够实现的侦听也可以实现但是侦听能够实现的计算属性不一定能够实现而且侦听能够实现异步操作。

当需要创建非Vue管理的函数时一般创建箭头函数及()=>{}因为箭头函数没有自己的this可方便的访问Vue本身的内容。

computed能实现的功能watch也能实现。watch能够实现的功能computed却不一定能实现watch能够实现异步操作computed需要一个返回值而watch只是需要实现一个方法所以watch更加灵活。

七、样式绑定

7.1 绑定class样式

Vue除了可以对数据进行绑定还可以为元素绑定样式。同样是使用v-bind:class可以简写为:class

7.1.1 对象语法

Vue可以通过布尔值控制元素的样式是否展示示例

<div id="root">
    <div class="basic" :class="classObj">Hello World</div>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        classObj: {
            fontSize: true,
            bgBisque: false,
            textAlign: true
        }
    }
})

结果渲染为

<div id="root">
    <div class="basic fontSize textAlign">Hello World</div>
</div>

当我们修改classObj中的元素的值时可以控制样式是否启用。

7.1.2 数组语法

除了可以为元素绑定对象外还可以以数组的形式为元素添加样式。示例

<div id="root">
    <div class="basic" :class="arr">
        Hello World
    </div>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        arr: ['fontSize', 'textAlign']
    }
})

经过渲染后的结果为

<div id="root">
    <div class="basic fontSize textAlign">
        Hello World
    </div>
</div>

7.1.3 字符串语法

也可以直接使用字符串示例

<div id="root">
    <div class="basic" :class="fontSize">
        Hello World
    </div>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        fontSize: 'fontSize'
    }
})

经过渲染后的结果为

<div id="root">
    <div class="basic fontSize">
        Hello World
    </div>
</div>

7.2 绑定style样式

在Vue中我们也可以使用内联样式通过在data中定义对象可以将对象的值渲染为内联的style样式。这里使用v-bind:style可以简写为:style。示例

<div id="root">
    <div class="basic" :style="styleObj">
        Hello World
    </div>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        styleObj: {
            fontSize: '24px',
            backgroundColor: 'red'
        }
    }
})

渲染后的结果为

<div id="root">
    <div class="basic" style="font-size:24px;background-color:red;">
        Hello World
    </div>
</div>

一般对于确定样式需要灵活控制是否使用时可以使用绑定class的对象语法

若不确定样式的内容需要添加删减时可以使用绑定class的数组语法

一般若样式名称不确定可以使用绑定class的字符串语法

八、条件渲染

8.1 v-if

Vue提供了v-if属性可以控制元素是否展示如果是多个结果判断的可以使用v-if v-else-ifv-else的组合该组合可以理解为if-else if-else它的值为布尔值或可以得出布尔值的表达式。示例

<div id="root">
    <h2>n: {{n}}</h2>
    <button @click="n++">n++</button>

    <h2 v-if="n === 1">Angular</h2>
    <h2 v-else-if="n === 2">React</h2>
    <h2 v-else-if="n === 3">Vue</h2>
    <h2 v-else>其他</h2>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        n: 1
    }
})

可以通过修改n的值来显示不同的li标签。

如果需要控制是否显示某一段代码还可以使用<template></template标签进行包裹。示例

<template v-if="result">
    <h2>Angular</h2>
    <h2>React</h2>
    <h2>Vue</h2>
</template>
const vm = new Vue({
    el: '#root',
    data: {
        result: false
    }
})

8.2 v-show

v-showv-if有着相同的效果也可以根据值的布尔值来控制元素是否显示但不同的是v-show一般只是简单的切换元素的display属性同时也不支持多结果判断和组件。示例

<div id="root">
    <h2 v-show="result">{{desc}}</h2>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        result: false,
        desc: '标题1'
    }
})

九、列表渲染

9.1 v-for

Vue提供了v-for属性控制元素的循环输出该元素可用于渲染对象、数组以及字符串也可以控制循环的次数。

9.1.1 渲染数组

<div id="root">
    <h2>个人信息</h2>
        <ul>
            <li v-for="(person, index) in persons">{{person.name}}-{{person.age}}-{{index}}</li>
        </ul>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        persons: [
            {
                id: 'S001',
                name: 'Tom',
                age: 18
            },
            {
                id: 'S002',
                name: 'Jack',
                age: 20
            },
            {
                id: 'S003',
                name: 'Mark',
                age: 23
            }
        ],
    }
})

渲染结果为

  • Tom-18-0
  • Jack-20-1
  • Mark-23-2

v-for可以携带2个参数第一个参数为对象的值对于数组来说第二个参数默认为该项的索引值。

9.1.2 渲染对象

<div id="root">
    <h2>学生信息</h2>
    <ul>
        <li v-for="(v, k) in student">{{k}}-{{v}}</li>
    </ul>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        student: {
            grade: '高三',
            class: '一班',
            name: '张三'
        },
    }
})

渲染效果为

  • grade-高三
  • class-一班
  • name-张三

其中k代表了对象中的属性值也就是键值针对于对象还可以携带第三个参数及对象的索引值例如v-for="(v, k, i) in student"其中i为索引值。

9.1.3 渲染数组

<div id="root">
    <h2>字符信息</h2>
    <ul>
        <li v-for="(c, i) in desc">{{c}}-{{i}}</li>
    </ul>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        desc: 'Hello World'
    }
})

渲染结果为

  • H-0
  • e-1
  • l-2
  • l-3
  • o-4
  • -5
  • W-6
  • o-7
  • r-8
  • l-9
  • d-10

其中i的值为该项的索引值。

9.2 v-for的key

v-for属性中还伴随着另一个属性为每一次循环的元素绑定一个key值及v-bind:key="key"可以简写为:key。如果数据项的顺序被改变Vue 将不会移动 DOM 元素来匹配数据项的顺序而是就地更新每个元素并且确保它们在每个索引位置正确渲染。因此如果是循环一个对象最好使用一个唯一主键作为key值这样可以将key值与数据进行绑定避免拥有子元素时更新发生错位的情况。

例如我们在每次循环时为循环内容添加一个input标签并创建一个点击按钮可以新增一个元素。

<div id="root">
    <h2>个人信息</h2>
    <button @click="add">点击添加人员</button>
    <ul>
        <li v-for="(person, index) in persons" :key="index">
            {{person.name}}-{{person.age}}-{{index}}
            <input type="text">
        </li>
    </ul>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        persons: [
            {
                id: 'S001',
                name: 'Tom',
                age: 18
            },
            {
                id: 'S002',
                name: 'Jack',
                age: 20
            },
            {
                id: 'S003',
                name: 'Mark',
                age: 23
            }
        ]
    },
    methods: {
        add() {
            const p = {
                id: 'S004',
                name: 'Jary',
                age: 40
            }

            this.persons.unshift(p)
        }
    }
})

使用默认索引作为key值当在input标签中输入内容后再添加新的元素会发现input标签并没有与前边的元素绑定会出现错位。

这里就引入了Vue在更新元素内容时的虚拟DOM对比原理根据Vue的虚拟DOM对比算法,对于新增的元素,会根据KEY值找到原有的对象进而对比元素的内容对于不同的地方进行更新。因此如果使用索引作为key值在已有数据中插入数据时可能会对界面显示造成错位。

所以可以使用对象的唯一主键作为key值例如<li v-for="(person, index) in persons" :key="person.id">便可以避免这个问题具体原理如下图

渲染效果如下

9.3 列表过滤

当我们想将一个数组过滤并保持源数组不变可以通过计算属性和侦听来实现。

9.3.1 侦听

<div id="root">
    <h2>个人信息</h2>
    <input type="text" placeholder="请输入关键词" v-model="keyWord">
    <ul>
        <li v-for="(person, index) in filPersons" :key="person.id">
            {{person.name}}-{{person.age}}-{{person.sex}}
        </li>
    </ul>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        keyWord: '',
        persons: [
            {
                id: 'S001',
                name: '马冬梅',
                age: 18,
                sex: '女'
            },
            {
                id: 'S002',
                name: '周冬雨',
                age: 20,
                sex: '女'
            },
            {
                id: 'S003',
                name: '周杰伦',
                age: 23,
                sex: '男'
            }
        ],
        filPersons: []
    },
    watch: {
        keyWord: {
            // 页面渲染时加载handler的方法
            immediate: true,
            // 当被监视的属性发生变化时就会调用该函数计算属性也可被监视
            handler(val) {
                this.filPersons = this.persons.filter((p)=>{
                    return p.name.indexOf(val) !== -1;
                });
            }
        }
    },
})

使用侦听时我们可以监控输入框的变化因此定义keyWord属性用于绑定输入内容当开启侦听后调用filter方法筛选符合的内容。

这里需要注意indexOf方法当参数为空值时返回值为0

为了不影响原有数组的内容可以重新定义一个数组用于接收筛选后的结果。

immediate属性是必须要添加的当页面进行渲染时若还没有开始侦听那么filPersons则为空无法显示结果。

9.3.2 计算属性

new Vue({
    el: '#root',
    data: {
        keyWord: '',
        persons: [
            {
                id: 'S001',
                name: '马冬梅',
                age: 18,
                sex: '女'
            },
            {
                id: 'S002',
                name: '周冬雨',
                age: 20,
                sex: '女'
            },
            {
                id: 'S003',
                name: '周杰伦',
                age: 23,
                sex: '男'
            }
        ]
    },
    computed: {
        filPersons() {
            return this.persons.filter((p)=>{
                return p.name.indexOf(this.keyWord) !== -1
            })
        }
    }

})

使用计算属性则要相对简单些我们可以直接计算出来filPersons的值这样就省略了对输入框的监听。

9.4 列表排序

同样的我们如果需要多数组的某一个值进行排序也可以使用计算属性进行处理。示例

<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="(person, index) in filPersons" :key="person.id">
            {{person.name}}-{{person.age}}-{{person.sex}}
        </li>
    </ul>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        keyWord: '',
        sortType: 0,
        persons: [
            {
                id: 'S001',
                name: '马冬梅',
                age: 18,
                sex: '女'
            },
            {
                id: 'S002',
                name: '周冬雨',
                age: 25,
                sex: '女'
            },
            {
                id: 'S003',
                name: '周杰伦',
                age: 23,
                sex: '男'
            }
        ]
    },
    computed: {
        filPersons() {
            // 对数组进行筛选
            const arr = this.persons.filter((p)=>{
                return p.name.indexOf(this.keyWord) !== -1
            })

            // 对数组进行排序
            if (this.sortType) {
                arr.sort((p1, p2)=>{
                    return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age
                })
            }

            return arr;
        }
    }

})

在上一个案例的基础上我们增加了对筛选后的结果对年龄进行排序将结果作为返回值。

9.5 数据监测原理

通过在控制台打印Vue对象可以看到通过数据代理形成的_data对象中的每个属性都具有Getter/Setter方法而且每个属性的子属性也都具有Getter/Setter方法这是Vue实现数据监测的根本每个层级的属性变化时都都会触发setter方法换而言之只有数据被_data代理才能实现动态更新。

实现一个一层的数据监测示例

let arr = [{name: 'Tom', age: 18}];

// 模拟Vue的封装
const vm = {};
vm._data = new Observer(arr);

function Observer(obj) {
    // 获取对象的key值
    const keys = Object.keys(obj);
    
    // 遍历key值
    keys.forEach((k)=>{
        Object.defineProperty(this, k, {
            get() {
                console.log('获取对象值');
                return obj[k];
            },
            set(val) {
                console.log('设置对象值');
                obj[k] = val;
            }
        });
    });
}

Vue底层也是通过这种方法为所有的属性绑定Getter/Setter方法这种过程也被称为数据劫持只是Vue底层实现了多级的数据劫持

因此针对数组如果需要变更其内容就不能直接操作元素示例

<div id="root">
    <button @click="modify">修改元素内容</button>
    <ul>
        <li v-for="(v, k) in students" :key="v.id">{{ v.name }}</li>
    </ul>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        students: [
            {
                id: 'S001',
                name: '马冬梅',
                age: 18,
                sex: '女'
            },
            {
                id: 'S002',
                name: '周冬雨',
                age: 25,
                sex: '女'
            },
            {
                id: 'S003',
                name: '周杰伦',
                age: 23,
                sex: '男'
            }
        ]
    },
    methods: {
        modify() {
            this.persons[0] = {id: 'S001', name: '周迅', age: 40, sex: '女'}
        }
    }
})

点击按钮后数组的第一个元素并没有被修改就是因为上述的原因修改后的数据无法触发元素的setter方法所以无法被Vue代理因此数据无法更新。

这里Vue提供了数组的变更方法使用这些方法就可以使Vue更新他的数据并实现代理。这些方法包含

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

我们在控制台对students对象调用这些方法观察结果。

除了上述的方法还可以使用非变更的方法及filter(),concat(),slice()方法这些方法不会变更原始数组只是后返回一个新的数组就可以用新的数组直接替换旧数组一样可以触发动态更新数组。

9.5 Vue.set方法

根据上一节所阐述的内容单纯的操作data对象是无法实现数据的动态更新的因为直接操作的内容无法被Vue代理。因此Vue提供了Vue.set方法也可以使用全局方法vm.$set该方法可以为某一个属性添加新的属性。示例

<div id="root">
    <h2>姓名{{student.name}}</h2>
    <h2>年级{{student.grade}}</h2>
    <h2>班级{{student.class}}</h2>
    <h2 v-if="student.age">年龄{{student.age}}</h2>
    <button @click="addAge">点击添加年龄</button>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        student: {
            grade: '高三',
            class: '一班',
            name: '张三'
        }
    },
    methods: {
        addAge() {
            // Vue.set(vm.student, 'age', 18);
            vm.$set(this.student, 'age', 18);
        }
    }
})

set方法使用三个参数第一个参数为需要绑定的对象第二个对象为待绑定的属性第三个参数为绑定的值。

需要注意的是该方法不能为vm的根对象新增属性例如this.$set(vm, ‘age’, 18)

十、表单数据的收集

表单数据可以使用v-model进行绑定获取每个表单的value值。示例

<div id="root">
    <form>
        账号<input type="text" v-model.trim="personInfo.account">
        <br>
        密码<input type="password" v-model="personInfo.password">
        <br>
        性别
        <input type="radio" name="sex" value="male" v-model="personInfo.sex"><input type="radio" name="sex" value="female" v-model="personInfo.sex"><br>
        年龄<input type="number" v-model.number="personInfo.age">
        <br>
        爱好
        <input type="checkbox" value="study" v-model="personInfo.hobby">学习
        <input type="checkbox" value="game" v-model="personInfo.hobby">游戏
        <input type="checkbox" value="run" v-model="personInfo.hobby">跑步
        <br>
        所在城市
        <select v-model="personInfo.city">
            <option value="">请选择城市</option>
            <option value="beijing">北京</option>
            <option value="shanghai">上海</option>
            <option value="guangzhou">广州</option>
        </select>
        <br>
        简介
        <textarea v-model.lazy="personInfo.info"></textarea>
        <br>
        <input type="checkbox" v-model="personInfo.checked">已阅读条款
        <br>
        <button>提交</button>
    </form>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        personInfo: {
            account: '',
            password: '',
            sex: '',
            age: '',
            hobby: [],
            city: '',
            info: '',
            checked: ''
        }
    },
})

文本输入框可根据输入的值进行双向绑定。单选按钮可绑定value的值及勾选’男‘时sex的值为male。多选框则会将value值添加到数组中这里要在data初始化为数组。

这里需要说明的上述案例的修饰符

  • lazy 当输入框失去焦点后再进行数据绑定
  • number将输入的数据转为数字类型常与type="number"同时使用确保输入的数据为数字类型
  • trim去除字符串前后的空格在输入框中输入空格将会自动删除空格

十一、指令

11.1 内置指令

Vue除了上述章节介绍的v-bind,v-model,v-on等指令还提供了v-text,v-html,v-once,v-clock,v-pre指令。

11.1.1 v-text

v-text用于将对象的内容绑定在元素中与插值语法类似但是该指令会替换元素原有的值无法拼接字符串不如插值语法使用灵活。

<div id="root">
    <h2>name:{{ name }}</h2>
    <h2 v-text="name">name:Jack</h2>
</div>
new Vue({
    el: '#root',
    data: {
        name: 'Tom'
    }
})

使用插值语法会显示name:Tom的效果但是使用v-text指令会直接覆盖原有的内容及只会显示Tom

11.1.2 v-html

v-html用于将对象的内容绑定在元素中可以解析标签但是该指令会替换元素原有的值无法拼接字符串。

<div id="root">
    <h2 v-html="html"></h2>
</div>
new Vue({
    el: '#root',
    data: {
        html: '<a href>点击跳转</a>'
    }
})

在对象中使用的a标签会被解析到DOM中。

11.1.3 v-cloak

v-cloak常与CSS搭配使用用于当Vue加载缓慢时避免未经渲染的模板直接呈现在页面上。

<div id="root">
    <h2 v-cloak>{{ name }}</h2>
</div>
<style>
    [v-cloak] {
        display: none;
    }
</style>
new Vue({
    el: '#root',
    data: {
        name: 'Tom'
    }
})

v-cloak指令会在Vue加载完毕后把该属性删除因此在Vue加载前可以使用CSS将该元素隐藏等Vue渲染后就可以直接最后的结果。

11.1.4 v-once

v-once绑定的元素包含的对象只会渲染一次若修改该对象的值不会引起该指令绑定元素的结果。

<div id="root">
    <h2 v-once>n初始化值为:{{ n }}</h2>
    <h2>n:{{ n }}</h2>
    <button @click="n++">点击n+1</button>
</div>
new Vue({
    el: '#root',
    data: {
        n: 1
    }
})

当修改n值时第一个h2标签的值不会发生改变第二个h2标签会根据点击时间逐次加1。

11.1.5 v-pre

v-pre指令会禁用对DOM元素的渲染。

<div id="root">
    <h2 v-once>n初始化值为:{{ n }}</h2>
    <h2 v-pre>n:{{ n }}</h2>
    <button @click="n++">点击n+1</button>
</div>

第二个h2标签将无法再被Vue渲染会保留原始的状态。

11.2 自定义指令

除了上述的Vue内置的指令外还可以通过自定义指令来实现一些特殊的操作基础语法为v-[指令名称]。例如

该示例实现了两个功能点击按钮后将n*10第二个输入框实现在页面刷新时自动获取焦点并将n值填充在输入框中。

<div id="root">
    <h2>n={{ n }}</h2>
    <h2>n*10=<span v-big="n"></span></h2>
    <button @click="n++">点击n+1</button>
    <hr>
    <input type="text" v-focus:value="n">
</div>
const vm = new Vue({
    el: '#root',
    data: {
        n: 1
    },
    directives: {
        big: function (element, binding) {
            element.innerText = binding.value * 10
        },
        focus: {
            bind(element, binding) {
                console.log('DOM元素与指令绑定时执行')
                element.value = binding.value;
            },
            inserted(element, binding) {
                console.log('DOM元素开始渲染时执行')
                element.focus()
            },
            update(element, binding) {
                console.log('当指令所在模板更新时执行')
                element.value = binding.value;
            }
        }
    }
})

使用directives属性定义自定义指令自定义指令有两种写法一种是函数方法另一种是对象写法。

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

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

  • update所在组件的 VNode 更新时调用但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 。

每个方法都拥有两个参数

  • element指令所绑定的元素可以用来直接操作 DOM。
  • binding一个对象包含以下 property
    • name指令名不包括 v- 前缀。
    • value指令的绑定值例如v-my-directive="1 + 1" 中绑定值为 2
    • oldValue指令绑定的前一个值仅在 updatecomponentUpdated 钩子中可用。无论值是否改变都可用。
    • expression字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中表达式为 "1 + 1"
    • arg传给指令的参数可选。例如 v-my-directive:foo 中参数为 "foo"
    • modifiers一个包含修饰符的对象。例如v-my-directive.foo.bar 中修饰符对象为 { foo: true, bar: true }

函数方式简化了三个方法但是对于一些需要操作DOM的操作就需要使用到inserted方法。

将自定义指令写在模板之中后只能服务于该模板其他模板就无法调用该指令所以Vue提供了全局指令的用法。示例

Vue.directive('focus', {
    bind(element, binding) {
        console.log('DOM元素与指令绑定时执行')
        element.value = binding.value;
    },
    inserted(element, binding) {
        console.log('DOM元素开始渲染时执行')
        element.focus()
    },
    update(element, binding) {
        console.log('当指令所在模板更新时执行')
        element.value = binding.value;
    }
})

如果需要简化使用函数写法将对象替换为函数即可。

这里需要注意的是全局指令需要定义在创建实例之前该指令可以被所有的模板调用

十二、过滤器

过滤器可以对绑定数据进行处理一般使用在插值语法中示例{{ message | filter }}也可以用在v-bind中示例 <div v-bind:id="message | filter"></div>

示例

<div id="root">
    <h2>当前时间{{ time }}</h2>
    <h2>当前时间{{ time | timeFormat }}</h2>
    <h2>当前时间{{ time | timeFormat('YYYY年MM月DD日') }}</h2>
    <h2>当前时间{{ time | timeFormat('YYYY年MM月DD日') | slice }}</h2>
</div>
const vm = new Vue({
    el: '#root',
    data: {
        time: 1665395277123
    },
    filters: {
        timeFormat: function(value, format) {
            return dayjs(value).format(format);
        },
        slice: function (value) {
            return value.slice(0, 4);
        }
    }
});

定义过滤器的第一个参数固定为绑定的值且可以传递参数第二个参数开始就为过滤器所定义的参数且过滤器可以串联后一个过滤器的参数是前一个过滤器的返回值。

在多组件开发中一个组件的过滤器无法被另一个组件调用但Vue提供了全局的过滤器这种过滤器就可以被所有的组件调用。

示例

Vue.filter('timeFormat', function(value, format) {
    return dayjs(value).format(format);
})

这里要注意的是全局的过滤器需要被写在初始化实例前。

十三、生命周期

Vue的实例从创建到销毁有自己的生命周期Vue的生命周期分为4个阶段8个方法如下图

13.1 实例初始化

在初始化阶段拥有两个方法实例加载前和实例加载后在此期间会进行数据代理和数据监测及产生_data

在Vue中提供了两个方法beforeCreate()用于在初始化前调用此时的_data属性还没有进行加载created()用于初始化之后调用此时就可以调用Vue实例中的属性和方法。

示例

const vm = new Vue({
    el: '#root',
    data: {
        
    },
    methods: {
        
    },
    beforeCreate() {
        console.log('beforeCreate:初始化生命周期在初始化前执行', this);
        debugger
    },
    created() {
        console.log('created:完成初始化步骤实现了数据代理和方法', this);
        debugger
    },
})

加入debugger断点查看Vue的实例对象

通过两个阶段输出的this对象可以看到created阶段所有的数据和方法就已经被代理完毕。

13.2 挂载阶段

挂载阶段Vue会生成虚拟DOM并将虚拟DOM渲染到页面上。该阶段也提供了两个方法beforeMount()方法执行时Vue产生了虚拟DOM但还未在页面进行渲染所以会得到最原始的界面mounted()方法执行时就会将虚拟DOM渲染在页面上在此期间就可以进行开启定时器、发送网络请求等操作而且此时操作DOM是有效的。

示例

const vm = new Vue({
    el: '#root',
    data: {
        
    },
    methods: {
        
    },
    beforeMount() {
        console.log('beforeMount:完成虚拟DOM的创建但未开始渲染页面在此期间操作的DOM皆无效', this)
        debugger
    },
    mounted() {
        console.log('mounted:完成页面的渲染可以在此阶段进行DOM的操作不建议启动定时器和发送请求等操作', this)
        debugger
    },
})

beforeMount方法中操作DOM是无效的因为会被虚拟DOM直接覆盖。

效果如图

13.3 更新阶段

当数据发生了变更就会触发该阶段主要分为计算属性和重新渲染模板两步。该阶段也包含两个函数beforeUpdate()调用该函数时Vue完成了重新计算属性的工作但是页面还未重新渲染updated()调用该函数时Vue已完成了页面的渲染保持了属性和页面的一致性。

示例

const vm = new Vue({
    el: '#root',
    data: {
        
    },
    methods: {
        
    },
    beforeUpdate() {
        console.log('beforeUpdate:更新数据时触发此时数据已被更新但是页面还未渲染', this)
        debugger
    },
    updated() {
        console.log('updated:更新后页面渲染结束', this)
        debugger
    },
})

13.4 销毁阶段

当用户调用了vm.$destroy()方法后就会进入销毁阶段。该阶段包含两个方法beforeDestroy()该方法在Vue实例开始销毁前执行一般在此阶段停止定时器取消订阅和解除自定义时间的绑定destroy()方法调用时Vue实例就已被完全销毁。

const vm = new Vue({
    el: '#root',
    data: {
        
    },
    methods: {
        
    },
    beforeDestroy() {
        console.log('beforeDestroy:销毁实例对象前触发用于在销毁前关闭定时器、取消订阅消息、解绑自定义事件等操作', this)
        debugger
    },
    destroyed() {
        console.log('destoryed:销毁实例对象', this)
        debugger
    },
})

两个阶段输出的this对象可以看到created阶段所有的数据和方法就已经被代理完毕。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: vue