vue3基础入门-p06-组件

组件化开发

组件的概念

组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。

在vue中都是组件化开发的,组件化开发就是把一个完整的页面分割成一个一个的小组件,组件包含独立的结构,样式,js

组件的优点:

  • 容易维护
  • 复用

vue组件分为全局组件和局部组件:

  • 全局注册的组件:全局注册的组件可以在应用中的任何组件的模板中使用。
  • 局部注册的组件:局部注册的组件在其子组件中不可用

全局组件与局部组件

  1. 定义全局组件

    • 通过vue实例 app.component(组件名,组件的配置项) 定义一个全局组件
    • 在vue实例的内部,通过components属性来定义局部组件
    • 因为组件是可复用的实例,所以它们与根实例接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。
    • 注意点1. 定义的组件名不能是html原先就存在的标签。
    • 注意点2:组件的模版只能有一个根元素
  2. 使用组件:

    • 把组件当成html标签来使用
<body>
<div id="app">
    <!--把组件名称当成html标签来使用 -->
    <com-a></com-a>
    <hr>
    <com-b></com-b>
</div>

<script src="./node_modules/vue/dist/vue.global.js"></script>

<script>

    // 创建一个Vue 应用,根组件
    const app = Vue.createApp({
        // 在vue实例的内部,通过components属性来定义局部组件
        components: {
            'com-b': {
                template: '<div><h3>我是【局部组件->com-b】</h3><sapn style="color:blue">在局部组件中引用了全局组件->com-a:[<com-a></com-a>]</sapn></div>'
            }
        }
    })

    // 通过vue实例 app.component()方法定义一个名为 com-a 的【全局组件】
    app.component('com-a', {
        // 如果有很多的内容,template可以使用字符串模版
        template: `<div>
            <h3>我是【全局组件->com-a】</h3>
            <span  style="color:blue">在全局组件中引用了局部组件->com-b:[<com-b></com-b>]</span>
            </div>` // 在典型的 Vue 应用中,我们使用单文件组件而不是字符串模板
    })
    const vm = app.mount('#app')
</script>
</body>

组件引用

组件是特殊的vue实例

可以将组件看成是一个vue的实例,因此,在vue实例中能配置的属性,在组件中依旧能够配置。

比如:data,method,watch,computed,钩子函数等

注意:组件中data属性必须是一个函数,返回值才是data的数据

  • 可以把组件看成一个vue实例
  • 组件是一个独立封闭的个体,组件之间的数据是无法相互使用的
<body>
<div id="app">
    <com-a></com-a>
    <com-a></com-a>
</div>

<script src="./node_modules/vue/dist/vue.global.js"></script>

<script>

    // 创建一个Vue 应用,根组件
    const app = Vue.createApp({})

    // 通过vue实例 app.component()定义一个【全局组件】
    // 可以把组件看成一个vue实例,在vue实例中能配置的属性,在组件中依旧能够配置
    app.component('com-a', {
        template: `
          <div>
          <h3>我是【全局组件】</h3>
          <span>{{ respect }}</span>
          <button @click="fn">点赞</button>
          <hr>
          </div>`,
        data() {
            return {respect: 100}
        },
        methods: {
            fn() {
                this.respect += 1
            }
        }
    })
    const vm = app.mount('#app')
</script>
</body>

组件是特殊的vue实例

组件通讯

因为组件是一个独立的个体,组件无法使用到外部的数据

但是在真实开发中,多个组件之间是需要相互使用彼此的数据的,因此需要使用组件通讯的技术,让组件之间能够相互传值。

组件通讯分为两类

  • 父组件传递值给子组件
  • 子组件传递值给父组件

组件通讯-父传子

  1. 子组件通过一个属性: props属性 接收父组件传递过来的数据
  2. 父组件给子组件传递数据:给子组件添加属性即可
<body>
<div id="app">
    <!-- son组件在根组件中渲染了, son组件和根组件就形成了父子关系 -->
   <!-- 父组件给子组件传递数据 -->
    <son class="son" :car="car" :money="money"></son>
</div>

<script src="./node_modules/vue/dist/vue.global.js"></script>

<script>
    // 根组件
    const app = Vue.createApp({
        data() {
            return {
                car: 'BMW',
                money: 1000000
            }
        }
    })
    // son组件
    app.component('son', {
         // props用于接收父组件传递过来的数据
        // props和data一样,都可以给组件提供数据
        // data是组件自己的私有数据  props是父组件传递过来的数据
        props: ['money', 'car'],
        // 子组件使用父组件传递过来的值
        template: `
          <div style="background: pink;border: red solid 1px">
          <h3>我是son组件</h3>
          <p>{{ car }}-----{{ money }}</p>
          </div>`
    })
    const vm = app.mount('#app')
</script>
</body>
  • 父传子-总结:
  1. 子组件通过一个属性: props属性 可以接受父组件传递过来的数据 props:['money', 'car']
  2. 父组件需要给子组件传值 只需要给子组件身上增加自定义的属性 <son money="100" :car="car"></son>
  • 区分props和data
  1. 组件的数据可以是data中,也可以是props中
  2. data是组件自己的数据 props是父组件传过来的数据
  3. data中的数据允许修改,但是props中的数据不允许修改(props是只读的)

注意:props负责获取父组件传递过来的数据,props中的值是只读的,不允许修改

组件通讯-子到父

