Vue 学习归纳

一、简介

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,易于上手。
Vue 有个特点是响应式,当数据发生变化时,视图会自动响应,元素会匹配新的数据。
在 JS 中,需要text = "hello world"; document.getElementById("id").innerHTML = text;才能改变元素的值;
而 Vue 中,只需要text = "hello world",元素的值就会自动改变。
参考了Vue教程Vue API

二、使用 Vue

  1. 使用html
    需要先使用<script></script>引入Vue,再在之后的<script></script>中使用new Vue()创建 Vue。
<body>
        <!-- 最新版本 -->
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <!-- 指定版本 -->
        <!-- <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script> -->
        <div id="div">
            
        </div>
        <script type="text/javascript">
            var vm = new Vue({
                el: "#div"
            })
        </script>
    </body>

el: "#div"表示将 Vue 挂载到id = "div"的元素上。

  1. 使用 main.js
    先使用npm install vue下载 Vue,在 main.js 中使用new Vue()初始化。
import Vue from 'vue'
import App from './App.vue'

new Vue({
    el: "#app",
    render: h => h(App)
})

App.vue是主页面,render: h => h(App)表示用主页面来渲染 Vue,el: "#app"表示将该 Vue 挂载到id="app"的元素上,该元素一般在 index.html 里。
注:通常使用第二种方式创建 Vue,界面代码写在.vue文件中,通过 Vue Router 跳转界面。

  1. 使用.vue文件
<template>
    <div>
    </div>
</template>

<script>
    export default {

    }
</script>

<style scoped="scoped">
</style>

.vue文件叫做单文件组件,<template></template>中为 html 语句。要在 div 中写界面代码,不然会报错,因为每个组件必须只有一个根元素。
<script></script>中为 JS 语句,export default { }中为 Vue 语句。
<style></style>中为 CSS 语句,通常添加scoped="scoped",表示样式只在当前组件中生效。
注:后面的介绍语法都是在单文件组件使用的,而不是官方教程那样在 html 中使用。

三、模板语法

1、修改文本
使用{{ }}为一个元素添加动态的内容。里面是一个变量,变量为data选项中的key。

<template>
    <div>
        <div>{{ name }}</div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                name: "zhangsan",
            };
        }
    }
</script>
  • 表达式
    {{ }}里面可以使用变量,也可以使用表达式。实际上,变量也是表达式。
    算术表达式,如{{ name + "lisi" }}
    三元表达式,如{{ isOk ? "zhangsan" : "lisi" }}isOk是一个值为布尔的变量。
    函数表达式,如{{ name.toUpperCase() }}
  • v-text
    可使用v-text指令来代替{{ }}插值的功能,如<div v-text="name"></div>相当于<div>{{ name }}</div>
  • v-once
    因为 Vue 的响应式,当变量name的值发生改变时,div 的内容会自动改变;
    若不想自动改变,可使用v-once指令,如<div v-once>{{ name }}</div>
    使用v-once指令后元素/组件及其子元素/组件只会渲染一次。
  • v-html
    变量的值通常是字符串、数字等,若想使用 html 语句,可使用v-html指令。
<template>
    <div>
        <div v-html="age"></div>
    </div>
</template>

<script>
    export default {
        data: function() {
            return {
                age: "<b>20</b>"
            };
        }
    }
</script>
<style scoped="scoped">
    b {
        color: red;
    }
</style>

修改了 DOM 中的element.innerHTML。此时相当于<div><b>20</b></div>
注:scoped的样式不能应用于v-html指令中的元素。如上面的 b 元素字体不会变为红色。

  • 响应式数据
    写在data选项中的数据就是响应式数据,选项是一个对象或返回对象的函数,可通过this.$data获取该对象。
    因为使用的是单文件组件,所以选项必须使用函数。
    插值时必须使用响应式数据,其它全局变量无效。
    使用this.$data.age获取数据,因为 Vue 实例代理了data选项中的所有变量,所以可使用this.age获取数据。
    有前缀_$的变量不会被 Vue 实例代理,所以只能使用this.$data._age获取数据。
    • 新增响应式数据
      若有一个响应式数据info: { title: "hello" },当使用this.info.content = "world"给对象添加一个新键值对时,该键值对并不是响应式的,{{ info.content }}是没有效果的。
      可使用this.$set()方法为对象新增响应式的键值对,如this.$set(this.info, "content", "world"),此时{{ info.content }}才有效果。
      $set()方法有三个参数:
      • 参数一为一个对象或数组,必须是响应式的。
      • 参数二为对象的键或数组的索引。
      • 参数三为数据的值。
    • 删除响应式数据
      使用this.info.title = nullthis.info.title = undefined删除一个数据。
      也可使用this.$delete()方法删除数据,能够保证 Vue 知道某数据被删除。
      方法有两个参数,与$set()方法的前两个参数相同。
  1. 修改属性
    在 html 中,通常元素的属性会赋值为字符串。使用v-bind指令,可为属性绑定一个表达式。
<template>
    <div>
        <div v-bind:title="detailAderss">{{ adress }}</div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                adress: "cd",
                detailAderss: "成都成华区"
            };
        }
    }
</script>

鼠标指着cd时


v-bind指令可简写成:,如<div :title="detailAderss">{{ adress }}</div>

  1. 指令
    指令(Directives)是带有v-前缀的特殊属性,如v-bind
    指令的值通常是一个表达式,除了v-for,如v-bind:title="detailAderss"后面的detailAderss
    指令也有参数,在:后面,如v-bind:title="detailAderss"后面的title就是指令的参数。
    查看全部指令
  • 动态参数
    使用[]为指令绑定一个变量,变量的值即为指令的参数。
<template>
    <div>
        <div v-bind:[attName]="detailAderss">{{ adress }}</div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                adress: "cd",
                detailAderss: "成都成华区",
                attName: "title"
            };
        }
    }
</script>

此时相当于<div v-bind:title="detailAderss">{{ adress }}</div>
指令参数的值通常是一个字符串,值为null则可以被显性地用于移除绑定。

  • 修饰符
    使用.为指令添加一个修饰符,修饰符用于指出指令应该实现某个功能,如v-on:click.prevent="".prevent修饰符的作用是让事件对象调用event.preventDefault()
    可同时添加多个修饰符,如v-on:click.prevent.once=""
    不同的指令有不同的修饰符,查看v-on修饰符

四、事件

  1. 绑定事件
    在 html 中,使用<div onclick="clickAction()"></div>来绑定 DOM 事件。
    在 Vue 中,使用v-on指令绑定 DOM 事件,如<div v-on:click="clickAction">

    • 要注意的是,要删除原事件前面的on,如onclick -> click
    • 当表达式是一个函数调用时,在 html 中要使用(),如onclick="clickAction()"
    • 而在 Vue 中可以不用,但当需要传入参数时,则必须添加(),如v-on:click="clickAction"、v-on:click="clickAction('a', 'b')"
    • 函数传入的参数可为$event,即为事件对象,如v-on:click="clickAction('a', $event)"

    v-on指令可简写成@,如<div @click="clickAction">

<template>
    <div>
        <div @click="clickAction">{{ funName }}</div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                funName: "click me"
            };
        },
        methods: {
            clickAction: function(a) {
                alert(event.type +" " + this.$data.funName)
            }
        }
    }
</script>

