PHP8.1.21版本已发布
vue8.1.21版本已发布
jquery8.1.21版本已发布

博客列表 > 【Vue框架学习】过滤器、自定义指令、生命周期、动画、组件、路由等知识点归纳总结

【Vue框架学习】过滤器、自定义指令、生命周期、动画、组件、路由等知识点归纳总结

 一纸荒凉* Armani
 一纸荒凉* Armani 原创
2021年05月17日 17:57:25 851浏览

Vue_过滤器

概念:Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示;

https://cn.vuejs.org/v2/guide/filters.html#ad

  • 管道符:| 第一个命令的输出作为第二个命令的输入
  • 全局:Vue.filter(name, function(data, arg1, arg2 …))
  • 局部:filters: {}
  • 参数解释:
    • data:被输入的值,管道符|前面的值
    • argN:作为函数参数
  • 如果不需要传入参数,则可以省略括号

全局过滤器

过滤器可以用在两个地方:双花括号插值和 v-bind 表达式

  1. <!-- 在双花括号中 -->
  2. {{ message | capitalize }}
  3. <!-- 在 `v-bind` 中 -->
  4. <div v-bind:id="rawId | formatId"></div>

全局过滤器的定义

  1. // 过滤器的定义语法
  2. Vue.filter('过滤器的名称', function(){})
  3. // 过滤器中的 function ,第一个参数,已经被规定死了,永远都是 过滤器 管道符前面 传递过来的数据
  4. Vue.filter('过滤器的名称', (data)=>{
  5. return data + '123';
  6. })

过滤器的使用示例

  1. <div id="app">
  2. <!-- 格式化为年月日 -->
  3. <p>{{ new Date() | dateFormat() }}</p>
  4. <!-- 给金额前加上¥货币符号 -->
  5. <p>{{ 399 | moneyFormat }}</p>
  6. <p>{{ 399 | moneyFormat('$')}}</p>
  7. <!-- 敏感词过滤 将其中单纯替换为** -->
  8. <p>{{ '曾经,我也是一个单纯的少年,单纯的我,傻傻的问,谁是世界上最单纯的男人!' | msgFormat('--') }}</p>
  9. </div>

Vue.filter定义全局过滤器

  1. // 时间过滤器
  2. Vue.filter('dateFormat', (data) => {
  3. const date = new Date(data)
  4. // 获取年月日
  5. const year = date.getFullYear()
  6. const month = date.getMonth()
  7. const day = date.getDate()
  8. return `${year} ${month} ${day} 日`
  9. });
  10. // 金额过滤器
  11. Vue.filter('moneyFormat',(data,sign="¥")=>{
  12. return sign+data;
  13. });
  14. // 过滤单纯敏感词
  15. Vue.filter('msgFormat', function(data, arg = "***") {
  16. return data.replace(/单纯/g, arg);
  17. });

私有过滤器

  • 私有过滤器,若全局过滤器中有同名过滤器,则优先调用私有过滤器

HTML元素:

  1. <div id="app">
  2. <p>{{ new Date() | dataFormat}}</p> <!-- 2021-05-05 18:26:25 -->
  3. <p>{{ new Date() | dataFormat('yyyy-mm-dd')}}</p> <!-- 2021-05-05 -->
  4. </div>

私有过滤器 filters 定义方式:

  1. // 全局过滤器
  2. Vue.filter('dateFormat', (data) => {
  3. const date = new Date(data)
  4. // 获取年月日
  5. const year = date.getFullYear()
  6. const month = date.getMonth()
  7. const day = date.getDate()
  8. return `${year} ${month} ${day} 日~~`
  9. });
  10. var vm = new Vue({
  11. el: '#app',
  12. data: {
  13. },
  14. methods: {
  15. },
  16. filters: { // 私有局部过滤器,只能在 当前 VM 对象所控制的 View 区域进行使用
  17. dataFormat(data, pattern = "") { // 在参数列表中 通过 pattern="" 来指定形参默认值,防止报错
  18. var dt = new Date(data);
  19. // 获取年月日
  20. var y = dt.getFullYear();
  21. var m = (dt.getMonth() + 1).toString().padStart(2, '0');
  22. var d = dt.getDate().toString().padStart(2, '0');
  23. // 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日
  24. // 否则,就返回 年-月-日 时:分:秒
  25. if (pattern.toLowerCase() === 'yyyy-mm-dd') {
  26. return `${y}-${m}-${d}`;
  27. } else {
  28. // 获取时分秒
  29. var hh = dt.getHours().toString().padStart(2, '0');
  30. var mm = dt.getMinutes().toString().padStart(2, '0');
  31. var ss = dt.getSeconds().toString().padStart(2, '0');
  32. return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
  33. }
  34. }
  35. }
  36. });
  37. // 使用ES6中的字符串新方法 String.prototype.padStart(maxLength, fillString='') 或 String.prototype.padEnd(maxLength, fillString='')来填充字符串长度;

注意:当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用!

串联过滤器

过滤器可以串联多个,前一个过滤器的返回值作为后一个的参数

给定一个状态 status ,值为 1、2、3
1:待付款 2:待发货 3:待收货
分别设置:红色、灰色、绿色

  1. <style>
  2. .no-pay {
  3. background-color: #ccc;
  4. padding: 10px;
  5. }
  6. .no-shipments {
  7. background-color: orange;
  8. padding: 10px;
  9. }
  10. .yes-shipments {
  11. background-color: green;
  12. padding: 10px;
  13. }
  14. .take-delivery {
  15. background-color: red;
  16. padding: 10px;
  17. }
  18. </style>
  19. <div id="app">
  20. <span v-bind:class="1 | getStatus | setStatusColor">{{ 1 | getStatus }}</span>
  21. <span v-bind:class="2 | getStatus | setStatusColor">{{ 2 | getStatus }}</span>
  22. <span v-bind:class="3 | getStatus | setStatusColor">{{ 3 | getStatus }}</span>
  23. <span v-bind:class="4 | getStatus | setStatusColor">{{ 4 | getStatus }}</span>
  24. </div>

