跳至主要內容

04:组件化开发

三思原创大约 10 分钟前端vue前端vuejs

本文介绍了Vue中组件化开发的基本概念和注册组件的步骤,包括全局组件和局部组件的区别,以及如何使用组件构造器对象来创建和注册组件。通过示例演示了如何在Vue应用中使用组件实现页面模块化开发。

image
image

Vue自学笔记4:组件化开发

本文章为观看B站codewhy老师教学视频自学vuejs过程中记录的笔记,对视频进行了清晰的整理。

(帮大家节省自己整理笔记的时间啦,剩下的时间一定要去上机实操,多多练习!!)

ps:最全最新Vue、Vuejs教程,从入门到精通_哔哩哔哩_bilibiliopen in new window(附上视频链接!)

三、组件化开发

1. 认识组件化

1.1 什么是组件化?

1.2 Vue组件化思想

2. 注册组件

2.1 注册组件的基本步骤

 <body>
     <div id="app">
         <!-- 3.使用组件 -->
         <my-cpn></my-cpn>
         <my-cpn></my-cpn>
         <my-cpn></my-cpn>
     </div>
 
     <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js"></script>
 
     <script>
         // 1.创建组件构造器对象
         const cpnC = Vue.extend({
             template: `
                 <div>
                 <h2>我是标题</h2>
                 <p>我是内容</p>
                 <p>我是内容</p>
                 </div>`
         })
     
         // 2.注册组件
         Vue.component('my-cpn', cpnC)
     
         const app  = new Vue({
             el: "#app",
             data: {
 
             }
         })
     </script>
 </body>

2.2 注册组件步骤解析

第三步的解析

3. 组件其他补充

3.1 全局组件和局部组件(局部组件最常用)

全局组件:

 <body>
     <div id="app">
         <!-- 3.使用组件 -->
         <cpn></cpn>
         <cpn></cpn>
         <cpn></cpn>
     </div>
 
     <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js"></script>
 
     <script>
         // 1.创建组件构造器对象
         const cpnC = Vue.extend({
             template: `
                 <div>
                 <h2>我是标题</h2>
                 <p>我是内容</p>
                 <p>我是内容</p>
                 </div>`
         })
 
         // 2.注册组件(全局组件,意味着可以在多个Vue的实例下面使用)
         Vue.component('cpn', cpnC)
 
         const app= new Vue({
             el: "#app",
             data: {
             }
         })
     </script>
 </body>

局部组件(用的最多):

 <body>
     <div id="app">
         <!-- 3.使用组件 -->
         <cpn></cpn>
         <cpn></cpn>
         <cpn></cpn>
     </div>
 
     <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js"></script>
 
     <script>
         // 1.创建组件构造器对象
         const cpnC = Vue.extend({
             template: `
                 <div>
                 <h2>我是标题</h2>
                 <p>我是内容</p>
                 <p>我是内容</p>
                 </div>`
         })
 
         const app = new Vue({
             el: "#app",
             data: {
             },
             //2.注册组件
             components: {
                 //cpn是使用组件时的标签名
                 cpn: cpnC
             }
         })
     </script>
 </body>

3.2 父组件和子组件

 <body>
     <div id="app">
         <cpn2></cpn2>
     </div>
     <script>
         // 1.创建第一个组件
         const cpnC1 = Vue.extend({
             template: `
                 <div>
                 <h2>我是组件1</h2>
                 <p>我是内容</p>
                 <p>我是内容</p>
                 </div>`
         })
 
         // 2.创建第二个组件,作为第一个组件的父组件
         const cpnC2 = Vue.extend({
             template: `
                 <div>
                 <h2>我是组件2</h2>
                 <p>我是内容</p>
                 <p>我是内容</p>
                 <cpn1></cpn1>
                 </div>`,//注册子组件
             components:{
                 cpn1:cpnC1
             }
             //组件2是组件1的父组件
         })
 
         const app = new Vue({
             el: "#app",
             data: {
 
             },//3.注册组件
             components: {
                 cpn2: cpnC2,
             }
         })
     </script>
 </body>

ps:

其实Vue的实例对象app也是一个组件,被称为根组件(root)

3.3 注册组件语法糖

 <body>
     <div id="app">
         <cpn1></cpn1>
         <cpn2></cpn2>
     </div>
     <script>
 
         //注册全局组件(语法糖)  
         Vue.component('cpn1', {
             template: `
                 <div>
                     <h2>
                         hello,world
                     </h2>
                     <p>i'm content1111</p>
                 </div>
             `
         })
 
         const app = new Vue({
             el: "#app",
             data: {
             },
             //注册局部组件(语法糖)
             components: {
                 cpn2: {
                     template: `
                         <div>
                             <h2>
                                  hello,world
                             </h2>
                              <p>i'm content222</p>
                         </div>
                     `
                 }
             }
         })
     </script>
 </body>