方法要写在methods选项中,它是一个对象,而每个方法则是对象中的一组键值对。
函数有两个写法,如clickAction: function() { }clickAction() { }
若函数有参数,则写成clickAction: function(a, b) { }clickAction(a, b) { }

  • 箭头函数
    函数还有一种写法是箭头函数,如clickAction: () => {}clickAction: (a, b) => {}
    若只有一个参数,可省略(),如clickAction: a => {}
    若函数里只有一句代码,可省略{},如clickAction: a => alert(a)
    注:在普通函数中,this指向 Vue 实例;但在箭头函数中,this不指向任何值。
  • 可使用对象
    在函数中,event对象表示函数的事件对象,this对象则表示当前的 Vue 实例。
    当未传入参数时,函数默认有一个传入参数,值为事件对象。
    与 JS 一样,函数中也能使用arguments对象,arguments[0]表示传入的第一个参数,arguments[1]表示传入的第二个参数,以至类推。
  1. 键盘事件
    使用@keydown、@keyup、@keypress绑定键盘事件,分别为按键按下、按键抬起、按键按下并抬起。
    在 JS 中,在函数中使用事件对象的keykeyCode属性判断按下了哪一个按键;在 Vue 中,使用修饰符判断按键,如<input @keyup.enter=""></input>,表示在按下回车键时触发。
  • 普通按键
    修饰符是事件对象的key属性的值通过kebab-case方式转换而来的,如Enter->enter、ArrowUp->arrow-up
    修饰符也可以使用数字,即事件对象的keyCode属性的值,如@keyup.enter=""相当于@keyup.13=""
    Vue 内置了一些修饰符:.enter .tab .delete (删除或退格键) .esc .space .up .down .left .right
<template>
    <div>
        <input @keyup.enter="keyupAction"></input>
    </div>
</template>

<script>
    export default {
        methods: {
            keyupAction: function() {
                alert(event.key + " " + event.keyCode)
            }
        }
    }
</script>

有些旧浏览器的keykeyCode的对应关系可能与新的不一致,可以使用全局属性Vue.config.keyCodes修改,如Vue.config.keyCodes.f1 = 112

  • 粘滞键
    在 JS 中,通过事件对象的ctrlKey、altKey、shiftKey、metaKey属性判断是否按下了这几种按键。
    在 Vue 中,使用修饰符.ctrl .alt .shift .meta判断是否按下,如@keyup.shift.enter="",表示按下shift+enter时触发。
    注:在不同系统中,alt、meta键可能不代表的按键不同,需要具体测试。
  • 精准按键
    使用修饰符.exact判断是否精确按下了按钮,如@keyup.shift.enter.exact=""只有在按下shift+enter时触发,按下shitf+alt+enter时不会触发。
  1. 鼠标事件
    在 JS 中,使用事件对象的button属性判断哪个鼠标按键被按下,012分别表示左键、中键、右键。
    在 Vue 中,使用修饰符.left .right .middle判断哪个鼠标按键被按下,如@click.right=""

  2. 绑定多个方法
    v-on指令的值可以是一个对象,可用于绑定多个方法,如v-on="{ click: clickAction, dblclick: dblclickAction }"
    但这种方式不支持修饰符,也不支持给函数传入参数。

五、计算属性

模板语法插值时,可向里面传入一个表达式,表达式可为一个变量,可为一个方法,也可为一个计算属性。
<div>{{ name }}</div>name是一个变量,值通常是一个简单的值,如{name: "zhangsan"}
<div>{{ getName() }}</div>getName()是一个方法,可在函数里实现复杂的运算,最后返回一个值。

      methods: {
            getName: function() {
                //一系列运算
                return "zhangsan"
            }
        }

<div>{{ getName1 }}</div>getName1是一个计算属性,也可在里面实现复杂的运算,最后返回一个值。

        computed: {
            getName1: function() {
                //一系列运算
                return "zhangsan"
            }
        }
  1. 计算属性与方法
    计算属性与方法实际上都是函数,但方法是写在methods选项中,而计算属性写在computed选项中,选项是一个对象。
    在调用方法时,每调用一次都会执行一次函数。
    在调用计算属性时,只会执行一次函数,再次调用时则使用之前的返回值;若函数中使用的响应式变量发生了变化,才会重新调用一次函数。如computed: { getName1: function() { return this.name.toUpperCase() } },只有当name的值变化时才会重新执行函数。
    注:响应式变量指的是data选项中的变量。

  2. 何时使用计算属性
    计算属性可以节约系统资源,当使用了一个大数组时,使用方法则会每次调用都遍历一次数组,而使用计算属性则只会遍历一次。

  3. 计算属性的setter

computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[1]
    }
  }
}

执行fullName = "Sherlock Holmes"时会调用set方法,使firstName和lastName变量更新;由于这两变量发生了变化,所以下次使用计算属性fullName时会重新执行一次函数。

六、侦听

  1. 侦听属性
    响应式变量改变时计算属性会重新调用一次函数,这实现了变量的被动监听,但这并不好用。
    而使用侦听属性可以实现变量的主动监听。
export default {
        data() {
            return {
                watchProperty: ""
            };
        },
        watch: {
            watchProperty: function(val, oldval) {
                alert("新值为" + val + ",旧值为" + oldval)
            }
        }
}

侦听属性需要写在watch选项中,选项是一个对象。
对象键值对的值可以是一个函数,函数会传入两个参数,参数为变量的新值和旧值。
还可以是字符串、数组、对象,详见watch

  • 实例方法
    除了写在watch选项中,还可以使用实例方法this.$watch()
    $watch()一般写在mounted选项中,选项是一个函数,它会在 Vue 实例被挂载后调用。
<script>
    export default {
        data: function() {
            return {
                message: "message"
            }
        },
        mounted: function() {
            this.$watch("message", function(newVal, oldVal) {
                alert(newVal+" "+oldVal)
            }, {})
        }
    }
</script>

$watch()有三个参数:

  • 参数一是一个表达式或函数。
    • 表达式通常是一个变量,会监听变量值是否发生了变化。若要监听对象中的键值对,可使用表达式a.b.c
    • 为函数时,会监听函数的返回值是否发生了变化。
  • 参数二是一个回调函数,会在监听的值发生变化时回调。函数有两个参数,分别为新值与旧值。
  • 参数三是一个对象,可使用两个键deep、immediate,它们都是布尔值。
    • deep为true,当监听一个对象时,对象中任意键值对变化,就能触发回调。为数组时不需这样设置。
    • immediate为true,会立即触发一次回调函数。
  • $watch()有一个返回值,返回值是一个函数,调用这个函数能取消监听。
    var unwatch = this.$watch("message", function() {});unwatch();
  1. 侦听事件
    通常,在组件上使用$emit()触发一个自定义事件,在父组件上使用v-on指令监听这个事件。详见 十一、组件 -- 3. 事件方法
    那么,在本组件中怎么监听由$emit()触发的自定义事件呢?可以使用实例方法this.$on()实现。
<template>
    <div>
        <div @click="$emit('tap')">title</div>
    </div>
</template>

<script>
    export default {
        mounted: function() {
            this.$on("tap", function() {
                alert(123)
            })
        }
    }
</script>

$on()一般写在mounted选项中,选项是一个函数,它会在 Vue 实例被挂载后调用。
$on()有两个参数:

  • 参数一为一个字符串或数组。
    • 字符串为$emit()触发的事件名。
    • 数组为多个事件名组成的数组。
  • 参数二为回调函数,回调函数会接收所有$emit()触发函数的参数。

$on(eventName, eventHandler) 侦听一个事件
$once(eventName, eventHandler) 一次性侦听一个事件,事件名不支持数组。
$off(eventName, eventHandler) 停止侦听一个事件

七、class与style的绑定