filters定义局部过滤器

  1. filters: {
  2. // 判断是什么状态
  3. getStatus(data) {
  4. switch(data) {
  5. case 1:
  6. return '待付款'
  7. case 2:
  8. return '待发货'
  9. case 3:
  10. return '已发货'
  11. case 4:
  12. return '待收货'
  13. }
  14. },
  15. // 设置状态文字颜色
  16. setStatusColor(data) {
  17. switch(data) {
  18. case '待付款':
  19. return 'no-pay'
  20. case '待发货':
  21. return 'no-shipments'
  22. case '已发货':
  23. return 'yes-shipments'
  24. case '待收货':
  25. return 'take-delivery'
  26. }
  27. }
  28. }

自定义指令

官方文档地址:https://cn.vuejs.org/v2/guide/custom-directive.html

使用directive定义全局的指令,directives定义局部的指令。其中在定义的时候,指令的名称前不需要加v-前缀。

自定义全局指令:

  1. // 使用 Vue.directive() 定义全局的指令 v-focus
  2. // 其中:参数1 : 指令的名称,注意,在定义的时候,指令的名称前面,不需要加 v- 前缀,
  3. // 但是: 在调用的时候,必须 在指令名称前 加上 v- 前缀来进行调用
  4. // 参数2: 是一个对象,这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作
  5. Vue.directive('focus', {
  6. bind: function(el) { // 每当指令绑定到元素上的时候,会立即执行这个 bind 函数,只执行一次
  7. // 注意: 在每个 函数中,第一个参数,永远是 el ,表示 被绑定了指令的那个元素,这个 el 参数,是一个原生的JS对象
  8. // 在元素 刚绑定了指令的时候,还没有 插入到 DOM中去,这时候,调用 focus 方法没有作用
  9. // 因为,一个元素,只有插入DOM之后,才能获取焦点
  10. // el.focus()
  11. },
  12. inserted: function(el) { // inserted 表示元素 插入到DOM中的时候,会执行 inserted 函数【触发1次】
  13. el.focus()
  14. // 和JS行为有关的操作,最好在 inserted 中去执行
  15. },
  16. updated: function(el) { // 当VNode更新的时候,会执行 updated, 可能会触发多次
  17. }
  18. })
  19. // 自定义一个 设置字体颜色的 指令
  20. Vue.directive('color', {
  21. // 样式,只要通过指令绑定给了元素,不管这个元素有没有被插入到页面中去,这个元素肯定有了一个内联的样式
  22. // 将来元素肯定会显示到页面中,这时候,浏览器的渲染引擎必然会解析样式,应用给这个元素
  23. bind: function(el, binding) {
  24. // el.style.color = 'red'
  25. // console.log(binding.name)
  26. // 和样式相关的操作,一般都可以在 bind 执行
  27. // console.log(binding.value)
  28. // console.log(binding.expression)
  29. el.style.color = binding.value
  30. }
  31. })

自定义局部指令

  1. // 自定义局部指令 v-color 和 v-font-weight,为绑定的元素设置指定的字体颜色 和 字体粗细:
  2. new Vue({
  3. el: '#app',
  4. data:{},
  5. methods:{},
  6. filters:{},
  7. directives: {
  8. focus: {
  9. // 指令的定义 当被绑定的元素插入到 DOM 中时……
  10. inserted: function (el) {
  11. el.focus()
  12. }
  13. },
  14. color: { // 为元素设置指定的字体颜色
  15. bind(el, binding) {
  16. el.style.color = binding.value;
  17. }
  18. },
  19. 'font-weight': function (el, binding2) {
  20. // 自定义指令的简写形式,等同于定义了 bind 和 update 两个钩子函数
  21. el.style.fontWeight = binding2.value;
  22. }
  23. 'fontsize': function(el,binding){
  24. el.style.fontSize = parseInt(binding.value) + 'px';
  25. }
  26. }
  27. });

指令的函数简写

在很多时候,你可能想在 bindupdate 时触发相同行为,而不关心其它的钩子。比如这样写:

  1. Vue.directive('color-swatch', function (el, binding) {
  2. el.style.backgroundColor = binding.value
  3. })

自定义指令的使用

调用自定义指令时,需要在指令名称前加上v-前缀调用自定义指令。

  1. <input type="text" v-model="searchName" v-focus v-color="'red'" v-font-weight="900" fontsize='36'>

钩子函数及传入参数

一个指令定义对象可以提供如下几个钩子函数 (均为可选):

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。

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

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

指令钩子函数会被传入以下参数:

  • el:指令所绑定的元素,可以用来直接操作 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 }
  • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
  • oldVnode:上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用。
  1. <div id="directive" v-demo:foo.a.b="message"></div>
  2. Vue.directive('demo', {
  3. bind: function (el, binding, vnode) {
  4. var s = JSON.stringify
  5. el.innerHTML =
  6. 'name: ' + s(binding.name) + '<br>' +
  7. 'value: ' + s(binding.value) + '<br>' +
  8. 'expression: ' + s(binding.expression) + '<br>' +
  9. 'argument: ' + s(binding.arg) + '<br>' +
  10. 'modifiers: ' + s(binding.modifiers) + '<br>' +
  11. 'vnode keys: ' + Object.keys(vnode).join(', ')
  12. }
  13. });
  14. new Vue({
  15. el: '#directive',
  16. data: {
  17. message: 'hello!'
  18. }
  19. });

