8.31_vue 组件/插槽

Vue

1.component(组件)

  父子组件:父组件只能用自己组件的data、methods等东西,子组件也只能用自己组件定义的data、methods等东西。父子组件想要用彼此的数据,只有两种方法,一种是父子间通信,一种是使用如$children、$refs等来进行父子访问。

  全局组件

    所有实例(如id=app是一个Vue实例)都能使用全局组件

<body>
    <div id="app">
        <!-- 调用组件 -->
        <new-component></new-component>
    </div>

    <!-- 说到底,Vue基于JavaScript -->
    <script>

        // 全局注册组件
        // 命名方式一定要使用短横线,否则在转换为DOM后调用的时候无法识别组件名而报错
        // new-component为自定义的组件名,template中写组件呈现的内容
        Vue.component('new-component', {
            template: `
            <h1>自定义组件</h1>
            `
        })

        var app = new Vue({
            //为此Vue实例提供挂载元素,可以是CSS选择器,也可以是HTML元素。如此实例中挂载到了id为app的div上,从而该html元素可以使用此Vue中的data、methods等
            el: '#app',
            data: {
            },
        })
    </script>
</body>

  局部组件

    局部组件在哪里注册,就只能在哪里使用。

<body>
// 根组件,是do-child2的父组件 <div id="app"> <!-- 调用组件 --> <do-child2></do-child2> </div> <!-- 说到底,Vue基于JavaScript --> <script> // 局部组件定义(子组件) var child1 = { template: ` <h1>局部组件1</h1> ` } // 父组件 var child2 = { // template中一定要有一个根元素,加一个div就行 // 这里调用do-child2执行的是child1,说明子组件中组件注册覆盖了父组件中的组件注册 template: ` <div> <h1>局部组件2</h1> <do-child2></do-child2> </div> `, components: { // 在第二个子组件中注册子组件一,可以使用子组件一 'do-child2': child1 } } var app = new Vue({ //为此Vue实例提供挂载元素,可以是CSS选择器,也可以是HTML元素。如此实例中挂载到了id为app的div上,从而该html元素可以使用此Vue中的data、methods等 el: '#app', data: { }, components: { // 引号中的child为调用该组件的组件名 // 子组件只能在父组件中使用'do-child2': child2 } }) </script> </body>

  组件注册分离写法

<body>
    <div id="app">
        <new></new>
        <do-child></do-child>
    </div>

    <!-- 组件分离写法:html语句写在template标签中,通过id和组件联系 -->
    <template id="new-component">
        <div>
            <h1>123</h1>
        </div>
    </template>

    <!-- 说到底,Vue基于JavaScript -->
    <script>
        // 全局组件注册
        Vue.component('new', {
            template: '#new-component'
        })

        // 局部组件注册
        var child = {
            template: '#new-component'
        }
        var app = new Vue({
            //为此Vue实例提供挂载元素,可以是CSS选择器,也可以是HTML元素。如此实例中挂载到了id为app的div上,从而该html元素可以使用此Vue中的data、methods等
            el: '#app',
            data: {
                text: ''
            },
            components: {
                'do-child': child
            }
        })
    </script>
</body>

  父组件给子组件传值(props)

    props是单向绑定,即父组件中变化时可以传递给子组件,反过来则不行

<body>
    <div id="app">
        <!-- 绑定text数据 -->
        <input type="text" v-model="text">
        <br>
        <!-- 父组件传值 -->
        <child :message="text" :message1="123"></child>
    </div>

    <!-- 说到底,Vue基于JavaScript -->
    <script>

        Vue.component('child', {
            // 使用数组声明props(不常用,多少版本之后可能有bug)
            props: ['message', 'message1'],
            // 接收父组件传值
            template: `
            <span>{{message}}+{{message1}}</span>
            `
        })
        var app = new Vue({
            //为此Vue实例提供挂载元素,可以是CSS选择器,也可以是HTML元素。如此实例中挂载到了id为app的div上,从而该html元素可以使用此Vue中的data、methods等
            el: '#app',
            data: {
                text: ''
            },
            components: {
            }
        })
    </script>
</body>

   props数据验证

    支持的数据类型:String、Number、Boolean、Array、Object、Date、Function、Symbol

