1. 组件概述

  • 在这一小节中,重点要理解的就是组件的编程思想。

  • 组件表示页面中的部分功能(包含自己的逻辑与样式),可以组合多个组件实现完整的页面功能。

  • 如下图所示:

  • 问题是,如何确定页面中哪些内容划分到一个组件中呢?

你可以将组件当作一种函数或者是对象来考虑(函数的功能是单一的),根据[单一功能原则]来判定组件的范围。也就是说,一个组件原则上只能负责一个功能。如果它需要负责更多的功能,这时候就应该考虑将它拆分成更小的组件。

定义:组件是可复用的Vue实例,准确讲它是VueComponent的实例,继承自Vue

分类:有状态组件(有data属性),functional

优点:组件化可以增加代码的复用性,可维护性和可测试性。

使用场景:什么时候使用组件?以下分类可以作为参数

第一:通用组件:实现最基本的功能,具有通用性,复用性。例如按钮组件,输入框组件,布局组件等。(Element UI组件库就是属于这种通用的组件)

第二:业务组件,用于完成具体的业务,具有一定的复用性。例如登录组件,轮播图组件。

第三:页面组件,组织应用各部分独立内容,需要时在不同页面组件间切换,例如:商品列表页,详情页组件。

组件有什么特点呢

可复用、维护、可组合

可复用:每个组件都是具有独立功能的,它可以被使用在多个场景中。

可组合:一个组件可以和其它的组件一起使用或者可以直接嵌套在另一个组件内部。

可维护:每个组件仅仅包含自身的逻辑,更容易被理解和维护。

组件化和模块化的不同:

  • 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一;

  • 组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用;

1.1 组件

  • 组件 (Component) 是 Vue.js 最强大的功能之一

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

1.2 全局注册

1.使用 Vue.extend 配合 Vue.component 方法:

<div id='app'>
        <my-login></my-login>
        <!-- 3.将组件名称以html标签的形式引入到页面中,
注意,如果定义组件是驼峰,实用的时候,要用-的形式 -->
    </div>
    <script>
        var login = Vue.extend({
            template: "<h1>我是一个组件</h1>"
            //1.通过template属性指定了我们要显示的Html结构
        })
        //2.使用Vue.component("组件名称","创建出来的组件配置对象这里就是login")
        Vue.component('myLogin', login)
        
  该方法可以进行升级,不需要中间变量login接收,直接将Vue.extend放到Vue.component("组件名称",       
  Vue.extend({})) 
  Vue.component("login",Vue.extend({
            template: "<h1>我是一个组件</h1>"
        }))
        引用时直接使用<login></login>

2.直接使用 Vue.component 方法:

