组件和插槽(vue中)

229 阅读2分钟

1.父组件与子组件

组件中引用其他组件,那么本组件就是父组件,被引用的组件就是子组件,可以层层嵌套;

1.1 组件的引入

在组件中使用其他组件前,是要将要使用的组件引入,在vue中有全局引入和局部引入两种方式;

  1. 全局引入
  • 在项目根目录下的main.js(也就是项目的入口文件,一般文件名都是main.js)中引入;
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

// 以下两句代码就是引入组件CompB的,这样CompB就可以在全局使用
import CompB from './CompB';
Vue.component(CompB.name,CompB)


// Root
new Vue({
  //  createElement方法
  render: h => h(App), // 接收Vue组件的选项配置,创建一个组件内容,并渲染
}).$mount('#app')
  1. 局部引用
  • 就是在父组件中引入子组件,这样引入的子组件只能在当前父组件中使用;
  • import后还需要在components字段默认导出;
<script>
// 1. 引入一个组件
import CompA from './CompA'

export default {
  name: 'App',
  components: {
    CompA
  }
}
</script>
1.2 父子组件数据流问题
  • 一般框架都遵循单向数据流规则,即数据由父组件传递到子组件,不允许子组件修改父组件的值;
  • 看一个数据流变更问题,在父组件App.vue中引入子组件CompC,传递数组outterArr到CompC,想在CompC实现数组数据的修改;
<template>
  <div id="app">
    <h2>数据流的变更问题</h2>
    <CompC :arr="outterArr" :changeFn="changeArr"/>
  </div>
</template>

<script>
// 1. 引入一个组件
import CompA from './CompA'

// 数据变更的问题
import CompC from './CompC';


export default {
  name: 'App',
  components: {
    CompC
  }
}
</script>
  • 此时的CompC
<template>
    <div>
        展示数据:
        <ul>
            <li v-for="item in arr">
                {{ item }}
            </li>
        </ul>
    </div>
</template>

<script>
export default {
    props: {
        arr: {
            type: Array,
        }
    }
}
</script>
  1. 方法一:直接覆盖覆盖父组件的数据(可以实现效果,但是不建议,而且会有一个vue发出的警告)
<template>
    <div>
        展示数据:
        <ul>
            <li v-for="item in arr">
                {{ item }}
            </li>
        </ul>

        <button @click="change1">更改数据1</button>
    </div>
</template>