整体思路

  1. 父组件给子组件注册一个自定义事件,监听子组件事件
  2. 子组件触发这个自定义事件,触发事件时把数据传递给父组件
<body>
<div id="app">
    <h3> 我是父组件:{{name}}----{{price}}</h3>
    <!-- son组件在根组件中渲染了, son组件和根组件就形成了父子关系 -->
    <!--在子组件上,自定义监听事件buycar,等待子组件触发,然后父组件执行响应监听函数getcar-->
    <son  @buycar="getcar"></son>
</div>

<script src="./node_modules/vue/dist/vue.global.js"></script>

<script>
    // 根组件
    const app = Vue.createApp({
        data() {
            return {
                name: '',
                price: ''
            }
        },
        methods:{
            // 父组件监听到子组件事件后,执行相应函数,在这里获取从子组件中传过来的值。
            getcar(car){
                this.name = car.name
                this.price = car.price
            }
        }
    })
    // son组件
    app.component('son', {
        template: `
          <div style="background: pink;border: red solid 1px">
          <h3>我是son组件</h3>
          <button @click="toParent"> 给老父亲买辆车</button>
          </div>`,
        methods:{
            toParent(){
                // 触发父组件所监听的事件buycar,传上相应的值
                this.$emit('buycar',{name:'奥迪Q7',price:'50w'})
            }
        }
    })
    const vm = app.mount('#app')
</script>
</body>

子传父

子传父-总结:

  1. 父组件给子组件注册一个自定义监听事件 <son @buycar="getcar"></son>
  2. 子组件去触发父组件所监听的事件,并且传值 this.$emit('buycar',{name:'奥迪Q7',price:'50w'})
  3. 父组件监听到子组件事件后,执行指定的监听函数,在函数内获得子组件传过来的值

案例-todomvc

slot插槽

当组件中某一项需要单独定义,那么就应该使用solt

插槽:内容分发,会把组件中的内容分发到slot中

  1. 默认插槽,匿名插槽: 在组件占位置
  2. 具名插槽, 给插槽提供一个名字
    如果是具名插槽, 给具名插槽传内容,内容需要包裹在一个template标签中
  3. 作用域插槽: 带有数据的插槽

单个slot

除非子组件模板包含至少一个 <slot> 插口,否则父组件的内容将会被丢弃 ,当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身。

在组件的模版中定义slot插槽

app.component('my-modal', {
  template: `
    <div class="modal">
      <div class="header">
        <h3>温馨提示</h3>
      </div>
      <div class="content">
        <slot></slot>
      </div>
      <div class="footer">
        <button>确定</button>
        <button>取消</button>
      </div>
    </div>
  `
})

父组件传值

<my-modal>你确定要退出系统吗?</my-modal>
<my-modal>你确定要删除这个内容吗?</my-modal>

具名插槽

如果一个组件中想使用多个slot那么此时就应该使用具名slot。

在组件的模版中定义slot插槽

app.component('my-modal', {
    template: `
      <div class="modal">
        <div class="header">
          <slot name="header"></slot>
        </div>
        <div class="content">
          <slot name="content"></slot>
        </div>
        <div class="footer">
            <slot name="footer">
                <button>确定</button>
                <button>取消</button>
            </slot>
        </div>
      </div>
    `
})

父组件传值

给具名插槽传内容,内容需要包裹在一个template标签中, 通过v-slot:插槽名#插槽名 指定插槽

<my-modal>
    <template v-slot:header>
        <h3>温馨提示</h3>
    </template>
    <template v-slot:content>
        <p>你确定要删除吗?</p>
    </template>
</my-modal>

<my-modal>
    <template #header>
        <h3>警告</h3>
    </template>
    <template #content>
        <p>你确定要删除吗?</p>
    </template>
    <template #footer>
        <a href="#">确定</a>
    </template>
</my-modal>

具名插槽

作用域插槽

场景: 在父组件中获取插槽上的值

<body>
<div id="app">
    <todo-list :todos="todos_list">
        <template #delete="scope">
            <button @click="del(scope.todo_id)">删除</button>
        </template>
    </todo-list>
</div>

<script src="./node_modules/vue/dist/vue.global.js"></script>

<script>

    // 创建一个Vue 应用,根组件
    const app = Vue.createApp({
        data() {
            return {
                todos_list: [
                    {id: 1, name: '吃饭', flag: true},
                    {id: 2, name: '喝酒', flag: false},
                    {id: 3, name: '唱歌', flag: false}
                ]
            }
        },
        methods: {
            del(id) {
                this.todos_list = this.todos_list.filter(item => item.id != id)
            }
        }
    })

    // 通过vue实例 app.component()定义一个【全局组件】
    app.component('todo-list', {
        template: `
          <ul>
          <li v-for="item in todos" :key="item.id"><span>{{ item.name }}</span>
            <slot name="delete" :todo_id="item.id"></slot>
          </li>
          </ul>
        `,
        props: ['todos']
    })
    const vm = app.mount('#app')
</script>
</body>

解析:

  1. 在组件模板中,插槽定义<slot name="delete" :todo_id="item.id"></slot> 中动态绑定属性todo_id
  2. 在父组件中,插入插槽内容时 ,给插槽添加一个作用域对象,如 #delete="scope",scope是一个对象(名称自定义,可以不是scope),这个对象中会包含插槽中所有的数据
    在这个例子中,scope.todo_id 就可以获取对应点击的todo_id

作用域插槽

vue学习总结

vue学习总结


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。
My Show My Code