什么是组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可;
组件化和模块化的不同:
<html>
一样
var com1 = Vue.extend({
template: '<h2>第一种方式</h2>',
data(){
return{
}
}
});
Vue.component('mycom1', com1);
Vue.component('mycom2', {
template: '<h4>第一种方式的最终简写</h4>',
data(){
return {
}
}
});
<script id="tmpl" type="template">
<div>
<a href="#">{{msg.login}}</a> | <a href="#">{{msg.register}}</a>
</div>
</script>
Vue.component('account', {
template: '#tmpl',
data(){
return{
msg: {login: '登录',register: '注册'}
}
}
});
<template>
中定义 HTML 结构
<template id="tmp">
<div>
<h2>第二种方式</h2>
<h3 v-text="msg"></h3>
</div>
</template>
使用 Vue.component 中 template 属性 进行调用
Vue.component('myCom3', {
template: '#tmp',
data(){
return{
msg: '好耶~'
}
}
});
new Vue({
el: '#app',
data: {},
methods:{},
filters:{},
directives:{},
components: {
myLogin: {
template: `<div>
<h1>login 组件</h1>
<p>私有组件</p>
</div>`,
data(){
return{
}
}
}
},
beforeCreate(){},
created(){},
beforeMount(){},
Mounted(){},
beforeUpdate(){},
updated(){},
beforeDestroy(){},
destroyed(){}
});
注意: 组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹!
分离私有组件的定义,将组件模板对象分开定义
const myLogin = {
template: `<div>
<h1>login 组件</h1>
<p>私有组件</p>
</div>`,
data(){
return{
}
}
}
new Vue({
el: '#app',
data: {},
methods:{},
components: {
// 组件名称:组件的模板对象
// myLogin: myLogin
myLogin,
}
});
我们的子组件是可以多级嵌套的,子组件中还可以在声明子组件。
<div id="app">
<account></account>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
components: {
account: {
template: `<div>
<h1>这是Account组件{{name}}</h1>
<login></login>
<register></register>
</div>`,
data(){
return {
name: 'hello world!'
}
},
components: {
login: {
template: "<h3>这是登录组件</h3>"
},
register: {
template: '<h3>这是注册组件</h3>'
}
}
}
}
});
</script>
<div id="app">
<mycom1></mycom1>
<mycom2></mycom2>
<my-com3></my-com3>
<my-login></my-login>
</div>
因为组件需要被重用,为了让每个组件有自己的私有作用域,data需要为函数并且返回一个对象
在组件中,data
需要被定义为一个方法
通过计数器案例演示
<div id="app">
<myCount></myCount><hr>
<myCount></myCount><hr>
<myCount></myCount><hr>
</div>
<template id="tmp">
<input type="button" value="+1" @click="increment">
<h3 v-text="count"></h3>
</template>
<script>
Vue.component('myCount',{
template: '#tmp',
data: function (){
return {
count: 0
}
},
methods:{
increment(){
this.count++;
}
}
});
new Vue({
el:'#app',
data:{ },
methods:{ }
})
</script>
<div id="app">
<a href="" @click.prevent="flag = true">登录</a>
<a href="" @click.prevent="flag = false">注册</a>
<transition mode="out-in">
<login v-if="flag"></login>
<register v-else="flag"></register>
</transition>
</div>
<script>
Vue.component('login',{
template: '<h3>登录组件</h3>'
})
Vue.component('register',{
template: '<h3>注册组件</h3>'
})
new Vue({
el: '#app',
data:{
flag: true,
},
methods:{
}
})
</script>
<component>
进行切换:is
指定要显示的组件名称
<style>
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(150px);
}
.v-enter-active,
.v-leave-active {
transition: all .5s ease;
}
</style>
<div id="app">
<a href="" @click.prevent="comName = 'login'">登录</a>
<a href="" @click.prevent="comName = 'register'">注册</a>
<a href="" @click.prevent="comName = 'forget'">忘记密码</a>
<!-- 通过mode属性,设置组件切换时候的模式 -->
<transition mode="out-in">
<component :is="comName"></component>
</transition>
</div>
<script>
Vue.component('login',{
template: '<h3>登录组件</h3>'
})
Vue.component('register',{
template: '<h3>注册组件</h3>'
})
Vue.component('forget',{
template: '<h3>忘记密码组件</h3>'
})
new Vue({
el: '#app',
data:{
comName: 'login'
},
methods:{
}
})
</script>
注意:一定要使用props
属性来定义父组件传递过来的数据,由于 props 默认是单向绑定,即父组件的属性发生改变,子组件也会改变。反之则不会。props中数据是只读的
使用v-bind
或简化指令:,将数据传递到子组件中
<div id="app">
<!-- 父组件可以在引用子组件的时候,通过属性绑定的形式 ,把需要传递给子组件的数据,以属性绑定的形式传递到子组件内部供使用-->
<my-component v-bind:parentmsg="msg" :msg="message"></my-component>
</div>
<script>
Vue.component('myComponent', {
template: '<span>{{ parentmsg }}--{{msg}}</span>',
// props:声明待接收的父组件数据
// props中数据都是只读的,无法重新赋值
props: ['parentmsg', 'msg'],
// 把父组件传递过来的parentmsg和msg属性,现在props数组中定义一下,这样才能使用父组件传递过来的数据
data() {
return {
// 子组件中也可以有自己的数据在data中
}
}
})
var vm = new Vue({
el: '#app',
data: {
msg: 'hello 子组件',
message: 'hello world'
}
})
</script>
<div id="app">
<!-- 默认 单向绑定 -->
<my-component :msg="message"></my-component>
<!-- 双向绑定 -->
<my-component :msg.sync="message"></my-component>
<!-- 单向绑定 -->
<my-component :msg.once="message"></my-component>
</div>
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
getMsg
是父组件中methods
中定义的方法名称,func
是子组件调用传递过来方法时候的方法名称
<son v-on:func="show"></son>
this.$emit('方法名', 要传递的数据)
方式,来调用父组件中的方法,同时把数据传递给父组件使用
<div id="app">
<h1>{{msg}}</h1>
<!-- 父组件向子组件传递方法,使用的是事件绑定机制 -->
<!-- v-on,当我们自定义了一个事件属性之后,那么子组件就能够通过某种方式来调用 -->
<son @func="getMsg"></son>
</div>
<!-- 组件模板定义 -->
<template id="son">
<div>
<h1>这是子组件</h1>
<input type="button" value="向父组件传值" @click="sendMsg" />
</div>
</template>
<script>
const son = {
template: '#son', // 组件模板Id
data(){
return {
msg: "我是子组件传递过来的数据"
}
},
methods: {
sendMsg() { // 按钮的点击事件
// 我们可以通过调用父组件传递过来的方法,将子组件数据通过参数形式传递给父组件中
this.$emit('func', this.msg); // 调用父组件传递过来的方法,同时把数据传递出去
}
}
}
// 子组件的定义方式
Vue.component('son',son);
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
msg: "父组件"
},
methods: {
getMsg(val){ // 子组件中,通过 this.$emit() 实际调用的方法,在此进行定义
this.msg = val;
console.log("调用了父组件身上的getMsg方法");
alert(val);
}
}
});
</script>
ref
属性,然后赋予一个优雅的名字
<h3 ref="myh3">我是H3</h3>
<!-- login组件 -->
<login ref="login"></login>
$refs
来获取DOM
getElement() { console.log(this.$refs.myh3); console.log(this.$refs.login.$el.innerText);}
<style>
.pagination {
display: flex;
list-style: none;
}
.page-item {
width: 25px;
height: 25px;
line-height: 25px;
text-align: center;
border: 1px solid #ccc;
}
.page-item.active {
background-color: skyblue;
}
</style>
<div id="app">
<page-component :total="total"></page-component>
</div>
<template id="page-component">
<ul class="pagination">
<li :class="p == page ? 'page-item active' : 'page-item'" v-for="p in pagecount">
<a href="#" class="page-link" @click.prevent="page = p">{{ p }}</a>
</li>
</ul>
</template>
<script>
const pageComponent = {
template: '#page-component',
name: 'PageComponent',
props: ['total'],
data: function() {
return {
page: 1, // 当前页码
pagesize: 10 // 每页显示条数
}
},
computed: {
pagecount: function() {
// 总页码数
return Math.ceil(this.total / this.pagesize)
}
}
}
var vm = new Vue({
el: '#app',
data: {
total: 35
},
components: {
pageComponent
}
})
</script>
<div id="app">
<div v-for="goods in goodslist">
<p>商品名称:{{ goods.name }}</p>
<p>单价:{{ goods.price }}</p>
<cart-component v-model="goods.count"></cart-component>
<hr>
</div>
<div>
订单总金额:{{ amount }} 元
</div>
</div>
<template id="cart-component">
<div class="cart">
<button @click="count--; updateCount();">-</button>
<input type="text" v-model="count" style="width: 50%;" @input="updateCount()">
<button @click="count++; updateCount();">+</button>
</div>
</template>
<script>
const cartComponent = {
name: 'Cart',
template: '#cart-component',
// 在组件中不允许直接修改 props 中的数据
props: ['value'],
data: function() {
return {
count: this.value
}
},
methods: {
// v-model 指令双向数据绑定,修改父组件内容
updateCount: function() {
// 触发 input 事件
this.$emit('input', this.count)
}
}
}
const app = new Vue({
el: '#app',
data: {
goodslist: [
{
name: 'iphone 8 plus',
price: 5888,
count: 0
},
{
name: 'iphone x',
price: 7222,
count: 0
}
]
},
computed: {
// 当前订单总金额
amount: function() {
var money = 0;
this.goodslist.forEach(goods => {
money += parseInt(goods.count) * parseInt(goods.price)
})
return money;
}
},
components: {
cartComponent
}
})
</script>
<div id="app">
<cmt-box @loadcomments="loadComments"></cmt-box>
<ul class="list-group">
<li class="list-group-item" v-for="item in list" :key="item.id">
<span class="badge">评论人:{{item.user}}</span> {{item.content}}
</li>
</ul>
</div>
<template id="temp">
<div>
<div class="form-group">
<label for="">评论人:</label>
<input type="text" class="form-control" v-model="user">
</div>
<div class="form-group">
<label for="">评论内容:</label>
<textarea class="form-control" v-model="content"></textarea>
</div>
<div class="form-group">
<input type="button" value="发表评论" @click="postComment">
</div>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data: {
list: []
},
methods: {
loadComments() {
const list = JSON.parse(localStorage.getItem('cmts') || '[]');
this.list = list;
console.log(6666);
}
},
created() {
this.loadComments();
},
components: {
'cmt-box': {
template: '#temp',
data() {
return {
user: '',
content: ''
}
},
methods: {
postComment() {
const comment = {
id: Date.now(),
user: this.user,
content: this.content
};
// 从localStorage中获取所有的评论
const list = JSON.parse(localStorage.getItem('cmts') || '[]');
list.unshift(comment);
// 保存最新的评论数据
localStorage.setItem('cmts', JSON.stringify(list));
this.user = this.content = '';
this.$emit('loadcomments');
}
},
}
}
});
</script>