1、新增特性

1.Composition API(组合API)

  • setup配置
  • ref与reactive
  • watch与watchEffect
  • provide与inject

2.新的内置组件

  • Fragment
  • Teleport
  • Suspense

3.其他改变

  • 新的生命周期钩子
  • data选项应始终被声明为一个函数
  • 移除keyCode支持作为v-on的修饰符

2、创建项目

1.Vue-cli脚手架搭建项目

## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue -V
## 安装获取升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue-test
## 启动
cd vue_test
npm run serve
注意:yarn安装npm install -g yarn

Vue3.0_前端

2.使用vite创建

  • 什么是vite?—— 是Vue团队打造的新一代前端构建工具。
  • 优势如下:
  • 1、开发环境中,无需打包操作,可快速的冷启动。
  • 2、轻量快速的热重载(HMR)。
  • 3、真正的按需编译,不再等待整个应用编译完成。
  • ​npm init @vitejs/app <项目名> ​

3、Composition API

1、是什么?

Composition API也叫组合式API,是Vue3.0的新特性。

通过创建Vue组件,
我们可以将接口的可重复部分及其功能提取到可重复的代码部分及其功能提取到可重用的代码段中。

2、为什么使用CompositionAPI?

Vue3.0_javascript_02

3、函数

Vue3.0_javascript_03

4、setup

setup()函数是vue3.0中,专门为组件提供的新属性,它为我们使用vue3的CompositionAPI新特性提供了统一的入口。

-1、组件中所用到的:数据、方法等等,均要配置在setup中。
2、若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。
若返回一个渲染函数:则可以自定义渲染内容。

<template>
<h1>博主的信息</h1>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>性别:{{gender}}</h2>
<button @click="sayInfo">显示信息</button>
</template>

<script>
// import {h} from 'vue'
export default {
name: "App",
//此处只是测试一下setup,暂时不考虑响应式的问题。
setup(){
// 数据
let name = "YK菌"
let age = 18
let gender = "男"

// 方法
function sayInfo(){
alert(`你好${name},你太厉害了吧`)
}
// 返回一个对象(常用)
return {
name,age, gender,sayInfo
}
// 返回一个函数(渲染函数)
// return ()=> h('h1','YK菌yyds')
}
};
</script>
props:组件传入的属性 context:上下文对象

Vue3.0_前端_04

5、ref函数

作用: 定义一个响应式的数据
语法: const xxx = ref(initValue)

  • 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
  • JS中操作数据: xxx.value
  • 模板中读取数据:不需要.value,直接: {{xxx}}

备注:

  • 接收的数据可以是:基本类型、也可以是对象类型。
  • 基本类型的数据:响应式依靠的是类上的getter与setter完成的(我们等下看下源码你就知道了)。
  • 对象类型的数据:内部 “ 求助 ”了Vue3.0中的一个新函数—— reactive函数。

<template>
<!-- <h3>数量:{{num}}</h3> -->
<h3>count数量:{{count}}</h3>
<input type="text" v-model="count">
<button @click="getCount()">获取</button>
</template>
<script>
import {ref} from 'vue'
export default {
//option API方式
// data(){
// return{
// num:0
// }
// }
//composition API方式
setup(){//data methods computed watch都是写在setup中
//直接这样定义 不是响应式的数据
// const count = 0;

//创建响应式的数据对象 我们的count 给初始值为0
const count = ref(0)

let getCount = ()=>{
//如果要访问ref() 创建出来响应式数据对象的值 必须通过.value属性才可以
console.log(count.value)
}
//模板中要使用这些变量和方法 都需要调用,所以需要return
return{
count,getCount
}
}
}
</script>
<style>
</style>

6、reactive

-作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)

  • 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
  • reactive定义的响应式数据是“深层次的”。
  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。

7、reactive对比ref

1.从定义数据角度对比

  • ref用来定义:基本类型数据。
  • reactive用来定义:对象(或数组)类型数据。
  • 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象。

2.从原理角度对比

-ref通过类中的的getter与setter来实现响应式(数据劫持)。

  • reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。

3.从使用角度对比

  • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value。
  • reactive定义的数据:操作数据与读取数据:均不需要.value。

4.从使用角度对比

8、toRefs

toRefs将响应式对象转换为普通对象,其中结果对象的每个property都是指向原始对象相应property的ref。

-作用:创建一个ref对象,其value值指向另一个对象中的某个属性。

  • 语法:const name = toRef(person,‘name’)
  • 应用: 要将响应式对象中的某个属性单独提供给外部使用时。
  • 扩展:toRefs与toRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)

9、computed

