一、介绍
什么是组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可;
组件化和模块化的不同:
模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一
组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用
二、定义的三种方式
1. 使用 Vue.extend 配合 Vue.component 方法
var login = Vue.extend({ template: '<h1>登录</h1>' }); Vue.component('login', login);
2. 直接使用 Vue.component 方法
Vue.component('register', { template: '<h1>注册</h1>' });
3. 将模板字符串,定义到script标签种
<script id="tmpl" type="x-template"> <div><a href="#">登录</a> | <a href="#">注册</a></div> </script> 同时,需要使用 Vue.component 来定义组件: Vue.component('account', { template: '#tmpl' });
4、例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="vue.js"></script> </head> <body> <div id="app"> <!-- 如果要使用组件,直接,把组件的名称,以 HTML 标签的形式,引入到页面中,即可 --> <com></com> <com2></com2> <p>{{ msg }}</p> <com3></com3> <login></login> </div> <template id="tmp3"> <div> <h1>这是通过 template 元素,在外部定义的组件结构,这个方式,有代码的只能提示和高亮</h1> </div> </template> <template id="tmpl2"> <h1>这是私有的 login 组件</h1> </template> <script> //创建方式一 Vue.component('com', Vue.extend({ template: '<h3>使用 Vue.extend 创建的组件</h3>' })); //创建方式二 Vue.component('com2', { template: '<div><h3>使用 Vue.component 创建出来的组件2222222</h3><span>123</span></div>' }); //创建方式三 Vue.component('com3', { template: '#tmp3' }) var vm = new Vue({ el: '#app', data: { msg:"aaa" }, methods: {}, components: { // 定义实例内部私有组件的 login: { template: '#tmpl2' } }, }); </script> </body> </html>
三、在组件中定义数据和方法
1、定义数据(在组件中,data需要被定义为一个方法)
注意:在子组件中,如果将模板字符串,定义到了script标签中,那么,要访问子组件身上的data属性中的值,需要使用this来访问
Vue.component('account', {
template: '#tmpl',
data() {
return {
msg: '大家好'
}
},
methods:{
login(){
alert('点击了登录按钮');
}
}
});
2、定义方法
Vue.component('com2',{
template:'#tmp2',
methods:{
login(){
alert('点击了登录按钮');
}
}
})
3、例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="vue.js"></script> </head> <body> <die id="app"> <com1></com1> <com2></com2> </die> <template id="tmp2"> <div> <input type="button" value="按钮" @click="login"> </div> </template> <script> // 1. 组件可以有自己的 data 数据 // 2. 组件的 data 和 实例的 data 有点不一样,实例中的 data 可以为一个对象,但是 组件中的 data 必须是一个方法 // 3. 组件中的 data 除了必须为一个方法之外,这个方法内部,还必须返回一个对象才行; // 4. 组件中 的data 数据,使用方式,和实例中的 data 使用方式完全一样!!! Vue.component('com1', { template: '<h1>这是全局组件 --- {{msg}}</h1>', data: function () { return { msg: '这是组件的中data定义的数据' } }, //组件中定义为一个方法 }); Vue.component('com2',{ template:'#tmp2', methods:{ login(){ alert('点击了登录按钮'); } } }) var vm = new Vue({ el: '#app', data: {}, methods: {}, }); </script> </body> </html>
四、Vue中组件切换例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="vue.js"></script> <!-- 动画App3使用的style--> <style> .v-enter, .v-leave-to { opacity: 0; transform: translateX(150px); } .v-enter-active, .v-leave-active { transition: all 0.5s ease; } </style> </head> <body> <!-- 方式一 --> <div id="app"> <a href="" @click.prevent="flag=true">登录1</a> <a href="" @click.prevent="flag=false">注册1</a> <login v-if="flag"></login> <register v-else="flag"></register> </div> <!-- 方式二 --> <div id="app2"> <h1>方式二</h1> <a href="" @click.prevent="comName='login'">登录2</a> <a href="" @click.prevent="comName='register'">注册2</a> <component :is="comName"></component> </div> <!-- 使用:is属性来做动画切换 --> <div id="app3"> <a href="" @click.prevent="comName='login'">动画登录</a> <a href="" @click.prevent="comName='register'">东环注册</a> <transition mode="out-in"> <component :is="comName"></component> </transition> </div> <script> Vue.component('login', { template: '<h3>登录组件</h3>' }) Vue.component('register', { template: '<h3>注册组件</h3>' }) // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { flag: false }, methods: {} }); var vm = new Vue({ el: '#app2', data: { comName: 'login' // 当前 component 中的 :is 绑定的组件的名称 }, methods: {} }); var vm = new Vue({ el: '#app3', data: { comName: 'login' // 当前 component 中的 :is 绑定的组件的名称 }, methods: {} }); </script> </body> </html>
五、Vue中的父子组件使用例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="vue.js"></script> </head> <body> <div id="app"> <!-- 父组件,可以在引用子组件的时候, 通过 属性绑定(v-bind:) 的形式, 把 需要传递给 子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用 --> <com1 v-bind:parentmsg="msg"></com1> </div> <h1>父组件把方法传个子组件演示</h1> <div id="app2"> <!-- 父组件向子组件 传递 方法,使用的是 事件绑定机制; v-on, 当我们自定义了 一个 事件属性之后,那么,子组件就能够,通过某些方式,来调用 传递进去的 这个 方法了 --> <com2 @func="show"></com2> </div> <template id="tmpl"> <div> <h1>这是 子组件</h1> <input type="button" value="这是子组件中的按钮 - 点击它,触发 父组件传递过来的 func 方法" @click="myclick"> </div> </template> <script> var vm = new Vue({ el: '#app', data: { msg: '父组件中的数据' }, methods: {}, components: { // 结论:经过演示,发现,子组件中,默认无法访问到 父组件中的 data 上的数据 和 methods 中的方法 com1: { data() { // 注意: 子组件中的 data 数据,并不是通过 父组件传递过来的,而是子组件自身私有的,比如: 子组件通过 Ajax ,请求回来的数据,都可以放到 data 身上; // data 上的数据,都是可读可写的; return { title: '123', content: 'qqq' } }, template: '<h1 @click="change">这是子组件 --- {{ parentmsg }}</h1>', // 注意: 组件中的 所有 props 中的数据,都是通过 父组件传递给子组件的 // props 中的数据,都是只读的,无法重新赋值 props: ['parentmsg'], // 把父组件传递过来的 parentmsg 属性,先在 props 数组中,定义一下,这样,才能使用这个数据 directives: {}, filters: {}, components: {}, methods: { change() { this.parentmsg = '被修改了' } } } } }); // 定义了一个字面量类型的 组件模板对象 var com2 = { template: '#tmpl', // 通过指定了一个 Id, 表示 说,要去加载 这个指定Id的 template 元素中的内容,当作 组件的HTML结构 data() { return { sonmsg: { name: '小头儿子', age: 6 } } }, methods: { myclick() { // 当点击子组件的按钮的时候,如何 拿到 父组件传递过来的 func 方法,并调用这个方法 alert(this.sonmsg.name) } } }; var vm = new Vue({ el: '#app2', data: { datamsgFormSon: null }, methods: { show(data) { // console.log('调用了父组件身上的 show 方法: --- ' + data) // console.log(data); this.datamsgFormSon = data } }, components: { com2 // com2: com2 } }); </script> </body> </html>
六、组件评论案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="vue.js"></script> </head> <body> <div id="app"> <cmt-box @func="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="tmpl"> <div> <div class="form-group"> <label>评论人:</label> <input type="text" class="form-control" v-model="user"> </div> <div class="form-group"> <label>评论内容:</label> <textarea class="form-control" v-model="content"></textarea> </div> <div class="form-group"> <input type="button" value="发表评论" class="btn btn-primary" @click="postComment"> </div> </div> </template> <script> var commentBox = { data() { return { user: '', content: '' } }, template: '#tmpl', methods: { postComment() { // 发表评论的方法 // 评论数据存放到 localStorage 中 localStorage.setItem('cmts', '') // localStorage 只支持存放字符串数据, 要先调用 JSON.stringify // 在保存 最新的 评论数据之前,要先从 localStorage 获取到之前的评论数据(string), 转换为 一个 数组对象, 然后,把最新的评论, push 到这个数组 // 如果获取到的 localStorage 中的 评论字符串,为空不存在, 则 可以 返回一个 '[]' 让 JSON.parse 去转换 // 把 最新的 评论列表数组,再次调用 JSON.stringify 转为 数组字符串,然后调用 localStorage.setItem() var comment = { id: Date.now(), user: this.user, content: this.content } // 从 localStorage 中获取所有的评论 var list = JSON.parse(localStorage.getItem('cmts') || '[]') list.unshift(comment) // 重新保存最新的 评论数据 localStorage.setItem('cmts', JSON.stringify(list)) this.user = this.content = '' //子组件可以使用 $emit 触发父组件的自定义事件 this.$emit('func') //触发loadComments()方法 } } }; var vm = new Vue({ el: '#app', data: { list: [ ] }, beforeCreate(){ // 注意:这里不能调用 loadComments 方法,因为在执行这个钩子函数的时候,data 和 methods 都还没有被初始化好 }, created(){ this.loadComments() }, methods: { loadComments() { // 从本地的 localStorage 中,加载评论列表 var list = JSON.parse(localStorage.getItem('cmts') || '[]') this.list = list } }, components: { 'cmt-box': commentBox } }); </script> </body> </html>