011.组件

一、组件的概念{Component}

组件是 Vue.js 最强大的功能之一。

  • 🎯特性

    1. 组件是可复用的Vue实例。
    2. 在较高层面上,组件是自定义元素
    3. 组件是{HTML、Css、JavaScript}的一个聚合体,封装性隔离性非常强。
  • ✨作用

    • 组件可以扩展 HTML元素,封装可重用的代码。

二、组件的使用

⑴定义组件➾ 创建组件

  • 语法

    1
    Vue.extend(options)
    • 🍬解析

      • options和new Vue(options)时,传入的options几乎一样,但也有点区别 ➾ 

        1. el配置项不要写。

          • 📌原因
            • 最终所有的组件都要被一个vm管理,由vm决中的el定服务于哪个容器。
            • 🪶即
              • vm来决定这个组件放到哪里就放到哪里,不能让他它自己决定。
        2. data配置项必须是函数式写法,

          • 而且这个函数的需要返回一个 ➾ 对象,对象内部保存着数据。

          • 📌原因

            • 一个组件可以进行多处复用。
            • 🍬解析
              1. 如果data配置项是 ➾ 一个对象
                • 那么所有复用的组件实例都将 ➾ 显示相同内容
              2. 不同复用处组件的数据,
                • 都会随着一处的更改而全部都改 ➾ 没有独立性
          • 🔥案例

            1. data配置项{对象式}写法 ➾ ❎

              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              <template id="temp">
              <div>
              代办项:
              <ul>
              <li>代办人: {{name}},代办详情: {{memo}}</li>
              </li>
              </div>
              </template>

              <div id="box">
              <tany></tany>
              </div>
              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              Vue.component('tany', {
              data: {
              name: '糖丸',
              memo: '办理大学毕业证'
              },
              template: '#temp',
              })

              var vm = new Vue({
              el: '#box'
              })

              image-20231223111411033

              • 🍬解析
                • 提示组件实例中data必须是一个函数。
            2. data配置项{函数式}写法 ➾ ✅

              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              <template id="temp">
              <div>
              代办项:
              <ul>
              <li>代办人: {{name}},代办详情: {{memo}}</li>
              </li>
              </div>
              </template>

              <div id="box">
              <tany></tany>
              </div>
              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              Vue.component('tany', {
              data() {
              return {
              name: '糖丸',
              memo: '办理大学毕业证'
              }
              },
              template: '#temp',
              })

              var vm = new Vue({
              el: '#box'
              })

              image-20231223111245894

            3. 组件中data配置项的错误使用

              1. 引用相同的counter ➾ ❎

                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                <template id="error-temp">
                <button @click="counter += 1 ">{{counter}}</button>
                </template>

                <div id="box">
                <h2>错误</h2>
                <error-counter></error-counter>
                <error-counter></error-counter>
                <error-counter></error-counter>
                </div>
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                11
                12
                13
                14
                var data = {
                counter: 0
                }

                Vue.component('error-counter', {
                data() {
                return data
                },
                template: '#error-temp',
                })

                var vm = new Vue({
                el: '#box'
                })

                gif-231223112729

                • 👁️‍🗨️分析
                  • 技术性上,data确实是一个函数了,因此Vue不会发出 ➾ 警告
                  • 但是返回给每个组件的Vue实例,却引用了 ➾ 同一个data对象
              2. 引用不同的counter ➾ ✅

                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                <template id="right-temp">
                <button @click="counter += 1 ">{{counter}}</button>
                </template>

                <div id="box">
                <h2>正确</h2>
                <right-counter></right-counter>
                <right-counter></right-counter>
                <right-counter></right-counter>
                </div>
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                11
                12
                Vue.component('right-counter', {
                data() {
                return {
                counter: 0
                }
                },
                template: '#right-temp',
                })

                var vm = new Vue({
                el: '#box'
                })

                gif-231223113005

⑵注册组件

*.template标签/属性用法

1ºVue实例绑定元素内外部的template标签
1`template标签在Vue实例绑定元素的➾ 内部

template标签中的内容在页面中 ➾ 会显示。但后台查看页面Dom结构时,不存在template标签。

  • 🔥案例

    1
    2
    3
    4
    5
    6
    <div id="box">
    <template>
    <div>你好,我是糖丸!</div>
    </template>
    <div>你好,我是小白!</div>
    </div>
    image-20231222141249342
  • ❗注意

    • Vue实例绑定元素内部的 ➾ 
      1. template标签不支持v-show指令。
      2. template标签支持{v-if、v-else-if、v-else、v-for}指令。
2`template标签在Vue实例绑定元素的➾ 外部

template标签中的内容在页面中 ➾ 不会显示。但后台查看页面Dom结构时,存在template标签。

  • 📌原因

    • template标签天生不可见,其已经初始设置display:none;属性。
  • 🔥案例

    1
    2
    3
    4
    5
    6
    7
    <template>
    <div>你好,我是糖丸!</div>
    </template>

    <div id="box">
    <div>你好,我是小白!</div>
    </div>
    image-20231222141929877
2ºVue实例中的template属性
  • 🎯特性

    1. 若Vue实例中有 ➾ template属性,则会对该template属性值进行 ➾ 编译

      • 进而将Vue实例绑定元素{用el绑定的元素}中的内容替换为编译后的template属性值{虚拟Dom}。

      • 🔔提示

        • 若该Vue实例绑定元素中 ➾ 存在内容,则这些内容将 ➾ 被覆盖{包括绑定元素}。

          • 🔥案例

            1
            2
            3
            <div id="box">
            <div>你好,我是小白!</div>
            </div>
            1
            2
            3
            4
            var vm = new Vue({
            el: '#box',
            template: '<div>你好,我是糖丸!</div>'
            })
            image-20231222143448079
            1
            2
            3
            <div id="box">
            <div>你好,我是小白!</div>
            </div>
            1
            2
            3
            var vm = new Vue({
            el: '#box'
            })
            image-20231222143557790
    2. template属性中的Dom结构只能有 ➾ 一个根元素

      • 🔔提示
        • 若有多个根元素需要使用{v-if、v-else、v-else-if}设置成只显示其中一个根元素。
    3. 在template属性对应的属性值中,可以使用Vue实例{data、methods…}中定义的数据。

      • 🔥案例

        1
        2
        3
        <div id="box">
        <div>你好,我是小白!</div>
        </div>
        1
        2
        3
        4
        5
        6
        7
        var vm = new Vue({
        el: '#box',
        data: {
        tany: '糖丸'
        },
        template: '<div>你好,我是{{tany}}!</div>'
        })

        image-20231222162348602

