定义Vue组件
什么是组件:组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能就可以去调对应的组件即可
组件化和模块化的不同
- 模块化:是从代码逻辑的角度进行划分的,方便代码分层开发,保证每个功能模块的职能单一
- 组件化:是从UI界面的角度进行划分的,前端的组件化,方便UI组件的重用
全局组件定义的三种方式
1.使用Vue.extend配合Vue.component
2.直接使用Vue.component方法
3.将模板字符串,定义到script标签中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>创建组件</title>
<script src="vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<!-- 还是使用标签形式,引入自己的组件 -->
<my-com1></my-com1>
</div>
<hr >
<div id="app2">
<my-com1></my-com1>
<mycom2></mycom2>
</div>
<template id="tmp1">
<h3>这是通过 template 元素,在外部定义的组件结构,这个方式有代码有提示和高亮</h3>
</template>
<script>
// 1.1 使用Vue.extend 来创建全局的Vue组件
// var com1 = Vue.extend({
// template:'<h3>5<sub>2<sub>这是使用Vue.extend 创建的组件5<sup>3<sup></h3>' //通过 template 属性,指定了组件要展示的HTML结构
// })
// 1.2 使用Vue.component('组件的名称',创建出来的组件模板对象)
//如果使用Vue.component 定义全局组件的时候,组件名称使用了驼峰命名,则在引用组件的时候,需要把大写的驼峰改为小写的字母,同时,两个单词之间使用-链接
// 如果不使用驼峰,则直接拿名称来使用即可
// Vue.component('myCom1',com1)
// Vue.component 第一个参数:组件的名称,将来在引用组件的时候,就是一个标签形式来引入它的
// 第二个参数:Vue.extend 创建的组件,其中template 就是组件将来要展示的HTML 内容
// 第一种创建组件的方式
// Vue.component('myCom1',Vue.extend({
// template:'<h3>这是使用Vue.extend 创建的组件</h3>'
// }))
// 第二种创建组件方式
// 注意:不论是哪种方式创建出来的组件,组件的template 属性指向的模板内容,必须有且只能有唯一的一个根元素
// Vue.component('myCom1',{template:'<div><h3>这是直接使用 Vue.component 创建出来的组件</h3><span>123</span></div>'})
// 第三种创建组件的方式
Vue.component('myCom1',{
template:'#tmp1'
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
});
// 定义私有组件
var vm = new Vue({
// 控制区域
el: '#app2',
// 私有数据
data: {},
// 私有方法
methods: {},
// 私有组件
components:{
mycom2:
{template:'<h3>这是私有组件</h3>'}
},
// 私有过滤器
filters:{},
// 自定义私有指令
directives:{},
// 生命周期函数
beforeCreate(){},
created(){},
beforeMount(){},
mounted(){},
beforeUpdate(){},
updated(){},
beforeDestroy(){},
destroyed(){
}
});
</script>
</body>
</html>
组件中展示数据和响应事件
1.在组价中,data需要被定义为一个方法
2.在组件中,如果将模板字符串,定义到了script标签中,那么,要访问子组件身上的data属性中的值,需要使用this来访问
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>组件中的data和methods</title>
<script src="vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<mycom1></mycom1>
</div>
<script>
// 1.组件可以有自己的data数据
// 2.组件中的data和实例的data有点不一样,实例中的data可以为一个对象,但是组件中的data必须是一个方法
// 3.组件中的data除了必须为一个方法之外,这个方法内部还必须返回一个对象才行
// 4.组件中的data数据,使用方式和实例中的data使用方法完全一样!!!
Vue.component('mycom1',
{
template:'<h1>这是全局组件-----{{msg}}</h1>',
data:function(){
return {
msg:'这是组件中data定义的数据'
}
}
})
var vm=new Vue({
el:'#app',
data:{},
methods:{}
});
</script>
</body>
</html>
【重点】为什么组件中的data属性必须定义为一个方法并返回一个对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>计数器</title>
<script src="vue-2.4.0.js"></script>
</head>
<body>
<!-- 需求:解释为什么组件中的data数据需要返回一个对象 -->
<div id="app">
<mycom1></mycom1>
<hr>
<mycom1></mycom1>
<hr>
<mycom1></mycom1>
<hr>
<mycom1></mycom1>
</div>
<template id="tmp">
<div id="">
<input type="button" name="" id="" value="+1" @click="add" />
<h3>{{count}}</h3>
</div>
</template>
<script>
// 创建一个对象
var dataobj = {
count: 0
}
// 这是一个计数器组件,身上有个按钮,每当点击按钮,让data中的count值+1
Vue.component('mycom1', {
template: '#tmp',
data: function() {
// 如果返回对象的话,组件会跟着一起动
// return dataobj
return {count:0}
},
methods: {
add() {
this.count++
}
},
}, )
var vm = new Vue({
el: '#app',
data: {
},
methods: {
}
});
</script>
</body>
</html>
使用flag标识符结合v-if和v-else切换组件和使用is属性来切换不同的子组件,并添加切换动画
v-if组件切换
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>组件切换</title>
<script src="vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<a href="" @click.prevent="flag=true">登录</a>
<a href="" @click.prevent="flag=false">注册</a>
<login v-if="flag"></login>
<register v-else="flag"></register>
</div>
<script>
Vue.component('login',{
template:'<h3>登录组件</h3>'
})
Vue.component('register',{
template:'<h3>注册组件</h3>'
})
var vm=new Vue({
el:'#app',
data:{
flag:false
},
methods:{}
});
</script>
</body>
</html>
:is属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>组件切换方式2</title>
<script src="vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<a href="" @click.prevent="comName='login'">登录</a>
<a href="" @click.prevent="comName='register'">注册</a>
<!-- Vue 提供component,来展示对应名称的组件 -->
<!-- component 是一个占位符,:is 属性,可以用来指定要展示的组件的名称 -->
<component :is="comName"></component>
<!-- 总结:当前学习了几个Vue 提供的标签 -->
<!-- component,template,transition,transitionGroup -->
</div>
<script>
// 创建组件
Vue.component('login',{template:'<h3>登录组件</h3>'})
Vue.component('register',{template:'<h3>注册组件</h3>'})
var vm=new Vue({
el:'#app',
data:{
comName:'login'
},
methods:{}
});
</script>
</body>
</html>
动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>组件切换方式2</title>
<style type="text/css">
.v-enter,
.v-leave-to{
opacity: 0;
transform: translateX(150px);
}
.v-enter-active,
.v-leave-active{
transition: all 0.8s ease;
}
</style>
<script src="vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<a href="" @click.prevent="comName='login'">登录</a>
<a href="" @click.prevent="comName='register'">注册</a>
<!-- Vue 提供component,来展示对应名称的组件 -->
<!-- component 是一个占位符,:is 属性,可以用来指定要展示的组件的名称 -->
<!-- 通过mode属性,设置组件切换时候的模式 -->
<transition mode="out-in">
<component :is="comName"></component>
</transition>
<!-- 总结:当前学习了几个Vue 提供的标签 -->
<!-- component,template,transition,transitionGroup -->
</div>
<script>
// 创建组件
Vue.component('login',{template:'<h3>登录组件</h3>'})
Vue.component('register',{template:'<h3>注册组件</h3>'})
var vm=new Vue({
el:'#app',
data:{
comName:'login'
},
methods:{}
});
</script>
</body>
</html>
父组件向子组件传值
1.组件实例定义方式,注意:一定要使用props属性来定义父组件传递过来的数据
2.使用v-bind或简化指令,将数据传递到子组件中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>父组件向子组件传值</title>
<script src="vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<!-- 父组件可以在引用子组件的时候,通过属性绑定(v-bind:)的形式,把需要传递给子组件的数据,以属性绑定的形式,传递到子组件的内部,供子组件使用 -->
<com1 :parentmsg="msg"></com1>
</div>
<script>
var vm=new Vue({
el:'#app',
data:{
msg:'这是父组件中的数据'
},
methods:{},
components:{
// 结论:经过演示,发现子组件中,默认无法访问到父组件中的data上的数据和methods中的方法
com1:{
data(){
// 注意:子组件中的data数据,并不是通过父组件传递过来的,而是子组件自身私有的,比如:子组件通过ajax,请求回来的数据,都可以放到data身上
// data上的数据,都是可读可写的
return {
title:'123',
conent:'qqq'
}
},
template:'<h1 >这是子组件----{{ parentmsg }}</h1>',
// 注意:组件中的所有props 中的数据,都是通过父组件传递给子组件的
// props 中的数据,都是只读的,无法重新赋值
props:['parentmsg'],//把父组件传递过来的parentmsg属性,先在props数组中,定义一下这样才能使用这个数据
methods:{
// change(){
// this.parentmsg='被修改了!'
// }
}
}
}
});
</script>
</body>
</html>
子组件向父组件传值
1.原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去
2.父组件将方法的引用传递给子组件,其中getmsg是父组件中methods中定义的方法名称,func是子组件调用床底过来方法时候的方法名称
3.子组件内部通过this.$emit(‘方法名’,要传递的数据)方式,来调用父组件中的方法,同事把数据传递给父组件使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>父组件向子组件传方法</title>
<script src="vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<h2>{{datamsgFormSon}}</h2>
<!-- 父组件向子组件传递方法,使用的是事件绑定机制:v-on: 当我们定义了一个事件属性之后,那么子组件就能够通过某些方式来调用传递进去的这个方法了 -->
<com1 @func="show" ></com1>
</div>
<template id="tmp1">
<div id="">
<h1>这是子组件 ---- {{sonmsg.name}}</h1>
<input type="button" name="" id="" value="这是子组件的按钮点击触发父组件的方法" @click="myclick" />
</div>
</template>
<script>
// 定义了一个字面量类型的组件模板对象
var com1 = {
template: '#tmp1', //通过指定了一个Id,表示说,要去加载这个指定Id的template 元素中的内容,当作组件的HTML结构
data() {
return {
sonmsg: {
name: '小头爸爸',
age: 35
}
}
},
methods: {
myclick() {
// 当点击子组件的按钮的时候,如何拿到父组件传递过来的func方法
// emit :触发、调用、发射的意思
// 第二个变量就是参数
this.$emit('func', this.sonmsg.name)
}
}
}
var vm = new Vue({
el: '#app',
data: {
datamsgFormSon: null
},
methods: {
// 可以带参数
show(data) {
console.log('调用了父组件身上的show方法 -----' + data)
console.log(data)
this.datamsgFormSon = data
}
},
components: {
com1
}
});
</script>
</body>
</html>
评论列表案例
使用this.$refs来获取元素和组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ref获取DOM元素和组件</title>
<script src="vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<input type="button" name="" id="" value="获取元素" @click="getElement" ref="mybtn" />
<h3 id="myh3" ref="myh3">哈哈哈,今天天气太好了!</h3>
<hr >
<login ref="mylogin"></login>
</div>
<script>
var login = {
template:'<h1>登录组件</h1>',
data(){
return {
msg:'son Msg'
}
},
methods:{
show(){
console.log('调用了子组件的方法')
}
}
}
var vm=new Vue({
el:'#app',
data:{},
methods:{
getElement(){
console.log(document.getElementById('myh3').innerHTML)
// ref 是英文单词【reference】值类型和引用类型 referenceError
console.log(this.$refs.myh3.innerHTML)
console.log(this.$refs.mylogin.msg)
console.log(this.$refs.mylogin.msg)
this.$refs.mylogin.show()
}
},
components:{
login
}
});
</script>
</body>
</html>
评论列表
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>评论列表</title>
<script src="vue-2.4.0.js"></script>
</head>
<link rel="stylesheet" type="text/css" href="bootstrap-3.3.7.css" />
<body>
<div id="app">
<login @func='loadComments'> </login>
<ul class="list-group">
<li class="list-group-item" v-for="item in list" :key="item.id">
<span id="" class="badge">评论人:{{item.user}}
</span>
{{item.content}}
</li>
</ul>
</div>
<template id="tmp">
<div>
<div class="form-group">
<label>评论人:</label>
<input type="text" class="form-control" v-model="user">
</div>
<div class="form-group">
<label>评论内容:</label>
<textarea name="" id="input" class="form-control" rows="3" required="required" v-model="content" @keyup.enter="postComment"></textarea>
</div>
<div class="form-group">
<input type="button" name="" id="" value="发表评论" class="btn btn-primary" @click="postComment" />
</div>
</div>
</template>
<script>
var login = {
template: '#tmp',
data() {
return {
user: '',
content: ''
}
},
methods: {
// 发表评论的方法
// 分析:发表评论的业务逻辑
// 1.评论数据存到哪里去??? 存放到 localstorage 中 localstorage 中localstorage.setItem('cmts','')
// 2.先组织出一个最新的评论数据对象
// 3.想办法,把第二步中,得到的评论对象,保存到localstorage中
// 3.1 localstorage 只支持存放字符串数据,要先调用JSON.stringify
// 3.2 在保存最新的评论数据之前,要先从localstorage获取到之前的评论数据(string),转换为一个数组对象,然后把最新的评论push到这个数组
// 3.3 如果获取到的localstorage中的评论字符串为空不存在,则可以返回一个[] 让JSON.parse去转换
// 3.4 把最新的评论列表数组,再次调用JSON.stringify转为数组字符串,然后调用localstorage.setItem()
postComment() {
var comment = {
id: Date.now(),
user: this.user,
content: this.content
}
// 从localstorage 中获取所有的评论
var list = JSON.parse(localStorage.getItem('cmts1') || '[]')
list.unshift(comment)
// 重新保存最新的评论数据
localStorage.setItem('cmts1', JSON.stringify(list))
this.user = this.content = ''
// this.loadComments()
this.$emit('func')
}
}
}
var vm = new Vue({
el: '#app',
data: {
list: [{
id: Date.now(),
user: '李白',
content: '黄河之水天上来'
},
{
id: Date.now(),
user: '李清照',
content: '至今思项羽'
},
{
id: Date.now(),
user: '屈原',
content: '虽九死其犹为悔'
},
]
},
methods: {
loadComments() { // 从本地的localstorage中,加载评论列表
var list = JSON.parse(localStorage.getItem('cmts1') || '[]')
this.list = list
}
},
components: {
login
},
beforeCreate() {
//注意:这里不能调用loadcomments方法,因为在执行这个钩子函数的时候,data和methods都还没有被初始化好
},
created() {
this.loadComments()
}
});
</script>
</body>
</html>