第2章:Vue组件化开发
1、局部组件和全局组件的创建和使用:
<!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> </head> <body> <div id="app"> <!-- 3.使用子组件 --> <App></App> </div> <script src="./vue.js"></script> <script> // App组件 html+css+js // 创建全局组件 第一个是组件名 第二个是配置 // 只要创建全局组件 可以在任意地方使用 (template) // Vue.component是创建全局组件。 Vue.component('Vheader', { template: ` <div> 我是导航组件 </div> ` }) Vue.component('Vaside', { template: ` <div> 我是侧边栏 </div> ` }) //const Vbtn是创建局部组件。 const Vbtn = { template:` <button>按钮</button> ` } const Vcontent = { data() { return { } }, template: ` <div> 我是内容组件 <Vbtn></Vbtn> <Vbtn></Vbtn> <Vbtn></Vbtn> <Vbtn></Vbtn> </div> `, //这里记得用div标签闭合,否则将会报错。 components:{ Vbtn } } // 使用局部组件的打油诗: 建子 挂子 用子 // 1.创建组件 // 注意:在组件中这个data必须是一个函数,返回一个对象 const App = { data() { return { msg: '我是App组件' } }, components: { Vcontent }, template: ` <div> <Vheader></Vheader> <div> <Vaside /> <Vcontent /> </div> </div> `, methods: { handleClick() { this.msg = '学习局部组件'; } }, computed: { } } new Vue({ el: '#app', data: { }, components: { // 2.挂载子组件 App } }) </script> </body> </html>
2、组件通信-父传子
<!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> </head> <body> <div id="app"> <!-- 3.使用子组件 --> <App></App> </div> <script src="./vue.js"></script> <script> // 全局组件 // 父传子:通过prop来进行通信 // 1.在子组件中声明props接收在父组件挂载的属性 // 2.可以在子组件的template中任意使用 // 3.在父组件绑定自定义的属性 Vue.component('Child',{ template:` <div> <h3>我是一个子组件</h3> <h4>{{childData}}</h4> </div> `, props:['childData'] }) const App = { data() { return { msg: '我是父组件传进来的值' } }, template: ` <div> <Child :childData = 'msg'></Child> </div> `, computed: { } } new Vue({ el: '#app', data: { }, components: { // 2.挂载子组件 App } }) </script> </body> </html>
3、组件通信-子传父
<!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> </head> <body> <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> </body> </html>
4、平行组件通信
<!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> </head> <body> <div id="app"> <!-- 3.使用子组件 --> <App></App> </div> <script src="./vue.js"></script> <script> const bus = new Vue(); // 中央事件总线 bus Vue.component('B', { data() { return { count: 0 } }, template: ` <div>{{count}}</div> `, created(){ // $on 绑定事件 bus.$on('add',(n)=>{ this.count+=n; }) } }) Vue.component('A', { data() { return { } }, template: ` <div> <button @click='handleClick'>加入购物车</button> </div> `, methods:{ handleClick(){ // 触发绑定的函数 // $emit 触发事件 bus.$emit('add',1); } } }) const App = { data() { return { } }, template: ` <div> <A></A> <B></B> </div> `, } new Vue({ el: '#app', data: { }, components: { // 2.挂载子组件 App } }) </script> </body> </html>
5、其它组件通信方式
<!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> </head> <body> <div id="app"> <!-- 3.使用子组件 --> <App></App> </div> <script src="./vue.js"></script> <script> // 插槽 留坑 // props 验证规则 // 组件的生命周期 // UI库 element-ui // **** 如何设计组件? ***** // 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> </body> </html>
6、匿名插槽
<!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> </head> <body> <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> </body> </html>
7、具名插槽
<!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> </head> <body> <div id="app"> <!-- 3.使用子组件 --> <App></App> </div> <script src="./vue.js"></script> <script> // 只要匹配到slot标签的name值 template中的内容就会被插入到这个槽中 Vue.component('MBtn', { template: ` <button> <slot name='submit'></slot> <slot name='login'></slot> <slot name='register'></slot> </button> ` }) const App = { data() { return { title: "老爹" } }, template: ` <div> <m-btn> <template slot='submit'> 提交 </template> </m-btn> <m-btn> <template slot='login'> <a href="#">登录</a> </template> </m-btn> <m-btn> <template slot='register'> 注册 </template> </m-btn> </div> `, } new Vue({ el: '#app', data: { }, components: { // 2.挂载子组件 App } }) </script> </body> </html>
8、作用域插槽
<!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> </head> <body> <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> </body> </html>
9、生命周期
<!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> <style> .active{ color: red; } </style> </head> <body> <div id="app"> <!-- 3.使用子组件 --> <App></App> </div> <script src="./vue.js"></script> <script> /* beforeCreate created beforeMount mounted beforeUpdate updated activated 激活 deactivated 停用 配合keep-alive beforeDestroy destroyed */ Vue.component('Test', { data() { return { msg: "小马哥", isRed:false } }, methods: { handlerClick() { this.msg = 'alex'; this.isRed = true; } }, template: ` <div> <button @click='handlerClick'>改变</button> <h3 :class='{active:isRed}'>{{msg}}</h3> </div> `, beforeCreate() { console.log('组件创建之前', this.$data); }, created() { // 非常重要的事情,在此时发送ajax 请求后端的数据 console.log('组件创建完成', this.$data); }, beforeMount() { // 即将挂载 console.log('DOM挂载之前', document.getElementById('app')); }, mounted() { // 发送ajax console.log('DOM挂载完成', document.getElementById('app')); }, beforeUpdate() { // 获取更新之前的DOM console.log('更新之前的DOM', document.getElementById('app').innerHTML); }, updated() { // 获取最新的DOM console.log('更新之后的DOM', document.getElementById('app').innerHTML); }, beforeDestroy() { console.log('销毁之前'); }, destroyed() { console.log('销毁完成'); }, activated(){ console.log('组件被激活了'); //activated需要结合下面的<keep-alive>标签使用。 }, deactivated(){ console.log('组件被停用了'); } }) const App = { data() { return { isShow: true } }, components: {}, methods: { clickHandler() { this.isShow = !this.isShow; } }, template: ` <div> <keep-alive> <Test v-if='isShow'></Test> </keep-alive> <button @click='clickHandler'>改变生死</button> </div> `, } new Vue({ el: '#app', data: { }, components: { App } }) </script> </body> </html>
10、异步组件加载
<!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> <style> </style> </head> <body> <div id="app"> <!-- 3.使用子组件 --> <App></App> </div> <script src="./vue.js"></script> <script type='module'> import xxx from './modules.js'; const App = { data() { return { isShow: false } }, methods: { asyncLoad() { this.isShow = !this.isShow; } }, components: { Test:()=>import('./Test.js') }, template: ` <div> <button @click='asyncLoad'>异步加载</button> <Test v-if='isShow'></Test> </div> `, } new Vue({ el: '#app', data: { }, components: { App } }) </script> </body> </html>
11、ref的使用
<!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> </head> <body> <div id="app"> <!-- 3.使用子组件 --> <App></App> </div> <script src="./vue.js"></script> <script> Vue.component('Test', { data() { return { msg: "小马哥", } }, template: ` <div> <h3>{{msg}}</h3> </div> `, }) const App = { data() { return { } }, mounted(){ // 1.如果给标签添加ref,获取的就是真实的DOM节点 // 2.如果给子组件添加ref,获取的是当前子组件对象 console.log(this.$refs.btn); // 加载页面,自动获取焦点 this.$refs.input.focus(); console.log(this.$refs.test); }, components: {}, template: ` <div> <Test ref='test'></Test> <input type="text" ref='input'/> <button ref='btn'>改变生死</button> </div> `, } new Vue({ el: '#app', data: { }, components: { App } }) </script> </body> </html>
12、nextTick的用法
<!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> </head> <body> <div id="app"> <h3>{{message}}</h3> </div> <script src="./vue.js"></script> <script> const vm = new Vue({ el:'#app', data:{ message:'小马哥' } }) vm.message = 'new Message'; // console.log(vm.$el.textContent); // 为了数据变化之后等待vue完成更新DOM,可以在数据变化之后立即使用Vue.nextTick 在当前的回调函数中能获取最新的DOM Vue.nextTick(()=>{ console.log(vm.$el.textContent); }) </script> </body> </html>
13、nextTick的应用
<!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> </head> <body> <div id='app'> <App></App> </div> <script src="./vue.js"></script> <script> /* 需求: 在页面拉取一个接口,这个接口返回一些数据,这些数据是这个页面的一个浮层组件要依赖的, 然后我在接口一返回数据就展示了这个浮层组件,展示的同时, 上报一些数据给后台(这些数据是父组件从接口拿的), 这个时候,神奇的事情发生了,虽然我拿到数据了,但是浮层展现的时候, 这些数据还未更新到组件上去,上报失败 */ const Pop = { data() { return { isShow: false } }, props: { name: { type: String, default: '' }, }, template: ` <div v-if='isShow'> {{name}} </div> `, methods: { show() { this.isShow = true; //弹窗组件展示 console.log(this.name); } }, } const App = { data() { return { name: '' } }, created() { // 模拟异步请求 setTimeout(() => { // 数据更新 this.name = '小马哥'; this.$nextTick(()=>{ this.$refs.pop.show(); }) }, 1000); }, components: { Pop }, template: `<pop ref='pop' :name='name'></pop>` } const vm = new Vue({ el: '#app', components: { App } }) </script> </body> </html>
14、对象变更检测
<!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> </head> <body> <div id="app"> <h3> {{user.name}},{{user.age}},{{user.phone}} <button @click='handlerAdd'>添加属性</button> </h3> </div> <script src="./vue.js"></script> <script> // Vue不能检测对象属性的添加和删除 new Vue({ el:"#app", data:{ user:{} }, methods: { handlerAdd() { // Vue.$set(object,key,value)添加响应式属性 // this.user.age = 20; // this.$set(this.user,'age',20); // 添加多个响应式属性 this.user = Object.assign({},this.user,{ age:20, phone:18511803134 }) } }, created(){ setTimeout(() => { this.user = { name:"张三" } }, 1250); } }) </script> </body> </html>
15、混入mixin偷懒技术
<!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> </head> <body> <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'); } }, } // mixin来分发Vue组件中的可复用功能 new Vue({ el:"#app", data:{ msg:"小马哥" }, created(){ console.log(1111); }, mixins:[myMixin] }) </script> </body> </html>
16、mixin混入偷懒技术的应用
<!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>mixin的应用</title> </head> <body> <div id='app'> </div> <script src="./vue.js"></script> <script> // 一个是模态框 一个提示框 // 它们看起来不一样,用法不一样,但是逻辑一样(切换boolean) /* // 全局的mixin 要格外小心 因为每个组件实例创建是,它都会被调用 Vue.mixin({ }) */ const toggleShow = { data() { return { isShow: false } },//数据 methods: { toggleShow() { this.isShow = !this.isShow } }//方法 }//两个组件共有的数据,方法在toggleShow对象里。 const Modal = { template: ` <div v-if='isShow'><h3>模态框组件</h3></div> `, // 局部的mixin mixins: [toggleShow] }//组件1 const ToolTip = { template: ` <div v-if='isShow'> <h2>提示框组件</h2> </div> `, mixins: [toggleShow] }//组件2 new Vue({ el: "#app", data: { }, components: { Modal, ToolTip },//components是挂载Modal,ToolTip。(注入) template: ` <div> <button @click='handleModel'>模态框</button> <button @click='handleToolTip'>提示框</button> <Modal ref='modal'></Modal> <ToolTip ref='toolTip'></ToolTip> </div> `, methods: { handleModel() { this.$refs.modal.toggleShow(); }, handleToolTip() { this.$refs.toolTip.toggleShow(); } }, }) </script> </body> </html>
作者:龙飞
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!