<login></login>
</div>
<script>
    //1.这种创建组件的方式,没有代码的智能提示
    //2.无论以哪种方法创建的组件,有且只能有一个根元素
    Vue.component("login",{
        template:'<div><h1>我是通过字面量的方式直接创建的组件</h1><h1>123</h1></div>'
    })
  • 将template元素定义到div#app之外。

  • <div id='app'>
    <login></login>
    </div>
    <template id="temp"> //定义在div#app之外
        <div>
            <h1>哈哈,我带代码的智能提示</h1>
            <h2>但是我也只能有一个根元素</h2>
        </div>
    </template>
    <script>
        Vue.component('login',{
            template:"#temp"
        })

    3.使用模板字符串的方式注册组件

  • <div id='app'>
    <login></login>
    </div>
    
    <script>
        Vue.component("login",{
            template:`
                <div>
                    <h1>我使用了模板字符串</h1> 
                    <h1>但是我没有智能提示</h1>
                </div>  
                    `
        })

    1.3 定义一个私有组件

    只能在当前注册它的vue实例中使用

  • <body>
    <div id='app'>
        <login></login>
        <register></register>
    </div>
    <template id="temp">
        <div>
            <h1>我是私有组件register</h1>
        </div>
    </template>
    <script>
     var vm=new Vue({
      el:'#app',
       data:{
    
     },
       methods:{
    
       },
       components: {
          login:{
            template:"<h1>我是一个私有组件login</h1>"
          },
          register:{
              template:"#temp"
          }
       }
       })
    </script>
    </body>

    1.4 组件中的data和method

  • <body>
    <div id='app'>
    <login></login>
    </div>
    <script>
        //1.组件身上可以自有己的data
        //2.组件中的data必须是一个function
        //3.data的function中必须返回一个对象
        //4.组件中data的使用方法同示例的使用方法
    Vue.component('login',{
        template:"<h1>我是组件template中的内容-------------{{msg}}</h1>",
        data:function(){
            return {
                msg:"我是组件中的data数据"
            }
        }
        
    })
     var vm=new Vue({
      el:'#app',
       data:{
    
     },
       methods:{
    
       }
       })
    </script>
    </body>

    组件基础 — Vue.js

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

    如果 Vue 没有这条规则,点击一个按钮就可能会像如下代码一样影响到其它所有实例

  • <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>Document</title>
        <script src="js/vue.js"></script>
      </head>
    
      <body>
        <div id="app">
          <counter></counter>
          <hr />
          <counter></counter>
          <hr />
          <counter></counter>
        </div>
    
        <template id="tmpl">
          <div>
            <input type="button" value="+1" @click="increment" />
            <h3>{{count}}</h3>
          </div>
        </template>
        <script>
          var dataObj = { count: 0 };
          // 这是一个计数器的组件, 身上有个按钮,每当点击按钮,让 data 中的 count 值 +1
          Vue.component('counter', {
            template: '#tmpl',
            data: function () {
              return dataObj;
              // return { count: 0 }
            },
            methods: {
              increment() {
                this.count++;
              }
            }
          });
    
          // 创建 Vue 实例,得到 ViewModel
          var vm = new Vue({
            el: '#app',
            data: {},
            methods: {}
          });
        </script>
      </body>
    </html>
  • 使用组件模板对象快速创建私有组件

  • <body>
    <div id='app'>
    <login></login>
    <register></register>
    </div>
    <script>
    var login = {
        template:"<h1>登录</h1>"
    }
    var register ={
        template:"<h1>注册</h1>"
    }
    Vue.component('login',login)
     var vm=new Vue({
      el:'#app',
       data:{
    
     },
       methods:{
    
       },
       components: {
           register
       }
       })
    </script>
    </body>

    1.5 组件注意事项

  • 组件参数的data值必须是函数同时这个函数要求返回一个对象

  • 组件模板必须是单个根元素

  • 组件模板的内容可以是模板字符串

  • 如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,但是在普通的标签模板中,必须使用短横线的方式使用组件。

1. 6 Vue 组件调试工具

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
    <style type="text/css">
      .root {
        height: 400px;
        background-color: orange;
      }
      .second {
        height: 160px;
        background-color: lightgreen;
      }
      .third {
        background-color: tomato;
      }
    </style>
  </head>
  <body>
    <div id="app" class="root">
      <div>{{root}}</div>
      <second-com></second-com>
      <second-com></second-com>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>
    <script type="text/javascript">
      /*
      Vue调试工具安装与基本使用
    */
      Vue.component('second-com', {
        data: function () {
          return {
            second: '二级组件'
          };
        },
        template: `<div class='second'>
        <div>{{second}}</div>
        <third-com></third-com>
        <third-com></third-com>
        <third-com></third-com>
      </div>`
      });
      Vue.component('third-com', {
        data: function () {
          return {
            third: '三级组件'
          };
        },
        template: '<div class="third"><div>{{third}}</div></div>'
      });

      var vm = new Vue({
        el: '#app',
        data: {
          root: '顶层组件'
        }
      });
    </script>
  </body>
</html>

1.7 切换组件

