Vue组件化开发

组件化开发

标准、分治、重用、组合

1.组件注册

命名:驼峰/短横线,驼峰命名的只能在字符串模板中使用驼峰式的组件,在普通的标签模板中需要转化为短横线使用。

①全局组件注册

Vue.component('组件名称',{
    data:对象,组件数据[函数,闭包环境],
    template:组件模板内容[只包含一个根元素;可以是`模板字符串`]
})
Vue.component('button-counter',{
    data:function(){
        return{
            count:0
        }
    },
    template:'<button @click="handle">点击{{count}}次</button>',
    methods:{
        handle:function(){
            this.count++;
        }
    }
})

<button-counter></button-counter>

②局部组件注册

局部组件只能在注册他的父组件中使用

var ButtonCounter = {
    data:function(){
        return{
            count:0
        }
    },
    template:'<button @click="handle">点击{{count}}次</button>',
    methods:{
        handle:function(){
            this.count++;
        }
    }
})


components:{
    'button-counter':ButtonCounter
}

2.Vue调试工具vue-devtools

3.组件间数据交互

父--->子 方式 父组件 子组件 其他
属性props,组件内部通过props接收传递过来的值 <menu-item :title='ptitle' content='hello'></menu-item> props:['title','content']
引用refs(dom操作) <hello-word ref="hw"></hello-word>
this.$refs.hw.foo ='bar'
// data
foo:'foo'
子元素children this.$children[0].foo = 'dong' 子元素不保证顺序,也不是响应式。数组只读。
子--->父 方式 父组件 子组件 其他
$emit(自定义事件,携带参数) <menu-item title='来自父组件的值' @enlarge-text='handle($event)'></menu-item> <div @click='$emit("enlarge-text",5)'>扩大字体大小</div> 观察者模式
非父子组件
1 任意两个组件 事件中心(事件总线)或vuex
2 兄弟组件 通过$parent共同的父组件进行传递
祖先--->后代 祖先 后代
provide/inject // 和data同级
provide:{
foo:'foo'
}
// 和data同级
inject:['foo']
用于高阶插件/组件库开发,不推荐直接用于应用程序代码。

created->mounted:父组件先于子组件加载。

①父组件向子组件传值

1️⃣属性props

  • 组件内部通过props接收传递过来的值

props:使用驼峰形式,模板中使用短横线形式;字符串形式的模板中没有限制。

props:['menuTitle']-------------menu-title

  • props属性
    • 字符串string:pstr
    • 数值number::pnum数值,pnum字符串
    • 布尔boolean::pboo布尔,pboo字符串
    • 数组array:parr
    • 对象object:pobject
// 子组件
Vue.component('menu-item',{
    props:['title','content'],
    data:function(){
        return{
            msg:'子组件数据'
        }
    },
    template:'<div>{{msg + "---" + title + content}}</div>',
})
  • 父组件通过属性将值传递给子组件
<!-- 父组件 -->
<menu-item title='来自父组件的值'></menu-item>
<menu-item :title='ptitle' content='hello'></menu-item>
data:{
    ptitle:'动态绑定属性'
}

2️⃣引用refs

应用场景:dom操作

  • 父组件
<hello-word ref="hw"></hello-word>
this.$refs.hw.foo ='bar'
  • 子组件
// data
foo:'foo'

3️⃣子元素children

  • 父组件
this.$children[0].foo = 'dong'

注:子元素不保证顺序,也不是响应式。数组只读。

②子组件向父组件传值

props传递数据原则:单向数据流,只允许父组件向子组件传递数据,不允许子组件直接操作数据

观察者模式:子组件派发,父组件监听。事件真正的监听者是子组件(谁派发谁监听)。

  • 子组件通过自定义事件$emit(自定义事件,携带参数)向父组件传递信息
// 子组件
Vue.component('menu-item',{
    props:['title'],
    template:`
		<div>
			<div @click='$emit("enlarge-text",5)'>扩大字体大小</div>
		</div>		`
})
  • 父组件模板中监听子组件的事件,事件名称需要一致enlarge-text,获取参数$event
<!-- 父组件 -->
<div :style='{fontSize:fontSize+"px"}'>{{pmsg}}</div>
<menu-item title='来自父组件的值' @enlarge-text='handle($event)'></menu-item>
// 父组件中
data:{
    pmsg:'hello',
    fontSize:10
}
// 子组件传递参数val=5
methods:{
    handle:function(val){
        this.fontSize += val;
    }
}

