一、Vue 实例的生命周期

1.1 什么是生命周期?

Vue 的实例具有生命周期,Vue 的实例在生成的时候,会经历一系列的初始化的过程;数据的监听,编译模板,实例挂载 DOM 元素,或者数据更新导致 DOM 更新,在执行的过程中,会运行一些叫做生命周期的钩子函数,在 Vue 实例生命周期中特定的时间点执行的函数称为生命周期的钩子函数;

如果我们需要在某个生命周期处理一些事情,我们可以把这些事情写在钩子函数中;等到 Vue 的实例生命周期到这个阶段就会执行这个钩子,而我们要做的事情也就得以处理了;

  • 生命周期的钩子函数不能人为的控制其执行的顺序;

1.2 常用的生命周期

  • beforeCreate 在实例初始化之后,数据观测 (data observer) 和 watch 配置之前被调用。
  • created 在实例创建完成后被立即调用。在这一步,实例已完成数据观测、属性和方法的运算、watch/event 事件回调;但是在现阶段还没有开始挂载,即还没挂载到根 DOM 元素上,所以 this.$el 属性不可见
  • beforeMount 在挂载开始之前被调用,创建虚拟 DOM(Virtual-DOM);虚拟 DOM 不是真实的 DOM 元素,而是 js 对象,其中包含了渲染成 DOM 元素信息;
  • mounted 把 Vue 的虚拟 DOM 挂载到真实的 DOM 上;如果要在 Vue 中获取 DOM 元素对象,一般在这个钩子中获取;项目中的 ajax 请求一般会在这里或者 created 里发送;
  • beforeUpdate 只有当数据发生变化时,才会触发这个函数;
  • updated 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用 updated。
  • beforeDestroy 在 Vue 的实例被销毁之前调用,如果页面中有定时器,我们会在这个钩子中清除定时器;
  • destroyed Vue 实例销毁后调用,实例中的属性也不再是响应式的,watch 被移除

1.3 示例代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <div @click="fn">{{msg}}</div>
  10. </div>
  11. <script src="vue.js"></script>
  12. <script>
  13. // 生命周期:
  14. // Vue 的实例具有生命周期,Vue 的实例在生成的时候,会经历一系列的初始化的过程;数据的监听,编译模板,实例挂载DOM元素,或者数据更新导致 DOM 更新,在执行的过程中,会运行一些叫做生命周期的钩子函数,在 Vue 实例生命周期中特定的时间点执行的函数称为生命周期的钩子函数;
  15. // 如果我们需要在某个生命周期处理一些事情,我们可以把这些事情写在钩子函数中;等到 Vue 的实例生命周期到这个阶段就会执行这个钩子,而我们要做的事情也就得以处理了
  16. // 生命周期的钩子函数不能人为的控制其执行的顺序;
  17. let vm = new Vue({
  18. data: {
  19. msg: 'hello'
  20. },
  21. methods: {
  22. fn() {console.log(11111)}
  23. },
  24. beforeCreate() {
  25. // 在实例初始化之后,数据观测 (data observer) 和 watch 配置之前被调用。
  26. console.log(1);
  27. console.log(this.msg);
  28. console.log(this.$el); // this.$el 是根 DOM 元素
  29. },
  30. created() {
  31. // 在实例创建完成后被立即调用。在这一步,实例已完成数据观测、属性和方法的运算、watch/event 事件回调
  32. // 但是在现阶段还没有开始挂载,即还没挂载到根 DOM 元素上,所以 this.$el 属性不可见
  33. console.log(2);
  34. console.log(this.msg);
  35. console.log(this.$el);
  36. },
  37. beforeMount() {
  38. // 在挂载开始之前被调用,创建虚拟DOM(Virtual-DOM);虚拟 DOM 不是真实的 DOM 元素,而是 js 对象,其中包含了渲染成 DOM 元素信息;
  39. console.log(3);
  40. console.log(this.msg);
  41. console.log(this.$el);
  42. },
  43. mounted() {
  44. // 把 Vue 的虚拟DOM挂载到真实的 DOM 上;
  45. // 如果要在 Vue 中获取 DOM 元素对象,一般在这个钩子中获取
  46. // 项目中的 ajax 请求一般会在这里或者 created 里发送;
  47. console.log(4);
  48. console.log(this.msg);
  49. console.log(this.$el);
  50. },
  51. // 只有当数据发生变化时,才会触发这个函数;
  52. beforeUpdate() {
  53. console.log(5)
  54. },
  55. updated() {
  56. // 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
  57. console.log(6);
  58. },
  59. beforeDestroy() {
  60. // 在 Vue 的实例被销毁之前调用,如果页面中有定时器,我们会在这个钩子中清除定时器;
  61. console.log(7);
  62. },
  63. destroyed() {
  64. // Vue 实例销毁后调用,实例中的属性也不再是响应式的,watch 被移除
  65. console.log(8);
  66. }
  67. });
  68. vm.$set(vm, 'msg', 'hello world'); // 因为 Vue 的数据都是响应式的,只有修改数据才会触发 beforeUpdate 和 updated 钩子
  69. vm.$mount('#app'); // 当创建实例时不传递 el 属性,可以手动挂载到 DOM 节点;
  70. vm.$destroy(); // 手动销毁实例;
  71. </script>
  72. </body>
  73. </html>