3ºVue组件中的template属性
  • 🎯特性

    1. 若Vue组件中有 ➾ template属性,则会对该template属性值进行 ➾ 编译

      • 进而将Vue实例绑定元素{用el绑定的元素}中的内容替换为编译后的template属性值{虚拟Dom}。

      • 🔔提示

        • 若该Vue实例绑定元素中 ➾ 存在内容,则这些内容将 ➾ 被覆盖{不包括绑定元素}。

          • 🔥案例

            1
            2
            3
            <div id="box">
            <my-component>你好,我是小白!</my-component>
            </div>
            1
            2
            3
            4
            5
            6
            7
            Vue.component('my-component', {
            template: '<div>你好,我是糖丸!</div>'
            })

            var vm = new Vue({
            el: '#box'
            })
            image-20231222144638618
    2. template属性中的Dom结构只能有 ➾ 一个根元素

      • 🔔提示

        • 若有多个根元素需要使用{v-if、v-else、v-else-if}设置成只显示其中一个根元素。

        • 🔥案例

          1
          2
          3
          <div id="box">
          <my-component>你好,我是小白!</my-component>
          </div>
          1
          2
          3
          4
          5
          6
          7
          Vue.component('my-component', {
          template: '<div>你好,我是糖丸!</div><h1>你好,我是小黑!</h1>'
          })

          var vm = new Vue({
          el: '#box'
          })

          image-20231222145141003

          • 🔔提示

            • 上述代码等价于{推荐}。

              1
              2
              3
              4
              5
              6
              7
              8
              <template id="temp">
              <div>你好,我是糖丸!</div>
              <h1>你好,我是小黑!</h1>
              </template>

              <div id="box">
              <my-component>你好,我是小白!</my-component>
              </div>
              1
              2
              3
              4
              5
              6
              7
              Vue.component('my-component', {
              template: '#temp'
              })

              var vm = new Vue({
              el: '#box'
              })

              image-20231222145545238

          • 🟩正确写法

            1
            2
            3
            4
            5
            6
            7
            8
            9
            <template id="temp">
            <div>你好,我是糖丸!
            <h1>你好,我是小黑!</h1>
            </div>
            </template>

            <div id="box">
            <my-component>你好,我是小白!</my-component>
            </div>
            1
            2
            3
            4
            5
            6
            7
            Vue.component('my-component', {
            template: '#temp'
            })

            var vm = new Vue({
            el: '#box'
            })
            image-20231222145724708
    3. 在template属性对应的属性值中,

      1. 不能使用Vue实例{data、methods…}中定义的数据 ➾

        • 🎯特性

          • 组件是单独功能模块的封装{孤岛}。
            1. 有 ➾ 属于自己的HTML模板。
            2. 可以有 ➾ 属性自己的数据data方法methods。
            3. 无法直接 ➾ 访问组件外部的状态{data}方法{methods}。
        • 👻特别的

          • Vue构造函数也被称为「根组件」 ➾ new Vue({})
        • 🔥案例

          1
          2
          3
          4
          5
          6
          7
          <template id="temp">
          <div>你好,我是{{tany}}!</div>
          </template>

          <div id="box">
          <my-component></my-component>
          </div>
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          Vue.component('my-component', {
          template: '#temp'
          })

          var vm = new Vue({
          el: '#box',
          data: {
          tany: '糖丸'
          }
          })

          image-20231222162922639

          image-20231222162932035

      2. 只能使用component构造函数{data、methods…}中定义的数据。

        • 🔥案例

          1
          2
          3
          4
          5
          6
          7
          <template id="temp">
          <div>你好,我是{{tany}}!</div>
          </template>

          <div id="box">
          <my-component></my-component>
          </div>
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          Vue.component('my-component', {
          data() {
          return {
          tany: '糖丸'
          }
          },
          template: '#temp'
          })

          var vm = new Vue({
          el: '#box'
          })
          • 🔔提示
            • 在组件中,data配置项必须是函数式写法。
          image-20231222163327081

1.全局注册

全局注册的组件,可以在每个Vue实例中使用。

*º组件命名方式
1`一个单词组成
  1. 第一种写法
    • 首字母小写 ➾ tany
  2. 第二种写法
    • 首字母大写 ➾ Tany
2`多个单词组成
  1. 第一种写法 ➾ 短横线分隔方式。

    • kebab-case命名方式 ➾ 以短横线{-}分隔命名 ➾ my-name-tany
  2. 第二种写法 ➾ 大驼峰命名方式。

    • PascalCase命名方式 ➾ 以每个首字母大写命名{大驼峰} ➾ MyNameTany
  3. 🪄总结

    image-20231226161442445

    • 👁️‍🗨️分析
      • 无论组件的名字采用kebab-casePascalCase命名方式。
        • 在外置模板中,总是 ➾ 使用kebab-case命名方式。
        • 📌原因
          • HTML语言对于 ➾ 大小写是不敏感的
            • 所以浏览器会把 ➾ 所有大写字符解释为小写字符。
              • 因此在外置模板中,不能直接使用PascalCase命名方式,
              • 需要使用其等价的kebab-case命名方式。
1º完整写法{不推荐}
  • 语法

    1
    2
    3
    4
    <div id="box">
    <!-- 3.使用组件 -->
    <组件标签名></组件标签名>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 1.定义组件
    var 注册组件的对象名 = Vue.extend({
    template: 'Dom结构'
    })

    // 2.注册组件
    Vue.component('组件标签名', 注册组件的对象名)

    var vm = new Vue({ ❗注意: 定义根组件需要再在最后!
    el: '#box'
    })
  • 🔥案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <template id="temp">
    <div>你好,我是糖丸!
    <h1>你好,我是小黑!</h1>
    </div>
    </template>

    <div id="box">
    <!-- 3.使用组件 -->
    <my-component></my-component>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 1.定义组件
    var myComponent = Vue.extend({
    template: '#temp'

    // ❗注意: 上条代码等价于👇➾ {不推荐}
    template: `<div>你好,我是糖丸!
    <h1>你好,我是小黑!</h1>
    </div>
    `
    })

    // 2.注册组件
    Vue.component('my-component', myComponent)

    var vm = new Vue({
    el: '#box'
    })
image-20231222145724708
2º简化写法{推荐}

有时为了简便,可以将第一个步骤第二个步骤合并。

  • 语法

    1
    2
    3
    4
    <div id="box">
    <!-- 2.使用组件 -->
    <组件名></组件名>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    // 1.定义并注册组件
    Vue.component('组件标签名', {
    template: 'Dom结构'
    }) // 🔔提示: 注册组件的对象。

    var vm = new Vue({ ❗注意: 定义根组件需要再在最后!
    el: '#box'
    })
  • 🔥案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <template id="temp">
    <div>你好,我是糖丸!
    <h1>你好,我是小黑!</h1>
    </div>
    </template>

    <div id="box">
    <!-- 2.使用组件 -->
    <my-component></my-component>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 1.定义并注册组件
    Vue.component('my-component', {
    template: '#temp'

    // ❗注意: 上条代码等价于👇➾ {不推荐}
    template: `<div>你好,我是糖丸!
    <h1>你好,我是小黑!</h1>
    </div>
    `
    })

    var vm = new Vue({
    el: '#box'
    })
image-20231222145724708

2.局部注册

局部注册的组件,只能在当前Vue实例中使用。

