Vue全家桶学习笔记(一)——Vue的基本概念与操作

918 阅读8分钟

写在前面

Vue全家桶的学习笔记参考了尚硅谷的Vue框架教学视频Vue官方教学文档

Vue介绍

Vue是一个js库,它的关键词是渐进式:Vue的库分为核心库和插件库,核心库能满足基本需求。插件库是用户需要用到时自行添加,这是一种“渐进”的概念。Vue库的作用在于——动态构建前端界面:将后台数据在前台动态显示渲染。

Vue与其他js框架的关联

  1. 借鉴了angular的模板数据绑定技术
  2. 借鉴了react的组件化虚拟DOM技术

Vue的特点

image.png

MVVM模式

何为MVVM?即M(数据model)+V(视图view)+VM(Vue实例对象ViewModel)。结合官方的起步教程来说明MVVM模式元素所指:

image.png Vue具体的MVVM模式如下图:

image.png DOM监听和数据绑定实现了View到Model的读写和Model到View的读写

Vue的插件

image.png

Vue的基本使用

模板语法

双大括号表达式

双大括号表达式用于文本插值,在html文本中使用{{value}}可以将js变量value插入html元素中,它的使用例子如下:

        <p>{{name}}</p>
        <p>{{name.toUpperCase()}}</p>
        <p v-text="name"></p> 
        <p v-html="name"></p><!-- 相当于innerHTML和innerText区别,前者能将值解析成html -->

强制数据绑定

强制数据绑定可以建立DOM元素的**属性(attribute)**与vm之间的桥梁,如下:

        <h2>2 强制数据绑定</h2>
        <img v-bind:src="imgUrl">
        <img :src="imgUrl">

imgUrl为vm中的data中的用户定义的js变量,保存了图片的src地址。

绑定事件监听

绑定事件监听可以给DOM元素绑定事件监听(如点击事件等):

        <h2>3 绑定事件监听</h2>
        <button v-on:click="test"></button>
        <button @click='test2(msg)'></button>

test为vm中的methods中的用户定义的js方法。

计算与监听

计算属性

当我们需要将data中的变量进行计算得到新的变量值的时候,我们会用到vm中的computed属性。在computed中定义方法,返回值是我们想要变量计算之后得到的值,在相应的变量进行改变时,会触发计算属性。例子如下(全名=姓+名):

    <div id="demo">
        姓:<input v-model="lastName" type="text"><br>
        名:<input v-model="firstName" type="text"> <br>
        姓名:<input v-model="fullName" type="text"><br>
    </div>
        var vm = new Vue({
            el: '#demo',
            data: {
                firstName: 'kobe',
                lastName: 'bryant',
            },
            computed: {
                fullName() {
                    return this.firstName + ' ' + this.lastName
                }
            }
        })

计算属性vs方法

我们可以将同一函数定义为一个方法而不是一个计算属性,在上图所示例子中,我们可以将fullName函数定义在methods组件中,可以达到一样的效果。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。计算属性存在缓存中,只在相关响应式依赖发生改变时它们才会重新求值。因此在重新渲染时,计算属性所需要的资源会更少。

监视

同上面的例子,我们也可以通过监视(watch) 来实现,通过监视某个变量的改变进行回调,他们实现的效果可以是一样的,使用例子如下:

    <div id="demo">
        姓:<input v-model="lastName" type="text"><br>
        名:<input v-model="firstName" type="text"> <br>
        姓名(单向2):<input v-model="fullName1" type="text"><br>
    </div>
        var vm = new Vue({
            el: '#demo',
            data: {
                firstName: 'kobe',
                lastName: 'bryant',
                fullName1: 'kobe bryant',
            },
            watch: {
                firstName: function (newval, oldval) {
                    this.fullName1 = newval + ' ' + this.lastName
                },
                lastName: function (newval, oldval) {
                    this.fullName1 = this.firstName + ' ' + newval
                }
            }
        })

计算属性的getter与setter

计算属性默认只有 getter,不过在需要时也可以提供一个setter回调,它在计算属性改变时立即调用:

            computed: {
                fullName2: {
                    get() {
                        return this.firstName + ' ' + this.lastName
                    },
                    set(value) {

                        this.firstName = value.split(' ')[0]
                        this.lastName = value.split(' ')[1]
                    }
                }
            }

通过计算属性的getter和setter,我们可以很方便完成相关联变量的双向改变。

class与style的绑定

操作元素的 class 列表和内联样式是数据绑定的一个常见需求。在将 v-bind 用于 class 和 style 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。

class的绑定