在 html 中,通过<div class="class1 class2" style="width: 100px;height: 30px;"></div>来绑定元素的 CSS 类名和样式。
在 Vue 中,则使用v-bind指令动态地绑定类名或样式。

  1. 对象语法
    v-bind指令可为属性绑定一个变量,当属性为classstyle时,这个变量可以是一个对象。
  • 变量写法
    • class
      <div :class="classObject"></div>,变量classObject为classObject: { class1: true, class2: true , class3: flase }。此时相当于<div class="class1 class2"></div>
    • style
      <div :style="styleObject"></div>,变量styleObject为styleObject: { color: 'red', fontSize: '16px' }。此时相当于<div style="color: red;font-size: 16px;"></div>
      在对象中,font-size要写成驼峰式fontSize,也可以写成'font-size'
  • 内联写法
    • class
      <div :class="{ class1: enable1, class2: enable2, class3: enable3 }"></div>,变量为enable1: true, enable2: true, enable3: false。此时也相当于<div class="class1 class2"></div>
    • style
      <div :style="{color: color, fontSize: size}"></div>,变量为color: 'red', size: '16px'。此时也相当于<div style="color: red;font-size: 16px;"></div>
      font-size要写成驼峰式fontSize,也可以写成'font-size'
  • 使用计算属性
    变量不仅可以是一个对象,也可以是计算属性。
<template>
    <div>
        <div v-bind:class="classObject"></div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                pageIndex: 5
            }
        },
        computed: {
            classObject: function() {
                return {
                    class1: this.pageIndex%3 == 0,
                    calss2: this.pageIndex%3 == 1,
                    calss3: this.pageIndex%3 == 2
                }
            }
        }
    }
</script>

变量为计算属性时,可以通过一系列判断后再返回对象。上面的作用是根据pageIndex来使用不同的类名。
当然,style也可以像上面一样使用计算属性。

  1. 数组语法
    v-bind指令可为属性绑定一个变量,当属性为classstyle时,这个变量可以是一个数组。
  • 变量写法
    <div :class="classArray"></div>,变量classArray为classArray: ['class1', 'class2']。此时相当于<div class="class1 class2"></div>
    与对象语法一样,变量也可以是一个计算属性,不同点在于返回值是一个数组。
  • 内联写法
    • class
      <div :class="['class1', 'class2']"></div>
      <div :class="[className, className2]"></div>,变量为className1: "class1", className2: "class2"
      <div :class="[isok1 ? 'class1' : '', {class2: isok2}]"></div>,变量为isok1: true, isok2: true
      上面三种都相当于<div class="class1 class2"></div>
    • style
      <div :style="[colorstyle, fontstyle]"></div>,变量为colorstyle: { color: 'red' }, fontstyle: { fontSize: '16px' },相当于<div style="color: red;font-size: 16px;"></div>
      font-size要写成驼峰式fontSize,也可以写成'font-size'

注:使用v-bind指令时,Vue 会在 CSS 属性前自动添加上各种浏览器的前缀。

八、条件渲染

  1. v-if
    与 JS 中的if、else if、else一样,在 Vue 中也能通过指令v-if、v-else-if、v-else来条件渲染不同的元素。
    三个指令的值都是一个表达式,且表达式的结果应该是一个布尔值。
    必须按顺序书写,在一组元素中,带有v-else-if指令的元素可存在零个或多个,带有v-else指令的元素可存在至多一个。
<template>
    <div>
        <div v-if="show1">if</div>
        <div v-else-if="show2">else if</div>
        <div v-else>else</div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                show1: true,
                show2: true
            }
        }
    }
</script>

当show1为true,渲染第一个div;当show1为false,show2为true,渲染第二个div;当都为false,渲染最后一个div。

  • template
    当需要条件渲染多个元素时,可以使用 template。
    <div v-if="show1"><div>1</div><div>2</div></div>,当 show1 改变时,实际上是切换最外层带有v-if指令的这个 div,只不过是该div中有两个子div。
    若想同时切换多个元素,可将最外层改成 template,如<template v-if="show1"><div>1</div><div>2</div></template>,此时实际上是在切换里面的两个div。
  • 元素的复用
<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" />
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" />
</template>

Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。
上面例子的 label 元素和 input 元素在切换时,实际上是修改 label 元素的内容和 input 元素的占位符。

若在输入框中输入了字符串,你会发现在切换时字符串并不会变化。
但这种情况有时并不符合要求。给需要重新渲染的元素设置不同的key值,就能解决这个问题。

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email">
</template>

此时 laebl 元素会复用,input 元素会重新渲染。

  1. v-show
    使用v-show指令也可以实现条件渲染,如<div v-show="show">show</div>
    v-show指令的值也是一个表达式,表达式的结果是一个布尔值。
    结果为true则显示,为false则不显示,实际上是将元素的CSS切换成display: none

v-if在初始渲染时条件为假时什么也不做;直到条件第一次变为真时,才会开始渲染。v-show不管初始条件是什么,元素都会被渲染。
v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果要频繁地切换,则使用v-show较好。
v-if支持 template 元素,而v-show则不支持。

九、循环渲染

在 JS 中,使用for来循环执行代码;在 Vue 中,可以使用v-for指令来循环渲染元素。
通常,指令的值都是一个表达式,而v-for指令的值为alias in expression
其中expression的值可为数组、对象、数字、字符串、Iterable(详见迭代协议)。

  • 在v-for中使用数组
    v-for指令的值为item in itemsitemsdata选项中的变量,是一个数组;item则是数组的元素,通常是一个对象。
<template>
    <div>
        <div v-for="item in items" :key="item.id">{{item.content}}</div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                items: [
                    {
                        id: "id1",
                        content: "item1"
                    },
                    {
                        id: "id2",
                        content: "item2"
                    }
                ]
            }
        }
    }
</script>

v-for指令的值还可以为"item,index in items"index为元素的索引。
in可以替换成of,如item of items"item,index of items"

  • 在v-for中使用对象
    v-for指令的值为value in objectobjectdata选项中的变量,是一个对象,value为对象的键值对的值。
    v-for指令的值还可以为value,key in objectvalue,key,index in objectkey为对象的键值对的键,index为索引。
    in也可以替换成of
    注:遍历对象时,会按Object.keys()的结果遍历。
  • 在v-for中使用数字
    v-for指令的值为num in 10,则会重复对应次数。
  • 在v-for中使用字符串
    v-for指令的值为charStr in 'string',则会重复字符串长度的次数,charStr为每一个字符。
    v-for指令的值了也可为charStr,index in 'string',index为索引值。
  • 使用key
    当更新使用v-for指令渲染的元素列表时,它默认使用 就地更新 的策略。如果数据项的顺序被改变,Vue 不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
    为了重用和重新排序现有元素,可以给每项提供一个key属性,如<div v-for="item in items" :key="item.id">{{item.content}}</div>
    注:
    key的值应该是一个字符串或数字,并且值不能够重复。
    在组件上使用v-for指令时,key是必须的。如<custom v-for="item in items" :key="item.id"></custom>
  • 使用template
    v-for指令可以用在 template 元素上,如<template v-for="item in items" :key="item.id"><div>{{ item.id }}</div><div>{{ item.content }}</div></template>
    v-if指令一样,当需要循环渲染多个元素,可以使用 template。
  • v-for与v-if
    当两个指令同时使用时,如<div v-for="item in items" v-if="show" :key="item.id">{{item.content}}</div>
    v-for指令的优先级比v-if指令高,即先遍历再判断。
    注:Vue 不推荐这两个指令组合使用。

十、表单绑定

在 html 中,给一个输入框设定初始值,如<input value="hello"></input>
当在输入框修改值后,实际上value的值并没有改变。可以监听oninput事件,在函数使用element.setAttribute()来改变value的值。
在 Vue 中,使用v-model指令来实现双向绑定,实际上也是通过监听输入事件以更新数据。

