Vue基础-Day04

声明式编程(命令式编程):Vue中写模板的代码风格

组件化开发

基本概念介绍

组件:其实就是对网页功能的封装(对界面封装 html+css+js)。

本质:其实也是一个vue实例,组件这个实例不支持el选项,其他选项都支持

  • 把一个页面拆分多个组件,组件分别开发完成可以组成一个完整的页面。

image.png

组件化带来的好处:

  • 方便分配任务
  • 方便后期的维护
  • 方便代码的复用

总结: 组件化开发:把一个复杂的页面拆分为很多独立的组件,每一个组件封装为独立的功能(html/css/js),再通过组件的组合形成一个更加复杂的页面功能。方便代码的重用(别人写好的组件,拿过来直接使用即可)

定义(注册)组件

  • 全局注册

Vue.component(参数一,参数二)

  • 参数一表示组件的名称
  • 参数二表示组件的配置选项
    • data 表示组件需要用到的数据,组件中的data属性值必须是函数,函数的返回值是对象(该对象用于提供组件的数据)
    • template 组件的模板(标签中填充数据的这种代码)
  1. // 自定义全局组件
  2. Vue.component('my-component-test', {
  3. data () {
  4. return {
  5. info: 'jerry'
  6. }
  7. },
  8. // template: '<div>{{info}}</div>'
  9. template: `
  10. <div>
  11. {{info}}
  12. </div>
  13. `
  14. })
  1. <div id="app">
  2. <div>{{msg}}</div>
  3. <div>
  4. <!-- 使用组件 -->
  5. <my-component-test></my-component-test>
  6. </div>
  7. </div>

总结:

  1. 先定义再使用
  2. 定义的时候需要起一个名字,并且添加相关的配置选项(data/template….)
  3. data必须是一个函数
  4. 起的名字就是组件的标签名称
  • 局部注册
  1. const MyComponentTest = {
  2. data () {
  3. return {
  4. info: 'spike'
  5. }
  6. },
  7. template: `
  8. <div>
  9. {{info}}
  10. </div>
  11. `
  12. }
  13. let vm = new Vue({
  14. el: '#app',
  15. data: {
  16. msg: 'hello'
  17. },
  18. components: {
  19. 'my-component-test': MyComponentTest
  20. }
  21. })

总结:

  1. 局部组件定义时,需要components选项( 左侧key是组件的名称,右侧value是组件的配置信息)
  2. 组件的具体配置信息可以抽取到独立的对象里面

组件命名规则

组件的命名规则,说白了,就是这么给组件取名字,在模板中怎么使用组件,的一些约定。

  • 以横线分隔写法 count-add
  1. <div id="app">
  2. <!-- 使用组件 -->
  3. <count-add></count-add>
  4. </div>
  5. <script>
  6. Vue.component('count-add',组件配置对象)
  7. new Vue({
  8. el: '#app'
  9. })
  10. </script>
  • 单词首字母大写写法 CountAdd
  1. <div id="app">
  2. <!-- 使用组件 -->
  3. <count-add></count-add>
  4. </div>
  5. <script>
  6. Vue.component('CountAdd',组件配置对象)
  7. new Vue({
  8. el: '#app'
  9. })
  10. </script>

总结:

  1. 定义组件时,组件起名字有两种方式:1,短横线方式;2、单词首字母都大写
  2. 现阶段:使用组件时,只能用小写的短横线方式,不可以使用首字符大写方式(后续可以)

组件之间嵌套

目标:熟悉组件之间的嵌套规则

  1. <div id="app">
  2. <com-parent></com-parent>
  3. </div>
  4. <script src="./vue.js"></script>
  5. <script>
  6. // 父组件
  7. Vue.component('com-parent',{
  8. template: `<div class="parent"> 父组件 <com-child></com-child></div>`
  9. })
  10. // 子组件
  11. Vue.component('com-child',{
  12. template: `<div class="child"> 子组件 </div>`
  13. })
  14. // 根实例
  15. new Vue({
  16. el: '#app'
  17. })
  18. </script>

