vue3学习笔记(二)

其它 Composition API

shallowReactive 与 shallowRef

1、 shallowReactive

只处理对象最外层属性的响应式(浅响应式)

import {  shallowReactive } from "vue"
...
setup() {
        let person = shallowReactive({
            name:'张三',
            age:18,
            job:{
                j1:{
                    salary:20
                }
            }
        })
        ...
}

这样只能响应式的修改 name 和 age,修改job.j1.salary并不会响应式的修改person中的数据
在这里插入图片描述
2、shallowRef

只处理基本数据类型的响应式, 不进行对象的响应式处理。

如果包裹 基本数据类型,则 shallowRef 与 ref 的作用是相同的,都会将其变成响应式
如果包裹对象类型:
(1)ref

		let x = ref({
	        y:0
		})
        console.log('ref:', x);

在这里插入图片描述
(2)shallowRef

		let x = shallowRef({
          	y:0
        })
        console.log('shallowRef:', x);

在这里插入图片描述
因此,在这种情况下 修改 x.y,并不会起作用。

3、总结——什么时候使用?

  • 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
  • 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。

readonly 与 shallowReadonly

  • readonly: 让一个响应式数据变为只读的(深只读)。
  • shallowReadonly:让一个响应式数据变为只读的(浅只读)。
  • 应用场景: 不希望数据被修改时。

(1)对于对象类型:

		person = readonly(person)
        person = shallowReadonly(person)

设置 readonly 后,对象中的所有属性都不能修改了,包括 嵌套的 job 对象中的 salary
设置 shallowReadonly 后,对象中的第一层属性 都无法修改,但 嵌套的 job 对象中的 salary可以修改
(2)对基本数据类型

		sum = readonly(sum)
        sum = shallowReadonly(sum)

设置 readonly 和 shallowReadonly的作用是一样的 ,使得 sum 无法修改。但是使用shallowReadonly 没什么必要,因为 基本数据类型 没有深层次的对象

toRaw 与 markRaw

1、toRaw:

  • 作用:将一个由reactive生成的响应式对象转为普通对象
  • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
	function showRowPerson() {
          const p = toRaw(person)
          console.log(p);
        }

在这里插入图片描述

2、 markRaw:

  • 作用:标记一个对象,使其永远不会再成为响应式对象。
  • 应用场景:
    1. 有些值不应被设置为响应式的,例如复杂的第三方类库等。
    2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。

栗子:点击按钮 向person对象中添加一个 car 对象

(1)第一种方式:

  <button @click="addCar">给人添加一台车</button>
  <button @click="person.car.name += '!'">换车名</button>
  <button @click="person.car.price++">换价格</button>
	......
  	function addCar(){
          let car = {name:'奔驰', price:40}
          person.car = car
       }
	......

此时,person 中的car对象也会被自动转换为 响应式的,即可以响应式修改它的 name及 price

(2)第二种方式,不想让car这个对象变成响应式的

		function addCar(){
          let car = {name:'奔驰', price:40}
          person.car = markRaw(car)
        }

此时,无法修改。

customRef

1、作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
2、应用实例——实现防抖效果:
在文本框输入内容后,延迟一会在文本框下方显示输入的内容

<template>
	<input type="text" v-model="keyword">
	<h3>{{keyword}}</h3>
</template>

<script>
	import {ref,customRef} from 'vue'
	export default {
		name:'Demo',
		setup(){
			// let keyword = ref('hello') //使用Vue准备好的内置ref
			//自定义一个myRef
			function myRef(value,delay){
				let timer
				//通过customRef去实现自定义
				return customRef((track,trigger)=>{
					return{
						get(){
							track() //告诉Vue这个value值是需要被“追踪”的(通知vue追踪 keyWord 的变化(提前和get商量一下,让他认为 value是有用的))
							return value
						},
						set(newValue){
							clearTimeout(timer)
							timer = setTimeout(()=>{
								value = newValue  //读取时使用get方法,返回值是value,所以应该将新值赋给value
								trigger() //告诉Vue去更新界面
							},delay)
						}
					}
				})
			}
			let keyword = myRef('hello',500) //使用程序员自定义的ref
			return {
				keyword
			}
		}
	}
