12.vue.js实战笔记(组件详解)

1.组件使用方法

(1)全局注册组件(要在父实例使用组件,就需要在父实例创建前注册组件)

<div id="app">
    <my-component></my-component>
</div>
<script>
    Vue.component('my-component',{
        template:'<div>这里是组件的内容</div>'   //要注意template的DOM结构必须被一个元素包裹,不带<div></div>无法渲染
    });
    var app = new Vue({
        el:'#app'
    })
</script>

(2)局部注册组件

在vue实例中,可以用components选项注册局部组件,该组件只在该实例作用域下有效(组件中也可以用components注册组件,使组件可以嵌套)

<div id="app">
    <my-component></my-component>
</div>
<script>
    var Child = {
        template:'<div>局部注册组件的内容</div>' 
    }
    var app = new Vue({
        el:'#app',
        components:{
            'my-component':Child
        } 
    })
</script>

(3)组件中的选项除了template之外,还有data,computed,methods等

   但组件内的data与实例中的data不同(data必须是函数,然后将数据return出去)

//vue实例中的data
var app = new Vue({
    el:'#app',
    data:{
        message:'a'
    }
})
//组件内的data
Vue.component('my-component',{
    template:'<div>{{message}}</div>',
    data(){
        return {
            message:'a'
        }
    }
})

(4)JavaScript对象是引用关系,如果return出的对象引用了外部的对象,那么这个对象是共享的,任何一方修改都会同步

<div id="app">
   <my-component></my-component>
   <my-component></my-component>
   <my-component></my-component>
</div>
var data = {
   counter:0
};
Vue.component('my-component',{
   template:'<button @click="counter++">{{counter}}</button>',
   data(){
      return data
   }
});
var app = new Vue({
   el:'#app',
})

所以解决方法就是给组件返回一个新的data对象来独立,3个按钮就不会互相影响了

<div id="app">
   <my-component></my-component>
   <my-component></my-component>
   <my-component></my-component>
</div>
Vue.component('my-component',{
   template:'<button @click="counter++">{{counter}}</button>',
   data(){
      return {
          counter:0
      }
   }
});
var app = new Vue({
   el:'#app',
})

2.使用props传递数据

(1)父组件向子组件正向传递参数或数据通过props来实现,props值有两种,一种是字符串数组,一种是对象

props数据和组件data里return出的数据的区别是:props的数据来自父级,data中的数据是组件自己的数据,作用域在组件本身

(2)如果子组件接收的是父组件的动态数据,就可以用v-bind动态绑定props的值:

<input type="text" v-model="parentMessage">
<my-component :message="parentMessage"></my-component>
Vue.component('my-component',{
   props:['message'],
   template:'<div>{{message}}</div>'
});
var app = new Vue({
  el:'#app',
  data:{
      parentMessage:''
  }
})

(3)单向数据流

单向数据流即父组件变化时会传递给子组件,但反过来不可以;

业务中会经常遇到两种需要改变props的情况:

第一种:父组件传初始值进来,子组件将其作为初始值保存起来,在自己作用域下随意使用和修改

这种情况可以在组件data内再声明一个数据,引用父组件的prop:

<my-component :init-count="1"></my-component>
Vue.component('my-component',{
    props:['initCount'],
    template:'<div>{{count+1}}</div>',
    data(){
      return{
         count:this.initCount
      }
    }
});
var app = new Vue({
  el:'#app'
})

组件中声明了数据count,它在组件初始化时会获取来自父组件的initCount,之后就与之无关了,只用维护count,可以避免直接操作initCount.

第二种:prop作为需要被转变的原始值传入,可用计算属性

 <my-component :width="100"></my-component>
Vue.component('my-component',{
   props:['width'],
   template:'<div :style="style">组件内容</div>',
   computed:{
        style(){
             return{
                width:this.width + 'px'
             }
        }
    }
});
var app = new Vue({
     el:'#app'
})

(4)数据验证

当prop需要验证时就需要对象写法:

 Vue.component('my-component',{
    props:{
       //必须是数字类型
       propA:Number,
       // 必须是字符串或数字类型
       propB:[String,Number],
       // 布尔值,如果没有定义,默认为true
       propC:{
           type:Boolean,
           default:true
       },
       // 数字,必传
       propD:{
           type:Number,
           required:true
       },
       // 若是数组或对象,默认值必须函数返回
       propE:{
           type:Array,
           default:function(){
               return []
           }
       },
       //自定义一个验证函数
       propF:{
           validator(value){
               return value > 10
           }
       }
   }
});

