企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] ## 1.定义Vue组件 什么是组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可;组件化和模块化的不同: * 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一; * 组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用; ### 1.1 全局组件定义的三种方式 #### **1. 使用 Vue.extend 配合 Vue.component 方法:** ~~~ var login = Vue.extend({     template: '<h1>登录</h1>'   }); Vue.component('login', login); ~~~ #### **2. 直接使用 Vue.component 方法**: 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'   }); ~~~ > 注意: 组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹! #### **4. 通过模板创建组件** 1. 在HTML中定义一个template模板,并制定id ~~~ <template id="tmpl"> <div> <h1>这是 子组件</h1> <input type="button" value="这是子组件中的按钮 - 点击它,触发 父组件传递过来的 func 方法" @click="myclick"> </div> </template> ~~~ 2. 定义一个模板组件对象,加入template模板 模板对象变量名就是 组件的名称,供后续的使用 ~~~ // 定义了一个字面量类型的 组件模板对象 var com2 = { template: '#tmpl', // 通过指定了一个 Id, 表示 说,要去加载 这个指定Id的 template 元素中的内容,当作 组件的HTML结构 data() { return { sonmsg: { name: '小头儿子', age: 6 } } }, methods: { myclick() { // 当点击子组件的按钮的时候,如何 拿到 父组件传递过来的 func 方法,并调用这个方法??? // emit 英文原意: 是触发,调用、发射的意思 // this.$emit('func123', 123, 456) this.$emit('func', this.sonmsg) } } } ~~~ 3. components注册组件 ~~~ components: { com2 } ~~~ ### **1.2 组件中展示数据和响应事件** **1. 在组件中,`data`需要被定义为一个方法,例如:** 即组件的每次使用,都返回相同的值,不会互相影响 ~~~ Vue.component('account', {     template: '#tmpl',     data() {       return {         msg: '大家好!'       }     },     methods:{       login(){         alert('点击了登录按钮');       }     }   }); ~~~ **2. 在子组件中,如果将模板字符串,定义到了script标签中,那么,要访问子组件身上的`data`属性中的值,需要使用`this`来访问;** ### 【重点】为什么组件中的data属性必须定义为一个方法并返回一个对象 1. 通过计数器案例演示 ### **1.3 使用`components`属性定义局部子组件** 1. 组件实例定义方式: ~~~ <script>   // 创建 Vue 实例,得到 ViewModel   var vm = new Vue({     el: '#app',     data: {},     methods: {},     components: { // 定义子组件       account: { // account 组件(组件名称)         template: '<div><h1>这是Account组件{{name}}</h1><login></login></div>', // 在这里使用定义的子组件         components: { // 定义子组件的子组件           login: { // login 组件             template: "<h3>这是登录组件</h3>"           }         }       }     }   }); </script> ~~~ 2. 引用组件: <组件名称>,就像定义了一个HTML标签一样 ~~~ <div id="app">   <account></account> </div> ~~~ ## 2. **使用`flag`标识符结合`v-if`和`v-else`切换组件** 1. 页面结构: ~~~ <div id="app">   <input type="button" value="toggle" @click="flag=!flag">   <my-com1 v-if="flag"></my-com1>   <my-com2 v-else="flag"></my-com2> </div> ~~~ 2. Vue实例定义: ~~~ <script>   Vue.component('myCom1', {     template: '<h3>奔波霸</h3>'   }) ​   Vue.component('myCom2', {     template: '<h3>霸波奔</h3>'   }) ​   // 创建 Vue 实例,得到 ViewModel   var vm = new Vue({     el: '#app',     data: {       flag: true     },     methods: {}   }); </script> ~~~ ## 3 **. 使用`:is`属性来切换不同的子组件,并添加切换动画** 1. 组件实例定义方式: ~~~ // 登录组件   const login = Vue.extend({     template: `<div>       <h3>登录组件</h3>     </div>`   });   Vue.component('login', login); ​   // 注册组件   const register = Vue.extend({     template: `<div>       <h3>注册组件</h3>     </div>`   });   Vue.component('register', register); ​   // 创建 Vue 实例,得到 ViewModel   var vm = new Vue({     el: '#app',     data: { comName: 'login' },     methods: {}   }); ~~~ 2. 使用`component`标签,来引用组件,并通过`:is`属性来指定要加载的组件: ~~~ <div id="app">   <a href="#" @click.prevent="comName='login'">登录</a>   <a href="#" @click.prevent="comName='register'">注册</a>   <hr>   <transition mode="out-in">     <component :is="comName"></component>   </transition> </div> ~~~ 3. 添加切换样式: ~~~ <style>   .v-enter,   .v-leave-to {     opacity: 0;     transform: translateX(30px);   } ​   .v-enter-active,   .v-leave-active {     position: absolute;     transition: all 0.3s ease;   } ​   h3{     margin: 0;   } </style> ~~~ ## **4. 父组件向子组件传值** ### 4.1 `props` 1. 组件实例定义方式,注意:一定要使用`props`属性来定义父组件传递过来的数据 ~~~ <script>   // 创建 Vue 实例,得到 ViewModel   var vm = new Vue({     el: '#app',     data: {       msg: '这是父组件中的消息'     },     components: {       son: {         template: '<h1>这是子组件 --- {{finfo}}</h1>',         props: ['finfo']       }     }   }); </script> ~~~ 2. 使用`v-bind`或简化指令,将数据传递到子组件中: ~~~ <div id="app">   <son :finfo="msg"></son> </div> ~~~ ## **5.子组件向父组件传值** 1. 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去; 2. 父组件将方法的引用传递给子组件,其中,`getMsg`是父组件中`methods`中定义的方法名称,`func`是子组件调用传递过来方法时候的方法名称 **总就是将父组件的方法,传入子组件,子组件调用父组件的方法(可传值)** ~~~ <son @func="getMsg"></son> ~~~ 3. 子组件内部通过`this.$emit('方法名', 要传递的数据)`方式,来调用父组件中的方法,同时把数据传递给父组件使用 ~~~ <div id="app">   <!-- 引用父组件,将父组件的函数引入 -->   <son @func="getMsg"></son> ​   <!-- 组件模板定义 -->   <script type="x-template" id="son">     <div>       <input type="button" value="向父组件传值" @click="sendMsg" />     </div>   </script> </div> ​ <script>   // 子组件的定义方式   Vue.component('son', {     template: '#son', // 组件模板Id     methods: {       sendMsg() { // 按钮的点击事件         this.$emit('func', 'OK'); // 调用父组件传递过来的方法,同时把数据传递出去       }     }   }); ​   // 创建 Vue 实例,得到 ViewModel   var vm = new Vue({     el: '#app',     data: {},     methods: {       getMsg(val){ // 子组件中,通过 this.$emit() 实际调用的方法,在此进行定义         alert(val);       }     }   }); </script> ~~~ ### 驼峰传值 ![](https://img.kancloud.cn/e7/37/e737dcbfba7d24e2ab405c4024507b10_930x348.png) ### 完整实例 ~~~ <!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>Document</title> <script src="./lib/vue-2.4.0.js"></script> </head> <body> <div id="app"> <!-- 父组件向子组件 传递 方法,使用的是 事件绑定机制; v-on, 当我们自定义了 一个 事件属性之后,那么,子组件就能够,通过某些方式,来调用 传递进去的 这个 方法了 --> <com2 @func="show"></com2> </div> <template id="tmpl"> <div> <h1>这是 子组件</h1> <input type="button" value="这是子组件中的按钮 - 点击它,触发 父组件传递过来的 func 方法" @click="myclick"> </div> </template> <script> // 定义了一个字面量类型的 组件模板对象 var com2 = { template: '#tmpl', // 通过指定了一个 Id, 表示 说,要去加载 这个指定Id的 template 元素中的内容,当作 组件的HTML结构 data() { return { sonmsg: { name: '小头儿子', age: 6 } } }, methods: { myclick() { // 当点击子组件的按钮的时候,如何 拿到 父组件传递过来的 func 方法,并调用这个方法??? // emit 英文原意: 是触发,调用、发射的意思 // this.$emit('func123', 123, 456) this.$emit('func', this.sonmsg) } } } // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { datamsgFormSon: null }, methods: { show(data) { // console.log('调用了父组件身上的 show 方法: --- ' + data) // console.log(data); this.datamsgFormSon = data } }, components: { com2 // com2: com2 } }); </script> </body> </html> ~~~ ## 5. 使用 `this.$refs` 来获取元素和子组件的值 1.写子组件时候在子组件上写上ref属性 ~~~ <child-first ref="first" ></child-first> ~~~ 2.调用子组件的方法或者data ~~~ //调用方法 还能获取返回值 let childData = this.$refs.first.getData(); //直接获取子组件的值 console.log(this.$refs.first.res) ~~~ ### 5.1 父子组件传值 目标:主要练习父子组件之间传值 ~~~ <div id="app">   <div>     <input type="button" value="获取元素内容" @click="getElement" />     <!-- 使用 ref 获取元素 -->     <h1 ref="myh1">这是一个大大的H1</h1> ​     <hr>     <!-- 使用 ref 获取子组件 -->     <my-com ref="mycom"></my-com>   </div> </div> ​ <script>   Vue.component('my-com', {     template: '<h5>这是一个子组件</h5>',     data() {       return {         name: '子组件'       }     }   }); ​   // 创建 Vue 实例,得到 ViewModel   var vm = new Vue({     el: '#app',     data: {},     methods: {       getElement() {         // 通过 this.$refs 来获取元素         console.log(this.$refs.myh1.innerText);         // 通过 this.$refs 来获取组件         console.log(this.$refs.mycom.name);       }     }   }); </script> ~~~ ### 5.2 子组件修改父组件的值 ``` Vue .sync修饰符与$emit(update:xxx) ``` **1. `.sync`修饰符的作用** > 在对一个 prop 进行“双向绑定,单向修改”的场景下,因为子组件不能直接修改父组件,sync在2.3版本引入,作为一个事件绑定语法糖,利用EventBus,当子组件触发事件时,父组件会响应事件并实现数据更新,避免了子组件直接修改父组件传过来的内容。 **2. `.sync`修饰符之前的写法** **父组件:** ~~~ <parent :myMessage=“bar” @update:myMessage=“func”> ~~~ **js定义函数:** ~~~ func(val){ this.bar = val; } ~~~ **子组件,事件触发函数:** ~~~javascript func2(){ this.$emit(‘update:myMessage’,valc); } ~~~ 也就是说,父组件需要传一个绑定值(myMessage)同时需要设置一个更新触发函数(func)给子组件修改绑定值的时候调用。 3. **使用.sync修饰符的写法** 会简化上面的写法,父组件不需要定义更新触发函数。 **父组件:** ~~~ <comp :myMessage.sync="bar"></comp> ~~~ **子组件:** ~~~ this.$emit('update:myMessage',valc); ~~~ **4. `sync`修饰符与`$emit(update:xxx)`,驼峰法 和 - 写法的区别,使用.sync修饰符,即变量应该使用驼峰法:** ~~~javascript // this.$emit('update:father-num',100); //无效 this.$emit('update:fatherNum',100); //有效 //...... <father v-bind:father-num.sync="test"></father> ~~~ 不适用 .sync 修饰符,变量应该使用 - ,即father-num ~~~javascript this.$emit('update:father-num',100); //有效 //this.$emit('update:fatherNum',100); // 无效 //...... <father v-bind:father-num="test" v-on:update:father-num="test=$event" ></father> ~~~ 但从实践中发现,用 .sync 修饰符,这两种写法都是有效的。 在vue之中,当父组件向子组件传递属性的时候,如下使用驼峰法 ~~~javascript <cpn :cMives="movies"></cpn> ~~~ 在子组件中props:\[‘cMives’\],是接收不到属性的,应该使用-来绑定属性,接收依旧使用驼峰法。 ~~~javascript <cpn :c-mives="movies"></cpn> ~~~ ## 综合案例 ~~~ <!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>Document</title> <script src="./lib/vue-2.4.0.js"></script> <link rel="stylesheet" href="./lib/bootstrap-3.3.7.css"> </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() { // 发表评论的方法 // 分析:发表评论的业务逻辑 // 1. 评论数据存到哪里去??? 存放到了 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() 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 = '' // this.loadComments() // ????? this.$emit('func') } } } // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { list: [ { id: Date.now(), user: '李白', content: '天生我材必有用' }, { id: Date.now(), user: '江小白', content: '劝君更尽一杯酒' }, { id: Date.now(), user: '小马', content: '我姓马, 风吹草低见牛羊的马' } ] }, 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> ~~~ ### 5.2 父组件调用子组件方法