3、VUE组件开发
组件注册
Vue.component(组件名称,{
data:组件数据,
template:组件模板内容
})简单用法
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button @click="add">点击了{{count}}次</button>',
methods: {
add() {
this.count++
}
},
})<div id="app">
<button-counter><button-counter>
</div>组件可以多次复用,并且每个组件之间互不影响。
组件注意事项
定义组件时
data属性为一个函数,函数内返回一个对象。这个对象中包含数据data: function () { return { count: 0 } }组件模板内容必须是单个根元素
template: '<button @click="add">点击了{{count}}次<p>p标签</p></button>',组件模板内容可以是模板字符串
组件命名方式
横线
Vue.component('button-counter', { data: function () { return { count: 0 } }, template: '', })驼峰式
Vue.component('HelloWrold', { data: function () { return { count: 0 } }, template: '', })驼峰式命名只能在模板中使用,不能在HTML中使用,如果使用需转换成横线式。
<hello-wrold></hello-wrold>
局部组件注册方式
var ComponentA = {}
var vm = new Vue({
el: '#app',
components: {
'component-a': ComponentA
}
});<body>
<div id="app">
<component-a></component-a>
<component-b></component-b>
<component-c></component-c>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
var ComponentA = {
data: function () {
return {
msg: 'HelloWorld'
}
},
template: '<div>{{msg}}</div>'
}
var ComponentB = {
data: function () {
return {
msg: 'HelloTOM'
}
},
template: '<div>{{msg}}</div>'
}
var ComponentC = {
data: function () {
return {
msg: 'HelloVue'
}
},
template: '<div>{{msg}}</div>'
}
var vm = new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB,
'component-c': ComponentC
}
});
</script>
</body>Vue调试工具
可以直接使用谷歌商城进行安装:https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd/related?hl=zh
也可以参考官方自行构建后安装:https://github.com/vuejs/vue-devtools
如果遇到Vue.js not detected问题可能导致的原因:
没有开启权限
组件间的交互
父组件向子组件传值
在组建中定义props属性,值为一个数组,用于接受来自父组件的值
Vue.component('menu-item', {
props: ['title'],
data: function () {
return {
msg: '子组件'
}
},
template: '<div>{{msg}}---{{title}}</div>'
})在使用时可以有两种写法,
静态传入
<menu-item title='来自父组件的值'></menu-item>通过属性绑定
<menu-item :title='ptitle'></menu-item>var vm = new Vue({ el: '#app', data: { pmsg: "父组件", ptitle: '来自父组件的值' } });
<body>
<div id="app">
<menu-item title='来自父组件的值'></menu-item>
<menu-item :title='ptitle'></menu-item>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
Vue.component('menu-item', {
props: ['title'],
data: function () {
return {
msg: '子组件'
}
},
template: '<div>{{msg}}---{{title}}</div>'
})
var vm = new Vue({
el: '#app',
data: {
pmsg: "父组件",
ptitle: '来自父组件的值'
}
});
</script>
</body>props属性名
规则
在
props使用驼峰式传值,那么模板中需要使用短横线的形式Vue.component('menu-item', { props: ['TitleMsg'], template: '<div>{{TitleMsg}}</div>' })<menu-item title-msg='nihao'></menu-item>在HTML中大小写是不敏感的。
字符串模板中没有这个限制
Vue.component('menu-item', { props: ['TitleMsg'], template: '<div><menu-item TitleMsg="nihao"></menu-item></div>' })
属性值
以下类型均可以传递
字符串
数值类型
如果通过
v-bind绑定,那么数值为数字类型布尔类型
如果通过
v-bind绑定,那么数值为布尔类型数组类型
对象
子组件向父组件传值
子组件通过自定义事件向父组件传递信息
<menu-item :parr='parr' @enlarge-text='handle'></menu-item>自定义事件名为
enlarge-text,触发的函数名为handleVue.component('menu-item', { props: ['parr'], template: ` <div> <ul> <li :key='index' v-for='(item,index) in parr'>{{item}}</li> </ul> <button @click='$emit("enlarge-text")'>扩大父组件中字体大小</button> </div> ` });对按钮进行单击事件绑定,绑定的事件为
enlarge-text。$emit为固定用法。父组件通过监听子组件的事件
var vm = new Vue({ el: '#app', data: { pmsg: '父组件中内容', parr: ['apple', 'orange', 'banana'], fontSize: 10 }, methods: { handle: function () { // 扩大字体大小 this.fontSize += 5; } } });
子组件向父组件传值-携带参数
定义组件时可以通过$emit的第二个参数进行传值。组件调用时通过$event来接受。
使用组件
<menu-item :parr='parr' @enlarge-text='handle($event)'></menu-item>定义组件
Vue.component('menu-item', {
props: ['parr'],
template: `
<div>
<ul>
<li :key='index' v-for='(item,index) in parr'>{{item}}</li>
</ul>
<button @click='$emit("enlarge-text",5)'>扩大父组件中字体大小</button>
</div>
`
});父组件接受值
methods: {
handle: function (val) {
// 扩大字体大小
this.fontSize += val;
}
}非父子组件间传值
单独的事件中心管理组件间的通信
var hub = new Vue()监听事件与销毁事件
hub.$on('tom-event', (val) => { this.num += val })hub.$off('tom-event')触发事件
hub.$emit('jerry-event', 1)
<body>
<div id="app">
<test-tom></test-tom>
<test-jerry></test-jerry>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
// 提供事件中心
var hub = new Vue()
Vue.component('test-tom', {
data: function () {
return {
num: 0
}
},
template: `
<div>
<div>TOM:{{num}}</div>
<div><button @click='handle'>点击</button></div>
</div>
`,
methods: {
handle: function () {
// 触发对方的事件
hub.$emit('jerry-event', 1)
}
},
mounted: function () {
hub.$on('tom-event', (val) => {
this.num += val
})
},
});
Vue.component('test-jerry', {
data: function () {
return {
num: 0
}
},
template: `
<div>
<div>jerry:{{num}}</div>
<div><button @click='handle'>点击</button></div>
</div>
`,
methods: {
handle: function () {
hub.$emit('tom-event', 2)
}
},
mounted: function () {
hub.$on('jerry-event', (val) => {
this.num += val
})
},
});
var vm = new Vue({
el: '#app',
});
</script>
</body>组件插槽
父组件向子组件传递内容(模板内容)
定义插槽
Vue.component('alert-box', { template: ` <div> <strong>Error:<strong> <slot>默认内容</slot> </div> ` })定义插槽使用
slot标签,如果没有传值,那么显示定义时的默认内容。使用插槽
<alert-box>有bug发生</alert-box> <alert-box></alert-box>当传递值时显示值的内容,否则显示默认内容(如果有)
具名插槽
即对slot标签添加name属性,表示当前插槽的名称。
Vue.component('base-layout', {
template: `
<div>
<header>
<slot name='header'></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name='footer'></slot>
</footer>
</div>
`
})调用时,通过slot属性指定填充插槽名。没有指定的则添加到默认插槽
<base-layout>
<p slot="header">标题</p>
<p>主要内容</p>
<p slot="footer">尾部</p>
</base-layout>关于调用也可以使用如下方法
<base-layout>
<template slot="header">
<p slot="header">标题</p>
</template>
<template>
<p>主要内容</p>
</template>
<template slot="footer">
<p slot="footer">尾部</p>
</template>
</base-layout>作用域插槽
父组件对子组件的内容进行加工处理。
插槽定义
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: 'orange' }, { id: 3, name: 'banana' }] } });插槽内容
<fruit-list :list='list'> <template v-slot='slotProps'> <strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong> <span v-else>{{slotProps.info.name}}</span> </template> </fruit-list>
值的传递:
插槽中定义了属性
info为item(每次遍历的结果)使用
v-slot指令接受,其值可以为任意喜欢的名字。<template v-slot='slotProps'></template>其
slotProps实际上就是info的值。<fruit-list :list='list'> <template v-slot='slotProps'> <strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong> <span v-else>{{slotProps.info.name}}</span> <span>{{slotProps}}</span> </template> </fruit-list>
购物车案例
组件化重构
<body>
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
var CartTitle = {
template: `
<div class="title">我的商品</div>
`
}
var CartList = {
template: `
<div>
<div class="item">
<img src="img/a.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/b.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/c.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/d.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/e.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
</div>
`
}
var CartTotal = {
template: `
<div class="total">
<span>总价:123</span>
<button>结算</button>
</div>
`
}
Vue.component('my-cart', {
template: `
<div class='cart'>
<cart-title></cart-title>
<cart-list></cart-list>
<cart-total></cart-total>
</div>
`,
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
}
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>标题和总价
标题的实现很简单,为了演示功能,定义一个属性记录用户名,并传递给header
Vue.component('my-cart', {
data: function () {
return {
uname: '张三',
}
},
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
<cart-list></cart-list>
<cart-total></cart-total>
</div>
`,
}将值显示出来
var CartTitle = {
props: ['uname'],
template: `
<div class="title">{{uname}}的商品</div>
`
}对于总价,提供一个数据用于记录购物车的列表。并将值传递给total
Vue.component('my-cart', {
data: function () {
return {
uname: '张三',
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1,
img: 'img/a.jpg'
}, {
id: 2,
name: '机顶盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
}, {
id: 3,
name: '海尔冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
}, {
id: 4,
name: '小米手机',
price: 1000,
num: 1,
img: 'img/d.jpg'
}, {
id: 5,
name: 'PPTV电视',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
}
},
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
<cart-list></cart-list>
<cart-total :list='list></cart-total>
</div>
`,
}在CartTotal组件中根据列表中的内容计算总价。
var CartTotal = {
props: ['list'],
template: `
<div class="total">
<span>总价:{{total}}</span>
<button>结算</button>
</div>
`,
computed: {
total: function () {
// 计算总价
var sum = 0
console.log(this.list);
this.list.forEach(item => {
sum += item.price * item.num
})
return sum
}
}
}列表展示和删除
列表展示只需要在父组件中将数组列表传递给子组件,子组件在进行循环遍历即可。
Vue.component('my-cart', {
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
<cart-list :list='list'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
}
)子组件中循环遍历数据
var CartList = {
props: ['list'],
template: `
<div>
<div :key='item.id' v-for='item in list' class="item">
<img :src="item.img"/>
<div class="name">{{item.name}}</div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del"'>×</div>
</div>
</div>
`
}对于删除操作来说,由于数据来源于父组件,所以不建议直接删除,而是通过自定义事件通知父级。
var CartList = {
props: ['list'],
template: `
<div>
<div :key='item.id' v-for='item in list' class="item">
<img :src="item.img"/>
<div class="name">{{item.name}}</div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del" @click='del(item.id)'>×</div>
</div>
</div>
`,
methods: {
del: function (id) {
// 通知父组件进行删除
this.$emit('cart-del', id)
}
},
}父级中定义删除的实际操作,并监听事件。
Vue.component('my-cart', {
methods: {
delCart: function (id) {
// 根据ID删除list中的数据
// 1. 找到索引
var index = this.list.findIndex(item => {
return item.id == id
})
// 2. 根据索引删除
this.list.splice(index, 1)
}
},
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
<cart-list :list='list' @cart-del='delCart($event)'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
});商品数量的变更
商品数量变更同样涉及到修改列表内容,同样的交给父组件去修改。
在子组件中定义自定义事件并对input标签绑定失去焦点时触发这个自定义事件。
var CartList = {
props: ['list'],
template: `
<div>
<div :key='item.id' v-for='item in list' class="item">
<img :src="item.img"/>
<div class="name">{{item.name}}</div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" :value='item.num' @blur='changeNum(item.id,$event)'/>
<a href="">+</a>
</div>
<div class="del" @click='del(item.id)'>×</div>
</div>
</div>
`,
methods: {
del: function (id) {
// 通知父组件进行删除
this.$emit('cart-del', id)
},
changeNum: function (id, event) {
// 修改商品数量
// 通知父组件进行删除
let value = event.target.value
this.$emit('change-num', { id: id, num: value })
}
},
}自定义事件中,将商品id和修改的值传递给父组件。
Vue.component('my-cart', {
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
<cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
methods: {
changeNum: function (val) {
console.log(val);
// 根据子组件传递的数据更新list中对应的数据
this.list.some(item => {
if (item.id == val.id) {
item.num = val.num
// 终止
return true
}
})
}
},
});父组件监听子组件定义的自定义方法,并触发父组件定义的自定义方法。
按钮操作
按钮操作同样是需要利用父组件来修改数据。为了复用changeNum事件,将其在添加一个值type用于记录当前的操作。子组件修改。
changeNum: function (id, event) {
// 修改商品数量
// 通知父组件进行删除
let value = event.target.value
this.$emit('change-num', {
id: id,
num: value,
type: 'change'
})
},
sub: function (id) {
this.$emit('change-num', {
id: id,
type: 'sub'
})
},
add: function (id) {
this.$emit('change-num', {
id: id,
type: 'add'
})
},父组件的修改
changeNum: function (val) {
if (val.type == 'change') {
// 根据子组件传递的数据更新list中对应的数据
this.list.some(item => {
if (item.id == val.id) {
item.num = val.num
// 终止
return true
}
})
} else if (val.type == 'sub') {
// 根据子组件传递的数据更新list中对应的数据
this.list.some(item => {
if (item.id == val.id) {
item.num -= 1
// 终止
return true
}
})
} else {
// 根据子组件传递的数据更新list中对应的数据
this.list.some(item => {
if (item.id == val.id) {
item.num += 1
// 终止
return true
}
})
}

