验证的type类型可以是:

String、number、boolean、object、array、function

3.组件通信

(1)自定义事件

当子组件向父组件传递数据时,会用到自定义事件:

子组件用$emit()来触发事件,父组件用$on()来监听子组件的事件(也可在子组件自定义标签上通过v-on来监听)

<p>总数:{{total}}</p>
<my-component @increase="handleGetTotal" @reduce="handleGetTotal"></my-component>
Vue.component('my-component',{
    template:`
    <div>
        <button @click="handleIncrease">+1</button>
        <button @click="handleReduce">-1</button>
    </div>`,
    data(){
        return{
            counter:0
        }
    },
    methods:{
        handleIncrease(){
            this.counter++
            this.$emit('increase',this.counter)
        },
        handleIncrease(){
            this.counter--
            this.$emit('reduce',this.counter)
        }
    }
});
var app = new Vue({
    el:'#app',
    data:{
        total:0
    },
    methods:{
        handleGetTotal(total){
            this.total = total
        }
    }
})

(2)使用v-model

用来创建自定义的表单输入组件,进行数据双向绑定:

<p>总数:{{total}}</p>
<my-component v-model="total"></my-component>
<button @click="handleReduce">-1</button>
Vue.component('my-component',{
    props:['value'],
    template:'<input :value="value" @input="updateValue">',
    methods:{
        updateValue(event){
            this.$emit('input',event.target.value)
        }
    }
});
var app = new Vue({
    el:'#app',
    data:{
        total:0
    },
    methods:{
        handleReduce(){
            this.total--
        }
    }
})

实现这样一个具有双向绑定的v-model组件要满足以下两个要求:

~接收一个value属性

~在有新的value时触发input事件

(3)非父子组件通信

使用一个空的vue实例作为中央事件总线bus,即一中介。

<div id="app">
   {{message}}
   <my-component></my-component>
</div>
var bus = new Vue()  // 中央事件总线bus
Vue.component('my-component',{
    template:'<button @click="handleEvent">传递事件</button>',
    methods:{
        handleEvent(){
            bus.$emit('on-message','来自组件my-component的内容')
        }
    }
});
var app = new Vue({
    el:'#app',
    data:{
        message:''
    },
    mounted(){
        var _this = this
        // 在实例初始化时 监听来自bus实例的事件
        bus.$on('on-message',(msg) => {
            _this.message = msg
        })
    }
})

(4)父链和子组件索引

父链:

在子组件中,使用this.$parent可以直接访问该组件的父实例或组件,父组件可以通过this.$children访问它所有的子组件;

而且可以递归向上或向下无限访问,直到根实例或最内层的组件。

{{message}}
<my-component></my-component>
Vue.component('my-component',{
    template:'<button @click="handleEvent">通过父链直接修改数据</button>',
    methods:{
        handleEvent(){
            // 访问到父链后,直接修改数据
            this.$parent.message = '来自组件my-component的内容'
        }
    }
});
var app = new Vue({
    el:'#app',
    data:{
        message:''
    }
})

子组件索引:

用属性ref为子组件指定一个索引名称

<button @click="handleRef">通过ref获取子组件实例</button>
<my-component ref="comA"></my-component>
Vue.component('my-component',{
    template:'<div>子组件</div>',
    data(){
        return{
            message:'子组件内容'
        }
    }
});
var app = new Vue({
    el:'#app',
    methods:{
        handleRef(){
            // 通过$ref来访问指定的实例
            var msg = this.$refs.comA.message
            console.log(msg)
        }
    }
})

4.使用slot分发内容

props传递数据、events触发事件、slot内容分发构成了vue组件的3个API来源

slot分发的内容,作用域是在父组件上的

(1)单个slot

在子组件内使用slot元素为子组件开启slot插槽,在父组件模板里,插入在子组件标签内的所有内容将替代子组件的<slot>标签及它的内容

<child-component>
    <p>分发的内容</p>
    <p>更多分发的内容</p>
