Vue3.x
文档目录
Vue3采用TS重写
vue-cli3目录结构
node_modules:存储相关依赖包
public:文件夹的静态文件都会被简单复制,不经过webpack
src:代码文件夹
dist:打包后生成的文件目录
|----assets:存储项目中自己的一些静态文件(图片/字体)
|----components:存储项目中的自定义组件(小组件,公共组件)
|----views:存储项目中的自定义组件(大组件,页面级组件,路由级别组件)
|----router:存储VueRouter相关文件
|----store:存储Vuex相关文件
|----App.vue 根组件
|----main.js 入口js文件
Vue3优点
- 性能比Vue2.x快1.2~2倍
- 按需编译,体积比Vue2.x更小
- 组合API(类似React Hooks)
- 更好的支持TS
- 暴露了自定义渲染API
- 更先进的组件
使用Vite创建Vue3项目
-
安装Vite
npm install -g create-vite-app
-
使用vite创建vue3项目
create-vite-app projectName
-
安装依赖运行项目
cd projectName npm install npm run dev
组合API
什么是组合API?
理解:将某个功能所需要的数据以及方法封装为一个模块
Vue2与Vue3之间对比
vue2:开发一个功能需要在data中添加指定数据结构,在methods中添加方法
vue3组合API:通过setup组合api函数入口创建指定方法,进行返回使用
通过组合API可以更好的管理每个模块
Vue3的一大特性函数:setup
1、setup函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之间的函数 也就说在 setup函数中是无法 使用 data 和 methods 中的数据和方法的
2、setup函数是 Composition API(组合API)的入口
3、在setup函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用
setup函数的注意点:
1、由于在执行 setup函数的时候,还没有执行 Created 生命周期方法,所以在 setup 函数中,无法使用 data 和 methods 的变量和方法
2、由于我们不能在 setup函数中使用 data 和 methods,所以 Vue 为了避免我们错误的使用,直接将 setup函数中的this修改成了 undefined
3、setup函数只能是同步的不能是异步的
实例:
import {ref} from 'vue';
export default {
name: 'App',
setup(){
//定义变量,使用ref让数据改变跟新页面内容
//ref注意点:ref函数只能监听简单类型的变化,不能监听复杂类型的变化(对象、数组)
const count = ref(0);
//定义方法
function add(){
count.value++;
}
//如果想要外界使用,必须将数据暴露出去
return{
count,
add
}
}
}
以上例子通过ref进行监听数据变化从而渲染页面,但是ref函数只能监听简单类型数据,如果想要监听复杂数据类型(比如对象,数组)需要使用reactive
函数
进行定义
import {reactive} from 'vue';
export default {
name: 'App',
setup(){
const {state,removeStu} = useRemoveStu();
const {stu,addStu} = useAddStu(state);
return {state,removeStu,stu,addStu}
},
}
//添加操作
function useAddStu(state) {
const stu = reactive({
stu:{
id:'',
name:'',
age:''
}
});
function addStu(e){
e.preventDefault();
let stu1 = Object.assign({},stu.stu);
state.stus.push(stu1);
stu.stu = {};
}
return {stu,addStu}
}
//删除操作
function useRemoveStu(){
const state = reactive({
stus:[
{id:1,name:'小三',age:12},
{id:2,name:'李四',age:19},
{id:3,name:'张三',age:20}
]
});
//删除操作
function removeStu(index){
state.stus = state.stus.filter((_item,_index)=>index!=_index)
}
return {state,removeStu}
}
这样就可以将某个功能封装成一个模块去使用,也可以将方法写到不同的js文件中,通过export进行导出,在需要使用某个功能时将js文件导入使用即可
组合API的本质
Composition API 和 Option API是可以进行混合使用
Composition API :Vue3中的setup函数
Option API:Vue2中的data、methods属性中等
组合API的本质:组合API的本质就是将setup函数中的方法以及数据注入到 Option API中进行使用
Reactive与Ref
什么是Reactive?
- reactive是Vue3中提供的实现响应式数据的方法
- 在Vue2中响应式数据是通过defineProperty来实现的,而在Vue3中响应式数据是通过ES6的Proxy来实现的
reactive注意点
- reactive参数必须是对象(json/arr)
- 如果给reactive传递了其他对象,默认情况下修改对象界面不会刷新,如果想刷新可以通过重新赋值的方式进行界面更新
什么是Ref ?
ref和reactive一样,也是用来实现响应式数据的方法,由于reactive必须传递一个对象,所以导致在企业开发中如果我们只想让某个变量实现响应式的时候会非常麻烦,所以Vue3就给我们提供了Ref方法,实现对简单值的监听
Ref本质:ref底层的本质其实还是reactive,系统会自动将我们传入的值转换成 ref(xxx) -> reactive({value:xxx})
Ref注意点:
- 在Vue中使用Ref的值不用通过value获取
- 在Js中使用Ref的值必须通过value获取
- 如果是通过ref创建的数据,那么在template中使用的时候不用通过.value获取,因为Vue会自动给我们添加.value
ref和reactive区别
如果在template里使用的是ref类型的数据,那么Vue会自动帮我们添加.value
如果在template里使用的是reactive类型的数据,那么Vue不会自动帮我们添加.value
Vue是如何决定是否需要自动添加.value的
Vue在解析数据之前,会自动判断这个数据是否是ref类型的,如果是就自动添加.value,如果不是就不自动添加.value
Vue是如何判断当前的数据是否是ref类型的
通过当前数据的__v_ref来判断的
如果有这个私有属性,并且取值为true,那么就代表是要给ref 类型的数据
*如何自己判断是否为ref类型以及reactive类型?
通过引入vue中的 isRef方法和isReactivce方法进行判断返回true/false
//引入
import {isRef,isReactive} from 'vue';
import {ref,reactive} from 'vue'
//使用
let age = ref(1);
const state = reactive({
stus:{id:1,name:'小明'}
});
console.log("是否是ref类型:",isRef(age)); //true
console.log("是否是Reactive类型:",isReactive(state)); //true
递归监听
默认情况下,无论是通过ref还是reactive都是递归监听,Vue3中为了实现数据响应式它会把每一层的数据包装成一个proxy对象
缺点:如果数据量较大,非常消耗性能
非递归监听shallowRef / shallowReactive
如果是shallowRef类型的数据,可以通过triggerRef方法来触发更新页面内容
默认情况会将第一层数据打包为proxy对象,当前数据发生改变时才会更新页面内容
注意点:如果是通过shallowRef创建的数据,那么Vue创建的
应用场景
一般情况使用ref和reactive即,只有在需要监听的数据量较大的时候,我们才使用shallowRef和shallowReactive
shallowRef本质
本质和之前的ref类似,shallowRef本质上是采用了shallowReactive进行使用,所以它只能监听value,这个value就是它的第一层
shallowRef (10) ==> shallowReactive({value:10}) value就是它的第一层数据
toRaw
获取ref/reactive引用的真实对象,通过修改真实对象无法触发界面更新
toRaw作用:提升性能,做一些不想在页面上改变的
ref/reactive数据类型的特点:
每次修改都会被监听,都会更新UI界面,但是这样其实是非常消耗性能的所以如果我们有一些操作不需要监听,不需要更新UI界面,那么这个时候就可以通过toRaw方法拿到原始数据进行对原始数据进行修改,这样就不会被监听
注意点:如果想使用toRaw获取ref中的原始数据,那么必须使用.value进行获取,因为ref本质就是使用的reactive进行创建对象将引用放入value属性中
即:let obj = toRaw(state.value);
例如:
let user = {name:‘xiaoge’,age:12}
const state = reactive(user); //在reactive包装的porxy中引用user对象
//使用toRaw获取原始引用对象
let user2 = toRaw(state);//传入reactive对象获取到对象引用 所以 user2 === user = true
MarkRaw: 使用这个方法将不会追踪此对象,也就是说无论怎么改变都不会更新UI页面,通过reactive方法也不可以
toRef
ref 和 toRef区别:
使用ref将某个对象中的属性进行包装,修改后它的原始引用值不会受影响,也就是将对象中的属性值直接复制到当前ref中,
更新数据后页面进行响应
toRef 会将之前的对象进行关联,并且更新数据后页面不会发生变化
setup(){
let user = {name:'xiaoge',age:12}
//使用toRef进行引用对象的属性
const state = toRef(user,'name');
//修改toRef包装的属性值,引用对象也发生变化并且UI不会进行响应
function change(){
state.value = "小王";
console.log("state",state);
console.log("user",user);
}
return {user,change,state}
}
toRefs
我们在使用toRef的时候不能将对象中的多个属性进行引用,使用toRefs即可关联多个属性
let user = {name:'xiaoming',age:12}
//toRef只能单个属性进行关联
let name = toRef(user,'name');
let age = toRef(user,'age');
//使用toRefs
let state = toRefs(user);//会将对象中的属性都关联进去
//通过这种方式进行赋值以及获取操作
state.name.value = '小红';
state.age.value = 10;
customRef
返回一个ref对象,可以显式地控制依赖追踪和触发响应,自定义Ref
我觉得这个是个自定义ref罢了,可以控制是否是响应式的以及在里面写上自己的逻辑
实例:
function myRef(value){
return customRef((track,trigger)=>({
get(){
track();//告诉Vue这个数据需要追踪变化的
console.log("get",value);
return value;
},
set(newValue){
console.log("set",newValue);
value = newValue;
trigger();
}
}))
}
track:告诉Vue这个数据需要追踪变化
trigger:触发数据改变UI界面
为什么要使用自定义ref呢?
我们可以自定义一个ref,在里面可以写入自己的代码逻辑,并且我们在开发时大部分需要请求外部接口,那么就需要这个
正常情况发送异步请求进行数据响应:
setup(){
const state = ref([]);
fetch('/data.json').then(res=>{
return res.json();
}).then(res=>{
state.value = res;
});
return {state}
}
通过自定义ref进行实现:
import {customRef} from "vue";
//自定义ref
function myRef(value){
return customRef((track,trigger)=>{
//发送请求
fetch(value).then(res=>{
return res.json();
}).then(res=>{
value = res;
trigger();
});
return {
get(){
track();//告诉Vue这个数据需要追踪变化的
console.log("get",value);
return value;
},
set(newValue){
console.log("set",newValue);
value = newValue;
trigger();
}
}
})
}
//组合APi入口
setup(){
const state = ref([]);
fetch('/data.json').then(res=>{
return res.json();
}).then(res=>{
state.value = res;
});
return {state}
}
注意点:不能将发送请求放入get方法中,因为在展示数据时会调用get,get执行后将更新界面这样会一直循环调用
数据改变后必须调用trigger触发响应
ref获取元素
在vue2.x中我们可以通过ref标记某个元素,然后通过this.$refs.xxx进行获取元素
在vue3.x中的setup中,无法使用this.$refs.xxx进行获取元素
在Vue3中声明周期被抽成一个方法,如果想要在setup中调用生命周期,直接导入方法,在声明周期回调中编写代码即可
在setup函数中无法通过this.$xxx获取vue内置方法
想要在setup中获取元素,需要将ref进行标记元素,然后在挂载生命周期函数中执行
<template>
<div ref="box"></div>
</template>
setup(){
const box = ref(null);
console.log(box.value);//打印null
//挂载生命周期
onMounted(()=>{
console.log(box.value);//打印<div></div>
});
return {box}
}
注意点:最后return出的变量名必须要与ref标记名称一致,否则无法获取到该元素
Readonly
Readonly:创建递归只读的数据,每一层都是只读的
shallowReadonly:只有第一层是只读的
isReadonly:是否是Readonly类型
const 和 Readonly的区别
-
const:赋值保护,不能对数据进行重新赋值
-
Readonly :属性保护,不能重新赋值