v-model指令针对不同类型的 input 元素使用不同的值与事件。
text 和 textarea 使用 value 属性和 input 事件;
checkbox 和 radio 使用 checked 属性和 change 事件;
select 使用 value 属性和 input 事件。

  • 使用v-model
    <input v-model="inputValue"></input>
    inputValuedata选项中的一个变量,必须设置初始值,即使是一个""
    checkbox 绑定的值是一个数组或布尔,radio 绑定的值是一个布尔,其他类型绑定的值则是一个字符串。
  • 修饰符
    text 和 textarea 绑定的值默认在input事件中更新,使用.lazy修饰符,使其在change事件中更新,如<input v-model.lazy="inputValue"></input>
    text 和 textarea 绑定的值默认是字符串,使用.number修饰符,可以让绑定的值变为数字,如<input v-model.number="age" type="number"></input>
    使用.trim修饰符,可以自动去除字符串前后的空格,如<input v-model.trim="inputValue"></input>

十一、组件

组件是可复用的 Vue 实例,.vue文件就是一个单文件组件。

  1. 使用组件
<template>
    <div>
        <CustomComponent></CustomComponent>
        <Custom-Component></Custom-Component>
        <custom-component></custom-component>
        
        <CustomComponent1></CustomComponent1>
        <custom-component1></custom-component1>

        <custom-component2></custom-component2>
    </div>
</template>

<script>
    import CustomComponent from './CustomComponent.vue'
    export default {
        components: {
            CustomComponent,
            CustomComponent1: {
                template: "<div>1234</div>",
                data: function() {
                    return {}
                }
            }
            'custom-component2': {
                template: "<div>1234</div>",
            }
        }
    }
</script>

可使用import引入其他的.vue文件,如组件CustomComponent;也可以在当前文件中直接创建组件,如组件CustomComponent1。
组件要写在components选项中,选项是一个对象。当对象的键值对的键值相同时,可以省略值,如{ CustomComponent }相当于{ CustomComponent: CustomComponent }

使用组件时,可以使用大驼峰CustomComponent,也可以使用分隔式Custom-Component,也可以使用小写分隔式custom-component,建议使用小写分隔式,如<custom-component></custom-component>
若注册组件时就使用的小写分隔式(此时两端要加引号),如'custom-component2',使用时也就只能使用小写分隔式。

  • 全局组件
    上面的组件只能在当前文件中使用,可使用Vue.component('name', {})注册全局组件。要注意的是,必须写在new Vue({})之前。
  1. 组件属性
    每个元素都有属性,自定义的组件在props选项中声明自定义属性,可使用this.$props获取选项的值。
    props选项中声明的属性可以像data选项中的变量一样,直接在组件中使用。
<template>
    <div>
        <h1>{{ title }}</h1>
        <p>{{ content }}</p>
    </div>
</template>

<script>
    export default {
        data: function() {
            return {
                title: "component"
            }
        },
        props: ['content']
    }
</script>

组件的data选项必须为一个函数,它返回一个对象。这样可以保证每个组件实例有只属于自己的数据。

  • 使用属性
    通过<custom-component content="message"></custom-component>使用自定义的属性,当然也可以使用:content=""为属性绑定一个表达式。
    <custom-component content></custom-component>,表示content的值为true。

  • v-bind
    当组件中声明的属性很多时,如果都使用的话代码就会很长,如<custom-component name="" age="" adress="" phone=""></custom-component>
    使用v-bind指令可将所有属性集中在一个对象中,如<custom-component v-bind="datas"></custom-component>,变量为datas: { name: "", age: "", adress: "", phone: "" }

  • sync
    上面不管是:content=""还是v-bind=""绑定变量时,都是单向的。当组件内的属性值改变时,不会改变父组件变量的值。
    若要进行双向绑定,可使用.sync修饰符,如:content.sync=""v-bind.sync=""
    注:.sync修饰符只支持变量,不支持表达式。

  • 属性命名
    自定义的属性通常使用小驼峰命名,在使用时,可使用小驼峰或分隔式,如<custom-component contentValue=""></custom-component><custom-component content-value=""></custom-component>

  • prop选项的类型
    prop选项可为数组或对象。

    • 为数组时,如props: ['title', 'likes', 'isPublished']
    • 为对象时,如props: { title: String, likes: Number, isPublished: Boolean}
      • 对象键值对的值为数据类型,可使用String、Number、Boolean、Array、Object、Date、Function、Symbol
      • 也支持构造函数,如function Person (firstName, lastName) { this.firstName = firstName;this.lastName = lastName },通过props: { person: Person}来使用。
  • props类型验证
    props选项为对象时:

    • 可让一个属性支持一种数据类型,如title: String
    • 也可支持多种数据类型,如title: [String, Number]
    • 键值对的值也可以是一个对象,如title: { type: String },相当于title: String
      这个对象有多种键:
      • type表示属性的数据类型,如title: { type: String }
      • required表示属性是否必填,如title: { type: String, required: true }
      • default表示默认值,如title: { type: String, default: "标题" }
        若数据类型为数组或对象,必须使用函数,如title: { type: Array, default: function() { return [] } }
      • validator表示验证函数,如title: {type: String, validator: function(value) { return value.length > 6 } },表示title的值的字符长度必须大于6。

    注:props选项中的属性会在组件实例创建之前进行验证,所以实例thisdefaultvalidator的函数中是不可用的。

  • 使用非props的属性
    <custom-component title=""></custom-component>
    若组件中并没有声明title属性,使用时,Vue 会将title自动添加到组件的根元素上。若根元素上已经设定了该属性,会被覆盖。但style、class会发生合并。
    若想让未声明的属性不自动添加到组件的根元素上,需要设置inheritAttrs选项为false。
    该选项并不会影响class、style的合并。

<script>
    export default {
        inheritAttrs:false
    }
</script>

使用$attrs属性可获取所有已使用且未在props中声明的属性,它是一个对象,里面不包括class、style

<template>
    <div>
        <div val1="$attrs['val1']"></div>
        <div val2="$attrs['val2']"></div>
        <div v-bind="$attrs"></div>
        <div>{{ $attrs["val1"] }}</div>
    </div>
</template>

<script>
    export default {
    }
</script>

使用<custom-component val1="" val2=""></custom-component>设置未声明的属性,在组件中也可使用$attrs属性来实现props的功能,但不推荐。

  1. 事件方法
    每个元素都有事件方法,自定义组件中使用实例方法$emit()来声明自定义方法。
<template>
    <div>
        <h1 @click="$emit('tap', 1, 2)">{{ title }}</h1>
        <p>{{ content }}</p>
    </div>
</template>

<script>
    export default {
        data: function() {
            return {
                title: "component"
            }
        },
        props: ['content']
    }
</script> 

<h1 @click="$emit('tap', 1, 2)">{{ title }}</h1>的功能是,当点击 h1 元素时,触发一个名为 tap 的事件,并为事件提供了两个参数1和2;$emit中事件名必须的。
当然,也可以将$emit()写在@click="clickAction"绑定的函数中。

父组件通过<custom-component @tap="tapAction"></custom-component>来监听 tap 事件,并通过tapAction: function(val1, val2) { }使用这两个参数,或在函数中通过arguments[0]、arguments[1]使用这两参数。

  • native
    在组件上监听原生事件时,如<custom-component @click="clickAction"></custom-component>,会发现点击时并不会调用 clickAction 函数,因为只能监听$emit()触发的事件。
    为了能顺利调用函数,需要使用.native修饰符,如@click.native="clickAction"
  • listeners
    .native修饰符可以监听根元素上的原生事件,若根元素是一个div,监听的事件是input,这当然不会有作用。
    可以使用$listeners属性获取所有v-on指令绑定的事件(不包括.native修饰符的事件)。
    这样就能将父组件要监听的input事件绑定到子组件的 input 元素上,如<input v-on:input="$listeners.input"></input>
    也可以将所有事件都绑定到 input 元素上,如<input v-on="$listeners"></input>
  1. 组件使用v-model
    使用v-model指令时,text 和 textarea 绑定的是 value 属性 和 input 事件,相当于<input :value="text" @input="text = $event.target.value">
    组件也能使用v-model指令,也默认绑定 value 属性与 input 事件,相当于<custom-component :value="text" @input="text = $event">
    在 input 元素中,$event表示事件对象;而在组件中,$event表示$emit()的第二个参数。
  • 更改默认
    组件使用v-model指令时默认绑定valueinput事件,可使用model选项更改默认值。
    选项是一个对象,对象有两个键prop、event,分别为要绑定的属性和事件。