1.7.1.使用flag标识符结合v-ifv-else切换组件
<body>
<div id='app'>
    <a href="" @click.prevent="flag=true">登录</a>
    <a href="" @click.prevent="flag=false">注册</a>
    <login v-if="flag"></login>
    <register v-else="flag"></register>
</div>
<script>
    Vue.component('login',{
        template:"<h3>登录组件</h3>"
    })
    Vue.component('register',{
        template:"<h3>注册组件</h3>"
    })
 var vm=new Vue({
  el:'#app',
   data:{
    flag:true
 },
   methods:{

   }
   })
</script>
</body>
1.7.2.使用官方提供的component占位符
<body>
<div id='app'>
    <!-- 1.Vue提供了component,来展示对应的组件
    2.component是一个占位符,:is属性,可以用来指定展示的组件名称 -->
    <a href="" @click.prevent="comName='login'">登录</a>
    <a href="" @click.prevent="comName='register'">注册</a>
    <component :is="comName"></component>
 
</div>
<script>
    Vue.component('login',{
        template:"<h3>登录组件</h3>"
    })
    Vue.component('register',{
        template:"<h3>注册组件</h3>"
    })
 var vm=new Vue({
  el:'#app',
   data:{
    comName:'login'
 },
   methods:{

   }
   })
</script>
</body>

2.0 Vue组件之间传值(组件通信)

2.1. 概述

  • 当我们将整个页面都拆分了不同的组件以后,这样就会涉及到组件之间的数据传递问题。

  • 常见的组件的通信可以分为三类:

  • 第一类: 父组件向子组件传递数据

  • 第二类: 子组件向父组件传递数据

  • 第三类:兄弟组件的数据传递。