1º完整写法{不推荐}
  • 语法

    1
    2
    3
    4
    <div id="box">
    <!-- 3.使用组件 -->
    <组件名></组件名>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 1.定义组件
    var 注册组件的对象名 = {
    template: 'Dom结构'
    }

    var vm = new Vue({
    el: '#box',
    // 2.注册组件
    components: {
    '组件标签名': 注册组件的对象名
    }
    })
  • 🔥案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <template id="temp">
    <div>你好,我是糖丸!
    <h1>你好,我是小黑!</h1>
    </div>
    </template>

    <div id="box">
    <!-- 3.使用组件 -->
    <my-component></my-component>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 1.定义组件
    var myComponent = {
    template: '#temp'

    // ❗注意: 上条代码等价于👇➾ {不推荐}
    template: `<div>你好,我是糖丸!
    <h1>你好,我是小黑!</h1>
    </div>
    `
    }

    var vm = new Vue({
    el: '#box',
    // 2.注册组件
    components: {
    'my-component': myComponent
    }
    })
image-20231222145724708
2º简化写法{推荐}

有时为了简便,可以将第一个步骤第二个步骤合并。

  • 语法

    1
    2
    3
    4
    <div id="box">
    <!-- 2.使用组件 -->
    <组件名></组件名>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var vm = new Vue({
    el: '#box',
    // 2.定义并注册组件
    components: {
    '组件标签名': {
    template: 'Dom结构'
    } // 🔔提示: 注册组件的对象。
    }
    })
  • 🔥案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <template id="temp">
    <div>你好,我是糖丸!
    <h1>你好,我是小黑!</h1>
    </div>
    </template>

    <div id="box">
    <!-- 2.使用组件 -->
    <my-component></my-component>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var vm = new Vue({
    el: '#box',
    // 2.定义并注册组件
    components: {
    'my-component': {
    template: '#temp'

    // ❗注意: 上条代码等价于👇➾ {不推荐}
    template: `<div>你好,我是糖丸!
    <h1>你好,我是小黑!</h1>
    </div>
    `
    }
    }
    })
image-20231222145724708

3.组件嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template id="F-temp">
<div>你好,我是{{tany}}!
<my-name-age></my-name-age>
</div>
</template>

<template id="C-temp">
<ul>我今年{{tAge}}岁。</ul>
</template>

<div id="box">
<!-- 2.使用组件 -->
<my-name-tany></my-name-tany>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 1.定义并注册组件
Vue.component('my-name-tany', {
data() {
return {
tany: '糖丸'
}
},
template: '#F-temp',
components: {
'my-name-age': {
data() {
return {
tAge: 18
}
},
template: '#C-temp'
}
}
})

var vm = new Vue({
el: '#box'
})

image-20231223104215648

4.体验组件作用域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template id="qj-temp">
<ul><em>我是全局注册的组件</em></ul>
</template>

<div id="box①">
<h2><font color="red">全局º注册的组件box①</font></h2>
<h3>1.1全局注册的组件,能在每个Vue实例中使用。</h3>
<qj-component></qj-component>

<h3>1.2局部注册的组件,只能在当前Vue实例中使用。</h3>
<jb-component></jb-component>
</div>

<template id="jb-temp">
<ul><em>我是局部注册的组件</em></ul>
</template>

<div id="box②">
<h2><font color="red">局部º注册的组件box②</font></h2>
<h3>2.1全局注册的组件,能在每个Vue实例中使用。</h3>
<qj-component></qj-component>

<h3>2.1局部注册的组件,只能在当前Vue实例中使用。</h3>
<jb-component></jb-component>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 全局注册的组件,能在每个Vue实例中使用。
Vue.component('qj-component', {
template: '#qj-temp'
})

var vm = new Vue({
el: '#box①'
})

var vm = new Vue({
el: '#box②',
components: {
// 局部注册的组件,只能在当前Vue实例中使用。
'jb-component': {
template: '#jb-temp'
}
}
})

image-20231222154802477

⑶使用组件➾ 编写组件标签

  • 语法

    1
    2
    3
    4
    <div id="box">
    <!-- 3.使用组件 -->
    <组件名></组件名>
    </div>

三、组件复用

你可以将组件进行 ➾ 任意次数的复用

1
2
3
4
5
<div id="components-demo">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>

gif-231226154110

  • 🔔提示
    • 当点击按钮时,每个组件都会各自独立维护它的 count
    • 📌原因
      • 你每用一次组件,就会有一个它的新实例被创建。

四、组件间的通信

组件组件间的{嵌套使用}避免不了数据之间的传递。

  • 那么Vue中组件的数据是如何传递的呢?
    • 组件间数据传递不同于Vue全局的数据传递,
      • 组件实例的数据之间是孤立的不能在子组件的模板内直接引用父组件的数据。
      • 如果要把数据从父组件传递到子组件,就需要使用props属性

⑴父组件向子组件传递数据 ➾ 自定义属性

*.单向数据流

父子组件间的数据传递相当于 ➾ 自上而下的水管 ➾ 只能从上往下流,不能逆流

  • 🪶即
    1. 当父组件的属性更新时,子组件的所有prop都会更新为 ➾ 最新值
    2. 但是不能在 ➾ 子组件内部改变prop,
      • 当然修改是有效的{可以通过this.$parent.name访问到父组件中的属性},
      • ❗注意
        • 如果你这么做,Vue也会有警告提示。
      • ✨作用
        1. 防止子组件无意间修改 ➾ 父组件的状态。
        2. 避免 ➾ 应用的数据流变得难以理解。

1.通过Prop传递数据

prop是子组件用来接受父组件传递过来的数据的一个自定义属性

  • 语法

    1
    2
    3
    <div id="box">
    <tany 自定义属性名=属性值></tany>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Vue.component('tany', {
    // 声明 props
    props: ['自定义属性名'],

    template: '<span>{{ 自定义属性名 }}</span>'
    // ❗注意: 同样也可以在 vm 实例中像 “this.自定义属性名” 这样使用。
    // 但是模版中不用this。
    })

    // 创建根实例
    new Vue({
    el: '#box'
    })
  • 🔥案例

    1
    2
    3
    <div id="box">
    <TanyMsg tany-message="hello!"></TanyMsg>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Vue.component('TanyMsg', {
    // 声明 props
    props: ['tanyMessage'],

    template: '<span>{{ tanyMessage }}</span>'
    // ❗注意: 同样也可以在 vm 实例中像 “this.message” 这样使用。
    // 但是模版中不用 “this”。
    })

    // 创建根实例
    new Vue({
    el: '#box'
    })

    image-20231226155334644

    • ❗注意
      • HTML中的属性名对于 ➾ 大小写是不敏感的
        • 所以浏览器会把 ➾ 所有大写字符解释为小写字符。
          • 因此在外置模板中,不能直接使用camelCase{小驼峰命名方式}命名,
          • 需要使用其等价的kebab-case{短横线分隔方式}命名。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <template id="F-temp">
    <div>你好,我是{{tany}}!
    <my-name-age :t-age="tAge"></my-name-age>
    </div>
    </template>

    <template id="C-temp">
    <ul>我今年{{tAge}}岁。</ul>
    </template>

    <div id="box">
    <my-name-tany></my-name-tany>
    </div>
    • 🔔提示
      • 自定义属性{prop}用于子组件 ➾ 
        • <my-name-age :t-age="tAge"></my-name-age>
        • tAge
          • 来自于父组件的数据。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    Vue.component('my-name-tany', {
    data() {
    return {
    tany: '糖丸',
    tAge: 18
    }
    },
    template: '#F-temp',
    components: {
    'my-name-age': {
    props: ['tAge'],
    template: '#C-temp'
    }
    }
    })

    var vm = new Vue({
    el: '#box'
    })
    • props: ['tAge']
      • 定义于子组件,呼应 ➾ 
        • <my-name-age :t-age="tAge"></my-name-age>