<script>
    export default {
        model: {
            prop: "content",
            event: "change"
        },
        props: ['content']
    }
</script>

此时变更为绑定content属性与change事件。

  1. 插槽
    html 元素大都可以通过<div>message</div>在开始和结束标签中添加内容。
    而对于组件,<custom-component>message</custom-component>标签中的内容是没有任何效果的。
    为了使添加的内容生效,可以使用 slot 元素。
<template>
    <div>
        <h1 @click="$emit('tap', 1, 2)">{{ title }}</h1>
        <slot></slot>
        <p>{{ content }}</p>
    </div>
</template>

此时,<custom-component>message</custom-component>标签中的message就会替换到<slot></slot>的位置。

  • 默认值
    <slot></slot>中添加默认值,如<slot>hello</slot>,那么当<custom-component></custom-component>时,就相当于<custom-component>hello</custom-component>
  • 具名插槽
    有时候,我们可能需要添加多个插槽,它们在组件的不同位置。
    这时需要使用插槽的name属性。
<template>
    <div>
        <slot name="a"></slot>
        <slot></slot>
        <slot name="b"></slot>
    </div>
</template>

<slot></slot>是一个默认插槽,name默认为default,相当于<slot name="default"></slot>

<custom-component2>
            <div>default</div>
            <template v-slot:a>aaa</template>
            <template v-slot:b>bbb</template>
        </custom-component2>

使用时具名插槽的内容必须写在 template 元素中,通过v-slot指令的参数来匹配插槽的名字。
若为默认插槽,可省略<template v-slot:default></template>


内容显示的顺序只与组件中各插槽的顺序有关。

  • 简写
    v-bind、v-on指令可以简写成: @一样,v-slot指令可以简写成#,如<template v-slot:default></template>简写成<template #default></template>
  • 作用域插槽
    通常,给插槽添加内容时,都是使用当前文件中的数据。如<custom-component2>{{ value }}</custom-component2>
    但组件内部也提供了许多数据给外面的插槽使用,使用v-slot指令来获取组件提供的数据。
<template>
    <div>
        <slot name="a"></slot>
        <slot age="20" v-bind:adress="adressV"></slot>
        <slot name="b"></slot>
    </div>
</template>

<script>
    export default {
        data: function() {
            return {
                adressV: "cd"
            }
        }
    }
</script>

在插槽 slot 上添加属性 age 和 adress,也可使用v-bind指令为属性绑定一个表达式。

<custom-component2>
            <template v-slot:default="value">
                {{ value }}
            </template>
        </custom-component2>

v-slot:default="value"表示给name="default"的插槽(即默认插槽)提供一个变量 value。
插槽就能够使用变量 value 的数据,而 value 的值为{ "age": "20", "adress": "cd" },即在 slot 上添加的属性与属性值。
若只有默认插槽,有另一种写法,如<custom-component2 v-slot="value">{{ value }}</custom-component2>

  • 使用部分数据
    上面 value 的值是一个对象{ "age": "20", "adress": "cd" },若想只返回对应属性的值,可写成v-slot:default="{age: a, adress: b}",这时a为20,b为cd。
    因为当对象中的键值相同时可以省略值,所以可以写成v-slot:default="{age, adress}",相当于v-slot:default="{age: age, adress: adress}",这时age为20,adress为cd。
  • 动态插槽名
    和其他指令一样,可以使用[]为指令设定动态的参数,而v-slot的参数即表示插槽名。如v-slot:[name]="value"{ name: 'default' }时相当于v-slot:default="value"
  1. 动态组件
    使用 component 元素和is属性来使用动态组件。
    <component is="custom-component"></component>相当于<custom-componen></custom-componen>,通常使用v-bind:is=""绑定一个变量,就可以改变变量的值来切换不同的组件。
    除了使用组件,也可以用在普通的 html 元素上,如<component is="div"></component>
  • 保活组件
    动态组件通常用于多标签栏切换界面。你会发现,切换不同界面时界面都会重置;这是因为在切换时,Vue 都会创建一个新的组件实例。
    但有时候,我们希望创建的组件能缓存下来,可以使用 keep-alive 元素,如<keep-alive><component :is="componentName"></component></keep-alive>
    在不同组件同切换时,组件中的activateddeactivated这两个生命周期钩子函数会执行,组件的子组件中的这两个钩子函数也会执行。
    keep-alive 元素有三个属性:

    • include 字符串或正则表达式,名称匹配的组件会被缓存。
      名称为组件name选项的值或父组件components选项的键值对的键。
    • exclude 字符串或正则表达式。名称匹配的组件不会被缓存。
    • max 数字。最多可以缓存多少组件实例,数字达到了,会释放最久没有访问的实例。

    注:keep-alive 元素只能用在有一个子组件的情形;若子组件使用了v-for,keep-alive 元素也没有效果。

  1. 异步组件
    通常,我们都是在本地创建组件,然后在本地直接使用组件。
    有时候,组件的代码在服务器端,则需要先从服务器获取组件代码,这时就要使用异步组件。
        components: {
            ServiceComponent: function(resolve, reject) {
                var http = new XMLHttpRequest()
                http.onreadystatechange = function() {
                    if (http.readyState == 4&&http.status == 200) {
                        const component = {
                            template: http.responseText
                        }
                        resolve(component)
                    } else {
                        reject("组件获取失败")
                    }
                }
                http.open("GET", "http_url", true)
                http.send()
            }
        }

Vue 允许你以一个函数的方式定义你的组件,这个函数会异步解析你的组件。
Vue 只有在这个组件需要被渲染的时候才会触发该函数,且会把结果缓存起来供以后再次渲染。
函数有两个回调参数,调用resolve()表示组件加载成功,调用reject()表示组件加载失败,

  1. 访问子组件
    使用ref属性为元素或子组件注册引用信息,如<custom-component ref="custom"></custom-component>
    再使用$refs引用就会指向子组件的实例,如this.$ref.custom
    若用在 html 元素上,引用就会指向 DOM 元素。
    注:$refs 只会在组件渲染完成之后生效,并且它不是响应式的。应该避免在 template 或计算属性中访问它。

  2. 访问父组件
    使用$parent可访问父组件的实例,如this.$parent

  • 依赖注入
    子组件不应该主动访问父组件的实例,可以使用provide、inject选项来依赖注入,让子组件能访问父组件提供的数据或方法。
    在父组件中使用provide选项提供数据,它是一个对象或返回对象的函数,如provide: {age: this.age};
    在子组件中使用inject选项接收数据,它是一个对象或数组,如inject: ['age']
    这样就能在子组件中通过age来使用父组件中的this.age。

相比$parent来说,依赖注入可以让我们在任意后代组件中访问数据,而不需要暴露整个父组件实例。
后代组件不需要知道被注入的数据来自哪里。
父组件不需要知道哪些后代组件使用了它提供的数据。
注入的数据是非响应式的。

十二、过渡

