vue-组件的总结

组件

全局组件

<body>
   <div id="app">
        <component-test></component-test>
   </div>

   <script type="text/template" id="template-test">
       <div>
           全局组件  {{count}}
       </div>
   </script>
   
   <script>
          Vue.component('component-test',{
                  template:"#template-test",
               data(){
                   return {
                       count:0
                   }
               },
          
       })

       var vm = new Vue({
           el: "#app",
           
       })
   </script>
</body>

局部组件

<body>
 <div id="app">
   <compa></compa>
 </div>

 <script>
   const compb = {
     template: '<div>compb</div>'
   }


   const compa = {
     template: `
     <div>
       compa
       <compb></compb>
     </div>
     `,
     components: {
       compb
     }
   }

   var vm = new Vue({
     el: "#app",
     components: {
       compa
     }
   })
 </script>
</body>

父传子

通过prop向子组件传递数据

<body>
   <div id="app">
        <component-test :mytitle="title" :mylist="list"></ccomponent-test> //@key
   </div>
 
   <script type="text/template" id="template-test">
      <div>
           <h2>{{mytitle}}</h2>
           <ul>
               <li v-for="user in mylist">
                  {{user.username}}
               </li>
           </ul>
    </div>
  </script>

   <script>

       Vue.component("component-test",{
           props:['mytitle','mylist'],//@key
           template:"#template-test"
       })

       var vm = new Vue({
           el:"#app",
           data:{
               title:"parent-title",
               list:[
                   {username:'hanye',age:20},
                   {username:'hansai',age:30}
               ]
           }
       })
   </script>
</body>

子传父

通过$emit 事件泡发

<body>
   <div id="app">
       {{parent_count}}
       <component-test @countchange="handleChange"></component-test> //@key
   </div>

   <script type="text/template" id="template-test">
       <div>
           <h2>{{count}}</h2>
           <button @click="increment">+</button>
       </div>
   </script>

<script>
   Vue.component('component-test',{
       template:"#template-test",
       data(){
           return {
               count:0
           }
       },
       methods:{
           increment(){
               this.count++
               this.$emit('countchange',this.count)//@Key
           }
       }
   })

   var vm = new Vue({
       el:"#app",
       data:{
           parent_count:0
       },
       methods:{
           handleChange(num){
               this. parent_count = num
           }
       }
   })
</script>
</body>

非父子

非父子组件之间进行通信,用 中央事件总线 eventbus

中央总线的特点,只要订阅了,触发的时候就能收到通知

<body>
 <div id="app">
   <component-a></component-a>
   <component-b></component-b>
 </div>
 
 
 <script>
 
 var eventbus = new Vue();//空vue实例 就是中央事件总线

 Vue.component('component-a',{
   template:`<div>组件a</div>`,
     mounted(){
        eventbus.$on("message",function(msg){  //@key  
             console.log(msg);//wanglei
         })
     }
 })

 Vue.component('component-b',{
   template:` <button @click="handleclick">组件b 点击</button>`,
   methods:{
        handleclick(){
            eventbus.$emit("message","收到通知了吗") //@key
        }
    }
 })
 
 
 var vm = new Vue({
    el:"#app",
    data:{
        username:"wanglei"
    },
 })
 </script>
 </body>

动态组件

通过 Vue 的 component 元素加一个特殊的 is 属性来实现

<div id="app">
   <!-- 动态组件 -->
   <component v-bind:is="currentTap"></component>
  
   <button v-for="tab in tabs" @click="handleChange(tab)">
     {{tab}}
   </button>
 </div>

 <script>
   Vue.component('tap-position', {
     template: '<div>position</div>'
   })

   Vue.component('tap-search', {
     template: '<div>search</div>'
   })

   var vm = new Vue({
     el: "#app",
     data: {
       currentTap: 'tap-position',
       tabs: ['tap-position', 'tap-search']
     },
      
     methods: {
       handleChange(tabs) {
         this.currentTap = tabs;
       }
     }
   })
 </script>

</body>

keep-alive

在动态组件上使用keep-alive

当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题

重新创建动态组件的行为通常是非常有用的,但是在这个案例中,我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 `` 元素将其动态组件包裹起来

<body>
 <div id="app">
   <!-- 动态组件keep-alive -->
   <keep-alive>
     <component v-bind:is="currentTap"></component>
   </keep-alive>
   
   <button v-for="tab in tabs" @click="handleChange(tab)">
     {{tab}}
   </button>
 </div>

 <script>
   Vue.component('tap-position', {
     template: '<div>position <input type="text"/></div>'
   })

   Vue.component('tap-search', {
     template: '<div>search</div>'
   })

   var vm = new Vue({
     el: "#app",
     data: {
       currentTap: 'tap-position',
       tabs: ['tap-position', 'tap-search']
     },
      
     methods: {
       handleChange(tabs) {
         this.currentTap = tabs;
       }
     }
   })
 </script>
</body>

在组件上使用v-model

