Vue组件基础知识点总结

199 阅读3分钟

什么是组件?

  • 组件 => 可复用的Vue实例,且带有一个名字

  • 例如:下拉菜单组件 => html + CSS + JS

  • 通常一个应用会以一颗嵌套的组件树来表示:

Component Tree

局部组件 & 全局组件

  • 使用局部组件:1. 建子 2. 挂子 3. 用子
  • ps:在组件中data必须是一个函数,返回一个对象,一个组件的data选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝
  • 使用全局组件:通过Vue.component(组件名, {})创建全局组件,此时该全局组件可以在任意模板(template)中使用
<div id="app">
  //3.使用子组件
  <App></App>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
  //App组件 html + css + js
  //创建全局组件
  Vue.component('Vheader',{
      template:`<div>我是导航组件</div>`
  })

  Vue.component('Vaside',{
      template:`<div>我是侧边栏</div>`
  })

  const Vbtn = {
      template:`<button>按钮</button>`
  }
  
  const Vcontent = {
      data() {
    	return {
        }
      },
      template:`'
              <div>
                  我是内容组件
                  <Vbtn></Vbtn>
                  <Vbtn></Vbtn>
                  <Vbtn></Vbtn>
              </div>`,
      components: {
            Vbtn
      }
   }
        
   //使用局部组件的打油诗:声子 挂子 用子
   //1. 创建组件
   //注意:在组件中这个data必须是一个函数,返回一个对象
   const App = {
       data() {
           return {
            msg: '我是App组件'
            }
       },
       components: {
            Vcontent
       },
       template:`
            <div>
                <Vheader></Vheader>
                <div>
                    <Vaside/>
                    <Vcontent/>
                </div>
            </div>`,
  	methods: {
    	handleClick () {
      	this.msg = '学习局部组件';
            }
          },
	}
  
	new Vue({
  	el:'app',
  	data:{
    
  	},
  	components: {
    	//2. 挂载子组件
            App
           }
	})
</script>

