组件化规范:
Web Components
<div id="app">
<!-- 子组件之间互相独立 -->
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script src=" https://cdn.jsdelivr.net/npm/vue/dist/vue.js "></script>
<script>
/*
组件注册
Vue.component(组件名称, {
data: 组件数据,
template:组件模板内容
})
*/
Vue.component('button-counter', {
data: function() {
return {
count: 0
}
},
template: '',
methods: {
handle: function() {
this.count++;
}
}
});
var app = new Vue({
el: "#app ",
data: {
}
});
</script>
(1) data
必须是一个函数
。
(2)组件模板内容
必须是单个根元素
。
(3)组件模板内容可以是模板字符串。
(4)组件命名方式:只能在字符串模板
中使用驼峰方式
,在普通的标签模板
中,必须使用短横线的方式
使用组件。
Vue.component('my-component', {
data: 组件数据,
template:组件模板内容
})
Vue.component('MyComponent', {
data: 组件数据,
template:组件模板内容
})
<div id="app">
<!-- 子组件之间互相独立 -->
<hello-world></hello-world>
<hello-tom></hello-tom>
<hello-jerry></hello-jerry>
</div>
<script src=" https://cdn.jsdelivr.net/npm/vue/dist/vue.js "></script>
<script>
/*
局部组件注册
局部组件只能在注册他的父组件中使用
*/
var HelloWorld = {
data: function() {
return {
msg: 'HelloWorld'
}
},
template: '{{msg}}',
};
var HelloTom = {
data: function() {
return {
msg: 'HelloTom'
}
},
template: '{{msg}}',
};
var HelloJerry = {
data: function() {
return {
msg: 'HelloJerry'
}
},
template: '{{msg}}',
};
var app = new Vue({
el: "#app ",
data: {},
components: {
'hello-world': HelloWorld,
'hello-tom': HelloTom,
'hello-jerry': HelloJerry,
}
});
</script>
<!-- 静态获取 -->
<menu-item title='来自父组件的值'></menu-item>
<!-- 动态绑定属性 -->
<menu-item :title='ptitle'></menu-item>
var app = new Vue({
el: "#app ",
data: {
pmsg: '父组件中内容',
ptitle: '动态绑定属性'
}
});
props
接收传递过来的值Vue.component('menu-item', {
props: ['title'],
data: function() {
return {
msg: '子组件本身的数据'
}
},
template: '{{msg+"----"+title}}'
});
props
中使用驼峰
形式,标签模板
中需要使用短横线
的形式。Vue.component('menu-item', {
// 在js中是驼峰形式
props: ['menuTitle'],
template: '{{menuTitle}}'
});
<!-- 在html中是短横线形式 -->
<menu-item :menu-title='ptitle'></menu-item>
字符串
形式的模板
中没
有这个限制
。Vue.component('third-com', {
props: ['testTitle'],
template: '{{testTitle}}'
});
Vue.component('menu-item', {
props: ['menuTitle'],
// 字符串模板中没有一定要短横线的规定
template: '{{menuTitle}} '
});
<body>
<div id="app">
<div>{{pmsg}}</div>
<menu-item :pstring='pstr' :pnumber='pnumber' :pbool='pbool' :parr='parr' :pobj='pobj'></menu-item>
</div>
<script src=" https://cdn.jsdelivr.net/npm/vue/dist/vue.js "></script>
<script>
Vue.component('menu-item', {
props: ['pstring', 'pnumber', 'pbool', 'parr', 'pobj'],
template: `
{{pstring}}
{{12+pnumber}}
{{pbool}}
- {{item}}
{{pobj.name}}
{{pobj.age}}
`
});
var app = new Vue({
el: "#app ",
data: {
pmsg: '父组件中内容',
// 字符串
pstr: 'hello',
// 数值
pnumber: 12,
//布尔值
pbool: true,
//数组
parr: ['apple', 'lemon', 'banana'],
//对象
pobj: {
name: 'lisi',
age: '21'
}
}
});
</script>
</body>
① 子组件通过自定义事件
向父组件传递信息。
<button @click="$emit('enlarge-text')">扩大父组件中的字体大小</button>
② 父组件监听子组件的事件。
<menu-item :parr='parr' @enlarge-text='fontsize += 5'></menu-item>
③ 子组件(具体传参)向父组件传递信息。
//子组件通过自定义事件向父组件传递信息
<button @click="$emit('enlarge-text',5)">扩大父组件中的字体大小</button>
//父组件监听子组件的事件
<menu-item :parr='parr' @enlarge-text='fontsize += $event'></menu-item>
var eventHub = new Vue();
② 监听事件与销毁事件。
eventHub.$on('add-todo',addTodo);
eventHub.$off('add-todo');
③ 触发事件。
eventHub.$emit('add-todo',id);
④ 具体案例:
<body>
<div id="app">
<div>父组件</div>
<div><button @click="handle">销毁事件</button></div>
<test-tom></test-tom>
<test-jerry></test-jerry>
</div>
<script src=" https://cdn.jsdelivr.net/npm/vue/dist/vue.js "></script>
<script>
// 提供事件中心
var hub = new Vue();
Vue.component('test-tom', {
data: function() {
return {
num: 0
}
},
template: `
Tom:{{num}}
`,
methods: {
handle: function() {
// 触发兄弟组件的事件
hub.$emit('jerry-event', 2)
}
},
// 模板就绪,可进行操作了
mounted: function() {
//监听事件
hub.$on('tom-event', (val) => {
this.num += val;
});
}
});
Vue.component('test-jerry', {
data: function() {
return {
num: 0
}
},
template: `
Jerry:{{num}}
`,
methods: {
handle: function() {
// 触发兄弟组件的事件
hub.$emit('tom-event', 1)
}
},
// 模板就绪,可进行操作了
mounted: function() {
//监听事件
hub.$on('jerry-event', (val) => {
this.num += val;
});
}
});
var app = new Vue({
el: "#app ",
data: {
parr: ['apple', 'lemon', 'banana'],
fontsize: 10
},
methods: {
handle: function() {
hub.$off('tom-event');
hub.$off('jerry-event');
}
}
});
</script>
</body>
① 插槽位置
Vue.component('alert-box', {
template: `
ERROR:
默认内容
`
});
② 插槽内容
<div id="app">
<alert-box>有bug发生</alert-box>
<alert-box>有一个警告</alert-box>
<!-- 父组件没有传递子组件内容,那么就默认是slot插槽内的内容 -->
<alert-box></alert-box>
</div>
① 插槽定义
Vue.component('base-layout', {
template: `
`
});
② 插槽内容
<base-layout>
<p slot="header">标题信息</p>
<p>主要内容1</p>
<p>主要内容2</p>
<p slot="footer">底部信息</p>
</base-layout>
父组件对子组件的内容进行加工处理
。① 插槽定义
Vue.component('fruit-list', {
props: ['list'],
template: `
-
`
});
② 插槽内容
<fruit-list :list='list'>
<!-- template填充slot -->
<!-- slotProps是自定义的名字,获取子模板中的slot -->
<template v-slot='slotProps'>
<strong v-if='slotProps.info.id==2' class="current"> {{slotProps.info.name}}</strong>
<strong v-else> {{slotProps.info.name}}</strong>
</template>
</fruit-list>
③ 具体视频链接:
https://www.bilibili.com/video/BV1vE411871g?p=66.
根据业务功能进行组件化划分
① 标题组件(展示文本)。
② 列表组件(列表展示、商品数量变更、商品删除)。
③ 结算组件(计算商品总额)。
- 实现整体布局和样式效果。
- 划分独立的功能组件。
- 组合所有的子组件形成整体结构。
- 逐个实现各个组件功能。
1. 标题组件
2. 列表组件
3. 结算组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>兄弟组件</title>
<!-- 引入car css -->
<link rel="stylesheet" href="./car.css">
</head>
<body>
<div id="app">
<!-- 购物车模块start -->
<div class="c-container">
<my-cart></my-cart>
</div>
<!-- 购物车模块end -->
</div>
<script src=" https://cdn.jsdelivr.net/npm/vue/dist/vue.js "></script>
<script>
// 三个子组件
var CartTitle = {
props: ['uname'],
template: `
{{uname}}的商品
`
}
var CartList = {
props: ['list'],
template: `
`,
methods: {
del: function(id) {
//把id传递给父组件
this.$emit('cart-del', id);
},
//event是传过来的最新的数据
changeNum: function(id, event) {
//把id传递给父组件
//console.log(id, event.target.value);
this.$emit('change-num', {
id: id,
type: 'change',
num: event.target.value
});
},
sub: function(id) {
this.$emit('change-num', {
id: id,
type: 'sub'
})
},
add: function(id) {
this.$emit('change-num', {
id: id,
type: 'add'
})
}
}
}
var CartTotal = {
props: ['list'],
template: `
`,
// 计算属性
computed: {
total: function() {
//计算商品的总价
var t = 0;
this.list.forEach(item => {
t += item.price * item.num;
});
return t;
}
}
}
// 全局组件包含三个子组件
Vue.component('my-cart', {
data: function() {
return {
uname: '张三',
list: [{
id: 1,
name: '儿童文学',
price: 13,
num: 1,
img: 'upload/p1.jpg'
}, {
id: 2,
name: '贴纸书',
price: 25,
num: 2,
img: 'upload/p2.jpg'
}, {
id: 3,
name: '唐诗三百首',
price: 30,
num: 3,
img: 'upload/p3.jpg'
}]
}
},
template: `
`,
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal,
},
methods: {
delCart: function(id) {
//根据id删除list中对应的数据
//1. 找到id所对应数据的索引
var index = this.list.findIndex(item => {
return item.id == id;
});
console.log(index);
//2. 根据索引删除对应数据
this.list.splice(index, 1);
},
changeNum: function(val) {
//分为三种情况:1.输入与变更 2.加号变更 3.减号变更
//1. 根据子组件传递过来的数据更新list中对应的数据
if (val.type == 'change') {
this.list.some(item => {
if (item.id == val.id) {
item.num = val.num;
//终止遍历
return true;
}
})
} else if (val.type == 'sub') {
//减一操作
this.list.some(item => {
if (item.id == val.id) {
if (item.num > 0) {
item.num -= 1;
} else {
alert('不能在减少了!');
}
//终止遍历
return true;
}
})
} else if (val.type == 'add') {
//减一操作
this.list.some(item => {
if (item.id == val.id) {
item.num += 1;
//终止遍历
return true;
}
})
}
}
}
});
var app = new Vue({
el: "#app ",
data: {
},
});
</script>
</body>
</html>