这个组件内的 input必须:

  • 将其 value attribute 绑定到一个名叫 value 的 prop 上
  • 在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出
  • 自己实现的v-model
<body>
    <div id="app">
        <h1>{{username}}</h1>
        <!-- <component-test v-model="username"></component-test> -->
        <!-- 这2句是等价的 -->
        <component-test :value="username"  @input="handleInput"></component-test>
    </div>
<script>
    Vue.component('component-test',{
        props:['value'],
        template:`  <input type="text" :value="value" @input="handleclick">`,
        data(){
            return {
                count:0
            }
        },
        methods:{
            handleclick(event){
                this.$emit('input',event.target.value)
            }
        }
    
    })

    var vm = new Vue({
        el:"#app",
        data:{
            username:"wanglei"
        },
        methods:{
            handleInput(username){
                this.username = username
            }
        }
    })
</script>
</body>
  • v-model
<body>
    <div id="app">
        <h1>{{username}}</h1>
        <component-test v-model="username"></component-test>
    </div>
<script>
    Vue.component('component-test',{
        props:['value'],
        template:`  <input type="text" :value="value" @input="handleclick">`,
        data(){
            return {
                count:0
            }
        },
        methods:{
            handleclick(event){
                this.$emit('input',event.target.value)
            }
        }
    
    })

    var vm = new Vue({
        el:"#app",
        data:{
            username:"wanglei"
        } 
    })
</script>
</body>

自定义组件

自定义组件的v-model

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的model 选项可以用来避免这样的冲突,改变默认值

<body>
    <div id="app">
        {{checkvalue}}
        <component-checkbox v-model="checkvalue"></component-checkbox>
    </div>

<script>
    Vue.component('component-checkbox',{
      //改变默认值
        model:{
            prop:'checked',//默认值是:value
            event:'change'//默认值:input
        },
        props:['checked'],
        template:`<input type="checkbox" :checked="checked" @change="handleChange"/>`,
        methods:{
            handleChange(event){
                this.$emit("change",event.target.checked)
            }
        }

    })
    

    var vm = new Vue({
        el:"#app",
        data:{
            checkvalue:true
        } 
    })
</script>
</body>

.sync修饰符

  • sync 代码
<body>
    <div id="app">
        {{name}}
        <component-input :myname.sync="name"></component-input>
    </div>

<script>
    Vue.component('component-input',{
        template:`<input type="text" @change="$emit('update:myname',$event.target.value)"/>`
    })

    var vm = new Vue({
        el:"#app",
        data:{
            name:''
        } 
    })
</script>
</body>
  • 上面sync的代码实现的功能和下面这些代码一样
<body>
    <div id="app">
        {{name}}
        <component-input @change="handleChange"></component-input>
    </div>


<script>
    Vue.component('component-input',{
        template:`<input type="text" @change="$emit('change',$event.target.value)"/>`
    })

    var vm = new Vue({
        el:"#app",
        data:{
            name:''
        },
        methods:{
            handleChange(val){
                this.name = val
            }
        }
    })
</script>
</body>

插槽

具名插槽

意思就是起了名字的插槽

<body>
   <div id="app">
       <component-a>
           <template #header>
               <h1>header</h1>
           </template>

           <template #main>
               <h1>main</h1>
           </template>

           <h1>footer</h1>
       </component-a>
   </div>
   

<script type="text/template" id="template-a"> 
   <div>
       <header>
           <slot name="header"></slot>
       </header>
       <main>
           <slot name="main"></slot>
       </main>
       <footer>
           <slot></slot>
       </footer>
   </div>
</script>


<script>
   Vue.component('component-a',{
       template:"#template-a"
   })

   var vm = new Vue({
       el:"#app",
   })
</script>
</body>

作用域插槽

有时让插槽内容能够访问子组件中才有的数据是很有用的

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的

<body>
   <div id="app">
       <component-user v-slot:default="obj"> //default是插槽的名字,default 可以省略掉
           {{obj.myname}}
       </component-user>
   </div>

<script>
   Vue.component('component-user',{
       template:`<div>
               <slot  :myname="name"></slot> //@key
           </div>`,
       data(){
           return {
               name:'hanye'
           }
       }    
   })

   var vm = new Vue({
       el:"#app",
       data:{
           name:''
       } 
   })
</script>
</body>

后备内容

意思其实就是默认值

<body>
 <div id="app">
   <my-button>确定</my-button>
 </div>

 <script>
   Vue.component('my-button', {
     template: `
         <button>
             <slot>submit</slot> //如果不传就是submit
         </button>
        `
   })


   var vm = new Vue({
     el: "#app",
     data: {
       username: 'hanye'
     }
   })
 </script>
</body>

prop