2.Prop类型

子组件定义 props 有三种方式 ➾ 

1º数组方式
  • 可以是 ➾ 

    1. 静态的传递字符串 ➾ 不需要使用到v-bind指令。
    2. 数字、布尔值、数组、对象 ➾ 需要使用到v-bind指令。
      • 📌原因
        • 如上数据是变量。
  • 语法

    1
    props: ['属性名1', '属性名2', ...]
  • 🔥案例

    1. 静态字符串

      1
      <v-string message="hello!" my-message="hello world!"></v-string>
      1
      2
      3
      4
      5
      6
      7
      8
      Vue.component('v-string', {
      props: ['message', 'myMessage'],
      template: '<span>{{ message }} | {{ myMessage }}</span>'
      })

      new Vue({
      el: '#box'
      })

      image-20231227095840681

      • 🔔提示
        1. 当使用外置模板时,
          • 小驼峰命名方式{myMessage}需要转化为 ➾ 短横线分隔式命名方式{my-message}。
        2. 当使用template模版时,
          • 不受限制,可以为所欲为。
    2. 动态对象

      1
      2
      3
      4
      5
      6
      <v-string 
      :father-msg="fatherMsg"
      :my-age="myAge"
      :father-sz="[2, 4, 6]"
      :user-info="userInfo">
      </v-string>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      Vue.component('v-string', {
      props: ['fatherMsg', 'myAge', 'fatherSz', 'userInfo'],
      template: '<span>{{fatherMsg}} | {{myAge}} {{typeof myAge}} | {{fatherSz.length}} | {{userInfo.names}}</span>'
      })

      new Vue({
      el: '#box',
      data: {
      fatherMsg: '我是父组件属性',
      myAge: 18,
      userInfo: {
      names: '张三', sex: '男'
      }
      }
      })

      image-20231227101349323

2º对象方式
  1. 属性验证 ➾ '属性名': 字面量

    • 可以验证参数的类型,如果不符合数据规格,Vue 会发出警告,但将显示的数据依旧会显示。

    • 🔔提示

      • 字面量有 ➾ 

        1. String
        2. Number
        3. Boolean
        4. Array
        5. Object
        6. Date
        7. Function
        8. Symbol
      • 语法

        1
        2
        3
        4
        5
        props: {
        '属性名1': 字面量, //基础的类型检查。
        '属性名2': [字面量1, 字面量2, ...], // 多个可能的类型。
        ...
        }
      • 🔥案例

        1. myAge: String ➾ ❎

          1
          2
          3
          4
          5
          6
          7
          8
          <div id="box">
          <v-string
          :father-msg="fatherMsg"
          :my-age="myAge"
          :father-sz="[2,4,6]"
          :user-info="userInfo">
          </v-string>
          </div>
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          Vue.component('v-string', {
          props: {
          fatherMsg: String, //基础的类型检查。
          myAge: String,
          fatherSz: [Array, Number], // 多个可能的类型。
          userInfo: Object
          },
          template: '<span>{{fatherMsg}} | {{myAge}} {{typeof myAge}} | {{fatherSz.length}} | {{userInfo.names}}</span>'
          })

          new Vue({
          el: '#box',
          data: {
          fatherMsg: '我是父组件属性',
          myAge: 18,
          userInfo: {
          names: '张三', sex: '男'
          }
          }
          })

          image-20231227101349323

          image-20231227104948356

          • 🔔提示
            1. 当prop验证失败,Vue会抛出警告 ➾ 如果使用的是开发版本但是将显示的数据依旧会显示。
            2. nullundefined 会通过任何类型验证。
        2. myAge: Number ➾ ✅

          1
          2
          3
          4
          5
          6
          7
          8
          <div id="box">
          <v-string
          :father-msg="fatherMsg"
          :my-age="myAge"
          :father-sz="[2,4,6]"
          :user-info="userInfo">
          </v-string>
          </div>
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          Vue.component('v-string', {
          props: {
          fatherMsg: String, //基础的类型检查。
          myAge: Number,
          fatherSz: [Array, Number], // 多个可能的类型。
          userInfo: Object
          },
          template: '<span>{{fatherMsg}} | {{myAge}} {{typeof myAge}} | {{fatherSz.length}} | {{userInfo.names}}</span>'
          })

          new Vue({
          el: '#box',
          data: {
          fatherMsg: '我是父组件属性',
          myAge: 18,
          userInfo: {
          names: '张三', sex: '男'
          }
          }
          })

          image-20231227101349323

  2. 属性验证 + 默认属性 + 必填项 + 自定义验证函数。

    1. 属性验证 ➾ type: 字面量

      • 可以验证参数的类型,如果不符合数据规格,Vue 会发出警告,但将显示的数据依旧会显示。
      • 🔔提示
        • 字面量有 ➾ 
          1. String
          2. Number
          3. Boolean
          4. Array
          5. Object
          6. Date
          7. Function
          8. Symbol
    2. 默认属性 ➾ default: 属性值

      • 若没有接收值,则为默认值。
    3. 必填项 ➾ required: true

      • 此属性必须有属性值。
    4. 自定义验证函数 ➾ validator(){}

      • 语法

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        props: {
        '属性名1': { //基础的类型检查。
        type: 字面量,
        default: 属性值,
        required: true,
        validator(){}
        },
        '属性名2': { // 多个可能的类型。
        type: [字面量1, 字面量2, ...],
        default: 属性值,
        required: true,
        validator(){}
        },
        ...
        }
      • 🔥案例

        1. 基础的类型检查。

          1
          2
          3
          props: {
          propA: Number
          }
          • ❗注意
            • nullundefined值会通过任何类型验证。
        2. 多个可能的类型。

          1
          2
          3
          props: {
          propB: [String, Number]
          }
        3. 带有默认值的数字。

          1
          2
          3
          4
          5
          6
          props: {
          propNumber: {
          type: Number,
          default: 100
          }
          }
        4. 具有默认值的数组。

          1
          2
          3
          4
          5
          6
          7
          8
          props: {
          propArray: {
          type: Array,
          default() {
          return []
          }
          }
          }
          • ❗注意
            • 数组的默认值必须从 ➾ 一个工厂函数返回。
            • 📌原因
              1. 数组是「引用类型」,
                • 直接复制默认数组会导致 ➾ 多个组件实例共享 ➾ 同一个数组
              2. 为了避免这个问题,
                • 我们需要使用一个函数返回一个新的数组作为默认值。
        5. 具有默认值的对象。

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          props: {
          propObject: {
          type: Object,
          default() {
          return {
          name: 'John',
          age: 25
          }
          }
          }
          }
          • ❗注意
            • 对象的默认值必须从 ➾ 一个工厂函数返回。
            • 📌原因
              1. 对象是「引用类型」,
                • 直接复制默认对象会导致 ➾ 多个组件实例共享 ➾ 同一个对象
              2. 为了避免这个问题,
                • 我们需要使用一个函数返回一个新的对象作为默认值。
        6. 具有默认值的函数。

          1
          2
          3
          4
          5
          6
          7
          8
          props: {
          propFunction: {
          type: Function,
          default() {
          // 在这里编写你的默认函数逻辑。
          }
          }
          }
        7. 必填的字符串。

          1
          2
          3
          4
          5
          6
          props: {
          propString: {
          type: String,
          required: true
          }
          }
        8. 自定义验证函数。

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          props: {
          propValidator: {
          type: Array,

          // 当 prop 验证失败时,(开发环境构建版本的)Vue将会产生一个控制台的警告。
          validator(value) {
          console.log(value[0])

          return value[0] > 2
          }
          }
          }
          • 🍬解析
            • value
              • 传递来的属性值。
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          props: {
          propValidator: {
          type: String,

          // 当 prop 验证失败时,(开发环境构建版本的)Vue将会产生一个控制台的警告。
          validator(value) {
          // 这个值必须与下列字符串中的其中一个相匹配
          return ['success', 'warning', 'danger'].includes(value)
          }
          }
          }
          • 🍬解析
            • value
              • 传递来的属性值。