③非父子组件间传值(任意两个组件之间)

1️⃣任意组件

事件中心(事件总线)或vuex

  • 单独的事件中心(事件总线)管理组件间的通信
image-20200831194931460
var eventHub = new Vue();
  • 监听事件与销毁事件
// 'add-todo'事件名称,addTodo事件函数
mounted:function(){
    eventHub.$on('add-todo',addTodo)
}

eventHub.$off('add-todo')
 eventHub.$on('add-todo',(val)=>{
     this.num += val
 })
  • 触发事件
handle:function(){
    eventHub.$emit('add-todo',id)
}
  • 事件中心(事件总线),全局
// main.js
Vue.prototype.$bus = new Vue()

// child1
this.$bus.$on('foo',handle)
// child2
this.$bus.$emit('foo')

2️⃣兄弟组件

通过$parent共同的父组件进行传递

④祖先和后代

由于嵌套层数过多,传递props不切实际,可以使用provide/inject

单向传值:祖先->后代

应用:用于高阶插件/组件库开发,不推荐直接用于应用程序代码。

// 祖先
provide(){
    return {foo:'foo'}
}
//后代
inject:['foo']
  • 父组件
// template
<hello-word></hello-word>
// 和data同级
provide:{
    foo:'foo'
}

// 传动态的值
provide(){
    return {foo:this.val}
}

data(){
    return {
        val:123
    }
}
  • 子组件
// 和data同级
inject:['foo']

4.组件插槽 模板内容

分类 子组件 父组件(插槽内容)
匿名插槽 <slot>默认内容</slot> <error-box>有bug发生</error-box>
具名插槽 <slot name="header"></slot> <h1 v-slot:header>标题内容</h1>
作用域插槽 <slot :item="item">{{item.name}}</slot> <template v-slot:default="scope">
{{scope.item.name}}
</template>

父组件向子组件传递内容

image-20200831195514415

①匿名插槽

  • 插槽位置,子组件模板做
// 子组件预留一个插槽位置slot
Vue.component('error-box',{
    template:`
		<div>
			<strong>ERROR:</strong>
			<slot>默认内容</slot>
		</div>		`
})
  • 插槽内容
<!-- 父组件传递插槽内容 -->
<error-box>有bug发生</error-box>

②具名插槽

根据名称匹配,没有匹配到的放到默认<slot>中。

v-slot 2.6.0引入slot 被官方废弃。

<!-- 子组件插槽定义 -->
<div>
    <header>
        <slot name="header"></slot>
    </header>
    <main>
        <slot></slot>
    </main>
    <footer>
        <slot name="footer"></slot>
    </footer>
</div>


<!-- 父组件插槽内容 -->
<!-- 一个标签 -->
<base-layout>
    <h1 v-slot:header>标题内容</h1>
    <p>主要内容1</p>
    <p>主要内容2</p>
    <p v-slot:footer>底部内容</p>
</base-layout>

<!-- 父组件插槽内容 -->
<!-- template多个标签 -->
<base-layout>
    <template v-slot:header>
        <h1>标题内容1</h1>
        <h1>标题内容2</h1>
    </template>
    <p>主要内容1</p>
    <p>主要内容2</p>
    <template v-slot:footer>
        <p>底部内容1</p>
        <p>底部内容2</p>
    </template>
</base-layout>

③作用域插槽

应用场景:父组件获取子组件的内容并进行加工处理

2.6.0以后 slot-scope 废弃,使用v-slot

// 子组件插槽定义 提供slot位置,绑定属性item
Vue.component('fruit-list',{
	props:['list'],
	template:`
        <div>
            <li v-for="item in list" :key="item.id">
                <slot :item="item">{{item.name}}</slot>
            </li>
        </div>
		`
})
<!-- 父组件 -->
<!-- slot-scope(v-slot)可以得到子组件绑定的属性item -->
<!-- 也可以解构直接写item,v-slot:default="item" -->
<fruit-list :list="list">
	<template v-slot:default="scope">
    	<strong v-if="scope.item.id==2" class="current">{{scope.item.name}}</strong>
        <span v-else>{{scope.item.name}}</span>
    </template>
</fruit-list>
date:{
    list:[{
        id:1,
        name:'apple'
    },{
        id:2,
        name='orange'
    }]
}
原文地址:https://www.cnblogs.com/wattmelon/p/13592266.html