vue3.0学习笔记
vue3转vue2:
https://vue-next-template-explorer.netlify.app/
1. Vue3.0六大两点
- Performance:性能比Vue2.x快1.2~2倍
- Tree shaking support:按需编译,体积比Vue2.x更小
- Composition API:组合API(类似React Hooks)
- Better TypeScript support:更好的 Ts 支持
- Custom Renderer API:暴露了自定义渲染API
- Fragment,Teleport(Protal),Suspense:更先进的组件
2. Vue3.0 是如何变快的?
diff方法优化:http://vue-next-template-explorer.netlify.app/
Vue2 中的虚拟dom是进行全量的对比
Vue3 新增了静态标记(PatchFlag)
在与上次虚拟节点进行对比时候,只对比带有 patch flag 的节点
并且可以通过 flag 的信息得知当前节点要对比的具体内容
在创建虚拟dom的时候,会根据DOM中的内容会不会发生变化,添加静态标记
hoistStatic 静态提升
- Vue2中无论元素是否参与更新,每次都会重新创建,然后再渲染
- Vue3中对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用即可
cacheHandlers 事件侦听缓存
- 默认情况下 onClick 会被视为动态绑定,所以每次都会去追踪它的变化
- 但是因为是同一个函数,所以没有追踪变化,直接缓存起来复用即可
ssr渲染
- 当有大量静态的内容时候,这些内容会被当做纯字符串推进一个Buffer里面,即使存在动态的绑定,会通过模板插值嵌入进去。这样会比通过虚拟dom来渲染快上很多。
- 当静态内容达到一定量级时候,会使用_createStaticVNode方法在客户端dom来渲染一个static node,这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染。
什么是 Vite?
- Vite是Vue作者开发的一款意图取代Webpack的工具
- 其实现原理是利用 ES6 的 import 会发送请求去加载文件的特性,拦截这些请求,做一些预编译,省去webpack冗长的打包时间
3. Composition API
setup 函数是组合api的入口函数
setup() {
// 定义了一个count变量,初始值为0
// 该变量发生改变后,Vue会自动更新UI
let count = ref(0)
// 在组合api中,如果定义方法,不需要定义到methods,直接定义即可
function myFun() {
count.value += 1
}
// * 注意点:
// * 在组合api中定义的变量/方法,要想在外界使用,必须通过return { xxx, xxx } 暴露出去
return { count, myFun }
}
数据和业务逻辑一体化
<template>
<div>
<ul>
<li v-for="(stu, index) in state.stus" :key="stu.id" @click="removeStu(index)">
{{ stu.name }}: {{ stu.age }}
</li>
</ul>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
// ref 函数注意点:
// ref函数只能监听简单类型的变化,不能监听复杂类型的变化(对象,数组)
let { state, removeStu } = useRemoveStudent()
return { state, removeStu }
}
}
function useRemoveStudent() {
// 数据和业务逻辑一体化,解决了 Vue2.x 中数据和业务逻辑分离的问题
let state = reactive({
stus: [
{ id: 1, name: '张三', age: 10 },
{ id: 2, name: '李四', age: 20 },
{ id: 3, name: '王五', age: 30 }
]
})
function removeStu(index) {
state.stus = state.stus.filter((stu, idx) => idx !== index)
}
return { state, removeStu }
}
</script>
模块化(业务逻辑可以抽离成单独的模块)
<template>
<div>
<form action="">
<input type="text" v-model="state2.stu.id">
<input type="text" v-model="state2.stu.name">
<input type="text" v-model="state2.stu.age">
<input type="submit" @click="addStu">
</form>
<ul>
<li v-for="(stu, index) in state.stus" :key="stu.id" @click="removeStu(index)">
{{ stu.name }}: {{ stu.age }}
</li>
</ul>
</div>
</template>
<script>
import useRemoveStudent from './remove'
import useAddStudent from './add'
export default {
name: 'App',
setup() {
// ref 函数注意点:
// ref函数只能监听简单类型的变化,不能监听复杂类型的变化(对象,数组)
let { state, removeStu } = useRemoveStudent()
let { state2, addStu } = useAddStudent(state)
return { state, removeStu, state2, addStu }
}
}
</script>
import { reactive } from 'vue'
function useAddStudent(state) {
let state2 = reactive({
stu: {
id: '',
name: '',
age: ''
}
})
function addStu(e) {
e.preventDefault()
const stu = Object.assign({}, state2.stu)
state.stus.push(stu)
state2.stu.id = ''
state2.stu.name = ''
state2.stu.age = ''
}
return { state2, addStu }
}
export default useAddStudent
import { reactive } from 'vue'
function useRemoveStudent() {
/**
* 数据和业务逻辑一体化,解决了 Vue2.x 中数据和业务逻辑分离的问题
*/
let state = reactive({
stus: [
{ id: 1, name: '张三', age: 10 },
{ id: 2, name: '李四', age: 20 },
{ id: 3, name: '王五', age: 30 }
]
})
function removeStu(index) {
state.stus = state.stus.filter((stu, idx) => idx !== index)
}
return { state, removeStu }
}
export default useRemoveStudent
Composition API 和 Option API 的混合使用
Composition API本质 (组合API/注入API)【本例:setup函数中,将数据(age)注入data,将方法(myFun2)注入 methods】
<template>
<div>
<p>{{ name }}</p>
<button @click="myFun">按钮</button>
<p>{{ age }}</p>
<button @click="myFun2">按钮</button>
</div>
</template> <script>
import { ref } from 'vue'
export default {
name: 'App',
data () {
return {
name: 'wh'
}
},
methods: {
myFun() {
alert('123')
}
},
setup() {
let age = ref(18)
function myFun2() {
age.value += 1
}
return {
age,
myFun2
}
}
} </script>
setup执行时机 & setup注意点
setup执行时机:beforeCreate -> setup -> created
beforeCreate: 表示组件刚刚创建出来,组件的 data 和 methods 还没有初始化好
created: 表示组件刚刚被创建出来,组件的 data 和 methods 已经初始化好
官方文档原文:
Because setup is run around the beforeCreate and created lifecycle hooks, you do not need to explicitly define them.
In other words, any code that would be written inside those hooks should be written directly in the setup function.
意思是:
因为 setup 是围绕 beforeCreate 和 created 的生命周期钩子运行的,所以不需要显式地去定义 beforeCreate 和 created
换句话说,在 beforeCreate 和 created 钩子中编写的任何代码都应该直接写在 setup 函数中
setup注意点
- 由于在执行setup函数的时候,还没有执行 created 生命周期方法,所以在 setup 函数中,是无法使用 data 和 methods
- 由于不能再 setup 函数中使用 data 和 methods,所以 Vue 为了避免我们的错误使用,它直接将 setup 函数中的 this 修改成了 undefined
- setup 函数只能是同步的,不能是异步的
<template>
<div>
<p>{{ name }}</p>
<button @click="myFun">按钮</button>
<p>{{ age }}</p>
<button @click="myFun2">按钮</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'App',
data () {
return {
name: 'wh'
}
},
methods: {
myFun() {
alert('123')
}
},
// async setup() {} 错误使用
setup() {
let age = ref(18)
function myFun2() {
age.value += 1
}
console.log(this)
return {
age,
myFun2
}
}
}
</script>
reactive 函数
- 什么是 reactive?
- reactive 是 Vue3 中提供的实现响应式数据的方法
- 在 Vue2 中响应式数据是通过 defineProperty 来实现的,而在 Vue3 中响应式数据是通过 ES6 的 Proxy 来实现的
- reactive 注意点
- reactive 参数必须是对象 (json/array)
- 如果给 reactive 传递了其他对象
- 默认情况下修改对象,界面不会自动更新
- 如果想更新,可以通过重新赋值的方式
<template>
<div>
<!-- <p>{{ state.age }}</p> -->
<!-- <p>{{ state }}</p> -->
<p>{{ state.time }}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
// 创建一个响应式数据
// 本质:就是将一个传入的数据包装成一个 Proxy对象
// let state = reactive(123)
// let state = reactive({
// age: 123
// })
// let state = reactive([1, 3, 5])
let state = reactive({
time: new Date() // 给 reactive 传递了其他对象, 默认情况下修改对象,界面不会自动更新
})
function myFun() {
// state = 666 // 由于在创建响应式数据的时候传递的不是一个对象,所以无法实现响应式
// state.age = 666
// state[0] = 666
// 直接修改以前的,界面不会自动更新
// state.time.setDate(state.time.getDate() + 1)
// 重新赋值
const newTime = new Date(state.time.getTime())
newTime.setDate(state.time.getDate() + 1)
state.time = newTime
// console.log(state.age)
console.log(state)
}
return { state, myFun }
}
}
</script>
ref 函数
- 什么是 ref?
ref 和 reactive 一样,也是用来实现响应式数据的方法
由于 reactive 必须传递一个对象,所以导致在企业开发中,如果我们只想让某个变量实现响应式的时候会非常麻烦,
所以 Vue3 就给我们提供了 ref 方法,实现对简单值的监听
- ref 本质
- ref 底层的本质其实还是 reactive,系统会自动根据我们给 ref 传入的值将它转换成 ref(xx) -> reactive({value: xx})
- ref 注意点
- 在 Vue 中使用 ref 的值不用通过 value 获取
- 在 JS 中使用 ref 的值必须通过 value 获取
<template>
<div>
<!--
注意点:
如果是通过 ref 创建的数据,那么在 template 中使用的时候不用通过 .value 来获取
因为 Vue 会自动给我们添加 .value
-->
<p>{{ age }}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'App',
setup() {
let age = ref(18)
function myFun() {
// age = 666
age.value += 2
}
return {
age,
myFun
}
}
}
</script>
ref 和 reactive 的区别
- 如果在 template 里使用的是 ref 类型的数据,那么 Vue 会自动帮我们添加 .value
- 如果在 template 里使用的是 reactive 类型的数据,那么 Vue 不会自动帮我们添加 .value
- Vue 是如何决定是否需要添加 .value 的?
- Vue 在解析数据之前,会自动判断这个数据是否是 ref 类型的,如果是就自动添加 .value,如果不是就不自动添加 .value
- Vue 是如何判断当前数据是否是 ref 类型的?
- 通过当前数据的 v_isRef 来判断的
- 如果存在这个私有的属性,并且取值为 true,那么就代表是一个 ref 类型的数据
- 因为是私有属性,所以我们是访问不到 v_isRef 属性的值
- Vue 提供了 isRef 和 isReactive 方法来判断一个数据是 ref 类型还是 reactive 类型
<template>
<div>
<p>{{ age }}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
/**
*/
import { ref, reactive, isRef, isReactive } from 'vue'
export default {
name: 'App',
setup() {
let age = ref(18)
// let age = reactive({
// value: 18
// })
function myFun() {
// age = 666
/**
* ref -> {"_rawValue":18,"_shallow":false,"__v_isRef":true,"_value":18}
* reactive -> {"value":18}
*/
console.log(isReactive(age))
console.log(isRef(age))
age.value += 2
}
return {
age,
myFun
}
}
}
</script>
递归监听 & 非递归监听
- 递归监听
- 默认情况下,无论是通过 ref 还是 reactive 都是递归监听
- 每一层都包装成了一个 proxy 对象
- 递归监听存在的问题
- 如果数据量比较大,非常消耗性能
- 非递归监听
- shallowReactive
- 非递归监听下,第一层被包装成了 proxy
- 这意味着:只有第一层的数据发生改变,才会触发 UI界面 的更新
- shallowRef
- 如果是通过 shallowRef 创建数据,那么 Vue监听的是 .value 的变化,并不是第一层的变化
- 如果想在修改其内部数据后触发界面的更新,可以调用 triggerRef 方法
- shallowReactive
- 应用场景
- 一般情况下 使用 ref 和 reactive 即可
- 只有在需要监听的数据量比较大的时候,我们才使用 shallowRef / shallowReactive
reactive
<template>
<div>
<p>{{ state.a }}</p>
<p>{{ state.gf.b }}</p>
<p>{{ state.gf.f.c }}</p>
<p>{{ state.gf.f.s.d }}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
import { ref, reactive, isRef, isReactive } from 'vue'
export default {
name: 'App',
setup() {
let state = reactive({
a: 'a',
gf: {
b: 'b',
f: {
c: 'c',
s: {
d: 'd'
}
}
}
})
function myFun() {
state.a = 1
state.gf.b = 2
state.gf.f.c = 3
state.gf.s.f.d = 4
}
return {
state,
myFun
}
}
}
</script>
<template>
<div>
<p>{{ state.a }}</p>
<p>{{ state.gf.b }}</p>
<p>{{ state.gf.f.c }}</p>
<p>{{ state.gf.f.s.d }}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
import { reactive, shallowReactive } from 'vue'
export default {
name: 'App',
setup() {
let state = shallowReactive({
a: 'a',
gf: {
b: 'b',
f: {
c: 'c',
s: {
d: 'd'
}
}
}
})
function myFun() {
/**
* 非递归监听下,该层被包装成了 proxy
* 这意味着:只有该层的数据发生改变,才会触发 UI 的更新
* 即:如果 state.a = 1 不执行,那么 界面是不会发生改变的
*/
// state.a = 1
state.gf.b = 2
state.gf.f.c = 3
state.gf.f.s.d = 4
console.log(state) // 只有这一层包装成了 proxy
console.log(state.gf)
console.log(state.gf.f)
console.log(state.gf.f.s)
}
return {
state,
myFun
}
}
}
</script>
ref
<template>
<div>
<p>{{ state.a }}</p>
<p>{{ state.gf.b }}</p>
<p>{{ state.gf.f.c }}</p>
<p>{{ state.gf.f.s.d }}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
import { ref, reactive, isRef, isReactive } from 'vue'
export default {
name: 'App',
setup() {
let state = ref({
a: 'a',
gf: {
b: 'b',
f: {
c: 'c',
s: {
d: 'd'
}
}
}
})
function myFun() {
state.value.a = 1
state.value.gf.b = 2
state.value.gf.f.c = 3
state.value.gf.f.s.d = 4
}
return {
state,
myFun
}
}
}
</script>
<template>
<div>
<p>{{ state.a }}</p>
<p>{{ state.gf.b }}</p>
<p>{{ state.gf.f.c }}</p>
<p>{{ state.gf.f.s.d }}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
import { ref, shallowRef, triggerRef } from 'vue'
export default {
name: 'App',
setup() {
let state = shallowRef({
a: 'a',
gf: {
b: 'b',
f: {
c: 'c',
s: {
d: 'd'
}
}
}
})
function myFun() {
// state.value.a = 1
// state.value.gf.b = 2
// state.value.gf.f.c = 3
state.value.gf.f.s.d = 4 // 如果想在修改该数据时,触发界面的更新,可以调用 triggerRef 方法主动触发
/**
* 注意点:
* + Vue3 只提供了 triggerRef 方法,没有提供 triggerReactive 方法
* + 所以如果是 reactive 类型的数据,那么是无法主动触发界面更新的
*/
triggerRef(state)
/**
* 注意点:
* + 如果是通过 shallowRef 创建数据,那么 Vue监听的是 .value 的变化,并不是第一层的变化
*/
// state.value = {
// a: '1',
// gf: {
// b: '2',
// f: {
// c: '3',
// s: {
// d: '4'
// }
// }
// }
// }
console.log(state)
console.log(state.value.gf)
console.log(state.value.gf.f)
console.log(state.value.gf.f.s)
}
return {
state,
myFun
}
}
}
</script>
shallowRef & shallowReative
- shallowRef 本质上还是 shallowReative
- 所以如果是通过 shallowRef 创建的数据,它监听的是 .value 的变化
<template>
<div>
<p>{{ state.a }}</p>
<p>{{ state.gf.b }}</p>
<p>{{ state.gf.f.c }}</p>
<p>{{ state.gf.f.s.d }}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
import { ref, shallowRef, triggerRef } from 'vue'
export default {
name: 'App',
setup() {
// shallowRef 本质上还是 shallowReative
// shallowRef(10) -> shallowReactive({ value: 10 })
// 所以如果是通过 shallowRef 创建的数据,它监听的是 .value 的变化
let state = shallowRef({
a: 'a',
gf: {
b: 'b',
f: {
c: 'c',
s: {
d: 'd'
}
}
}
})
function myFun() {
// state.value.a = 1
// state.value.gf.b = 2
// state.value.gf.f.c = 3
state.value.gf.f.s.d = 4 // 如果想在修改该数据时,触发界面的更新,可以调用 triggerRef 方法主动触发
/**
* 注意点:
* + Vue3 只提供了 triggerRef 方法,没有提供 triggerReactive 方法
* + 所以如果是 reactive 类型的数据,那么是无法主动触发界面更新的
*/
triggerRef(state)
/**
* 注意点:
* + 如果是通过 shallowRef 创建数据,那么 Vue监听的是 .value 的变化,并不是第一层的变化
*/
// state.value = {
// a: '1',
// gf: {
// b: '2',
// f: {
// c: '3',
// s: {
// d: '4'
// }
// }
// }
// }
console.log(state)
console.log(state.value.gf)
console.log(state.value.gf.f)
console.log(state.value.gf.f.s)
}
return {
state,
myFun
}
}
}
</script>
toRaw & markRaw 函数
- ref/reactive 数据类型的特点:
- 每次修改都会被追踪,都会更新 UI 界面,但是这样是非常消耗性能的
- 所以如果我们有一些操作不需要追踪,不需要更新 UI 界面,那么这个时候,
我们就可以通过 toRaw 方法拿到它的原始数据,对数据进行修改,这样就不会被追踪,也就不会去更新 UI 界面,性能就会有所提升
<template>
<div>
<p>{{ state }}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
/**
*/
import { reactive, toRaw } from 'vue'
export default {
name: 'App',
setup() {
/**
* ref/reactive 数据类型的特点:
* + 每次修改都会被追踪,都会更新 UI 界面,但是这样是非常消耗性能的
* + 所以如果我们有一些操作不需要追踪,不需要更新 UI 界面,那么这个时候,
* 我们就可以通过 toRaw 方法拿到它的原始数据,对数据进行修改,这样就不会被追踪,
* 也就不会去更新 UI 界面,性能就会有所提升
*/
let obj = { name: 'wh', age: 18 }
let state = reactive(obj)
let obj2 = toRaw(state)
console.log(obj2)
// state 和 obj 的关系:
// + 引用关系,state的本质是 proxy 对象,在这个 proxy 对象中引用了 obj
function myFun() {
// state.name = 'zs'
// 如果直接修改 obj,那么是无法触发界面更新的
// 只有通过包装之后的对象来修改,才会出发界面的更新
obj2.name = 'zs'
console.log(obj2) // {name: "zs", age: 18}
console.log(state) // Proxy {name: "zs", age: 18}
}
return {
state,
myFun
}
}
}
</script>
<template>
<div>
<p>{{ state }}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
/**
*/
import { ref, toRaw } from 'vue'
export default {
name: 'App',
setup() {
/**
* 1. ref本质: reactive
* ref(obj) -> reactive({value: obj})
*
*/
let obj = { name: 'wh', age: 18 }
let state = ref(obj)
// 注意点:如果想通过 toRaw 拿到 ref 类型的原始数据(创建时传入的那个数据)
// 那么必须明确的告诉 toRaw 方法,要获取的是 .value
// 因为经过 Vue 处理之后,.value 中保存的才是当初创建的时候传入的那个原始数据
let obj2 = toRaw(state.value)
console.log(obj2)
// state 和 obj 的关系:
// + 引用关系,state的本质是 proxy 对象,在这个 proxy 对象中引用了 obj
function myFun() {
// state.name = 'zs'
// 如果直接修改 obj,那么是无法触发界面更新的
// 只有通过包装之后的对象来修改,才会出发界面的更新
obj2.name = 'zs'
console.log(obj2) // {name: "zs", age: 18}
console.log(state) // Proxy {name: "zs", age: 18}
}
return {
state,
myFun
}
}
}
</script>
<template>
<div>
<p>{{ state }}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
import { reactive, markRaw } from 'vue'
export default {
name: 'App',
setup() {
let obj = { name: 'wh', age: 18 }
obj = markRaw(obj) // 限制该数据永远无法被追踪
let state = reactive(obj)
function myFun() {
state.name = 'zs'
}
return {
state,
myFun
}
}
}
</script>
toRef & toRefs 函数
ref->复制:
- 修改响应式数据不会影响以前的数据
- 数据发生改变,界面就会自动更新
toRef->引用:
修改响应式数据会影响以前的数据
数据发生改变,界面也不会自动更新
toRef 应用场景:
- 如果想让响应式数据和以前的数据关联起来,并且更新响应式数据之后还不想更新 UI,那么就可以使用 toRef
<template>
<div>
<p>{{ state }}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
/**
*
*/
import { ref, toRef } from 'vue'
export default {
name: 'App',
setup() {
let obj = { name: 'wh' }
// ref:复制
// let state = ref(obj.name)
// toRef:引用
/**
* ref 和 toRef 区别:
* + ref->复制:
* - 修改响应式数据不会影响以前的数据
* - 数据发生改变,界面就会自动更新
* + toRef->引用:
* - 修改响应式数据会影响以前的数据
* - 数据发生改变,界面也不会自动更新
*
* toRef 应用场景:
* + 如果想让响应式数据和以前的数据关联起来,并且更新响应式数据之后还不想更新 UI,那么就可以使用 toRef
*/
let state = toRef(obj, 'name')
console.log(state)
function myFun() {
state.value = "zs"
/**
* 结论:如果利用 ref 将某一个对象的属性变为响应式的数据
* 那么修改响应式数据是不会影响到原始数据的
*/
/**
* 结论:如果利用 toRef 将某一个对象中的属性变成响应式的数据
* 那么修改响应式数据是会影响到原始数据的
* 但是如果响应式的数据通过 toRef 创建,那么修改了数据并不会触发UI界面的更新
*/
console.log(obj) // wh
console.log(state) // zs
}
return {
state,
myFun
}
}
}
</script>
<template>
<div>
<!-- <p>{{ name }}</p>-->
<!-- <p>{{ age }}</p>-->
<p>{{state}}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
/**
*
*/
import { ref, toRef, toRefs } from 'vue'
export default {
name: 'App',
setup() {
let obj = { name: 'wh', age: 18 }
// ref:复制
// let state = ref(obj.name)
// toRef:引用
/**
* ref 和 toRef 区别:
* + ref->复制:
* - 修改响应式数据不会影响以前的数据
* - 数据发生改变,界面就会自动更新
* + toRef->引用:
* - 修改响应式数据会影响以前的数据
* - 数据发生改变,界面也不会自动更新
*
* toRef 应用场景:
* + 如果想让响应式数据和以前的数据关联起来,并且更新响应式数据之后还不想更新 UI,那么就可以使用 toRef
*/
// let name = toRef(obj, 'name')
// let age = toRef(obj, 'age')
let state = toRefs(obj)
console.log(state)
function myFun() {
// name.value = "zs"
// age.value = "666"
state.name.value = 'zs'
state.age.value = 666
/**
* 结论:如果利用 ref 将某一个对象的属性变为响应式的数据
* 那么修改响应式数据是不会影响到原始数据的
*/
/**
* 结论:如果利用 toRef 将某一个对象中的属性变成响应式的数据
* 那么修改响应式数据是会影响到原始数据的
* 但是如果响应式的数据通过 toRef 创建,那么修改了数据并不会触发UI界面的更新
*/
console.log('obj', obj) // obj {name: "zs", age: 666}
console.log('state', state) // state {name: "zs", age: 666}
}
return {
// name,
// age,
state,
myFun
}
}
}
</script>
customRef 函数
- 返回一个 ref 对象,可以显式的控制依赖追踪和触发响应
- 异步处理服务器数据的时候可能会用到
- 需要注意的是:不能在 get 方法中发送网络请求,因为这会导致不断发送请求,不断更新界面的无限循环
<template>
<div>
<p>{{age}}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
/**
* 1. custemRef
* + 返回一个 ref 对象,可以显式的控制依赖追踪和触发响应
*/
import { ref, customRef } from 'vue'
/**
* 自定义 Ref
* @param value
* @returns {Ref<*>}
*/
function myRef(value) {
return customRef((track, trigger) => {
// track -> 追踪 trigger -> 触发
return {
get() {
track() // 告诉 Vue 这个数据是需要追踪变化的
console.log('get', value)
return value
},
set(newValue) {
console.log('set', newValue)
value = newValue
trigger() // 告诉 Vue 触发界面更新
}
}
})
}
export default {
name: 'App',
setup() {
// let age = ref(18) // reactive({value: 18})
let age = myRef(18)
function myFun() {
age.value += 1
}
return { age, myFun }
}
}
</script>
/**
* data.json
*/
[
{"id": 1, "name": "鲁班"},
{"id": 2, "name": "虞姬"},
{"id": 3, "name": "黄忠"}
]
<template>
<div>
<ul>
<li v-for="item in state" :key="item.id"> {{ item.name }} </li>
</ul>
</div>
</template>
<script>
/**
* 1. custemRef
* + 返回一个 ref 对象,可以显式的控制依赖追踪和触发响应
*/
import { ref, customRef } from 'vue'
/**
* 自定义 Ref
* @param value
* @returns {Ref<*>}
*/
function myRef(value) {
return customRef((track, trigger) => {
// track -> 追踪 trigger -> 触发
fetch(value)
.then(res => res.json())
.then(data=>{
console.log(data)
value = data
trigger()
})
.catch(err => {
console.log(err);
})
return {
get() {
track() // 告诉 Vue 这个数据是需要追踪变化的
console.log('get', value)
/**
* 注意点:
* + 不能在 get 方法中发送网络请求
* + 渲染界面 -> 调用 get -> 发送网络请求
* + 保存数据 -> 更新界面 -> 调用 get
*/
return value
},
set(newValue) {
console.log('set', newValue)
value = newValue
trigger() // 告诉 Vue 触发界面更新
}
}
})
}
export default {
name: 'App',
// setup函数:只能是一个同步的函数,不能是一个异步的函数
// async setup() {
// const data = await fetch('../public/data.json')
setup() {
/*
let state = ref([])
fetch('../public/data.json')
.then(res => res.json())
.then(data=>{
console.log(data)
state.value = data
})
.catch(err => {
console.log(err);
})
*/
let state = myRef('../public/data.json')
return { state }
}
}
</script>
生命周期
<template>
<div ref="box">我是div</div>
</template>
<script>
/**
* 1. 获取元素
* + 在 Vue2.x 中我们可以通过给元素添加 ref='xxx',
* 然后在代码中通过 ref.xxx 的方式来获取元素
* 在 Vue3.x 中我们也可以通过 ref 来获取元素
*
*/
import { ref, onMounted } from 'vue'
export default {
name: 'App',
setup() {
// console.log(this.$refs.box) // vue3 中不能这样使用
let box = ref(null)
onMounted(()=>{
console.log('onMounted', box.value) // onMounted <div>我是div</div>
})
console.log(box.value) // null
return { box }
}
}
</script>
readonly 函数
- readonly:用于创建一个只读的数据,并且是递归只读
- shallowReadonly: 用于创建一个只读数据,但是不是递归只读的
- isReadonly:对于 readonly 和 shallowReadonly 创建的数据,返回结果均为 true
- const 和 readonly 的区别:
- const: 赋值保护,不能给变量重新赋值
- readonly:属性保护,不能给属性重新赋值
<template>
<div>
<p>{{ state.name }}</p>
<p>{{ state.attr.age }}</p>
<p>{{ state.attr.height }}</p>
<button @click="myFun()">按钮</button>
</div>
</template>
<script>
/**
* 1. readonly
* +
*
*/
import { readonly, shallowReadonly, isReadonly } from 'vue'
export default {
name: 'App',
setup() {
// readonly:用于创建一个只读的数据,并且是递归只读
// let state = readonly({name: 'wh', attr: {age: 18, height: 1.88}})
// shallowReadonly:用于创建一个只读数据,但是不是递归只读的
let state = shallowReadonly({name: 'wh', attr: {age: 18, height: 1.88}})
/** const 和 readonly 的区别:
* + const: 赋值保护,不能给变量重新赋值
* + readonly:属性保护,不能给属性重新赋值
*/
// const value = 123
const value = { name: 'zs', age: 123 }
function myFun() {
state.name = 'zs'
state.attr.age = 666
state.attr.height = 1.222
value.name = 'ls'
value.age = '456' // { name: 'ls', age: 456 }
console.log(state)
console.log(isReadonly(state)) // true
console.log(value)
}
return { state, myFun }
}
}
</script>
Proxy 实现数据绑定
对象
/*
1. Vue3 响应式数据的本质
+ 在 Vue2.x 中是通过 defineProperty 来实现响应式数据的
详见:手写 vue 全家桶视频
+ 在 Vue3.x 中通过 Proxy 来实现响应式数据的
*/
let obj = { name: 'wh', age: 18 }
let state = new Proxy(obj, {
get(obj, key) {
console.log(obj, key) // { name: 'wh', age: 18 } name
return obj[key]
},
set(obj, key, value) {
console.log(obj, key, value) // { name: 'wh', age: 18 } name zs
obj[key] = value
console.log('更新UI界面')
}
})
// console.log(state.name) // wh
state.name = "zs"
console.log(state) // { name: 'zs', age: 18 }
数组
/*
1. Vue3 响应式数据的本质
+ 在 Vue2.x 中是通过 defineProperty 来实现响应式数据的
详见:手写 vue 全家桶视频
+ 在 Vue3.x 中通过 Proxy 来实现响应式数据的
*/
let arr = [ 1, 3, 5 ]
let state = new Proxy(arr, {
get(obj, key) {
console.log(obj, key) // [ 1, 3, 5 ] 1
return obj[key]
},
set(obj, key, value) {
// 第1次 [ 1, 3, 5 ] 3 7
// 第2次 [ 1, 3, 5, 7 ] length 4
console.log(obj, key, value)
obj[key] = value
console.log('更新UI界面')
return true // 必须要返回 true 才会继续更新 length
}
})
// console.log(state[1]) // 3
state.push(7) // 先追加 7 ,再更新 length
手写 shallowReactive, shallowRef
/*
1. shallowReactive, shallowRef
2. shallowReadonly
3. reactive, ref
4. readonly
*/
function shallowRef(val) {
return shallowReactive({ value: val })
}
function shallowReactive(obj) {
return new Proxy(obj, {
get(obj, key) {
return obj[key]
},
set(obj, key, value) {
obj[key] = value
console.log('更新UI界面')
return true
}
})
}
let obj = {
a: 'a',
gf: {
b: 'b',
f: {
c: 'c',
s: {
d: 'd'
}
}
}
}
/*
let state = shallowReactive(obj)
// state.a = 1 // 更新UI界面
state.gf.b = 2
state.gf.f.c = 3
state.gf.f.s.d = 4
*/
let state = shallowRef(obj)
// state.value.a = 1
// state.value.gf.b = 2
// state.value.gf.f.c = 3
// state.value.gf.f.s.d = 4
state.value = {
a: '1',
gf: {
b: '2',
f: {
c: '3',
s: {
d: '4'
}
}
}
}
手写 reactive, ref
/*
1. shallowReactive, shallowRef
2. shallowReadonly
3. reactive, ref
4. readonly
*/
// let arr = [{id: 1, name: '鲁班'}, {id: 2, name: '虞姬'}]
// let obj = {a:{id: 1, name: '鲁班'}, b:{id: 2, name: '虞姬'}}
function ref(val) {
return reactive({value: val})
}
function reactive(obj) {
if(typeof obj === 'object') {
if (obj instanceof Array) {
// 如果是一个数据,那么取出数组中的每一个元素
// 判断每一个元素是否又是一个对象,如果又是一个对象,那么也需要包装成 Proxy
obj.forEach((item, index) => {
if(typeof item === 'object') {
obj[index] = reactive(item)
}
})
} else {
// 如果是一个对象,那么去除对象属性的取值
// 判断对象属性的取值是否又是一个对象,如果又是一个对象,那么也需要包装成 Proxy
for (let key in obj) {
let item = obj[key]
if (typeof item === 'object') {
obj[key] = reactive(item)
}
}
}
} else {
console.warn(`${JSON.stringify(obj)} is not object`)
}
return new Proxy(obj, {
get(obj, key) {
return obj[key]
},
set(obj, key, value) {
obj[key] = value
console.log('更新UI界面')
return true
}
})
}
let obj = {
a: 'a',
gf: {
b: 'b',
f: {
c: 'c',
s: {
d: 'd'
}
}
}
}
// let state = reactive(obj)
// state.a = 1 // 更新UI界面
// state.gf.b = 2 // 更新UI界面
// state.gf.f.c = 3 // 更新UI界面
// state.gf.f.s.d = 4 // 更新UI界面
let arr = [{id: 1, name: '鲁班'}, {id: 2, name: '虞姬'}]
let state = reactive(arr)
state[0].name = '张三' // 更新UI界面
state[0].age = 666 // 更新UI界面
state[1].id = 3 // 更新UI界面
手写 readonly, shallowReadonly
/*
1. shallowReactive, shallowRef
2. shallowReadonly
3. reactive, ref
4. readonly
*/
function readonly(obj) {
if(typeof obj === 'object') {
if (obj instanceof Array) {
// 如果是一个数据,那么取出数组中的每一个元素
// 判断每一个元素是否又是一个对象,如果又是一个对象,那么也需要包装成 Proxy
obj.forEach((item, index) => {
if(typeof item === 'object') {
obj[index] = rereadonlyactive(item)
}
})
} else {
// 如果是一个对象,那么去除对象属性的取值
// 判断对象属性的取值是否又是一个对象,如果又是一个对象,那么也需要包装成 Proxy
for (let key in obj) {
let item = obj[key]
if (typeof item === 'object') {
obj[key] = readonly(item)
}
}
}
} else {
console.warn(`${JSON.stringify(obj)} is not object`)
}
return new Proxy(obj, {
get(obj, key) {
return obj[key]
},
set(obj, key, value) {
console.warn(`${key}是只读的,不能赋值`)
return true
}
})
}
function shallowReadonly(obj) {
return new Proxy(obj, {
get(obj, key) {
return obj[key]
},
set(obj, key, value) {
console.warn(`${key}是只读的,不能赋值`)
}
})
}
let obj = {
a: 'a',
gf: {
b: 'b',
f: {
c: 'c',
s: {
d: 'd'
}
}
}
}
// let state = shallowReadonly(obj)
// state.a = 1 // a是只读的,不能赋值
// state.gf.b = 2 // 空行
let state1 = readonly(obj)
state1.a = 1 // a是只读的,不能赋值
state1.gf.b = 2 // b是只读的,不能赋值
vue3.0学习笔记的更多相关文章
- vue3.0学习笔记(二)
一.选择合适的ide 推荐使用vs code编辑器,界面清晰.使用方便,控制台功能很好用.webstorm也可以,看个人喜好. 二.ui框架选择 目前,pc端一般是选择element ui(饿了么), ...
- vue3.0学习笔记(一)
一.搭建工作环境环境 1.从node.js官网下载相应版本进行安装即可 https://nodejs.org/zh-cn/download/,安装完成后在命令行输入 node -v 如果可以查询到版 ...
- DirectX 总结和DirectX 9.0 学习笔记
转自:http://www.cnblogs.com/graphics/archive/2009/11/25/1583682.html DirectX 总结 DDS DirectXDraw Surfac ...
- 一起学ASP.NET Core 2.0学习笔记(二): ef core2.0 及mysql provider 、Fluent API相关配置及迁移
不得不说微软的技术迭代还是很快的,上了微软的船就得跟着她走下去,前文一起学ASP.NET Core 2.0学习笔记(一): CentOS下 .net core2 sdk nginx.superviso ...
- vue2.0学习笔记之路由(二)路由嵌套+动画
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- vue2.0学习笔记之路由(二)路由嵌套
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- hdcms v5.7.0学习笔记
hdcms v5.7.0学习笔记 https://note.youdao.com/ynoteshare1/index.html?id=c404d63ac910eb15a440452f73d6a6db& ...
- dhtmlxgrid v3.0学习笔记
dhtmlxgrid v3.0学习笔记 分类: dhtmlx JavaScript2012-01-31 15:41 1744人阅读 评论(0) 收藏 举报 stylesheetdatecalendar ...
- OAuth 2.0学习笔记
文章目录 OAuth的作用就是让"客户端"安全可控地获取"用户"的授权,与"服务商提供商"进行互动. OAuth在"客户端&quo ...
- 一起学ASP.NET Core 2.0学习笔记(一): CentOS下 .net core2 sdk nginx、supervisor、mysql环境搭建
作为.neter,看到.net core 2.0的正式发布,心里是有点小激动的,迫不及待的体验了一把,发现速度确实是快了很多,其中也遇到一些小问题,所以整理了一些学习笔记: 阅读目录 环境说明 安装C ...
随机推荐
- TCP/IP RTT算法比较
TCP重传机制Timeout特点: 设长了,重发就慢,效率和性能差: 设短了,重发就快,可能导致没有丢就重发,增加网络拥塞,导致更多的超时,更多的超时导致更多的重发. TCP协议引入2个概念: RTT ...
- Python+chatGPT编程5分钟快速上手,强烈推荐!!!
最近一段时间chatGPT火爆出圈!无论是在互联网行业,还是其他各行业都赚足了话题. 俗话说:"外行看笑话,内行看门道",今天从chatGPT个人体验感受以及如何用的角度来分享一下 ...
- Mybatis连接数据库
从零开始Mybatis连接数据库 创建Maven文件 File-->new-->project-->maven,点击next 配置 在出现的pom.xml文件中<project ...
- Android:DrawerLayout 抽屉布局没有反应
<androidx.drawerlayout.widget.DrawerLayout android:id="@+id/drawer_layout" android:layo ...
- GPIO 和轮询控制 LED 的状态
GPIO 概念 I/O 是输入(Input)和输出(Output)的意思,GPIO(General Purpose I/O)是基本输入输出,是 I/O 的最基本形式.STM32F103ZET6 大概有 ...
- Golang如何快速构建一个CLI小工示例
这篇文章主要为大家介绍了Golang如何快速构建一个CLI小工具详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪 如何Golang快速构建一个CLI小工具 在现实开发的 ...
- [NPUCTF2020]EzRSA
[NPUCTF2020]EzRSA 题目: from gmpy2 import lcm , powmod , invert , gcd , mpz from Crypto.Util.number im ...
- 加载properties文件
import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java ...
- 判断js对象每个字段是否为空
for(var key in obj) { if (!obj[key])return; }
- golang 中使用mysql报错:“ scannable dest type slice with >1 columns (4) in result”
query := fmt.Sprintf("SELECT .... infos = make([]*struct, 0, 10) err = s.db.GetContext(ctx, &am ...