1-1 组件的定义及复用性,局部组件和全局组件
把复杂的应用拆分成很多小的组件来进行维护
全局组件
<script>
const app = Vue.createApp({
template: `
<div>
<counter-parent />
<counter />
</div>
`
})
app.component('counter-parent', {
template:`
<counter />
`
})
app.component('counter', {
data() {
return{
count: 0
}
},
template: `
<div @click="count += 1">{{count}}</div>
`
})
const vm = app.mount('#root');
</script>
这个就是全局组件,假如我们根组件没有引用子组件
const app = Vue.createApp({
template: `
<div>
hello Y
</div>
`
})
但是子组件任然占据根组件的内存,这个就是全局组件,只要定义了,处处可以使用,性能不高,但是使用起来简单
局部组件
<script>
const Counter = {
data() {
return {
count: 1
}
},
template: `
<div @click="count += 1">{{count}}</div>
`
}
const app = Vue.createApp({
components: {
counter: Counter
},
template: `
<div>
<counter />
</div>
`
})
const vm = app.mount('#root');
</script>
引用一个局部组件需要用components
来注册,局部组件,定义了,要注册之后才能使用,性能比较高,使用起来有些麻烦,建议大写字母开头,驼峰命名。
局部组件使用时,要做一个名字和组件间的映射对象,你不写映射,Vue 底层也会自动尝试帮你做映射
1-2 组件间传值及传值校验
之前做过的那个TodoList就有用到组件间的传值,先贴一个很简单的代码
<script>
const Counter = {
props: ['content'],
template: `
<div>{{content}}</div>
`
}
const app = Vue.createApp({
components: {
counter: Counter
},
template: `
<div>
<counter content="hello Y" />
</div>
`
})
const vm = app.mount('#root');
</script>
这里父组件要传给子组件的值,子组件用props
接收,但是这种传值是静态的,也可以说是固定的,传过去的值是String
类型
<script>
const Counter = {
props: ['content'],
template: `
<div>{{typeof content}}</div>
`
}
const app = Vue.createApp({
components: {
counter: Counter
},
template: `
<div>
<counter content="123" />
</div>
`
})
const vm = app.mount('#root');
</script>
要动态传值,只需要在content
前面加一个冒号就行了
const app = Vue.createApp({
data () {
return {
name: 'Jennie'
}
},
components: {
counter: Counter
},
template: `
<div>
<counter :content="name" />
</div>
`
})
这样写就行了
对于子组件接收父组件传过来的值时,可以设置一些属性
const Counter = {
props: {
content: {
type: String,
required: true,
default: 'Jie',
validator: function(value) {
return value < 1000
}
}
},
template: `
<div>{{content}}</div>
`
}
type
设置content
的类型,可以是String
、Boolean
、Array
、Object
、Function
、Symbol
required
:这个值是否是必传,如果设置为true
,但是父组件没有传,后台就会警告
default
:默认值,假如父组件没有传值,就会使用默认值,也可以用函数表示
default: function() {
return 'Jie'
},
validator
:数据校验
1-3 单项数据流的理解
先对上一节做一个补充
<script>
const app = Vue.createApp({
data () {
return {
a: 'aaaa',
b: 'bbbb',
c: 'cccc',
d: 'dddd'
}
},
template: `
<div>
<counter :a="a" :b="b" :c="c" :d="d" />
</div>
`
})
app.component('counter', {
props:['a', 'b', 'c', 'd'],
template: `
<div>{{a}} - {{b}} - {{c}} - {{d}}</div>
`
})
const vm = app.mount('#root');
</script>
显然!!这样子传值很麻烦,万一要传很多值,那要写一大堆,很臃肿,我们可以简化一下
const app = Vue.createApp({
data () {
return {
params:{
a: 'aaaa',
b: 'bbbb',
c: 'cccc',
d: 'dddd'
}
}
},
template: `
<div>
<counter v-bind="params" />
</div>
`
})
等价于<counter :a="params.a" :b="params.b" :c="params.c" :d="params.d" />
还有一个需要注意到的地方是,我们在传值的时候,假如传的值命名很长,看下面的代码
<script>
const app = Vue.createApp({
data () {
return {
name: 'Jennie'
}
},
template: `
<div>
<counter :name-abc="name" />
</div>
`
})
app.component('counter', {
props:['nameAbc'],
template: `
<div>{{nameAbc}}</div>
`
})
const vm = app.mount('#root');
</script>
也就是说,属性传值的时候,使用name-abc
这种命名,接的时候,使用nameAbc
命名。
好了进入正题!单向数据流是什么?先看一段代码
<script>
const app = Vue.createApp({
data () {
return {
count: 1
}
},
template: `
<div>
<counter :count="count" />
</div>
`
})
app.component('counter', {
props:['count'],
template: `
<div @click="count += 1">{{count}}</div>
`
})
const vm = app.mount('#root');
</script>
父组件向子组件传count
这个值,我们点击div
,count
加1。但是实际上我们点击div
的时候,控制台会有警告,我们是不能直接修改父组件传过来的值的!!!为什么呢?
假设一下:
<div>
<counter :count="count" />
<counter :count="count" />
<counter :count="count" />
</div>
如果能修改,我们点击第一个counter
,后面两个的组件的值都会发生变化,会造成逻辑混乱,维护的时候也不知道是哪个组件改变的值(每个子组件应该是相互独立的,不应该存在数据耦合)
单项数据流的概念:子组件可以使用父组件传递过来的数据,但是绝对不能修改传递过来的数据
1-4 Non-Prop 属性是什么
先看一个代码
<script>
const app = Vue.createApp({
template: `
<div>
<counter msg="hello" />
</div>
`
})
app.component('counter', {
template: `
<div>Jennie</div>
`
})
const vm = app.mount('#root');
</script>
子组件没有用Props
接收父组件传来的值,那么这些数据会变成子组件最外层dom节点上的属性
如果我们不想接收Non-Prop 属性,我们可以
app.component('counter', {
inheritAttrs: false,
template: `
<div>Jennie</div>
`
})
但是假如子组件是这样的:
app.component('counter', {
template: `
<div>Jennie</div>
<div>Jennie</div>
<div>Jennie</div>
`
})
因为子组件最外层的节点不止一个,属性不知道要挂在哪一个节点上,我们可以通过下面的方式来接收
<script>
const app = Vue.createApp({
template: `
<div>
<counter msg="hello" msg1="hello1" />
</div>
`
})
app.component('counter', {
template: `
<div v-bind="$attrs">Jennie</div>
<div :msg="$attrs.msg">Jennie</div>
<div :msg1="$attrs.msg1">Jennie</div>
`
})
const vm = app.mount('#root');
</script>
v-bind="$attrs"
:接收所有的Non-Prop属性
:msg="$attrs.msg"
:接收指定的Non-Prop属性
Non-Prop 属性的应用
可以设置style
属性
<script>
const app = Vue.createApp({
template: `
<div>
<counter style="color: pink" />
</div>
`
})
app.component('counter', {
template: `
<div>Jennie</div>
`
})
const vm = app.mount('#root');
</script>
还可以绑定class
属性
<style>
.blue {
color: blue;
}
</style>
<script>
const app = Vue.createApp({
template: `
<div>
<counter class="blue" />
</div>
`
})
app.component('counter', {
template: `
<div>Jennie</div>
`
})
const vm = app.mount('#root');
</script>
1-5 父子组件如何通过事件进行通信
先看一个很简单的父子组件通过事件进行通信的例子:
<script>
const app = Vue.createApp({
data () {
return {
count: 1
}
},
methods:{
handleAdd() {
this.count += 1
}
},
template: `
<div>
<counter :count="count" @add-one="handleAdd"/>
</div>
`
})
app.component('counter', {
props:['count'],
methods: {
handleClick() {
this.$emit('addOne')
}
},
template: `
<div @click="handleClick">{{count}}</div>
`
})
const vm = app.mount('#root');
</script>
子组件通过$emit
向父组件触发一个addOne
事件,告诉父组件让他帮自己改count
的值
这个$emit
不仅可以触发事件,还能传递参数,也能写js表达式,举例^ ^
传递参数(参数可以不止一个^ ^):
// 子组件
handleClick() {
this.$emit('addOne', 2)
}
// 父组件
handleAdd(num) {
this.count += num
}
js表达式:
// 子组件
handleClick() {
this.$emit('addOne', this.count + 2)
}
// 父组件
handleAdd(num) {
this.count = num
}
还有就是我们可以这样写:
在子组件里写:emits:['addOne'],
,这样不仅可以很直观的展示在这个组件里我们会向父组件触发哪些事件,还可以:
emits:{
addOne: (num) => {
if(num < 4) {
return true
} else {
return false
}
}
}
这样写可以对$emit
里传的值进行校验
最后说一个稍微“高级”的知识
前面我们用input
框的时候已经知道v-model
可以实现数据的双向绑定,那我们父子组件的通信能不能用v-model
来实现捏?
当然是可以的 > <
<script>
const app = Vue.createApp({
data () {
return {
count: 1
}
},
template: `
<counter v-model="count"/>
`
})
app.component('counter', {
props:['modelValue'],
methods: {
handleClick() {
this.$emit('update:modelValue', this.modelValue + 2)
}
},
template: `
<div @click="handleClick">{{modelValue}}</div>
`
})
const vm = app.mount('#root');
</script>
使用v-model
传值时,就收的props
里写modelValue
是固定的!!比如写成modelValue1
就会报错
$emit
里面写update:modelValue
也是固定的!!
但是这个modelValue
也不是真的不能改,我们可以通过<counter v-model:add="count"/>
,这样就可以把子组件里的modelValue
换成add
这个知识点也不是特别常用,知道就好