父组件向子组件传值
- 通过子组件的 props 选项接收父组件的传值。 注意:props 不要与 data 存在同名属性。
Props 命名规则
建议 prop 命名使用 camelCase,父组件绑定时使用 kebab-case。
div id="app">
<my-component
:item-title="item.title"
:item-content="item.content"
></my-component>
</div>
<script>
Vue.component('my-component', {
props: ['itemTitle', 'itemContent'],
template: `
<div>
<h3>{{ itemTitle }}</h3>
<p>{{ itemContent }}</p>
</div>
`
})
new Vue({
el: '#app',
data: {
item: {
title: '这是示例标题',
content: '这是示例内容'
}
}
})
</script>
通过 v-for 遍历数据 items,创建组件并生成内容
<div id="app">
<demo-item
v-for="item in items"
:item-title="item.title"
:item-content="item.content"
:key="item.title"
:item="item"
></demo-item>
</div>
<script>
Vue.component('demoItem', {
props: ['itemTitle', 'itemContent', 'item'],
template: `
<div>
<h3>{{ itemTitle }}</h3>
<p> {{ itemContent }} </p>
</div>
`
})
new Vue({
el: '#app',
data: {
// 准备给子组件使用的数据
items: [
{
title: '示例标题1',
content: '示例内容1'
},
{
title: '示例标题2',
content: '示例内容2'
},
{
title: '示例标题3',
content: '示例内容3'
},
]
}
})
</script>
单向数据流
- 父子组件间的所有 prop 都是单向下行绑定的。
- 如果子组件要处理 prop 数据,应当存储在 data 中后操作。
- 注意,如果 prop 为数组或对象时,子组件操作将会影响到父组件的状态。
<div id="app">
<my-component
:initial-title="title"
:obj="obj"
></my-component>
</div>
<script>
Vue.component('my-component', {
props: ['initialTitle', 'obj'],
template: `
<div>
{{ title }}
<button @click="fn">按钮</button>
</div>
`,
data () {
return {
title: this.initialTitle
}
},
methods: {
fn () {
this.title = '这是新的标题';
this.initialTitle = '这是新的标题'; // 不会影响父组件
this.obj.name = 'jack';
}
}
});
new Vue({
el: '#app',
data: {
title: '这是示例标题',
obj: {
name: 'william',
age: 18
}
}
});
</script>
Props 类型
- Prop 可以设置类型检查,这时需要将 props 更改为一个带有验 证需求的对象,并指定对应类型。
- Prop 还可以同时指定多个类型,通过数组方式保存即可。
<div id="app">
<my-component
:par-str="str"
:par-num="num"
:par-arr="arr"
:par-obj="obj"
:par-any="any"
:par-data="str"
></my-component>
</div>
<script>
Vue.component('MyComponent', {
// 如果要设置 props 的具体规则,需要更改为对象写法
props: {
parStr: String,
parNum: Number,
parArr: Array,
parObj: Object,
parAny: undefined, // null
parData: [String, Boolean] //同时指定多个类型
},
template: `
<div>
{{ parStr }}
{{ parNum }}
{{ parArr }}
{{ parObj }}
{{ parAny }}
{{ parData }}
</div>
`
})
new Vue({
el: '#app',
data: {
num: 100,
str: 'abc',
arr: [1, 2, 3],
obj: {
content1: '示例内容1',
content2: '示例内容2'
},
any: [1, 2, 3]
}
});
</script>
Props 验证
- 当 prop 需要设置多种规则时,可以将 prop 的值设置为选项对象。
- 之前的类型检测功能通过 type 选项设置。
Vue.component('MyComponent', {
props: {
parStr: {
type: String
},
parData: {
type: [Array, Object]
}
},
template: `<div></div>`
});
- required 用于设置数据为必填项。
Vue.component('MyComponent', {
props: {
parStr: {
type: String,
required: true
}
},
template: `<div> {{ parStr }} </div>`
});
- default 用于给可选项指定默认值,当父组件未传递数据时生效。
- 注意:当默认值为数组或对象时,必须为工厂函数返回的形式。
Vue.component('MyComponent', {
props: {
parNum: {
type: Number,
default: 100
},
parArr: {
type: Array,
default () {
return [1, 2, 3];
}
}
},
template: `<div> {{ parArr }} </div>`
});
- validator 用于给传入的 prop 设置校验函数,return 值为 false 时 Vue.js 会发出警告。
- 注意:验证函数中无法使用实例的 data、methods 等功能。
Vue.component('MyComponent', {
props: {
parStr: {
type: String,
validator (value) {
return value.startsWith('user');
}
}
},
template: `<div> {{ parStr }} </div>`
});
非 Props 属性
- 当父组件给子组件设置了属性,但此属性在 props 中不存在,这时会自动绑定到子组件的根元素上。
- 如果组件根元素已经存在了对应属性,则会替换组件内部的值。
- class 与 style 是例外,当内外都设置时,属性会自动合并。
- 如果不希望继承父组件设置的属性,可以设置 inheritAttrs: false,但只适用于普通属性,class 与 style 不受影响。
<div id="app">
<my-component
data-index="3"
:title="'示例标题内容'"
style="height: 200px;"
class="colorRed"
></my-component>
</div>
<script>
Vue.component('MyComponent', {
inheritAttrs: false,
template: `
<div data-index="6"
title="旧的title"
class="abc"
style="width: 200px;">
<p>这是组件的内容</p>
</div>
`
});
new Vue({
el: '#app',
data: {}
});
</script>
子组件向父组件传值
- 子向父传值需要通过自定义事件实现。
- 子组件数据变化时,通过 $emit() 触发自定义事件。
- 自定义事件名称建议使用 kebab-case。
- 父组件监听子组件的自定义事件,并设置处理程序。
<div id="app">
<h3>购物车</h3>
<product-item
v-for="product in products"
:key="product.id"
:title="product.title"
<!--父组件在监听事件时需要接收子组件传递的数据。-->
@count-change="onCountChange"
></product-item>
<p>商品总个数为:{{ totalCount }}</p>
</div>
<script>
// 子组件
Vue.component('ProductItem', {
props: ['title'],
template: `
<div>
<span>商品名称: {{ title }}, 商品个数: {{ count }}</span>
<button @click="countIns1">+1</button>
<button @click="countIns5">+5</button>
</div>
`,
data () {
return {
count: 0
}
},
methods: {
countIns1 () {
//子组件触发事件时可以向父组件传值
this.$emit('count-change', 1);
this.count++;
},
countIns5 () {
this.$emit('count-change', 5);
this.count += 5;
}
}
});
// 父组件
new Vue({
el: '#app',
data: {
products: [
{
id: 1,
title: '苹果一斤'
},
{
id: 2,
title: '香蕉一根'
},
{
id: 3,
title: '橙子一个'
}
],
totalCount: 0
},
methods: {
onCountChange (productCount) {
this.totalCount += productCount;
}
}
});
</script>
组件与 v-model
v-model 用于组件时,需要通过 props 与自定义事件实现。
<div id="app">
<p>输入框内容为:{{ iptValue }}</p>
<com-input v-model="iptValue"></com-input>
</div>
<script>
// 子组件
var ComInput = {
props: ['value'],
template: `
<input
type="text"
:value="value"
@input="onInput"
>
`,
methods: {
onInput (event) {
this.$emit('input', event.target.value)
}
}
}
// 根实例
new Vue({
el: '#app',
data: {
iptValue: ''
},
components: {
ComInput
}
});
</script>
非父子组件传值
非父子组件指的是兄弟组件或完全无关的两个组件。
兄弟组件传值
兄弟组件可以通过父组件进行数据中转
<div id="app">
<!-- 父组件接收子组件A的数据 -->
<com-a
@value-change="value = $event"
></com-a>
<!-- 父组件将数据传递给子组件B -->
<com-b
:value="value"
></com-b>
</div>
<script>
// 子组件A:发送数据
Vue.component('ComA', {
template: `
<div>
组件A的内容: {{ value }}
<button
@click="$emit('value-change', value)"
>发送</button>
</div>
`,
data () {
return {
value: '这是组件A中的数据'
}
}
});
// 子组件B:接收数据
Vue.component('ComB', {
props: ['value'],
template: `
<div>
组件B接收到: {{ value }}
</div>
`
});
// 根实例(父组件)
new Vue({
el: '#app',
data: {
// 用于数据中转
value: ''
}
})
</script>
EventBus
<div id="app">
<h3>购物车</h3>
<product-item></product-item>
<product-total></product-total>
</div>
<script>
var bus = new Vue();
// 商品组件
Vue.component('ProductItem', {
template: `
<div>
<span>商品名称:苹果,商品个数:{{ count }}</span>
<button
@click="countIns"
>+1</button>
</div>
`,
data () {
return {
count: 0
}
},
methods: {
countIns () {
// 给bus触发自定义事件,传递数据
bus.$emit('countChange', 1);
this.count++;
}
}
});
// 计数组件
Vue.component('ProductTotal', {
template: `
<p>总个数为: {{ totalCount }}</p>
`,
data () {
return {
totalCount: 0
}
},
created () {
// 给 bus 注册事件,并接收数据
bus.$on('countChange', (productCount) => {
// 实例创建完毕,可以使用 data 等功能
this.totalCount += productCount;
});
}
})
// 根实例
new Vue({
el: '#app',
data: {
}
});
</script>
其他传值方式
$root
- $root 用于访问当前组件树根实例,设置简单的 Vue 应用时可以 通过此方式进行组件传值。
- 除了
$root, Vue.js 中还提供了$parent与$children用于 便捷访问父子组件。
<div id="app">
<com-a></com-a>
</div>
<script>
// 根实例的子组件A的子组件B
var ComB = {
template: `
<div>
组件B: {{ $root.count }}
<button @click="clickFn">按钮</button>
</div>
`,
methods: {
clickFn () {
this.$root.count = 200;
}
}
};
// 子组件A
var ComA = {
template: `
<div>
组件A: {{ $root.count }}
<button @click="clickFn">按钮</button>
<com-b></com-b>
</div>
`,
methods: {
clickFn () {
this.$root.count = 100;
}
},
components: {
ComB
}
};
// 根实例
new Vue({
el: '#app',
data: {
count: 0
},
components: {
ComA
}
});
</script>
$refs
- $refs 用于获取设置了 ref 属性的 HTML 标签或子组件。
- 给普通 HTML 标签设置 ref 属性,$refs 可以获取 DOM 对象。
- 给子组件设置 ref 属性,渲染后可通过 $refs 获取子组件实例。
<div id="app">
<!-- 给 HTML 标签设置 ref 属性 -->
<input type="text" ref="inp">
<button @click="fn">按钮</button>
<!-- 给子组件设置 ref 属性 -->
<com-a ref="comA"></com-a>
</div>
<script>
var ComA = {
template: `<div>组件A的内容:{{ value }}</div>`,
data () {
return {
value: '示例内容'
}
}
}
var vm = new Vue({
el: '#app',
methods: {
fn () {
// 点击后修改 HTML 标签焦点状态
this.$refs.inp.focus();
this.$refs.comA.value = '新的内容';
}
},
components: {
ComA
},
mounted () {
console.log(this.$refs);
this.$refs.comA.value = "修改后的内容";
},
});
</script>