vue3更新

🫵认识vue3.0

  • ue3.0发布时间为2020-9-18,从项目体验上,vue3.0比起vue2.0有以下优势
	打包大小减少41%
 	初次渲染块55%,更新渲染块133%
	内存占比少54%

🫵创建vue3.0

  • 使用vue-cli
  • 需要确保vue-cli版本在4.5.0以上。
	vue create vue-name 选择vue3.0项目
  • 使用vite
  • 注意:使用 Vite 需要 12.x 以上的 Node.js 版本。
	npm init vue@latest
	npm init vite@latest 

项目结构

使用的为vue-cli创建的项目

  • src\main.js
// 引入的为一个名为createApp的工厂函数,不再是Vue构造函数
import { createApp } from 'vue'
import App from './App.vue'

// 创建应用实例对象,类似于Vue2中的vm,但是更“轻”,并挂载根标签
createApp(App).mount('#app')
  • src\App.vue
<template>
  <!-- Vue3组件中的模板结构可以没有根标签 -->
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

Vue3开发者工具的安装 及引用

🫵组合式API

🌀setup函数

  • vue3.0使用setup作为vue的配置项,数据、方法等都配置在setup里面,也就是说vue2.0中全部的配置都配置在setup里面
  • setup是一个函数避免使用this,没有指向 组件实例
  • setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
<template>
</template>

<script>
	export default{
		setup(){
			return{}
		}
	}
</script>

setup函数参数

  • setup会在beforeCreate之前调用一次,此时this为underfined
    在这里插入图片描述

setup函数的两种返回值

  • 返回值为一个对象,该对象中的属性方法, 在模板template中均可以直接使用
<template>
  <h1>个人介绍</h1>
  <h2>name: {{name}}</h2>
  <h2>age: {{age}}</h2>
  <button @click="sayHello">打招呼</button>
</template>

<script>
export default {
  name: 'App',
  // 测试setup, 不考虑响应式
  setup() {
    // 数据
    let name = 'ZS'
    let age = 22

    // 方法
    function sayHello() {
      alert(`my name is ${name}, I am ${age}, hello`)
    }

    // 返回一个对象
    return {
      name,
      age,
      sayHello
    }
  }
}
</script>
  • 若返回一个渲染函数:则可以自定义渲染内容
<template>
  <h1>个人介绍</h1>
  <h2>name: {{name}}</h2>
  <h2>age: {{age}}</h2>
  <h2>sex: {{sex}}</h2>
  <button @click="sayHello">打招呼 vue3</button>
  <button @click="sayWelcome">打招呼 vue2</button>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      sex: 'male'
    }
  },
  methods: {
    sayWelcome() {
      alert('hello world vue2')
    }
  },
  // 测试setup, 不考虑响应式
  setup() {
    // 数据
    let name = 'ZS'
    let age = 22
    // 方法
    function sayHello() {
      alert(`my name is ${name}, I am ${age}, hello`)
    }
    // 返回一个对象
    return {
      name,
      age,
      sayHello
    }
  }
}
</script>

vue2 vue3混用注意

  • 在Vue3中仍然可以使用Vue2的配置项
  • setup尽量不要与Vue2.x配置混用
  • Vue2.x配置(data、methos、computed…)中可以访问到setup中的属性、方法
  • 但在setup中不能访问到Vue2.x配置(data、methos、computed…)内容会是underfined
  • Vue2配置项如果与setup中的属性或方法冲突,setup优先

🌀script setup语法糖

  • 更好的代码提示
  • 能够使用纯 Typescript 声明 prop 和抛出事件
  • 更好的运行时性能
<script setup> 
</script>

<template>
</template>
  • 任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容) 都能在模板中直接使用
  • 导入的组件直接使用

🌀reactive函数

  • 作用: 定义一个任意引用类型 的 响应式数据(基本类型不要用它,要用ref函数)
  • 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
  • reactive定义的响应式数据是“深层次的”。
  • 内部原理基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作