</script>

provide 与 inject(注入)

在这里插入图片描述
1、作用:实现祖孙(跨级)组件之间通信

2、套路:父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据

3、具体写法:

(1)祖组件中:

	setup(){
		......
	    let car = reactive({name:'奔驰',price:'40万'})
	    provide('car',car)
	    ......
	}

(2)孙组件中:

	setup(props,context){
		......
	    const car = inject('car')
	    return {car}
		......
	}

备注:子组件中虽然也可以通过这种方式获取值,但是父子组件之间的通信常用的还是更简单的方式 props

响应式数据的判断

  • isRef: 检查一个值是否为一个 ref 对象
  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
setup() {
      let car = reactive({name:'奔驰', price:'40W'})
      let sum = ref(0)
      let car2 = readonly(car)

      console.log(isRef(sum));         //true
      console.log(isReactive(car));    //true
      console.log(isReadonly(car2));   //true
      console.log(isProxy(car));       //true
      console.log(isProxy(car2));      //true (readonly返回的仍然是一个代理对象)
      console.log(isProxy(sum));       //false(ref对基本数据使用Object.defineProperty封装,所以不是Proxy对象)
      ......
    },

Composition API 的优势

Options API (vue2)存在的问题

使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。
在这里插入图片描述

Composition API 的优势

我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。
(借助 hook)
在这里插入图片描述

新组件

Fragment

  • 在Vue2中: 组件必须有一个根标签
  • 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
  • 好处: 减少标签层级, 减小内存占用

Teleport

  • 什么是Teleport?—— Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。

应用举例:实现点击按钮 弹窗功能
Dialog.vue

<template>
    <div>
        <button @click="isShow = true">点我弹个窗</button>
        <div v-if="isShow" class="dialog">
            <h3>我是一个弹窗</h3>
            <h4>内容</h4>
            <h4>内容</h4>
            <h4>内容</h4>
            <button @click="isShow = false">关闭弹窗</button>
        </div>
    </div>
</template>

<script>
import { ref } from "vue";
export default {
    name:'Dialog',
    setup(){
        let isShow = ref(false)
        return{isShow}
    }
}
</script>

<style>
    .dialog{
        width:300px;
        height: 300px;
        text-align: center;
        background-color: green;
    }
</style>

弹窗前
在这里插入图片描述
弹窗后
在这里插入图片描述
由此可见,如果组件嵌套的很深的话,通过这种方式实现的弹窗会将其他的组件给撑开。并且这个弹窗的位置也不好调,因为涉及多个组件。

这个时候,可以使用Teleport,将弹窗这段代码移到 指定位置

<template>
    <div>
        <button @click="isShow = true">点我弹个窗</button>
        <teleport to='body' >
            <div v-if="isShow" class="mask">
                <div  class="dialog">
                    <h3>我是一个弹窗</h3>
                    <h4>内容</h4>
                    <h4>内容</h4>
                    <h4>内容</h4>
                    <button @click="isShow = false">关闭弹窗</button>
                </div>
            </div>
        </teleport>
    </div>
</template>

<script>
import { ref } from "vue";
export default {
    name:'Dialog',
    setup(){
        let isShow = ref(false)
        return{isShow}
    }
}
</script>

<style>
    /* 实现弹窗后的背景变为半透明,且弹窗之外的内容无法点击 */
    .mask{
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        background-color: rgba(0, 0, 0, .5);
    }
    .dialog{
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%,-50%);
        width:300px;
        height: 300px;
        text-align: center;
        background-color: green;
    }
</style>

通过<teleport to='body' ></teleport >将弹窗放到了 body中
在这里插入图片描述
在这里插入图片描述
就可以相对 body来进行定位

Suspense

1、等待异步组件时渲染一些额外内容,让应用有更好的用户体验

