在进入这个话题之前, 首先我们先来想一下在 vue 里, 如何写一个父子组件. 为了简单起见, 下面的代码我都没用脚手架来构建项目, 直接在 html 文件里引入 vue.js 来作为例子. 父子组件的写法如下:
- <div id="app">
- <parent>
- </parent>
- </div>
- <script src="node_modules/vue/dist/vue.js">
- </script>
- <script>
- let childNode = {
- template: ` < div > childNode < /div>`
- }
- let parentNode = {
- template: `
- <div>
- <child></child > </div>
- `,
- components: {
- child: childNode
- }
- }
- new Vue({
- el: '#app',
- components: {
- parent: parentNode
- }
- })
- /
- </script>
在这个代码里, template 表示的是模板, components 表示的是组件. 子组件先要在父组件里显示就要通过 components 把组件对应的模板引擎 (childNode 对象) 给挂载到父组件的 components 上去. 然后子组件就会在父组件上得以显示.
在父子组件中, 如果想要父组件的变量传递到子组件中, 则需要通过 props 属性来进行传递. props 有静态的也有动态的, 下面来介绍静态的写法:
- <script>
- let childNode = {
- template: ` < div > {
- {
- message
- }
- } < /div>`,
- props: ['message']
- }
- let parentNode = {
- template: `
- <div>
- <child message="child"></child > </div>
- `,
- components: {
- child: childNode
- }
- }
- new Vue({
- el: '#app',
- components: {
- parent: parentNode
- }
- })
- /
- </script>
在这里, 子组件通过在 props 里写入想要获取的父组件的某个属性值, 在父组件里, 通过父组件在子组件里的占位符添加属性的方式来传值. 在这里有一个命名规范, 在子组件里, 如果 props 里的参数由多个词组成则应该用驼峰命名的写法; 而在父组件中则用横线做分隔符的写法. 如:
- <script>
- let childNode = {
- template: ` < div > {
- {
- myMessage
- }
- } < /div>`,
- props: ['myMessage']
- }
- let parentNode = {
- template: `
- <div>
- <child my-message="child"></child > </div>
- `,
- components: {
- child: childNode
- }
- }
- new Vue({
- el: '#app',
- components: {
- parent: parentNode
- }
- })
- /
- </script>
而动态 props 的用法是将占位符进行绑定, 将父组件的数据绑定到子组件的 props 中, 可以实现父组件的数据发生改变的时候子组件的数据也会发生改变的动态效果. 如:
- <script>
- let childNode = {
- template: ` < div > {
- {
- myMessage
- }
- } < /div>`,
- props: ['myMessage']
- }
- let parentNode = {
- template: `
- <div>
- <input type="text" v-model="myData">
- <div>
- <child :my-message="child"></child > </div>
- </div > `,
- components: {
- child: childNode
- },
- data() {
- return {
- child: '',
- myData: ''
- }
- },
- watch: {
- myData(newValue, oldValue) {
- this.child = newValue
- }
- }
- }
- new Vue({
- el: '#app',
- components: {
- parent: parentNode
- }
- })
- </script>
在这里, 我通过利用 v-model 和 watch 来实现输入框发生变化的时候父组件数据动态修改后子组件的数据跟着改变. 为什么这里不用 computed 而用 watch 呢? 这里其实也涉及到 watch 和 computed 的区别, 在 vue 里, 这两个方法都能监听数据的变化, 不同的是 computed 是只能读取不能写入, 而 watch 可以读取也可以写入. 当父组件的 myData 这个值发生改变的时候就将 myData 的值不断赋值给 child, 由于对占位符进行了绑定, 所以子组件能够接收到父组件的改变.
动态 props 和静态 props 除了上面的区别以外还有一个区别, 就是传递参数的时候数据类型发生变化. 如下面的静态 props 里
- <script>
- let childNode = {
- template: ` < div > {
- {
- myMessage
- }
- }的类型是 {
- {
- type
- }
- } < /div>`,
- props: ['myMessage'],
- computed: {
- type() {
- return typeof this.myMessage
- }
- }
- }
- let parentNode = {
- template: `
- <div>
- <div>
- <child my-message="1"></child > </div>
- </div > `,
- components: {
- child: childNode
- }
- }
- new Vue({
- el: '#app',
- components: {
- parent: parentNode
- }
- })
- </script>
我们传的 1 在子组件里却是 string 类型, 而如果是加入了绑定变成动态 props:
- <div>
- <child :my-message="1">
- </child>
- </div>
则会变成 nunber, 这种情况除了在数字类型发生以外, 其他类型也会发生, 需要注意.
在 props 里, 我们可以验证父组件传过来的参数是否有问题, 下面的例子主要是验证父组件传过来的数据是否是 Number 类型, 如果不是的话则会在控制台里看到报错. 在用这个方法的时候不要引用 vue.min.JS 而要引用 vue.JS, 因为 vue.min.JS 会省略掉相应的报错提示, 不利于开发的时候查看:
- <script>
- let childNode = {
- template: ` < div > {
- {
- myMessage
- }
- } < /div>`,
- props: {
- myMessage: Number
- }
- }
- let parentNode = {
- template: `
- <div>
- <input type="text" v-model="myData">
- <div>
- <child :my-message="child"></child > </div>
- </div > `,
- components: {
- child: childNode
- },
- data() {
- return {
- child: 123,
- myData: ''
- }
- },
- watch: {
- myData(newValue, oldValue) {
- if (/^[0-9]*$/.test(newValue)) {
- this.child = parseInt(newValue)
- } else {
- this.child = '输入的数据不是 number 类型'
- }
- }
- }
- }
- new Vue({
- el: '#app',
- components: {
- parent: parentNode
- }
- })
- </script>
可以看到我是在 props 里添加了一个对象, 该对象里的 myMessage 属性里有属性值 Number, 表示验证数字类型, 当 myMessage 的值不是数字类型的时候就会报错. 除了 Number 的验证规则以外还有 String,Boolean,Function,Object,Array 和 Symbol 等验证规则, 同时也可以写成这样来验证多种类型:
- props: {
- myMessage: [Number, String]
- }
或者可以通过一个自定义一个工厂函数来进行匹配, 如:
- props: {
- myMessage: {
- validator: function (value) {
- return value> 10
- }
- }
- }
当然, 在 props 对象里面, 还有另外三个属性, 一个是 default 属性, 表示的是父组件传入值的时候子组件默认的值, 另一个是 require, 表示的是该值是否要传入, 而 type 则表示的是验证规则. 如:
- <script>
- let childNode = {
- template: ` < div > {
- {
- myMessage
- }
- } < /div>`,
- props: {
- myMessage: {
- type: Number,
- / / required: true,
- default:
- 321
- }
- }
- };let parentNode = {
- template: ` < div > <div > <child > </child>
- </div > </div>
- `,
- components: {
- child: childNode
- },
- data() {
- return {
- child: 123
- }
- }
- };
- new Vue({
- el: '#app',
- components: {
- parent: parentNode
- }
- })
- /
- </script>
一般来说, 当 props 里的 require 表示为 true 的时候, 则父组件要加上占位符, 因为父组件有传值过来, 所以 default 的值被覆盖了, 如:
- <script>
- let childNode = {
- template: ` < div > {
- {
- myMessage
- }
- } < /div>`,
- props: {
- myMessage: {
- type: Number,
- required: true,
- default: 321
- }
- }
- };
- let parentNode = {
- template: `
- <div>
- <div>
- <child :my-message="child"></child > </div>
- </div > `,
- components: {
- child: childNode
- },
- data() {
- return {
- child: 123
- }
- }
- };
- new Vue({
- el: '#app',
- components: {
- parent: parentNode
- }
- })
- </script>
由于 props 是单向数据流, 所以父组件传过来的数据子组件修改了也不会修改到父组件的数据, 而父组件一修改了则会修改到子组件的数据. 所以想要反过来将子组件的数据传到给父组件则要换一种方法. 用的比较多的是自定义一个事件, 如:
- <script>
- let childNode = {
- template: ` < div > <button@click = "toParent" > 子传父 < /button>
- </div > `,
- methods: {
- toParent() {
- this.$emit('myParent', 'hello')
- }
- }
- };
- let parentNode = {
- template: ` < child@myParent = "change": message = "message" > </child>
- `,
- components: {
- 'child': childNode
- },
- methods: {
- change(data) {
- console.log(data)
- }
- }
- };
- new Vue({
- el: '#app',
- components: {
- parent: parentNode
- }
- })
- /
- </script>
在这里, 子组件通过比如点击事件来触发一个自定义事件, 该事件里有 this.$emit 的处理函数, 第一个参数表示的是父组件里负责监听的函数名, 第二个参数表示的传递的数值. 在父组件里, 通过在子组件占位符绑定一个子组件 this.$emit 定义的方法名即可监听得到传入的参数.
详细讲解 vue.JS 里的父子组件通信(props 和 $emit)
来源: http://www.bubuko.com/infodetail-3102218.html