Vue组件

一. 组件化开发思想

1.1 现实中的组件化思想体现

  • 标准
  • 分治
  • 重用
  • 组合

1.2 编程中的组件化思想体现

在这里插入图片描述

1.3 组件化规范: Web Components

  • 我们希望尽可能多的重用代码
  • 自定义组件的方式不太容易(html、css和js)
  • 多次使用组件可能导致冲突

Web Components 通过创建封装好功能的定制元素解决上述问题

规范网址:https://developer.mozilla.org/zh-CN/docs/Web/Web_Components

Vue部分实现了上述规范

二. 组件注册

2.1 全局组件注册语法

Vue.component(组件名称, {  
	data: 组 件 数 据 ,  
	template: 组件模板内容
})

案例:

// 注册一个名为 button-counter 的新组件
Vue.component('button-counter', {  
	data: function () {
		return {
			count: 0;
		}
	},
	template: '<button v-on:click="count++">点击了{{ count }}次.</button>'
})

2.2 组件用法

<div id="app">
	<button-counter></button-counter>
</div>
<div id="app">
	<button-counter></button-counter>
	<button-counter></button-counter>
	<button-counter></button-counter>
</div>

2.3 组件注册注意事项

1.data必须是一个函数

  • 分析函数与普通对象的对比

2.组件模板内容必须是单个跟元素

  • 分析演示实际的效果