2、应用举例:App组件包含 Child组件

(1)静态引入时:

<template>
  <div class="app">
    <h3>App组件</h3>
    <Child/>
  </div>
</template>

<script>
  import Child from '../src/components/Child.vue'  //静态引入
  export default {
    name: "App",
    components: { Child }
}
</script>

无论网速快慢与否,App组件与 Child 组件总是同时出现。(所有组件等最慢的那个组件加载出后才一起出现)

(2)异步引入:

<template>
  <div class="app">
    <h3>App组件</h3>
    <Suspense>
      <template v-slot:default>
        <Child/>
      </template>
      <template v-slot:fallback>
        <h3>加载中...</h3>
      </template>
    </Suspense>
  </div>
</template>

<script>
  import { defineAsyncComponent } from "vue";
  const Child = defineAsyncComponent(() => import('./components/Child.vue'))
  export default {
    name: "App",
    components: { Child }
}
</script>

此时,当网速较慢时,APP组件会先出现,如果Child组件尚未加载完毕,会先显示 加载中…,等Child组件加载完毕后再显示Child组件中的内容。

**补充:**如果Child中 返回的是一个间隔一定时间返回成功结果的 promise对象,

<template>
  <div class="child">
    <h3>Child(子组件)</h3>
    {{sum}}
  </div>
</template>

<script>
  import { ref } from "vue";
  export default {
    name: 'Child',
    setup() {
      let sum = ref(0)
      return new Promise((resolve,reject) => {
        setTimeout(() => {
          resolve({sum})
        },1000)
      })
    }
  }

那么在等待过程中也会出现 加载中…
在这里插入图片描述

3、总结–使用步骤:

(1)异步引入组件

	import {defineAsyncComponent} from 'vue'
	const Child = defineAsyncComponent(()=>import('./components/Child.vue'))

(2)使用Suspense包裹组件,并配置好defaultfallback

	<template>
		<div class="app">
			<h3>我是App组件</h3>
			<Suspense>
				<template v-slot:default>
					<Child/>
				</template>
				<template v-slot:fallback>
					<h3>加载中.....</h3>
				</template>
			</Suspense>
		</div>
	</template>

vue3中的其他变化

全局API的转移

Vue 2.x 有许多全局 API 和配置。

  • 例如:注册全局组件、注册全局指令等。
	//注册全局组件
	Vue.component('MyButton', {
	  data: () => ({
	    count: 0
	  }),
	  template: '<button @click="count++">Clicked {{ count }} times.</button>'
	})
	
	//注册全局指令
	Vue.directive('focus', {
	  inserted: el => el.focus()
	}

Vue3.0中对这些API做出了调整:

  • 将全局的API,即:Vue.xxx调整到应用实例(app)上
2.x 全局 API(Vue3.x 实例 API (app)
Vue.config.xxxxapp.config.xxxx
Vue.config.productionTip移除
Vue.componentapp.component
Vue.directiveapp.directive
Vue.mixinapp.mixin
Vue.useapp.use
Vue.prototypeapp.config.globalProperties

其他改变

1、data选项应始终被声明为一个函数。

2、过渡类名的更改:

  • Vue2.x写法
	.v-enter,
	.v-leave-to {
	  opacity: 0;
	}
	.v-leave,
	.v-enter-to {
	  opacity: 1;
	}
  • Vue3.x写法
	.v-enter-from,
	.v-leave-to {
	  opacity: 0;
	}
	
	.v-leave-from,
	.v-enter-to {
	  opacity: 1;
	}

3、移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes。(兼容性差)

4、移除v-on.native修饰符

  • 父组件中绑定事件
	<my-component
	  v-on:close="handleComponentEvent"
	  v-on:click="handleNativeClickEvent"
	/>
  • 子组件中声明自定义事件
	<script>
	  export default {
	    emits: ['close']  //在这不声明 click,则默认 父组件中的 click 是原生事件
	  }
	</script>

5、移除过滤器(filter)

过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值