⑵子组件向父组件传递数据 ➾ 自定义事件

父组件使用props传递数据给子组件,但如果子组件要把数据传递回去,就需要使用自定义事件

  • 我们可以使用v-on指令绑定自定义事件,如此每个Vue实例都将实现事件接口,🪶即 ➾ 
    1. 使用$on(eventName)监听事件。
    2. 使用$emit(eventName)触发事件。

1.自定义事件

*º事件名

不同于组件prop,事件名不存在任何自动化的大小写转换。

  • 而是触发的事件名需要 ➾ 完全匹配 ➾ 监听这个事件所用的名称。

  • 🔥案例

    1. 事件名用 ➾ camelCase命名方式命名的事件。

      1
      this.$emit('myEvent')
    2. 监听这个事件名用 ➾ kebab-case命名方式不会 ➾ 有任何效果。

      1
      <my-component v-on:my-event="doSomething"></my-component>
      • 🔔提示
        • 不同于组件prop,
          1. 事件名不会被用作 ➾ 一个JavaScript变量名属性名,
            • 所以就没有理由使用 ➾ camelCase命名方式PascalCase命名方式。
          2. 并且v-on事件监听器在外置模板中,会被自动转换为 ➾ 全小写
            • 📌原因
              • HTML对于大小写不敏感。
              • 所以v-on:myEvent将会变成 ➾ 
                • v-on:myevent导致myEvent不可能被监听到。
                • 因此,我们推荐 ➾ 你始终使用kebab-case命名方式命名的事件名。
1º通过 events 传递数据

操作子组件中的控件,触发父组件中定义的事件。

  1. 父组件

    • 🔥案例

      1. 无附加参数

        1
        2
        3
        4
        5
        6
        7
        8
        9
        <template id="F-temp">
        <div>你好,我是{{tany}}!
        <my-name-age :t-age="tAge" @tany-welcome="changeAge"></my-name-age>
        </div>
        </template>

        <div id="box">
        <my-name-tany></my-name-tany>
        </div>
        • 🔔提示
          1. 子组件定义自定义事件,绑定父组件事件处理函数。
          2. @welcome="changeAge" ➾ @自定义事件名 = "事件处理函数名"
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        Vue.component('my-name-tany', {
        data() {
        return {
        tany: '糖丸',
        tAge: 18
        }
        },
        template: '#F-temp',
        methods: {
        changeAge() {
        this.tAge = 20
        }
        },
        components: {
        'my-name-age': {
        props: ['tAge'],
        template: '#C-temp'
        }
        }
        })

        var vm = new Vue({
        el: '#box'
        })

        gif-231230152845

      2. 有附加参数

        1
        2
        3
        4
        5
        6
        7
        8
        9
        <template id="F-temp">
        <div>你好,我是{{tany}}!
        <my-name-age :t-age="tAge" :per-e="perE" @tany-welcome="changes"></my-name-age>
        </div>
        </template>

        <div id="box">
        <my-name-tany></my-name-tany>
        </div>
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        Vue.component('my-name-tany', {
        data() {
        return {
        tany: '糖丸',
        tAge: 18,
        perE: ''
        }
        },
        template: '#F-temp',
        methods: {
        changes(age, perE) {
        this.tAge = age
        this.perE = perE
        }
        },
        components: {
        'my-name-age': {
        data() {
        return {
        pers: ['友好', '乐观', '开朗']
        }
        },
        props: ['tAge', 'perE'],
        template: '#C-temp',
        methods: {
        handlerClick() {
        this.$emit('tany-welcome', 20, this.pers[2])
        }
        }
        }
        }
        })

        var vm = new Vue({
        el: '#box'
        })

        gif-231230155102

  2. 子组件

    • 通过$emit触发父组件上的自定义事件,发送 ➾ 参数。

    • 🔥案例

      1. 无附加参数

        1
        2
        3
        4
        5
        6
        7
        <template id="C-temp">
        <div>
        <ul>我今年{{tAge}}岁。
        <button @click="$emit('tany-welcome')">改变年龄</button>
        </ul>
        </div>
        </template>
        • 🔔提示
          • $emit( 'eventName', [arg1, arg2, ...] )
            1. 'eventName'
              • 事件名,会绑定一个方法。当组件触发事件后,将调用这个方法。
            2. [arg1, arg2, ...]
              • 附加参数,可由上述绑定的事件接收使用。
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        Vue.component('my-name-tany', {
        data() {
        return {
        tany: '糖丸',
        tAge: 18
        }
        },
        template: '#F-temp',
        methods: {
        changeAge() {
        this.tAge = 20
        }
        },
        components: {
        'my-name-age': {
        props: ['tAge'],
        template: '#C-temp'
        }
        }
        })

        var vm = new Vue({
        el: '#box'
        })

        gif-231230152845

      2. 有附加参数

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        <template id="C-temp">
        <div>
        <ul>
        <li>我今年{{tAge}}岁。</li>
        <li>{{perE}}</li>
        <br>
        <button @click="handlerClick">改变年龄&给出性格评价</button>
        </ul>
        </div>
        </template>
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        Vue.component('my-name-tany', {
        data() {
        return {
        tany: '糖丸',
        tAge: 18,
        perE: ''
        }
        },
        template: '#F-temp',
        methods: {
        changes(age, perE) {
        this.tAge = age
        this.perE = perE
        }
        },
        components: {
        'my-name-age': {
        data() {
        return {
        pers: ['友好', '乐观', '开朗']
        }
        },
        props: ['tAge', 'perE'],
        template: '#C-temp',
        methods: {
        handlerClick() {
        this.$emit('tany-welcome', 20, this.pers[2])
        }
        }
        }
        }
        })

        var vm = new Vue({
        el: '#box'
        })

        gif-231230155102