使用 transition 元素,给其他元素或组件添加进入、离开过渡/动画效果。
将要过渡/动画的元素放在 transition 中,如<transition name=""><div></div></transition>
然后在CSS类名中添加过渡/动画效果。

  1. 类名
    在进入/离开的过渡中,会有6个类名切换。
    v-enter定义进入过渡/动画的开始状态。在元素插入之前生效,在元素插入之后的下一帧移除。为动画时会在animationend事件触发时移除。
    v-enter-active定义进入过渡/动画生效时的状态。在这里设定进入过渡/动画的过程时间、延迟和曲线函数。
    v-enter-to定义进入过渡/动画的结束状态。在元素插入之后下一帧生效 (v-enter移除之时),在过渡/动画完成之后移除。
    v-leave定义离开过渡/动画的开始状态。在元素删除之前生效,在元素删除之后的下一帧移除。为动画时会在animationend事件触发时移除。
    v-leave-active定义离开过渡/动画生效时的状态。被用来定义离开过渡/动画的过程时间、延迟和曲线函数。
    v-leave-to定义离开过渡/动画的结束状态。在元素删除之后的下一帧生效 (v-leave移除之时),在过渡/动画完成之后移除。

    前面的v表示 transition 元素的name的值,如<transition name="div"></transition>时使用div-enter-active
    当没有设置name时,默认使用v。


    v-enter-active表示元素插入、执行过渡/动画、过渡/动画结束这三个阶段。
    v-enter-active = v-enter + v-enter-to,大概相当于v-enterv-enter-active的前1%,v-enter-to占后面99%。
    v-leave-activev-enter-active类同。

<template>
    <div>
        <button @click="show = !show">switch</button>
        <transition name="div">
            <div v-if="show">animation</div>
        </transition>
        <transition name="div1">
            <div v-if="show">transtion</div>
        </transition>
    </div>
</template>

<script>
    export default {
        data: function() {
            return {
                show: true
            }
        }
    }
</script>

<style>
    /* 过渡 */
    .div1-enter-active, .div1-leave-active {
      transition: all 1s ease;
    }
    /* 设置进入过渡执行前或离开过渡执行后的CSS,通常这两个时间点CSS相同 */
    .div1-enter, .div1-leave-to  {
      opacity: 0;
      transform: translateX(60px);
    }
    
    /* 动画 */
    .div-enter-active {
      animation: framename 2s;
    }
    /* 通常离开动画是进入动画的反向 */
    .div-leave-active {
        animation: framename 2s reverse;
    }
    /* from中设置动画的初始状态CSS to中设置结束状态CSS */
    @keyframes framename{
        from{
            transform: translate(0px, 0px);
        }
        25% {
            transform: translate(50px, 0px)
        }
        50% {
            transform: translate(50px, 50px)
        }
        75% {
            transform: translate(0px, 50px)
        }
        to{
            transform: translate(0px, 0px);
        }
    }
</style>
  • 自定义类名
<transition name="div1" enter-active-class="aaa" leave-active-class="bbb">
    <div v-if="show">transtion</div>
</transition>

.aaa, .bbb {
    transition: all 1s ease;
}

使用属性enter-class、enter-active-class、enter-to-class、leave-class、leave-active-class、leave-to-class自定义过渡/动画的类名。
如上,aaa、bbb相当于div1-enter-active、div1-leave-active类名。

  1. 监听类型
    Vue 会监听transitionendanimationend事件来确定过渡/动画的结束。若为一个元素同时设定过渡和动画,Vue 并不知道监听哪一个事件来确定过渡/动画的结束。
    可以将type属性设为animation、transition来确定监听哪一个事件,如<transition type="animation"></transition>

  2. 持续时间
    过渡/动画的持续时间通常是在CSS属性中设置,也可以使用duration属性设定持续时间,如<transition duration="1000"></transition>
    可以分别设定进入和离开的持续时间,如<transition duration="{ enter: 500, leave: 800 }"></transition>
    duration属性设定的持续时间优先度更高。当到了设定的持续时间,即使过渡或动画还在执行,也会立即结束过渡或动画。

  3. 事件
    transition 元素有before-enter、enter、after-enter、enter-cancelled、before-leave、leave、after-leave、leave-cancelled这几种事件,会在过渡前、过渡时、过渡后、过渡取消时触发,如<transition @before-enter="beforeEnter" @enter="enter"></transition>
    事件可以与 CSS 类名一起使用,建议单独使用。使用<transition :css="false"></transition>可屏蔽 CSS。

    • 事件会给绑定的函数提供一个参数,参数值为执行过渡/动画的元素。
    • 若为enter、leave事件,还会多提供一个参数,参数为一个回调函数。
      需要在过渡/动画完成时执行回调函数,如enter: function(el, done) { done() }
      若不执行回调,过渡/动画就不会结束,就不会触发after-enter事件;执行回调函数后,过渡/动画会立即结束。
  4. 初始渲染过渡
    当元素初次渲染到界面时,也可以执行过渡。使用appear属性来实现初始渲染过渡。

<template>
    <div>
        <button @click="show = !show">switch</button>
        <transition name="div" appear="appearname">
            <div v-if="show">transtion init1</div>
        </transition>
    </div>
</template>

<script>
    export default {
        data: function() {
            return {
                show: true
            }
        }
    }
</script>

<style scoped="scoped">
    .div-enter-active, .div-leave-active {
      transition: all 1s ease;
    }
    .div-enter, .div-leave-to  {
      opacity: 0;
      transform: translateX(60px);
    }

    .div-appearname  {
      opacity: 0;
      transform: translateX(60px);
    }
    .div-appearname-active {
      transition: all 1s ease;
    }
    .div-appearname-to {
        
    }
</style>

初始渲染过渡有三个类名,默认为v-appear、v-appear-active、v-appear-to,表示初始渲染过渡前、初始渲染过渡中、初始渲染过渡完成。
根据name属性和appear属性的值决定类名,若<transition name="div" appear="appearname">,那么类名为div-appearname、div-appearname-active、div-appearname-to

初始渲染过渡也可以通过appear-class、appear-active-class、appear-to-class使用自定义类名,如<transition appear appear-active-class="" appear-class="" appear-to-class=""></transition>
初始渲染过渡也可以使用事件before-appear、appear、after-appear、appear-cancelled。表示初始渲染过渡前、初始渲染过渡时、初始渲染过渡后、初始渲染过渡取消。

  1. 元素切换的过渡
    上面介绍的都是单个元素的进入与离开过渡,若想使用多个元素间切换时的过渡,可使用v-if、v-else-if、v-else
<template>
    <div>
        <button @click="show = !show">switch</button>
        <transition name="div">
            <div v-if="show" key="init1">transtion init1</div>
            <div v-else key="init2">transtion init2</div>
        </transition>
    </div>
</template>


此时就会在两个 div 中切换。
必须给每个元素的key属性设置不同的值,否则 Vue 会直接改变元素的内容,就不会产生过渡效果。
若两个元素是不同的元素,也可以不设置key属性,但建议设置。

  • 过渡模式
    你会发现两个元素它们会同时过渡,即离开与进入过渡同时进行。可以使用mode属性修改过渡模式。

    • in-out新元素先进行过渡,完成之后当前元素过渡。
    • out-in当前元素先进行过渡,完成之后新元素过渡。(经常使用)

    当不设置mode时,两者会同时过渡。
    设置<transition name="div" appear="appearname" mode="out-in">

  • 组件切换的过渡
    通过动态组件实现组件切换的过渡,动态组件详见 十一、组件 -- 6. 动态组件
    这里因为是不同的组件,所以可以不设置key属性。

<template>
    <div>
        <button @click="name == 'component1' ? 'component2' : 'component1'">switch</button>
        <transition name="div" mode="out-in">
            <component :is="name"></component>
        </transition>
    </div>