import {reactive} from 'vue'
const pageInfo = reactive({current:1,pageSize:5});

🌀ref函数

  • 作用: 定义一个响应式的数据
  • 语法: const xxx = ref(initValue)
    • 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
    • setup 函数内部JS中)操作数据: xxx.value
    • 模板中读取数据: 不需要.value,Vue会自动进行解包操作,直接:<div>{{xxx}}</div>
  • 注意:
    • 接收的数据可以是:基本类型、也可以是对象类型
    • 基本类型的数据:靠Object.defineProperty()的get与set,实现响应式(数据劫持
    • 对象类型的数据:内部会自动通过reactive(window.Proxy)转为代理对象
import {ref} from 'vue'
let age = ref(22)

reactive 和 ref 的区别

  • 定义数据角度对比:
    • ref 定义:基本类型数据
    • reactive 定义:对象(或数组)类型数据
    • 注意:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象
  • 原理角度对比:
    • ref 定义基本数据类型 通过Object.defineProperty()的get与set来实现响应式(数据劫持)
    • reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据
  • 使用角度对比:
    • ref定义数据:操作数据需要.value,读取数据时模板中直接读取不需要.value
    • reactive定义的数据:操作数据与读取数据:均不需要.value
    • 一般将数据封装在一个data对象中,利用reactive函数将该对象变为响应式数据对象

🌀响应式原理

  • 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等
get(target, propName):target原对象,propName读取的属性名

set(target, propName, value):target原对象,propName修改的属性名,value修改成的属性值,target[propName]原属性值

deleteProperty(target, propName):target原对象,propName删除的属性名,
//源数据
let person = {
	name:'张三',
	age:18
}

//模拟Vue3中实现响应式
//Proxy对属性的增删改查都可以监测得到
//#region 
const p = new Proxy(person,{
	//有人读取p的某个属性时调用
	get(target,propName){
		console.log(`有人读取了p身上的${propName}属性`)
		return target[propName]
	},
	//有人修改p的某个属性、或给p追加某个属性时调用
	set(target,propName,value){
		console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`)
		target[propName] = value
	},
	//有人删除p的某个属性时调用
	deleteProperty(target,propName){
		console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
		return delete target[propName]
	}
})
  • 通过Reflect(反射): 对源对象的属性进行操作
  • 使用反射对数据进行增删改,(很多Object的方法都逐渐移到Reflect上,主要是如果同名的时候,反射会有个返回值告诉是否操作成功)
// Reflect(反射)
// 读取对象指定属性的值
Reflect.get(object, '属性名')
// 修改对象指定属性的值
Reflect.set(object, '属性名', '新属性值')
// 删除对象指定属性
Reflect.deleteProperty(object, '属性名')
//模拟Vue3中实现响应式
//#region 
const p = new Proxy(person,{
	//有人读取p的某个属性时调用
	get(target,propName){
		console.log(`有人读取了p身上的${propName}属性`)
		return Reflect.get(target,propName)
	},
	//有人修改p的某个属性、或给p追加某个属性时调用
	set(target,propName,value){
		console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`)
		Reflect.set(target,propName,value)
	},
	//有人删除p的某个属性时调用
	deleteProperty(target,propName){
		console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
		return Reflect.deleteProperty(target,propName)
	}
})

🌀响应式数据的判断

  • isRef: 检查一个值是否为一个 ref 对象
  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

💥shallowReactive和shallowRef

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

  • shallowRef:只考虑基本数据类型的响应式,不考虑引用类型的响应

  • 什么时候使用?

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

💥readonly和shallowReadonly

  • readonly 与 shallowReadonly 均接收一个响应式数据为参数
  • 普通对象也行,只不过不是响应式了
  • readonly: 让一个响应式数据变为只读的(深只读),传入的响应式数据不管有几层,都不能进行修改
  • shallowReadonly:让一个响应式数据变为只读的(浅只读),传入的响应式数据只有最外层数据不能进行修改
  • 应用场景: 不希望数据被修改时。将数据交给其他组件并且不希望这个组件对数据进行更改