二、refs 和 DOM 操作

  • Vue 是数据驱动的,不提倡操作 DOM,但是必要的时候还是要操作 DOM,Vue 提供了一个行内属性,专门用来获取 DOM;
  • vm.$refs 是 Vue 提供的实例属性,专门用来获取 DOM 元素

2.1 使用 refs

在需要获取的标签添加 ref=”标识符” 行内属性;然后在 Vue 的实例中通过 vm.$refs.标识符 获取这个元素对象,如果有多个相同标识符的 ref,vm.$refs 将会获得一个数组;

2.3 代码示例

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <p ref="p1">{{msg}}</p>
  10. <ul ref="ulList">
  11. <li v-for="(item, index) in arr" :key="index" ref="wrap">{{item}}</li>
  12. </ul>
  13. </div>
  14. <script src="vue.js"></script>
  15. <script>
  16. // Vue 是数据驱动的,不提倡操作 DOM,但是必要的时候还是要操作 DOM,Vue 提供了一个行内属性,专门用来获取 DOM;
  17. // this.$refs
  18. let vm = new Vue({
  19. el: '#app',
  20. data: {
  21. msg: 'zfpx',
  22. arr: [1, 2, 3, 4]
  23. },
  24. mounted() {
  25. console.log(this.$refs);
  26. // ref 属性可以用来获取 DOM;
  27. // this.$refs.xxx xxx 是你要获取的 DOM 元素对象上 ref 属性的值,例如获取上面的 p 标签,
  28. console.log(this.$refs.p1);
  29. // 如果相同的 ref 值的元素有多个,那么获取到的是一个数组
  30. console.log(this.$refs.wrap);
  31. }
  32. })
  33. </script>
  34. </body>
  35. </html>

三、Vue 异步的 DOM 更新和 nextTick

3.1 Vue 更新 DOM 的机制

Vue 的 DOM 更新不是同步的,而是 异步 的,如果我们希望获取更新数据后渲染出来的 DOM,我们需要使用 nextTick ;

3.2 nextTick

  • 语法:
  1. this.$nextTick(callback)
  • 把 callback 放到 DOM 更新后执行

3.3 代码示例

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <p ref="p1">{{msg}}</p>
  10. <ul ref="ulList">
  11. <li v-for="(item, index) in arr" :key="index" ref="wrap">{{item}}</li>
  12. </ul>
  13. </div>
  14. <script src="vue.js"></script>
  15. <script>
  16. // this.$refs
  17. let vm = new Vue({
  18. el: '#app',
  19. data: {
  20. msg: 'zfpx',
  21. arr: [1, 2, 3, 4]
  22. },
  23. mounted() {
  24. this.arr.push(5, 6, 7);
  25. console.log(this.$refs.wrap.length); // 4
  26. // 为啥是4?不是7?
  27. // 因为 Vue 的 DOM 更新是异步的,如果我们希望获取更新数据后渲染出来的 DOM,我们需要使用 nextTick
  28. this.$nextTick(() => {
  29. // 这个 $nextTick 方法会在数据更新后,新的 DOM 挂载后执行;
  30. console.log(this.$refs.wrap.length); // 7
  31. })
  32. }
  33. })
  34. </script>
  35. </body>
  36. </html>

四、template属性

在创建组件或者 Vue 的实例时可以设置一个 template 属性,可以指定 HTML 中的模板 id 或者以字符串的形式声明模板;