class的绑定是动态的,它可以与html的class(静态)相结合,我们可以用静态指定固定的样式,用class绑定实现class的动态改变,它们不会覆盖而是会合并。如:

        <p class="fontSize" v-bind:class="Pcolor">这是一段话</p>

字符串表达式

一个简单的例子,点击按钮切换class:

    <div id="color">
        <h2>style的变化</h2>
        <p v-bind:class="Pcolor">这是一段话</p>
        <button @click='update'>更新</button>
    </div>
        var ColorVm = new Vue({
            el: '#color',
            data: {
                Pcolor: 'redFonts',
            },
            methods: {
                update() {
                    this.Pcolor = 'blueFonts'
                }
            }
        })

对象表达式

一个例子:

        <p :class='ObjClass'>class绑定了一个js对象</p>
            data: {
                Pcolor: 'redFonts',
                ObjClass: {
                    fontSize: true,
                    redFonts: true //fontSize和redFonts为class名
                }
            },

数组表达式(用的较少)

一个例子:

<div v-bind:class="[activeClass, errorClass]"></div>
data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}

style绑定

例子:

<div v-bind:style="styleObject"></div>
data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

条件渲染

v-if和v-else-if和v-else

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true 值的时候被渲染,v-else-if和v-else需要搭配v-if使用。

<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else-if="horrible">Vue is horrible!</h1>
<h1 v-else>Oh no 😢</h1>

v-show

另一个用于根据条件展示元素的选项是 v-show 指令。用法大致一样:

<h1 v-show="ok">Hello!</h1>

两种条件的区别

v-if的隐藏方式是在DOM中移除元素,v-show的隐藏方式是给元素添加display=none。

列表渲染

v-for可以实现列表的渲染,一个例子,需求如下:用v-for实现渲染一个列表,列表的每个li标签内部含有用户的索引、名字和年龄,以及删除按钮和更新按钮(将当前li的信息更新为指定的信息)。实现过程如下:

    <div id="demo">
        <h2>v-for遍历数组</h2>

        <ul>
            <li v-for="(person,index) in persons">
                {{index}}----{{person.name}}----{{person.age}}
                ---<button @click='del(index)'>删除</button>
                ---<button @click='update(index)'>更新</button>
            </li>
        </ul>
    </div>
    <script>
        new Vue({
            el: '#demo',
            data: {
                persons: [
                    { name: 'Tom', age: 16 },
                    { name: 'Jack', age: 17 },
                    { name: 'Rose', age: 18 },
                    { name: 'Bob', age: 19 },
                ],
                newPerson: {
                    name: 'Daddy',
                    age: '40'
                }
            },
            methods: {
                del(index) {
                    this.persons.splice(index, 1)
                },
                update(index) {
                    this.persons[index] = this.newPerson
                }
            },
        })
    </script>

但是更新功能无法实现!通过vue调试工具,发现点击更新按钮后虽然Persons数组内部的值已经变化了,但是在DOM中并没有响应更新!难道之前学的是错的?原来响应式变化需要检测到变量名的指向发送改变之后才能响应,在这个例子中,Persons数组的内部发生了改变:this.persons[index] = this.newPerson,但是Person的指向还是原数组,因此这条语句不会触发视图改变。

But...为啥删除功能就有用呢?明明splice也只是删除了原数组内部的值,并没有返回新数组。翻看vue文档说明,发现这牵涉到了数组更新检测问题

数组更新检测问题

变更方法

Vue 将被侦听的数组的变更方法进行了包装,所以它们也将会触发视图更新。这些被包裹过的方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse() 因此回到例子中,我们将update的代码修改: this.persons.splice(index, 1, this.newPerson),搞定!

替换数组

上述方法对数组进行了变更,但是也有直接替换数组的办法——这些方法返回了新数组,如filter()、concat() 和 slice() 。你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。幸运的是,事实并非如此。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的启发式方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。

v-for也可以遍历对象,如下:

        <h2>v-for遍历对象</h2>
        <ul>
            <li v-for="(item,index) in persons[0]" :key="index">
                {{index}}----{{item}}
            </li>
        </ul>

key属性的作用

其中被绑定的每个key值都唯一,在v-for中添加key是为了性能提升,当v-for绑定的数组插入新的值导致页面重新渲染和数组时,没有key值相当于对数组做插入操作,虚拟DOM在进行diff比较时会渲染索引改变的数组元素;而增加key之后该数组就相当于链表,插入效率明显提高。

列表的搜索和排序

这里跟着尚硅谷的教程敲了一个案例:

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