五、兄弟组件通信🏅①

⑴中间人模式

在Vue中,每一个Vue实例就是一个根组件,而在根组件中,创建的全局组件都是属于 ➾ 这个根组件的子组件

  • 「中间人模式」就是 ➾ 

    • 两个组件通过子传父,把数据传递给根组件,然后根组件在把数据通过父传子传递给需要的子组件。
    • 🍒例如
      • 组件A组件B通信,组件A把数据对象data传送给根组件
        • 根组件接收到后,再把数据对象data传送给组件B,「根组件」在这个通信过程中就叫中间人
  • 🔥案例

    1. 父传子 ➾ 将数据传递给子组件 ➾ 显示内容。

      1
      2
      3
      4
      5
      <div id="box">
      <film-item v-for="item in datalist" :key="item.filmId" :mydata="item" @event="handleEvent"></film-item>

      <film-detail :film-data="filmData"></film-detail>
      </div>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      Vue.component("filmItem", {
      props: ["mydata"],
      template: `
      <div class="item">
      <img :src="mydata.poster"/>
      {{mydata.name}}
      <div>
      <button @click="handleClick">详情</button>
      </div>
      </div>
      `,
      methods: {
      handleClick() {
      // console.log(this.mydata.synopsis)
      this.$emit("event", this.mydata.synopsis)
      }
      }
      })
      1
      2
      3
      4
      5
      6
      7
      8
      Vue.component("filmDetail", {
      props: ["filmData"],
      template: `
      <div class="filminfo">
      {{filmData}}
      </div>
      `
      })
    2. 子传父 ➾ 点击详情按钮,将数据传递给父组件。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      new Vue({
      el: "#box",
      data: {
      datalist: [],
      filmData: ""
      },
      mounted() {
      fetch("./json/test.json")
      .then(res => res.json())
      .then(res => {
      console.log(res.data.films)
      this.datalist = res.data.films
      })
      },

      // 自定义事件处理器
      handleEvent(data) {
      console.log("父组件定义", data)

      this.filmData = data
      }
      })
      gif-231231105129

⑵中央事件总线

在之前我们只用过{父传子、子传父}进行传递数据,

  • 但是当组件嵌套比较深比较复杂的情况,这时候就需要用到 ➾ 中央事件总线{EventBus}。

  • 其跟「中间人模式」类似, 不过比较方便,

    • 这种模式的中间人bus,不用刻意的接收数据,这个bus更像是 ➾ 一个临时数据存放的 ➾ 容器

    • 🎭原理图示

      image-20240102102422436
  • 🎢步骤

    1. 创建一个空的Vue对象作为bus对象。

      1
      var bus = new Vue()
    2. 创建两个组件child1child2

      1
      2
      3
      4
      <div id="app">
      <child1></child1>
      <child2></child2>
      </div>
      1
      2
      3
      var app = new Vue({
      el: "#app",
      })
    3. 触发组件的事件\text{: }bus.$emit('事件名', [参数1, 参数2, ...]) ➾ 发布者

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      Vue.component("child1", {
      template: `
      <div> 发布者➾
      <button @click="handleClick">发布</button>
      </div>
      `,
      methods: { // 绑定一个事件用来触发 bus.$emit
      handleClick() {
      console.log("child1发送", "数据对象")
      bus.$emit("tany", "数据对象")
      } // 在这里通过一个中间人bus, 使用 $emit 在bus上绑定数据
      }
      })
    4. 给组件绑定事件\text{: }bus.$on('事件名',函数)  ➾ 订阅者

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      Vue.component("child2", {
      template: `
      <div> 订阅者➾ </div>
      `,
      mounted() {
      bus.$on("tany", (data) => { // 通过中间人提取 $on 监听到的数据
      console.log("child2接收", data)
      })
      }
      })
      • 🔔提示
        • mounted()
          • mounted为生命周期函数,在Dom结构创建完成后 ➾ 自动触发
    5. 给组件解绑事件\text{: }bus.$off('事件名',函数名)

      gif-231231103612

六、组件注意事项🏅②

⑴自定义属性

  • 父组件传递给子组件的属性,只有父组件可以重新传递,但不允许子组件随意修改。

  • 📌原因

    • 为了确保单向数据流的原则组件间的数据流动清晰。
  • 🔥案例

    1
    2
    3
    4
    5
    6
    <div id="box">
    <div>
    {{name}}
    </div>
    <child :myname="name"></child>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    Vue.component("child", {
    props: ["myname"],
    template: `
    <div>
    child➾ {{myname}}
    <button @click="handleClikc">click</button>
    </div>
    `,
    methods: {
    handleClikc() {
    this.myname = "糖宝"
    }
    }
    })

    var vm = new Vue({
    el: "#box",
    data: {
    name: "糖丸"
    }
    })

    gif-240103091516

    image-20240103091532634

    • 🔔提示
      • 虽然子组件修改成功了,但是Vue发出了警告 ➾ 
        • 子组件不要随意修改状态{data},下次页面更新时,仍会重新渲染。

⑵状态{data}

组件内部的状态,可以随意修改。

⑶v-once 指令🏅②

仅渲染指定组件元素一次,并跳过未来对其的更新

  • 使用场景

    • 如果我们有一些元素组件,在初始化渲染后,不再需要变化,适合使用v-once指令。
      • 哪怕这些数据变化,Vue也会跳过更新,是 ➾ 一种代码优化手段
  • 🔥案例

    1. <div v-once>

      1
      2
      3
      4
      5
      6
      <div id="box">
      <div v-once>
      {{name}}
      </div>
      <child :myname="name"></child>
      </div>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      Vue.component("child", {
      props: ["myname"],
      template: `
      <div>
      child➾ {{myname}}
      <button @click="handleClick">click</button>
      </div>
      `,
      methods: {
      handleClikc() {
      this.myname = "糖宝"
      }
      }
      })

      var vm = new Vue({
      el: "#box",
      data: {
      name: "糖丸"
      }
      })
      gif-240103093503
    2. <div v-once> + <div v-once>

      1
      2
      3
      4
      5
      6
      <div id="box">
      <div v-once>
      {{name}}
      </div>
      <child :myname="name"></child>
      </div>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      Vue.component("child", {
      props: ["myname"],
      template: `
      <div v-once>
      child➾ {{myname}}
      <button @click="handleClick">click</button>
      </div>
      `,
      methods: {
      handleClikc() {
      this.myname = "糖宝"
      }
      }
      })

      var vm = new Vue({
      el: "#box",
      data: {
      name: "糖丸"
      }
      })
      gif-240103093428

