目录
vue3.0的六大亮点优化了diff算法标记的类型静态提升事件监听缓存vue3.0项目的创建Vue3.0是兼容vue2.x版本的组合逻辑API组合API的使用添加用户
setup的注意点reactive方法什么是ref函数ref 和 reactive 的区别递归监听toRaw函数markRawtoRef函数toRefs自定义一个ref?ref获取页面的元素
vue3.0的六大亮点
Performance:性能比vue2.x块1.2倍到2.2倍Tree shaking support:按需编译,体积比vue2.x还小Composition API:组合API(类似于React中的 Hook )Better TypeScript Support:更好的支持TsCustom Renderer API:暴露了自定义的渲染APIFragment,Teleport(Prptal),Suspense:更先进的组件
优化了diff算法
vue2.0对于虚拟DOM是进行全量比较的
所谓全量比较:就是对每一个标签进行前后比较 vue3.0新增了静态标记(PatchFlag)
会根据DOM节点是否发生变化,添加静态标记 -> {{ }} 类似这种,给添加标记!然后对这些静态标记进行比较 怎么添加静态标记?
标记的类型
text = 1 -> 动态文本class // 2 -> 动态classstyle // 4 动态styleprops // 8 动态属性,但不包含类名和样式FULL_PROPS // 16 具有动态的key属性
静态提升
vue2.x无论元素是否参与更新,每次都会重新创建,然后渲染vue3.0中对于不参与更新的元素,会做静态提升,之后被创建一次,在渲染的时候直接复用即可
<div
>
Hello World
!
<p
>{{ msg
}}</p
>
</div
>
export function render(_ctx
, _cache
, $props
, $setup
, $data
, $options
) {
return (_openBlock(), _createBlock("div", null, [
_createTextVNode(" Hello World! "),
_createVNode("p", null, _toDisplayString(_ctx
.msg
), 1 )
]))
}
const _hoisted_1
= _createTextVNode(" Hello World! ")
export function render(_ctx
, _cache
, $props
, $setup
, $data
, $options
) {
return (_openBlock(), _createBlock("div", null, [
_hoisted_1
,
_createVNode("p", null, _toDisplayString(_ctx
.msg
), 1 )
]))
}
事件监听缓存
默认情况下onClick会被视为动态绑定,所以每次都会缓存起来复用即可但是因为是同一个函数,所以没有追踪它的变化
<div
>
<button @click
="add">按钮
</button
>
</div
>
export function render(_ctx
, _cache
, $props
, $setup
, $data
, $options
) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("button", { onClick
: _ctx
.add
}, "按钮", 8 , ["onClick"])
]))
}
export function render(_ctx
, _cache
, $props
, $setup
, $data
, $options
) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("button", {
onClick
: _cache
[1] || (_cache
[1] = (...args
) => (_ctx
.add(...args
)))
}, "按钮")
]))
}
vue3.0项目的创建
Vite || Vue-Cli || Webpack
什么是Vite?
Vite是Vue作者开发的一款意图取代webpack的一款工具原理:使用ES6的import会发送请求去加载文件的特性拦截这些请求,做一些编译,省去冗长的webpack的打包时间
安装:
npm i -g create-vite-app
使用:
npm i 安装依赖npm run dev 启动项目
Vue3.0是兼容vue2.x版本的
依旧可以使用 data数据中的绑定方式
vue2.x版本的增删
<template>
<div>
<p>{{msg}}</p>
<input type="text" v-model="stuObj.id" />
<input type="text" v-model="stuObj.name" />
<input type="submit" value="提交" @click="submit" />
<ul>
<li v-for="item in stu" :key="item.id" @click="removeItem(item.id)">{{ item.name }}</li>
</ul>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
msg: "ppp",
stuObj: {
id: "",
name: "",
},
stu: [
{
id: 1,
name: "ppp",
},
{
id: 2,
name: "www",
},
{
id: 3,
name: "qqq",
},
],
};
},
methods: {
removeItem(id) {
this.stu = this.stu.filter((item) => item.id !== id);
},
submit() {
const stu = [...this.stu];
stu.push({
id: this.stuObj.id,
name: this.stuObj.name,
});
this.stu = stu;
this.stuObj.id = "";
this.stuObj.name = "";
},
},
};
</script>
组合逻辑API
setup() 是组合API的入口函数
这个入口函数内,定义的变量或者方法都需要向外暴露出去,使用return {}需要引入{ref} 对变量的定义初始值
<template>
<div>
<p>{{ count}}</p>
<button @click="add">+1按钮</button>
</div>
</template>
<script>
import {
ref
} from "vue";
export default {
name: "App",
setup() {
//定义一个变量 count 初始值为0
let count = ref(0);
function add() {
count.value++
}
return {
count,
add
}
}
};
</script>
ref函数的注意点
ref函数只能监听简单数据类型的变化,不能监听复杂数据类型的变化
reactive函数
用于监听复杂数据类型的变化
组合API的使用
<template>
<div>
<ul>
<li v-for="item in state.stu" :key="item.id" @click="removeItem(item.id)">{{ item.name }}</li>
</ul>
</div>
</template>
<script>
import {
reactive
} from "vue";
export default {
name: "App",
setup() {
let {
state,
removeItem
} = useRemove()
return {
state,
removeItem
}
},
};
//关于 用户删除的 数据 与 逻辑 组合api
//使用的时候 在setup之中直接结构出 这边方法的导入的数据
function useRemove() {
let state = reactive({
stu: [{
id: 1,
name: "ppp",
},
{
id: 2,
name: "www",
},
]
})
function removeItem(id) {
state.stu = state.stu.filter(item => item.id !== id)
}
return {
state,
removeItem
}
}
</script>
添加用户
注意点:就是关于 用户添加 用户删除的话 可以这这两个抽离出来 然后使用 import方式导入,使得每一个功能为一个单独的模块!
<template>
<div>
<input type="text" v-model="stuObj.id" />
<input type="text" v-model="stuObj.name" />
<input type="submit" value="提交" @click="addItem" />
<ul>
<li v-for="item in state.stu" :key="item.id" @click="removeItem(item.id)">{{ item.name }}</li>
</ul>
</div>
</template>
<script>
import {
reactive
} from "vue";
export default {
name: "App",
setup() {
let {
state,
removeItem
} = useRemove()
let {
stuObj,
addItem,
} = useAdd(state)
return {
state,
removeItem,
stuObj,
addItem
}
},
};
//关于 用户删除的 数据 与 逻辑 组合api
//使用的时候 在setup之中直接结构出 这边方法的导入的数据
function useRemove() {
let state = reactive({
stu: [{
id: 1,
name: "ppp",
},
{
id: 2,
name: "www",
},
]
})
function removeItem(id) {
state.stu = state.stu.filter(item => item.id !== id)
}
return {
state,
removeItem
}
}
//添加
function useAdd(state) {
let stuObj = reactive({
id: "",
name: ""
})
function addItem() {
state.stu.push({
id: this.stuObj.id,
name: this.stuObj.name
})
this.stuObj.id = "";
this.stuObj.name = ""
}
return {
stuObj,
addItem,
}
}
</script>
setup的注意点
就是setup() 函数是介于beforeCreate() 之前 此时还没有初始化好数据 无法拿到vue实例中的methods data但是vue为了避免我们错误使用,他直接把this修改为underfinesetup()只能是同步的,不能是异步的!
reactive方法
作用:实现复杂数据的响应式方法
而在vue2.x版本之中使用的是,Object.defineProperty()数据的劫持而vue3.0之中使用的proxy()来实现的 注意点:
reactive的参数必须为一个对象(json/arr)如果给reactive传递了其他对象
默认情况下修改对象,界面不会自动刷新如果想更新,可以通过重新赋值的方式
<template>
<div>
<p>{{state.count }}</p>
<button @click="add">+1按钮</button>
<p>{{ state.time }}</p>
<button @click="addTime">时间按钮</button>
</div>
</template>
<script>
import {
reactive
} from "vue";
export default {
name: "App",
setup() {
//创建一个响应式数据
//传入一个对象,这个对象转化为proxy对象
// let state = reactive(123)//由于传递是 非对象形式,无法作为响应式数据 因此 add() 触发的时候,不会修改视图
let state = reactive({
count: 1,
time: new Date()
})
function add() {
state.count++
}
function addTime() {
//传入了 其他对象 非 json arr 的 这需重新给这个数据赋值,否则不是响应式数据
let newTime = new Date();
newTime.setDate(state.time.getDate() + 1)
state.time = newTime
}
return {
state,
add,
addTime
}
}
}
</script>
什么是ref函数
作用:就是把简单数据类型转化为响应式数据本质:其实还是ref函数传递一个值之后,ref函数底层会自动将ref转化成reactive
ref(18) -> reacyive({ value:18 })因此在修改的时候,需要使用 xxx.value = XXX在template中使用的使用的时候,不需要使用.value 直接使用 xxx即可
<template>
<div>
<p>{{age }}</p>
<button @click="changeAge">按钮</button>
</div>
</template>
<script>
import {
ref
} from "vue";
export default {
name: "App",
setup() {
let age = ref(1)
function changeAge() {
age.value = 123
}
return {
age,
changeAge
}
}
}
</script>
ref 和 reactive 的区别
如果在template之中使用ref数据的时候,会自动帮我们添加.value
如果在template之中使用reactive数据时候,不会自动帮我们添加.value
Vue是如何决定是否添加.value的呢?
Vue在解析数据之前,会先判断这个数据是否属于ref类型,是的话就添加.value,不是的话就不添加
Vue怎么判断数据是ref类型?
是通过当前数据的__V_ref来判断的如果有这个私有属性,并且取值为true,那么判断为ref类型,就添加.value自我判断 -> isRef isReactive 可以判断类型
isRef(XXX)isReactive(XXX)
递归监听
1:递归监听
默认情况下,不管是ref还是reactive都是递归监听 2:递归监听存在的问题
如果数据量比较大,非常消耗性能
因为每一次都是包装为proxy对象的形式 递归监听
<template>
<div>
<p>{{state.gf.a }}</p>
<p>{{state.gf.f.b }}</p>
<p>{{state.gf.f.s.c }}</p>
<button @click="changeBtn">按钮</button>
</div>
</template>
<script>
import {
reactive
} from "vue";
export default {
name: "App",
setup() {
let state = reactive({
gf: {
a: "a",
f: {
b: "b",
s: {
c: "c"
}
}
}
})
function changeBtn() {
state.gf.a = "1"; // state.value.gf.a = "1";
state.gf.f.b = "2";
state.gf.f.s.c = "3";
}
return {
state,
changeBtn
}
}
}
</script>
3:非递归监听
只能监听第一层数据,不能监听内层的数据
<template>
<div>
<p>{{state.z }}</p>
<p>{{state.gf.a }}</p>
<p>{{state.gf.f.b }}</p>
<p>{{state.gf.f.s.c }}</p>
<button @click="changeBtn">按钮</button>
</div>
</template>
<script>
import {
// reactive
// shallowReactive
shallowRef,
triggerRef
} from "vue";
export default {
name: "App",
setup() {
// let state = reactive({
// let state = shallowReactive({
let state = shallowRef({
z: "z",
gf: {
a: "a",
f: {
b: "b",
s: {
c: "c"
}
}
}
})
function changeBtn() {
// state.z = 0 // shallowReactive 非递归方式 监听 只有最外层不修改,那么后面就算数据修改了,监听不了,更新不了视图
// state.gf.a = "1"; // 这是ref方式修改值 state.value.gf.a = "1";
// state.gf.f.b = "2";
// state.gf.f.s.c = "3";
// console.log(state.gf);
// shallowRef
//注意点:如果是通过shallowRef创建的数据
//那么Vue监听的是 .value的变化,并不是第一层的变化 因此不会修改数据
// state.value.z = 0
// state.value.gf.a = "1";
// state.value.gf.f.b = "2";
// state.value.gf.f.s.c = "3";
// 监听 .value数据的变化,因此可以修改state数据 更新视图
// state.value = {
// z: "0",
// gf: {
// a: "1",
// f: {
// b: "2",
// s: {
// c: "3"
// }
// }
// }
// }
//triggerRef() 主动修改 第几层的数据后 主动调用数据 修改数据并且更新视图
state.value.gf.f.s.c = "33";
triggerRef(state)
console.log(state);
console.log(state.value.gf);
}
return {
state,
changeBtn
}
}
}
</script>
toRaw函数
作用:拿到原始数据,对原始数据进行修改
这样的话就不会被追踪了,也不会更新UI界面,使得性能更好!
<template>
<div>
</div>
</template>
<script>
import {
ref,
toRaw,
reactive
} from "vue";
export default {
name: "App",
setup() {
let obj = {
name: "ppp",
age: 12
} //原始数据
let state = ref(obj) //响应式数据
let obj2 = toRaw(state) //获取到响应式数据的原始数据
console.log(obj === state);
console.log(obj); //原始数据 {name: "ppp", age: 12 }
console.log(state); //响应式数据 RefImpl {_rawValue: {…}, _shallow: false, __v_isRef: true, _value: Proxy}
// 注意点:就是使用 toRaw拿到 ref类型的原始数据(创建时传入的那个数据) 那么需要告诉toRaw这个要拿的值是 .value
console.log(obj2.value); //从响应式数据中拿到 原始数据 {name: "ppp", age: 12 }
return {
state,
}
}
}
</script>
markRaw
作用:就是实现数据不被追踪,数据修改后,但是界面不刷新!
<template>
<div>
<p>{{state.name }}</p>
<button @click="change">按钮</button>
</div>
</template>
<script>
import {
reactive,
markRaw
} from "vue";
export default {
name: "App",
setup() {
let obj = {
name: "ppp",
age: 12
} //原始数据
//设置 obj 这个数据 不被追踪
obj = markRaw(obj)
let state = reactive(obj)
function change() {
state.name = "lll"
console.log(state); //{name: "lll", age: 12, __v_skip: true} 但是由于设置数据不追踪,界面不刷新 还是之前的 ppp
}
return {
state,
change
}
}
}
</script>
toRef函数
作用:用来创建响应式数据的
<template>
<div>
<p>{{state }}</p>
<button @click="change">按钮</button>
</div>
</template>
<script>
import {
ref,
reactive,
toRef
} from "vue";
export default {
name: "App",
setup() {
let obj = {
name: "ppp",
age: 12
} //原始数据
//把name属性 设置为响应式数据
// let state = ref(obj.name)
//使用toRef 修改为响应式数据
let state = toRef(obj, 'name')
function change() {
//结论:如果使用ref将一个对象中的属性变成响应式的数据 我们修改响应式的数据是不会影响到原始数据的!
// state.value = "lll" //点击后 界面更新为 lll
// console.log(obj); //{name: "ppp", age: 12}
// console.log(state); //RefImpl {_rawValue: "lll", _shallow: false, __v_isRef: true, _value: "lll"}
//结论:使用toRef方式来设置响应式数据,那么我们修改的响应式数据是会影响原始数据的 但是不会更新UI
//应用场景:就是修改响应式数据之后,不让UI发送变化,那么就使用toRaw了
state.value = "lll" //点击后 界面更新为 lll
console.log(obj); //{name: "lll", age: 12}
console.log(state); //{ ... _object: {name: "lll", age: 12} }
}
return {
state,
change
}
}
}
</script>
toRefs
作用:把一个对象中的多个属性设置为响应式属性
<template>
<div>
<p>{{state }}</p>
<button @click="change">按钮</button>
</div>
</template>
<script>
import {
ref,
reactive,
toRefs
} from "vue";
export default {
name: "App",
setup() {
let obj = {
name: "ppp",
age: 12
} //原始数据
// 把obj中的设置为响应式属性
let state = toRefs(obj)
function change() {
state.value = "lll" //点击后 界面更新为 lll
state.age = 30
console.log(obj); //{name: "ppp", age: 12}
console.log(state); // {name: ObjectRefImpl, age: 30, value: "lll"}
}
return {
state,
change
}
}
}
</script>
自定义一个ref
<template>
<div>
<p>{{age }}</p>
<button @click="change">按钮</button>
</div>
</template>
<script>
import {
customRef,
ref,
} from "vue";
export default {
name: "App",
setup() {
function myRef(value) {
return customRef((track, trigger) => {
return {
get() {
track() // 告诉Vue数据是需要追踪的
return value
},
set(newValue) {
trigger() //告诉Vue触发界面更新
value = newValue
}
}
})
}
let age = myRef(18);
function change() {
age.value++
}
return {
age,
change
}
}
}
</script>
?
ref获取页面的元素
<template>
<div>
<div ref="box">我是</div>
</div>
</template>
<script>
import {
ref,
onMounted
} from "vue";
export default {
name: "App",
setup() {
let box = ref()
//需要在生命周期中的 onMounted 拿到
onMounted(() => {
console.log("onMounted", box.value); // onMounted <div ref="box">我是</div>
})
return {
box,
}
}
}
</script>