</child-component>
Vue.component('child-component',{
    template:`<div>
        <slot>
            <p>如果父组件没有插入内容,我将作为默认内容出现</p>
        </slot>
    </div>`
});
var app = new Vue({
    el:'#app'
})

子组件<slot>内的备用内容,作用域是子组件本身

(2)具名slot

给<slot>元素指定一个name后可以分发多个内容

<child-component>
    <h2 slot="header">标题</h2>
    <p>正文内容</p>
    <p>更多正文内容</p>
    <div slot="footer">底部信息</div>
</child-component>
Vue.component('child-component',{
    template:`
    <div class="container">
        <div class="header">
            <slot name="header"></slot>
        </div>
        <div class="main">
            <slot></slot>
        </div>
        <div class="footer">
            <slot name="footer"></slot>
        </div>
    </div>
    `
});
var app = new Vue({
    el:'#app'
})

(3)作用域插槽

使用一个可复用的模板替换已渲染元素

<child-component>
    <template scope="props">
        <p>来自父组件的内容</p>
        <p>{{props.msg}}</p>
    </template>
</child-component>
Vue.component('child-component',{
    template:`
    <div class="container">
        <slot msg="来自子组件的内容"></slot>
    </div>
    `
});
var app = new Vue({
    el:'#app'
})

作用域插槽更具代表性的用例是列表组件,允许组件自定义应该如何渲染列表每一项

<!-- :books="books"是父组件传给子组件的数据 -->
<my-list :books="books"> 
    <!-- 作用域插槽也可以是具名的slot -->
    <template slot="book" scope="props">  
        <li>{{props.bookName}}</li>
    </template>
</my-list>
Vue.component('my-list',{
    props:{
        books:{
            type:Array,
            default(){
                return []
            }
        }
    },
    template:`
    <ul>
        <slot name="book" v-for="book in books" :book-name="book.name"></slot>
    </ul>
    `
});
var app = new Vue({
    el:'#app',
    data:{
        books:[
            {name:'《vue.js实战》'},
            {name:'《vue.js实战2》'},
            {name:'《vue.js实战3》'}
        ]
    }
})

通过$slots可以访问某个具名slot,this.$slots.default包括了所有没有被包含在具名slot中的节点。

5.组件高级用法

(1)递归组件

给组件设置name,组件可以在它的模板内自己调用自己;但必须给一个条件来限制递归数量,否则会抛出错误

<child-component :count="1"></child-component>
Vue.component('child-component',{
    name:'child-component',
    props:{
        count:{
            type:Number,
            default:1
        }
    },
    template:`
    <div class="child">
        <child-component :count="count+1" v-if="count<3"></child-component>
    </div>
    `
});
var app = new Vue({
    el:'#app'
})

(2)内联模板

使用组件时,给组件标签使用inline-template特性,组件就会把它的内容当做模板,而不是当内容分发。

但缺点就是作用域比较难以理解,所以不要轻易使用内联模板。

(3)动态组件

使用元素<component>及其is特性动态挂载不同的组件

<component :is="currentView"></component>
<button @click="handleChangeView('A')">切换到A</button>
<button @click="handleChangeView('B')">切换到B</button>
<button @click="handleChangeView('C')">切换到C</button>
var app = new Vue({
    el:'#app',
    components:{
        comA:{
            template:'<div>组件A</div>'
        },
        comB:{
            template:'<div>组件B</div>'
        },
        comC:{
            template:'<div>组件C</div>'
        }
    },
    data:{
        currentView:'comA'
    },
    methods:{
        handleChangeView(com){
            this.currentView = 'com' + com
        }
    }
})

(4)异步组件

vue.js只在组件需要渲染时触发工厂函数,并把结果缓存起来,用于后面的再次渲染。

(5)$nextTick

this.$nextTick()将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。

它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。

(6)手动挂载实例

Vue.extend和$mount 这两个方法可以手动挂载一个实例

Vue.extend是基础vue构造器,创建一个子类,参数是一个包含组件选项的对象

如果vue实例在实例化时没有el选项,就处于未挂载状态,没有关联的DOM元素,可以使用$mount()手动挂载一个未挂载的实例。

<div id="mount-div">

</div>
var myComponent = Vue.extend({
    template:'<div>Hello {{name}}</div>',
    data(){
        return{
            name:'lyf'
        }
    }
})
new myComponent().$mount('#mount-div')

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值