七、ref属性🏅①

ref属性被用来给元素子组件注册 ➾ 引用信息

  • 引用信息将会注册在父组件的$refs对象上。

  • 语法

    • ref="属性值"this.$refs.属性值
  • 🎈用法

    1. 如果在普通的DOM元素上使用,引用指向的就是 ➾ DOM元素

      • 🔥案例

        1
        2
        3
        4
        5
        <div id="box">
        <input type="text" ref="mytext" />
        <input type="password" ref="mypassword"/>
        <button @click="handleAdd">add</button>
        </div>
        1
        2
        3
        4
        5
        6
        7
        8
        new Vue({
        el: "#box",
        methods: {
        handleAdd() {
        console.log(this.$refs)
        }
        }
        })

        image-20240102110735477

        1
        2
        3
        4
        5
        6
        7
        8
        new Vue({
        el: "#box",
        methods: {
        handleAdd() {
        console.log(this.$refs.mytext, this.$refs.mypassword)
        }
        }
        })

        image-20240102110919430

    2. 如果在子组件上使用,引用指向的就是 ➾ 组件实例化对象

      • 🔥案例

        1
        2
        3
        4
        <div id="box">
        <child ref="mychild"></child>
        <button @click="handleAdd">add</button>
        </div>
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        Vue.component("child", {
        data() {
        return {
        myname: "tany"
        }
        },
        template: `<div>
        child➾ {{myname}}
        </div>`
        })

        new Vue({
        el: "#box",
        methods: {
        handleAdd() {
        console.log(this.$refs)
        }
        }
        })

        image-20240102111328300

        1
        2
        3
        4
        5
        6
        7
        8
        new Vue({
        el: "#box",
        methods: {
        handleAdd() {
        console.log(this.$refs.mychild)
        }
        }
        })

        image-20240102111413348

        1
        2
        3
        4
        5
        6
        7
        8
        new Vue({
        el: "#box",
        methods: {
        handleAdd() {
        console.log(this.$refs.mychild.myname)
        }
        }
        })

        image-20240102111523130

        1
        2
        3
        4
        5
        6
        7
        8
        new Vue({
        el: "#box",
        methods: {
        handleAdd() {
        this.$refs.mychild.myname = '糖丸'
        }
        }
        })

        gif-240102111803

八、动态组件🏅②

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="box">

<home v-show=" which==='home' "></home>
<list v-show=" which==='list' "></list>
<shopcar v-show=" which==='shopcar' "></shopcar>

<footer>
<ul>
<li @click=" which='home' ">
首页
</li>
<li @click=" which='list' ">
列表
</li>
<li @click=" which='shopcar' ">
购物车
</li>
</ul>
</footer>

</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Vue.component("home", {
template: `
<div>
home
<br/>
<input type="search"/>
</div>
`
})

Vue.component("list", {
template: `
<div>
list
</div>
`
})

Vue.component("shopcar", {
template: `
<div>
shopcar
</div>
`
})

var vm = new Vue({
el: "#box",
data: {
which: 'home'
}
})
gif-240103095126

⑴component组件的使用

component是Vue的内置组件,主要作用为 ➾ 动态渲染组件。

  • 语法

    1
    <component :is="componentName"></component>
    • 🍬解析
      • componentName
        • 组件名。
        • 🔔提示
          • 将依is的值{componentName},来决定哪个组件被渲染。
  • 🔥案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <div id="box">

    <component :is="which"></component>

    <footer>
    <ul>
    <li @click=" which='home' ">
    首页
    </li>
    <li @click=" which='list' ">
    列表
    </li>
    <li @click=" which='shopcar' ">
    购物车
    </li>
    </ul>
    </footer>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    Vue.component("home", {
    template: `
    <div>
    home
    <br/>
    <input type="search"/>
    </div>
    `
    })

    Vue.component("list", {
    template: `
    <div>
    list
    </div>
    `
    })

    Vue.component("shopcar", {
    template: `
    <div>
    shopcar
    </div>
    `
    })

    var vm = new Vue({
    el: "#box",
    data: {
    which: 'home'
    }
    })
    gif-240103095126
  • 缺点

    • 在每次切换完组件后,原先组件将会被 ➾ 彻底销毁,意味着数据不会保留。

      gif-240103101140

⑵keep-alive组件的使用🏅②

keep-alive是Vue的内置组件。

  • 🎈用法

    • 包裹动态组件时,会缓存 ➾ 不活动的组件实例,而不是销毁它们。
  • 属性

    1. include

      • 字符串正则表达式。只有 ➾ 名称匹配的组件 ➾ 被缓存

      • 🔔提示

        • 这里说的匹配的组件是 ➾ 通过Vue组件{name属性}匹配的。
      • 🔥案例

        1. 逗号分隔字符串。

          1
          2
          3
          <keep-alive include="a, b">
          <component :is="view"></component>
          </keep-alive>
        2. 正则表达式 ➾ 使用 v-bind

          1
          2
          3
          <keep-alive :include="/a|b/">
          <component :is="view"></component>
          </keep-alive>
        3. 数组 ➾ 使用 v-bind

          1
          2
          3
          <keep-alive :include="['a', 'b']">
          <component :is="view"></component>
          </keep-alive>
    2. exclude

      • 字符串正则表达式。任何 ➾ 名称匹配的组件都不会 ➾ 被缓存
      • 🔔提示
        • 这里说的匹配的组件是 ➾ 通过Vue组件{name属性}匹配的。
    3. max

      • 数字。「最多」可以缓存多少 ➾ 组件实例。
  • 🔥案例

    • 在每次切换完组件后,防止原先组件将会被 ➾ 彻底销毁

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      <div id="box">

      <keep-alive>
      <component :is="which"></component>
      </keep-alive>

      <footer>
      <ul>
      <li @click=" which='home' ">
      首页
      </li>
      <li @click=" which='list' ">
      列表
      </li>
      <li @click=" which='shopcar' ">
      购物车
      </li>
      </ul>
      </footer>
      </div>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      Vue.component("home", {
      template: `
      <div>
      home
      <br/>
      <input type="search"/>
      </div>
      `
      })

      Vue.component("list", {
      template: `
      <div>
      list
      </div>
      `
      })

      Vue.component("shopcar", {
      template: `
      <div>
      shopcar
      </div>
      `
      })

      var vm = new Vue({
      el: "#box",
      data: {
      which: 'home'
      }
      })
      gif-240103101253

九、插槽🏅③