import {readonly,shallowReadonly} from 'vue'
let data = reactive({
     name: 'xiaozhi',
     age: 23,
    });
data=readonly(data)

💥triggerRef

  • 手动触发和 shallowRef 相关联的副作用
    在这里插入图片描述

💥toRaw 与 markRaw

  • toRaw
    • toRaw 接收一个响应式对象为参数,只能接收reactive生成的响应式对象,不能处理ref生成的响应式数据
    • 作用:将一个由reactive生成的响应式对象转为普通对象
    • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
  • markRaw
    • 接收一个对象类型数据为参数
    • 作用:标记一个对象,使其永远不会再成为响应式对象。向一个已经是响应式对象的数据追加一个属性,该属性的值为对象类型数据,vue会为其自动添加响应式,当不希望该属性的值为响应式时可以使用该函数,减小开销。
    • 应用场景:
      1. 有些值不应被设置为响应式的,例如复杂的第三方类库等,如果向响应式对象追加一个第三方类库对象(一般属性多且层次多),开销会很大。
      2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。

💥toRef 和 toRefs

  • 作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性
  • 语法:const name = toRef(obj,'name')
  • 应用: 对reactive返回的对象进行解构获取值,响应式会失效,toRef要将响应式对象中的某个属性单独提供给外部使用时。
  • 扩展:toRefs与toRef功能一致,但可以批量创建多个 ref 对象,创建一个对象中所有属性对应的 ref 对象,语法:toRefs(person)
<template>
	<h4>{{person}}</h4>
	<h2>姓名:{{name}}</h2>
	<h2>年龄:{{age}}</h2>
</template>

<script>
	import {ref,reactive,toRef,toRefs} from 'vue'
	export default {
		name: 'Demo',
		setup(){
			//数据
			let person = reactive({
				name:'张三',
				age:18,
				job:{
					j1:{
						salary:20
					}
				}
			})
			const x = toRefs(person)
			console.log('******',x)

			//返回一个对象(常用)
			return {
				person,
				// name:toRef(person,'name'),
				// age:toRef(person,'age'),
				// salary:toRef(person.job.j1,'salary'),
				...toRefs(person)
			}
		}
	}
</script>

💥unref

  • 如果我们想要获取一个ref引用中的value,那么也可以通过unref方法
  • 如果参数是一个 ref对象,则返回内部值value否则返回参数本身
  • 这是 val = isRef(val) ? val.value : val 的语法糖函数;
    在这里插入图片描述

🌀computed计算方法

  • 返回一个不可变的响应式 ref 对象
<template>
    <div>{{ data.num1 }}+{{ data.num2 }}={{ num }}</div>
    <button @click="change1">加法</button>
    <button @click="change2">减法</button>
</template>
<script setup>
import { reactive, computed } from 'vue';

        let data = reactive({
            num1: 9,
            num2: 10
        });
        let num = computed(() => {
            return data.num1 + data.num2;
        });
        function change1() {
            data.num1 = data.num1 + 1;
        }
        function change2() {
            data.num2 = data.num2 - 100;
        }
    }
};
</script>
<style scoped></style>
  • 如果直接赋值 computed 属性,vue会有警告
报错:computed value is readonly
  • 可以通过setter实现修改,但是不推荐
  • 概念:当你在写一个函数时,其实就是在写函数的getter方法
let num = computed({
    get() {
       console.log('调用了');
       return data.num1 + data.num2;
    },
    set(newValue, oldValue) {
       console.log("修改调用");
    }
});
  • 此时其实计算方法 返回一个可变的(可读写)ref 对象,修改的话 也需要.value

💥在setup中 获取 ref

  • 要定义一个ref对象,绑定到元素或者组件的ref属性上即可;
    在这里插入图片描述