3.4 模板的分离写法

Vue提供了两种方案来定义HTML模块内容:使用<script>​标签 和 使用<template>​标签

 <body>
     <div id="app1">
         <cpn1></cpn1>
         <cpn2></cpn2>
     </div>
 
     <!-- 方法一:script标签,注意:类型必须是text-template -->
     <!-- <script type="text/x-template" id="cpn1">
         <div>
             <h2>i'm title</h2>
             <p>i'm content</p>
         </div>
     </script> -->
 
     <!-- 方法二:template标签 -->
     <template id="cpn-tem">
         <div>
             <h2>i'm title</h2>
             <p>i'm content</p>
         </div>
     </template>
 
 
     <script>
         //注册全局组件
         Vue.component('cpn1', {
             template: '#cpn-tem'
         })
 
         const app = new Vue({
             el: '#app1',
             data: {
 
             },//注册局部组件
             components: {
                 cpn2: {
                     template:'#cpn-tem'
                 }
             }
         })
     </script>
 </body>

4. 组件数据存放

4.1 组件可以访问Vue实例数据吗?

4.2 组件数据的存放

 <body>
     <div id="app1">
         <cpn></cpn>                           //3.调用组件
     </div>
   
     <template id="cpn1">                      //1.组件模板
         <div>
             <h2>{{title}}</h2>              
             <p>i'm content</p>
         </div>
     </template>
 
     <script>
         //组件中的数据存放在这
         Vue.component('cpn', {                //2.注册组件
             template: '#cpn1',
             data(){
                 return {
                     title:'我是组件的数据'
                 }
             }
         })
 
         const app = new Vue({
             el: '#app1',
             data: {
 
             }
         })
     </script>
 </body>

为什么是一个函数呢?

(详见视频教程https://www.open in new windowbilibili.com/video/BV15open in new window741177Eh?p=58&vd_source=507e428c4f7818b4cbe408245176625eopen in new window)

如果#app1中多次调用cpn组件产生多个组件实例化对象,因为使用是函数,所以返回每个对象的数据之间是不互通的,是相互独立的.

5. 父子组件的通信

5.1 父级向子级传递

1) props基本用法

prop是property的缩写,意思是:属性

 <body>
     <!-- 父组件模板 -->
     <div id="app">
         <!-- 把:cmessage="message"删掉,就会显示默认值 -->
         <cpn :cmovies="movies" :cmessage="message"></cpn>
     </div>
 
     <!-- 子组件模板 -->
     <template id="cpn">
         <div>
             <p>{{cmovies}}</p>
             <h2>{{cmessage}}</h2>
         </div>
     </template>
 
     <script>
         //子组件
         const cpn = {
             template: '#cpn',
             //方式一:
             // props: ['cmovies', 'cmessage'],
 
             //方式二:
             props: {
                 //2.1.类型限制
                 // cmovies: Array,
                 // cmessag: String
 
                 //2.2.提供一些默认值
                 cmovies: {
                     type: Array,
                     default(){
                         return ['a','b','c']
                     }//类型时数组,对象时,默认值必须是一个函数
                 },
                 cmessage: {
                     type: String,
                     default: 'default message',
                     require: true
                     //require表示,在使用cmessage时,必须传值(传值指在使用cpn组件标签里面的:cmessage=""),否则报错
                 }
             },
             data() {
                 return {}
             },
             methods: {}
         }
 
         //父组件
         const app = new Vue({
             el: "#app",
             data: {
                 message: 'hello,world',
                 movies: ['lalaland', 'star-war', 'her']
             },
             components: {
                 cpn
             }
         })
     </script>
 </body>

解读:

5.2 子级向父级传递

通过自定义事件来完成

 <body>
     <!-- 父组件模板 -->
     <div id="app">
         <!-- 监听事件item_click -->
         <!-- cpn_click未填写参数,默认传送发射事件的第二个参数,既item对象 -->
         <cpn @item_click="cpn_click"></cpn>
     </div>
 
     <!-- 子组件模板 -->
     <template id="cpn_tem">
         <div>
             <button v-for="item in categories" @click="btn_click(item)">{{item.name}}</button>
         </div>
     </template>
 
     <script>
         //子组件
         const cpn = {
             template: '#cpn_tem',
             data() {
                 return {
                     categories: [
                         { id: 'aa', name: '热门推荐' },
                         { id: 'bb', name: '手机数码' },
                         { id: 'cc', name: '家用家电' },
                         { id: 'dd', name: '电脑办公' }
                     ]
                 }
             },
             methods:{
                 btn_click(item){
                     // 发射事件:自定义事件item_click
                     this.$emit('item_click',item)
                 }
             }
         }
 
         //父组件
         const app = new Vue({
             el: "#app",
             data: {
 
             },
             components: {
                 cpn
             },
             methods:{
                 cpn_click(item){
                     console.log('父组件接收到了子组件传来的数据:'+item.name);
                 }
             }
         })
     </script>
 </body>
 

