KnightSama‘s Blog

vuePress-theme-reco KnightSama    2021
KnightSama‘s Blog KnightSama‘s Blog

Choose mode

  • dark
  • auto
  • light
首页
分类
  • iOS
  • 集锦
  • JavaScript
  • Github
  • Python
标签
时间线
GitHub
author-avatar

KnightSama

27

文章

14

标签

首页
分类
  • iOS
  • 集锦
  • JavaScript
  • Github
  • Python
标签
时间线
GitHub
  • Vue 学习笔记—组件

    • 组件注册
      • 全局组件注册
      • 局部组件注册
    • 给组件添加数据
      • 给组件添加方法
        • 动态组件
          • 通过 v-if 切换
          • 通过 is 属性切换
        • 异步组件
          • 单文件组件
            • Src 导入
            • 使用单文件组件
          • 自定义属性
            • prop
            • 传递对象的所有属性
          • 自定义事件
            • 监听组件事件
            • 让组件支持 v-model
          • 组件传值
            • 父组件向子组件传值
            • 子组件向父组件传值
            • 访问根实例与父组件实例
            • 依赖注入
            • 访问子组件实例
          • 插槽
            • 默认内容
            • 具名插槽
            • 作用域插槽

        Vue 学习笔记—组件

        vuePress-theme-reco KnightSama    2021

        Vue 学习笔记—组件


        KnightSama 2020-05-25 Vue

        组件 (component) 是可复用的 Vue 实例,可以扩展 HTML 元素,封装可重用的代码。我们可以通过不同的组件来划分不同的功能模块,当我们需要相应功能就去调用对应的组件即可。 因为组件是可复用的 Vue 实例,所以它与普通的 Vue 实例接收相同的选项,例如 data 、 computed 、 watch 、 methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

        # 组件注册

        # 全局组件注册

        我们可以通过 Vue.component 来注册全局组件,其有两个参数,第一个参数是要创建的组件名称,第二个参数是组件的定义。在组件的定义中通过 template 关键字来指明组件的 html 元素,当我们需要使用组件时通过组件名称来调用。组件只能使用在 Vue 实例控制的区域

        # 注册组件方式一

        将组件内容直接放在组件定义中,当内容比较简单时可以采用该方式

        <html>
        
        <div id="vue-example-1">
            <!-- 使用组件 -->
            <my-component-1></my-component-1>
        </div>
        
        <script>
            // 定义组件
            Vue.component('my-component-1', {
                template: '<div><p>hello Vue</p></div>'
            });
            
            new Vue({
                el: '#vue-example-1',
            });
        </script>
        
        </html>
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19

        # 注册组件方式二

        将组件内容放到 template 标签中去,通过 id 来指示模板,这也是推荐的用法

        <html>
        
        <!-- 定义模板 -->
        <template id="my-template-1">
            <div>
                <p>hello Vue</p>
            </div>
        </template>
        
        <div id="vue-example-2">
            <!-- 使用组件 -->
            <my-component-2></my-component-2>
        </div>
        
        <script>
            // 定义组件
            Vue.component('my-component-2', {
                // 使用模板 id
                template: '#my-template-1'
            });
            
            new Vue({
                el: '#vue-example-2',
            });
        </script>
        
        </html>
        
        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

        # 局部组件注册

        我们可以在某个 Vue 实例中注册局部组件,这样注册的组件只在该实例中有效

        <html>
        
        <!-- 定义模板 -->
        <template id="my-template-2">
            <div>
                <p>hello Vue</p>
            </div>
        </template>
        
        <div id="vue-example-3">
            <!-- 使用组件 -->
            <my-component-3></my-component-3>
        </div>
        
        <script> 
            new Vue({
                el: '#vue-example-3',
                components: {  // 注册局部组件
                    'my-component-3': {
                        template: '#my-template-2'
                    }
                }
            });
        </script>
        
        </html>
        
        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

        # 给组件添加数据

        当组件需要动态展示信息时就需要给组件添加 data , 给组件添加的 data 其作用域只限于该组件中

        <html>
        
        <!-- 定义模板 -->
        <template id="my-template-3">
            <div>
                <p>{{message}}</p>
            </div>
        </template>
        
        <div id="vue-example-4">
            <!-- 使用组件 -->
            <my-component-4></my-component-4>
        </div>
        
        <script> 
            // 定义组件
            Vue.component('my-component-4', {
                template: '#my-template-3',
                // 添加 data
                data: function () {
                    return {
                        message: 'hello Vue'
                    }
                }
            });
            
            new Vue({
                el: '#vue-example-4',
            });
        </script>
        
        </html>
        
        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 实例不同,组件中的 data 不再是一个对象而是一个函数,这个函数将数据对象返回,也就是说组件的数据对象必须包裹在函数中。除此之外组件中的 data 使用与普通的 Vue 实例完全相同

        为什么组件的 data 是函数

        组件通过函数返回对象,是为了让每个组件都有自己独立的数据存储,保证每个组件数据的独立性。由于组件可以被多次使用,如果简单的通过对象定义,则每次创建该组件时都会使用同一套数据,组件与组件间就不再独立。而通过函数返回值的方式来定义可以保证每次组件创建时都会调用该函数重新创建数据对象,保证每个组件数据存储的独立性。

        # 给组件添加方法

        跟 data 一样我们通过 method 给组件添加方法,方法的作用域局限于该组件中

        <html>
        
        <!-- 定义模板 -->
        <template id="my-template-4">
            <div>
                <p>{{count}}</p>
                <input type="button" value="点击增加数字" @click="add">
            </div>
        </template>
        
        <div id="vue-example-5">
            <!-- 使用组件 -->
            <my-component-5></my-component-5>
        </div>
        
        <script> 
            // 定义组件
            Vue.component('my-component-5', {
                template: '#my-template-4',
                data: function() {
                    return {
                        count: 0
                    }
                },
                // 添加方法
                methods: {
                    add: function() {
                        this.count++
                    }
                }
            });
            
            new Vue({
                el: '#vue-example-5',
            });
        </script>
        
        </html>
        
        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

        # 动态组件

        # 通过 v-if 切换

        我们可以通过 v-if 与 v-else 组合的方式控制多个组件之间显示的切换

        <html>
        
        <div id="vue-example-6">
            <my-component-6 v-if=flag></my-component-6>
            <my-component-7 v-else=flag></my-component-7>
            <input type="button" value="点击切换组件" @click="flag = !flag">
        </div>
        
        <script> 
            Vue.component('my-component-6', {
                template: '<div style="width: 100px;height: 100px;background-color: red"></div>'
            });
            
            Vue.component('my-component-7', {
                template: '<div style="width: 100px;height: 100px;background-color: green"></div>'
            });
            
            new Vue({
                el: '#vue-example-6',
                data: {
                     flag: true
                }
            });
        </script>
        
        </html>
        
        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

        # 通过 is 属性切换

        当我们需要对多个组件进行切换时 Vue 提供了 <component> 标签实现,通过 :is 属性来指明需要显示的组件名称或名称变量

        <html>
        
        <div id="vue-example-7">
            <!-- 此处显示 name 变量指定的组件 -->
            <component :is="name"></component>
            <input type="button" value="点击切换组件" @click="change">
        </div>
        
        <script> 
            Vue.component('my-component-8', {
                template: '<div style="width: 100px;height: 100px;background-color: red"></div>'
            });
            
            Vue.component('my-component-9', {
                template: '<div style="width: 100px;height: 100px;background-color: green"></div>'
            });
            
            new Vue({
                el: '#vue-example-7',
                data: {
                     name: "my-component-8"
                },
                methods: {
                     change: function() {
                         this.name = (this.name === "my-component-8") ? "my-component-9" : "my-component-8"
                     }
                }
            });
        </script>
        
        </html>
        
        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

        # 保持组件状态

        组件切换时 Vue 会重新创建一个组件的实例,但有时后我们希望能够将组件缓存下来,避免反复重渲染导致的性能问题。这个时候可以使用 <keep-alive> 标签将需要缓存的组件包裹起来。这样我们就可以保存组件切换时的状态,例如在切换选择列表时记住用户上次的选择

        下面的例子中被 <keep-alive> 包裹的组件输入框在切换时输入的内容会保存下来,而没有包裹的组件内容会丢失

        <html>
        
        <div id="vue-example-17">
            <h5>没有 keep-alive 标签的组件切换会丢失之前输入的内容</h5>
            <component :is="name"></component>
            <h5>有 keep-alive 标签的组件切换会保留之前的内容</h5>
            <keep-alive>
                <component :is="name"></component>
            </keep-alive>
            <br/>
            <input type="button" value="点击切换组件" @click="change">
        </div>
        
        <script> 
            Vue.component('my-component-18', {
                template: '<div>姓名:<input type="text"></div>'
            });
            
            Vue.component('my-component-19', {
                template: '<div>年龄:<input type="text"></div>'
            });
            
            new Vue({
                el: '#vue-example-17',
                data: {
                     name: "my-component-18"
                },
                methods: {
                     change: function() {
                         this.name = (this.name === "my-component-18") ? "my-component-19" : "my-component-18"
                     }
                }
            });
        </script>
        
        </html>
        
        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 允许以一个工厂函数的方式定义组件,这个工厂函数会异步解析组件定义,Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。

        下面的例子中用 setTimeout 模拟一个异步请求,实际情况中这里大多是一个网络请求,返回的组件定义类通过 resolve 异步返回,通过回调 reject(reason) 来表示加载失败

        <html>
        
        <div id="vue-example-18">
            <my-component-20></my-component-20>
        </div>
        
        <script> 
            Vue.component('my-component-20', function (resolve, reject) {
                // 模拟异步请求返回
                setTimeout(function () {
                    resolve({
                        template: '<div>这是异步组件</div>'
                    })
                }, 1000)
            });
            
            new Vue({
                el: '#vue-example-18',
            });
        </script>
        
        </html>
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22

        我们还可以在工厂方法中返回一个 Promise 对象

        // 这里 import 返回一个 Promise 对象
        Vue.component('my-component-20', () => import('./async-component')});
        
        1
        2

        # 单文件组件

        除了上面定义组件的方法外我们还可以将组件按格式定义到一个单独的 .vue 文件中,这就是单文件的组件。 .vue 文件是一个自定义的文件类型,用类 HTML 语法描述一个 Vue 组件。每个 .vue 文件包含三种类型的顶级语言块 <template> 、 <script> 和 <style> ,还允许添加可选的自定义块

        <template>
          <div class="example">{{ msg }}</div>
        </template>
        
        <script>
        export default {
          name: "组件名称",
          data () {
            return {
              msg: 'Hello Vue'
            }
          }
        }
        </script>
        
        <style>
        .example {
          color: red;
        }
        </style>
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20

        这个文件无法被浏览器直接使用,需要通过 vue-loader 解析。 vue-loader 支持使用非默认语言,例如,你可以像下面这样使用 Sass 语法编写样式

        <style lang="sass">
          ...
        </style>
        
        1
        2
        3

        # Src 导入

        除了上面将所有内容写到一个文件中外,还可以用下面的方式从多个文件中导入

        <template src="./template.html"></template>
        <style src="./style.css"></style>
        <script src="./script.js"></script>
        
        1
        2
        3

        # 使用单文件组件

        首先需要通过 import 导入你要引入的组件,然后像使用普通组件那样注册使用就可以了。例如我们要在某个组件中引入 myComponent.vue 中的 myComponent 组件

        <template>
            <div id="app">
                <my-component></my-component>
            </div>
        </template>
        
        <script>
            import myComponent from './myComponent.vue'
            
            export default {
                name: 'app',
                components: {
                    myComponent
                }
            }
        </script>
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16

        # 自定义属性

        # prop

        Vue 允许我们在自己的组件中注册自定义的属性,这些自定义的属性称为 prop 。在使用组件时给这些属性赋值可以改变组件内部的状态或数据。当一个值传递给一个 prop 属性的时候,它就变成了那个组件实例的一个属性。一个组件默认可以拥有任意数量的 prop ,任何值都可以传递给 prop 。

        我们可以通过在组件对象中添加 props 属性来定义 prop ,同时我们可以在组件的模板中使用这些 prop 属性就如同使用 data 中的属性一样。并且在使用模板时可以为自定义的属性赋值,还可以使用 v-bind 绑定动态数据。

        <html>
        
        <div id="prop-example">
            <!-- 直接给自定义的 title 属性赋值 -->
            <!-- 绑定自定义的 message 属性到 info 变量上 -->
            <prop-component title="测试 prop" v-bind:message="info"></prop-component>
        </div>
        
        <script> 
            Vue.component('prop-component', {
                // 在模板中使用自定义属性
                template: '<div><h3>{{title}}</h3><p>{{message}}</p></div>',
                // 为组件添加两个自定义属性
                props: ['title', 'message']
            });
            
            new Vue({
                el: '#prop-example',
                data: {
                    info:"这是绑定的 Vue 实例中的数据"
                }
            });
        </script>
        
        </html>
        
        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

        除了上面例子中传入字符串外,我们可以将各种类型的数据传递给 prop ,如数字、布尔值、数组、对象等,不过当传入这些数据时需要使用 v-bind 绑定作为 JavaScript 表达式传入,否则会被当做字符串

        <!-- 传入数字,没有 v-bind 会变成字符串 -->
        <component v-bind:data="520"></component>
        <!-- 传入布尔值 -->
        <component v-bind:data="false"></component>
        <!-- 传入数组 -->
        <component v-bind:data="['a','b','c']"></component>
        <!-- 传入对象 -->
        <component v-bind:data="{title: '标题', message: '消息'}"></component>
        
        1
        2
        3
        4
        5
        6
        7
        8

        # 传递对象的所有属性

        如果需要将一个对象的所有属性全部传递给 prop ,可以使用不带参数的 v-bind 取代 v-bind:xxx 。例如下面的对象 message

        message: {id: 1, title: "标题"}
        
        1

        全部传递给 prop

        <component v-bind="message"></component>
        
        1

        等价于

        <component v-bind:id="message.id" v-bind:title="message.title"></component>
        
        1

        # 自定义事件

        # 监听组件事件

        我们已经知道 Vue 可以通过 v-on 来绑定事件,如 <input> 标签的点击事件等。那如何监听我们自己组件内部产生的事件呢,Vue 实例提供了一个自定义事件的系统来解决这个问题。子组件可以通过内建方法 $emit 来触发一个事件, $emit 通过传入事件名来触发事件,同时可以携带多个参数。父级组件可以通过 v-on 绑定来监听子组件实例的任意事件。

        <html>
        
        <template id="my-template-7">
            <div>
                <!-- 通过 $emit 触发一个名称为 child-event 的事件    -->
                <input type="button" value="点击调用父组件方法" @click="$emit('child-event')"> 
            </div>
        </template>
        
        <div id="vue-example-9">
            <!-- 通过 v-on 监听组件事件 child-event 并绑定到事件 alert  -->
            <my-component-11 @child-event="alert"></my-component-11>
        </div>
        
        <script> 
            Vue.component('my-component-11', {
                template: '#my-template-7',
            });
            
            new Vue({
                el: '#vue-example-9',
                methods: {
                    // 定义要调用的父组件方法
                    alert: function () {
                        alert("监听到了组件的点击事件")
                    }
                }
            });
        </script>
        
        </html>
        
        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

        # 让组件支持 v-model

        我们知道表单组件可以通过 v-model 进行双向绑定,如何让我们的组件也支持呢?实际上 v-model 相当于先通过 v-bind 绑定 value 属性到一个变量,然后通过 v-on 监听表单的输入事件改变变量的值

        <input type="text" v-model="message">
        
        1

        等价于

        <input type="text" v-bind:value="message" v-on:input="message = $event.target.value">
        
        1

        因此要使我们自己的组件支持 v-model ,首先需要添加一个名为 value 的 prop ,然后触发名称为 input 的事件。组件内的 <input> 控件将 value 属性绑定到名为 value 的 prop 属性,同时监听 input 事件触发一个名为 input 的事件

        <html>
        
        <template id="my-template-13">
            <div>
                <input type="text" v-bind:value="value" v-on:input="$emit('input',$event.target.value)">
            </div>
        </template>
        
        <div id="vue-example-19">
            <h5>{{message}}</h5>
            <my-component-21 v-model="message"></my-component-21>
        </div>
        
        <script> 
            Vue.component('my-component-21', {
                template: '#my-template-13',
                props: ['value'],
            });
            
            new Vue({
                el: '#vue-example-19',
                data:{
                    message: "测试组件 v-model"
                }
            });
        </script>
        
        </html>
        
        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

        通常情况下组件的 v-model 默认利用名为 value 的 prop 跟名为 input 的事件,但是像单选框这种应该使用 checked 跟 change 事件。这个时候需要使用组件实例的 model 属性变更默认的 prop 与事件。

        <html>
        
        <template id="my-template-14">
            <div>
                <input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change',$event.target.checked)">点击选择框查看选中结果
            </div>
        </template>
        
        <div id="vue-example-20">
            <h5>{{selected ? "选中" : "未选中"}}</h5>
            <my-component-22 v-model="selected"></my-component-22>
        </div>
        
        <script> 
            Vue.component('my-component-22', {
                template: '#my-template-14',
                props: ['checked'],
                model: {
                    prop: "checked",
                    event: "change"
                }
            });
            
            new Vue({
                el: '#vue-example-20',
                data:{
                    selected: true
                }
            });
        </script>
        
        </html>
        
        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 实例理解为一个父组件,自己定义的组件就是子组件,子组件无法直接使用父组件的数据,这时需要通过 prop 进行传值。

        第一步:在子组件的 props 属性数组中定义一个用来接收父组件传递值的自定义属性

        第二步:在子组件模板中需要使用父组件值的位置使用自定义的属性

        第三步:使用组件时将父组件要传值的属性绑定到 prop 上

        <html>
        
        <template id="my-template-6">
            <div>
                <!-- 定义模板时使用 props 中的属性 -->
                <p>{{parentMsg}}</p>
            </div>
        </template>
        
        <div id="vue-example-8">
            <!-- 使用模板时将 prop 属性与父组件要传值的属性进行绑定 -->
            <my-component-10 :parent-msg="message"></my-component-10>
        </div>
        
        <script> 
            Vue.component('my-component-10', {
                template: '#my-template-6',
                // 声明需要接收父组件值的属性
                props: ['parentMsg']
            });
            
            new Vue({
                el: '#vue-example-8',
                data: {
                     // 父组件要传递的值
                     message: "hello Vue"
                }
            });
        </script>
        
        </html>
        
        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

        注意

        props 中的属性为只读的不可修改,也就是子组件无法通过这种方式修改父组件的值

        # 子组件向父组件传值

        子组件向父组件传值需要子组件通过 $emit 调用父组件的方法,并把值通过参数传递出去。

        <html>
        
        <template id="my-template-8">
            <div>
                <input type="button" value="点击调用父组件方法并传值" @click="childMethod"> 
            </div>
        </template>
        
        <div id="vue-example-10">
            <my-component-12 @parent-method="alert"></my-component-12>
        </div>
        
        <script> 
            Vue.component('my-component-12', {
                template: '#my-template-8',
                data: function () {
                    return {
                        // 要传递的数据
                        message: "调用父组件方法并传值"
                    }
                },
                methods: {
                    // 在子组件中定义一个方法通过 emit 触发父组件方法,并通过参数携带数据
                    childMethod: function () {
                        this.$emit("parent-method",this.message)
                    }
                }
            });
            
            new Vue({
                el: '#vue-example-10',
                methods: {
                    // 定义要调用的父组件方法并带有参数接收数据,可以有多个参数来接收多个数据
                    alert: function (arg) {
                        alert(arg)
                    }
                }
            });
        </script>
        
        </html>
        
        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

        # 访问根实例与父组件实例

        每个 Vue 实例的子组件都可以使用 $root 来访问这个 Vue 根实例,使用 $parent 可以访问其父组件实例。

        <html>
        
        <div id="vue-example-21">
            <my-component-parent></my-component-parent>
        </div>
        
        <script> 
            Vue.component('my-component-parent', {
                template: '<div><my-component-child></my-component-child></div>',
                data: function () {
                    return {
                        message: "父组件实例的数据"
                    }
                }
            });
            
            Vue.component('my-component-child', {
                template: '<h3>{{this.$root.message}} - {{this.$parent.message}}</h3>',
            });
            
            new Vue({
                el: '#vue-example-21',
                data:{
                    message: "根实例的数据"
                }
            });
        </script>
        
        </html>
        
        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

        # 依赖注入

        当出现多层组件嵌套时,如果最内层的组件需要通过 $parent 访问外层组件的实例有可能会出现 this.$parent.$parent.message 这样的情况,甚至可能会嵌套更多层。这个时候我们可以使用依赖注入的方式来实现。依赖注入需要用到两个组件的属性 provide 和 inject 。

        provide 允许组件指定想让子组件访问的数据或方法。任何子组件都可以通过 inject 来接收我们想要添加在这个组件上的属性。这时我们就可以在这个子组件中直接访问数据。

        <html>
        
        <div id="vue-example-22">
            <my-component-provide></my-component-provide>
        </div>
        
        <script> 
            Vue.component('my-component-provide', {
                template: '<div><my-component-inject></my-component-inject></div>',
                data: function () {
                    return {
                        message: "通过依赖注入获取父组件的数据"
                    }
                },
                provide: function () {
                    return {
                        // 让子组件通过 getMessage 访问 message
                        getMessage: this.message
                    }
                }
            });
            
            Vue.component('my-component-inject', {
                // 模板中直接使用 getMessage 获取父组件数据
                template: '<h3>{{this.getMessage}}</h3>',
                // 子组件接收 getMessage
                inject: ['getMessage']
            });
            
            new Vue({
                el: '#vue-example-22',
            });
        </script>
        
        </html>
        
        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

        # 访问子组件实例

        在 Vue 中我们可以通过 ref 属性来获取 DOM 元素

        第一步:给标签设置 ref 属性

        第二步:通过 this.$refs.xxx 获取 DOM 元素

        <html>
        
        <div id="vue-example-11">
            <div ref="example">通过 ref 获取 DOM 元素</div>
            <input type="button" value="点击获取 DOM 元素" @click="alert">
        </div>
        
        <script> 
            new Vue({
                el: '#vue-example-11',
                methods: {
                    alert: function () {
                        alert(this.$refs.example.innerText)
                    }
                }
            });
        </script>
        
        </html>
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19

        同样我们将 ref 属性添加到子组件上,这样就可以在父组件中直接拿到子组件以及其中的数据与方法

        <html>
        
        <div id="vue-example-12">
            <!-- 在子组件上添加 ref -->
            <my-component-13 ref="component13"></my-component-13>
            <input type="button" value="点击获取子组件数据" @click="getChildData">
            <input type="button" value="点击调用子组件方法" @click="getChildMethod">
        </div>
        
        <script> 
            Vue.component('my-component-13', {
                template: '<div>获取子组件</div>',
                data: function () {
                    return {
                        // 要传递的数据
                        message: "子组件的数据"
                    }
                },
                methods: {
                    // 在子组件中定义一个方法通过 emit 触发父组件方法,并通过参数携带数据
                    childMethod: function () {
                        alert("调用了子组件的方法")
                    }
                }
            });
            
            new Vue({
                el: '#vue-example-12',
                methods: {
                    getChildData: function (arg) {
                        alert(this.$refs.component13.message)
                    },
                    getChildMethod: function (arg) {
                        this.$refs.component13.childMethod()
                    }
                }
            });
        </script>
        
        </html>
        
        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

        # 插槽

        在子组件中插入 slot 标签,当组件渲染时该标签将被替换为组件标签间包裹的内容。如果子组件中没有 slot 标签则中间包裹的内容将全部丢弃。

        <html>
        
        <template id="my-template-9">
            <div>
                <!-- 渲染时会被替换 -->
                <slot></slot>
            </div>
        </template>
        
        <div id="vue-example-13">
            <my-component-14>
                这里的内容会去替换子组件中的 slot 标签,若子组件没有 slot 标签,则该处的内容会被丢弃
            </my-component-14>
            <my-component-14>
                <p>这里可以插入任何模板代码</p>
                <p>{{message}}</p>
            </my-component-14>
        </div>
        
        <script> 
            Vue.component('my-component-14', {
                template: '#my-template-9',
            });
            
            new Vue({
                el: '#vue-example-13',
                data: {
                    message:"这里还可以使用 Vue 实例中的数据"
                }
            });
        </script>
        
        </html>
        
        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

        在 2.6.0 以后的版本 slot 已被替换为 v-slot

        # 默认内容

        我们可以在 slot 标签间插入默认内容,当没有内容替换时就显示默认内容

        <html>
        
        <template id="my-template-10">
            <div>
                <slot>这是默认内容</slot>
            </div>
        </template>
        
        <div id="vue-example-14">
            <my-component-15></my-component-15>
            <my-component-15><p>默认内容已被替换</p></my-component-15>
        </div>
        
        <script> 
            Vue.component('my-component-15', {
                template: '#my-template-10',
            });
            
            new Vue({
                el: '#vue-example-14',
                data: {
                    message:"这里还可以使用 Vue 实例中的数据"
                }
            });
        </script>
        
        </html>
        
        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

        # 具名插槽

        有时我们可能在一个组件中添加多个不同的插槽,这时需要使用 name 属性来区分,一个不带 name 属性的插槽默认为 default。

        <slot name="xxx"></slot>
        
        1

        在向具名插糟提供内容时需要使用 <template> 元素并添加 v-slot 参数形式的指令来指明插槽

        <template v-slot:xxx>替换插糟内容</template>
        
        1
        <html>
        
        <template id="my-template-11">
            <div>
                <slot name="header"></slot>
                <slot></slot>
                <slot name="footer"></slot>
            </div>
        </template>
        
        <div id="vue-example-15">
            <my-component-16>
                <template v-slot:header><p>此处将替换 name 为 header 的插槽</p></template>
                <p>此处将替换没有 name 的插槽</p>
                <template v-slot:footer><p>此处将替换 name 为 footer 的插槽</p></template>
            </my-component-16>
        </div>
        
        <script> 
            Vue.component('my-component-16', {
                template: '#my-template-11',
            });
            
            new Vue({
                el: '#vue-example-15',
            });
        </script>
        
        </html>
        
        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

        动态插槽名

        v-slot 可以使用动态的插槽名

        <template v-slot:[slotName]></template>
        
        1

        v-slot 缩写

        v-slot: 可以缩写为 #

        <template v-slot:header></template>
        
        1

        可以缩写为

        <template #header></slot>
        
        1

        # 作用域插槽

        通常情况下用来替换插槽的内容处于组件的父级作用域内因此无法访问组件的数据,如果我们想让插槽内容访问组件数据需要使用插槽 prop

        第一步:在 <slot> 上将要访问的子组件数据与属性进行绑定,绑定在 <slot> 元素上的属性被称为插槽 prop

        第二步:在父级作用域上使用带值的 v-slot 定义插槽 prop 的名称

        第三步:使用定义的名称来访问绑定到相应 slot 上的数据

        <html>
        
        <template id="my-template-12">
            <div>
                <!-- 绑定要访问的数据 -->
                <slot v-bind:message="message"></slot>
            </div>
        </template>
        
        <div id="vue-example-16">
            <my-component-17>
                <!-- 在父级作用域上通过带参 v-slot 定义插槽 prop 的名称 -->
                <!-- 通过定义的名称访问绑定的子组件数据 -->
                <!-- 不带 name 属性的 slot 默认为 default -->
                <template v-slot:default="prop"><p>{{prop.message}}</p></template>
            </my-component-17>
        </div>
        
        <script> 
            Vue.component('my-component-17', {
                template: '#my-template-12',
                data: function () {
                    return {
                        message:"访问到子组件的数据"
                    }
                }
            });
            
            new Vue({
                el: '#vue-example-16',
            });
        </script>
        
        </html>
        
        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

        简写作用域插槽

        像上面的例子中如果只有一个默认插槽则我们可以吧 v-slot 直接写到组件标签上且无需 <template> 标签

        <my-component-17>
            <template v-slot:default="prop"><p>{{prop.message}}</p></template>
        </my-component-17>
        
        1
        2
        3

        可以简写为

        <my-component-17 v-slot="prop">
            <p>{{prop.message}}</p>
        </my-component-17>
        
        1
        2
        3

        但是如果有多个具名插槽则不可以简写,必须使用 template 标签并用带参 v-slot

        欢迎来到 KnightSama‘s Blog
        看板娘