3.组件模板内容可以是模板字符串

  • 模板字符串需要浏览器提供支持(ES6语法
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>组件注册</title>
</head>

<body>
    <div id="app">
        <!-- 组件可以重用,每个组件都是独立的实例,且每个组件的数据都是独立的,相互不影响。 -->
        <button-counter></button-counter>
        <button-counter></button-counter>
        <button-counter></button-counter>
    </div>
    <script src="./js/vue.js"></script>
    <script type="text/javascript">
        Vue.component('button-counter', {
            data: function() {
                // 这里构成了一个闭包的环境,使每个组件的数据都是独立的。
                return {
                    count: 0
                }
            },
            // 组件模板内容必须是单个跟元素
            // template: `
            //	  <button @click="handle">点击了{{count}}次</button>
            //    <button>按钮</button>
            // `,               ------错误
            // template: `
            //	<div>
            //		<button @click="handle">点击了{{count}}次</button>
            //         <button>按钮</button>
        	//	</div>`,        ------正确
            // 组件模板内容可以是模板字符串
            template: `
                <div>
                    <button @click="handle">点击了{{count}}次</button>
                    <button>测试</button>
                </div>
            `,
            methods: {
                handle: function() {
                    this.count += 2;
                }
            }
        });
        // 其实下列实例对象也是一个组件
        var vm = new Vue({
            el: '#app',
            data: {},
        });
    </script>
</body>

</html>

4. 组件命名方式

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>组件注册</title>
</head>

<body>
    <div id="app">
        <button-counter></button-counter>
        <hello-world></hello-world>
    </div>
    <script src="./js/vue.js"></script>
    <script type="text/javascript">
        // 注意 
        // 如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方法使用组件,
        // 但是在普通的标签模板中,必须使用短横线的方法只用组件。
        
        // 驼峰方式
        Vue.component('HelloWorld', {
            data: function() {
                return {
                    msg: 'Good Vue',
                };
            },
            template: `<div>{{msg}}</div>`
        });
        // 短横线方式
        Vue.component('button-counter', {
            data: function() {
                return {
                    count: 0
                }
            },
            template: `
                <div>
                    <button @click="handle">点击了{{count}}次</button>
                    <button>测试</button>
                    <HelloWorld></HelloWorld>
                </div>
            `,
            methods: {
                handle: function() {
                    this.count += 2;
                }
            }
        });
        var vm = new Vue({
            el: '#app',
            data: {},
        });
    </script>
</body>

</html>

2.4 局部组件注册

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>定义局部组件</title>
</head>

<body>
    <div id="app">
        <hello-vue></hello-vue>
        <hello-duan></hello-duan>
        <hello-js></hello-js>
        <!-- <text-box></text-box> -->
    </div>
    <script src="./js/vue.js"></script>
    <script type="text/javascript">
        // Vue.component('text-box', {
        //     template: `
        //         <div>
        //             <span>text</span>
        //             <HelloDuan></HelloDuan>
        //             <hello-duan></hello-duan>
        //         </div>
        //     `
        // });
        var HelloVue = {
            data: function() {
                return {
                    msg: 'Hello Vue'
                };
            },
            template: '<div>{{msg}}</div>'
        };
        var HelloDuan = {
            data: function() {
                return {
                    msg: 'Hello Duanxx'
                };
            },
            template: '<div>{{msg}}</div>'
        };
        var HelloJS = {
            data: function() {
                return {
                    msg: 'Hello JS'
                };
            },
            template: '<div>{{msg}}</div>'
        };
        var vm = new Vue({
            el: '#app',
            data: {},
            components: {
                'hello-vue': HelloVue,
                'hello-duan': HelloDuan,
                'hello-js': HelloJS,
            }
        })
    </script>
</body>

</html>

三. Vue调试工具

3.1 调试工具安装

  • 方法1

    ① 克隆仓库

    ② 安装依赖包

    ③ 构建

    ④ 打开Chrome扩展页面

    ⑤ 选中开发者模式

    ⑥ 加载已解压的扩展,选择shells/chrome

  • 方法2

    在这里插入图片描述

四. 组件间数据交互

4.1 父组件向子组件传值

1. 组件内部通过props接收传递过来的值

Vue.component('menu-item', {  
	props: ['title'],
	template: '<div>{{ title }}</div>'
});

2. 父组件通过属性将值传递给子组件

<menu-item title="来自父组件的数据"></menu-item>
<menu-item :title="title"></menu-item>

案例

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>父组件向子组件传值</title>
</head>

<body>
    <div id="app">
        <div>{{pmsg}}</div>
        <menu-item title="来自父组件中的内容"></menu-item>
        <menu-item :title="omsg" content="段小小"></menu-item>
    </div>
    <script src="./js/vue.js"></script>
    <script type="text/javascript">
        Vue.component('menu-item', {
            props: ['title', 'content'],
            data: function() {
                return {
                    msg: '子组件本身数据',
                }
            },
            template: `<div>{{msg + "----" + title + "----" + content}}</div>`,
        })
        var vm = new Vue({
            el: '#app',
            data: {
                pmsg: '父组件中的内容',
                omsg: '动态绑定属性'
            },
        })
    </script>
</body>

</html>

3.props属性名规则

  • 在props中使用驼峰形式,模板中需要使用短横线的形式
  • 字符串形式的模板中没有这个限制
Vue.component('menu-item', {
	// 在 JavaScript 中是驼峰式的
	props: [‘menuTitle'],
	template: '<div>{{ menuTitle }}</div>'
});
<!– 在html中是短横线方式的 -->
<menu-item menu-title="nihao"></menu-item>

案例

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>props命名规范</title>
</head>

<body>
    <div id="app">
        <div>{{pmsg}}</div>
        <!-- 在props中使用驼峰形式,模板中需要使用短横线的形式 -->
        <menu-item :menu-title='ptitle'></menu-item>
    </div>
    <script src="./js/vue.js"></script>
    <script type="text/javascript">
        // 三级组件
        Vue.component('third-com', {
            props: ['testTile'],
            template: '<div>{{testTile}}</div>'
        });
        // 二级组件
        Vue.component('menu-item', {
            props: ['menuTitle'],
            // 字符串形式的模板中没有这个限制
            template: '<div>{{menuTitle}}<third-com testTile="Hello Vue"></third-com></div>'
        })
        var vm = new Vue({
            el: '#app',
            data: {
                pmsg: '父组件中的内容',
                ptitle: '动态绑定属性'
            },
        })
    </script>
</body>

</html>

4.props属性值类型

  • 字符串 String
  • 数值 Number
  • 布尔值 Boolean
  • 数组 Array
  • 对象 Object

案例

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>props属性值类型</title>
</head>

<body>
    <div id="app">
        <div>{{pmsg}}</div>
        <!-- 对于 数值类型 和 布尔类型 通过 v-bind 绑定显示原类型,否则显示为 string -->
        <menu-item :pstr="pstr" :pnum="12" :pboo="true" :parr="parr" :pobj="pobj"></menu-item>
    </div>
    <script src="./js/vue.js"></script>
    <script type="text/javascript">
        Vue.component('menu-item', {
            props: ['pstr', 'pnum', 'pboo', 'parr', 'pobj'],
            template: `
                <div>
                    <div>{{pstr}}</div>
                    <div>{{12 + pnum}}</div>
                    <div>{{typeof pboo}}</div>
                    <ul>
                        <li :key='index' v-for='(item,index) in parr'>{{item}}</li>
                    </ul>
                    <div>
                        <span>{{pobj.name}}</span>
                        <span>{{pobj.age}}</span>
                    </div>
                </div>
            `,
        })
        var vm = new Vue({
            el: '#app',
            data: {
                pmsg: '父组件中的内容',
                pstr: 'Hello Vue',
                parr: ['apple', 'banana', 'orange'],
                pobj: {
                    name: '段小小',
                    age: 18
                }
            },
        })
    </script>
</body>

</html>

4.2 子组件向父组件传值

1. 不带参数

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>子传父基本用法</title>
</head>

<body>
    <div id="app">
        <div :style='{fontSize:fontSize + "px"}'>{{pmsg}}</div>
        <!-- 2.	父组件监听子组件的事件 -->
        <menu-item :parr='parr' @enlarge-text='handle'></menu-item>
        <!-- <menu-item v-on:enlarge-text=事件对应的处理逻辑'></menu-item> -->
    </div>
    <script src="./js/vue.js"></script>
    <script type="text/javascript">
        // props 传递数据原则:单向数据流(只允许父组件向子组件传递参数,不允许子组件操作 props)
        // 如果允许子组件操作,数据的控制逻辑就比较复杂,不太容易控制。
        // 从代码运行中可看出子组件可以操作 props 但是这种操作不推荐。
        Vue.component('menu-item', {
            props: ['parr'],
            template: `
                <div>
                    <ul>
                        <li :key='index' v-for='(item,index) in parr'>{{item}}</li>
                    </ul>
                    <!-- 不推荐 -->
                    <button @click='parr.push("lemon")'>点击</button>
				  <!-- 1. 子组件通过自定义事件向父组件传递信息 -->
                    <button @click='$emit("enlarge-text")'>扩大父组件中字体大小</button>
                </div>
            `
        });
        var vm = new Vue({
            el: '#app',
            data: {
                pmsg: '父组件中内容',
                parr: ['apple', 'orange', 'banana'],
                fontSize: 10
            },
            methods: {
                handle: function() {
                    // 扩大字体大小
                    this.fontSize += 5;
                }
            }
        })
    </script>
</body>

</html>

2.带参数

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>子传父基本用法</title>
</head>

<body>
    <div id="app">
        <div :style='{fontSize:fontSize + "px"}'>{{pmsg}}</div>
        <!-- 4.	父组件监听子组件的事件 -->
        <menu-item :parr='parr' @enlarge-text='handle($event)'></menu-item>
    </div>
    <script src="./js/vue.js"></script>
    <script type="text/javascript">
        Vue.component('menu-item', {
            props: ['parr'],
            template: `
                <div>
                    <!-- 3.	子组件通过自定义事件向父组件传递信息 -->
                    <button @click='$emit("enlarge-text",5)'>扩大父组件中字体大小</button>
                    <button @click='$emit("enlarge-text",10)'>扩大父组件中字体大小</button>
                </div>
            `
        });
        var vm = new Vue({
            el: '#app',
            data: {
                pmsg: '父组件中内容',
                fontSize: 10
            },
            methods: {
                handle: function(val) {
                    // 扩大字体大小
                    this.fontSize += val;
                }
            }
        })
    </script>
</body>

</html>

4.3 非父子组件间传值

1. 单独的事件中心管理组件间的通信

var eventHub = new Vue();

2. 监听事件与销毁事件

eventHub.$on('add-todo', addTodo)
// 参数1:事件名称 参数2:事件函数
eventHub.$off('add-todo')

3. 触发事件

eventHub.$emit(‘add-todo', id)

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>兄弟互传</title>
</head>

<body>
    <div id="app">
        <h2>父组件</h2>
        <div>
            <button @click='handle'>销毁事件</button>
        </div>
        <module-one></module-one>
        <module-two></module-two>
    </div>
    <script src="./js/vue.js"></script>
    <script type="text/javascript">
        //提供事件中心
        var hub = new Vue();
        Vue.component('module-one', {
            data: function() {
                return {
                    num: 0
                }
            },
            template: `
                <div>
                    <div>One:{{num}}</div>
                    <div>
                        <button @click='handle'>点击</button>
                    </div>
                </div>
            `,
            methods: {
                handle: function() {
                    // 触发兄弟组件的事件
                    hub.$emit('two-event', 2);
                },
            },
            mounted: function() {
                // 监听事件
                hub.$on('one-event', (val) => {
                    this.num += val;
                });
            }
        });
        Vue.component('module-two', {
            data: function() {
                return {
                    num: 0
                }
            },
            template: `
                <div>
                    <div>Two:{{num}}</div>
                    <div>
                        <button @click='handle'>点击</button>
                    </div>
                </div>
            `,
            methods: {
                handle: function() {
                    // 触发兄弟事件
                    hub.$emit('one-event', 1);
                }
            },
            mounted: function() {
                // 监听事件
                hub.$on('two-event', (val) => {
                    this.num += val;
                });
            }
        });
        var vm = new Vue({
            el: '#app',
            data: {},
            methods: {
                handle: function() {
                    hub.$off('one-event');
                    hub.$off('two-event');
                }
            }
        })
    </script>
</body>

</html>

五. 组件插槽

5.1 组件插槽的作用

1.父组件向子组件传递内容

在这里插入图片描述

5.2 组件插槽基本用法

1. 插槽位置

Vue.component('alert-box', {  
	template: `
		<div class="demo-alert-box">
			<strong>Error!</strong>
			<slot></slot>
		</div>
	`
});

2. 插槽内容

<alert-box>Something bad happened.</alert-box>

案例

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>组件插槽基本用法</title>
</head>

<body>
    <div id="app">
        <err-box>语法错误!</err-box>
        <err-box>数据上传错误!</err-box>
        <err-box></err-box>
    </div>

    <script src="./js/vue.js"></script>
    <script type="text/javascript">
        Vue.component('err-box', {
            template: `
                <div>
                    <strong>ERROR:</strong>
                    <slot>出现了错误!</slot>
                </div>
            `,
        })
        var vm = new Vue({
            el: '#app',
            data: {

            },
            methods: {

            }
        })
    </script>
</body>

</html>

5.3 具名插槽用法

案例

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>具名插槽</title>
</head>

<body>
    <div id="app">
        <span>---------------- 用法一 ------------------</span>
        <!-- 2. 插槽内容 -->
        <base-layout>
            <p slot="header">标题信息</p>
            <p>主要内容</p>
            <p>段小小大帅逼!</p>
            <p slot="footer">底部信息内容</p>
        </base-layout>
        <span>---------------- 用法二 ------------------</span>
        <base-layout>
            <!-- template 属于 vue API -->
            <!-- 可以向插槽中放入多个标签 -->
            <template slot="header">
                <p>标题信息</p>
                <p>my logo</p>
            </template>
            <p>主要内容</p>
            <p>段仙子大帅逼!</p>
            <template slot="footer">
                <p>底部信息内容</p>
            </template>
        </base-layout>
    </div>
    <script src="./js/vue.js"></script>
    <script type="text/javascript">
        // 具名插槽
        Vue.component('base-layout', {
            template: `
			   <!-- 1. 插槽定义 -->
                <div>
                    <header>
                        <slot name='header'></slot>
                    </header>
                    <main>
                        <slot></slot>
                    </main>
                    <footer>
                        <slot name='footer'></slot>
                    </footer>
                </div>
            `,
        });
        var vm = new Vue({
            el: '#app',
            data: {},
        })
    </script>
</body>

</html>

5.4 作用域插槽

应用场景:

父组件对子组件的内容进行加工处理

案例

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>作用域插槽</title>
    <style>
        .current {
            color: orange;
        }
    </style>
</head>

<body>
    <div id="app">
        <fruit-list :list='list'>
            <template slot-scope='slotProps'>
                <strong v-if='slotProps.info.id == 2' class="current">{{slotProps.info.name}}</strong>
                <span v-else>{{slotProps.info.name}}</span>
            </template>
        </fruit-list>
    </div>
    <script src="./js/vue.js"></script>
    <script type="text/javascript">
        Vue.component('fruit-list', {
            props: ['list'],
            template: `
                <div>
                    <li :key='item.id' v-for='item in list'>
                        <slot :info='item'>{{item.name}}</slot>
                    </li>
                </div>
            `,
        })
        var vm = new Vue({
            el: '#app',
            data: {
                list: [{
                    id: 1,
                    name: 'apple',
                }, {
                    id: 2,
                    name: 'lemon',
                }, {
                    id: 3,
                    name: 'orange',
                }]
            },
        })
    </script>
</body>

</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值