解读:

5.3 组件通信-父子组件通信案例

视频链接:https://www.open in new windowbilibili.com/video/BV15open in new window741177Eh?p=64&spm_id_from=pageDriver&vd_source=507e428c4f7818b4cbe408245176625eopen in new window

目的: input框中输入的数据,与子组件props(既cnum),data(既dnum)中的变量都实现捆绑,并且与父组件传来的变量(既num)也实现捆绑

 <body>
     <div id="app">
         <cpn :cnum1="num1" :cnum2="num2" @num1change="appnum1change" @num2change="appnum2change"></cpn>
         <h2>num1:{{num1}}</h2>
         <h2>num2:{{num2}}</h2>
     </div>
 
     <template id="cpn_tem">
         <div>
             <h2>cnum1:{{cnum1}}</h2>
             <h2>dnum1:{{dnum1}}</h2>
             <input type="number" :value="dnum1" @input="dnum1Input">
 
             <h2>cnum2:{{cnum2}}</h2>
             <h2>dnum2:{{dnum2}}</h2>
             <input type="number" :value="dnum2" @input="dnum2Input">
         </div>
     </template>
 
     <script>
         const app = new Vue({
             el: '#app',
             data: {
                 num1: 1,
                 num2: 2
             },
             methods: {
                 appnum1change(value) {
                     this.num1 = value
                 },
                 appnum2change(value) {
                     this.num2 = value
                 }
             },
             components: {
                 cpn: {
                     template: '#cpn_tem',
                     props: {
                         cnum1: Number,
                         cnum2: Number
                     },
                     data() {
                         return {
                             dnum1: this.cnum1,
                             dnum2: this.cnum2
                         }
                     },
                     methods: {
                         dnum1Input(event) {
                             this.dnum1 = event.target.value
                             this.$emit('num1change', this.dnum1)
                         },
                         dnum2Input(event) {
                             this.dnum2 = event.target.value
                             this.$emit('num2change', this.dnum2)
                         }
                     }
                 }
             }
         })
     </script>
 </body>

6.父子组件的访问

6.1 父组件访问子组件

1) $children

不常用,因为一些缺陷(当遇到该父组件使用多次同名子组件时​)

2) $refs(常用)

 <body>
     <div id="app">
         <cpn></cpn>
         <cpn></cpn>
         <cpn ref="aaa"></cpn>
         <button @click="btnClick"></button>
     </div>
 
     <template id="cpn_tem">
         <div>
             我是子组件
         </div>
     </template>
 
     <script>
         const app = new Vue({
             el: '#app',
             data: {
             },
             methods: {
                 btnClick(){
                     // 1. $children
                     // console.log(this.$children);
                     // console.log(this.$children[0]);
                     // console.log(this.$children[0].name);
                 
                     // 2.$refs
                     console.log(this.$ref.aaa);
                 }
             },
             components: {
                 cpn: {
                     template: '#cpn_tem',
                     props: {
                     },
                     data() {
                         return {
                             name:'我是子组件的name'
                         }
                     },
                     methods: {
                        
                     }
                 }
             }
         })
     </script>
 </body>

6.2子组件访问父组件

1)$parent

访问父组件

不常用,因为耦合度太高

 <body>
     <div id="app">
         <cpn></cpn>
     </div>
 
     <template id="cpn_tem">
         <div>
             <h2>我是cpn组件</h2>
             <ccpn></ccpn>
         </div>
     </template>
 
     <template id="ccpn_tem">
         <div>
             <h2>我是ccpn组件</h2>
             <button @click="btnClick">按钮</button>
         </div>
     </template>
     <script>
         const app = new Vue({
             el: '#app',
             data: {},
             methods: {
 
             },
             components: {
                 cpn: {
                     template: '#cpn_tem',
                     data() {
                         return {
                             name:'我是cpn的name'
                         }
                     },
                     components: {
                         ccpn: {
                             template: '#ccpn_tem',
                             methods: {
                                 btnClick() {
                                     // 1.访问父组件$parent
                                     console.log(this.$parent.name);
                                     //子组件ccpn可以通过$parent访问到其父组件cpn的data
                                 }
                             }
 
                         }
 
                     }
                 }
             }
         })
     </script>
 </body>