</template>

<script>
    export default {
        data: function() {
            return {
                name: "component1"
            }
        },
        components: {
            component1: {
                template: '<div>Component 1</div>'
            },
            component2: {
                template: '<div>Component 2</div>'
            }
        }
    }
</script>

<style scoped="scoped">
    .div-enter-active, .div-leave-active {
      transition: all 1s ease;
    }
    .div-enter, .div-leave-to  {
      opacity: 0;
      transform: translateX(60px);
    }
</style>
  1. 列表元素的过渡
    上面我们过渡的,实际上都是单一元素或组件的过渡;若同时要过渡多个元素,需要使用 transition-group 元素,且多个元素必须设置不同的key
    不同于 transition 元素,transition-group 会以一个真实元素呈现,默认为一个 span,使用tag属性更换为其他元素。
<template>
    <div>
        <button @click="click">add</button>
        <br>
        <transition-group>
            <div v-for="item in datas" :key="item" style="display: inline-block;">{{ item }}</div>
        </transition-group>
    </div>
</template>

<script>
    export default {
        data: function() {
            return {
                datas: [1, 2, 3, 4, 5],
                next: 6
            }
        },
        methods: {
            click: function() {
                var num = Math.floor(Math.random() * this.datas.length)
                this.datas.splice(num, 0, this.next++)
            }
        }
    }
</script>

<style scoped="scoped">
    .v-enter-active, .v-leave-active {
      transition: all 1s ease;
    }
    .v-enter, .v-leave-to  {
      opacity: 0;
      transform: translateY(30px);
    }
</style>
  • 平滑过渡
    可以看出,当添加一个元素时,周围的元素会瞬间移动到他们的新布局的位置。
    为了让周围元素能平滑的移动到新位置,可以使用v-move类名。
    像其他类名一样,可以使用name属性改变类名,也可以使用move-class属性自定义类名。
    上面的例子添加.v-move { transition: all 1s ease; }

    注:要使用平滑过渡,元素不能为display: inline
  1. 状态过渡
    上面都是元素的进入或离开过渡,若想让数据元素本身产生动效,可以使用状态过渡。如数字从0变到100的渐增式效果。
    详见状态过渡

十三、混入

混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能,一个混入对象包含任意的组件选项。

export default {
    data: function() {
        return {
            b: 22,
            c: 33
        }
    },
    created: function() {
        console.log('hello!11')
        this.action()
    },
    methods: {
        action: function() {
            console.log('action11')
        }
    }
}

.js文件中添加要混入的选项。

<template>
    <div>
        <div>{{ a }}</div>
        <div>{{ b }}</div>
        <div>{{ c }}</div>
    </div>
</template>

<script>
    import Test from './Test11.js'
    export default {
        data: function() {
            return {
                a: 1,
                b: 2//同名冲突时 使用组件中的数据
            }
        },
        mixins: [Test],
        //生命钩子 同名冲突 两个函数都会调用 优先调用混入中的函数
        created: function() {
            console.log('hello!22')
            this.action()
        },
        //方法 同名冲突 优先使用组件中的方法
        methods: {
            action: function() {
                console.log('action22')
            }
        }
    }
</script>

在单文件组件中通过import引入.js文件,混入的对象写在mixins选项中,选项是一个数组。

  1. 选项合并
    当组件和混入对象含有同名选项时,这些选项会进行合并。
  • data选项里的数据对象会递归合并,若遇到同名冲突,以组件的数据优先。
  • 值为对象的选项(methods、components、directives等),将被合并为同一个对象。若遇到同名冲突,以组件的数据优先。
  • 生命周期钩子函数(created、beforeCreate、mounted等),若遇到同名冲突,两个函数会合并为一个数组,它们都会调用,但混入对象中的函数优先调用。
  1. 全局混入
    使用Vue.mixin({})来注册全局混入,需要写在new Vue()前面。

  2. 自定义合并
    混入数据与组件数据合并时,有一个默认的合并策略。使用Vue.config.optionMergeStrategies可修改合并策略,详见自定义选项合并策略

  3. 扩展
    扩展(extend)和混入功能差不多,都是给组件添加其他的选项功能。
    混入使用mixins选项,它是一个数组;而扩展使用extend选项,它是一个对象或函数。

<script>
    export default {
        extends: {
            created: function() {
                
            },
            methods: {
                click: function() {
                    
                }
            }
        }
    }
</script>

扩展与混入的选项合并策略相同,不同点在于mixins可同时混入多组对象,而extend只能扩展一组对象。
扩展的优先级比混入高,但都低于组件本身。

  • 全局扩展
    使用Vue.extend({})来注册全局扩展,需要写在new Vue()前面。

十四、自定义指令

Vue 有自带的指令,但也可以使用directives选项来自定义指令,选项是一个对象。

<template>
    <div>
        <div v-redtext></div>
    </div>
</template>

<script>
    export default {
        directives: {
            redtext: {
                inserted: function (el) {
                  el.style.color = "red"
                  el.innerText = "default text"
                }
              }
        }
    }
</script>
  • 全局指令
    使用Vue.directive('name', {})注册全局指令,必须写在new Vue()前面。
  1. 钩子函数
    自定义指令有几个钩子函数:
    • bind 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
    • inserted 元素插入父节点时调用 (仅保证父节点存在,但不一定已插入到文档中)。
    • update 元素的 VNode 更新时调用,可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。
    • componentUpdated 元素的 VNode 及其子 VNode 全部更新后调用。
    • unbind只调用一次,指令与元素解绑时调用。
  • 钩子函数参数
    钩子函数有4个参数,其中el是可读写的,其他都是只读的。
    • el 指令绑定的元素,可以用来直接操作 DOM。
    • binding 一个对象,包含以下的键。
      • name 指令名,不包括 v-前缀。
      • value 指令的绑定值,如v-directive="1 + 1"中的2。
      • oldValue 指令绑定的前一个值,仅在update、componentUpdated钩子中可用。无论值是否改变都可用。
      • expression 指令的表达式。如v-directive="1 + 1"中的1 + 1。
      • arg 指令的参数,可选。如v-directive:arg中的arg。
      • modifiers 一个包含修饰符的对象。如v-directive.mod1.mod2,修饰符对象为{ mod1: true, mod2: true }
    • vnode Vue 编译生成的虚拟节点。详见 VNode
    • oldVnode 上一个虚拟节点,仅在update、componentUpdated钩子中可用。
  1. 动态参数
    指令可使用[]设置动态参数,如v-on:[dynamic]=""
    自定义的指令也可以使用动态参数,如v-redtext:[dynamic],在钩子函数中通过binding.arg获取参数。

十五、渲染函数

通常使用模板来创建组件,也可以使用render选项来创建组件。

<script>
    export default {
        render: function (createElement) {
            return createElement('div', {}, [
                createElement("h1", {}, "title")
            ])
        }
    }
</script>

<style>
</style>

上面例子相当于<template><div><h1>title</h1></div></template>
使用render选项来创建组件时需要删除最上面的<template></template>,也会忽略template选项的值。

  • render选项是一个函数,函数需要返回一个 VNode。函数有一个参数,参数是一个回调函数,可通过这个回调函数来创建 VNode。
  • render选项可以使用箭头函数,如render: createElement => createElement()
    回调函数通常使用字母h,即render: h => h(),可看到 main.js 中就是这种写法。
  1. VNode
    每个元素都是一个节点,每段文字也是一个节点,注释和属性也都是节点。一个节点就是页面的一个部分,每个节点都可以有子节点 。
    Vue 通过建立一个虚拟 DOM 来追踪自己要如何改变真实 DOM,虚拟 DOM 由虚拟节点(virtual node)组成,即VNode。
    回调函数createElement()可以创建一个虚拟节点 。它包含的信息会告诉 Vue 需要渲染什么样的节点,包括节点及其子节点的描述信息。
    VNode 必须是唯一的,下面是错误的。
