Vue基础06组件化实践

149 阅读1分钟

Vue基础06组件化实践

1 组件注册及数据传递,课程列表组件

注意:

  1. 组件模板单根元素
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>购物车</title>
</head>
<body>
    <div id="app">
        <h2>{{title}}</h2>

        <input v-model="course" v-on:keydown.enter="addCourse"> 
        <button @click="addCourse">新增</button>
        
        <course-list :courses="courses"></course-list>
        
        <div>课程总数:{{this.courses.length}}门</div>
    </div>

    <script src="../js/vue.js"></script>

    <script>

        // 课程列表组件
        Vue.component('course-list', {
            data() {
                return {
                    selectedCourse: ''
                }
            },
            props: {
                courses: {
                    type: Array,
                    default: []
                }
            },
            template: `
            <div>
                <div v-for="item in courses" :key="item" 
                    @click="selectedCourse = item" 
                    :style="{backgroundColor: (selectedCourse == item ? '#ddd' : 'transparent')}">
                    {{ item }}
                </div>
            </div>
            `
        })

        // 模拟异步数据调用
        function getCourses() {
            return new Promise(resolve => {
                setTimeout(() => {
                    resolve(['Java课程', 'Web课程', '爬虫课程' ])
                }, 1000)
            })
        }

        const app = new Vue({
            el: '#app',
            data() {
                return {
                    title: '购物车',
                    courses: [],
                    course: ''
                }
            },
            async created() {
                const courses = await getCourses()
                this.courses = courses
            },
            methods: {
                addCourse() {
                    if (this.course == '' || this.courses.indexOf(this.course) > -1) {
                        return
                    }
                    this.courses.push(this.course)
                    this.course = ''
                }
            }
        })
    </script>
</body>
</html>

2 自定义事件及监听,新增课程组件

说明:

  1. course变量由子组件维护(有状态组件)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>购物车</title>
</head>
<body>
    <div id="app">
        <h2>{{title}}</h2>

        <course-add @add-course="addCourse"></course-add>
        
        <course-list :courses="courses"></course-list>
        
        <div>课程总数:{{this.courses.length}}门</div>
    </div>

    <script src="../js/vue.js"></script>

    <script>

        // 课程新增组件
        Vue.component('course-add', {
            data() {
                return {
                    course: ''
                }
            },
            template: `
                <div>
                    <input v-model="course" v-on:keydown.enter="addCourse"> 
                    <button @click="addCourse">新增</button>
                </div>
            `,
            methods: {
                addCourse() {
                    this.$emit('add-course', this.course)
                    this.course = ''
                }
            },
        })

        // 课程列表组件
        Vue.component('course-list', {
            data() {
                return {
                    selectedCourse: ''
                }
            },
            props: {
                courses: {
                    type: Array,
                    default: []
                }
            },
            template: `
            <div>
                <div v-for="item in courses" :key="item" 
                    @click="selectedCourse = item" 
                    :style="{backgroundColor: (selectedCourse == item ? '#ddd' : 'transparent')}">
                    {{ item }}
                </div>
            </div>
            `
        })

        // 模拟异步数据调用
        function getCourses() {
            return new Promise(resolve => {
                setTimeout(() => {
                    resolve(['Java课程', 'Web课程', '爬虫课程' ])
                }, 1000)
            })
        }

        const app = new Vue({
            el: '#app',
            data() {
                return {
                    title: '购物车',
                    courses: []
                }
            },
            async created() {
                const courses = await getCourses()
                this.courses = courses
            },
            methods: {
                addCourse(course) {
                    if (course == '' || this.courses.indexOf(course) > -1) {
                        return
                    }
                    this.courses.push(course)
                }
            }
        })
    </script>
</body>
</html>

3 自定义组件实现双绑,新增课程组件无状态化

说明:

  1. 让组件无状态化
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>购物车</title>
</head>
<body>
    <div id="app">
        <h2>{{title}}</h2>

        <course-add v-model="course" @add-course="addCourse"></course-add>
        <!-- <course-add :value="course" @input="course=$event" @add-course="addCourse"></course-add> -->
        
        <course-list :courses="courses"></course-list>
        
        <div>课程总数:{{this.courses.length}}门</div>
    </div>

    <script src="../js/vue.js"></script>

    <script>

        // 课程新增组件
        Vue.component('course-add', {
            props: ['value'],
            template: `
                <div>
                    <input :value="value" 
                        @input="onInput"
                        v-on:keydown.enter="addCourse"> 
                    <button @click="addCourse">新增</button>
                </div>
            `,
            methods: {
                addCourse() {
                    this.$emit('add-course')
                },
                onInput(e) {
                    this.$emit('input', e.target.value)
                }
            },
        })

        // 课程列表组件
        Vue.component('course-list', {
            data() {
                return {
                    selectedCourse: ''
                }
            },
            props: {
                courses: {
                    type: Array,
                    default: []
                }
            },
            template: `
            <div>
                <div v-for="item in courses" :key="item" 
                    @click="selectedCourse = item" 
                    :style="{backgroundColor: (selectedCourse == item ? '#ddd' : 'transparent')}">
                    {{ item }}
                </div>
            </div>
            `
        })

        // 模拟异步数据调用
        function getCourses() {
            return new Promise(resolve => {
                setTimeout(() => {
                    resolve(['Java课程', 'Web课程', '爬虫课程' ])
                }, 1000)
            })
        }

        const app = new Vue({
            el: '#app',
            data() {
                return {
                    title: '购物车',
                    courses: [],
                    course: ''
                }
            },
            async created() {
                const courses = await getCourses()
                this.courses = courses
            },
            methods: {
                addCourse() {
                    if (this.course == '' || this.courses.indexOf(this.course) > -1) {
                        return
                    }
                    this.courses.push(this.course)
                    this.course = ''
                }
            }
        })
    </script>
