- vue的set
this.$set('target',key,'value)
例子:
// target 目标对象 key更改的具体数据 value重新赋的值
data(){
return {
list:{
key:'例子'
}
}
}
this.$set(this.list,addKey,'我是通过set添加上去的')
// 注意不能直接在data上面新加或改变数据源
- vue的filter
<div>{{num | addNum}}</div>
new Vue({
el:'#root',
data(){
return {
num:10
}
},
// 局部过滤器
filters:{
// value 为插值语法中过滤器侧边的元素值
addNum(value){
return value * 2 // 是改变页面的元素值,被过滤的值本身不变
}
}
})
// 全局过滤器
Vue.filter('globalFilter'function(value){
return value
})
-vue的directive (自定义指令)
<div v-big="n"><div>
<script>
new Vue({
el:'$root',
data(){
return {
n:1
}
},
// 自定义局部指令
// 定义指令的两种写法
directvies:{
// elemnt是绑定在自定义指令身上的Dom元素
// binding可通过binding.value获取元素身上的value值
// 1.简写形式(相对于只调用bind,和updata)
big(element,binding){
element.innerText = binding.value
},
// 2.对象形式
pfoucs:{
// 元素与指令成功绑定时(一上来时)
bind(element,binding){
},
// 指令所在元素被插入页面时
inserted(element,binding){
},
//指令所在模板被重新解析时
update(element,binding){
}
}
}
</script>
- vue的render
// 入口文件main.js中**render**函数用于挂载App.vue到页面
render(createElement) => {
return createElement(App) // 过载App.vue到页面
}
==
render:h => h(app) // 简写形式 等同于上面
- vue的路由传参
// router传参的两种方式 query params
this.$router.push(path:'about',query:{getPage:'query传参'})
this.$router.push(name:'about',params:{getPage:'params传参'})
// 注意:使用params传递参数,前面必须使用name关键字进行跳转,否则params为空
// 接收$router跳转传递的参数
this.$route.params.xxx // 接收params方式传递的参数
this.$route.query.xxx // 接收query方式传递的参数
-vue的minixs (混入)
minxs同个方法,可在多个组建中引入使用
brother.vue文件
<telmplate>
<div>
<text @click="showName">{{name}}</text>
</div>
</telpmate>
<script>
import {hunru} ../mixins.js
export default{
name:'brother',
data(){
return {
name:'minx混入'
}
},
mixins:[hunru], // 注册minxs中的方法
}
</script>
litterBrother.vue文件
<telmplate>
<div>
<text @click="showName">{{name}}</text>
</div>
</telpmate>
<script>
import {hunru} ../mixins.js
export default{
name:'brother',
data(){
return {
name:'minx混入'
}
},
mixins:[hunru], // 注册minxs中的方法
}
</script>
mixins.js
const hunru = {
methods:{
showName(){
alert(this.name)
}
},
mounted(){
console.log('你好') // 引用mixins组件的页面一加载会触发到minxs的mounted钩子上面的事件
},
}
// 全局minxs引入
app.js中引入minxs.js
import {hunru} from "../minix.js"
import Vue from "Vue"
Vue.mixin(hunru) // 可在全局vm身上使用此方法
- scoped样式 作用:让样式在布局生效,防止冲突
// 写法
<style scoped>
</style>
- 組件的自定义事件 ($emit)
- 一直组件间通信的方式,适用于:子组件 ==> 父组件
- 使用场景:A是父组件,B是子组件,B想给A传递数据,那么就在A中给B绑定自定义事件 (事件的回调在A中)
- 绑定自定义事件
1. 第一种方式:在父组件中<Demo @testing="demo"></Demo> 或 <Demo v-on:testing="demo"></Demo>
2. 第二种方式:在父组件中:
<Demo ref="demo"></Demo>
......
mounted(){
this.$refs.demo.$on('testing',this.test)
}
3.若想让自定义事件只触发一次,可以使用 once修饰符,或$once 方法。
<Demo @testing.once="demo"></Demo>
this.$refs.demo.$on.$once('testing',this.test)
- 触发自定义事件: this.$emit('testing',数据)
- 解绑自定义事件:this.$off('testing') 6.组件上也可以绑定元素DOM事件,需要使用 native 修饰符。
<Demo @click.native="demo"></Demo>
....
methods:{
demo(){
alert('测试')
}
}
- 注意:通过 this.on('testing',回调),绑定自定义事件,回调要么配置在methods中,要么使用箭头函数,否则this质量会出问题!
全局事件总线 (GlobalEventBus)
1.一种组件通信的方式,使用于任意组件间通信
2.安装全局事件总线
// main.js
new Vue({
........
beforeCreate(){
Vue.prototype.$bus = this // 安装全局事件总线,将Vue的原型实例对象绑定指向在$bus上
},
........
})
3.使用全局总线 ¥bus
1.接收数据:A想要接收B组件传递过来的数据,则在A组件中绑定自定义事件,事件的回调留在A组件自身
1.组件A中接收参数
methods(){
demo(data){.......}
} mounted(){
this.$bus.$on('parc',this.demo)
}
```
2.组件B中提供数据
```js
.....
data(){
return {
content:'$bus传参'
}
},
created(){
this.$bus.$emit('parc',this.content)
}
消息订阅与发布
个人理解也是同GlobleEventBus相同的跨组件传参,不过需引入第三库,如 pubsub.js
1.如A组件想要接收B组件的参数,则要进行消息订阅
2.而B组件进行发布
// A.vue 订阅消息
import pubsub from "pubsub.js"
......
export default{
mounted(){
// this.$bus.$emit('pract',this.content)
// 消息订阅 (等同于上方)
this.pubId = pubsub.subscribe('hello',(mesName,data) => {
console.log(mesName); // 传递过来的参数名
console.log(data); // 参数值
})
},
beforeDestory(){
// 取消消息订阅
pubsub.unsubscribe(this.pubId) // 取消不是填写发布的参数名,而是填写每次订阅消息的Id (pubsub.js的特征)
}
}
......
// B.vue 消息的发布
......
import pubsub from "pubsub.js"
export default{
mounted(){
pubsub.publish('hello','消息订阅与发布传递的参数!')
}
}
......
vue配置代理
在 vue.config.js
moudle.exports = {
pages:{
index:{
// 入口
entry:"src/main.js"
}
},
lintoOnSace:false, // 关闭语法检查
// **开发代理服务器**
// 1. 方式一
devServer:{
proxy:'http://localhost:5000'
}
// 说明:
// 1.优点:配置简单,请求资源时直接发送前段(8080)即可。
// 2.缺点:不能配置多个代理,不能灵活的控制是否走代理。
// 3.工作方式:若按照上述配置代理,当请求了前段不存在的资源时,那么该请求会转发给服务器(优点匹配前端本地资源)
// 2. 方式二
devServer:{
proxy:{
'/api':{
target:'http://localhost:5000', // 代理目标的基础路径
changeOrigin:true,
pathRewtire:{'^/api':''} // 过滤代理服务器路径
},
'/api2':{
target:'http://localhost:5000', // 代理目标的基础路径
changeOrigin:true,
pathRewtire:{'^/api2':''} // 过滤代理服务器路径
}
}
}
}
// 说明:
// 1.优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
/* 2.缺点:配置略微繁琐,请求资源时必须加前缀。
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000 (撒谎)
changeIrigin设置为false时,服务器收到的请求头中的host为:localhost:8080 (不撒谎)
changeOrigin默认值为false
*/
vue插槽
1.作用:让父组件(使用该组件的组件)可以向被使用的子组件指定位置插入html结构,同时也是一种组件间的通信方式,适用于 父组件 ===> 子组件
2.分类:默认插槽、具名插槽、作用域插槽
3.使用方式:
1.默认插槽
父组件中:
<Category>
<div>html结构1</div>
</Category>
子组件中:
<tempalte>
<div>
<slot>插槽默认内荣</slot>
</div>
</template>
2.具名插槽
父组件中:
<Category>
<--方式1-->
<div slot="center">html结构1</div>
<--方式2-->
<div v-slot="center">html结构1</div>
</Category>
子组件中:
<tempalte>
<div>
<slot name="slot">插槽默认内荣</slot>
</div>
</template>
3.作用域插槽
1.理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定
2.注意:使用作用域插槽,必须使用template来接收传递来的数据
父组件中:
<Category>
<--方式1-->
<tmeplate scope="getChildData">
<div>
<li>{{getChildData.data1}}</li>
</div>
</tempalte>
<--方式2 推荐-->
<tmeplate slot-scope="{data2}"> // 使用了es6对象解构
<div>
<h1>{{data2}}</h1>
</div>
</tempalte>
</Category>
子组件中:
<tempalte>
<div>
<slot name="slot">插槽默认内荣</slot>
</div>
</template>
export deafualt {
name:Category,
data(){
return{
data1:'数据1',
data2:'数据2'
}
}
}
vueX
- 概念 在Vue中实现集中式状态(数据)管理的一个Vue插件,对Vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间的通信方式,且使用于任意组件间通信。
- 何时使用
多个组件需要共享数据市 - 搭建Vuex环境
1.创建文件/store/index.js
// 引入Vue核心库
import Vue from "vue"
// 引入Vuex
import Vuex from "vuex"
// 安装Vuex插件
Vue.use(Vuex)
// 准备actions对象-响应组件中用户的动作
const actions = {}
// 准备mutations对象-修改state中的数据
const muations = {}
// 准备state对象-保存具体的数据
const state ={}
// 创建且暴露store
export default new Vue.Store{
actions,
muations,
state,
}
2.在入口文件main.js中创建vm时传入stotr配置项
...
//引入store
import stroe form "./store/index"
new Vue({
store,
render:h => h(App)
})
...
4.Vuex的基本使用 1.准备要的初始化数据、配置actions、配置muations、配置state。
// 引入核心库Vue
import Vue from "vue"
// 引入Vuex
import Vue from "vuex"
// 安装Vuex
Vue.use(Vuex)
const actions = {
// 响应组件中的事件
add(context,value){
// 通过contex(可访问上下文).commit触发mutations的add事件,并传递value
context.commit('ADD',value)
}
}
const mutations = {
ADD(state,value){
state.num += value
}
}
const state = {
num:0
}
// 创建并保留store
export default new Vuex.Store{
actions,
mutations,
state
}
2.组件中读取Vuex中的数据:$stroe.state.num。
3.组件中修改Vuex中的数据:$stroe.dispath('actions中的方法名',数据)或$store.commit('mutations中的方法名',数据)
备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,既不写dispath,直接编写commit
5.getters的使用
1.概念:当vuex中的state需要经过加工后再使用,可以使用getters加工。
2.在stroe.js中追加getters配置
...
const getters = {
bigNum(state){
return state.num * 10
}
}
...
3.组件中读取数据:$store.getters.bigNum
4.四个map方法的使用
1.mapState方法:用于帮助我们映射(获取)state中的数据为计算属性
<template>
<div>
{num} // 为vuex中的state数据num
</div>
</template>
import {mapState} from 'vuex'
computed(){
// 借助mapState生成计算属性,num(对象写法)
...mapState({num:'num'})
// 借助mapState生成计算属性,num(数组写法) [数组key、value相对可使用Es6数组简写]
...mapState([num])
}
2.mapGetters方法:用于帮助我们映射(获取)getters中的数据为计算属性
<template>
<div>
{bigNum} // 为vuex中的getters函数return出来的数据
</div>
</template>
import {mapGetters} from 'vuex'
computed(){
// 借助mapGetters生成计算属性,bigNum(对象写法)
...mapGtate({bigNum:'bigNum'})
// 借助mapGetters生成计算属性,bigNum(数组写法) [数组key、value相对可使用Es6数组简写]
...mapState([bigNum])
}
3.mapActions方法:用于帮助我们生成与actions对话的方法,(白话:会帮助我们调用$store.dispatch(xxx))函数
import {mapActions} from 'vuex'
methods:{
...
//incrementOdd(){
// this.$store.dispath(jiaOdd,this.num)
//}
// 靠mapActions生成:incrementOdd(对象形式)
...mapActions({incrementOdd,'jiaOdd'})
// 靠mapActions生成:incrementOdd
...mapActions(['jiaOdd'])
}
4.mapMutations:用于帮助我们生成与mutations对话的方法,(白话:会帮助我们调用$store.commit(xxx))函数
import {mapMutations} from 'vuex'
methods:{
...
//incrementOdd(){
// this.$store.commit(JIA,this.num)
//}
// 靠mapMutations生成:incrementOdd(对象形式)
..mapMutations({incrementOdd:'JIA'})
// 靠mapMutations生成:JIA(对象形式)
...mapMutations(['JIA'])
}
备注:mapActions与mapMutations使用时,若需要传递数据:需要在模板中绑定事件时传递好参数,否则参数则是默认的对象事件
路由
1.基本使用
1.安装vue-router,命令行:npm i vue-router 2.应用插件:Vue.use(VueRouter) 3.编写router配置项
// 引入VueRouter
import vueRouter from 'vue-router'
// 引入路由组件
import About from '../components/About'
import Home from '../components/Home'
// 创建并暴露router实例对象,去管理一组一组的路由规则
export default new vueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
components:Home
}
]
})
4.实现切换(active-class可配置高亮样式)
<router-link active-class='active' to='/about'>About</router-link>
5.指定展示位置
<router-view></router-view>
2.几个注意点
1.路由组件通常存放在 page文件夹,一遍组件通常存放在components文件夹。
2.通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
3.每个组件都有自己的$route属性,里面存储这自己的路由信息。
4.整个应用只有一个router,可以通过组件的$router属性获取到。
3.多级路由
1.配置路由规则,使用children配置项:
routes:[
{
path:'/about',
component:About,
chidlren:[ //通过children配置子路由
{
path:'news', //此处一定不要写成:/news,子级路由不用加 /
component:News
}
]
}
]
2.跳转(要写完整路径):
<router-link to="/about/news">News</router-link>
4.路由的query参数
1.传递参数
<!-- 跳转并携带query参数,to的字符串写法 -->
<router-link :to="`/home/message/detail?id=${666}&title=${你好}`"></router-link>
<!-- 跳转并携带query参数,to的对象写法 -->
<router-link :to={path:'/home/message/detail',query:{id:666,titile:'你好'}}></router-link>
2.接收参数 route.query.title
5.命名路由
1.作用:可以简易化路由的跳转。 2.如何使用 1.给路由命名
{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello', // 给路由命名
path:'welcome',
component:Hello
}
]
}
]
}
2.简化跳转:
<!-- 简化前,需要写完整路径 -->
<router-link to='/demo/test/welcome'>跳转</router-link>
<!-- 简化后,直接通过名字跳转 -->
<router-link :to={name:'hello'}>跳转</router-link>
<!-- 简化写法配合传递参数 -->
<router-link :to={name:'hello',query:{id:666,title:'你好'}}></router-link>
6.路由的params参数
1.配置路由,声明接收params参数
{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello', // 给路由命名
path:'welcome/:id/:title', // 使用占位符声明接收params参数
component:Hello
}
]
}
]
}
2.传递参数
<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="demo/test/hello/666/你好">跳转</router-link>
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link :to={name:'Hello',params:{id:666,title:'你好'}}></router-link>
注意:路由携带params参数时,若使用to的对象写法,则不用使用path配置项,必须使用name配置!
3.接收参数
this.$route.params.id
7.路由的props配置
const routers =[
{
path:'/home',
name:'Home',
components:home,
// 1.props对象写法 (在Home组件中用:props:[title]接收使用)
props:{title:'标题'}
// 2.props配置为true (在Home组件中用props:[params键名]接收传递的params参数值)
props:true,
// 3,props函数式 (自动回调函数,$router可访问到组件的params和query参数)
props($router){
return title:$router.query.title || title:$router.params.title
}
}
]
8. <router-link>的replace属性
1.作用:控制路由跳转时操作浏览器历史记录的模式 2.浏览器的历史记录有两种,push和replace,push是追加记录,而replace是替换当前记录。路由跳转默认为push 3.如何开启replace模式:<router-link replace ......>
9.编程式路由导航
1.作用:不借助实现路由跳转,让路由更加灵活。 2.具体代码
// $router的五个API
this.$router.push({
name:'Home',
params:{
title:xxx,
id:xxx
}
})
this.$router.replace({
name:'Home',
params:{
title:xxx,
id:xxx
}
})
// 后退
this.$router.back()
// 前进
this.$router.forward()
// to (后面要携带参数,正数为前跳,负数为后退)
this.$router.go(-1)
10. 缓存路由组件
1.作用:让不展示的路由组件保持挂载,不被销毁。(切换路由市,默认上个路由会被销毁掉) 2.具体代码
// include配置是填写保持挂载的路由组件,后天填写必须是组件的name,而不是router的name!
<keep-alive include="Homes">
<router-view></router-view>
</keep-alive>
11.两个新的生命周期钩子
1.作用:路由组件独有的两个钩子,用于捕获路由组件的激活状态。
2.具体名字
1.`activated`路由组件被激活时触发。
2.`deactivated`路由组件失活时触发。
12 路由守卫
1.作用:对路由进行权限控制
2.分类:全局守卫、独享守卫、组件内守卫
3.全局守卫
// 全局前置守卫:初始化时执行、每次路由切换时也会执行
router.beforeEach((to,from,next) => {
// 访问跳转去路由身上meta自定义配置的isAuth属性 (作用:是否要进行权限匹配)
if(to.meta.isAuth){
if(localStorage.getItem('school') == 'md'){ // 权限控制的de具体规则
next() // 放行
} else{
alert('暂无权限查看')
}
} else{
next() // 放行
}
})
// 全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from) => {
if(to.meat.title){
document.title = to.meta.title // 修改网页的title
}
})
4.独享路由守卫:
// 写在router配置项里面的
{
path:'/home',
name:'Home',
component:Home,
meta:{isAuth:true,title:'首页'}
beforeEnter((to,from,next) => {
if(to.meta.isAuth){ // 判断当前路由是否需要权限控制
if(localStorage.getItem('token')){
next()
} else {
alert('无权限查看')
}
}
})
}
5.组件内路由守卫
// 进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from,next){
},
// 离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave(to,from,next){
}
13.路由器的两种工作模式
- 对于一个url来说,什么是hash值? -----#及后面的内容就是hash值。
- hash值不会包含在HTTP请求中,既:hash值不会带给服务器。
- hash模式
- 地址中永远带着#号,不美观。
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
- 兼容性好
- history模式:
- 地址干净,美观。
- 兼容性和hash模式相比略差。
- 应用部署上线需要后端人员支持,解决刷新页面服务端404的问题。
Vue3快速上手
1.使用vue-cli创建
## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version || vuer -V
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve
2.使用vite创建
## 创建项目
npm init vite-app <project-name>
## 进入项目目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev
3.拉开序幕的setup
1.理解:Vue3.0中一个新的配置项,值为一个函数。
2.setup是所有Composition API "表演的舞台"
3.组件中所用到的:数据、方法等等,均要配置在setup中。
4.setup函数的两种返回值。
1.若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点关注)。<br>
2.若返回一个渲染函数:则可以自定义渲染内容。(了解)。
5.注意点:
1.尽量不要与Vue2.x配置混用。
- Vue2.x配置(data、methods、computed...)中可以访问到setup中的属性、方法。
- 但在setup中不能访问到Vue2.x配置(data、nethods、computed...)。
- 如果用重名,setup优先。
2.setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性
4.ref函数
- 作用:定义一个相适应的数据。
- 语法:const xxx = ref(initValue)
1.创建一个包含响应式数据的
引用对象(reference对象,简称ref对象)
2.js中操作数据:xxx.value
3.模板中读取数据:不需要.value,直接<div>{{xxx}}</div> - 备注:
1.接收的数据可以是:基本类型,也可以是对象类型。
2.基本类型的数据:响应式依然是靠 Object.defineProperty()的get与set完成的
3.对象类型的数据:内部“求助”了Vue3.0中的一个新函数-----reactive函数
5.reactive函数
- 作用:定义一个
对象类型的相适应数据(基本类型不要用它,用ref函数) - 语法:const 代理对象 = reactive(源对象)接收一个对象(或数组),返回一个
代理对象(Proxy的实例对象,简称proxy对象) - reacitve定义的响应式数据是“深层次的”。
- 内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据进行操作。
6.Vue3.0中的响应式原理
# vue2.x的响应式原理:
- 对象类型:通过 Object.defineProperty()对属性的读取、修改进行了拦截(数据劫持)。
- 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Obejct.defineProperty(data,'count,{
get(){}
set(){}
})
-存在的问题
1.新增属性、删除属性,界面不会更新。
2.直接通过下标修改数组,界面不会自动更新。
5.Vue3.0的响应式原理
- 通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等。
- 通过Reflect(反射):对源对象的属性进行操作。
new Proxy(data,{
// 拦截读取属性值
get(target,prop){
return Reflect.get(traget,prop)
},
// 拦截设置属性值或添加新属性
set(traget,prop,value){
return Reflect.set(target,prop,value)
},
// 拦截删除属性
deleteProperty(target,prop){
return Reflect.deleteProprty(target,prop)
}
})
reactive对比ref
-
从定义数据角度来说
- ref用来定义:
基本类型数据。 - reactive用来定义:
对象(或数组)类型数据。 - 备注:ref也可以用来定义
对象(或数组)类型数据,它内部会自动通过reactive转为代理对象。
- ref用来定义:
-
从原理角度来说:
- ref通过Object.defineProperty()的
get与set来实现响应式(数据劫持)。 - reactive通过使用
Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据。
- ref通过Object.defineProperty()的
-
从使用角度来说:
- ref定义的数据:操作数据
需要.value,读取数据时模板中直接读取不需要.value. - reactive定义的数据:操作数据与读取数据:
均不需要.value。
- ref定义的数据:操作数据
6.setup的两个注意点
-
setup执行的时机
- 在beforeCreated之前执行依次,this是undefined
-
setup的两个参数
-
props:值为对象,包含:组件外部传递过来,且组件内部声明接收来的属性
-
context:上下文对象
- attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于this.$attrs.
- slots:收到的插槽内容,相当于this.$slots。
- emit:分发自定义事件的函数,相当于this.$emit
-
7计算属性与监视
1.compued函数
- 与vue2.x中computed配置功能一致。
- 写法
import {computed} from "vue"
setup(){
...
// 计算属性-简写
let fulleName = computed(() => {
reurn person.firstName + '-' + person.lastName
})
// 计算属性-完整写法
let fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split("-")
person.firstName = nameArr[0]
person.lasttName = nameArr[1]
}
})
}
2.watch函数
-
与Vue2.x中watch配置功能一致。
-
两个小航“坑”
- 监视reactive定义的响应式数据时,oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
- 监视reactive定义的响应式数据中某个属性时:deep配置有效。
...
import (ref,reactive,watch) from Vue
setup(){
let sum =ref(0)
let msg = ref('你好')
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
},
//情况1:监视ref所定义的一个响应式数据
watch(sum,(newValue,oldValue) => {
console.log('sum变了',newValue,oldValue)
},{immediate:true})
//情况2:监视ref所定义的多个响应式数据
watch([sum,msg],(newValue,oldValue) => {
console.log('sum或msg变了',newValue,oldValue)
},{immediate:true})
/* 情况3:监视reactvie所定义的一个响应式数据的全部属性
1.注意:此处无法正确的获取oldValue
2.注意:强制开启了深度监视(deep配置无效)
*/
watch(person,(newValue,oldValue) => {
console.log('person变化了',newValue,oldValue)
},{deep:true})
//情况4:监视reactive所定义的一个响应式数据中的某个属性
watch(() => person.name,(newValue,oldValue) => {
console.log('person.name变化了',newValue,oldValue)
})
//情况5:监视reactive所定义的一个响应式数据的某些属性
watch([() => person.name,() => person.age],(newValue,oldValue) => {
console.log('person的name或age变化了',newValue,oldValue)
})
// 特殊情况
watch(() => person.obj,(newValue,oldValue) => {
console.log('person的obj变化了',newValue,oldValue)
},{deep:true})// 此处由于监视的是reactice定义的对象中的某个属性,所以deep配置有效
3. watchEffect函数
- watch的套路是:既要指明监视的属性,也要指明监视的回调。
- watchEffect的套路是:不用指明监视某个属性,监视的回調中用到哪个属性,那就监视哪个属性。
- watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值
// watchEffect更指定的回调中用到的数据只要发生变化,则直接重新执行回调
watchEffect(() => {
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
8.生命周期
-
Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
- beforeDestroy改名为beforeUnmount
- destory改名为unmounted
-
Vue3.0也提供了Composition Api形式的生命周期钩子,与Vue2.x中钩子对应关系如下。
- beforeCreate===>setup()
- created===>setup()
- beforeMount===>onBeforeMount
- mounted===>onMounted
- beforeUpdate===>onBeforeUpdate
- update===>onUpdate
- beforeUnmount===>onBeforeUnmount
- unmounted===>onUnmounted
9.自定义hook函数
- 什么是hook?---本质是一个函数,吧setup函数中使用的Composition API进行了封装。
- 类似于Vue2.x中的minx
- 自定义hook的优势:复用代码,让setup中的逻辑更清楚易懂。
10.toRef
- 作用:创建一个ref对象,让value值指向另一个对象中的某个属性。
- 语法:const name = toRef(person,'name')
- 应用:要将响应式对象中的某个属性单独提供给外部使用时。
- 扩展:toRefs与toRef功能一致,但可以批量创建多个ref对象,语法toRefs(person)
11.provide与inject
-
作用:实现
祖孙组件间通信 -
套路:父组件有一个
provide选项来提供数据,子组件有一个inject选项来开始使用这些数据 -
具体写法
1.祖组件中
setup(){
.......
let car = reactive({name:'奔驰',price:'40万'})
provide('car',car)
}
2. 孙组件中
setup(props,context){
.......
const car = inject('car')
return{car}
.......
}
其他Composition API
1.shallowReactive与shadllowRef
- shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
- shallowRef:只处理基本数据类型的响应式,不进行对象的相适应处理。
- 什么时候使用?
- 如果有一个对象数据,结构比较深,但变化时只是外层属性变化 ===> shallowReactvie
- 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生成新的对象来替换===>shallowRef
2.readonly与shalllowReadyonly
- readonly:让一个响应式数据变为只读的(深只读)
- shallowReadonly:让一个响应式数据变为只读的(浅只读)。
- 应用场景:不希望数据被修改时。
3.toRae与markRaw
- toRaw:
- 作用:将一个由reactive生成的
响应式对象转为普通对象 - 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
- 作用:将一个由reactive生成的
- markRaw:
- 作用:标记一个对象,使其用于不会再称为响应式对象。
- 引用场景:
- 有些值不应被设置为响应式的,例如复杂的第三类库等。
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
4.customRef
- 作用:创建一个自定义的ref,并对其依赖项进行跟踪和更新触发进行显示控制。
- 实现防抖效果:
<template>
<input v-model="keyword">
<text>{{keyword}}</text>
</template>
<script>
import {ref,customRef} from 'vue'
export default {
name: 'App',
setup(){
// 自定向一个ref————名为myRef
function meyRef(value){
let timeId
return customRef((track,trigget) => {
return {
get(){
track() // 通知Vue追踪value的变化(提前和get商量一下,让他认为这个value是有用的)
return value
},
set(newValue){
clearTimeout(timeId)
timeId = setTimeOUt(() => {
value = newValue
trigget()// 通知Vue去重新解析模板
})
}
}
})
}
let keyWord = meyRef('hello')
return {
keyWord
}
}
}
</script>
5.响应式数据的判断
- isRef:检查一个值是否为一个ref对象
- isReactive:检查一个对象是否由reactive创建的响应式代理。
- isReadonly:检查一个对象是否是由readonly创建的只读代理
- isProxy:检查一个对象是否是由reactvie或者readonly方法创建的代理
新的组件
1.Fragment
- 在Vue2中:组件必须有一个跟标签。
- 在Vue3中:组件可以没有跟标签,内部会将多个标签包含在一个Fragment虚拟元素中
- 好处:减少标签层级,减少内存占用。
2.Teleport
- 什么是Teleport?————Teleport是一种能够将我们的
组件html结构移动到指定位置的技术。
<teleport>
<di v-is='isShow' class='mask'>
<div class='dialog'>
<h3>我是一个弹窗</h3>
<button @click='isShow = false'>关闭弹窗</button>
</div>
</div>
</teleport>
3.Suspense
- 等待异步组件时渲染一些额外内容,让应用有更好的用户体验。
- 使用步骤
- 异步引入组件
import {defineAsyncComponent} from 'vue' const child = defineAsyncComponent(() => import('./component/Child.vue'))- 使用Suspense包裹组件,并配置好default与fallback
<template>
<div class='app'>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>正在加载中...</h3>
</template>
</Suspense>
</div>
</template>