<script>
export default {
    methods: {
        change1() {
            this.arr = [];// 不建议覆盖父组件的数据
        },
    props: {
        arr: {
            type: Array,
        }
    }
}
</script>
  1. 方法二:父组件给子组件传递一个可以改变outterArr数组值的方法,子组件调用父的方法,让其自己改

父组件:

<script>
// 数据变更的问题
import CompC from './CompC';

export default {
  methods:{
  //向子组件传递的方法
    changeArr(){
      this.outterArr = [];
    }
  },
  data() {
    return {
      varText: 'Test',
      outterArr:[{id:1},{id:2},{id:3}]
    }
  },
  name: 'App',
  components: {
    CompC
  }
}
</script>

子组件:

<template>
    <div>
        展示数据:
        <ul>
            <li v-for="item in arr">
                {{ item }}
            </li>
        </ul>

        <button @click="change1">更改数据1</button>
        <hr />
        <button @click="change2">更改数据2(遵循单向数据流思想)</button>
    </div>
</template>

<script>
export default {
    methods: {
        change1() {
            this.arr = [];// 不建议覆盖父组件的数据
        },
        change2() {
            this.$attrs.changeFn();// 调用父的方法,让其自己改
        },
    props: {
        arr: {
            type: Array,
        }
    }
}
</script>
  1. 方法三:在子组件件内修改数组内部的值(因为直接修改为空,是修改数组值的地址指向,会报错,但是修改数组内的值并没有改变数组的内存指向,所以不会再有警告)
<template>
    <div>
        展示数据:
        <ul>
            <li v-for="item in arr">
                {{ item }}
            </li>
        </ul>

        <button @click="change1">更改数据1</button>
        <hr />
        <button @click="change2">更改数据2(遵循单向数据流思想)</button>
        <hr />
        <button @click="change3">更改数据3</button>
    </div>
</template>

<script>
export default {
    methods: {
        change1() {
            this.arr = [];// 不建议覆盖父组件的数据
        },
        change2() {
            this.$attrs.changeFn();// 调用父的方法,让其自己改
        },
        change3() {
            this.arr[0].id = 5;
            this.arr[1].id = 15;
            this.arr[2].id = 25;
        }
    },
    props: {
        arr: {
            type: Array,
        }
    }
}
</script>
  1. 方法四:在子组件深拷贝一份,然后修改自己拷贝的这一份的值
<template>
    <div>
        展示数据:
        <ul>
            <li v-for="item in arr">
                {{ item }}
            </li>
        </ul>

        <button @click="change1">更改数据1</button>
        <hr />
        <button @click="change2">更改数据2(遵循单向数据流思想)</button>
        <hr />
        <button @click="change3">更改数据3</button>
        <hr>
        <h2>改变自己的数据</h2>
        {{ selfArr }}
        <button @click="change4">更改数据4</button>

    </div>
</template>

<script>
export default {
    methods: {
        change1() {
            this.arr = [];// 不建议覆盖父组件的数据
        },
        change2() {
            this.$attrs.changeFn();// 调用父的方法,让其自己改
        },
        change3() {
            this.arr[0].id = 5;
            this.arr[1].id = 15;
            this.arr[2].id = 25;
        },
        // 方式4: 单独copy一份
        change4() {
            this.selfArr = []
        }
    },
    data() {
        return {
            selfArr: JSON.parse(JSON.stringify(this.arr))
        }
    },
    props: {
        arr: {
            type: Array,
        }
    }
}
</script>

2. 插槽(slot标签)

2.1 使用全局组件来测试插槽

有以下4中使用方式:

  1. 默认插槽的内容#default;
  2. #content1="scope",然后取scope对象中的值scope.num1;
  3. #content2="{ num2 }",用{}取变量值;
  4. #content3="{ num1, num2, num3 },一次性传递多个变量值;
  • 父组件App.vue(CompB组件在项目根目录下的main.js中全局引用了)
<template>
  <div id="app">
    <!-- 使用全局组件来测试插槽 -->
    <CompB :data="[1]">
    // 1. 默认插槽的内容#default
      <template #default>
        <button>默认插槽的内容(default可以省略template)</button>
      </template>
      <template #content1="scope">
       // 2. #content1="scope",然后取scope对象中的值scope.num1
        <button>默认插槽的内容1:{{ scope.num1 }}</button>
      </template>
      <template #content2="{ num2 }">
      // 3. #content2="{ num2 }",用{}取变量值
        <button>默认插槽的内容2:{{ num2 }}</button>
      </template>
      // 4. #content3="{ num1, num2, num3 },一次性传递多个变量值
      <template #content3="{ num1, num2, num3 }">
        <button>默认插槽的内容3:{{ num1 }} {{ num2 }} {{ num3 }}</button>
      </template>
    </CompB>
  </div>
</template>
  • 子组件CompB(接收参数的格式要与父组件传递参数的方式相结合)
<template>
    <div>
        
        <h2>传递进来的插槽slot</h2>
        <div style="background:red">
        // 1. 默认插槽的内容#default
           <slot name="default"></slot>
        </div>

        <div v-for="item in data">
            // 2. #content1="scope",然后取scope对象中的值scope.num1
            <slot name="content1" :num1="item" ></slot>
            <br/>
            // 3. #content2="{ num2 }",用{}取变量值
            <slot name="content2"  :num2="item" ></slot>
            <br/>
            // 4. #content3="{ num1, num2, num3 },一次性传递多个变量值
            <slot name="content3" v-bind="{num1:1,num2:2,num3:3}"  ></slot>
        </div>
    </div>
</template>

<script>
export default {
    props:{
        data:{
            type:Array,
            default:()=>[]
        }
    },
    name:'CompB'
}
</script>