render: function (createElement) {
  var myParagraphVNode = createElement('p', 'hi')
  return createElement('div', [
    // 错误 - 重复的 VNode
    myParagraphVNode, myParagraphVNode
  ])
}

但可以写成:

render: function (createElement) {
  return createElement('div', [
    createElement('p', 'hi'), createElement('p', 'hi')
  ])
}
  1. 参数
    createElement()需要传三个参数,如createElement("h1", {}, "title")
    • 参数一是必填项,可为一个 HTML 标签名、组件选项对象,或者 resolve 了上述任何一种的async函数。
    • 参数二是一个对象,为元素的各种属性,如props、class、id等。
    • 参数三是一个由子 VNode 组成的数组,如createElement("div", {}, [createElement("h1", "title"), createElement("p", "content")])
      或为一个字符串,内容为元素的innerHTML
  • 参数二
{
  // 与 `v-bind:class` 的 API 相同,
  // 接受一个字符串、对象或字符串和对象组成的数组
  'class': {
    foo: true,
    bar: false
  },
  // 与 `v-bind:style` 的 API 相同,
  // 接受一个字符串、对象,或对象组成的数组
  style: {
    color: 'red',
    fontSize: '14px'
  },
  // 普通的 HTML attribute
  attrs: {
    id: 'foo'
  },
  // 组件 prop
  props: {
    myProp: 'bar'
  },
  // DOM property
  domProps: {
    innerHTML: 'baz'
  },
  // 事件监听器在 `on` 内,
  // 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
  // 需要在处理函数中手动检查 keyCode。
  on: {
    click: this.clickHandler
  },
  // 仅用于组件,用于监听原生事件,而不是组件内部使用
  // `vm.$emit` 触发的事件。
  nativeOn: {
    click: this.nativeClickHandler
  },
  // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
  // 赋值,因为 Vue 已经自动为你进行了同步。
  directives: [
    {
      name: 'my-custom-directive',
      value: '2',
      expression: '1 + 1',
      arg: 'foo',
      modifiers: {
        bar: true
      }
    }
  ],
  // 作用域插槽的格式为
  // { name: props => VNode | Array<VNode> }
  scopedSlots: {
    default: props => createElement('span', props.text)
  },
  // 如果组件是其它组件的子组件,需为插槽指定名称
  slot: 'name-of-slot',
  // 其它特殊顶层 property
  key: 'myKey',
  ref: 'myRef',
  // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
  // 那么 `$refs.myRef` 会变成一个数组。
  refInFor: true
}
  • 事件修饰符
    在参数二的 on 中可设置事件监听,但事件名后是不能添加修饰符的,如on { click.once: this.clickAction }是错误的。为了解决这个问题,Vue 提供了一些前缀。

    • &表示.passive修饰符。
    • !表示.capture修饰符。
    • ~表示.once修饰符。
    • ~!表示.capture.once、.once.capture修饰符。

    on { !click: this.clickAction }相当于@click.capture="clickAction"
    而其他修饰符,可以在函数中实现相同的功能。

    • event.stopPropagation()相当于.stop修饰符。
    • event.preventDefault()相当于.prevent修饰符。
    • if (event.target !== event.currentTarget) return相当于.self修饰符。
    • if (event.keyCode !== 13) return相当于.enter、.13修饰符。
    • if (!event.ctrlKey) return相当于.ctrl修饰符。
  1. 插槽
  • 使用this.$slots访问插槽,每个插槽都是一个 VNode 组成的数组。
    render: function (h) { return h('div', this.$slots.default) }相当于<div><slot></slot></div>
    render: function (h) { return h('div', [this.$slots.header, this.$slots.default]) }相当于<div><slot name="header"></slot><slot></slot></div>
    注:使用插槽时不能用箭头函数,因为不能访问this。
  • 使用this.$scopedSlots访问作用域插槽。
data: function() {
            return {
                title: "标题",
                content: "内容"
            }
        },
        render: function (h) {
          return h('div', [this.$scopedSlots.header({
              title: this.title
          }), this.$scopedSlots.default({
              content: this.content
          })])
        }

相当于<div><slot :title="title" name="header"></slot><slot :content="content"></slot></div>

  1. JSX
    JSX是一个插件,可使用模板来代替渲染函数。详见JSX
    render: h =>h("div", [h("h1", "title"), h("div", "contnet")])可写成redder: h=> (<div><h1>title</h1><div>content</div></div>)

  2. 函数式组件
    组件比较简单时,即没有管理任何状态,没有监听任何传递给它的状态,也没有生命周期方法,它相当于一个接受一些props的函数。
    在这样的场景下,我们可以将组件的functional选项设为true,这意味它无状态,没有响应式数据(即没有data选项),也没有实例 (即没有this)。

functional: true,
        props: {
            
        },
        render: function(createElement, context) {
            createElement("div", context.data, context.children)
        }

因为函数式组件只是函数,所以渲染开销也低很多。

  • context
    因为在函数式组件中this不可用,所以所有属性都保存在参数context中。
    它是一个对象,包含下列字段:
    • props 提供所有自定义属性的对象。
    • children 子虚拟节点的数组,通常作为createElement()的第三个参数传入组件。
    • slots 一个函数,返回了包含所有插槽的对象。
    • scopedSlots 一个包含的作用域插槽的对象。
    • data 传递给组件的数据对象,通常作为createElement()的第二个参数传入组件。
    • parent 对父组件的引用。
    • listeners 一个包含了所有父组件为当前组件注册的事件监听器的对象。data.on的别名。
    • injections 如果使用了inject选项,则该对象包含了被注入的属性。注入详见 十一、组件 -- 9. 访问父组件

十六、过滤器

过滤器用于一些常见的文本格式化。过滤器可以用在两个地方:{{ }}插值和 v-bind指令的值,如<div>{{ mesage | filter }}</div>、<div :title="message | filter"></div>
过滤器写在filters选项中,选项是一个对象。

<template>
    <div>
        <div>{{ message | lowercase | uppercase }}</div>
    </div>
</template>

<script>
    export default {
        data: function () {
            return {
                message: "Hello World"
            }
        },
        filters: {
            lowercase: function(value) {
                return value.toLowerCase()
            },
            uppercase: function(value) {
                return value.toUpperCase()
            }
        }
    }
</script>

过滤器使用|与表达式相连,过滤器对象的键值对的值是一个函数,且表达式为函数的第一个参数。
可多个过滤器串联使用,如{{ message | lowercase | uppercase }},前面过滤器得出的结果变为后面过滤器的第一个参数。
过滤器可传入其他参数,如{{ message | lowercase(arg1, arg2) }},第一个参数为message,参数二三为arg1、arg2。

  • 全局过滤器
    使用Vue.filter('name', function (value) {})注册全局过滤器,必须写在new Vue()前面。
    当全局过滤器与局部过滤器重名时,优先使用局部过滤器。

十七、生命周期

整个 Vue 实例初始化到销毁会经历如下过程。


生命周期分为多个阶段,每个阶段都会触发相应的钩子。
钩子有beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed这几种,详见生命周期钩子
它们都是函数,使用时不要用箭头函数,因为箭头函数中this不指向 Vue 实例。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,049评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,478评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,109评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,097评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,115评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,280评论 1 279
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,748评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,398评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,553评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,440评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,487评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,176评论 3 317
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,750评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,821评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,049评论 1 257
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,559评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,150评论 2 341

推荐阅读更多精彩内容