<body>
    <div id="demo">
        <input type="text" v-model="searchName">
        <ul>
            <li v-for="(person,index) in Filterpersons">
                {{index}}----{{person.name}}----{{person.age}}
            </li>
        </ul>
        <button @click='setOrderType(1)'>年龄升序</button>
        <button @click='setOrderType(2)'>年龄降序</button>
        <button @click='setOrderType(0)'>原本顺序</button>
    </div>
    <script>
        var input = document.querySelector('input')
        new Vue({
            el: '#demo',
            data: {
                searchName: '',
                persons: [
                    { name: 'Tom', age: 16 },
                    { name: 'Jack', age: 17 },
                    { name: 'Rose', age: 18 },
                    { name: 'Bob', age: 15 },
                    { name: 'Jessica', age: 14 }
                ],
                orderType: 0,//0代表正常排序,1代表升序排序,2代表降序排序
            },
            computed: {
                Filterpersons() {
                    //所需要的变量
                    const { searchName, persons, orderType } = this
                    //搜索框过滤
                    let fpersons = persons.filter(person => person.name.indexOf(searchName) >= 0)
                    //排序处理
                    if (orderType !== 0) {
                        orderType == 1 ? fpersons.sort(function (a, b) {
                            return a.age - b.age;
                        }) : fpersons.sort(function (a, b) {
                            return b.age - a.age;
                        })
                    }

                    return fpersons
                }
            },
            methods: {
                setOrderType(type) {
                    this.orderType = type;
                },

            }
        })
    </script>
</body>

</html>

但是有一个小bug暂时不明白,当我把排序的代码放在搜索框过滤之前时,排列成原本顺序的功能会失效?,也就是这样:

                Filterpersons() {
                    //所需要的变量
                    const { searchName, persons, orderType } = this
                    let fpersons = persons
                    
                    if (orderType !== 0) {
                        orderType == 1 ? fpersons.sort(function (a, b) {
                            return a.age - b.age;
                        }) : fpersons.sort(function (a, b) {
                            return b.age - a.age;
                        })
                    }

                    return fpersons.filter(person => person.name.indexOf(searchName) >= 0)
                }

事件处理

绑定监听

绑定监听在之前有写过,这里要介绍一下事件监听时的event参数如何传入。当监听回调函数没有传入参数时:

<button @click='test'>test</button>
                test(event) {
                    alert(event.target.innerHTML)
                }

否则需要在html代码处用 $event传参:

<button @click='test(number,$event)'>test</button>
                test(number,event) {
                    alert(number,event.target.innerHTML)
                }

事件修饰符

之前在学原生js的DOM事件处理方法解决阻止默认事件和阻止冒泡阶段,一般会用到even.preventDefault()和event.stopPropagation(),而Vue提供了更加简洁的方案——事件修饰符!如下:

        <h2>事件修饰符</h2>
        <div style='width: 200px;height: 200px;background-color: yellow;' @click='Outer'>
            <div style='width: 100px;height: 100px;background-color: blue;' @click.stop='Inner'>
            </div>
        </div>
        <a href="www.baidu.com" @click.prevent>这个链接不会跳转</a>

Vue提供的事件修饰符如下:

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive

按键修饰符

在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:

<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">

懒人福音,再也不用去js里面写判断条件了...

表单的输入绑定

对于表单元素——text/textarea,radio(单选),checkbox(多选),select(下拉列表),可以利用v-model来创建双向数据绑定,例子如下:

<body>
    <form action="" id="demo">
        <span>用户名:</span>
        <input type="text" name="" id="" v-model="usrname">
        <span>密码:</span>
        <input type="text" name="" id="" v-model="pwd">
        <span>性别:</span>
        <input type="radio" value="女" id="female" v-model='sex'>
        <label for="female"></label>
        <input type="radio" value="男" id="male" v-model='sex'>
        <label for="male"></label>
        <input type="submit" value="提交" @click.prevent='onSubmit'>
    </form>
    <script>
        new Vue({
            el: '#demo',
            data: {
                usrname: '',
                pwd: '',
                sex: '',
            },
            methods: {
                onSubmit(event) {
                    alert(this.$data.toString())
                }
            }
        })
    </script>
</body>

其实v-model本质上是两个命令的结合:v-bind:value + v-on:input

v-model的修饰符

简单介绍一下v-model的三个修饰符:lazy/number/trim,他们的使用方法如下:
<input type="text" name="" id="" v-model.lazy="usrname">

  • lazy:输入失焦后才更新数据
  • number:限制输入为数字
  • trim:自动剔除输入首尾的空格

Vue的生命周期

拿了官方给的示例图,可以把生命周期分为三个部分:初始化、更新、死亡。红色圆角方框部分代表了生命周期中调用的回调函数,可以在Vue实例中定义,官方称其为生命周期钩子。常用的钩子有mounted和beforeDestroy。 image.png