<template>
  <!-- 1.获取元素 -->
  <h2 ref="titleRef">我是标题</h2>
  <button ref="btnRef">按钮</button>

  <!-- 2.获取组件实例 -->
  <show-info ref="showInfoRef"></show-info>

  <button @click="getElements">获取元素</button>
</template>

<script>
  import { ref, onMounted } from 'vue'
  //子组件
  import ShowInfo from './ShowInfo.vue'

  export default {
    components: {
      ShowInfo
    },
    setup() {
      const titleRef = ref()
      const btnRef = ref()
      const showInfoRef = ref()

      // mounted的生命周期函数
      onMounted(() => {
        console.log(titleRef.value)
        console.log(btnRef.value)
        console.log(showInfoRef.value)

        showInfoRef.value.showInfoFoo()
      })

      function getElements() {
        console.log(titleRef.value)
      }

      return {
        titleRef,
        btnRef,
        showInfoRef,
        getElements
      }
    }
  }
</script>

<style scoped>
</style>

🌀生命周期

  • setup() 是围绕beforeCreate ,created 运行的,不需要显示定义了,代码写在 setup()中

在这里插入图片描述

<template>
</template>

<script setup> 
	import { onMounted } from 'vue'
	onMounted( ()=>{ console.log("这个回调函数才是生命周期钩子函数") })
</script>

🌀Provide函数 / Inject函数

  • 如果祖先元素的数据是具有响应式的,同时修改一方的数据,另一方的数据也会跟着变

  • 祖先组件依赖:

setup(){
	......
    let car = reactive({name:'奔驰',price:'40万'})
    // 给后代组件传递数据 
    // 第一个参数为对传递数据的命名,第二个参数为传递的数据
    provide('car',car)
}
  • 后代组件注入:
setup(props,context){
	......
	// 获取祖组件传递过来命名为car的数据
    const car = inject('car'"这里是默认值")
    return {car}
}

🌀watch函数

  • watch(被监听的数据, 回调函数 ,{ 配置对象 })
  • 其他可选参数:
    • immediate:在侦听器创建时立即触发回调。第一次调用时旧值是 undefined。
    • deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。
    • flush:调整回调函数的刷新时机。pre默认值,在元素 挂载 或者 更新 之前执行; post将会使侦听器延迟到组件渲染之后再执行;sync 在响应式依赖发生改变时立即触发侦听器。该设置应谨慎使用,因为如果有多个属性同时更新,这将导致一些性能和数据一致性的问题。
    • onTrack / onTrigger:调试侦听器的依赖
  • 监听单个ref对象的时候,不用.value
  • 监听 reactive 定义的响应式数据时,无法正确的获取 oldValue、强制开启了深度监听(deep 配置失效)
  • 监听 reactive 定义的响应式对象中的属性时,需要将该属性放在一个函数中返回,作为 watch 函数的第一个参数
<template>
  <div>
    {{ msg }}<br />
    {{ obj.name }}<br />
    {{ subject }}<br />
    {{ obj.job.salary }}<br />
    <button @click="updateMsg">修改msg</button><br />
    <button @click="updateName">修改obj的name</button><br />
    <button @click="updateSubject">修改subject</button><br />
    <button @click="updateSalary">修改obj.job.salary</button><br />
  </div>
</template>

<script >
import { reactive, ref, watch, toRefs } from "vue";

