Vue2-重要知识点
数据监视
vm是Vue的实例对象
数据代理:data中的数据都做了数据代理来自vm._data,可以直接写vm.a代替vm._data,是因为Vue有数据代理
数据劫持:简单点就是被代理对应有set和get方法
Vue监测对象中的数据,对后添加的属性加响应式
Vue.set(target,propertyName,value) 或 vm.$set(target,propertyName,value)
例1:this.$set(this.student,‘name’,‘张三’)(响应式)
例2:list = [{age:18}]给List第一个元素对象添加属性 this.list[0].name = ‘张三’ (无响应式)
Vue监测数组中的数据
- 用这七个API:push(),pop(),shift(),unshift(),splice(),sort(),reverse()
- Vue.set(target,index,value) 或 vm.$set(target,index,value)
例:this.arrName.splice(0,1,‘张三’) 或者 this.$set(this.arrName,0,‘张三’)
特别注意:Vue.set() 和 vm.set() 不能对vm 或vm的数据对象(data 和 _data)添加属性!!!
Vc和Vm关系
简单创建vm(Vue的实例对象)和vc(组件的实例对象)
<div id="root"><school/>
</div>
<script>//组件的实例对象就是vc(VueComponent)const school = Vue.extend({name:'school',})//Vue的实例对象就是vmconst vm = new Vue({el:'root',// 注册school组件(局部注册)components:{school,}})
</script>
vm和vc的关系
ref 属性
<template><div><h1 ref="title">欢迎学习Vue</h1><School ref="sch"/></div>
</template><script>import School from './components/School'export default {name:'App',components:{ School },data() {return {}},methods: {showDOM(){console.log(this.$refs.title) // 真实DOM元素console.log(this.$refs.sch) // School组件的实例对象(vc)}},}
</script>
props 配置项
<template><div><Student name="李四" sex="女" :age="18"/></div>
</template><script>import Student from './components/Student'export default {name:'App',components:{ Student }}
</script>
<template><div><h2>学生姓名:{{ name }}</h2><h2>学生性别:{{ sex }}</h2><h2>学生年龄:{{ myAge }}</h2><button @click="updateAge">尝试修改收到的年龄</button></div>
</template><script>
export default {name: "Student",data() {return {myAge: this.age,};},methods: {updateAge() {this.myAge++; // 不能直接改变age,通过myAge间接改变}, },// 第一种:简单声明接收// props:['name','age','sex']// 第二种:接收的同时对数据进行类型限制// props: {// name: String,// age: Number,// sex: String,// }// 第三种:接收的同时对数据:进行类型限制+默认值的指定+必要性的限制props: {name: {type: String, //字符串类型required: true, //name是必要的},age: {type: Number,default: 99, //默认值},sex: {type: String,required: true,},},
};
</script>
注意:
- props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中,然后去修改data中的数据
- 也可以传递方法,通过方法名传递(跟变量名一样)。
mixin 混入
将一些通用的变量、方法、初始化方法等封装起来
定义混入(mixin.js)
export const hunhe = {methods: {showName(){alert(this.name)}},mounted(){console.log('你好啊!')}
}
export const hunhe2 = {data(){return{x: 100,y: 200}}
}
使用混入
<template><div><h1 @click="showName">学生姓名:{{name}}</h1><h1>x:{{x}}</h1><h1>y:{{y}}</h1></div>
</template><script>
import {hunhe,hunhe2} from '../mixin'
export default {name:'Student',data() {return {name: "张三"}},mixins:[hunhe,hunhe2]
}
</script>
全局使用(不建议使用)
import Vue from 'vue'
import App from './App.vue'
import {hunhe,hunhe2} from "@/mixin";Vue.config.productionTip = false
Vue.mixin(hunhe)
Vue.mixin(hunhe2)
new Vue({render: h => h(App),
}).$mount('#app')
注意:
- 混入中的变量、方法和组件中的重复会优先使用组件中的(不使用混入中的)
- 混入中mounted()这种生命周期钩子和组件中重复,双放都生效,先加载混入中的
plugin 插件
install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
定义插件(plugins.js)
export default {install(Vue,x,y,z){console.log(x,y,z)//全局过滤器Vue.filter('mySlice', function(value){return value.slice(0,4)})//定义混入Vue.mixin({data() {return {x:100,y:200}},})//给Vue原型上添加一个方法(vm和vc就都能用了)Vue.prototype.hello = ()=>{alert('你好啊')}}
}
使用插件
import Vue from 'vue'
import App from './App.vue'
import plugins from './plugins' // 引入插件Vue.config.productionTip = falseVue.use(plugins,1,2,3) // 应用(使用)插件new Vue({el:'#app',render: h => h(App)
})
<template><div><h2>{{ name | mySlice }}</h2><h2>{{ x }}</h2><h2>{{ y }}</h2><button @click="test">点我测试一个hello方法</button></div>
</template><script>
export default {name:'School',data() {return {name:'111111111111111111111',}},methods: {test(){this.hello()}},
}
</script>
scoped样式
- 作用:让样式在局部生效,防止多个组件用定义相同样式名冲突
- 写法:
游览器本地存储
存储内容大小一般支持 5MB 左右(不同浏览器可能还不一样)
浏览器端通过Window.sessionStorage 或者 Window.localStorage属性来实现本地存储机制
- SessionStorage存储的内容会随着浏览器窗口关闭而消失
- LocalStorage存储的内容,需要手动清除才会消失
相关API:
- xxxStorage.setItem(‘key’, ‘value’)把键值对添加到存储中,如果键名存在,则更新其对应的值
- xxxStorage.getItem(‘key’)
- xxxStorage.removeItem(‘key’)该键名从存储中删除
- xxxStorage.clear()该方法会清空存储中的所有数据
localStorage
<button onclick="saveDate()">点我保存数据</button><br/>
<button onclick="readDate()">点我读数据</button><br/>
<button onclick="deleteDate()">点我删除数据</button><br/>
<button onclick="deleteAllDate()">点我清空数据</button><br/>
<script>let person = {name:"JOJO",age:20}function saveDate(){localStorage.setItem('msg','localStorage')localStorage.setItem('person',JSON.stringify(person))}function readDate(){console.log(localStorage.getItem('msg'))const person = localStorage.getItem('person')console.log(JSON.parse(person))}function deleteDate(){localStorage.removeItem('msg')localStorage.removeItem('person')}function deleteAllDate(){localStorage.clear()}
</script>
sessionStorage
<button onclick="saveDate()">点我保存数据</button><br/>
<button onclick="readDate()">点我读数据</button><br/>
<button onclick="deleteDate()">点我删除数据</button><br/>
<button onclick="deleteAllDate()">点我清空数据</button><br/>
<script>let person = {name:"JOJO",age:20}function saveDate(){sessionStorage.setItem('msg','sessionStorage')sessionStorage.setItem('person',JSON.stringify(person))}function readDate(){console.log(sessionStorage.getItem('msg'))const person = sessionStorage.getItem('person')console.log(JSON.parse(person))}function deleteDate(){sessionStorage.removeItem('msg')sessionStorage.removeItem('person')}function deleteAllDate(){sessionStorage.clear()}
</script>
组件的自定义事件
子给父转数据
props方式:
- 一种组件间通信的方式,适用于:父组件 ===> 子组件 (父给子传数据)
自定义事件:
- 通过父组件给子组件绑定一个自定义事件实现子给父传递数据(第一种写法,使用@或v-on)
- 通过父组件给子组件绑定一个自定义事件实现子给父传递数据(第二种写法,使用ref)
全局事件总线:
App.vue
<template><div class="app"><!-- props方式: --> <!-- 通过父组件给子组件传递函数类型的props实现子给父传递数据 --><School :mps="getSchoolName"/><!-- 自定义事件: --> <!-- 通过父组件给子组件绑定一个自定义事件实现子给父传递数据(第一种写法,使用@或v-on) --><Student @mps="getStudentName"/><!-- 通过父组件给子组件绑定一个自定义事件实现子给父传递数据(第二种写法,使用ref) --><Student ref="student"/></div>
</template><script>
import Student from './components/Student'
import School from './components/School'export default {name:'App',components:{School,Student},methods: {getSchoolName(name){console.log('App收到了学校名:',name)},getStudentName(name){console.log('App收到了学生名:',name)},},mounted() {this.$refs.student.$on('mps',this.getStudentName) // 绑定自定义事件// this.$refs.student.$once('mps',this.getStudentName) // 绑定自定义事件(一次性)},
}
</script>
School.vue
<template><div><h2>学校名称:{{ name }}</h2><button @click="schoolName111">给App传schoolName</button></div>
</template><script>
export default {name: 'School',data() {return {name: "石家庄信息工程",};},props:['mps'],methods: {schoolName111(){this.mps(this.name)}},
};
</script>
Student.vue
<template><div class="demo"><h2>学生名称:{{ name }}</h2><button @click="studentName111">给App传studentName</button><button @click="unbind">解绑atguigu事件</button><button @click="death">销毁当前Student组件的实例(vc)</button></div>
</template><script>
export default {name:'Student',data() {return {name:'马朋帅',}},methods: {studentName111(){// 触发Student组件实例身上的atguigu事件this.$emit('mps',this.name)},unbind(){// 解绑this.$off('mps') //解绑一个自定义事件// this.$off(['mps','demo']) //解绑多个自定义事件// this.$off() //解绑所有的自定义事件},death(){// 销毁了当前Student组件的实例,销毁后所有Student实例的自定义事件全都不奏效this.$destroy()}},
}
</script>
注意:直接在里面写回调函数要用箭头函数(this会指向这条代码所在的vc)
//结合上面的代码
this.$refs.student.$on('mps',(name)=>{console.log(name)console.log(this) //App的vc对象
}) this.$refs.student.$on('mps',function (name){console.log(name)console.log(this) //Student的vc对象
})
全局事件总线
一种可以在任意组件间通信的方式
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = falsenew Vue({render: h => h(App),beforeCreate() {Vue.prototype.$bus = this // 安装全局事件总线}}).$mount('#app')
App.vue
<template><div class="app"><School/><Student/></div>
</template><script>
import Student from './components/Student'
import School from './components/School'export default {name:'App',components:{ School, Student }
}
</script>
School.vue
<template><div></div>
</template><script>
export default {name: "School",mounted() {this.$bus.$on("mps", (data) => {console.log("我是School组件,收到了数据", data);});},beforeDestroy() {this.$bus.$off("mps");},
};
</script>
Student.vue
<template><div><h2>学生姓名:{{ name }}</h2><button @click="sendStudentName">把学生名给School组件</button></div>
</template><script>
export default {name:'Student',data() {return {name:'马朋帅',}},methods: {sendStudentName(){this.$bus.$emit('mps', this.name)}}
}
</script>
消息的订阅与发布
一种组件间通信的方式,适用于任意组件间通信。
使用步骤:
- 安装pubsub:npm i pubsub-js
- 引入: import pubsub from ‘pubsub-js’
- 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods:{demo(data){......}
}
......
mounted() {this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
}
- 提供数据:pubsub.publish(‘xxx’,数据)
- 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅。
示例代码
- 订阅消息(School.vue)
<script>import pubsub from 'pubsub-js'export default {name:'School',data() {return {name:'尚硅谷',address:'北京',}},mounted() {this.pubId = pubsub.subscribe('hello',(msgName,data)=>{console.log('有人发布了hello消息,hello消息的回调执行了',msgName,data)})},beforeDestroy() {pubsub.unsubscribe(this.pubId)},}
</script>
- 发布消息(Student.vue)
<script>import pubsub from 'pubsub-js'export default {name:'Student',data() {return {name:'张三',sex:'男',}},methods: {sendStudentName(){pubsub.publish('hello',666)}},}
</script>
$nextTick
- 语法:
this.$nextTick(回调函数)
- 作用:在下一次 DOM 更新结束后执行其指定的回调。
- 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
具体案例
this.$nextTick(function(){this.$refs.inputTitle.focus()
}
过度与动画
作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。
写法:
元素进入的样式:
v-enter:进入的起点
v-enter-active:进入过程中
v-enter-to:进入的终点
元素离开的样式:
v-leave:离开的起点
v-leave-active:离开过程中
v-leave-to:离开的终点
使用
<transition>
包裹要过渡的元素,并配置name属性:若有多个元素需要过度,则需要使用:
<transition-group>
,且每个元素都要指定key
值。appear属性的作用:刷页面时,执行需要执行的动画
具体案例(单个元素过渡)
<template><div><button @click="isShow = !isShow">显示/隐藏</button><transition appear><h1 v-show="isShow">你好啊!</h1></transition></div>
</template><script>export default {name:'Test',data() {return {isShow:true}},}
</script><style scoped>h1{background-color: orange;}.v-enter-active{animation: move 0.5s linear;}.v-leave-active{animation: move 0.5s linear reverse;}@keyframes move {from{transform: translateX(-100%);}to{transform: translateX(0px);}}
</style>
name 的作用可以让让不同的元素有不同的动画效果
<template><div><button @click="isShow = !isShow">显示/隐藏</button><transition name="hello" appear><h1 v-show="isShow">你好啊!</h1></transition></div>
</template><script>export default {name:'Test',data() {return {isShow:true}},}
</script><style scoped>h1{background-color: orange;}.hello-enter-active{animation: move 0.5s linear;}.hello-leave-active{animation: move 0.5s linear reverse;}@keyframes move {from{transform: translateX(-100%);}to{transform: translateX(0px);}}
</style>
具体案例(多个元素过渡)
<template><div><button @click="isShow = !isShow">显示/隐藏</button><transition-group name="hello" appear><h1 v-show="!isShow" key="1">你好啊!</h1><h1 v-show="isShow" key="2">哈哈哈!</h1></transition-group></div>
</template><script>export default {name:'Test',data() {return {isShow:true}},}
</script><style scoped>h1{background-color: orange;}/* 进入的起点、离开的终点 */.hello-enter,.hello-leave-to{transform: translateX(-100%);}.hello-enter-active,.hello-leave-active{transition: 0.5s linear;}/* 进入的终点、离开的起点 */.hello-enter-to,.hello-leave{transform: translateX(0);}
</style>
使用第三库的具体案例,库的名称:Animate.css
安装:npm i animate.css
引入:import ‘animate.css’
<template><div><button @click="isShow = !isShow">显示/隐藏</button><transition-groupappearname="animate__animated animate__bounce"enter-active-class="animate__swing"leave-active-class="animate__backOutUp"><h1 v-show="!isShow" key="1">你好啊!</h1><h1 v-show="isShow" key="2">哈哈哈!</h1></transition-group></div>
</template><script>
import 'animate.css'
export default {name:'Test',data() {return {isShow:true}},
}
</script><style scoped>
h1{background-color: orange;
}
</style>
插槽
1.作用
- 让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件 。
2.分类
- 默认插槽
- 具名插槽
- 作用域插槽
- 普通的作用域插槽
- 解构插槽
3.使用方式
1.默认插槽:
父组件中:<Category><div>html结构1</div></Category>
子组件中:<template><div><!-- 定义插槽 --><slot>插槽默认内容...</slot></div></template>
注意:
- 当父组件没有给插槽传数据时则显示里面的内容。
- 如果子组件的
template
中没有包含一个<slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
2.具名插槽:
父组件中:<Category><template v-slot:center><div>html结构1</div></template><!--没有包含在带有v-slot的<template>中的内容都会被视为默认插槽的内容或者<template>上加v-slot:default。--><!--<template v-slot:default><div>html结构2</div></template>--><template><div>html结构2</div></template><template v-slot:footer><div>html结构3</div></template><!-- 注意:v-slot:footer 和 #footer 是两种不同的写法,效果都一样。--><!--<template #footer><a href="">更多</a></template>--></Category>
子组件中:<template><div><!-- 定义插槽 --><slot name="center">center插槽默认内容...</slot><slot>默认插槽默认内容...</slot><slot name="footer">footer插槽默认内容...</slot></div></template>
3.作用域插槽:
-
理解:数据在组件的自身(子组件),但根据数据生成的结构需要组件的使用者(父组件)来决定。(games数据在Category(子)组件中,但使用数据所遍历出来的结构由App(父)组件决定)
-
具体编码:
父组件中:<Category><!-- scopeData可以是自定义的任何名称 --><template v-slot:default="scopeData"><ul><li v-for="g in scopeData.games" :key="g">{{g}}</li></ul></template></Category><Category><!-- scopeData获取子组件的信息,在内容中使用 --><template v-slot="scopeData"><h4 v-for="g in scopeData.games" :key="g">{{g}}</h4></template></Category> 子组件中:<template><div><!-- 通过数据绑定就可以把子组件的数据传到父组件 --><slot :games="games"></slot></div></template><script>export default {name:'Category',//数据在子组件自身data() {return {games:['红色警戒','穿越火线','劲舞团','超级玛丽']}},}</script>
注意:v-slot始终放在组件里面的标签上,特殊情况可以直接放在组件标签上但是懒的记,总之放在上不会错。
4.解构插槽
-
理解:使用解构插槽可以直接使用传递过来的对象,跟上面的插槽相比就是去掉了scopeData,直接使用games。
-
具体代码:
-
未使用解构插槽的方式
子组件current-user中:<template><div><slot :user="user">默认内容</slot></div></template><script>export default {data() {return {user: {firstName: 'du',lastName: 'li'}}}}</script> 父组件中:<current-user><template v-slot:default="slotProps">{{ slotProps.user.firstName }}</template></current-user>
-
使用解构插槽的方式
父组件中:<current-user><!-- 解构插槽 --><!-- 名称必须跟子组件的保持一致都用user --><template v-slot="{ user }">{{ user.firstName }}</template></current-user>
-
给user重命名为person
父组件中:<current-user><template v-slot="{ user: person }">{{ person.firstName }}</template></current-user>
-
给user设置默认值
父组件中:<current-user><template v-slot="{ user = { firstName: 'duli' } }">{{ user.firstName }}</template></current-user>
-
4.简写方式
-
v-slot:
替换为字符#
。如v-slot:center
缩写为#center
<Category><template #center><div>html结构1</div></template><template ><div>html结构2</div></template><template #footer><div>html结构3</div></template> </Category>
-
解构插槽没用名称时缩写注意点
<!-- 这样会报错 --> <current-user><template #="{ user }">{{ user.firstName }}</template> </current-user> <!-- 正确写法 --> <current-user><template #default="{ user }">{{ user.firstName }}</template> </current-user>