4.1 以HTML模板形式:

  1. <!--template 标签并不会被渲染出来-->
  2. <template id="tpls">
  3. <div>
  4. <p v-for="(a, i) in arr">{{a}}</p>
  5. </div>
  6. </template>
  • 创建 Vue 实例

  • 设置 template 属性的值为上面 template 标签的 id,即 “#tpls”

  1. let vm = new Vue({
  2. el: '#app',
  3. data: {
  4. msg: 'zfpx',
  5. arr: [1, 2, 3, 4, 5, 6]
  6. },
  7. template: '#tpls'
  8. })

4.2 设置模板字符串

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. </div>
  10. <!--template 标签并不会被渲染出来-->
  11. <template id="tpls">
  12. <div>
  13. <p v-for="(a, i) in arr">{{a}}</p>
  14. </div>
  15. </template>
  16. <script src="vue.js"></script>
  17. <script>
  18. let vm = new Vue({
  19. el: '#app',
  20. data: {
  21. msg: 'zfpx',
  22. arr: [1, 2, 3, 4, 5, 6]
  23. },
  24. template: `<div><p v-for="(a, i) in arr">{{a}}</p></div>`
  25. })
  26. </script>
  27. </body>
  28. </html>

4.3 使用 template 属性的注意事项:

  1. template: <div><p v-for="(a, i) in arr">{{a}}</p></div> 或者 template: ‘#id’
  2. template 属性渲染后会把根DOM元素替换掉
  3. template 标签的只能有一个子元素

五、组件化和全局组件

5.1 什么是组件?

组件:把页面中重复的的功能抽离出来封装成一个单独的组件,在任何需要的地方使用该组件即可;提高代码的可复用程度和可维护性;

每个组件都是一个 Vue 的实例,那么这个组件也有自己的生命周期,并且也有 data、computed、methods、watch这些属性,每个组件都有自己私有的数据;还可以接受来自上层组件传入的数据;

5.2 注册全局组件

  1. Vue.component(componentName, config)
  1. componentName 可以使用驼峰,也可以使用 component-name
  2. 但是在 HTML 中引用时只能写 -
  3. Vue.component 是全局注册组件,在其他各个 Vue 实例中可以直接使用

5.3 使用组件

在组件的模板(template)或者 HTML 中使用 <组件名></组件名>

5.4 示例:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <handsome-man></handsome-man>
  10. </div>
  11. <div id="app2">
  12. <handsome-man></handsome-man>
  13. </div>
  14. <script src="vue.js"></script>
  15. <script>
  16. // 组件:把页面中重复的的功能抽离出来封装成一个单独的组件,在任何需要的地方使用该组件即可;
  17. // 每个组件都是一个 Vue 的实例,那么这个组件也有自己的生命周期,并且也有data、computed、methods、watch这些属性,每个组件都有自己私有的数据;还可以接受来自上层组件传入的数据;
  18. // 注册一个组件:
  19. // 全局组件 Vue.component(componentName, config)
  20. // 1. componentName 可以使用驼峰,也可以使用 component-name
  21. // 2. 但是在HTML中引用时只能写 -
  22. // 3. Vue.component 是全局注册组件,在其他各个 Vue 实例中可以直接使用
  23. Vue.component('handsomeMan', {
  24. data () {
  25. // 注册组件时 data 属性需要用一个函数返回一个对象
  26. return {
  27. msg: 'zfpx'
  28. }
  29. },
  30. template: `<div>{{msg}}</div>`
  31. });
  32. let vm1 = new Vue({
  33. el: '#app',
  34. data: {}
  35. });
  36. let vm2 = new Vue({
  37. el: '#app2',
  38. data: {}
  39. });
  40. </script>
  41. </body>
  42. </html>

六、局部组件

6.1 局部组件

局部组件是只能在当前 Vue 实例中使用的组件;

6.2 使用局部组件的步骤

  1. 创建组件
  2. 注册组件
  3. 使用组件