<div id="app">
        <!-- 绑定text数据 -->
        <input type="text" v-model="text">
        <br>
        <!-- 父组件传值 -->
        <child :message="text"></child>
    </div>

    <!-- 说到底,Vue基于JavaScript -->
    <script>

        Vue.component('child', {
            // 使用对象声明props(常用)
            props: {
                // 类型限制
                message: [String, Number],
                message1: {
                    type: String,
                    // 当父组件没有传值的时候,显示此默认值,否则将默认值替换
                    default: '默认显示',
                    // 布尔值required,设定父组件此属性必须传值,不传报错
                    // required: true
                },
          
          // 自定义验证函数
                validator: function(value) {
                    // 必须匹配下列字符串中的一个
                    return ['success', 'warning', 'danger'].indexOf(value) !== -1
                }

            },
            // 接收父组件传值
            template: `
            <span>{{message}}+{{message1}}</span>
            `
        })

  子传父

    使用自定义事件

<body>
    <div id="app">
        <!-- 3.在这里监听子组件发射出来的事件,同时触发父组件事件 -->
        <!-- 这里默认会传递参数,所以reflect不用加括号写参数,但是下面methods中的
        reflect函数要加括号加参数来接收传递到的数据 -->
        <c-t-f @item-click="reflect"></c-t-f>
    </div>

    <template id="c-t-f">
        <div>
            <!-- 1.绑定点击事件,将点击的哪个按钮传递给子组件事件并发射出去 -->
            <button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
        </div>
    </template>

    <script>
        const childToFather = {
            template: '#c-t-f',
            data() {
                return {
                    categories: [
                        {id: 'aaa', name: '热门推荐'},
                        {id: 'bbb', name: '手机数码'},
                        {id: 'ccc', name: '家用家电'},
                        {id: 'ddd', name: '电脑办公'}
                    ]
                }
            },
            methods: {
                btnClick(item) {
                    // 2.子组件发射一个事件(事件名随便起),后面跟需要携带的参数
                    this.$emit('item-click', item)
                }
            }
        }

        var app = new Vue({
            el: '#app',
            data: {
                text: ''
            },
            components: {
                "c-t-f": childToFather
            },
            methods: {
                reflect(item) {
                    console.log("父组件已接收到"+item.name)
                }
            }
        })
    </script>
</body>

  父子组件的访问方式

    有时候需要父组件直接访问子组件或是子组件直接访问父组件

    父组件访问子组件:$children或$refs(this.$children是一个数组类型,包含所有子组件对象)

<body>
    <div id="app">
        <f-t-c></f-t-c>
        <f-t-c ref="aaa"></f-t-c>
        <f-t-c></f-t-c>
        <button @click="btnClick">点我访问子组件</button>
    </div>

    <template id="ftc">
        <div>
            <span>我是子组件</span>
        </div>
    </template>

    <script>
        const fatherToChild = {
            template: '#ftc',
            methods: {
                showMessage() {
                    console.log("我被父组件访问到了!")
                }
            },
            data() {
                return {
                    name: '我是子组件!'
                }
            }
        }
        var app = new Vue({
            el: '#app',
            data: {
            },
            components: {
                'f-t-c': fatherToChild
            },
            methods: {
                btnClick() {
                    // 可以看一下this.$children具体是什么
                    console.log(this.$children)
                    // 通过this.$children[0]访问到子组件中的内容。其中因为$children是数组,
                    // [0]代表这是父组件中的第一个子组件
                    this.$children[0].showMessage()
                    console.log(this.$children[0].name)

                    // 如果使用下标值来选择子组件,当父组件中添加其他子组件的时候需要来回修改,很不方便
                    // 所以一般使用$refs
                    console.log(this.$refs.aaa)
                    this.$refs.aaa.showMessage()
                    console.log(this.$refs.aaa.name)
                }
            }
        })
    </script>
</body>

 

 

    子组件访问父组件:$parent(访问根组件$root)

      但是如果这样做的话会降低子组件的复用性,因为有的父组件不一定有子组件需要获取的数据。因此$parent在实际开发中用的非常少;$root用的也非常少,因为根组件的Vue实例几乎不放任何实质性的东西

 

<body>
    <div id="app">
        <c-t-f></c-t-f>
    </div>

    <template id="ctf">
        <div>
            <span>我是子组件</span>
            <button @click="btnClick">点击访问父组件</button>
        </div>
    </template>

    <script>
        const childToFather = {
            template: '#ctf',
            // 这次因为要访问父组件,所以在子组件中做监听
            methods: {
                btnClick() {
                    // 看一下$parent
                    console.log(this.$parent)
                    console.log(this.$parent.name)

                    // 访问根组件$root
                    console.log(this.$root)
                    console.log(this.$root.name)
                }
            },
            data() {
                return {
                }
            }
        }
        var app = new Vue({
            el: '#app',
            data: {
                name: '父组件被访问到!'
            },
            components: {
                'c-t-f': childToFather
            },
            methods: {
            }
        })
    </script>