3.$root

访问根组件

也不常用,因为根组件是vue,而真实开发时vue中很简洁,没什么好访问的

 <body>
     <div id="app">
         <cpn></cpn>
     </div>
     <template id="cpn_tem">
         <div>
             <h2>我是cpn组件</h2>
             <ccpn></ccpn>
         </div>
     </template>
 
     <template id="ccpn_tem">
         <div>
             <h2>我是ccpn组件</h2>
             <button @click="btnClick">按钮</button>
         </div>
     </template>
     <script>
         const app = new Vue({
             el: '#app',
             data: {
                 message:'我是app的message'
             },
             methods: {
 
             },
             components: {
                 cpn: {
                     template: '#cpn_tem',
                     data() {
                         return {
                             name:'我是cpn的name'
                         }
                     },
                     components: {
                         ccpn: {
                             template: '#ccpn_tem',
                             methods: {
                                 btnClick() {
                                     // 2.访问根组件$root
                                     console.log(this.$root.message);
                                 }
                             }
                         }
                     }
                 }
             }
         })
     </script>
 </body>

7. 插槽slot

7.1 前缀知识:编译作用域

7.2 slot的作用

7.2 slot基本使用

 <body>
     <div id="app">
         <cpn><button>按钮</button></cpn>
         <cpn><span>我是插槽里的内容</span></cpn>
         <cpn><i>我也是插槽里的内容</i></cpn>
         <cpn><input type="text" value="请输入内容"></cpn>
     </div>
 
     <template id="cpn_tem">
         <div>
             <h2>我是组件</h2>
             <h1>我下面是插槽</h1>
             <slot></slot>
             <p>------------------</p>
         </div>
     </template>
 
     <script>
         const app = new Vue({
             el: '#app',
             data: {},
             methods: {},
             components: {
                 cpn: {
                     template: '#cpn_tem',
                 }
             }
         })
     </script>
 </body>

7.3 (使用v-slot)具名插槽slot

 <body>
     <div id = "app">
         <cpn><button slot="center">修改中间默认值</button></cpn>
         <cpn><button>修改无名默认值</button></cpn>
     </div>
 
     <template id="cpn_tem">
         <div>
             <slot name="left"><span>左边默认内容</span></slot> 
             <slot name="center"><span>中间默认内容</span></slot>
             <slot name="right"><span>右边默认内容</span></slot>
             <slot><span>无名默认内容</span></slot>
         </div>
     </template>
 
     <script>
         const app = new Vue({
             el:'#app',
             data:{},
             method:{},
             components:{
                 cpn:{
                     template:'#cpn_tem'
                 }
             }
         })
     </script>
 </body>

7.4 编译作用域

image

7.5 (已弃用)作用域插槽

1) 准备

image

2) 使用

8. (已取代slot了)v-slot

8.1 v-slot实现具名插槽

 <body>
     <div id="app">
         <!-- <cpn slot="name1"></cpn> -->
         <cpn v-slot:name1>v-slot修改默认内容</cpn>
     </div>
 
     <template id="cpn_tem">
         <div>
             <slot name="name1"><span>插槽默认内容</span></slot>
         </div>
     </template>
 
     <script>
         const app = new Vue({
             el: '#app',
             data: {},
             method: {},
             components: {
                 cpn: {
                     template: '#cpn_tem'
                 }
             }
 
         })
     </script>
 </body>

8.2 v-slot实现作用域插槽

实现功能:父组件获得子组件数据,将其放在插槽中

 <body>
     <!-- 2.6版本后才能使用 -->
     <div id="app">
         <!-- 未指定具名插槽时,则为无名插槽提供数据 -->
         <!-- <cpn v-slot="message">{{message.msg}}</cpn> -->
 
         <!-- 若子组件有多个具名插槽,想为名叫aaa的具名插槽提供数据 -->
         <cpn v-slot:aaa="message">{{message.msg}}</cpn>
     </div>
 
     <template id="cpn_tem">
         <div>
             <slot :msg="son_data">第一个插槽,默认内容</slot>
             <br>
             <slot name="aaa" :msg="son_data">第二个插槽,默认内容</slot>
         </div>
     </template>
 
     <script>
         const app = new Vue({
             el: '#app',
             data: {},
             method: {},
             components: {
                 cpn: {
                     template: '#cpn_tem',
                     data() {
                         return {
                             son_data: '我是son_data'
                         }
                     }
                 }
             }
 
         })
     </script>
 </body>