6.3 代码示例

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <mabin></mabin>
  10. <cxk></cxk>
  11. </div>
  12. <script src="vue.js"></script>
  13. <script type="module">
  14. // 局部组件使用三部曲
  15. // 1. 创建组件
  16. // 2. 注册组件
  17. // 3. 使用组件
  18. // import mabin from './mabin.js';
  19. // import cxk from './cxk.js';
  20. let cxk = {
  21. data() {
  22. return {
  23. content: [
  24. 'sing',
  25. 'dance',
  26. 'rap',
  27. 'basketball'
  28. ]
  29. }
  30. },
  31. template: `<div>CXK: <span v-for="(item, index) in content" :key="index">{{item}};</span></div>`
  32. };
  33. let mabin = {
  34. // 每一个组件都是一个 Vue 的实例,那么每个组件都有自己的生命周期、computed...
  35. data () {
  36. return {
  37. name: 'mabin',
  38. arr: []
  39. }
  40. },
  41. methods: {
  42. fn() {
  43. this.name = '马宾';
  44. }
  45. },
  46. template: "<div @click='fn'>{{name}}</div>"
  47. };
  48. let vm = new Vue({
  49. el: '#app',
  50. data() {
  51. return {
  52. x: 1,
  53. y: 2
  54. }
  55. },
  56. components: {
  57. // 注册局部组件,像 mabin, cxk 这种被注册的称为子组件,而组件的注册时在的实例称为父组件
  58. mabin,
  59. cxk
  60. }
  61. })
  62. </script>
  63. </body>
  64. </html>

七、组件嵌套

7.1 为什么会嵌套?

父组件使用了子组件,而子组件又使用了孙子组件;即A功能依赖B组件,B组件依赖C组件;

创建一个组件 => 在对应的父组件中进行注册 => 在父组件的标签中直接嵌套子组件的标签名;

7.2 代码示例

grandson 组件

  1. export default {
  2. data() {
  3. return {
  4. gen: 'Grandson'
  5. }
  6. },
  7. template: `<div>{{gen}}</div>`
  8. }

son 组件

  1. import grandson from './grandson.js';
  2. export default {
  3. data() {
  4. return {
  5. gen: 'Son'
  6. }
  7. },
  8. components: {
  9. grandson
  10. },
  11. template: `<div>{{gen}} <grandson></grandson> </div>`
  12. }

最终父组件

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <div>{{gen}}</div>
  10. <son></son>
  11. </div>
  12. <script src="vue.js"></script>
  13. <script type="module">
  14. import son from './son.js';
  15. let vm = new Vue({
  16. el: '#app',
  17. data() {
  18. return {
  19. gen: 'Parent'
  20. }
  21. },
  22. components: {
  23. son
  24. }
  25. });
  26. // 创建一个组件 => 在对应的父组件中进行注册 => 在父组件的标签中直接嵌套子组件的标签名;
  27. </script>
  28. </body>
  29. </html>

八、组件的数据传递(父传子)

8.1 为什么传递数据

  1. 子组件中的数据不能全是写死的,而是有一部分从父组件传递过来的
  2. 为了把父组件的数据传递给子组件,子组件在标签上动态绑定一个属性,这个属性绑定父组件的数据,并且在子组件的 props 中注册这个属性
  3. 子组件如果想使用父组件的数据,就使用对应的 props 就可以(父传子用 props)

8.2 单向数据流

单向数据流:数据只能通过父组件传递给子组件,而不能直接从子组件传给父组件,子组件也不能直接修改父组件的数据;当父组件的数据发生改变之后,子组件的收到的数据也会跟着变化;如上面的例子,直接修改从父组件中的数据会引发 Vue 的报错;
如果子组件想修改父组件的数据,只能通知父组件,让父组件修改数据;

8.3 示例代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <son :msg="pmsg"></son>
  10. </div>
  11. <script src="vue.js"></script>
  12. <script>
  13. let son = {
  14. data() {
  15. return {
  16. hello: 'xxxx'
  17. }
  18. },
  19. props: ['msg', 'changePMSG'],
  20. template: '<span>{{msg}} <button @click="fn">dddd</button></span>',
  21. methods: {
  22. fn() {
  23. this.msg = 1233; // props 中的数据也会代理到子组件的实例身上,可以直接通过 this 访问
  24. }
  25. }
  26. };
  27. let vm = new Vue({
  28. el: '#app',
  29. data: {
  30. pmsg: 'msg from parent'
  31. },
  32. methods: {
  33. changeMsg() {
  34. this.pmsg = 123445
  35. }
  36. },
  37. components: {
  38. son
  39. }
  40. });
  41. // 1. 子组件中的数据不能全是写死的,而是有一部分从父组件传递过来的
  42. // 2. 为了把父组件的数据传递给子组件,子组件在标签上动态绑定一个属性,这个属性绑定父组件的数据,并且在子组件的props中注册这个属性
  43. // 3. 子组件如果想使用父组件的数据,就使用对应的 props 就可以(父传子用props)
  44. // 单向数据流:数据只能通过父组件传递给子组件,而不能直接从子组件传给父组件,子组件也不能直接修改父组件的数据;当父组件的数据发生改变之后,子组件的收到的数据也会跟着变化;如上面的例子,直接修改从父组件中的数据会引发Vue的报错;
  45. // 如果子组件想修改父组件的数据,只能通知父组件,让父组件修改数据;
  46. </script>
  47. </body>
  48. </html>