组件标签内部嵌套其他元素 ➾ 无效,而是会用模板将组件标签整体替换。

  • 🔥案例

    1
    2
    3
    4
    5
    <div id="box">
    <child :myname="name">
    <div>测试</div>
    </child>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Vue.component("child", {
    props: ["myname"],
    template: `
    <div>
    child➾ {{myname}}
    </div>
    `
    })

    var vm = new Vue({
    el: "#box",
    data: {
    name: "糖丸"
    }
    })

    image-20240104095438140

⑴旧版slot

slot插槽是对组件的扩展,通过slot插槽向组件内部指定位置传递内容。

1.匿名插槽

又称为单个插槽、默认插槽,由于它隐藏的name属性为default,故而它是不需要设置 name 属性的。

  • 🎯特性

    1. 可以放置在组件的任意位置。
    2. 一个组件中,只能有 ➾ 一个匿名插槽
    3. 匿名插槽只能作为 ➾ 没有slot属性的元素的插槽。
  • 🔥案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <template id="temp">
    <div>
    我的信息➾
    <slot></slot>
    </div>
    </template>

    <div id="box">
    <tany>

    <!-- 我的信息➾ -->
    <div>
    <ul>
    <li>姓名: 糖丸</li>
    <li>年龄: 21</li>
    </ul>
    </div>

    </tany>
    </div>
    1
    2
    3
    4
    5
    6
    7
    Vue.component("tany", {
    template: '#temp'
    })

    new Vue({
    el: "#box"
    })

    image-20240104101813545

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <template id="temp">
    <div>
    我的信息➾
    <slot></slot>
    </div>
    </template>

    <div id="box">
    <tany>

    <!-- 我的信息➾ -->
    <div>
    <ul>
    <li>姓名: 糖丸</li>
    <li>年龄: 21</li>
    </ul>
    </div>

    <div>
    我的性格➾
    <ul>
    <li>可爱</li>
    <li>活泼</li>
    </ul>
    </div>

    </tany>
    </div>
    1
    2
    3
    4
    5
    6
    7
    Vue.component("tany", {
    template: '#temp'
    })

    new Vue({
    el: "#box"
    })

    image-20240104102155055

2.具名插槽

具有名字的插槽,名字通过name属性来定义。

  • 🎯特性

    1. 可以放置在组件的任意位置。
    2. 一个组件中,可以有 ➾ 多个具名插槽
    3. 具有插槽只能作为 ➾ 有slot属性的元素的插槽。
  • 🔥案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    <template id="temp">
    <div>
    <!-- 我的信息➾ -->
    <slot name="info"></slot>

    <!-- 我的性格➾ -->
    <slot name="per"></slot>

    <!-- 其他信息 -->
    <slot></slot>
    </div>
    </template>

    <div id="box">
    <tany>

    <div slot="info">
    我的信息➾
    <ul>
    <li>姓名: 糖丸</li>
    <li>年龄: 21</li>
    </ul>
    </div>

    <div slot="per">
    我的性格➾
    <ul>
    <li>可爱</li>
    <li>活泼</li>
    </ul>
    </div>

    <div>其他信息1</div>
    <div>其他信息2</div>

    </tany>
    </div>
    1
    2
    3
    4
    5
    6
    7
    Vue.component("tany", {
    template: '#temp'
    })

    new Vue({
    el: "#box"
    })
    image-20240104102954060

⑵新版slot

1.具名插槽🏅③

  • 语法

    1
    <template v-slot:name属性值>  === <template # name属性值>
  • 🔥案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    <template id="temp">
    <div>
    <!-- 我的信息➾ -->
    <slot name="info"></slot>

    <!-- 我的性格➾ -->
    <slot name="per"></slot>

    <!-- 其他信息 -->
    <slot></slot>
    </div>
    </template>

    <div id="box">
    <tany>

    <template v-slot:info>
    <div>
    我的信息➾
    <ul>
    <li>姓名: 糖丸</li>
    <li>年龄: 21</li>
    </ul>
    </div>
    </template>

    <template #per>
    <div>
    我的性格➾
    <ul>
    <li>可爱</li>
    <li>活泼</li>
    </ul>
    </div>
    </template>

    <div>其他信息1</div>
    <div>其他信息2</div>

    </tany>
    </div>
    1
    2
    3
    4
    5
    6
    7
    Vue.component("tany", {
    template: '#temp'
    })

    new Vue({
    el: "#box"
    })

    image-20240104102155055

十、过渡&动画🏅④

⑴transition组件的使用

transition是Vue的内置组件。

  • ✨作用

    • Vue在插入、更新移除 DOM 时,提供多种不同方式的应用过渡效果。
  • 🎈用法

    • 在下列情况下,将触发 ➾ 过渡动画效果。
      1. 条件渲染{使用v-if指令}。
      2. 条件展示{使用-show指令}。
      3. 动态组件。
      4. 组件根节点。
  • 语法

    1. transition组件属性

      1
      2
      3
      <transition name="value" appear>
      <p v-if="show">hello</p>
      </transition>
      • 🍬解析
        1. value「string」
          • 用于自动生成 CSS 过渡类名。
          • 🍒例如
            1. name: 'fade' 将自动拓展为 ➾ 
              • .fade-enter,.fade-enter-active等。
            2. 默认类名为v
        2. appear「boolean」
          1. 是否在初始渲染时,使用过渡。
          2. 默认值为false
    2. 过渡的类名

      • 在进入/离开的过渡中,会有6个class切换。

        1. v-enter

          • 定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
        2. v-enter-active

          • 定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
        3. v-enter-to

          • 2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
        4. v-leave

          • 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
        5. v-leave-active

          • 定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
        6. v-leave-to

          • 2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。

          image-20240108111810697

  • 🔥案例

    1. name="fade"

      1
      2
      3
      4
      5
      6
      7
      8
      9
      <div id="box">
      <button v-on:click="show = !show">
      Toggle
      </button>

      <transition name="fade">
      <p v-if="show">hello</p>
      </transition>
      </div>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      .fade-enter-active,
      .fade-leave-active {
      transition: opacity .5s;
      }

      .fade-enter,
      .fade-leave-to {
      opacity: 0;
      }
      1
      2
      3
      4
      5
      6
      new Vue({
      el: '#box',
      data: {
      show: true
      }
      })

      gif-240108104911

      • 👁️‍🗨️分析

        • 当插入删除包含在transition组件中的元素时,Vue 将会做以下处理 ➾ 

          1. 自动检测目标元素是否应用了 ➾ CSS 过渡或动画,

            • 如果是,在恰当的时机添加/删除 CSS 类名。

              gif-240108105626
          2. 若过渡组件提供了 JavaScript 钩子函数,则这些钩子函数将在恰当的时机被调用。

          3. 若没有找到 JavaScript 钩子并且也没有检测到 CSS 过渡/动画,

            • 则DOM 操作(插入/删除)在下一帧中立即执行。
    2. appear

      1
      2
      3
      4
      5
      6
      7
      8
      9
      <div id="box">
      <button v-on:click="show = !show">
      Toggle
      </button>

      <transition name="fade" appear>
      <p v-if="show">hello</p>
      </transition>
      </div>

      gif-240108111644

⑵⑶⑷⑸