键盘修饰符

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

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

你可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。

  1. <input v-on:keyup.page-down="onPageDown">

在上述示例中,处理函数只会在 $event.key 等于 PageDown 时被调用。

内置按键码

Vue 提供了绝大多数常用的按键码的别名

  • .enter
  • .tab
  • .delete (捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

使用按键码别名可以做到兼容:
有一些按键 (.esc 以及所有的方向键) 在 IE9 中有不同的 key 值, 如果你想支持 IE9,这些内置的别名应该是首选。

1.x中自定义键盘修饰符【了解即可】

  1. Vue.directive('on').keyCodes.f2 = 113;

2.x中自定义键盘修饰符

  1. 通过Vue.config.keyCodes.名称 = 按键值来自定义案件修饰符的别名:
  1. Vue.config.keyCodes.f2 = 113;
  1. 使用自定义的按键修饰符:
  1. <input type="text" v-model="name" @keyup.f2="add">

vue实例的生命周期

  • 什么是生命周期:从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期!
  • 生命周期钩子:就是生命周期事件的别名而已;
  • 生命周期钩子 = 生命周期函数 = 生命周期事件

这张图对于Vue的生命周期和钩子函数说的非常的明白。下面我们看一张关于Vue生命周期中出现的钩子函数示意图。

Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、销毁等一系列过程,我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程,就是生命周期。

Vue应用程序中有4个主要事件(8个主要钩子)。主要的生命周期函数分类:

  • 创建 — 在组件创建时执行
  • 挂载 — DOM 被挂载时执行
  • 更新 — 当响应数据被修改时执行
  • 销毁 — 在元素被销毁之前立即运行

创建期间的生命周期函数:

  • beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
  • created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板

挂载期间的生命周期函数:

  • beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中
  • mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示

更新期间的生命周期函数:

  • beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点
  • updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!

销毁期间的生命周期函数:

  • beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
  • destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>生命周期全过程</title>
  5. <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <p>{{ message }}</p>
  10. </div>
  11. <script type="text/javascript">
  12. var app = new Vue({
  13. el: '#app',
  14. data: {
  15. message : "hello world!"
  16. },
  17. beforeCreate: function () {
  18. console.group('beforeCreate 创建前状态===============》');
  19. console.log("%c%s", "color:red" , "el : " + this.$el); //undefined
  20. console.log("%c%s", "color:red","data : " + this.$data); //undefined
  21. console.log("%c%s", "color:red","message: " + this.message) //undefined
  22. },
  23. created: function () {
  24. console.group('created 创建完毕状态===============》');
  25. console.log("%c%s", "color:red","el : " + this.$el); //undefined
  26. console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
  27. console.log("%c%s", "color:red","message: " + this.message); //已被初始化
  28. },
  29. beforeMount: function () {
  30. console.group('beforeMount 挂载前状态===============》');
  31. console.log("%c%s", "color:red","el : " + (this.$el)); //已被初始化
  32. console.log(this.$el);
  33. console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
  34. console.log("%c%s", "color:red","message: " + this.message); //已被初始化
  35. },
  36. mounted: function () {
  37. console.group('mounted 挂载结束状态===============》');
  38. console.log("%c%s", "color:red","el : " + this.$el); //已被初始化
  39. console.log(this.$el);
  40. console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
  41. console.log("%c%s", "color:red","message: " + this.message); //已被初始化
  42. },
  43. beforeUpdate: function () {
  44. console.group('beforeUpdate 更新前状态===============》');
  45. console.log("%c%s", "color:red","el : " + this.$el);
  46. console.log(document.querySelector('#app').innerHTML);
  47. console.log("%c%s", "color:red","data : " + this.$data);
  48. console.log("%c%s", "color:red","message: " + this.message);
  49. },
  50. updated: function () {
  51. console.group('updated 更新完成状态===============》');
  52. console.log("%c%s", "color:red","el : " + this.$el);
  53. console.log(document.querySelector('#app').innerHTML);
  54. console.log("%c%s", "color:red","data : " + this.$data);
  55. console.log("%c%s", "color:red","message: " + this.message);
  56. },
  57. beforeDestroy: function () {
  58. console.group('beforeDestroy 销毁前状态===============》');
  59. console.log("%c%s", "color:red","el : " + this.$el);
  60. console.log(this.$el);
  61. console.log("%c%s", "color:red","data : " + this.$data);
  62. console.log("%c%s", "color:red","message: " + this.message);
  63. },
  64. destroyed: function () {
  65. console.group('destroyed 销毁完成状态===============》');
  66. console.log("%c%s", "color:red","el : " + this.$el);
  67. console.log(this.$el);
  68. console.log("%c%s", "color:red","data : " + this.$data);
  69. console.log("%c%s", "color:red","message: " + this.message)
  70. }
  71. })
  72. </script>
  73. </body>
  74. </html>

每一个组件或者实例都会经历一个完整的生命周期,总共分为三个阶段:初始化、运行中、销毁。

  1. 实例、组件通过new Vue() 创建出来之后会初始化事件和生命周期,然后就会执行beforeCreate钩子函数,这个时候,数据还没有挂载呢,只是一个空壳,无法访问到数据和真实的dom,一般不做操作
  2. 挂载数据,绑定事件等等,然后执行created函数,这个时候已经可以使用到数据,也可以更改数据,在这里更改数据不会触发updated函数,在这里可以在渲染前倒数第二次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取
  3. 接下来开始找实例或者组件对应的模板,编译模板为虚拟dom放入到render函数中准备渲染,然后执行beforeMount钩子函数,在这个函数中虚拟dom已经创建完成,马上就要渲染,在这里也可以更改数据,不会触发updated,在这里可以在渲染前最后一次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取
  4. 接下来开始render,渲染出真实dom,然后执行mounted钩子函数,此时,组件已经出现在页面中,数据、真实dom都已经处理好了,事件都已经挂载好了,可以在这里操作真实dom等事情…
  5. 当组件或实例的数据更改之后,会立即执行beforeUpdate,然后vue的虚拟dom机制会重新构建虚拟dom与上一次的虚拟dom树利用diff算法进行对比之后重新渲染,一般不做什么事儿
  6. 当更新完成后,执行updated,数据已经更改完成,dom也重新render完成,可以操作更新后的虚拟dom
  7. 当经过某种途径调用$destroy方法后,立即执行beforeDestroy,一般在这里做一些善后工作,例如清除计时器、清除非指令绑定的事件等等
  8. 组件的数据绑定、监听…去掉后只剩下dom空壳,这个时候,执行destroyed,在这里做善后工作也可以
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>完整的生命周期</title>
  8. </head>
  9. <body>
  10. <div id="app">
  11. <my-template></my-template>
  12. </div>
  13. <template id="myTemplate">
  14. <div>
  15. <p class="myp">A组件</p>
  16. <button @click="destroy">destroy</button>
  17. <input type="text" v-model="msg">
  18. <p>msg:{{msg}}</p>
  19. </div>
  20. </template>
  21. </body>
  22. <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script>
  23. <script>
  24. //生命周期:初始化阶段 运行中阶段 销毁阶段
  25. Vue.component("myTemplate",{
  26. template:"#myTemplate",
  27. data:function(){
  28. return {msg:'hello'}
  29. },
  30. timer:null,
  31. methods:{
  32. destroy:function(){
  33. this.$destroy(); // 断点 销毁组件
  34. }
  35. },
  36. beforeCreate:function(){
  37. console.group('beforeCreate 创建前状态===============》');
  38. console.log('beforeCreate:刚刚new Vue()之后,这个时候,数据还没有挂载呢,只是一个空壳')
  39. console.log(this.msg)//undefined
  40. console.log(document.getElementsByClassName("myp")[0])//undefined
  41. },
  42. created:function(){
  43. console.group('created 创建完毕状态===============》');
  44. console.log('created:这个时候已经可以使用到数据,也可以更改数据,在这里更改数据不会触发updated函数')
  45. this.msg+='!!!'
  46. console.log('在这里可以在渲染前倒数第二次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取')
  47. console.log('接下来开始找实例或者组件对应的模板,编译模板为虚拟dom放入到render函数中准备渲染')
  48. },
  49. beforeMount:function(){
  50. console.group('beforeMount 挂载前状态===============》');
  51. console.log('beforeMount:虚拟dom已经创建完成,马上就要渲染,在这里也可以更改数据,不会触发updated')
  52. this.msg+='@@@@';
  53. console.log('在这里可以在渲染前最后一次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取')
  54. console.log(document.getElementsByClassName("myp")[0])//undefined
  55. console.log('接下来开始render,渲染出真实dom')
  56. },
  57. // render:function(createElement){
  58. // console.log('render')
  59. // return createElement('div','hahaha')
  60. // },
  61. mounted:function(){
  62. console.group('beforeMount 挂载结束状态===============》');
  63. console.log('mounted:此时,组件已经出现在页面中,数据、真实dom都已经处理好了,事件都已经挂载好了')
  64. console.log(document.getElementsByClassName("myp")[0])
  65. console.log('可以在这里操作真实dom等事情...')
  66. // this.$options.timer = setInterval(function () {
  67. // console.log('setInterval')
  68. // this.msg+='!'
  69. // }.bind(this),500)
  70. },
  71. beforeUpdate:function(){
  72. console.group('updated 更新前状态===============》');
  73. //这里不能更改数据,否则会陷入死循环
  74. console.log('beforeUpdate:重新渲染之前触发')
  75. console.log('然后vue的虚拟dom机制会重新构建虚拟dom与上一次的虚拟dom树利用diff算法进行对比之后重新渲染')
  76. },
  77. updated:function(){
  78. console.group('updated 更新完成状态===============》');
  79. //这里不能更改数据,否则会陷入死循环
  80. console.log('updated:数据已经更改完成,dom也重新render完成')
  81. },
  82. beforeDestroy:function(){
  83. console.group('destroyed 销毁前状态===============》');
  84. console.log('beforeDestory:销毁前执行($destroy方法被调用的时候就会执行),一般在这里善后:清除计时器、清除非指令绑定的事件等等...')
  85. // clearInterval(this.$options.timer)
  86. },
  87. destroyed:function(){
  88. console.group('destroyed 销毁完成状态===============》');
  89. console.log('destroyed:组件的数据绑定、监听...都去掉了,只剩下dom空壳,这里也可以善后')
  90. }
  91. })
  92. const vm = new Vue({
  93. }).$mount('#app')
  94. </script>
  95. </html>

生命周期总结

这么多钩子函数,我们怎么用呢,我想大家可能有这样的疑问吧,我也有,哈哈哈。

  • beforecreate : 举个栗子:可以在这加个loading事件
  • created :在这结束loading,还做一些初始化,实现函数自执行
  • mounted : 在这发起后端请求,拿回数据,配合路由钩子做一些事情
  • beforeDestory: 你确认删除XX吗?
  • destoryed :当前组件已被删除,清空相关内容

Vue中的动画

为什么要有动画:动画能够提高用户的体验,帮助用户更好的理解页面中的功能;

使用过渡类名

  1. HTML结构:
  1. <div id="app">
  2. <input type="button" value="动起来" @click="myAnimate">
  3. <!-- 使用 transition 将需要过渡的元素包裹起来 -->
  4. <transition name="fade">
  5. <div v-show="isshow">动画哦</div>
  6. </transition>
  7. </div>
  1. VM 实例:
  1. // 创建 Vue 实例,得到 ViewModel
  2. var vm = new Vue({
  3. el: '#app',
  4. data: {
  5. isshow: false
  6. },
  7. methods: {
  8. myAnimate() {
  9. this.isshow = !this.isshow;
  10. }
  11. }
  12. });
  1. 定义两组类样式:
  1. /* 定义进入和离开时候的过渡状态 */
  2. .fade-enter-active,
  3. .fade-leave-active {
  4. transition: all 0.2s ease;
  5. position: absolute;
  6. }
  7. /* 定义进入过渡的开始状态 和 离开过渡的结束状态 */
  8. .fade-enter,
  9. .fade-leave-to {
  10. opacity: 0;
  11. transform: translateX(100px);
  12. }

使用第三方 CSS 动画库

  1. 导入动画类库:
  1. <link rel="stylesheet" type="text/css" href="./lib/animate.css">
  1. 定义 transition 及属性:
  1. <transition
  2. enter-active-class="fadeInRight"
  3. leave-active-class="fadeOutRight"
  4. :duration="{ enter: 500, leave: 800 }">
  5. <div class="animated" v-show="isshow">动画哦</div>
  6. </transition>

使用动画钩子函数

  1. 定义 transition 组件以及三个钩子函数:
  1. <div id="app">
  2. <input type="button" value="切换动画" @click="isshow = !isshow">
  3. <transition
  4. @before-enter="beforeEnter"
  5. @enter="enter"
  6. @after-enter="afterEnter">
  7. <div v-if="isshow" class="show">OK</div>
  8. </transition>
  9. </div>
  1. 定义三个 methods 钩子方法:
  1. methods: {
  2. beforeEnter(el) { // 动画进入之前的回调
  3. el.style.transform = 'translateX(500px)';
  4. },
  5. enter(el, done) { // 动画进入完成时候的回调
  6. el.offsetWidth;
  7. el.style.transform = 'translateX(0px)';
  8. done();
  9. },
  10. afterEnter(el) { // 动画进入完成之后的回调
  11. this.isshow = !this.isshow;
  12. }
  13. }
  1. 定义动画过渡时长和样式:
  1. .show{
  2. transition: all 0.4s ease;
  3. }

v-for 的列表过渡

  1. 定义过渡样式:
  1. <style>
  2. .list-enter,
  3. .list-leave-to {
  4. opacity: 0;
  5. transform: translateY(10px);
  6. }
  7. .list-enter-active,
  8. .list-leave-active {
  9. transition: all 0.3s ease;
  10. }
  11. </style>
  1. 定义DOM结构,其中,需要使用 transition-group 组件把v-for循环的列表包裹起来:
  1. <div id="app">
  2. <input type="text" v-model="txt" @keyup.enter="add">
  3. <transition-group tag="ul" name="list">
  4. <li v-for="(item, i) in list" :key="i">{{item}}</li>
  5. </transition-group>
  6. </div>
  1. 定义 VM中的结构:
  1. // 创建 Vue 实例,得到 ViewModel
  2. var vm = new Vue({
  3. el: '#app',
  4. data: {
  5. txt: '',
  6. list: [1, 2, 3, 4]
  7. },
  8. methods: {
  9. add() {
  10. this.list.push(this.txt);
  11. this.txt = '';
  12. }
  13. }
  14. });

列表的排序过渡

<transition-group> 组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的 v-move 特性,它会在元素的改变定位的过程中应用

  • v-movev-leave-active 结合使用,能够让列表的过渡更加平缓柔和:
  1. .v-move{
  2. transition: all 0.8s ease;
  3. }
  4. .v-leave-active{
  5. position: absolute;
  6. }

创建组件

什么是组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可;
组件化和模块化的不同:

  • 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一;
  • 组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用;

创建全局组件

  • 注意:模板中有且只有一个根元素,就像一个网页只有一个根元素为<html>一样

第一种方式

  • 使用 Vue.extend 配合 Vue.component 方法:
  1. var com1 = Vue.extend({
  2. template: '<h2>第一种方式</h2>',
  3. data(){
  4. return{
  5. }
  6. }
  7. });
  8. Vue.component('mycom1', com1);
  • 直接使用 Vue.component 方法:
  1. Vue.component('mycom2', {
  2. template: '<h4>第一种方式的最终简写</h4>',
  3. data(){
  4. return {
  5. }
  6. }
  7. });

第二种创建方式

  • 将模板字符串,定义到script标签中:
  1. <script id="tmpl" type="template">
  2. <div>
  3. <a href="#">{{msg.login}}</a> | <a href="#">{{msg.register}}</a>
  4. </div>
  5. </script>
  • 同时,需要使用 Vue.component 来定义组件:
  1. Vue.component('account', {
  2. template: '#tmpl',
  3. data(){
  4. return{
  5. msg: {login: '登录',register: '注册'}
  6. }
  7. }
  8. });

第三种创建方式

  • <template>中定义 HTML 结构
  1. <template id="tmp">
  2. <div>
  3. <h2>第二种方式</h2>
  4. <h3 v-text="msg"></h3>
  5. </div>
  6. </template>

使用 Vue.component 中 template 属性 进行调用

  1. Vue.component('myCom3', {
  2. template: '#tmp',
  3. data(){
  4. return{
  5. msg: '好耶~'
  6. }
  7. }
  8. });

创建私有组件

  1. new Vue({
  2. el: '#app',
  3. data: {},
  4. methods:{},
  5. filters:{},
  6. directives:{},
  7. components: {
  8. myLogin: {
  9. template: `<div>
  10. <h1>login 组件</h1>
  11. <p>私有组件</p>
  12. </div>`,
  13. data(){
  14. return{
  15. }
  16. }
  17. }
  18. },
  19. beforeCreate(){},
  20. created(){},
  21. beforeMount(){},
  22. Mounted(){},
  23. beforeUpdate(){},
  24. updated(){},
  25. beforeDestroy(){},
  26. destroyed(){}
  27. });

注意: 组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹!

分离私有组件的定义,将组件模板对象分开定义

  1. const myLogin = {
  2. template: `<div>
  3. <h1>login 组件</h1>
  4. <p>私有组件</p>
  5. </div>`,
  6. data(){
  7. return{
  8. }
  9. }
  10. }
  11. new Vue({
  12. el: '#app',
  13. data: {},
  14. methods:{},
  15. components: {
  16. // 组件名称:组件的模板对象
  17. // myLogin: myLogin
  18. myLogin,
  19. }
  20. });

组件的嵌套

我们的子组件是可以多级嵌套的,子组件中还可以在声明子组件。

  1. <div id="app">
  2. <account></account>
  3. </div>
  4. <script>
  5. // 创建 Vue 实例,得到 ViewModel
  6. var vm = new Vue({
  7. el: '#app',
  8. data: {},
  9. methods: {},
  10. components: {
  11. account: {
  12. template: `<div>
  13. <h1>这是Account组件{{name}}</h1>
  14. <login></login>
  15. <register></register>
  16. </div>`,
  17. data(){
  18. return {
  19. name: 'hello world!'
  20. }
  21. },
  22. components: {
  23. login: {
  24. template: "<h3>这是登录组件</h3>"
  25. },
  26. register: {
  27. template: '<h3>这是注册组件</h3>'
  28. }
  29. }
  30. }
  31. }
  32. });
  33. </script>

组件使用方式

  • 在HTML中直接用标签应用即可
  1. <div id="app">
  2. <mycom1></mycom1>
  3. <mycom2></mycom2>
  4. <my-com3></my-com3>
  5. <my-login></my-login>
  6. </div>
  • 非驼峰命名法,则直接照着写就好了
  • 驼峰命名法:定义时使用驼峰,使用时需要以 “ - ” 分隔

组件中展示数据和响应事件

  • data 定义方式不太一样,但是使用方式和实例中的 data 没什么太大区别
  • 因为组件需要被重用,为了让每个组件有自己的私有作用域,data需要为函数并且返回一个对象

  • 在组件中,data需要被定义为一个方法

通过计数器案例演示

  1. <div id="app">
  2. <myCount></myCount><hr>
  3. <myCount></myCount><hr>
  4. <myCount></myCount><hr>
  5. </div>
  6. <template id="tmp">
  7. <input type="button" value="+1" @click="increment">
  8. <h3 v-text="count"></h3>
  9. </template>
  10. <script>
  11. Vue.component('myCount',{
  12. template: '#tmp',
  13. data: function (){
  14. return {
  15. count: 0
  16. }
  17. },
  18. methods:{
  19. increment(){
  20. this.count++;
  21. }
  22. }
  23. });
  24. new Vue({
  25. el:'#app',
  26. data:{ },
  27. methods:{ }
  28. })
  29. </script>

组件切换

使用 v-if 和 v-else 配合进行切换

  1. <div id="app">
  2. <a href="" @click.prevent="flag = true">登录</a>
  3. <a href="" @click.prevent="flag = false">注册</a>
  4. <transition mode="out-in">
  5. <login v-if="flag"></login>
  6. <register v-else="flag"></register>
  7. </transition>
  8. </div>
  9. <script>
  10. Vue.component('login',{
  11. template: '<h3>登录组件</h3>'
  12. })
  13. Vue.component('register',{
  14. template: '<h3>注册组件</h3>'
  15. })
  16. new Vue({
  17. el: '#app',
  18. data:{
  19. flag: true,
  20. },
  21. methods:{
  22. }
  23. })
  24. </script>

使用<component>进行切换

  • :is 指定要显示的组件名称
  1. <style>
  2. .v-enter,
  3. .v-leave-to {
  4. opacity: 0;
  5. transform: translateX(150px);
  6. }
  7. .v-enter-active,
  8. .v-leave-active {
  9. transition: all .5s ease;
  10. }
  11. </style>
  12. <div id="app">
  13. <a href="" @click.prevent="comName = 'login'">登录</a>
  14. <a href="" @click.prevent="comName = 'register'">注册</a>
  15. <a href="" @click.prevent="comName = 'forget'">忘记密码</a>
  16. <!-- 通过mode属性,设置组件切换时候的模式 -->
  17. <transition mode="out-in">
  18. <component :is="comName"></component>
  19. </transition>
  20. </div>
  21. <script>
  22. Vue.component('login',{
  23. template: '<h3>登录组件</h3>'
  24. })
  25. Vue.component('register',{
  26. template: '<h3>注册组件</h3>'
  27. })
  28. Vue.component('forget',{
  29. template: '<h3>忘记密码组件</h3>'
  30. })
  31. new Vue({
  32. el: '#app',
  33. data:{
  34. comName: 'login'
  35. },
  36. methods:{
  37. }
  38. })
  39. </script>

父组件向子组件传值(属性)

  • 注意:一定要使用props属性来定义父组件传递过来的数据,由于 props 默认是单向绑定,即父组件的属性发生改变,子组件也会改变。反之则不会。props中数据是只读的

  • 使用v-bind或简化指令:,将数据传递到子组件中

  1. <div id="app">
  2. <!-- 父组件可以在引用子组件的时候,通过属性绑定的形式 ,把需要传递给子组件的数据,以属性绑定的形式传递到子组件内部供使用-->
  3. <my-component v-bind:parentmsg="msg" :msg="message"></my-component>
  4. </div>
  5. <script>
  6. Vue.component('myComponent', {
  7. template: '<span>{{ parentmsg }}--{{msg}}</span>',
  8. // props:声明待接收的父组件数据
  9. // props中数据都是只读的,无法重新赋值
  10. props: ['parentmsg', 'msg'],
  11. // 把父组件传递过来的parentmsg和msg属性,现在props数组中定义一下,这样才能使用父组件传递过来的数据
  12. data() {
  13. return {
  14. // 子组件中也可以有自己的数据在data中
  15. }
  16. }
  17. })
  18. var vm = new Vue({
  19. el: '#app',
  20. data: {
  21. msg: 'hello 子组件',
  22. message: 'hello world'
  23. }
  24. })
  25. </script>

绑定修饰符

  1. <div id="app">
  2. <!-- 默认 单向绑定 -->
  3. <my-component :msg="message"></my-component>
  4. <!-- 双向绑定 -->
  5. <my-component :msg.sync="message"></my-component>
  6. <!-- 单向绑定 -->
  7. <my-component :msg.once="message"></my-component>
  8. </div>
  • 注意:若传递引用类型,则无论哪种绑定类型,在子组件修改 props 都会影响父组件的状态。

props 类型验证

  1. Vue.component('my-component', {
  2. props: {
  3. // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
  4. propA: Number,
  5. // 多个可能的类型
  6. propB: [String, Number],
  7. // 必填的字符串
  8. propC: {
  9. type: String,
  10. required: true
  11. },
  12. // 带有默认值的数字
  13. propD: {
  14. type: Number,
  15. default: 100
  16. },
  17. // 带有默认值的对象
  18. propE: {
  19. type: Object,
  20. // 对象或数组默认值必须从一个工厂函数获取
  21. default: function () {
  22. return { message: 'hello' }
  23. }
  24. },
  25. // 自定义验证函数
  26. propF: {
  27. validator: function (value) {
  28. // 这个值必须匹配下列字符串中的一个
  29. return ['success', 'warning', 'danger'].indexOf(value) !== -1
  30. }
  31. }
  32. }
  33. })

父子组件间的事件通信(方法)

  • 使用事件绑定机制,父组件向子组件传递方法,绑定后,子组件可以通过某些方式来调用传递过来的这个方法了
  • 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;
  • 父组件将方法的引用传递给子组件,其中,getMsg是父组件中methods中定义的方法名称,func是子组件调用传递过来方法时候的方法名称
  1. <son v-on:func="show"></son>
  • 子组件内部通过this.$emit('方法名', 要传递的数据)方式,来调用父组件中的方法,同时把数据传递给父组件使用
  1. <div id="app">
  2. <h1>{{msg}}</h1>
  3. <!-- 父组件向子组件传递方法,使用的是事件绑定机制 -->
  4. <!-- v-on,当我们自定义了一个事件属性之后,那么子组件就能够通过某种方式来调用 -->
  5. <son @func="getMsg"></son>
  6. </div>
  7. <!-- 组件模板定义 -->
  8. <template id="son">
  9. <div>
  10. <h1>这是子组件</h1>
  11. <input type="button" value="向父组件传值" @click="sendMsg" />
  12. </div>
  13. </template>
  14. <script>
  15. const son = {
  16. template: '#son', // 组件模板Id
  17. data(){
  18. return {
  19. msg: "我是子组件传递过来的数据"
  20. }
  21. },
  22. methods: {
  23. sendMsg() { // 按钮的点击事件
  24. // 我们可以通过调用父组件传递过来的方法,将子组件数据通过参数形式传递给父组件中
  25. this.$emit('func', this.msg); // 调用父组件传递过来的方法,同时把数据传递出去
  26. }
  27. }
  28. }
  29. // 子组件的定义方式
  30. Vue.component('son',son);
  31. // 创建 Vue 实例,得到 ViewModel
  32. var vm = new Vue({
  33. el: '#app',
  34. data: {
  35. msg: "父组件"
  36. },
  37. methods: {
  38. getMsg(val){ // 子组件中,通过 this.$emit() 实际调用的方法,在此进行定义
  39. this.msg = val;
  40. console.log("调用了父组件身上的getMsg方法");
  41. alert(val);
  42. }
  43. }
  44. });
  45. </script>

$emit() 与 $on() 函数

  • vm.$emit( event, arg ) // 触发当前实例上的事件
  • vm.$on( event, fn ) // 监听 event 事件后运行 fn;
  • 用法:
    1. 父组件可以使用 props 把数据传给子组件。
    2. 子组件可以使用 $emit 触发父组件的自定义事件。

非父子组件事件通信

父子组件访问

  • $parent:访问当前组件的父组件
  • $root:访问当前主键的根组件

ref 获取DOM元素和组件

  • 我们可以在元素上使用ref属性,然后赋予一个优雅的名字
  1. <h3 ref="myh3">我是H3</h3>
  2. <!-- login组件 -->
  3. <login ref="login"></login>
  • 在实例中,我们通过$refs来获取DOM
  1. getElement() { console.log(this.$refs.myh3); console.log(this.$refs.login.$el.innerText);}

组件的应用案例

分页组件

  1. <style>
  2. .pagination {
  3. display: flex;
  4. list-style: none;
  5. }
  6. .page-item {
  7. width: 25px;
  8. height: 25px;
  9. line-height: 25px;
  10. text-align: center;
  11. border: 1px solid #ccc;
  12. }
  13. .page-item.active {
  14. background-color: skyblue;
  15. }
  16. </style>
  17. <div id="app">
  18. <page-component :total="total"></page-component>
  19. </div>
  20. <template id="page-component">
  21. <ul class="pagination">
  22. <li :class="p == page ? 'page-item active' : 'page-item'" v-for="p in pagecount">
  23. <a href="#" class="page-link" @click.prevent="page = p">{{ p }}</a>
  24. </li>
  25. </ul>
  26. </template>
  27. <script>
  28. const pageComponent = {
  29. template: '#page-component',
  30. name: 'PageComponent',
  31. props: ['total'],
  32. data: function() {
  33. return {
  34. page: 1, // 当前页码
  35. pagesize: 10 // 每页显示条数
  36. }
  37. },
  38. computed: {
  39. pagecount: function() {
  40. // 总页码数
  41. return Math.ceil(this.total / this.pagesize)
  42. }
  43. }
  44. }
  45. var vm = new Vue({
  46. el: '#app',
  47. data: {
  48. total: 35
  49. },
  50. components: {
  51. pageComponent
  52. }
  53. })
  54. </script>

购物车组件

  1. <div id="app">
  2. <div v-for="goods in goodslist">
  3. <p>商品名称:{{ goods.name }}</p>
  4. <p>单价:{{ goods.price }}</p>
  5. <cart-component v-model="goods.count"></cart-component>
  6. <hr>
  7. </div>
  8. <div>
  9. 订单总金额:{{ amount }} 元
  10. </div>
  11. </div>
  12. <template id="cart-component">
  13. <div class="cart">
  14. <button @click="count--; updateCount();">-</button>
  15. <input type="text" v-model="count" style="width: 50%;" @input="updateCount()">
  16. <button @click="count++; updateCount();">+</button>
  17. </div>
  18. </template>
  19. <script>
  20. const cartComponent = {
  21. name: 'Cart',
  22. template: '#cart-component',
  23. // 在组件中不允许直接修改 props 中的数据
  24. props: ['value'],
  25. data: function() {
  26. return {
  27. count: this.value
  28. }
  29. },
  30. methods: {
  31. // v-model 指令双向数据绑定,修改父组件内容
  32. updateCount: function() {
  33. // 触发 input 事件
  34. this.$emit('input', this.count)
  35. }
  36. }
  37. }
  38. const app = new Vue({
  39. el: '#app',
  40. data: {
  41. goodslist: [
  42. {
  43. name: 'iphone 8 plus',
  44. price: 5888,
  45. count: 0
  46. },
  47. {
  48. name: 'iphone x',
  49. price: 7222,
  50. count: 0
  51. }
  52. ]
  53. },
  54. computed: {
  55. // 当前订单总金额
  56. amount: function() {
  57. var money = 0;
  58. this.goodslist.forEach(goods => {
  59. money += parseInt(goods.count) * parseInt(goods.price)
  60. })
  61. return money;
  62. }
  63. },
  64. components: {
  65. cartComponent
  66. }
  67. })
  68. </script>

评论列表案例

  1. <div id="app">
  2. <cmt-box @loadcomments="loadComments"></cmt-box>
  3. <ul class="list-group">
  4. <li class="list-group-item" v-for="item in list" :key="item.id">
  5. <span class="badge">评论人:{{item.user}}</span> {{item.content}}
  6. </li>
  7. </ul>
  8. </div>
  9. <template id="temp">
  10. <div>
  11. <div class="form-group">
  12. <label for="">评论人:</label>
  13. <input type="text" class="form-control" v-model="user">
  14. </div>
  15. <div class="form-group">
  16. <label for="">评论内容:</label>
  17. <textarea class="form-control" v-model="content"></textarea>
  18. </div>
  19. <div class="form-group">
  20. <input type="button" value="发表评论" @click="postComment">
  21. </div>
  22. </div>
  23. </template>
  24. <script>
  25. const vm = new Vue({
  26. el: '#app',
  27. data: {
  28. list: []
  29. },
  30. methods: {
  31. loadComments() {
  32. const list = JSON.parse(localStorage.getItem('cmts') || '[]');
  33. this.list = list;
  34. console.log(6666);
  35. }
  36. },
  37. created() {
  38. this.loadComments();
  39. },
  40. components: {
  41. 'cmt-box': {
  42. template: '#temp',
  43. data() {
  44. return {
  45. user: '',
  46. content: ''
  47. }
  48. },
  49. methods: {
  50. postComment() {
  51. const comment = {
  52. id: Date.now(),
  53. user: this.user,
  54. content: this.content
  55. };
  56. // 从localStorage中获取所有的评论
  57. const list = JSON.parse(localStorage.getItem('cmts') || '[]');
  58. list.unshift(comment);
  59. // 保存最新的评论数据
  60. localStorage.setItem('cmts', JSON.stringify(list));
  61. this.user = this.content = '';
  62. this.$emit('loadcomments');
  63. }
  64. },
  65. }
  66. }
  67. });
  68. </script>
声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议