2.2 父组件向子组件传值

  • 第一: 父组件通过属性将值传递给子组件

  • 第二:子组件内部通过props接收传递过来的值。

  • 1.使用v-bind或简化指令,将数据传递到子组件中:

  • <div id="app">
        <son :finfo="msg"></son>
      </div>

    2.子组件内部一定要使用props属性来定义父组件传递过来的数据

  • <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            msg: '这是父组件中的消息'
          },
          components: {
            son: {
              template: '<h1>这是子组件 --- {{finfo}}</h1>',
              props: ['finfo'],
            }
          }
        });
      </script>
    <!DOCTYPE html>
    <html lang='en'>
    <head>
    <meta charset='UTF-8'>
    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
    <title>Document</title>
    <style>
    /**{margin:0;padding:0}
    a {text-decoration:none}
    ul,li {list-style:none}*/
    </style>
    <script src='./js/vue.js'></script>
    </head>
    <body>
    <div id='app'>
        <!-- 1.父组件可以在引用子组件的时候,通过属性绑定的形式把需要传递给子组件的数据以属性绑定的形式传递到子组件内部
        注意这里不要使用驼峰自定义属性 如果使用parent-msg形式 ,props需要使用驼峰形式接收。
        -->
    <login :parentmsg="msg"></login>
    </div>
    <script>
     var vm=new Vue({
      el:'#app',
       data:{
        msg:"父组件中的数据"
     },
       methods:{
    
       },
       components: {
           'login':{
            //    默认情况,子组件无法访问父组件中data的数据
            //    template:'<h1>我是子组件-----{{msg}}</h1>',
                  template:'<h1 @click="handle">我是子组件-----{{parentmsg}}</h1>',
               //2.父组件传递过来的msg以字符串的形式在props[]中定义一下,这是一个数组
               props: ['parentmsg'], // 把父组件传递过来的 parentmsg 属性,先在 props 数组中,定义一下,这样,					才能使用这个数据,实际开发中我们在进行组件封装时经常会使用以下方式。
              // props : {
              //     parentmsg : {
              //         type:String,
              //         default:""
              //     } 
              // },
             
               data:function(){
                   return {
                       msg:"132"
                   }
                 // 注意: 子组件中的 data 数据,并不是通过 父组件传递过来的,而是子组件自身私有的,比如: 子组件通过 Ajax ,请求回来的数据,都可以放到 data 身上;
                // data 上的数据,都是可读可写的;
               // props 中的数据,都是只读的,无法重新赋值
               },
               methods: {
                   handle(){
                       this.parentmsg="123"
                   }
               }
               
           }
       }
       })
    </script>
    </body>
    </html>

    2.3 子组件向父组件传值

  • - 1.原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;
    - 2.演示:父组件将方法的引用传递给子组件,其中,`getMsg`是父组件中`methods`中定义的方法名称,`func`是子组件调用传递过来方法时候的方法名称
    <son @func="show"></son>
  • 3.子组件内部通过`this.$emit('方法名', 要传递的数据)`方式,来调用父组件中的方法,同时把数据传递给父组件使用
  • <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="js/vue.js"></script>
    </head>
    
    <body>
      <div id="app">
        <!-- 父组件向子组件 传递 方法,使用的是 事件绑定机制; v-on, 当我们自定义了 一个 事件属性之后,那么,子组件就能够,通过某些方式,来调用 传递进去的 这个 方法了 -->
        <com2 @func="show"></com2>
      </div>
    
      <template id="tmpl">
        <div>
          <h1>这是 子组件</h1>
          <input type="button" value="这是子组件中的按钮 - 点击它,触发 父组件传递过来的 func 方法" @click="myclick">
        </div>
      </template>
    
      <script>
    
        // 定义了一个字面量类型的 组件模板对象
        var com2 = {
          template: '#tmpl', // 通过指定了一个 Id, 表示 说,要去加载 这个指定Id的 template 元素中的内容,当作 组件的HTML结构
          data() {
            return {
              sonmsg: { name: '小头儿子', age: 6 }
            }
          },
          methods: {
            myclick() {
              // 当点击子组件的按钮的时候,如何 拿到 父组件传递过来的 func 方法,并调用这个方法???
              //  emit 英文原意: 是触发,调用、发射的意思
              // this.$emit('func123', 123, 456)
              this.$emit('func', this.sonmsg)
            }
          }
        }
    
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            datamsgFormSon: null
          },
          methods: {
            show(data) {
              // console.log('调用了父组件身上的 show 方法: --- ' + data)
              // console.log(data);
              this.datamsgFormSon = data
            }
          },
    
          components: {
            com2
            // com2: com2
          }
        });
      </script>
    </body>
    
    </html>

    2.4 兄弟之间的传递 (创建事件总线)

  • 兄弟之间传递数据需要借助于事件中心,通过事件中心传递数据

  • 提供事件中心 var hub = new Vue()

  • 传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)

  • 接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名

  • 销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据

  • <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style>
          /**{margin:0;padding:0}
    a {text-decoration:none}
    ul,li {list-style:none}*/
        </style>
        <script src="./js/vue.js"></script>
      </head>
      <body>
        <div id="app">
          <button @click="dest">销毁</button>
          <login></login>
          <hr />
          <register></register>
        </div>
        <script>
          // 1.分别定义2个子组件login 和register
          // 2.定义一个事件中心对象
          // 3.使用钩子函数给每一个子组件注册监听事件,并对应事件处理函数
          // mounted () {
          //      hub.$on('loginEvent',(val)=>{
          //         this.msg+=val
          //      })
          //  }
          // 4.在每个子组件内部的method中的方法触发时,通过hub.$emti('')
          // 分别触发对方组件的监听事件
    
          // 5.解除绑定事件(销毁事件)方法,在父组件上定义一个method方法,
          // 通过hub.$off()方式分别解除2个监听事件
    
          var hub = new Vue();
    
          var login = {
            template: `
                <div>
                    <h1>登录组件</h1>
                    <input type="button" value="点我" @click="add">
                    <p>{{msg}}</p>
                </div>
               `,
            data() {
              return {
                msg: 0
              };
            },
            methods: {
              add() {
                hub.$emit('registerEvent', 10);
              }
            },
            mounted() {
              hub.$on('loginEvent', (val) => {
                this.msg += val;
              });
            }
          };
          var register = {
            template: `
                <div>
                    <h1>注册组件</h1>
                    <input type="button" value="点我" @click="add">
                    <p>{{msg}}</p>
                </div>
               `,
            data() {
              return {
                msg: 0
              };
            },
            methods: {
              add() {
                hub.$emit('loginEvent', 20);
              }
            },
            mounted() {
              hub.$on('registerEvent', (val) => {
                this.msg += val;
              });
            }
          };
          var vm = new Vue({
            el: '#app',
            data: {},
            methods: {
              dest() {
                hub.$off('registerEvent');
                hub.$off('loginEvent');
              }
            },
            components: {
              login,
              register
            }
          });
        </script>
      </body>
    </html>

    2.5 使用 this.$refs 来获取元素和组件

  • <!DOCTYPE html>
    <html lang='en'>
    <head>
    <meta charset='UTF-8'>
    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
    <title>Document</title>
    <style>
    /**{margin:0;padding:0}
    a {text-decoration:none}
    ul,li {list-style:none}*/
    </style>
    <script src='./js/vue.js'></script>
    </head>
    <body>
    <div id='app'>
        <input type="button" value="获取" @click="print">
        <h1 ref="myh1">我是一个大大的h1标题</h1>
        <login ref="mylogin"></login>
    </div>
    <script>
        var login = {
            template:"<div>{{msg}}</div>",
            data:function () {
                return {
                    msg:"我是一个组件"
                }
            },
            methods: {
                myclick () {
                    console.log("ok")
                }
            }
        }
        Vue.component('login',login)
     var vm=new Vue({
      el:'#app',
       data:{
            
     },
       methods:{
        print () {
           // console.log(document.getElementsByTagName("h1")[0].innerText);
              console.log(this.$refs.myh1.innerText); //访问dom
              console.log(this.$refs.mylogin.msg);  //访问组件msg
             // this.$refs.mylogin.msg = "123"    //修改组件msg
              this.$refs.mylogin.myclick()     //调用组件方法
        }
       }
       })
    </script>
    </body>
    </html>

    2.6 使用v-model完成组件通信

    自定义事件也可以用于创建支持 v-model 的自定义输入组件。

  • <input v-model="username">  
    等价于
    		<div id="app">
          <input :value="username" @input="username = $event.target.value" />
          <h3>{{ username }}</h3>
        </div>
        <script>
          const vm = new Vue({
            el: '#app',
            data: {
              username: ''
            },
            methods: {}
          });
      </script>

    当用在组件上时,v-model 则会这样:

  • <custom-input :value="username" @input="username = $event"></custom-input>
  • 上面用 $event 接收子组件用 $emit() 向上传递过来的数据

  • 为了让它正常工作,这个组件内的 <input> 必须:

  • 将其 value attribute 绑定到一个名叫 value 的 prop 上

  • 在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出

  • 写成代码之后是这样:

  • <h3>{{ username }}</h3>
     <custom-input v-model="username"></custom-input>
     ...
     Vue.component('custom-input', {
            props: ['value'],
            template: `
       <input
          :value="value" 
          @input="$emit('input', $event.target.value)"
        >
      `
     });

    3. 组件插槽

    3.1 概述

  • 生活中的插槽

  • 其实我们生活中有很多很多的插槽。比如电脑的USB插槽、插板中的电源插槽等等。每个插槽都有它们之间的价值。比如电脑的USB插槽,可以用来插U盘,链接鼠标,链接手机、音响等等,通过这些插槽,大大拓展了原有设备的功能。

  • 组件中的插槽

  •   组件中的插槽,让使用者可以决定组件内部的一些内容到底展示什么,也就是,插槽可以实现父组件向子组件传递模板内容。具有插槽的组件将会有更加强大的拓展性,默认自定义标签内不允许添加内容,而插槽可以让我们在内部添加内容。

  • 下面看一个实际应用的例子来体会一下插槽的引用场景。

  • 三个页面中都有导航栏,基本结构都是一样的:左中右分别有一个东西,只是显示的内容不同而已。那我们如何来实现这种结构相似但是内容不同呢?  你一定是想着,直接定义三个组件,然后在模板中分别显示不同的内容,对不对?恭喜你,你就快要被炒了。  首先,如果我们封装成三个组件,显然不合适,比如每个页面都有返回,这部分的内容我们就要重复去封装  其次,如果我们封装成一个,还是不合理,因为有些左侧是菜单栏,有些中间是搜索框,有些是文字。 那我们该怎么办呢?其实很简单,用组件插槽。

    上面最佳的解决办法是将共性抽取到组件中,将不同暴露给插槽,一旦我们使用了插槽,就相当于预留了空间空间的内容取决于使用者

  • 组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力