export default {
  setup() {
    const msg = ref("一个消息");
    const subject = ref("一个话题");

    const obj = reactive({
      name: "张三",
      age: 22,
      job: {
        salary: 20,
      },
    });

    function updateMsg() {
      msg.value = "一个新消息";
    }

    function updateName() {
      obj.name = "李四";
    }

    function updateSubject() {
      subject.value = "另一个话题";
    }

    function updateSalary() {
      obj.job.salary ++;
    }
	//监听一个ref对象
    watch(msg, function (newVal, oldVal) {
      console.log("msg旧值:", oldVal);
      console.log("msg新值:", newVal);
    });

    //监听多个ref对象,新旧值也是数组
    watch([msg, subject], (newVal, oldVal) => {
      console.log("监听多个属性的方法被调用", newVal, oldVal);
    });

    //监听对象中的属性,即使是深度监听也不行
    watch(
      obj,
      (newVal, oldVal) => {
        console.log("obj 旧值:", oldVal);
        console.log("obj 新值:", newVal);
      },
      { deep: true }
    );

    //监听对象中的属性,无法监听,会报一个警告
    watch(
      obj.name,
      (newVal, oldVal) => {
        console.log("深度监听obj.name旧值:", oldVal);
        console.log("深度监听obj.name新值:", newVal);
      },
      { deep: true }
    );

    //监听对象属性的方法一
    watch(
      () => obj.name,
      (newVal, oldVal) => {
        console.log("方法一:obj.name旧值:", oldVal);
        console.log("方法一:obj.name新值:", newVal);
      }
    );

    //监听对象属性的方法二
    const { name: tempName } = toRefs(obj);
    watch(tempName, (newVal, oldVal) => {
      console.log("方法二:obj.name旧值:", oldVal);
      console.log("方法二:obj.name新值:", newVal);
    });
    

    //监听对象的对象属性, 需要开启 deep
    watch(() => obj.job, (newVal, oldVal) => {
      console.log("job 旧值:", oldVal);
      console.log("job 新值:", newVal);
    }, {deep: true});

    //这时不需要开启 deep 
    watch(obj.job, (newVal, oldVal) => {
      console.log("job 旧值2:", oldVal);
      console.log("job 新值2:", newVal);
    }, {deep: false});

    return { msg, updateMsg, obj, updateName, subject, updateSubject, updateSalary };
  },
};
</script>

🌀watchEffect函数

  • watchEffect传入的函数会被立即执行一次,并且在执行的过程中会收集依赖
  • 只有收集的依赖发生变化时,watchEffect传入的函数才会再次执行;
  • 立即执行,没有惰性,页面的首次加载就会执行
  • 不需要传递要侦听的内容 会自动感知代码依赖,不需要传递很多参数,只要传递一个回调函数
  • 不能获取之前数据的值 只能获取当前值
<template>
  <div>
    <h2>当前计数: {{ counter }}</h2>
    <button @click="counter++">+1</button>
    <button @click="name = 'kobe'">修改name</button>
  </div>
</template>

<script setup>
  import { watchEffect, ref } from 'vue'

  const counter = ref(0)
  const name = ref("why")

  const stopWatch = watchEffect(() => {
    console.log("-------", counter.value, name.value)
  })
</script>
  • watchEffect的停止侦听
<template>
  <div>
    <h2>当前计数: {{ counter }}</h2>
    <button @click="counter++">+1</button>
    <button @click="name = 'kobe'">修改name</button>
  </div>
</template>

<script setup>
  import { watchEffect, ref } from 'vue'

  const counter = ref(0)
  const name = ref("why")

  const stopWatch = watchEffect(() => {
    console.log("-------", counter.value, name.value)
	//设置条件, stopWatch 
    if (counter.value >= 10) {
       stopWatch()
    }
  })
</script>

🌀defineProps函数

  • 定义props
<script setup>

// 定义props
const props = defineProps({
  name: {
    type: String,
    default: "默认值"
  },
  age: {
    type: Number,
    default: 0
  }
})

</script>

🌀defineEmits函数

  • 绑定函数, 并且发出事件
<script setup>

// 绑定函数, 并且发出事件
const emits = defineEmits(["自定义事件"])
function showInfoBtnClick() {
  emits("自定义事件", 参数payload)
}

</script>

🌀defineExpose函数

  • 使用 <script setup> 的组件是默认关闭的:
    • 通过模板 ref 或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定
  • 指定组件需要暴露出去的内容
  • 父组件ref获取子组件实例,必须暴漏才能访问内容
<script setup>

// 定义foo的函数
function foo() {
  console.log("foo function")
}
defineExpose({
  foo
})

</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值