</body>

 

 

 

 

 

 

 

 

 

 

2.slot(插槽)

  组件插槽是为了让封装的组件更具拓展性,可以让使用者决定组件呈现的内容。最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽

  插槽基本使用:

<div id="app">
        <!-- demo元素内部的所有元素(无论几个)都会放到组件定义中的插槽的位置 -->
        <demo>
      <
button>123</button>
      <h1>456</h1>
      <h2>789</h2>
     </demo>
    </div>
    <!-- 组件定义 -->
    <template id="demo">
        <div>
            <h2>我是组件</h2>
            <slot></slot>
        </div>
    </template>

  默认插槽:

<div id="app">
        <!-- 如果组件调用没有传递新的内容给组件,那么就会调用默认值插槽
        否则会将使用者自定义的内容覆盖到插槽上 -->
        <demo></demo>
        <demo>哈哈哈</demo>
    </div>
    <!-- 组件定义 -->
    <template id="demo">
        <div>
            <h2>我是组件</h2>
            <!-- 相当于在插槽中定义了默认值 -->
            <slot><button>123</button></slot>
        </div>
    </template>

  具名插槽:

// 父组件
<
div id="app"> <!-- 通过slot指定要将该元素放到哪个插槽中 --> <demo><span slot="center">具名插槽</span></demo> </div> <!-- 子组件定义 --> <template id="demo"> <div> <h2>我是组件</h2> <!-- 定义三个具名插槽 --> <slot name="left">左边</slot> <slot name="center">中间</slot> <slot name="right">右边</slot> </div> </template>

  作用域插槽

    是为了解决这样一种情景:父组件对子组件的数据展示不满意,希望拿到子组件的数据自己来展示。即父组件替换插槽的标签,但是内容由子组件来提供

<div id="app">
        <!-- 因为父组件对应的变量作用域只有实例变量,无法去到子组件的变量作用域获取变量
        所以父组件想要获取子组件的数据重新展示,只能使用作用域插槽 -->
        <demo>
            <!-- 2.5.x版本之后不强制使用template,可以使用如div等元素,为了兼容性,此处使用template -->
            <!-- 2.使用slot-scope来拿到子组件的slot引用,引号里的名字随便起。其中slot="first"是具名插槽,用来指定此template中的内容替换哪一个slot -->
            <template slot-scope="slotttt" slot="first">
                <!-- 3.然后就可以拿到slot中定义的data(最主要的是组件中设置的数据绑定,根据绑定的数据来引用,slot-scope可以理解成拿到组件的slot引用)指向的pLanguages来使用了 -->
                <!-- 成功从父组件访问到子组件的数据,并进行修改后放入到对应具名插槽中 -->
                <span v-for="item in slotttt.data">{{item}}-</span>
            </template>

            <!-- 作用域插槽加具名插槽可以实现父组件对多组子组件数据的使用。这里的num只是一个名字,可以随便起,目的是拿到组件的slot的引用,其中data1才是真正的子组件中的num变量的引用 -->
            <template slot-scope="num" slot="second">
                <span v-for="item in num.data1">{{item}}-</span>
            </template>
        </demo>
    </div>

    <!-- 组件定义 -->
    <template id="demo">
        <div>
            <!-- 1.首先slot要引用到需要使用的数据,:data中的data是随便起的名字,之后父组件调用会用到 -->
            <!-- 这个插槽其实就是一个拿数据的作用 -->
            <slot :data="pLanguages" name="first">
                <!-- 默认值就会被替换掉 -->
                <ul>
                    <li v-for="item in pLanguages">{{item}}</li>
                </ul>
            </slot>
            <slot :data1="num" name="second">
                <ul>
                    <li v-for="item in pLanguages">{{item}}</li>
                </ul>
            </slot>
        </div>
    </template>

    <!-- 说到底,Vue基于JavaScript -->
    <script>
        var app = new Vue({
            el: '#app',      //为此Vue实例提供挂载元素,可以是CSS选择器,也可以是HTML元素。如此实例中挂载到了id为app的div上,从而该html元素可以使用此Vue中的data、methods等
            // 父组件变量作用域
            data: {
            },
            components: {
                demo: {
                    template: '#demo',
                    // 子组件变量作用域
                    data() {
                        return {
                            pLanguages: ['JavaScript', 'C++', 'Java'],
                            num: ['123', '456', '789']
                        }
                    }
                }
            }
        })
    </script>

 

posted @ 2020-08-31 20:53  ajjoker  阅读(96)  评论(0)    收藏  举报