组件开发思路:
1.定义一个form表单,提交全局校验,数据的传递
provide(){return{}},下面的子孙组件可以inject获取数据
2.formItem校验、label、错误显示、插槽
3.数据收集v-model,通知校验
4.校验
4.1不能用孙组件通知校验,可以通知子组件formItem校验
this.
p
a
r
e
n
t
.
parent.
parent.emit(‘validate’)
4.2 formItem组件里面监听
mounted(){
this.$on(‘validate’,()=>{
this.validate()
})
}
methods:{
validate(){
// 做校验
// 1.获取数值和规则
const value = this.form.model[this.prop]
const rule = this.form.rules[this.prop]
// 2.创建校验
// import Schema from 'async-validator'
const schema = new Schema({[this.prop]:rule})
schema.validate({[this.prop]:value}, errors=>{
if(errors){
this.error = erros[0].message;
}else{
this.error = ''
}
})
}
}
4.3安装npm i async-validator -S
4.4 设置当前的 prop=“username” // 做校验必须的prop
4.5 form.vue
validate(cb){
const tasks = this.$children
.filter(item=>item.prop)
.map(item => item.validate()) // 返回的是promise
Promise.all(tasks)
.then(()=>cb(true))
.catch(()=>cb(false))
}
5.抽离公共组件数据可方法到mixin
一、父子组件通信
0。
父子组件通讯:
父组件:
子组件:
props: {
myvisible: {
type: Boolean,
default: false,
},
},
btnClose(){
this.$emit(‘update:myvisible’, false)
}
1.props
//child
props:{msg:String}
//parent
2.特性
a
t
t
r
s
.
父级字段名,
attrs.父级字段名,
attrs.父级字段名,listener监听事件
//child
{{$attrs.foo}}
//parent
3.refs
//parent
mounted(){
this.$refs.hw.xx = ‘xxx’
}
4.children
//parent
this.$children[0].xx = ‘xxx’ // 下标是无序的
二、子传父
// child
this.
e
m
i
t
(
′
a
d
d
′
,
g
o
o
d
)
/
/
p
a
r
e
n
t
<
c
a
r
t
@
a
d
d
=
"
c
a
r
t
A
D
D
(
emit('add', good) //parent <cart @add="cartADD(
emit(′add′,good)//parent<cart@add="cartADD(event)">
三、兄弟组件传值
通过共同的祖辈组件塔桥,
p
a
r
e
n
t
或者
parent或者
parent或者root
//brother1
this.
p
a
r
e
n
t
.
parent.
parent.on(‘foo’,handle)
//brother2
this.
p
a
r
e
n
t
.
parent.
parent.emit(‘foo’)
mounted(){
this.
o
n
(
′
f
o
o
′
,
(
)
=
>
c
o
n
s
o
l
e
.
l
o
g
(
′
f
o
o
′
)
)
t
h
i
s
.
on('foo', ()=>{ console.log('foo~~~~') }) this.
on(′foo′,()=>console.log(′foo ′))this.emit(‘foo’);
}
四、祖孙组件传值
a
t
t
r
s
、
attrs、
attrs、listener
provide需要是对象,才能响应式
// ancestor 祖宗
data () {
return {
fooObj:{
foo:5
}
}
},
//依赖注入传值
provide(){
return{
newFoo:this.fooObj
}
}
{{fooObj.foo}}
// descendant
inject:[‘newFoo’],或者inject:{newFoo:String}
五、任意两个组件之间:事件eventBus或者 vuex
1.eventBus
//main.js
import Vue from ‘vue’
const eventBus = new Vue()
export {eventBus}
// child1
eventBus.KaTeX parse error: Expected '}', got 'EOF' at end of input: …troy() { Bus.off(“onSearchwordKey”);
}
// child2
eventBus.$emit(‘foo’)
插槽slot
1.匿名插槽
// child comp
//parent
hello
2.具名插槽
// child comp
//parent
匿名插槽
匿名插槽
//v-slot:===> 缩写#,只能在template标签中使用
<template #content>我是main内容
3.作用域插槽
作用域插槽是:父组件可以拿到子组件的数据。
应用场景:多层往上传递数据时,比emit好。
子组件暴露数据给父级 :
父组件获取到数据,判断做出不同操作。
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!-- <script src="./vue.js"></script> -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<!-- <script src="http://cdn.staticfile.org/vue/2.6.10/vue.common.dev.js"></script> -->
</head>
<body>
<div id="root">
<div @click="addmsg">点击加2{{fooObj.foo}}</div>
<child>
<!-- 作用域插槽必须是template开头且结尾 -->
<template slot-scope="props">
<li v-html="props.value ===2? '<strong style=\'color:red;\'>2</strong>':props.value" ></li>
</template>
</child>
</div>
<script type="text/javascript">
Vue.component("child", {
data: function() {
return {
list: [1, 2, 3, 4, 5, 6]
}
},
inject:['newFoo'],
mounted(){
console.log('newFoo', this.newFoo)
},
methods:{
addmsg(){
this.newFoo.foo --
console.log(this.newFoo.foo)
}
},
template: `<div>
<ul>
<li v-for="item of list">{{item}}</li>
</ul>
-------
<div @click="addmsg">addmsg{{newFoo.foo}}</div>
<ul>
//这种方式的作用是:显示什么,怎么显示不再是子组件决定了,而是父组件调子组件的时候给子组件传递模版:
<slot v-for="item of list" :value=item></slot>
</ul>
</div>`
});
var vm = new Vue({
el: "#root",
data(){
return{
fooObj:{
foo:5
}
}
},
methods:{
addmsg(){
this.fooObj.foo ++
console.log(this.fooObj.foo)
}
},
provide() {
return {
newFoo: this.fooObj
}
}
})
</script>
</body>
</html>
vue3,不用new Vue,而用Vue.createApp({}).mount(“#root”)
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!-- <script src="./vue.js"></script> -->
<script src="https://unpkg.com/vue@next"></script>
<!-- <script src="http://cdn.staticfile.org/vue/2.6.10/vue.common.dev.js"></script> -->
</head>
<body>
<div id="root">
<div @click="addmsg">点击加2{{fooObj.foo}}</div>
<child>
<!-- 作用域插槽必须是template开头且结尾 -->
<template slot-scope="props">
<li v-html="props.value ===2? '<strong style=\'color:red;\'>2</strong>':props.value" ></li>
</template>
</child>
</div>
<script type="text/javascript">
Vue.createApp({
data(){
return {
hi:'hellow world'
}
},
render() {
return Vue.h('div', {}, this.hi)
}
}).mount("#root");
// const RootComponent = {
// data(){
// return {
// hi:'hellow world'
// }
// },
// render() {
// return Vue.h('div', {}, this.hi)
// }
// }
// const app = Vue.createApp(RootComponent)
// const vm = app.mount('#root')
</script>
</body>
</html>
复杂的dom(比如几十个v-if判断的dom)可以用render来简化