3.2 匿名插槽

使用方法:

  1. 直接在template中插入slot空标签,即可在自定义标签中插入内容,内容会自动替换空slot

  2. 组件标签中插入的无论插入多少值,都会一起作为替换元素,替换slot

  3. 如果在slot中添加默认值,组件标签中没有插入内容则显示默认值

  4. <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style>
          .moban {
            width: 200px;
            height: 60px;
            background-color: pink;
            font-size: 20px;
            line-height: 60px;
            text-align: center;
            color: Red;
            margin: 10px;
          }
        </style>
        <script src="./js/vue.js"></script>
      </head>
      <body>
        <div id="app">
          <error>找不到对应组件</error>
          <error>出现未知错误</error>
          <error></error>
        </div>
        <template id="temp">
          <div class="moban">
            <slot><h1>默认显示</h1></slot>
          </div>
        </template>
        <script>
          var error = {
            template: '#temp'
          };
          var vm = new Vue({
            el: '#app',
            data: {},
            methods: {},
            components: {
              error
            }
          });
        </script>
      </body>
    </html>

3.3 具名插槽

  • 当有多个插槽时,我们只想替换其中一个,就需要用到具名插槽了

  • 具有名字的插槽

  • 使用 <slot> 中的 "name" 属性绑定元素,引用时要通过 slot ="name"的形式传递

  • 具名插槽的渲染顺序,完全取决于模板,而不是取决于父组件中元素的顺序

  • <!DOCTYPE html>
    <html lang='zh-CN'>
    <head>
    <meta charset='UTF-8'>
    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
    <title>Document</title>
    <style>
    </style>
    <script src='./js/vue.js'></script>
    </head>
    <body>
    <div id='app'>
        <poetry>
                <h1 slot="header"> 标题:</h1>
                <p>"望庐山瀑布",</p>
                <p>"日照香炉紫烟",</p>
                <p>"遥看瀑布挂前川",</p>
                <p>"飞流直下三千尺",</p>
                <p>"疑是银河落九天",</p>
                <h3 slot="author">"李白"</h3>
        </poetry>
    </div>
    <script>
     const poetry = {
         template:`
         <div>
                <slot name="header"></slot>
                <slot></slot>
                <slot name="author"></slot>    
         </div>
         `
    //    1、使用 <slot> 中的 "name" 属性绑定元素 指定当前插槽的名字 
    //    2、组件引用时要通过 slot ="name"的形式传递
    //    3、具名插槽的渲染顺序,完全取决于模板,而不是取决于父组件中元素的顺序 
       
     }
     const vm = new Vue({
      el:'#app',
       data:{
        
     },
       methods:{
       
       },
       components: {
        poetry
       }
       })
    </script>
    </body>
    </html>

    3.5 插槽总结:

    组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力。

    组件的复用性常见情形如在有相似功能的模块中,他们具有类似的UI界面,通过使用组件间的通信机制传递数据,从而达到一套代码渲染不同数据的效果

    什么情况下使用插槽

    顾名思义,插槽即往卡槽中插入一段功能块,如果有一百个基本相似,只有一个模块功能不同的页面,而我们只想写一个组件。可以将不同的那个模块单独处理成一个卡片,在需要使用的时候将对应的卡片插入到组件中即可实现对应的完整的功能页。而不是在组件中把所有的情形用if-else罗列出来。

      可能你会想,那我把一个组件分割成一片片的插槽,需要什么拼接什么,岂不是只要一个组件就能完成所有的功能?思路上没错,但是需要明白的是,卡片是在父组件上代替子组件实现的功能,使用插槽无疑是在给父组件页面增加规模,如果全都使用拼装的方式,和不用组件又有什么区别。因此,插槽并不是用的越多越好

    插槽是组件最大化利用的一种手段,而不是替代组件的策略,当然也不能替代组件。如果能在组件中实现的模块,或者只需要使用一次v-else, 或一次v-else-ifv-else就能解决的问题,都建议直接在组件中实现。

    3.6 函数式组件

    组件没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法时,可以将组件标记为functional.这意味它无状态(没有响应式数据),也没有实例(没有this上下文)

    因为只是函数,所以渲染的开销相对来说,较小。

    函数化的组件中的 Render 函数,提供了第二个参数 context 作为上下文,data、props、slots、children 以及 parent 都可以通过 context 来访问。

  • <script>
    export default {
      functional: true, // 函数式组件
      props: ["level", "title"],
      // heading组件
      // <heading :level="1" :title="title" icon="cart">{{title}}</heading>
      // <h2 title=""></h2>
      render(h, context) {
        // 子节点数组
        let children = [];
        // 没有this,同样也没有$slot.default
        // 默认插槽,默认子元素的写法有所更改
        children = children.concat(context.children);
    
        const { title, level } = context.props;
        return h(
          "h" + level, // 参数1,tagname
          { attrs: { title } }, // 参数2:{。。。}
          children // 虚拟子节点VNode不能再使用$slot.default
        );
      },
    };
    </script>
    
    <style lang="scss" scoped>
    </style>

    这块内容简单了解一下就可以。

    渲染函数 & JSX — Vue.js

    3.7 混入

    混入(mixin)提供了一种非常灵活的方式,来分发Vue组件中的可复用功能,一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项被“混合”进入该组件本身的选项。

  • <!DOCTYPE html>
    <html lang='zh-CN'>
    <head>
    <meta charset='UTF-8'>
    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
    <title>Document</title>
    <style>
    </style>
    <script src='./js/vue.js'></script>
    </head>
    <body>
    <div id='app1'>
          <button @click="handle">触发VM1</button>
    </div>
    <br>
    <div id='app2'>
          <button @click="handle">触发VM2</button>
    </div>
    <script>
    // 定义一个混入对象
    var myMixin = {
        data () {
            return {
              conmonData : "公共数据"
            }
        },
        methods:{
          // 公共方法
          getConmonData (msg) {
                console.log(this.conmonData +"========"+ msg)
            }
        }
    }
    
      
     const vm1 = new Vue({
      mixins:[myMixin],
      el:'#app1',
       data:{
        
     },
       methods:{
        handle () {
         this.getConmonData ("vm1")
        }
       } 
       })
        
     const vm2 = new Vue({
      mixins:[myMixin],
      el:'#app2',
       data:{
        
     },
       methods:{
        handle () {
          this.getConmonData ("vm2")
        } 
       }
       })
    </script>
    </body>
    </html>

    “混入”可以提高组件的复用功能,例如:上面所写的getConmonData这个方法,不仅在一个组件中使用,还会

    在其它组件中使用.那么,我们的处理方式就是,可以将getConmonData 这个方法单独定义在一个地方,如果某个组件想要使用,可以直接将该方法注入到组件中。

Logo

前往低代码交流专区

更多推荐