</body>
</html>

4 插槽,弹窗组件

通过使用vue的元素可以给组件传递内容

分类:默认插槽、具名插槽、作用域插槽

关闭事件,.sync父组件可以少写一个事件,默认一个@update:show事件

image-20220310220702560.png

image-20220310220830629.png

image-20220310220912313.png

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>购物车</title>
    <style>
        .message-box {
            padding: 10px 20px;
            background: #4fc08d;
            border: 1px solid #42b983;
        }
        .message-box-close {
            float: right;
        }
    </style>
</head>
<body>
    <div id="app">
        <message :show.sync="show">
            <template v-slot:title>
                <strong>恭喜</strong>
            </template>
            <!-- 默认插槽内容 -->
            <template>新增成功</template>
        </message>

        <h2>{{title}}</h2>

        <course-add v-model="course" @add-course="addCourse"></course-add>
        
        <course-list :courses="courses"></course-list>
        
        <div>课程总数:{{this.courses.length}}门</div>
    </div>

    <script src="../js/vue.js"></script>

    <script>

        // 弹窗组件
        Vue.component('message', {
            props: ['show'],
            template: `
            <div class="message-box" v-if="show">
                <!-- 具名插槽 -->
                <slot name="title"></slot>
                <!-- 通过slot插槽获取传入内容 -->
                <slot></slot>
                <span class="message-box-close" @click="$emit('update:show', false)">X</span>
            </div>
            `
        })

        // 课程新增组件
        Vue.component('course-add', {
            props: ['value'],
            template: `
                <div>
                    <input :value="value" 
                        @input="onInput"
                        v-on:keydown.enter="addCourse"> 
                    <button @click="addCourse">新增</button>
                </div>
            `,
            methods: {
                addCourse() {
                    this.$emit('add-course')
                },
                onInput(e) {
                    this.$emit('input', e.target.value)
                }
            },
        })

        // 课程列表组件
        Vue.component('course-list', {
            data() {
                return {
                    selectedCourse: ''
                }
            },
            props: {
                courses: {
                    type: Array,
                    default: []
                }
            },
            template: `
            <div>
                <div v-for="item in courses" :key="item" 
                    @click="selectedCourse = item" 
                    :style="{backgroundColor: (selectedCourse == item ? '#ddd' : 'transparent')}">
                    {{ item }}
                </div>
            </div>
            `
        })

        // 模拟异步数据调用
        function getCourses() {
            return new Promise(resolve => {
                setTimeout(() => {
                    resolve(['Java课程', 'Web课程', '爬虫课程' ])
                }, 1000)
            })
        }

        const app = new Vue({
            el: '#app',
            data() {
                return {
                    title: '购物车',
                    courses: [],
                    course: '',
                    show: false
                }
            },
            async created() {
                const courses = await getCourses()
                this.courses = courses
            },
            methods: {
                addCourse() {
                    if (this.course == '' || this.courses.indexOf(this.course) > -1) {
                        return
                    }
                    this.courses.push(this.course)
                    this.course = ''
                    this.show = true
                    setTimeout(() => {this.show = false}, 3000)
                }
            }
        })
    </script>
</body>
</html>

5 组件化探讨

组件化是Vue的精髓,Vue应用就是由一个个组件构成的。Vue的组件化涉及到的内容,可以从下面几点进行阐述:

  1. 定义:组件是可利用的Vue实例,准确讲它们是VueComponent的实例,继承自Vue
  2. 优点:组件化可以增加代码的利用性、可维护性和可测试性
  3. 使用场景:什么时候使用组件?以下分类可作为参考
    1. 通用组件:实现最基本的功能,具有能用性、可复用性,例如按钮组件、输入框组件、布局组件等
    2. 业务组件:完成具体业务,具有一定的复用性,例如登录组件、轮播组件
    3. 页面组件:组织应用各部分独立内容,需要时在不同页面组件间切换,例如列表页、详情组件
  4. 如何使用组件
    1. 定义(全局、局部):Vue.component(), components选项,sfc(single file component)
    2. 分类:有状态组件(有data的组件),functional(无状态组件也称为函数式组件), abstract(抽象组件,缓存、动画等)
    3. 通信:props, emit()/emit()/on(), provide/inject, children/children/parent/root/root/attrs/$listeners
    4. 内容分发: <slot>, <template>, v-slot
    5. 使用及优化: is, keep-alive, 异步组件
  5. 组件的本质
    1. 组件配置--->VueComponent实例--->render()--->Virtual DOM--->DOM
    2. 所以组件的本质是产生虚拟DOM