v-if与v-show的生命周期影响
再深刻认识 v-if和v-show以及组件的生命周期 v-if对组件的生命周期每次都会执行一遍 而v-show只会执行一遍。
1. scoped 样式的问题
我们需要为一个.vue中的style添加一个scoped属性,以保证样式也是局部的。
<style scoped> </style>
-
app.vue中的样式是全局的
<style> .box{ margin:20px; border: 2px solid salmon; } </style>
2.组件通信(重点)
分两种:
1)父子如何通信
2)非父子之间如何通信
为什么要通信,因为每组件中的data只能在当前使用,所以要用一些通信手段 。
2.1 父组件向子组件传递数据
2.1.1语法
-
父
<template> <div class="box"> <h2>parent : 父传子语法</h2> username: {{username}} <!-- 1. 在使用子组件以绑定属性的方式传递数据 --> <v-child a="my is a" :b="20" :c="username" :age="age" ></v-child> </div> </template> <script> import vChild from './child.vue' export default { components:{ vChild }, data(){ return { username:'鲁班', age:30 } } } </script> <style> </style>
-
子
<template> <div class="box"> <h2>child</h2> <!-- 3. 在页面中使用父亲传递过来的数据 --> a: {{ a }} <hr> b: {{ b }} <hr> c: {{ c }} <hr> age: {{age}} </div> </template> <script> export default { //2. 使用props接收 props:[ 'b','a','c','age' ], //4. 在js中访问传递过来的数据 mounted(){ console.log( this.b,'b' ) console.log( this.age,'age' ) } } </script> <style> </style>
2.1.2 父传子案例:教师列表
-
父
<template> <div class="box"> <h2>web老师</h2> <v-list :teachers="teachers"></v-list> </div> </template> <script> import vList from './vList.vue' export default { data() { return { teachers:[ { id: "w0001", username: "web-李老师", img: "http://www.ujiuye.com/uploadfile/2019/0109/20190109105342884.jpg", }, { id: "w000333", username: "霍老师", img: "http://www.ujiuye.com/uploadfile/2019/0516/20190516094131820.jpg", }, ] }; }, components:{ vList } }; </script> <style scoped> </style>
-
子
<template> <ul> <li v-for="item in teachers" :key="item.id"> 姓名:{{ item.username }} <img :src="item.img" alt="" /> </li> </ul> </template> <script> export default { data() { return {}; }, props: ["teachers"], //接收父组件传递过来的数据 }; </script> <style scoped> ul { overflow: hidden; } ul li { float: left; margin: 5px; } ul li img { width: 150px; height: 200px; } </style>
2.1.3props验证
props的值为{}时,可以做props验证. type(类型) :props做校验时的常见类型:String,Number,Array,Object,Function,Boolean required(必填项) validator(验证器) default(默认值)
-
父
<template> <div class="box"> <h2>parent: props数据验证</h2> <!-- 15811112222 0371- --> <v-child :age="15"></v-child> <v-child :tel="tel" :age="110"></v-child> {{}} </div> </template> <script> import vChild from './vChild.vue' export default { components:{ vChild }, data(){ return { tel:'15811112223' } } } </script>
-
子
<template> <div class="box"> <h2>child</h2> {{tel}} <hr> {{ tel.substring(0,3) + '****' + tel.substring(7) }} <hr> age:{{age}} </div> </template> <script> export default { //type,required,default //自定义验证规则: validator props:{ tel:{ type:String ,//验证数据类型,如果传递的数据不符合数据类型,则报错 default(){ return '15688883333'//如果未传则默认值生效 } }, age:{ required:true,//必须传 type:Number,//年龄应该是个数值类型 validator(value){ //自定义验证规则。 // 1-130 //console.log( value,'就是属性具体的值' ); //return 一个布尔值 return value >=1 && value <= 130 } } } } </script>
2.2 子组件向父组件传递数据
子传父: 1.必须触发事件 子组件:通过触发$emit来触发自定义事件 父组件:通过触发自定义事件来接收数据 注意: 父组件中通过触发自定义事件时,传递的参数需要通过$event来传递,但是$event可以省略
2.2.1 语法
-
父
<template> <div class="box"> <h4>子传父</h4> <!-- 1. 父亲通过子组件中先传递一个函数,以绑定事件( 自定义事件 )的方式去传递 2. 子组件中去触发,父组件传递的这个函数,子组件中不用接收,自动会绑定到子组件实例上 --> username: {{ username }} <!-- <button @click></button> --> <v-child @f="parentfn"></v-child> </div> </template> <script> import vChild from './vChild.vue' export default { data(){ return { username:'ls' } }, components:{ vChild }, methods:{ parentfn(e){ //e 就是事件对象,其实就是接收子组件传递过来的数据 console.log( 'my is parentfn',e ); this.username = e.username //传递给父组件数据,同时修改父组件的username值 } } } </script>
-
子
<template> <div class="box"> <h4>child</h4> <button @click="sonFn">触发父组件的f事件</button> </div> </template> <script> export default { methods:{ sonFn(){ console.log( 'sonfn 点击事件' ); //触发父组件的f事件 this.$emit( 事件名,传递实际数据 ) //console.log( this ) this.$emit('f' ,{ username:'zs',age:18 } ) } } } </script>
2.2.2 子传父应用
用户和你的网站进行交互了,会用的多一点。加入购物车/登录/注册
-
父
<template> <div class="box"> <h4>子传父应用</h4> <!-- <div class="mask" v-if="isShow"> <span>登录成功</span> </div> --> <v-toast v-if="isShow" @hide="isShow=false" :msg="msg" ></v-toast> <div class="box"> <h5>登录</h5> <!-- <button @click="isShow = true">登录按钮</button> --> <button @click="login">登录按钮</button> </div> <div class="box"> <h5>注册</h5> <!-- <button @click="isShow = true">注册按钮</button> --> <button @click="register">注册按钮</button> </div> <div class="box"> <h5>加入购物车</h5> <button>加入购物车按钮</button> </div> </div> </template> <script> import vToast from './Toast.vue' export default { data(){ return { isShow:false, msg:'加入购物车成功' } }, components:{ vToast }, mounted(){ }, methods:{ login(){ console.log( 'login' ); this.isShow = true this.msg = '登录失败' }, register(){ console.log( 'register' ); this.msg = '注册成功' this.isShow = true } } } </script>
-
子
<template> <div> <!-- 一个弹框组件 --> <div class="mask"> <span>{{msg}}</span> </div> </div> </template> <script> export default { props:[ 'msg' ], mounted(){ //console.log( 'toast弹框挂载成功了,并开启了一个定时器' ); setTimeout(()=>{ //箭头函数中的this是上层环境 console.log( '我要把show改成false' ); this.$emit( 'hide' ) },2000) } } </script> <style scoped> .mask{ width: 100vw; height: 100vh; position: absolute; top:0; left: 0; } .mask span{ display: inline-block; position:absolute; top: 50%; left: 50%; transform: translate(-50% -50%); background-color: rgba(0,0,0,0.6); color: #fff; padding: 10px 20px; border-radius: 20px; } </style>
2.3 父传子总结
父传子,父组件传递数据给子组件,默认是父变,子变;子变,父不变,还报错
直接修改:父变,子变,子变,父变,还不报错 ,传递对象。因为本质上对象是引用类型
间接修改:子组件想要修改父组件的值,使用子传父。$emit
-
父
<template> <div class="box"> <h4>父传子,子传父总结</h4> <!-- 父传子,父组件传递数据给子组件,默认是父变,子变;子变,父不变,还报错 直接修改:父变,子变,子变,父变,还不报错 ,传递对象。因为本质上对象是引用类型 间接修改:子组件想要修改父组件的值,使用子传父。$emit --> username:{{ username }} <hr /> <input type="text" v-model="username" /> <hr /> persons:{{ persons.username }} <hr /> <input type="text" v-model="persons.username" /> <v-child :username="username" :persons="persons"></v-child> </div> </template> <script> import vChild from "./vChild.vue"; export default { components: { vChild, }, data() { return { username: "zs", persons: { username: "ls", age: 20, }, }; }, }; </script> <style> </style>
-
子
<template> <div class="box"> <h4>child</h4> username:{{username}} <hr> <input type="text" v-model="username"> <hr> persons:{{ persons.username }} <hr> <input type="text" v-model="persons.username"> </div> </template> <script> export default { props:[ 'username','persons' ] } </script>
2.4 非父子组件之间通信
eventBus:事件总线 (了解), 子传父的原理其实就是事件总线 。
实现非父子组件通信有三种: 1.eventBus 前提: A B需要同时存在 2.本地存储 localStorage sessionStorage 3.vuex(状态管理模式): 重点 4.cookie/session
-
index.vue
<template> <div class="box"> <h4>非父子之间的通信</h4> <!-- 事件总线:eventBus 事件的订阅与发布:事件的兼听和触发 谁要数据谁就绑定事件兼听的函数,谁给数据谁就触发事件 A要B里的数据。A绑定一个事件兼听,B触发A的事件 步骤: 1. 在main.js中添加如下代码 Vue.prototype.$eventBus = new Vue() 就相当于一个公共的池子 2. 在任意文件中的this实例上都可以到 this.$eventBus 就可以访问到 因为每个.vue组件实例对象都继承于Vue构造函数 但是每个this是当前组件的实例对象,而且每个页面之间是不共享的 3. A要B里的数据。A绑定一个事件兼听,B触发A的事件 3.1 A绑定一个事件兼听 this.$eventBus.$on(事件,事件函数) 3.2 B触发A中兼听的事件函数,并且可以传参 --> <v-a></v-a> <v-b></v-b> </div> </template> <script> import vA from './vA.vue' import vB from './vB.vue' export default { components:{ vB, vA }, mounted(){ // console.log( this,'index.vue' ); // console.log( this.$aa,'---aa--' ); console.log( this.$eventBus,'index.vue' ); } } </script> <style> </style>
-
vA.vue
<template> <div class="box"> <h4>a</h4> {{ username }} </div> </template> <script> export default { data() { return { username: "鲁肃", }; }, mounted() { //console.log( this.$eventBus,'a.vue' ); this.$eventBus.$on("aEvent", (e) => { console.log("触发了aEvent事件", e); this.username = e.username }); }, }; </script> <style> </style>
-
vB.vue
<template> <div class="box"> <h4>b</h4> <button @click="bFN">触发A中的事件</button> </div> </template> <script> export default { methods:{ bFN(){ this.$eventBus.$emit('aEvent',{ username:'鲁班' }) } } } </script> <style> </style>
3.ref属性
在vue组件中,系统提供了一个属性ref, 往html内置标签或组件身上绑定ref属性, 在js中可以通过 this.$refs.ref的属性值 访问到DOM节点或子组件
3.1 ref操作普通DOM元素
3.2 ref属性操作子组件(重点)
-
父
<template> <div class="box aa"> <h4>ref选择DOM-操作组件</h4> <!-- 1. 为内置标签添加ref --> <button ref="btn1">按钮</button> <button ref="btn2">按钮</button> <!-- 2. 为组件添加ref,可以在父组件中直接访问子组件中的数据和方法 --> <v-child ref="a"></v-child> </div> </template> <script> import vChild from './vChild.vue' // 在js中通过this.$refs获取 export default { mounted(){ // console.log( this.$refs ) // console.log( this ) this.$refs.btn1.style.background = 'red' this.$refs.btn2.style.background = 'yellow' // console.log( this.$refs.a.username ); this.$refs.a.username = 'ls' //可以直接修改子组件中的数据 // this.$refs.a.fn() }, components:{ vChild } } </script> <style> </style>
-
子
<template> <div class="box"> <h4>child</h4> username:{{ username }} </div> </template> <script> export default { data(){ return { username:'zs' } }, methods:{ fn(){ console.log( 'child fn' ); } } } </script> <style> </style>
4.is动态组件: 了解
主要是解决一些组件嵌套的问题,componet内置组件
<template> <div> <!-- 什么时候用is。 ul应该嵌套 li table应该嵌套 tr, 那么在li和tr身上使用动态的is属性来绑定组件 --> <!-- <ul> <li is="v-a"></li> </ul> <table> <tr is="v-b"></tr> </table> --> <!-- component 动态组件 --> <button @click="cname='v-b'">登录</button> <button @click="cname='v-a'">注册</button> <component :is="cname"></component> </div> </template> <script> import vA from './vA.vue' import vB from './vB.vue' export default { components:{ vA, vB }, data(){ return { cname:'v-a' } } } </script>
5. 脚手架使用动画
animate.css
<!-- 使用动画 1. 下载 npm i animate.css 2. 在main.js中引入animate.css --> <!-- leave-active-class="animate__animated animate__headShake" --> <transition enter-active-class="animate__animated animate__headShake" > <component :is="cname"></component> </transition>
6.插槽
vue中提供了 一个新的组件slot 书写格式: <slot></slot>
6.1 匿名插槽
-
父
没有名称的槽口称之为匿名槽口 <v-header> <!-- 放到子组件中 slot插槽中 --> <button>回到首页1</button> <button>回到首页2</button> </v-header>
-
子
<h5>京东的头部</h5> <!-- 1. 匿名插槽 --> <h6>1. 匿名插槽</h6> <slot></slot>
6.2 具名插槽
-
父
<v-header> <button slot="shang">回到首页33</button> <button slot="xia">回到首页44</button> </v-header>
-
子
<!-- 2. 具名插槽 --> <h6>2. 具名插槽</h6> <slot name="shang"></slot> <slot name="xia"></slot>
6.3 插槽作用域(重要)
1.接收子组件通过槽口传递的所有数据的方式有两种: slot-scope:指令 v-slot:指令 2.slot-scope或者v-slot对应的属性值名可以自定义 3.此时的scope的值就是子组件通过slot遍历的每一项
-
父
<!-- 什么时候用作用域插槽 1. 有时候不确定要往slot中放什么标签才用slot插槽 2. 还要传递数据给子组件通过父传子 3. 还要再让子组件再通过插槽再返回给父组件(因为不确定此处要放什么标签) 每个人都加个age过来 --> <v-two :persons="persons"> <!-- 用v-slot或slot-scope接收 子组件中通过slot传递过来的数据 --> <!-- v-slot : 这是新提出的 slot-scope: 这是vue刚出来的时候提供的 --> <template v-slot="scope"> <ul > <!-- {{scope}} --> <li v-for="item in scope.rows" :key="item.id"> {{ item.username }} -- {{ item.age }} </li> </ul> </template> </v-two> <v-two> <!-- <template slot-scope="scope"> --> <ol slot-scope="scope"> <!-- {{scope}} --> <li v-for="item in scope.rows" :key="item.id"> {{ item.username }} -- {{ item.age }} </li> </ol> <!-- </template> --> </v-two> <script> export default { components: { vHeader, vTwo, }, data() { return { persons: [ { id: 1, username: "zs" }, { id: 2, username: "ls" }, ], }; }, }; </script>
-
子
<template> <div class="box"> <h5>two:作用域插槽</h5> <slot :rows="rows"></slot> </div> </template> <script> export default { props: ["persons"], data() { return { //这个数据就是子组件返回给父组件 rows: [ { id: 1, username: "zs" ,age:20}, { id: 2, username: "ls" ,age:21}, ], }; }, mounted() {}, }; </script> <style> </style>
7.mixin混入(了解)
1.mixin是一个对象,可以将这个对象混入到组件中. 2.组件中的选项都可以在mixin对象中声明 作用: 将组件中共享的数据一般存放在mixin中, 比如:你的项目都做好了,(组件都已经完成了),需求:我关心组件中的访问时间
-
vMixins.vue
<template> <div class="box"> <h5>mixin:混入</h5> <v-a></v-a> </div> </template> <script> import vA from './vA.vue' import mixinsa from './mixins' export default { components:{ vA }, data(){ return { username:'混入中的username' } }, mixins:[ mixinsa ], //植入混入对象 // mounted(){ // console.log(this.accesstime,'vMixins.vue') // } } </script> <style> </style>
-
mixins.js
/* 1. 创建混入文件 会导出一个对象 这个对象参考组件中的对象即可 */ export default { data(){ return { accesstime: new Date().toLocaleDateString() + new Date().toLocaleTimeString() } }, mounted(){ console.log(this.accesstime,'vMixins.vue') } }
8.缓存组件(了解)
vue中提供了一个组件keep-alive,作为缓存组件. 使用keep-alive包裹的组件就是缓存组件.
8.1 缓存组件钩子函数
-
activated 组件激活时的状态
-
deactivated 组件失活时的状态
父
<template> <div class="box"> <h4>keepAlive: 缓存组件</h4> <!-- 优化你的组件加载的。 --> <button @click="flag = true">加载A</button> <button @click="flag = false">加载B</button> <!-- <v-a v-show="flag"></v-a> <v-b v-show="!flag"></v-b> --> <keep-alive> <v-a v-if="flag"></v-a> <v-b v-else></v-b> </keep-alive> <!-- <v-a v-if="flag"></v-a> <v-b v-else></v-b> --> </div> </template> <script> import vA from "./vA.vue"; import vB from "./vB.vue"; export default { components: { vB, vA, }, data() { return { flag: true, //true A false B }; }, }; </script> <style> </style>
A.vue
<template> <div class="box"> <h2>a</h2> </div> </template> <script> export default { /* 加上keep-alive以后,组件就不会触发 created、destroyed等生命周期了 */ created() { console.log("请求了a页面的ajax数据列表"); }, mounted() { //console.log("绑定了a的window.scroll事件"); // console.log("绑定了a的window.scroll事件"); }, destroyed() { // console.log("解除了a的window.scroll事件"); }, activated() { console.log("a组件激活了,显示了"); console.log("绑定了a的window.scroll事件"); }, deactivated() { console.log("a组件失活了,不显示了"); console.log("解除了a的window.scroll事件"); }, }; </script> <style> </style>
-
index.vue
添加按钮: 展示form.vue
import list.vue
import form.vue
-
list.vue
展示表格
-
form.vue
添加数据的表单
修改功能是扩展。