Vue3.0支持大多数的Vue2的特性,Vue3中设计了一套强大的composition组合API代替Vue2中的optionAPI,复用性更强大了,对TypeScript有更好的支持。
1、Composition API(组合API)
2、新的内置组件
3、其他改变
(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
npm init @vitejs/app <项目名>
Composition API也叫组合式API,是Vue3.0的新特性。
通过创建Vue组件,我们可以将接口的可重复部分及其功能提取到可重复的代码部分及其功能提取到可重用的代码段中。
setup()函数是vue3.0中,专门为组件提供的新属性,它为我们使用vue3的CompositionAPI新特性提供了统一的入口。
<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:上下文对象
作用: 定义一个响应式的数据
语法: const xxx = ref(initValue)
xxx.value
.value
,直接:{{xxx}}
备注:
<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>
1、从定义数据角度对比
ref
用来定义:基本类型数据。
reactive
用来定义:对象(或数组)类型数据。
备注:ref
也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive
转为代理对象。
2、从原理角度对比
ref
通过类中的的getter
与setter
来实现响应式(数据劫持)。
reactive
通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
3、从使用角度对比
ref
定义的数据:操作数据需要.value
,读取数据时模板中直接读取不需要.value
。
reactive
定义的数据:操作数据与读取数据:均不需要.value
。
toRefs将响应式对象转换为普通对象,其中结果对象的每个property都是指向原始对象相应property的ref。
ref
对象,其value值指向另一个对象中的某个属性。const name = toRef(person,'name')
toRefs
与toRef
功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
user:{{test}}
姓名:{{user.name}}
{{item}}
姓:
名:
全名:{{fullName}}
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)
}
}
}
watch
的套路是:既要指明监视的属性,也要指明监视的回调。
watchEffect
的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
watchEffect
有点像computed
:
computed
注重的计算出来的值(回调函数的返回值),所以必须要写返回值。watchEffect
更注重的是过程(回调函数的函数体),所以不用写返回值。Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
hook
?—— 本质是一个函数,把setup
函数中使用的Composition API
进行了封装。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>
shallowReactive
与 shallowRef
shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理。
什么时候使用?
1、如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive
。
2、如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===>shallowRef
。
readonly
与 shallowReadonly
toRaw
与 markRaw
toRaw:
响应式对象
转为普通对象
。markRaw:
customRef
<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>
provide: 向子组件以及子孙组件传递数据。接收两个参数,第一个参数是key.即数据的名称;第二个参数为value,即数据的值
inject: 接收父组件或祖先组件传递过来的数据。接收一个参数key,即父组件或祖先组件传递的数据名称
isRef:
检查一个值是否为一个ref
对象isReactive:
检查一个对象是否是由 reactive
创建的响应式代理isReadonly:
检查一个对象是否是由readonly
创建的只读代理isProxy:
检查一个对象是否是由 reactive
或者 readonly
方法创建的代理什么是Teleport
?—— Teleport
是一种能够将我们的组件html结构移动到指定位置的技术。
等待异步组件时渲染一些额外内容,让应用有更好的用户体验。
如果要让TypeScript正确推断Vue组件选项中的类型,需要使用defineComponent全局方法定义组件:
<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>
引入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
}
}
如果在使用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在组件内进行检索。
请注意,在模板中我们仍然可以访问$store,所以不需要在setup中返回store
将会被传递到 @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 - 响应式原理 - 新特性