总结:

  1. 组件之间可以无限嵌套:从而可以形成:父子、兄弟、祖孙。。。
  2. 组件之间的这些关系靠模板的标签嵌套形成

  1. 所有的组件的模板必须有唯一的跟元素
  2. 局部的含义:只能在定义它的组件的模板中使用

组件之间数据交互介绍

组件与组件之间数据,是相互独立的,当然有需要组件与组件之间进行数据通信的情况。

  • 传递数据的方式和组件与组件之前的关系有关。
    • 父组件 传值 子组件
    • 子组件 传值 父组件
    • 非父子关系,组件传值
  • 组件本身的数据是相互隔离的,如果完全隔离,那么组件之间的联系就弱,无法实现复杂的功能,所有必须有一种机制:保证组件之间可以方便的进行数据交互。

总结:

  1. 组件之间的数据是需要交互,否则交互能很有限
  2. 主要存在的交互关系
    1. 父子关系
      1. 父向子传值
      2. 子向父传递
    2. 非父子关系(后续单独介绍)

父组件向子组件传值

目标:熟悉父组件向子组件传值用法

  • 父组件向子组件传值

    • 通过标签属性传值(属性值可以是静态的,也可以是动态绑定)

      1. <!--父组件通过标签的属性向子组件传递数据-->
      2. <com-child info='tom' :abc='abc'></com-child>
    • 子组件通过props选项接收值

      1. Vue.component('com-child', {
      2. // 子组件通过props属性值接收父组件传递过来的数据
      3. props: ['info', 'abc'],
      4. data () {
      5. return {
      6. msg: 'child'
      7. }
      8. }
      9. }
  • 案例代码

  1. /*
  2. 父组件向子组件传值
  3. */
  4. Vue.component('com-parent', {
  5. data () {
  6. return {
  7. msg: 'parent',
  8. abc: 'jerry'
  9. }
  10. },
  11. template: `
  12. <div>
  13. <div>{{msg}}</div>
  14. <!--父组件通过标签的属性向子组件传递数据-->
  15. <com-child info='tom' :abc='abc'></com-child>
  16. </div>
  17. `
  18. })
  19. Vue.component('com-child', {
  20. // 子组件通过props属性值接收父组件传递过来的数据
  21. props: ['info', 'abc'],
  22. data () {
  23. return {
  24. msg: 'child'
  25. }
  26. },
  27. template: `
  28. <div>
  29. <div>{{msg}}</div>
  30. <div>{{info}}</div>
  31. <div>{{abc}}</div>
  32. </div>
  33. `
  34. })
  35. new Vue({
  36. el: '#app',
  37. data: {
  38. msg: 'hello'
  39. }
  40. })
  41. </script>

总结:

  1. 父组件通过标签的属性向子组件传值(可以是动态绑定的,也可以是静态的)
  2. 子组件通过props接收父组件传递过来的值
  3. props传递的数据是只读的(单向数据流)

总结

  • 前后端交互
    • json-server包的基本使用
    • 测试json-server提供的接口(熟悉Restful接口的风格)
    • 图书管理案例-接口版(Promise和Async函数)
      • 图书列表展示
      • 添加图书
      • 删除图书
      • 搜索图书
    • 侦听器用法(监听data中数据的变化)
  • 组件化开发
    • 理解组件化开发的基本概念
    • 如何定义组件
      • 全局组件
      • 局部组件
    • 如何使用组件
      • 当做自定义标签使用
      • 标签的命名规则:1、短横线方式;2、首字母大写方式
    • 组件的嵌套:模板的嵌套可以形成复杂的组件关系
    • 组件的模板必须有唯一的根节点
    • 组件之间需要数据的交互
      • 父子关系
        • 父向子传值:props (只读的)
        • 子向父传值
      • 非父子关系(vuex 后续讲解)

子组件向父组件传值

目标:熟悉子组件向父组件传值用法

  1. // 组件化:子组件向父组件传值
  2. // 创建父子组件
  3. Vue.component('my-parent', {
  4. data() {
  5. return {
  6. msg: 'hello:'
  7. }
  8. },
  9. template: `
  10. <div>
  11. <div>父组件模板{{msg}}</div>
  12. <!-- 1、父组件通过自定义事件的方式获取子组件传递过来的数据 -->
  13. <my-child @get-info='handleInfo'></my-child>
  14. </div>
  15. `,
  16. methods: {
  17. handleInfo(v) {
  18. this.msg = this.msg + v
  19. }
  20. }
  21. })
  22. Vue.component('my-child', {
  23. data() {
  24. return {
  25. info: 'tom'
  26. }
  27. },
  28. template: `
  29. <div>
  30. <div>子组件模板{{info}}</div>
  31. <button @click='handleClick'>点击</button>
  32. </div>
  33. `,
  34. methods: {
  35. handleClick() {
  36. // 点击按钮时把info的值传递给父组件
  37. // 参数一表示自定义事件的名称
  38. // 参数二表示传递过去的数据
  39. this.$emit('get-info', this.info)
  40. }
  41. }
  42. })
  43. new Vue({
  44. el: '#app',
  45. data: {}
  46. })

总结:子组件向父组件传递数据

  1. 子组件向父组件传递数据
    • 参数一表示触发的事件名称
    • 参数二表示传递的数据
  1. this.$emit('get-info', this.info)
  1. 父组件接收子组件传递过来的值
    • 父组件触发事件后自动调用函数,通过函数的形参可以获取子组件传递过来的值
  1. <com-child @get-info='handleInfo'></com-child>
  1. methods: { handleInfo (v) { // 获取子组件传递过来的数据 this.msg = this.msg + v }}

总结:通过自定义事件的方式子向父组件传值

  1. 触发事件:this.$emit
  2. 监听事件:标签上进行事件监听

组件的多次使用

目标:熟悉组件多次使用时,数据的访问问题

多次使用组件相当于创建了不同的组件实例对象,彼此之间的数据是相互独立的。

  1. <div id="app"> <my-component-test></my-component-test> <my-component-test></my-component-test> <my-component-test></my-component-test></div>
  1. Vue.component('my-component-test', { data () { return { count: 0 } }, methods: { handleAdd () { this.count += 1 } }, template: ` <div> <div>总数:{{count}}</div> <div> <button @click='handleAdd'>点击</button> </div> </div> `})new Vue({ el: '#app', data: { msg: 'hello' }})

总结:组件的多个实例的数据是相互独立的,不是共享的。

投票案例

目标:通过案例掌握父子之间的传值的应用场景

  • 父向子传值:基于标签的属性 <my-person uname='张三'></my-person>
  • 子向父传值:基于自定义事件监听 <my-person @update-total='handleTotal'></my-person>
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <style>
  9. .poll {
  10. width: 300px;
  11. height: 300px;
  12. border: 1px solid blue;
  13. text-align: center;
  14. }
  15. .persons {
  16. display: flex;
  17. }
  18. .person {
  19. flex: 1;
  20. border: 1px solid orange;
  21. text-align: center;
  22. height: 260px;
  23. }
  24. </style>
  25. </head>
  26. <body>
  27. <div id="app">
  28. <my-poll></my-poll>
  29. </div>
  30. <script src="lib/vue.js"></script>
  31. <script>
  32. // 组件化-组件的多次使用
  33. // 投票根组件
  34. Vue.component('my-poll', {
  35. data() {
  36. return {
  37. total: 0
  38. }
  39. },
  40. template: `
  41. <div class='poll'>
  42. <div>当前参与投票的总数{{total}}</div>
  43. <div class='persons'>
  44. <my-person uname='张三' @update-total='handleTotal'></my-person>
  45. <my-person uname='李四' @update-total='handleTotal'></my-person>
  46. </div>
  47. </div>
  48. `,
  49. methods: {
  50. handleTotal(n) {
  51. // 修改票的总数
  52. this.total += n
  53. }
  54. }
  55. })
  56. Vue.component('my-person', {
  57. props: ['uname'],
  58. data() {
  59. return {
  60. count: 0
  61. }
  62. },
  63. template: `
  64. <div class='person'>
  65. <div>{{uname}}的票数{{count}}</div>
  66. <button @click='handleClick'>投票</button>
  67. </div>
  68. `,
  69. methods: {
  70. handleClick() {
  71. // 修改本组件的自己的票数
  72. this.count += 1
  73. // 同时需要影响总票数
  74. this.$emit('update-total', 2)
  75. }
  76. }
  77. })
  78. new Vue({
  79. el: '#app',
  80. data: {}
  81. })
  82. </script>
  83. </body>
  84. </html>

总结:投票场景(子向父传值;父向子传值)

ref直接操作组件实例

目标:熟悉ref直接操作组件实例的步骤

  • ref可以直接操作DOM元素
  • ref也可以直接操作组件的实例对象
  1. <body>
  2. <div id="app">
  3. <div ref='myinfo'>hello</div>
  4. <button @click='handleClick'>点击</button>
  5. <my-com-test ref='mytest'></my-com-test>
  6. </div>
  7. <script src="lib/vue.js"></script>
  8. <script>
  9. // 组件化-通过ref直接操作组件实例
  10. Vue.component('my-com-test', {
  11. data() {
  12. return {
  13. msg: 'nihao'
  14. }
  15. },
  16. template: `
  17. <div>
  18. <div>ref直接操作组件实例</div>
  19. </div>
  20. `,
  21. methods: {
  22. showMsg() {
  23. console.log(this.msg + 'tom')
  24. }
  25. }
  26. })
  27. new Vue({
  28. el: '#app',
  29. data: {},
  30. methods: {
  31. handleClick() {
  32. // console.log(this.$refs.myinfo.innerHTML)
  33. // this.$refs.mytest表示组件my-com-test的实例对象
  34. // 通过ref得到组件实例对象后,可以直接访问data中的数据和methods中的方法
  35. // console.log(this.$refs.mytest.msg)
  36. this.$refs.mytest.showMsg()
  37. }
  38. }
  39. })
  40. </script>
  41. </body>

总结:

  1. ref可以直接操作DOM元素
  2. ref也可以直接操作组件的实例对象
  3. 通过组件的实例对象可以访问data中的数据和methods中的方法(也可以访问其他特性)

组件的生命周期

目标:熟悉组件的生命周期概念,以及相关函数的应用场景

  • 生命周期:一种事物从生到死的过程
  • 组件的生命周期:组件从创建到销毁的过程(包括更新)
    • 创建阶段
      • beforeCreate
      • created
      • beforeMount
      • mounted
    • 更新阶段
      • beforeUpdate
      • updated
    • 销毁阶段
      • beforeDestroy
      • destroyed
  • Vue组件创建、更新和销毁的过程中会执行一系列的函数,这些函数是自动触发的,这些函数还有另一种说法:钩子函数(生命周期函数)
  • 所有的生命周期函数都是自动触发的,不允许显示调用。

创建阶段

  • created
    • 典型场景:初始化data中的数据(调用接口)
  • mounted
    • 典型场景:直接操作DOM

更新阶段

  • beforeUpdate 更新前触发
  • updated 更新后触发

注意:更新指的是组件的data和props中的数据的更新 典型应用场景:实现更新的加载提示效果

销毁阶段

  • beforeDestroy 销毁前触发
  • destroyed 销毁后触发

典型应用场景:销毁组件不再使用的资源(比如销毁定时任务)

总结

  • 为什么需要组件化开发?方便重用;方便协作;方便维护
  • 如何定义组件?全局定义;局部定义
  • 如何使用组件?作为自定义标签使用
  • 组件的命名规则:短横线方式;首字符大写方式
  • 组件之间的关系:父子关系;非父子关系(通过模板的标签嵌套)
  • 关于全局和局部的区别:使用的范围不同
  • 组件之间的数据交互(组件本身的数据是隔离的,只有组件内部可以使用)
    • 父组件向子组件传递数据:props
      • props数据是只读的(单向数据流)
    • 子组件向父组件传递数据:自定义事件
      • 父组件监听自定义事件
      • 子组件触发自定义事件 $emit
  • 组件的多次使用(数据是独立的,而不是共享的)
  • 通过ref直接操作组件实例的用法
  • 组件的生命周期:创建阶段;更新阶段;销毁阶段
    • created : 此时可以操作data中的数据和methods中定义的方法,但是不可以操作DOM
    • mounted:此时可以操作DOM