Blogger Information
Blog 128
fans 9
comment 5
visits 241316
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
【Vue框架学习】过滤器、自定义指令、生命周期、动画、组件、路由等知识点归纳总结
 一纸荒凉* Armani
Original
887 people have browsed it

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>
Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post