最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 【Vue学习(二)组件和插槽】

    正文概述 掘金(Moluuu)   2020-12-05   429

    组件

    组件是 Vue 强大的功能之一

    Vue组件具有封装可复用的特点,能够让你在复杂的应用中拆分成独立模块使用

    在开发中,我们可以将重复使用的功能封装为组件,方便开发提高效率

    因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项

    例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

    为什么需要组件

    1. 提高复用性
    2. 解耦
    3. 提升开发效率

    创建组件

    Vue.js 使用component();函数来创建组件,该函数中可以传入两个参数 分别为组件名、以对象的形式定义(描述)组件。

    我们来看一下如何定义一个简单的组件

    <body>
    <div id="app">
        <!--
        定义一个 sc标签,该标签没有任何功能
        甚至可以说该标签在 html中不合法,
        我们可以 以定义组件的方式使其拥有功能且合法
        -->
        <sc></sc>
    </div>
        
        <!--引入Vue.js-->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        //定义组件: 第一个参数为组件名,第二个参数为定义组件
        Vue.component("sc",{
            data: function () {
                return {
                    x: 0
                }
            },
            //template属性的值就是组件的模板,这里我们定义了一个点击会 +1的小按钮
            template: '<div><button v-on:click="x++">点我+1s</button><span>--->{{x}}s</span></div>'
        });
        //组件是可复用的 Vue 实例,且带有一个名字,在这个例子中是 <sc>。
        // 我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用
        var vm = new Vue({
            el: "#app"
        });
    </script>
    </body>
    

    这里就不测试了,感兴趣可以自己拷过去玩一下。

    组件的复用

    组件是可复用的,且每个组件中数据是封装在组件内部的,相同组件之间数据互不影响。

    我们可以将上述组件多复制几个来测试一下

    <div id="app">
        <!--将 sc组件复制四份在网页中观察组件中的数据是否独立-->
        <sc></sc>
        <sc></sc>
        <sc></sc>
        <sc></sc>
    </div>
    

    【Vue学习(二)组件和插槽】

    没有问题,x的值是独立的

    当我们定义这个 <sc> 组件时,你可能会发现它的 data 并不是像这样直接提供一个对象:

    data: {
      x: 0
    }
    

    取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

    data: function () {
      return {
        count: 0
      }
    }
    

    如果 Vue 没有这条规则,那我们就没办法做到组件之间的数据相互独立。

    组件的组织

    通常一个应用会以一棵嵌套的组件树的形式来组织:

    【Vue学习(二)组件和插槽】

    例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。

    ​ 为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册局部注册

    以 Vue.component();函数定义的组件为全局注册的组件

    ​ 全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。

    Vue.component('component-a', { /* ... */ })
    Vue.component('component-b', { /* ... */ })
    Vue.component('component-c', { /* ... */ })
    
    new Vue({ el: '#app' })
    
    <div id="app">
      <component-a></component-a>
      <component-b></component-b>
      <component-c></component-c>
    </div>
    

    在所有子组件中也是如此,也就是说这三个组件在各自内部也都可以相互使用

    局部注册

    在这些情况下,你可以通过一个普通的 JavaScript对象来定义组件,并使用 Vue实例中的 components属性来添加一个组件作为该实例中的局部组件

    <body>
    <div id="app">
        <sc></sc>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        // 使用普通 JavaScript对象的形式定义一个组件实例
        var sc = {
            data(){
                return{
                    x: 0
                }
            },
            template:'<div><button v-on:click="x++">点我+1s</button><span>--->{{x}}s</span></div>'
        }
    
        var vm = new Vue({
            el: "#app",
            // 使用 components属性局部注册到该 Vue实例中
            components: {
                // key为 组件名,value为组件实例
                'sc':sc
            }
        });
    </script>
    </body>
    

    注意:局部注册的组件在其子组件中不可用

    例如,如果你希望 ComponentAComponentB 中可用,则你需要这样写:

    var ComponentA = { /* ... */ }
    
    var ComponentB = {
      components: {
        'component-a': ComponentA
      },
      // ...
    }
    

    Prop

    每个Vue组件实例都有独立范围的作用域的,这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。

    但 Vue.js可以通过使用 props参数实现父组件向子组件传递数据这一操作。

    示例:

    为了便于理解,你可以将这个Vue实例看作<lc>的父组件。 如果我们想使父组件的数据,则必须先在子组件中定义props属性,否则无法拿到父组件中的数据。

    <body>
    <div id="app">
        <!--但实际上并拿不到-->
        <lc v-for="item in la"></lc>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('lc',{
            // 组件定义没有任何问题,按理应该可以拿到数组中遍历的每一项
            template: '<li>{{item}}</li>'
        });
        new Vue({
            el: "#app",
            data: {
                la: ["js","java","c#","go"]
            }
        });
    </script>
    </body>
    

    【Vue学习(二)组件和插槽】

    那么父组件如何动态的传递数据给子组件呢?

    还记得v-bind指令的作用吗,其作用是用于动态绑定 html属性或者是组件的 props值,所以应该使用v-bind指令来动态传递数据。

    <body>
    <div id="app">
        <!--添加 v-bind:将遍历出来的每一项绑定到 test中-->
        <lc v-for="item in la" v-bind:test="item"></lc>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('lc',{
            // 使用 props属性拿到上面 v-bind指令中绑定的test
            props:['test'],
            // 在模板中取出props拿到的 test
            template: '<li>{{test}}</li>'
        });
        new Vue({
            el: "#app",
            data: {
                la: ["js","java","c#","go"]
            }
        });
    </script>
    </body>
    

    【Vue学习(二)组件和插槽】

    关于Prop如果存在疑问请移步 Vue官网

    template标签

    在上文中我们使用 component();函数创建组件,该方式属于一种注册语法糖。

    尽管语法糖简化了组件注册,但在 template属性中拼接 HTML元素是比较麻烦的。

    这也导致了 HTML和 JavaScript的高耦合性,庆幸的是,Vue.js提供了两种方式将定义在 JavaScript中的 HTML模板分离出来。

    一种是使用 script标签,但这种方式仍不是最优解 就不提了。

    我们直接快进到 <template></template>

    <body>
    <div id="app">
        <!-- 3、使用该组件-->
    <mc></mc>
    </div>
    <!-- 1、使用 template标签,在标签体中定义组件的内容-->
    <template id="myComponent">
        <div>
            <strong>在这里定义组件模板</strong>
            <p>的话就不需要</p>
            <span>做一些拼接 Html标签的操作了</span>
        </div>
    </template>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        // 2、注册组件
        Vue.component("mc",{
            // 选中 template标签中的 id
            template: "#myComponent"
        });
        new Vue({
            el: "#app"
        });
    </script>
    </body>
    

    【Vue学习(二)组件和插槽】

    如上

    插槽

    为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。

    这个处理称为内容分发,Vue.js实现了一个内容分发 API,使用特殊的 <slot> 元素作为原始内容的插槽。

    插槽简单示例

    如下,定义了一个父组件和一个子组件。

    在父组件中使用了子组件,并尝试为子组件分发文本内容。

    <body>
    <div id="app">
        <!--使用父组件-->
        <parent></parent>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
    // 注册父组件
    Vue.component("parent",{
        // 在父组件的模板中使用子组件
        template: '<div><p>我是父组件</p><child><p>使用子组件,看看能不能在渲染时显示这行字</p></child></div>'
    });
    // 注册子组件
    Vue.component("child",{
        template: '<div><p>我是子组件</p></div>'
    });
    new Vue({
        el: "#app"
    });
    </script>
    </body>
    

    测试:

    【Vue学习(二)组件和插槽】

    结果是父组件分发的文本内容 并没有显示出来。

    怎么让父组件分发的内容显示出来呢?

    很简单,在子组件中为父组件留一个插槽即可 也就是使用<solt></solt>标签

    使用<solt></solt>标签后,如果父组件在使用子组件时 为子组件分发了内容,则显示父组件分发的内容。

    反之未分发内容 则会显示子组件<solt></solt>标签中的内容

    // 注册子组件
    Vue.component("child",{
        // 在子组件模板中留一个插槽
        template: '<div><slot>我是子组件中的 slot</slot></div>'
    });
    

    使用该标签后,再进行测试

    【Vue学习(二)组件和插槽】

    再将父组件中分发的内容注掉

    // 注册父组件
    Vue.component("parent",{
        // 在父组件的模板中使用子组件,注释掉父标签分发的内容
        template: '<div><p>我是父组件</p><child><!--<p>使用子组件,看看能不能在渲染时显示这行字</p>--></child></div>'
    });
    

    【Vue学习(二)组件和插槽】

    插槽也有分类,像上面使用的这种就是默认插槽,它是一个匿名slot,它只能表示一个插槽。

    除了默认插槽还有具名插槽以及作用域插槽。

    具名插槽

    顾名思义,具名插槽 就是存在名字的插槽,也就是拥有 name属性的插槽。

    为插槽定义 name自然是为了能够更准确的将数据插入到指定的插槽中。

    我们来看一下示例:

    下列代码和默认插槽示的例代码没有太大区别,无非是多了个插槽 且都加上了 name属性。

    可以预见的 网页中会渲染出子组件中默认的标题和文本内容。

    <body>
    <div id="app">
    <!-- 3、使用父组件,不进行任何操作-->
    <page></page>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
    Vue.component('page',{
        // 2、在父组件的模板中调用子组件,不进行任何额外操作。
        template: "<div><ct></ct></div>"
    });
    // 1、注册子组件
    Vue.component('ct',{
        // 1.1、在子组件的模板中定义两个插槽,并为这两个个插槽分别 添加 name属性
        template: "<div><h1><slot name='pageTitle'>默认标题</slot></h1><p><slot name='pageText'>默认文本内容</slot></p></div>"
    });
    new Vue({
        el: "#app",
    });
    </script>
    </body>
    

    网页中的显示:

    【Vue学习(二)组件和插槽】

    我们在父组件的模板中做一些操作

    Vue.component('page',{
        // 对模板进行一些改动,在子组件中新增 span标签,为该 span标签添加 slot属性,值为'pageText'
        template: "<div><ct><span slot='pageText'>新文本内容</span></ct></div>"
    });
    

    在模板中 往调用的子组件里 写一个 span标签,并为其添加 slot属性。

    该属性的值为:子组件中定义插槽 name属性的值

    <slot name='pageText'>默认文本内容</slot>
    

    我们再来看看网页会如何渲染

    【Vue学习(二)组件和插槽】

    子组件中 name为 'pageText' 插槽的文本内容,被替换为了 span标签中的文本内容。

    那我们再将该 span标签的 slot属性值改为 'pageTitle',是不是就意味着能够替换掉 name为 'pageTitle' 插槽的文本内容呢?

    Vue.component('page',{
        // 对模板进行一些改动,在子组件中新增 span标签,为该 p标签添加 slot属性,值为'pageTitle'
        template: "<div><ct><span slot='pageTitle'>新文本内容</span></ct></div>"
    });
    

    【Vue学习(二)组件和插槽】

    确实是如此

    也就是说为插槽设置 name后,父组件想要使用哪个插槽 指定该插槽的 name即可。

    当然了,该用法早已废弃。❌(虽然被废弃,但仍可继续使用)

    新用法

    我们再看看2.6.0之后的具名插槽如何使用

    在2.6.0后,v-slot 指令替代了 slot属性。

    需要注意的是,v-slot指令只能使用在 template标签中,这一点和已经废弃的 slot属性有所不同。

    我们在一个 <template> 元素上使用 v-slot 指令,并传入参数的形式提供其名称:

    <body>
    <div id="app">
    <!--3、使用父组件-->
    <page>
    </page>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
    
        // 1、注册子组件
    Vue.component("ct",{
        // 1.1、在模板中使用 slot标签并添加对应的 name属性
        template: `<div> 
                      <slot name="pageTitle">默认标签</slot>
                      <slot>默认插槽的内容</slot>
                      <slot name="pageText">默认内容</slot>
                  /div>`
    });
    // 2、注册父组件
    Vue.component("page",{
        template: `<div>
                        // 2.1、在父组件模板中调用子组件标签
                        <ct>
                            // 2.2、在子组件标签中书写 template标签,并使用 v-slot指令,指令的参数为插槽 name的值
                            <template v-slot:pageTitle>
                                <h1>
                                    <span>新标签</span>
                                </h1></template>
                            // 2.3、该 template标签不使用 v-slot指令
                            <template>
                            <p>我会替换掉默认插槽的内容</p>
                            </template>
                            <template v-slot:pageText>
                            </template>
                        </ct>
                    </div>`
    });
    </script>
    </body>
    

    <template> 元素中的所有内容都将会被传入相应的插槽。

    任何没有被包裹在带有 v-slot<template> 中的内容都会被视为默认插槽的内容。

    测试:

    【Vue学习(二)组件和插槽】

    一个未添加 name属性 的 <slot>标签 ,会默认携带隐藏的 name属性值 “default” 。

    也就是说我们可以在 template标签中使用 v-slot指令,传入 'default' 参数使其包裹的内容改变为默认插槽的内容。

    template: `<div>
                    // 2.1、在父组件模板中调用子组件标签
                    <ct>
                        // 2.2、在子组件标签中使用 template标签,并使用 v-slot指令,指令的参数为插槽 name的值
                        <template v-slot:pageTitle>
                            <h1>
                                <span>新标签</span>
                            </h1></template>
                        // 2.3、该 template标签不使用 v-slot指令
                        <template>
                        <p>我会替换掉默认插槽的内容</p>
                        </template>
        				/* 
        				  2.4、为该template标签的 v-slot指令传入参数 default,
        				  使其覆盖掉第二个 template标签中默认插槽的内容。
        				*/
                        <template v-slot:default>
                        <p>尝试再次替换默认插槽的内容</p>
                        </template>
                    </ct>
                </div>`
    

    测试:

    【Vue学习(二)组件和插槽】

    作用域插槽

    在上述两种插槽中,都是子组件接收父组件传递的数据。

    而作用域插槽则是用来,向父组件中传入子组件的数据

    可能有些人就要问了,我们直接在父组件调用子组件时不分发内容,使用子组件插槽中的内容不就实现了该操作吗?

    匿名插槽和具名插槽中,插槽上不绑定数据,所以父组件提供的模板既要包括样式又要包括数据。

    而作用域插槽是子组件提供数据,父组件只需要提供一套样式即可。

    使用作用域插槽比较简单,无非两步:

    1. 将子组件数据绑定给 slot 上的属性
    2. 父组件模板中通过 slot-scope 拿到 slot 对象,并进行属性访问。

    以下为示例:

    <body>
    <div id="app">
    <!-- 2、为子组件中拿到的数据添加不同的样式-->
    <ct>
        <!--
        2.1、调用子组件标签后书写 template标签,并使用 slot-scope拿到子组件插槽中的数据,
        将该数据命名为 sc(这个可以随意),我们可以通过该名称访问到子组件中的数据
        -->
        <template slot-scope="sc">
            <!--ul li的形式输出-->
            <ul>
                <li v-for="item in sc.test">{{item}}</li>
            </ul>
        </template>
    </ct>
    <ct>
        <template slot-scope="sc">
                <!--以加粗的形式输出,每个值之间使用 "--》"字符串分隔 -->
                <strong>{{sc.test.join('-->' )}}</strong>
        </template>
    </ct>
    <ct>
        <template slot-scope="sc">
            <!--正常输出,每个值之间使用 " / "分隔-->
            <p>{{sc.test.join(' / ')}}</p>
        </template>
    </ct>
    
    
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
    
        // 1、注册子组件
    Vue.component("ct",{
        // 1.1、在模板中 通过:test="name" ,将子组件的 name绑定到 slot的 test属性上
        template: '<div><slot :test="names"></slot></div>',
        data(){
            return{
                names: ["molu","qiu","lin","zhang"]
            }
        }
    });
    new Vue({
        el: "#app"
    });
    </script>
    </body>
    

    代码块比较长,但都是一些很基础的东西,对着注解顺序应该很容易看懂

    测试:

    【Vue学习(二)组件和插槽】

    同样的,作用域插槽在 2.6.0之后的版本也有新的写法,上面这种写法也已被废弃。

    新用法

    新写法没什么讲究也没什么需要注意的,将 slot-scope 修改为 v-slot指令即可,如下:

    <ct>
        <!--将 slot-scope 修改为 v-slot-->
        <template v-slot:default="sc">
            <ul>
                <li v-for="item in sc.test">{{item}}</li>
            </ul>
        </template>
    </ct>
    

    关于插槽更多的可以移步Vue.js官网

    作者本人实际上是写后端的(从代码风格应该也能看出端倪),对这些东西研究尚浅 就不发表什么高论了。

    也查阅了很多,发现作用域插槽好像能做不少文章 不是我一个业余前端能说明白的,感兴趣可以自行搜索其他文章。

    写的比较匆忙也比较乱,应该有不少错误的地方吧。

    醒过来再检查吧......现在的时间是凌晨三点半,写到昏厥


    放松一下眼睛

    【Vue学习(二)组件和插槽】

    原图P站地址

    画师主页



    起源地下载网 » 【Vue学习(二)组件和插槽】

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元