<template>
<hr>
<input type="text" v-model="test">
<h2>user:{{test}}</h2>
<!-- <h3>姓名:{{data.user.name}}</h3> -->
<h3>姓名:{{user.name}}</h3>
<li v-for="item in list ">
{{item}}
</li>
<hr>
<p>
姓:<input type="text" v-model="firstName">
</p>
<p>
名:<input type="text" v-model="lastName">
</p>
<h4>全名:{{fullName}}</h4>

<button @click="getFullName">获取计算属性</button>
</template>

<script>

import {ref,toRefs,reactive,computed} from 'vue'
export default{
setup(){
//reactive创建响应式数据对象
const data = reactive({
test:"测试",
user:{
name:"木道人",
sex:"男"
},
list:["特斯拉","追梦","请叫我小平头"]
})

//创建一个响应式对象 user
const user = reactive({
firstName:"",
lastName:""
})

//它会根据user的值 创建一个响应式的计算属性fullName 自动计算并返回一个新的响应式数据(ref)
const fullName = computed(()=>{
return user.firstName + " " + user.lastName
})

const getFullName = ()=>{
console.log(fullName.value)
}

return{
// 解构响应式对象数据
/* 用 ...扩展运算解构响应式对象数据,是不可行。因为解构后就变成成了一个普通变量,不在具有响应式的能力 */
// ...data
// toRefs 把一个响应式的对象转换成普通对象,对data进行了包装,再使用...拓展符的方式将data解构
...toRefs(data),
...toRefs(user),fullName,getFullName
}
}
}
</script>
<style>
</style>

10、watch

watch()函数用来监视某些数据项的变化,从而触发某些特定的操作,使用之前需要按需导入:

注意:
1、监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
2、监视reactive定义的响应式数据中某个属性时:deep配置有效。

export default{
setup(props,context){//composition API的入口
const num = ref(0)
const str = ref("测试")
const state = reactive({
id:101,
uname:'莫愁'
})

//只要num有变化 会触发watch的回调 watch会在创建时自动调用
// watch(()=>{console.log(num.value)})

/* val:新的值 oval:旧的值 */
watch(num,(val,oval)=>{
console.log(val,oval)
console.log("c num:",num.value)
},{//第二个参数obj immediate deep
immediate:false //默认就是只有数据改变时才会监听,第一次不会执行,设置true第一次也会执行
})

//侦听state下的id,数据的变化
watch(state,(val,oval)=>{
console.log("id:",val.id,oval)
},{
immediate:true ,//默认就是只有数据改变时才会监听,第一次不会执行,设置true第一次也会执行
deep:true,//开启深度侦听 能够侦听到对象的属性值的变化
})

//单个侦听state下的uname
watch(()=>state.uname,(uname,p)=>{
console.log(uname,p)
},{
immediate:true ,//默认就是只有数据改变时才会监听,第一次不会执行,设置true第一次也会执行
})
//const s = toRefs(state) watch(s.id,()=>{}) 等于 ()=>state.id

//多个侦听数据
const stop = watch([()=>state.id,()=>state.uname],([id,uname],[oid,oname])=>{
console.log("id:",id,oid)
console.log("uname",uname,oname)
})
return{
num,stop,
...toRefs(state)
}
}
}

11、watchEffect函数【用到哪个、监视哪个】

1.watch的套路是:既要指明监视的属性,也要指明监视的回调。
2.watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
watchEffect有点像computed:

  1. 1、但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
  2. 2、而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。

12、API

Vue3.0_vue.js_05

Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:

Vue3.0_vue.js_06

13、自定义 hook函数【重点】

  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。
  • 类似于vue2.x中的混合机制mixin。
  • 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。

1、创建一个hooks文件夹,里面创建文件usePoint.js

import { reactive, onMounted, onBeforeUnmount } from "vue";
export default function() {
//实现鼠标“打点”相关的数据
let point = reactive({
x: 0,
y: 0,
});

//实现鼠标“打点”相关的方法
function savePoint(event) {
point.x = event.pageX;
point.y = event.pageY;
console.log(event.pageX, event.pageY);
}

//实现鼠标“打点”相关的生命周期钩子
onMounted(() => {
window.addEventListener("click", savePoint);
});

onBeforeUnmount(() => {
window.removeEventListener("click", savePoint);
});

return point;
}

2、在组件中使用

<template>
<h2>我是HelloWorld组件</h2>
<h2>当前点击时鼠标的坐标为:x:{{point.x}},y:{{point.y}}</h2>
</template>

<script>
import usePoint from '../hooks/usePoint'
export default {
name:'HelloWorld',
setup(){
const point = usePoint()
return {point}
}
}
</script>

14、其它 Composition API

1.shallowReactive 与 shallowRef

Vue3.0_javascript_07

2.readonly 与 shallowReadonly

Vue3.0_vue.js_08

3.toRaw 与 markRaw

Vue3.0_前端_09

4.customRef

  1. 作用: 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
  2. 实现防抖效果

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