<body>
 <div id="app">
   <component-a   a="123" :b="123" :c="[1,2]" :d="{name:'wanglei',age:'20'}"  :f="handle"             h="banana">
   </component-a>
 </div>

 <script>
   Vue.component('component-a', {
     props:{
       a:String,
       b:{
         required:true,
         type:Number
       },
       c:Array,
       f:Function,
       d:{
         type:Object,
         default:function(){
           return {message:'hello'}
         }
       },
       i:{
         required:false,
          type:Number,
          default:100
       },
       h:{
         required:true,
         validator:function(value){
           if(value.indexOf('banana') > -1) {
             return true;
           } else {
             return false;
           }
         }
       }

     },
     template: '<div>test</div>'
   })

    
   var vm = new Vue({
     el: "#app",
     methods:{
       handle(){

       }
     }
   })
 </script>
</body>

处理边界情况

访问根 和父级组件实例

意思是子组件拿父组件的数据

通过root,parent

<body>
   <div id="app">
       <compa></compa>
   </div>

   
<script>
   const compb = {
       template:`<div>
          访问跟组件的数据: {{$root.username}} <br/>
          访问父元素的数据: {{$parent.title}}
           </div>`,
       mounted(){
         console.log(this.$root.username);
         console.log(this.$parent.username);
         
       }    
   }

   const compa = {
       template:`<div >
           <compb></compb>
           </div>`,
      components:{
        compb
      },
      data(){
        return {
          title:"compa-title"
        }
      }     
   }

   var vm = new Vue({
       el:"#app",
       data:{
           username:"wanglei"
       },
       components:{
           compa
       }
   })
</script>
</body>

访问子组件实例

意思是父组件拿子组件的数据

尽管存在 prop 和事件,有的时候你仍可能需要在 JavaScript 里直接访问一个子组件。为了达到这个目的,你可以通过 ref 这个 attribute 为子组件赋予一个 ID 引用

<body>
   <div id="app">
       <compa ref="refcompa"></compa>
   </div>

   
<script>
   const compa = {
       template:`<div >
             compa
           </div>`,
      data(){
        return {
          title:"compa-title"
        }
      }     
   }

   var vm = new Vue({
       el:"#app",
       data:{
           username:"wanglei"
       },
       components:{
           compa
       },
       mounted(){
         console.log(this.$refs.refcompa.title);
         
       }
   })
</script>
</body>

依赖注入

provide 选项允许我们指定我们想要提供给后代组件的数据/方法,然后在任何后代组件里,我们都可以使用 inject 选项来接收指定的我们想要添加在这个实例上的属性

(注意不是响应式)

<body>
   <div id="app">
       <compa ></compa>
   </div>
   

<script>
   const compa = {
       inject:['username'],//@key
       template:`<div >
              {{username}}
           </div>`,
   }

   var vm = new Vue({
       el:"#app",
       data:{
           username:"wanglei2"
       },
       components:{
           compa
       },
       provide(){   //@key
           return {
               username:this.username
           }    
       }
       
   })
</script>
</body>

访问元素

<body>
 <div id="app">
   <input type="text" ref="myinput"> //@key
 </div>

 <script>
   var vm = new Vue({
     el: "#app",
     mounted() {
       console.log(this.$refs.myinput);
     }
   })
 </script>
</body>

组件的生命周期

<body>
   <div id="app">
       {{username}}
        <compa></compa>
   </div>
   

<script>
    var compa = {
        template:`<div>组件a</div>`,
        beforeCreate(){
          console.log("child beforeCreate");
       },
       created(){
           console.log("child created");
       },
       beforeMount(){
               console.log("child beforeMounted");
       },
       mounted(){
           console.log("child mounted");
       },
       beforeUpdate(){
           console.log("child beforeUpdate");
       },
       updated(){
               console.log('child updated')
       },
       beforeDestroy(){
           console.log("child beforeDestory");
       },
       destroyed(){
           console.log("child destoryed");
       }

    }

   var vm = new Vue({
       el:"#app",
       data:{
           username:"wanglei"
       },
       components:{
           compa
       },
      beforeCreate(){
          console.log("root beforeCreate");
      },
      created(){
          console.log("root created");
      },
      beforeMount(){
           console.log("root beforeMounted");
      },
      mounted(){
          console.log("root mounted");
          this.username="guoguo"
          setTimeout(() => {
         vm.$destroy();
       }, 1000)
      },
      beforeUpdate(){
          console.log("root beforeUpdate");
      },
      updated(){
           console.log('root updated')
      },
      beforeDestroy(){
          console.log("root beforeDestory");
      },
      destroyed(){
          console.log("root destoryed");
      }
   })
</script>
</body>
打印出来的结果是:
root beforeCreate
root created
root beforeMounted
child beforeCreate
child created
child beforeMounted
child mounted
root mounted
root beforeUpdate
root updated

root beforeDestory
child beforeDestory
child destoryed
root destoryed

组件之间传递数据的总结

  • props emit

    ​ 缺点:如果组件嵌套层次多的话,数据传递比较繁琐

  • provide inject(依赖注入)

    缺点:不支持响应式

  • root, parent refs

  • eventbus

    缺点:数据不支持响应式

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342