九、组件数据传递(子传父)

因为单向数据流的原因,子组件不能直接修改父组件的数据;

9.1 子传父的机制

  1. 子组件传递给父组件通过事件机制,通知父组件,让父组件修改数据;
  2. 父组件中使用子组件时要监听一个自定义的事件,如上面的 @change-msg=modify 【事件名不要写驼峰】
  3. 当子组件要修改某个数据时调用 this.$emit(事件名, 数据);
  4. 子组件 $emit 后,父组件收到这个事件会执事件绑定的的方法,方法的形参可以接收子组件 $emit 的数据
  • 父组件监听事件,给事件绑定一个方法,这个方法有一个形参用于接收子组件 $emit 的数据
  • 子组件 emit 事件,并且传入数据

9.3 示例代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <son :pmsg="msg" @change-msg="modify"></son>
  10. </div>
  11. <script src="vue.js"></script>
  12. <script>
  13. let son = {
  14. data() {
  15. return {
  16. msg: '123'
  17. }
  18. },
  19. template: `<div>{{pmsg}} <button @click="fn">修改</button></div>`,
  20. props: ['pmsg'],
  21. methods: {
  22. fn() {
  23. this.$emit('change-msg', '12345上山打老虎');
  24. }
  25. }
  26. };
  27. let vm = new Vue({
  28. el: '#app',
  29. data: {
  30. msg: 'msg from parent'
  31. },
  32. methods: {
  33. modify(val) {
  34. // console.log(val);
  35. this.msg = val;
  36. }
  37. },
  38. components: {
  39. son
  40. }
  41. });
  42. // 子组件传递给父组件通过事件机制,通知父组件,让父组件修改数据;
  43. // 父组件中使用子组件时要监听一个自定义的事件,如上面的 @change-msg=modify 【事件名不要写驼峰】
  44. // 当子组件要修改某个数据时调用 this.$emit(事件名, 数据);
  45. // 子组件 $emit 后,父组件收到这个事件会执事件绑定的的方法,方法的形参可以接收子组件 $emit 的数据
  46. // 父组件监听事件,给事件绑定一个方法,这个方法有一个形参用于接收子组件 $emit 的数据
  47. // 子组件 emit 事件,并且传入数据
  48. </script>
  49. </body>
  50. </html>

十、props验证

props 属性除了设置为一个数组还可以设置一个对象,其中 key 是 props 名,值是一个对象,在这个对象中可以设置默认值以及对这个 props 的校验规则;

10.1 常见的校验:

  1. type: Number, // 类型校验,要求 pmsg 必须是某种类型,如果类型不对会抛出警告
  2. required: true, // 必传校验,如果不传会引发警告
  3. default: 250, // 设置默认值,如果不传的时候使用默认值
  4. validator(val) { // 验证器函数,有问题可以抛出异常,没有问题要 return true

10.2 代码示例

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <child :msg="pmsg"></child>
  10. </div>
  11. <script src="vue.js"></script>
  12. <script>
  13. let child = {
  14. data () {
  15. return {
  16. xxx: 111
  17. }
  18. },
  19. template: `<p>{{msg}}</p>`,
  20. props: {
  21. msg: {
  22. type: Number, // 类型校验,要求 pmsg 必须是某种类型,如果类型不对会抛出警告
  23. required: true, // 必传校验,如果不传会引发警告
  24. default: 250, // 设置默认值,如果不传的时候使用默认值
  25. validator (val) { // 验证器
  26. console.log(val);
  27. if (val > 250) {
  28. throw new Error('太大了')
  29. } else {
  30. return true
  31. }
  32. }
  33. }
  34. }
  35. };
  36. let vm = new Vue({
  37. el: '#app',
  38. data: {
  39. pmsg: 200
  40. },
  41. components: {
  42. child
  43. }
  44. })
  45. </script>
  46. </body>
  47. </html>