<script>
import { customRef } from "vue";
export default {
name: "App",
setup() {
//自定义一个ref——名为:myRef
function myRef(value, delay) {
let timer;
return customRef((track, trigger) => {
return {
get() {
console.log(`有人从myRef这个容器中读取数据了,我把${value}给他了`);
track(); // 通知Vue追踪value的变化(提前和get商量一下,让他认为这个value是有用的)
return value;
},
set(newValue) {
console.log(`有人把myRef这个容器中数据改为了:${newValue}`);
clearTimeout(timer);
timer = setTimeout(() => {
value = newValue;
trigger(); // 通知Vue去重新解析模板
}, delay);
},
};
});
}

// let keyWord = ref('hello') //使用Vue提供的ref
let keyWord = myRef("hello", 500); //使用程序员自定义的ref

return { keyWord };
},
};
</script>

15、Provide&inject

1、provide

provide: 向子组件以及子孙组件传递数据。接收两个参数,第一个参数是key.即数据的名称;第二个参数为value,即数据的值

Vue3.0_vue.js_10

2、inject

inject: 接收父组件或祖先组件传递过来的数据。接收一个参数key,即父组件或祖先组件传递的数据名称

Vue3.0_数据_11

16、响应式数据的判断

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

17、新的组件

1.Fragment

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

2.Teleport

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

3.Suspense

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

18、vue3.x中集成typescript

1.为什么要使用ts

Vue3.0_数据_12

2.vue-cli(脚手架)安装ts

Vue3.0_前端_13

3.vite安装ts

Vue3.0_javascript_14

4.定义组件

Vue3.0_vue.js_15

5.案例代码

<template>
<h2>集成ts与compositionAPI一起使用</h2>
<h4>姓名:{{uname}}</h4>
<h4>兴趣:{{hobby}}</h4>
<h4>年龄:{{age}}</h4>
<h5>getUNname:{{getUNname(111)}}</h5>
<p>{{count}}</p>
<button @click="setCount()">setCount</button>
</template>

<script lang="ts">
//defineComponent 启用类型校验
import {defineComponent,reactive,toRefs,ref} from 'vue'
//约束data中的属性与方法
interface user{
uname:string,
hobby:string,
age:number,
getUNname(msg:string):string
}
export default defineComponent ({
setup(){
// 三种方式: : <> as
const data:user = reactive({
uname:"肖雅涵",
hobby:"舞蹈",
age:20,
getUNname(msg){
return msg
}
})
//不支持:这种方式约束类型
// const count:number = ref(0)
const count= ref<number>(0)
function setCount(){
count.value = 123
}
return{
...toRefs(data),count,setCount
}
}
})
</script>
<style>
</style>

18、组合式API结合vue-router

  • 引入setup和Vue的组合式API,开辟了新的可能性,但要充分发挥Vue Router的潜力,我们需要使用一些新的函数来代替访问this和组件内导航守卫。
  • 因为我们在setup里面没有访问this,所以我们不能在直接访问this.Vue3.0_javascript_16route。作为替代,我们使用useRouter和useRoute函数。
//引入路由
import {useRouter,useRoute} from 'vue-router'
export default{
setup(){
const router = useRouter()//等同于this.$router
const route = useRoute()//等同于this.$route

}
}

19、组合式API结合vuex

如果在使用Vue 中的 组合式API进行编写组件,就不能延续之前的写法(如: this.s t o r e ) 。 由 于 我 们 无 权 访 问 s e t u p 的 内 部 t h i s , 因 此 要 在 s e t u p 中 使 用 s t o r e , 可 以 调 用 该 u s e S t o r e 函 数 。 这 等 效 t h i s . store)。由于我们无权访问setup的内部this,因此要在setup中使用store,可以调用该useStore函数。这等效this.store)。由于我们无权访问setup的内部this,因此要在setup中使用store,可以调用该useStore函数。这等效this.store于使用Option API在组件内进行检索。

setup中访问vuex

请注意,在模板中我们仍然可以访问$store,所以不需要在setup中返回store

20、resolve alias 别名定义

将会被传递到 @rollup/plugin-alias 作为它的 entries。也可以是一个对象,或一个 { find,replacement } 的数组.
当使用文件系统路径的别名时,请始终使用绝对路径。相对路径作别名值将按原样使用导致不会解析到文件系统路径中。

export default defineConfig({
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
"@comps": path.resolve(__dirname, "src/components"),
"@views": path.resolve(__dirname, "src/views"),
},
},
plugins: [vue()],
})

或者 数组的形式

import {defineConfig} from 'vite'
import path from "path";
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: [{
find: '@',
replacement: path.resolve(__dirname, 'src')
},
{
find: 'components',
replacement: path.resolve(__dirname, 'src/components')
}
],
},
plugins: [vue()],
});