vue组件
文章目录
什么是组件
- 组件 (Component) 是 Vue.js 最强大的功能之一
- 组件可以扩展 HTML 元素,封装可重用的代码
组件注册
全局注册
- Vue.component(‘组件名称’, { }) 第1个参数是标签名称,第2个参数是一个选项对象
- 全局组件注册后,任何vue实例都可以用
组件基础用法
<div id="example">
<!-- 2、 组件使用 组件名称 是以HTML标签的形式使用 -->
<my-component></my-component>
</div>
<script>
// 注册组件
// 1、 my-component 就是组件中自定义的标签名
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
// 创建根实例
new Vue({
el: '#example'
})
</script>
组件注意事项
- 组件参数的data值必须是函数同时这个函数要求返回一个对象
- 组件模板必须是单个根元素
- 组件模板的内容可以是模板字符串
<div id="app">
<!--
4、 组件可以重复使用多次
因为data中返回的是一个对象所以每个组件中的数据是私有的
即每个实例可以维护一份被返回对象的独立的拷贝
-->
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<!-- 8、必须使用短横线的方式使用组件 -->
<hello-world></hello-world>
</div>
<script type="text/javascript">
//5 如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,
// 7、但是在普通的标签模板中,必须使用短横线的方式使用组件
Vue.component('HelloWorld', {
data: function(){
return {
msg: 'HelloWorld'
}
},
template: '<div>{{msg}}</div>'
});
Vue.component('button-counter', {
// 1、组件参数的data值必须是函数
// 同时这个函数要求返回一个对象
data: function(){
return {
count: 0
}
},
// 2、组件模板必须是单个根元素
// 3、组件模板的内容可以是模板字符串
template: `
<div>
<button @click="handle">点击了{{count}}次</button>
<button>测试123</button>
# 6 在字符串模板中可以使用驼峰的方式使用组件
<HelloWorld></HelloWorld>
</div>
`,
methods: {
handle: function(){
this.count += 2;
}
}
})
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
局部注册
- 只能在当前注册它的vue实例中使用
<div id="app">
<my-component></my-component>
</div>
<script>
// 定义组件的模板
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
//局部注册组件
components: {
// <my-component> 将只在父模板可用 一定要在实例上注册了才能在html文件中使用
'my-component': Child
}
})
</script>
组件通信
父组件向子组件传值
- 父组件发送的形式是以属性的形式绑定值到子组件身上。
- 然后子组件用属性props接收
- 在props中使用驼峰形式,模板中需要使用短横线的形式字符串形式的模板中没有这个限制
<div id="app">
<div>{{pmsg}}</div>
<!--1、menu-item 在 APP中嵌套着 故 menu-item 为 子组件 -->
<!-- 给子组件传入一个静态的值 -->
<menu-item title='来自父组件的值'></menu-item>
<!-- 2、 需要动态的数据的时候 需要属性绑定的形式设置 此时 ptitle 来自父组件data 中的数据 .
传的值可以是数字、对象、数组等等
-->
<menu-item :title='ptitle' content='hello'></menu-item>
</div>
<script type="text/javascript">
Vue.component('menu-item', {
// 3、 子组件用属性props接收父组件传递过来的数据
props: ['title', 'content'],
data: function() {
return {
msg: '子组件本身的数据'
}
},
template: '<div>{{msg + "----" + title + "-----" + content}}</div>'
});
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件中内容',
ptitle: '动态绑定属性'
}
});
</script>
1. 在⼦组件中声明props接收在⽗组件挂载的属性
2. 可以在⼦组件的template中任意使⽤
3. 在⽗组件绑定⾃定义的属性
子组件向父组件传值
- 子组件用
$emit()
触发事件 $emit()
第一个参数为 自定义的事件名称 第二个参数为需要传递的数据- 父组件用v-on 监听子组件的事件
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script src="./vue.js"></script>
<script>
// 全局组件
// 子往父传值
// 在父组件中 子组件上绑定自定义事件
// 在子组件中 触发原生的事件 在事件函数通过this.$emit触发自定义的事件
Vue.component('Child', {
template: `
<div>
<h3>我是一个子组件</h3>
<h4>{{childData}}</h4>
<input type="text" @input = 'handleInput'/>
</div>
`,
props: ['childData'],
methods:{
handleInput(e){
const val = e.target.value;
this.$emit('inputHandler',val);
}
},
})
const App = {
data() {
return {
msg: '我是父组件传进来的值',
newVal:''
}
},
methods:{
input(newVal){
// console.log(newVal);
this.newVal = newVal;
}
},
template: `
<div>
<div class='father'>
数据:{{newVal}}
</div>
<Child :childData = 'msg' @inputHandler = 'input'></Child>
</div>
`,
computed: {
}
}
new Vue({
el: '#app',
data: {
},
components: {
// 2.挂载子组件
App
}
})
</script>
1. 在⽗组件中 ⼦组件上绑定⾃定义事件
2. 在⼦组件中 触发原⽣的事件 在事件函数通过this.$emit触发⾃定义的事件
平⾏组件之间通信
- 兄弟之间传递数据需要借助于事件中心,通过事件中心传递数据
- 提供事件中心 var hub = new Vue()
- 传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)
- 接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名
- 销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
<div id="app">
<div>父组件</div>
<div>
<button @click='handle'>销毁事件</button>
</div>
<test-tom></test-tom>
<test-jerry></test-jerry>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
兄弟组件之间数据传递
*/
//1、 提供事件中心
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(){
//2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据) 触发兄弟组件的事件
hub.$emit('jerry-event', 2);
}
},
mounted: function() {
// 3、接收数据方,通过mounted(){} 钩子中 触发hub.$on(方法名
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(){
//2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据) 触发兄弟组件的事件
hub.$emit('tom-event', 1);
}
},
mounted: function() {
// 3、接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名
hub.$on('jerry-event', (val) => {
this.num += val;
});
}
});
var vm = new Vue({
el: '#app',
data: {
},
methods: {
handle: function(){
//4、销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
hub.$off('tom-event');
hub.$off('jerry-event');
}
}
});
</script>
其它组件通信⽅式
- ⽗组件 provide来提供变量,然后再⼦组件中通过inject来注⼊变量.⽆论组件嵌套多深
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script>
// **** 如何设计组件? *****
// provide
// inject
// 父组件 provide来提供变量,然后再子组件中通过inject来注入变量.无论组件嵌套多深
// 中央事件总线 bus
Vue.component('B', {
data() {
return {
count: 0
}
},
inject:['msg'],
created(){
console.log(this.msg);
},
template: `
<div>
{{msg}}
</div>
`,
})
Vue.component('A', {
data() {
return {
}
},
created(){
// console.log(this.$parent.$parent);
// console.log(this.$children);
console.log(this);
},
template: `
<div>
<B></B>
</div>
`
})
const App = {
data() {
return {
title:"老爹"
}
},
provide(){
return {
msg:"老爹的数据"
}
},
template: `
<div>
<A></A>
</div>
`,
}
new Vue({
el: '#app',
data: {
},
components: {
// 2.挂载子组件
App
}
})
</script>
组件插槽
- 组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力
匿名插槽
- ⼦组件定义 slot 插槽,但并未具名,因此也可以说是默认插槽。只要在⽗元素中插⼊的内容,默认加⼊到这个插槽中去
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script src="./vue.js"></script>
<script>
Vue.component('MBtn',{
template:`
<button>
<slot></slot>
</button>
`
})
const App = {
data() {
return {
title: "老爹"
}
},
template: `
<div>
<m-btn><a href="#">登录</a></m-btn>
<m-btn>注册</m-btn>
</div>
`,
}
new Vue({
el: '#app',
data: {
},
components: {
// 2.挂载子组件
App
}
})
</script>
具名插槽
- 具有名字的插槽
- 使用 中的 “name” 属性绑定元素
- 具名插槽可以出现在不同的地⽅,不限制出现的次数。只要匹配了name 那么这些内容就会被插⼊到这个 name 的插槽中去
<div id="app">
<base-layout>
<!-- 2、 通过slot属性来指定, 这个slot的值必须和下面slot组件得name值对应上
如果没有匹配到 则放到匿名的插槽中 -->
<p slot='header'>标题信息</p>
<p>主要内容1</p>
<p>主要内容2</p>
<p slot='footer'>底部信息信息</p>
</base-layout>
<base-layout>
<!-- 注意点:template临时的包裹标签最终不会渲染到页面上 -->
<template slot='header'>
<p>标题信息1</p>
<p>标题信息2</p>
</template>
<p>主要内容1</p>
<p>主要内容2</p>
<template slot='footer'>
<p>底部信息信息1</p>
<p>底部信息信息2</p>
</template>
</base-layout>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
具名插槽
*/
Vue.component('base-layout', {
template: `
<div>
<header>
### 1、 使用 <slot> 中的 "name" 属性绑定元素 指定当前插槽的名字
<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>
作用域插槽
- 父组件对子组件加工处理
- 既可以复用子组件的slot,又可以使slot内容不一致
Vue.component('MyComp', {
data(){
return {
data:{
username:'⼩⻢哥'
}
}
},
template: `
<div>
<slot :data = 'data'></slot>
<slot :data = 'data' name='one'></slot>
</div>
`
})
const App = {
data() {
return {
}
},
template: `
<div>
<MyComp>
<!--默认的插槽 default可以省略-->
<template v-slot:default='user'>
{{user.data.username}}
</template>
</MyComp>
<MyComp>
<!--与具名插槽配合使⽤-->
<template v-slot:one='user'>
{{user.data.username}}
</template>
</MyComp>
</div>
`,}
new Vue({
el: '#app',
data: {
},
components: {
App
}
})
作⽤域插槽应⽤
先说⼀下我们假设的应⽤常⽤场景,我们已经开发了⼀个代办事项列表的组件,很多模块在⽤,现在要求在不影响已测试通过的模块功能
和展示的情况下,给已完成的代办项增加⼀个对勾效果。也就是说,代办事项列表组件要满⾜⼀下⼏点
-
之前数据格式和引⽤接⼝不变,正常展示
-
新的功能模块增加对勾
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script src="./vue.js"></script>
<script>
// 已经开发了一个待办事项列表的组件,很多模块都在
// A B
// 1.之前数据格式和引用接口不变,正常显示
// 2.新功能模块增加对勾
const todoList = {
data() {
return {
}
},
props: {
todos: Array,
defaultValue: []
},
template: `
<ul>
<li v-for='item in todos' :key='item.id'>
<slot :itemValue = 'item'>
</slot>
{{item.title}}
</li>
</ul>
`
}
const App = {
data() {
return {
todoList: [{
title: '大哥你好么',
isComplate: true,
id: 1
},
{
title: '小弟我还行',
isComplate: false,
id: 2
},
{
title: '你在干什么',
isComplate: false,
id: 3
},
{
title: '抽烟喝酒烫头',
isComplate: true,
id: 4
}
]
}
},
components: {
todoList
},
template: `
<todoList :todos='todoList'>
<template v-slot='data'>
<input type="checkbox" v-model='data.itemValue.isComplate' />
</template>
</todoList>
`,
}
new Vue({
el: '#app',
data: {
},
components: {
App
}
})
</script>
生命周期
-
事物从出生到死亡的过程
-
每个 Vue 实例在被创建时都要经过⼀系列的初始化过程。 例如:从开始创建、初始化数据、编译模板、挂载Dom、数据变化时更新
DOM、卸载等⼀系列过程。 我们称 这⼀系列的过程 就是Vue的⽣命周期。 通俗说就是Vue实例从创建到销毁的过程,就是⽣命周期。 同时在这个过程中也会运⾏⼀些叫做⽣命周期钩⼦的函数,这给了⽤户在不同阶段添加⾃⼰的代码的机会,利⽤各个钩⼦来完成我们的业务代码。
常用的钩子函数
beforeCreate | 在实例初始化之后,数据观测和事件配置之前被调用 此时data 和 methods 以及页面的DOM结构都没有初始化 什么都做不了 |
---|---|
created | 在实例创建完成后被立即调用此时data 和 methods已经可以使用 但是页面还没有渲染出来,可以在此时发起ajax |
beforeMount | 在挂载开始之前被调用 此时页面上还看不到真实数据 只是一个模板页面而已 |
mounted | el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。 数据已经真实渲染到页面上 在这个钩子函数里面我们可以使用一些第三方的插件 |
beforeUpdate | 数据更新时调用,发生在虚拟DOM打补丁之前。 页面上数据还是旧的,在更新DOM之前 调⽤该钩⼦,应⽤:可以获取原始的DOM |
updated | 由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。 页面上数据已经替换成最新的,应⽤:可以获取最新的DOM |
beforeDestroy | 实例销毁之前调用 |
destroyed | 实例销毁后调用 |
activated | 被 keep-alive 缓存的组件激活时调用。当配合vue的内置组件 ⼀起使⽤的时候,才会调⽤下⾯此方法, 组件的作⽤它可以缓存当前组件 |
deactivated | 被 keep-alive 缓存的组件停用时调用。当配合vue的内置组件 ⼀起使⽤的时候,才会调⽤下⾯此方法, 组件的作⽤它可以缓存当前组件 |
组件进阶
动态组件
- 有的时候,在不同组件之间进⾏动态切换是⾮常有⽤的,⽐如在⼀个多标签的界⾯⾥
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
.active {
color: red;
}
</style>
</head>
<body>
<div id="app">
<App></App>
</div>
</body>
<script src="vue.js"></script>
<script>
const bus = new Vue();
Vue.component('TabLi', {
data() {
return {}
},
methods: {
clickHandler(title) {
bus.$emit('handleChange', title);
}
},
props: ['tabTitles'],
template: `
<ul>
<li @click='clickHandler(title)' v-for='(title,i) in tabTitles' :key='i'>{{title}}</li>
</ul>
`
})
const Home = {
data() {
return {
isActive: false
}
},
methods: {
handleClick() {
this.isActive = true;
}
},
template: `<div @click='handleClick'
:class='{active:isActive}'>Home Component</div>`
}
const Posts = {
data() {
return {}
},
template: `<div>Posts Component</div>`
}
const Archive = {
data() {
return {}
},
template: `<div>Archive Component</div>`
}
Vue.component('TabComp', {
data() {
return {
title: 'Home'
}
},
created() {
bus.$on('handleChange', (title) => {
this.title = title
})
},
template: `
<keep-alive>
<componet :is='title'></componet>
</keep-alive>
`,
components: {
Home,
Posts,
Archive
}
})
const App = {
data() {
return {
tabTitles: ['Home', 'Posts', 'Archive']
}
},
template: `
<div>
<TabLi :tabTitles='tabTitles'></TabLi>
<TabComp></TabComp>
</div>
`,
}
new Vue({
el: '#app',
data() {
return {}
},
components: {
App,
}
})
</script>
</html>
在动态组件上使用keep-alive
- 使⽤ is 特性来切换不同的组件,当在这些组件之间切换的时候,有时候会想保持这些组件的状态,以避免反复渲染导致的性能问题
<keep-alive>
<componet :is='title'></componet>
</keep-alive>
异步组件
- 在⼤型应⽤中,我们可能需要将应⽤分割成⼩⼀些的代码块,并且只在需要的时候才从服务器加载⼀个模块。为了简化,Vue 允许你以⼀个⼯⼚函数的⽅式定义你的组件,这个⼯⼚函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该⼯⼚函数,且会把结果缓存起来供未来重渲染。例如:
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
const App = {
data() {
return {
isShow:false
}
},
methods:{
asyncLoadTest(){
this.isShow = true;
}
},
template:`
<div>
<button @click='asyncLoadTest'>异步加载</button>
<test v-if='isShow'/>
</div>
`,
components:{
//异步加载组件
test:()=>import('./Test.js')
}
}
new Vue({
el:'#app',
data(){
return {
}
},
components:{
App
}
})
获取DOM和⼦组件对象
尽管存在 prop 和事件,有的时候你仍可能需要在 JavaScript ⾥直接访问⼀个⼦组件。为了达到这个⽬的,你可以通过 ref 特性为这个⼦组件赋予⼀个 ID 引⽤。例如:
const Test = {
template: `<div class='test'>我是测试组件</div>`
}
const App = {
data() {
return {
}
},
created() {
console.log(this.$refs.test); //undefined
},
mounted() {
// 如果是组件挂载了ref 获取是组件对象,如果是标签挂载了ref,则获取的是DOM元素
console.log(this.$refs.test);
console.log(this.$refs.btn);
// 加载⻚⾯ 让input⾃动获取焦点
this.$refs.input.focus();
},
components: {
Test
},
template: `
<div>
<button ref = 'btn'></button>
<input type="text" ref='input'>
<Test ref = 'test'></Test>
</div>
` }
new Vue({
el: '#app',
data: {
},
components: {
App
}
})
nextTick的⽤法
将回调延迟到下次 DOM 更新循环之后执⾏。在修改数据之后⽴即使⽤它,然后等待 DOM 更新
有些事情你可能想不到,vue在更新DOM时是异步执⾏的.只要侦听到数据变化,Vue将开启⼀个队列,并缓存在同⼀事件循环中发⽣的所有数据变更.如果同⼀个wather被多次触发,只会被推⼊到队列中⼀次.这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是⾮常重要的。然后,在下⼀个的事件循环“tick”中,Vue 刷新队列并执⾏实际(已去重的) 工作
<div id="app">
<h3>{{message}}</h3>
</div> <script src="./vue.js"></script> <script>
const vm = new Vue({
el:'#app',
data:{
message:'123'
}
})
vm.message = 'new Message';//更新数据
console.log(vm.$el.textContent); //123
Vue.nextTick(()=>{
console.log(vm.$el.textContent); //new Message
})
</script>
当你设置 vm.message = ‘new Message’ ,该组件不会⽴即重新渲染.当刷新队列时,组件会在下⼀个事件循环’tick’中更新.多数情况我们不需要关⼼这个过程,但是如果你想基于更新后的 DOM 状态来做点什么,这就可能会有些棘⼿。虽然 Vue.js 通常⿎励开发⼈员使⽤“数据驱动”的⽅式思考,避免直接接触 DOM,但是有时我们必须要这么做。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后⽴即使⽤ Vue.nextTick(callback) 。这样回调函数将在 DOM更新完成后被调⽤。
nextTick的应⽤
- 有个需求:在⻚⾯拉取⼀个接⼝,这个接⼝返回⼀些数据,这些数据是这个⻚⾯的⼀个浮层组件要依赖的,然后我在接⼝⼀返回数据就展示了这个浮层组件,展示的同时,上报⼀些数据给后台(这些数据就是⽗组件从接⼝拿的),这个时候,神奇的事情发⽣了,虽然我拿到数据了,但是浮层展现的时候,这些数据还未更新到组件上去,上报失败
const Pop = {
data() {
return {
isShow:false
}
},
template:`
<div v-show = 'isShow'>
{{name}}
</div>
`,
props:['name'],
methods: {
show(){
this.isShow = true;
alert(this.name);
}
},
}
const App = {
data() {
return {
name:''
}
},
created() {
// 模拟异步请求的数据
setTimeout(() => {
this.name = '嘻嘻嘻',
this.$refs.pop.show();
}, 2000);
},
components:{Pop},
template: `<pop ref='pop' :name='name'></pop>`
}
const vm = new Vue({
el: '#app',
components: {
App
}
})
完美解决:
created() {
// 模拟异步请求的数据
setTimeout(() => {
this.name = '嘻嘻嘻',
this.$nextTick(()=>{
this.$refs.pop.show();
})
}, 2000);
},
对象变更检测注意事项
由于JavaScript的限制,Vue不能检测对象属性的添加和删除对于已经创建的实例,Vue不允许动态添加根级别的响应式属性.但是,可以通过 Vue.set(object,key,value) ⽅法向嵌套独享添加响应式属性
<div id="app">
<h3>
{{user.name}}{{user.age}}
<button @click='handleAdd'>添加年龄</button>
</h3>
</div> <script src="./vue.js"></script> <script>
new Vue({
el:'#app',
data:{
user:{},
},
created() {
setTimeout(() => {
this.user = {
name:'张三'
}
}, 1250);
},
methods: {
handleAdd(){
console.log(this);
// ⽆响应式
// this.user.age = 20;
// 响应式的
this.$set(this.user,'age',20);
}
},
})
</script>
this.$set(this.user,'age',20);//它只是全局Vue.set的别名
如果想为已存在的对象赋值多个属性,可以使⽤ Object.assign()
// ⼀次性响应式的添加多个属性
this.user = Object.assign({}, this.user, {
age: 20,
phone: '113131313'
})
混⼊mixin偷懒
混⼊(mixin)提供了⼀种⾮常灵活的⽅式,来分发Vue组件中的可复⽤功能.⼀个混⼊对象可以包含任意组件选项。当组件使⽤混⼊对象时,所有混⼊对象的选项将被“混合”进⼊该组件本身的选项。
<div id="app">
{{msg}} //嘻嘻嘻
</div>
<script src="./vue.js"></script>
<script>
const myMixin = {
data(){
return {
msg:'123'
}
},
created() {
this.sayHello()
},
methods: {
sayHello(){
console.log('hello mixin')
}
},
}
new Vue({
el: '#app',
data(){
return {
msg:'嘻嘻嘻'
}
},
mixins: [myMixin]
})
mixin应用
我们有⼀对不同的组件,他们的作⽤是切换⼀个状态布尔值,⼀个模态框和⼀个提示框.这些提示框和模态框除了在功能,没有其它共同点:它们看起来不⼀样,⽤法不⼀样,但是逻辑⼀样
<div id="app">
<App></App>
</div> <script src="./vue.js"></script> <script>
// 全局混⼊ 要格外⼩⼼ 每次实例创建 都会调⽤
Vue.mixin({
created(){
console.log('hello from mixin!!');
}
})
// 抽离
const toggleShow = {
data() {
return {
isShow: false
}
},
methods: {
toggleShow() {
this.isShow = !this.isShow
}
}
}
const Modal = {
template: `<div v-if='isShow'><h3>模态框组件</h3></div>`,
data() {
return {
}
},
mixins:[toggleShow]
}
const ToolTip = {
data() {
return {
}
},
template: `<div v-if='isShow'><h3>提示组件</h3></div>`,
mixins:[toggleShow]
}
const App = {
data() {
return {
}
},
template: `
<div>
<button @click='handleModel'>模态框
</button>
<button @click='handleToolTip'>提示框
</button>
<Modal ref='modal'></Modal>
<ToolTip ref="toolTip"></ToolTip>
</div>
`,
components: {
Modal,
ToolTip
},
methods: {
handleModel() {
this.$refs.modal.toggleShow()
},
handleToolTip() {
this.$refs.toolTip.toggleShow()
}
},
}
new Vue({
el: '#app',
data: {},
components: {
App
},
})
购物车案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.container {
}
.container .cart {
width: 300px;
margin: auto;
}
.container .title {
background-color: lightblue;
height: 40px;
line-height: 40px;
text-align: center;
/*color: #fff;*/
}
.container .total {
background-color: #FFCE46;
height: 50px;
line-height: 50px;
text-align: right;
}
.container .total button {
margin: 0 10px;
background-color: #DC4C40;
height: 35px;
width: 80px;
border: 0;
}
.container .total span {
color: red;
font-weight: bold;
}
.container .item {
height: 55px;
line-height: 55px;
position: relative;
border-top: 1px solid #ADD8E6;
}
.container .item img {
width: 45px;
height: 45px;
margin: 5px;
}
.container .item .name {
position: absolute;
width: 90px;
top: 0;left: 55px;
font-size: 16px;
}
.container .item .change {
width: 100px;
position: absolute;
top: 0;
right: 50px;
}
.container .item .change a {
font-size: 20px;
width: 30px;
text-decoration:none;
background-color: lightgray;
vertical-align: middle;
}
.container .item .change .num {
width: 40px;
height: 25px;
}
.container .item .del {
position: absolute;
top: 0;
right: 0px;
width: 40px;
text-align: center;
font-size: 40px;
cursor: pointer;
color: red;
}
.container .item .del:hover {
background-color: orange;
}
</style>
</head>
<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 = {
props: ['uname'],
template: `
<div class="title">{{uname}}的商品</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="" @click.prevent='sub(item.id)'>-</a>
<input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>
<a href="" @click.prevent='add(item.id)'>+</a>
</div>
<div class="del" @click='del(item.id)'>×</div>
</div>
</div>
`,
methods: {
changeNum: function(id, event){
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'
});
},
del: function(id){
// 把id传递给父组件
this.$emit('cart-del', id);
}
}
}
var CartTotal = {
props: ['list'],
template: `
<div class="total">
<span>总价:{{total}}</span>
<button>结算</button>
</div>
`,
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: '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 :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
},
methods: {
changeNum: function(val) {
// 分为三种情况:输入域变更、加号变更、减号变更
if(val.type=='change') {
// 根据子组件传递过来的数据,跟新list中对应的数据
this.list.some(item=>{
if(item.id == val.id) {
item.num = val.num;
if(val.num<0){
item.num=0
}
// 终止遍历
return true;
}
});
}else if(val.type=='sub'){
// 减一操作
this.list.some(item=>{
if(item.id == val.id) {
item.num -= 1;
if(item.num<0){
item.num=0
}
// 终止遍历
return true;
}
});
}else if(val.type=='add'){
// 加一操作
this.list.some(item=>{
if(item.id == val.id) {
item.num += 1;
// 终止遍历
return true;
}
});
}
},
delCart: function(id) {
// 根据id删除list中对应的数据
// 1、找到id所对应数据的索引
var index = this.list.findIndex(item=>{
return item.id == id;
});
// 2、根据索引删除对应数据
this.list.splice(index, 1);
}
}
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
结束寄语
完结,撒花【鼓掌】
相信通过上面的知识点和案列,应该对vue组件有了一定的了解吧,接下来我们将会继续学习到vue-cli的知识喔…