组件通信

  • 父传子:通过Prop向子组件传递数据

    <div id="app">
      <App></App>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script>
      //全局组件
      //父传子:通过props来进行通信
      //1. 在子组件中声明props接收在父组件挂载的属性
      //2. 可以在子组件的template中任意使用
      //3. 在父组件绑定自定义的属性
      Vue.component('Child' , {
        template:`
              <div>
                  <h2>我是一个子组件</h2>
                  <h3>{{childData}}<h3>
             </div>`,
        props:['childData']
      })
      
      const App = {
        data() {
          return {
            msg: '我是父组件传来的值'
          }
        },
        template: `
              <div>
                  <Child :childData = 'msg'></Child>
              </div>`,
      }  
      
      new Vue({
           el: 'app',
           data: {
    
           },
           components: {
               App
           }
      })
    </script>
    

    单向数据流

    所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。

    额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

    这里有两种常见的试图变更一个 prop 的情形:

    1. 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。 在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:

      props: ['initialCounter'],
      data: function () {
        return {
          counter: this.initialCounter
        }
      }
      
    2. 这个 prop 以一种原始的值传入且需要进行转换。 在这种情况下,最好使用这个 prop 的值来定义一个计算属性:

      props: ['size'],
      computed: {
        normalizedSize: function () {
          return this.size.trim().toLowerCase()
        }
      }
      
  • 子传父:监听子组件事件,使用事件抛出一个值

    <div id="app">
      <App></App>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script>
      //全局组件
      //子往父传值
      //在父组件中 绑定自定义事件
      //在子组件中触发原生的事件 在事件函数通过this.$emit触发自定义的事件
      Vue.component('Child', {
      	template: `
              <div>
                  <input type = "text" @input = 'handleInput'/>
              </div>`,
          methods: {
              handleInput(e) {
                 const val = e.target.value;
                 this.$emit('inputHandler', val);
              }
          },
      })
    
      const App = {
        data() {
          return {
            newVal: ''
          }
        },
        methods: {
          input(newVal) {
            this.newVal = newVal;
          }
        },
        template: `
              <div>
                  <div class = 'father'>数据:{{newVal}}</div>
    		<Child @inputHandler = 'input'></Child>
              </div>`,
    
       new Vue({
        el: 'app',
        data: {
        
      	},    
        components: {
            App
        }
     })
    </script>
    
  • 平行组件

    在开发中,可能会存在没有关系的组件通信,比如有个博客内容显示组件,还有一个表单提交组件,我们现在提交数据得到博客内容组件显示,这样的操作,应用上述两种已经学习过的通信方式可能很难办到,为了解决这种问题,在vue中我们可以使用bus,创建中央事件总线

    <div id="app">
      <App></App>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script>
      const bus = new Vue();
    	//中央事件总线 bus
    	Vue.component('B', {
        data() {
          return {
            count: 0
          }
        },
        template: ` 
              <div>{{count}}</div>`,
        created() {
          //$on 绑定事件
          bus.$on('add',(n) => {
            this.count += n;
          })
        },
      })
    
      Vue.component('A', {
        data() {
          return {
            
          }
        }, 
        template: `
            <div>
                <button @click = 'handleClick'>加入购物车</button>
            </div>`,
        methods: {
          handleClick() {
            //触发绑定的函数 
            //$emit 触发事件
            bus.$emit('add', 1);
          }
        },
      })
    
      const App = {
        data() {
          return {
            
          }
        },
        template: `
              <div>
                  <A></A>
                  <B></B>
              </div>`,
      	}
    
        new Vue({
            el: 'app',
            data: {
          
            },
            components: {
              App
            }
      })
    </script>
    
  • 其他的组件通信方式:父组件provide来提供变量,然后子组件中通过inject来注入变量,无论组件嵌套多深

    1. provide 选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的 property
    2. inject 选项应该是:一个字符串数组,或一个对象,对象的 key 是本地的绑定名
    <div id="app">
      <App></App>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script>
      //provide & inject
      //父组件 provide来提供变量,然后在子组件中通过inject来注入变量,无论组件嵌套多深
      Vue.component('B', {
      	data() {
        	return {
          	count: 0
       		}
      	},
      	inject: ['msg'],
        created() {
          console.log(this.msg);
        },
        template: `
                  <div>{{msg}}</div>`,
      })
    
      Vue.component('A', {
        data() {
          return {
            
          }
        },
        created() {
          console.log(this.$parent);
        },
        template: `
          <div>
              <B></B>
          </div>`,
      })
    
      const App = {
        data() {
          return {
              tile:"Isaac"
            }
          },
          template: `
            <div>
                <A></A>
            </div>`,
      }
    
      new Vue({
        el: '#app',
        data: {
    
        },
        provide() {
          return {
            msg: '老爹的数据'
          }
        },
        components: {
          App
        }
      })
    </script>
    

插槽

  • 匿名插槽:子组件定义slot插槽,但并未具名,因此也可以说是默认插槽,只要在父元素中插入的内容,默认加入到这个插槽中去

    <div id="app">
      <App></App>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script>
      Vue.component('MBtn', {
      	template:`
              <button>
    		<slot></slot>
              </button>`
    	})
      const App = {
        data() {
          return {
            
          }
        },
        template: `
            <div>
                <MBtn>登录</MBtn>
                <MBtn>注册</MBtn>
            </div>`,
       }
       new Vue({
         el: '#app',
         data: {
    
         },
         components: {
           App
         }
       })
    </script>
    
  • 具名插槽:具名插槽可以出现在不同的地方,不限制出现的次数,只要匹配了name那么这些内容就会被插入到这个name的槽中去

    <div id="app">
      <App></App>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script>
      Vue.component('MBtn', {
      	template: `
            <button>
              <slot name='login'></slot>
              <slot name='submit'></slot>
              <slot name='register'></slot>
            </button>`
      })
      const App = {
        data() {
          return {
            
          }
        },
        template: `
            <div>
                <MBtn>
                    <template slot='login'>
                        <a href="#">登录</a>
                    </template>
               </MBtn>
               <MBtn>
                    <template slot='submit'>提交</template>
               </MBtn>
               <MBtn>
                    <template slot='register'>注册</template>
               </MBtn>
        </div>`,
       }
    
       new Vue({
         el: '#app',
         data: {
           
         },
         components: {
           App
         }
       })
    </script>