一、vue3.0快速上手
1、Vue3简介
Vue3.0支持大多数的Vue2的特性,Vue3中设计了一套强大的composition组合API代替Vue2中的optionAPI,复用性更强大了,对TypeScript有更好的支持。
2、Vue3带来了什么?
1、性能的提升
- 打包大小减少了41%
- 初次渲染块55%,更新渲染块133%
- 内存减少54%
…
2、源码的升级
- 使用Proxy代替defineProperty实现响应式
- 重写虚拟DOM的实现和Tree-Shaking
…
3、拥抱TypeScript
- Vue3可以更好的支持ypeScript
4、新的特性
1、Composition API(组合API)
- setup配置
- ref与reactive
- watch与watchEffect
- provide与inject
…
2、新的内置组件
- Fragment
- Teleport
- Suspense
3、其他改变
- 新的生命周期钩子
- data选项应始终被声明为一个函数
- 移除keyCode支持作为v-on的修饰符
…
二、创建项目
2.1Vue-cli脚手架搭建项目
(1)首先查看你的版本
## 查看@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
2.2、使用vite创建
- 什么是vite?—— 是Vue团队打造的新一代前端构建工具。
- 优势如下:
1、开发环境中,无需打包操作,可快速的冷启动。
2、轻量快速的热重载(HMR)。
3、真正的按需编译,不再等待整个应用编译完成。
npm init @vitejs/app <项目名>
三、Composition API
3.1是什么?
Composition API也叫组合式API,是Vue3.0的新特性。
通过创建Vue组件,我们可以将接口的可重复部分及其功能提取到可重复的代码部分及其功能提取到可重用的代码段中。
3.2为什么使用CompositionAPI?
3.3函数
3.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:上下文对象
3.5(二)ref函数
作用: 定义一个响应式的数据
语法: const xxx = ref(initValue)
- 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
- JS中操作数据:
xxx.value
- 模板中读取数据:不需要
.value
,直接:<div>{{xxx}}</div>
备注:
- 接收的数据可以是:基本类型、也可以是对象类型。
- 基本类型的数据:响应式依靠的是类上的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>
3.5(三)reactive
- 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
- 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
- reactive定义的响应式数据是“深层次的”。
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
3.6(四)reactive对比ref
1、从定义数据角度对比
ref
用来定义:基本类型数据。
reactive
用来定义:对象(或数组)类型数据。
备注:ref
也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive
转为代理对象。
2、从原理角度对比
ref
通过类中的的getter
与setter
来实现响应式(数据劫持)。
reactive
通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
3、从使用角度对比
ref
定义的数据:操作数据需要.value
,读取数据时模板中直接读取不需要.value
。
reactive
定义的数据:操作数据与读取数据:均不需要.value
。
3.7(五)toRefs
toRefs将响应式对象转换为普通对象,其中结果对象的每个property都是指向原始对象相应property的ref。
- 作用:创建一个
ref
对象,其value值指向另一个对象中的某个属性。 - 语法:
const name = toRef(person,'name')
- 应用: 要将响应式对象中的某个属性单独提供给外部使用时。
- 扩展:
toRefs
与toRef
功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
3.8(六)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>
3.9(七)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)
}
}
}
3.10(八)watchEffect函数【用到哪个、监视哪个】
watch
的套路是:既要指明监视的属性,也要指明监视的回调。
watchEffect
的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
watchEffect
有点像computed
:
- 1、但
computed
注重的计算出来的值(回调函数的返回值),所以必须要写返回值。 - 2、而
watchEffect
更注重的是过程(回调函数的函数体),所以不用写返回值。
3.11(九)API
Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
- beforeCreate===>setup()
- created=======>setup()
- beforeMount===>onBeforeMount
- mounted=======>onMounted
- beforeUpdate===>onBeforeUpdate
- updated =======>onUpdated
- beforeUnmount ==>onBeforeUnmount
- unmounted =====>onUnmounted
3.12(十)自定义 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>
四、其它 Composition API
1、shallowReactive
与 shallowRef
-
shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
-
shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理。
-
什么时候使用?
1、如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===>
shallowReactive
。
2、如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===>shallowRef
。
2、readonly
与 shallowReadonly
- readonly: 让一个响应式数据变为只读的(深只读)。
- shallowReadonly: 让一个响应式数据变为只读的(浅只读)。
- 应用场景: 不希望数据被修改时。
3、toRaw
与 markRaw
toRaw:
- 作用: 将一个由reactive生成的
响应式对象
转为普通对象
。 - 使用场景: 用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
markRaw:
- 作用: 标记一个对象,使其永远不会再成为响应式对象。
- 应用场景: ①有些值不应被设置为响应式的,例如复杂的第三方类库等。②当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
4、customRef
- 作用: 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
- 实现防抖效果
<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>
5、Provide&inject
1、provide
provide: 向子组件以及子孙组件传递数据。接收两个参数,第一个参数是key.即数据的名称;第二个参数为value,即数据的值
2、inject
inject: 接收父组件或祖先组件传递过来的数据。接收一个参数key,即父组件或祖先组件传递的数据名称
6、响应式数据的判断
isRef:
检查一个值是否为一个ref
对象isReactive:
检查一个对象是否是由reactive
创建的响应式代理isReadonly:
检查一个对象是否是由readonly
创建的只读代理isProxy:
检查一个对象是否是由reactive
或者readonly
方法创建的代理
五、新的组件
1、Fragment
- 在Vue2中: 组件必须有一个根标签
- 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
- 好处: 减少标签层级, 减小内存占用
2、Teleport
什么是Teleport
?—— Teleport
是一种能够将我们的组件html结构移动到指定位置的技术。
3、Suspense
等待异步组件时渲染一些额外内容,让应用有更好的用户体验。
六、vue3.x中集成typescript
6.1为什么要使用ts
- 类型检查、直接编译到原生的js、引入语法糖。
- TypeScript的设计目的应该是解决JavaScript的“痛点”:【弱类型、没有命名空间。导致很难模块化、不适合开发大型程序】;
- 另外它还提供了一些语法糖来帮助大家更方便地实践面向对象的编程。
6.2vue-cli(脚手架)安装ts
6.3vite安装ts
6.4定义组件
如果要让TypeScript正确推断Vue组件选项中的类型,需要使用defineComponent全局方法定义组件:
6.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>
七、组合式API结合vue-router
引入setup和Vue的组合式API,开辟了新的可能性,但要充分发挥Vue Router的潜力,我们需要使用一些新的函数来代替访问this和组件内导航守卫。
因为我们在setup里面没有访问this,所以我们不能在直接访问this.$router或this.$route
。作为替代,我们使用useRouter和useRoute函数。
//引入路由
import {useRouter,useRoute} from 'vue-router'
export default{
setup(){
const router = useRouter()//等同于this.$router
const route = useRoute()//等同于this.$route
}
}
八、组合式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在组件内进行检索。
8.1setup中访问vuex
请注意,在模板中我们仍然可以访问$store,所以不需要在setup中返回store
九、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()],
});
其他文章:【Vue】带你快速上手Vue3 - 使用 - Composition API - 响应式原理 - 新特性