Comments (9)
什么时候用setup()函数方式,什么时候不用?
官网推荐,对于结合单文件组件使用的组合式 API,推荐通过 <script setup>
- setup直接以属性的方式写在script标签上
- 在Vue3中,在xxx.vue单文件组件中的
<script setup></script>
,会有一个setup标记, - 只要标记了它,那就说明,在script标签代码块内就具备了vue3的语法环境,
- 可以使用Vue3中的一些特性,比如组合式API函数,ref,reactive等
- 可以在script中声明响应式数据状态,定义声明完后,可以直接在模板中使用
<template>
<!--在模板中不用.value,它会被自动解析,直接可以使用变量名--->
{{name}}
</template>
<script setup>
import {ref} from "vue";
let name = ref("itclanCoder");
//在逻辑中,想要读取name的值,需要.value方式
console.log(name.value);
</script>
setup()以函数配置选项方式
在vue3当中,它是向下兼容的,如果想要在vue2中体验vue3当中的一些新特性,
在vue3中新增了一setup的配置选项 其他情况下,都应该优先使用<script setup></script>语法
<template>
<div>
{{name}}
</div>
</template>
<script>
import { ref } from "vue";
export default {
// 其他配置选项
data() {
return {
num: 1
}
},
setup() {
const name = ref('itclanCoder');
// 在逻辑中访问,同样需要使用.value的方式访问
console.log(name.value);
//在vue3的配置里去读取vue2的属性
console.log(this.num);
// 声明的变量或函数,都需要暴露出去,否则在模板中使用,会不起作用
return {
name
}
}
}
</script>
重点注意
[1]. 在setup()函数内定义声明的响应式数据变量或函数,都需要对外暴露出去,否则在模板中直接使用的话,是不起作用的
[2]. 在模板中可以直接使用定义的响应式数据变量,或函数,因为它可以自动解析,而在逻辑中需要使用变量.value读取
[3]. setup()函数自身并不具备对组件实例的访问权限,所以在setup()函数作用域内,访问this会是undefined,但在选项式API中可以访问组合式API对外暴露出来的值,反过来却不行,也就是在setup()中无法通过this访问选项式API配置选项下的data数据或methods方法
[4]. 当选项式API中的data数据,methods方法名与setup()内定义的变量名或函数同名时,后者会覆盖前者
[5]. 凡是组合中用到数据,方法,均要配置在setup中,并且需要通过return对外暴露出去
[6]. setup函数有两种返回值,一个是对象,另一个是函数
[7]. 如果setup()函数返回一个对象,则对象中的属性,方法,在模板中可以直接使用,但在逻辑内,普通变量需要使用.value访问读取,若返回一个渲染函数,则可以自定义渲染内容
[8]. setup不能是一个async函数,因为返回值不在是return的对象,而是propmise,这样会令,模板看不到return对象中属性(其实也可以返回一个Promises实例,但需要结合Suspense和异步组件配合进行使用)
setup()是在什么时候执行的?
setup()是Vue提供的一个钩子,它的执行时机是在beforeCreate()函数之前执行的,在setup()函数里面访问this是undefined
总结
setup是vue3新增的一个特性,有两种使用,一种是直接写在script标签上,此时script标签代码块内可写vue3的新特性
而在选项式API编码风格当中,若想要使用vue3,那么需要借用setup()这配置选项,所有的组合响应式API函数,响应式变量,
都需要放在setup()函数里面同时,需要对外暴露出去
两种方式都是可以使用的,不过在官方推荐的使用当中,应当优先使用setup放在script标签上,
当需要基于选项式API的组合中,基于组合式API的代码时或第三方库整合的项目中,
或非单文件组件中使用组合式API时,可以用setup()函数。
from dailyr-ecord.
ref和reactive异同
- ref接收内部值返回响应式Ref对象,reactive返回响应式代理对象
- reactive内部采用的proxy,ref内部采用的是defineProperty
- 从定义上看ref通常用于处理单值的响应式,reactive用于处理对象类型的数据响应式
- 两者均是用于构造响应式数据,但是ref主要解决原始值的响应式问题
- ref返回的响应式数据在JS中使用需要加上.value才能访问其值,在视图中使用会自动脱ref,不需要.value;ref可以接收对象或数组等非原始值,但内部依然是reactive实现响应式;reactive内部如果接收Ref对象会自动脱ref;使用展开运算符(...)展开reactive返回的响应式对象会使其失去响应性,可以结合toRefs()将值转换为Ref对象之后再展开。
- reactive内部使用Proxy代理传入对象并拦截该对象各种操作(trap),从而实现响应式。ref内部封装一个RefImpl类,并设置get value/set value,拦截用户对值的访问,从而实现响应式。
ref
<template>
<!--vue3的组件模版结构可以没有根标签-->
<h1>我叫{{ name }}, {{ age }}岁</h1>
<h3>职位:{{ job.type }}</h3>
<h3>薪水:{{ job.salary }}</h3>
<button @click="changeInfo">修改人的信息</button>
</template>
<script setup lang="ts">
import { ref } from 'vue';
//ref实现响应式(基本类型)也是采用Object.definedProperty()来实现的 getter和setter
let name = ref('小明');
let age = ref(21);
//ref实现响应式(对象类型)也是采用Proxy来实现
let job = ref({
type: '前端',
salary: 30000
});
function changeInfo() {
name.value = '李四';
age.value = 42;
job.value.type = 'UI developer';
console.log(name, age); //不是响应式的
}
// export default {
// setup() {
// //ref实现响应式(基本类型)也是采用Object.definedProperty()来实现的 getter和setter
// let name = ref('小明');
// let age = ref(21);
// //ref实现响应式(对象类型)也是采用Proxy来实现
// let job = ref({
// type: '前端',
// salary: 30000
// });
// function changeInfo() {
// name.value = '李四';
// age.value = 42;
// job.value.type = 'UI developer';
// console.log(name, age); //不是响应式的
// }
// //返回一个对象
// return {
// name,
// age,
// job,
// changeInfo
// }
// }
// }
</script>
reactive
<template>
<!--vue3的组件模版结构可以没有根标签-->
<h1>我是app组件</h1>
<h1>我叫{{ person.name }}, {{ person.age }}岁</h1>
<h3>职位:{{ person.type }}</h3>
<h3>薪水:{{ person.salary }}</h3>
<h3>爱好:{{ person.hobbies }}</h3>
<h4>测试的数据c:{{ person.a.b.c }}</h4>
{{ a.b.c }}
</template>
<!-- <script lang="ts">
import { reactive, toRefs } from 'vue';
export default {
setup() {
let person = reactive({
name: 'py',
age: 21,
type: 'frontend developer',
salary: '30',
hobbies: ['抽烟', '喝酒', '烫头'],
a: {
b: {
c: 666
}
}
});
function changeInfo() {
person.name = '李四';
person.age = 42;
// job.value.type = 'xxx'
person.type = 'UI developer';
//测试reactive能否监测深层次变化
person.a.b.c = 100;
person.hobbies[0] = 'play tennis';
}
//返回一个对象
return {
person,
...toRefs(person), //当从组合式函数中返回响应式对象时,toRefs 解构/展开返回的对象而不会失去响应性:
changeInfo
}
}
}
</script> -->
<script setup lang="ts">
import { reactive, toRefs } from 'vue';
let person = reactive({
name: 'py',
age: 21,
type: 'frontend developer',
salary: '30',
hobbies: ['抽烟', '喝酒', '烫头'],
a: {
b: {
c: 666
}
}
});
function changeInfo() {
person.name = '李四';
person.age = 42;
// job.value.type = 'xxx'
person.type = 'UI developer';
//测试reactive能否监测深层次变化
person.a.b.c = 100;
person.hobbies[0] = 'play tennis';
return {
...toRefs(person), //当从组合式函数中返回响应式对象时,toRefs 解构/展开返回的对象而不会失去响应性:
}
}
const { a } = changeInfo();
</script>
from dailyr-ecord.
watch和computed的区别以及选择?
- 先看两者定义,列举使用上的差异
- 列举使用场景上的差异,如何选择
- 使用细节、注意事项
- vue3变化
computed 表示计算属性, 通常用于处理数据, 方便在模板中的简化书写;
watch 表示监听,
在项目开发中, 当有一些数据需要处理: 随着其它数据变动而变动时,你很容易滥用 watch, 但是通常更好的做法是使用计算属性 computed, 而不是命令式的 watch 回调
使用过程中有一些细节,比如计算属性也是可以传递对象,成为既可读又可写的计算属性。watch可以传递对象,设置deep、immediate等选项。
vue3中watch选项发生了一些变化,例如不再能侦测一个点操作符之外的字符串形式的表达式; reactivity API中新出现了watch、watchEffect可以完全替代目前的watch选项,且功能更加强大。
computed
<template>
<h1>一个人的信息</h1>
姓:<input type="text" v-model="person.firstName" />
<br />
名:<input type="text" v-model="person.lastName" />
<p>姓名:{{ fullName }}</p>
<br />
修改全名:<input type="text" v-model="fullName1" />
</template>
<script setup lang="ts">
import { reactive, computed } from 'vue';
let person = reactive({
firstName: 'pan',
lastName: 'yue',
age: 21,
});
//计算属性(简写,没有考虑计算属性被修改的情况)
const fullName = computed(() => {
return person.firstName + '-' + person.lastName;
})
//计算属性(完整写法,既考虑了读也考虑了改)
const fullName1 = computed({
get() {
return person.firstName + '-' + person.lastName
},
set(name) {
let [fn, ln] = name.split('-');
//响应式
person.firstName = fn;
person.lastName = ln;
}
})
</script>
watch
<template>
<h1>当前求和为:{{ sum }}</h1>
<button @click="sum++">点我加一</button>
<hr />
<h2>当前的信息为:{{ msg }}</h2>
<button @click="msg += '!'">修改信息</button>
<hr />
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>薪资:{{ person.job.j1.salary }}K</h2>
<button @click="person.name = person.name + '~'">修改姓名</button>
<button @click="person.age++">增长年龄</button>
<button @click="person.job.j1.salary++">增长薪资</button>
</template>
<script lang="ts">
import { ref, reactive, watch } from 'vue';
export default {
setup() {
let sum = ref(0);
let msg = ref('你好');
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
//情况一: 监视ref所定义的响应式数据
watch(sum, (nv, ov) => {
//这里我并不需要this,所以剪头函数,普通函数我可以乱粥
console.log('sum的值发生变化了');
console.log(`newValue:${nv}, oldValue:${ov}`);
}, {
//监视的配置
immediate: true //一上来就更新
});
//情况二:监视ref所定义的多个响应式数据
// watch([sum, msg], (nv, ov) => {
// //此时nv和ov都是被监视属性值的数组
// // console.log(Array.isArray(ov)); //true
// console.log('sum的值或者msg的值发生变化了');
// console.log(`newValue:${nv}, oldValue:${ov}`);
// },{
// immediate: true
// });
/**
* 情况三:监视reactive所定义的一个响应式数据
* 坑:1.此处无法获取正确的ov(oldValue)
* 2.强制开启了深度监视
*/
// watch(person, (nv, ov) => {
// console.log('person变化了');
// console.log(nv, ov);
// }, {
// deep: false //此处的deep配置是无效的
// });
//情况四:监视reactive所定义的响应式中的某一个属性
// watch(() => person.age, (nv, ov) => {
// console.log('person的age变了', nv, ov);
// })
//情况五:监视reactive所定义的响应式中的某些属性:并不只是一个
// watch([() => person.age, () => person.name], (nv, ov) => {
// //此时nv和ov都是数组
// console.log('person的age或name发生改变了',nv, ov);
// });
//特殊情况
// watch(() => person.job, (nv, ov) => {
// //这里依然无法拿到正确的ov,因为依然监视的是对象
// console.log('person的job信息发生改变了',nv, ov);
// }, {
// //这里必须要加deep:true注意
// deep: true //此处因为监视的是reactive所定义的响应式对象的一个属性(这个属性的值它依然是一个对象),所以deep配置有效
// })
//返回一个对象
return {
sum,
msg,
person
}
}
}
</script>
from dailyr-ecord.
watch和watchEffect异同?
-
watchEffect立即运行一个函数,然后被动地追踪它的依赖,当这些依赖改变时重新执行该函数。watchEffect就是一种特殊的watch实现。
-
watch侦测一个或多个响应式数据源并在数据源变化时调用一个回调函数。
-
watchEffect立即运行一个函数,然后被动地追踪它的依赖,当这些依赖改变时重新执行该函数。
-
watch侦测一个或多个响应式数据源并在数据源变化时调用一个回调函数。
-
watchEffect(effect)是一种特殊watch,传入的函数既是依赖收集的数据源,也是回调函数。
-
如果我们不关心响应式数据变化前后的值,只是想拿这些数据做些事情,那么watchEffect就是我们需要的。
-
watch更底层,可以接收多种数据源,包括用于依赖收集的getter函数,因此它完全可以实现watchEffect的功能,
-
同时由于可以指定getter函数,依赖可以控制的更精确,还能获取数据变化前后的值,因此如果需要这些时我们会使用watch。
-
watchEffect在使用时,传入的函数会立刻执行一次。
-
watch默认情况下并不会执行回调函数,除非我们手动设置immediate选项。
-
从实现上来说,watchEffect(fn)相当于watch(fn,fn,{immediate:true})
设置 flush: 'post' =>watchPostEffect 将会使侦听器延迟到组件渲染之后再执行。
在某些特殊情况下 (例如要使缓存失效),可能有必要在响应式依赖发生改变时立即触发侦听器。这可以通过设置 flush: 'sync' =>watchSyncEffect来实现。然而,该设置应谨慎使用,因为如果有多个属性同时更新,这将导致一些性能和数据一致性的问题。
watchEffect
<template>
<h1>当前求和为:{{ sum }}</h1>
<button @click="sum++">点我加一</button>
<hr />
<h2>当前的信息为:{{ msg }}</h2>
<button @click="msg += '!'">修改信息</button>
<hr />
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>薪资:{{ person.job.j1.salary }}K</h2>
<button @click="person.name = person.name + '~'">修改姓名</button>
<button @click="person.age++">增长年龄</button>
<button @click="person.job.j1.salary++">增长薪资</button>
</template>
<script lang="ts">
import { reactive, ref, watchEffect } from 'vue';
export default {
setup() {
let sum = ref(0);
let msg = ref('你好');
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
});
//watchEffect
//不确定监视对象
//默认开启了immediate:true
watchEffect(() => {
console.log(`watch effect指定的回调执行了!!`)
//依赖收集,你用到了谁它就监视谁!!
//这里用到sum, person.job.j1.salary了,所以可以被监视到(只要它们发生变化就重新执行watchEffect)
//与computed有点类似,依赖收集.(侧重点不一致,watchEffect注重过程,而computed注重计算函数的返回值)
const x1 = sum.value;
const x2 = person.job.j1.salary;
})
//返回一个对象
return {
sum,
msg,
person
}
}
}
</script>
watch
<template>
<h1>当前求和为:{{ sum }}</h1>
<button @click="sum++">点我加一</button>
<hr />
<h2>当前的信息为:{{ msg }}</h2>
<button @click="msg += '!'">修改信息</button>
<hr />
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>薪资:{{ person.job.j1.salary }}K</h2>
<button @click="person.name = person.name + '~'">修改姓名</button>
<button @click="person.age++">增长年龄</button>
<button @click="person.job.j1.salary++">增长薪资</button>
</template>
<script lang="ts">
import { ref, watch } from 'vue';
export default {
setup() {
let sum = ref(0);
let msg = ref('你好');
let person = ref({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
});
//监测的不是一个值,而是一个保存值的结构(不能写成sum.value) 不能监视一个具体的值注意
watch(sum, (nv, ov) => {
console.log(nv, ov);
});
console.log(person);
//person是RefImpl
//开启深度监视不会存在问题
// watch(person, (nv, ov) => {
// console.log(nv, ov);
// },{
// deep: true
// });
//这里如果不是person.value则会出现问题 因为person是一个RefImpl(默认没开启深度监视)
//但是person.value是一个借助了proxy生成的reactive响应式对象 所以这里可以解决问题
// watch(person.value, (nv, ov) => {
// console.log(nv, ov);
// });
console.log(sum);
console.log(msg);
console.log(person);
//返回一个对象
return {
sum,
msg,
person
}
}
}
</script>
from dailyr-ecord.
readOnly和shallowReadOnly
readOnly接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。
只读代理是深层的:对任何嵌套属性的访问都将是只读的。它的 ref 解包行为与 reactive() 相同,但解包得到的值是只读的。
要避免深层级的转换行为,请使用 [shallowReadonly()]作替代。
<template>
<h2>当前求和为:{{ sum }}</h2>
<button @click="sum++">sum+1</button>
<hr />
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ job.j1.salary }}K</h2>
<button @click="name = name + '~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">增长薪资</button>
</template>
<script lang="ts">
import { ref, reactive, toRefs, readonly, shallowReadonly } from 'vue';
export default {
setup() {
let sum = ref(0);
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
});
// person = readonly(person); //此时person里面的属性值都不允许修改
//person = shallowReadonly(person); //第一层不能改(name,age), 但j1和salary仍然可以改动
// sum = readonly(sum); //同理
// sum = shallowReadonly(sum)
return {
sum,
...toRefs(person),
};
}
}
</script>
from dailyr-ecord.
readOnly和shallowReadOnly
readOnly接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。
只读代理是深层的:对任何嵌套属性的访问都将是只读的。它的 ref 解包行为与 reactive() 相同,但解包得到的值是只读的。
要避免深层级的转换行为,请使用 [shallowReadonly()]作替代。
<template>
<h2>当前求和为:{{ sum }}</h2>
<button @click="sum++">sum+1</button>
<hr />
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ job.j1.salary }}K</h2>
<button @click="name = name + '~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">增长薪资</button>
</template>
<script lang="ts">
import { ref, reactive, toRefs, readonly, shallowReadonly } from 'vue';
export default {
setup() {
let sum = ref(0);
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
});
// person = readonly(person); //此时person里面的属性值都不允许修改
//person = shallowReadonly(person); //第一层不能改(name,age), 但j1和salary仍然可以改动
// sum = readonly(sum); //同理
// sum = shallowReadonly(sum)
return {
sum,
...toRefs(person),
};
}
}
</script>
from dailyr-ecord.
响应式 API:工具函数
unref
省略ref调用值时的.value操作,直接进行数据的操作获取;
当使用.value太频繁的时候,不知道后面的值到底有没有.value,此时就可以用该API进行包裹访问
import { unref } from "vue";
let msgref = ref('你好')
console.log(unref(msgref)) // 你好
let msg = '你好'
console.log(unref(msg)) // 你好
toRef
可以为源响应式对象上的某个属性新创建一个ref,且ref可以被传递,会保持对源属性的响应式连接
import { ref, toRef } from "vue";
// 使用 toRef 后 两个变量数据 会产生链式关系 互相响应 一个数据发送改变 另一个也会跟随 改变
const user = ref({
name: "Lbxin",
age: 22,
});
const newAge = toRef(user.value, "age");
newAge.value = 20;
console.log(user.value.age); // 20
user.value.age = 18;
console.log(newAge.value); // 18
from dailyr-ecord.
vue3生命周期
Vue生命周期总共可以分为8个阶段:创建前后, 载入前后, 更新前后, 销毁前后,以及一些特殊场景的生命周期。vue3中新增了三个用于调试和服务端渲染场景。
beforeCreate:通常用于插件开发中执行一些初始化任务
created:组件初始化完毕,可以访问各种数据,获取接口数据等
mounted:dom已创建,可用于获取访问数据和dom元素;访问子组件等。
beforeUpdate:此时view层还未更新,可用于获取更新前各种状态
updated:完成view层的更新,更新后,所有状态已是最新
beforeunmounted:实例被销毁前调用,可用于一些定时器或订阅的取消
unmounted:销毁一个实例。可清理它与其它实例的连接,解绑它的全部指令及事件监听器
from dailyr-ecord.
defineComponent
defineComponent 是 Vue 3 推出的一个全新 API ,可用于对 TypeScript 代码的类型推导,帮助开发者简化掉很多编码过程中的类型声明。
defineComponent最重要的是:在TypeScript下,给予了组件 正确的参数类型推断 。
import { defineComponent } from 'vue'
// 使用 `defineComponent` 包裹组件的内部逻辑
export default defineComponent({
setup(props, context) {
// ...
return {
// ...
}
},
})
from dailyr-ecord.
Related Issues (9)
- 2022年8月8号学习记录
- ES6面试题汇总 HOT 17
- React学习记录 HOT 16
- angular 面试总结 HOT 13
- Css知识积累 HOT 1
- 常见http问题
- Vue2 基础知识积累 HOT 12
- vue3 基础知识
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from dailyr-ecord.