VUE3的 单文件组件 <script setup>理解与实际上手案例
第一次使用VUE3时,项目中使用的时兼容VUE2引入setup函数的写法,上手很快也容易理解。直到发现官方的源码展示都用了全新的单文件组件写法,意识到这个才是主流。这篇文章记录下学习过程。
第一次使用VUE3时,项目中使用的时兼容VUE2引入setup函数的写法,上手很快也容易理解。
直到发现element plus官方的源码展示都用了全新的单文件组件写法,意识到这个才是主流。
这篇文章记录下学习过程。
理解:
类似VUE2写法与最原始HTML脚本
的结合,将Vue2的写法export default {}的代码整体暴露出去 变成 手动调用API进行封装;
<script setup>
是在单文件组件 (SFC) 中使用组合式 API的编译时语法糖。相比于普通的<script>
语法,它具有更多优势
1.更简洁的代码
2.能够使用纯 Typescript 声明 props 和 抛出事件,<script setup lang="ts">
。
3.更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。
4.更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)。
<script setup>
内的代码会被编译成组件 setup() 函数的内容。这意味着与普通的<script>
只在组件被首次引入的时候执行一次不同,script setup>
中的代码会在每次组件实例被创建的时候执行。
HTML脚本
<p id="two">JavaScript 可以触发事件。</p>
<button type="button" onclick="twoFun()">点我</button>
<script>
function twoFun()
{
document.getElementById("two").innerHTML="Hello JavaScript!";
}
</script>
VUE2单文件组件
<template>
<div>
...
</div>
</template>
<script>
import { AAAA } from '@/api/model'
export default {
name:'',
components: {},
props: {},
data(){return {}},
computed: {},
created (){},
mounted (){},
methods: {},
watch: {}
}
</script>
VUE3单文件组件
<script setup>
import { AAAA } from '@/api/model'
import { ref,reactive } from "vue";
const props = defineProps({
detailData: Object
});
const showModel = ref(1)
const evaluate = reactive({A:'a'})
function myEvaluate() {
showModel.value = 3
}
</script>
<template>
<div>
...
</div>
</template>
案例
- 当使用
<script setup>
的时候,任何在<script setup>
声明的顶层的绑定 (包括变量,函数声明,以及 import 导入的内容) 都能在模板中直接使用 - 与早期语法setup()函数导入类似,可以使用
ref,reactive
响应式API来创建与模板双向绑定的数据。还可以引入watch,生命周期
等 - 引入子组件时,无需像VUE2语法那样需要在components: {},函数中申明。
- 使用
defineProps()
来引入父组件绑定的数据,类似VUE2的props
对象。
使用defineEmits()
来引入父组件绑定的数据,类似VUE2的this.$emit('父组件方法名', 参数组)
写法
都是只能在<script setup>
中使用的编译器宏。他们不需要导入 - Vue2中可直接全局调用的变量在Vite中会发生改变,例如
process.env.VUE_APP_BASE_API
变为import.meta.env.VITE_APP_BASE_AP
详情见vite官网
<script setup>
import DemandList from "@/components/DemandList.vue";//子组件
import { ref,reactive,computed,watch,watchEffect,onMounted } from "vue";//各种API
import {submitFeedbacEvaluate} from "@/http/index.js";//引入函数
onMounted(()=> {
//setup 是围绕beforeCreate和created生命周期钩子运行的,不需要显式地定义它们。在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。
//生命周期基本都被重命名 首字母大写后加上on前缀 例如beforeUpdate => onBeforeUpdate
console.log('onMounted')
})
const props = defineProps({
detailData: Object//父组件绑定数据
});
const feedbackId = computed(()=>{
return detailData.id
})
const emit = defineEmits(["closeDrawer"]);// 传递父组建内容
const showModel = ref(1) //创建响应式基本类型数据
const evaluate = reactive({ feedbackId:'',opinion:''}) //创建响应式引用类型数据
function toEvaluate() {
if(props.detailData.feedbackEvaluateList.length>=5){
ElNotification({
title: "Error",
message: "最多支持评价5次!",
type: "error",
});
return;
}
showModel.value = 2
}
function myEvaluate() {showModel.value = 3}
function returnDetail() {showModel.value = 1}
watch(
()=>props.detailData,
(newVal)=>{
console.log('watch 已触发', newValue)
},
{ deep: true }),
watch(() =>
router.currentRoute.value.path,
(toPath:any) => {
//要执行的方法
const query = router.currentRoute.value.query;
},{immediate: true,deep: true})
watchEffect(()=> {
console.log(props.detailData)//Vue3.0新增的一个监听属性的方法,可直接获取到每一次响应式数据的改变
})
function submitEvaluate() {
evaluate.feedbackId = props.detailData.id //在模板中直接使用顶层暴露的数据与函数
for(let key in evaluate){
if(evaluate[key]==''){
ElNotification({
title: "Error",
message: "必填内容未填写!",
type: "error",
});
return;
}
}
submitFeedbacEvaluate(evaluate).then((res) => {
if(res.errCode=='00'){
ElNotification({
title: "Success",
message: res.data,
type: "success",
});
returnDetail();
handleClose();
}
});
}
function handleClose() {
emit("closeDrawer");
}
</script>
<template>
<div class="detail">
<!-- 直接使用引入组件 直接使用顶层声明的函数与变量-->
<demand-list :id="detailData.id" v-if="detailData.id"/>
<el-button @click="toEvaluate" type="primary" v-if="detailData.feedbackEvaluateList.length==0">去评价</el-button>
<el-button @click="myEvaluate" type="primary" v-else>我的评价</el-button>
<el-button @click="returnDetail" type="primary" >返回</el-button>
<el-input clearable show-word-limit v-model="evaluate.opinion" :rows="3" type="textarea" maxlength="500"/>
<el-button @click="submitEvaluate" type="primary">提交</el-button>
</div>
</template>
-
可以使用带 . 的组件标签,例如 <Foo.Bar> 来引用嵌套在对象属性中的组件。这在需要从单个文件中导入多个组件的时候非常有用:
-
使用动态组件的时候,应该使用动态的 :is 来绑定:
-
如何写promise函数?
<script setup>
中可以使用顶层 await。结果代码会被编译成 async setup();
官网这句话可以简单理解为当语法糖代码中含有await时,编译时会自动变为async setup()。即省去了手动定义async函数的过程,其他与VUE2中promise函数的使用几无区别
理解promise
<script setup>
const listData = await getData();//也可以将promise写为公告方法从外部引入使用
//JS代码仍是自上而下一步步执行 在这里会等待await执行完毕后继续向下
function getData() {
return new promise((resolve,reject)=>{
setTimeout(()=>{
resolve('模拟异步调取接口')
},2000)
})
}
</script>
ref()、isRef()、unref()、reactive()、toRefs()
- ref() 定义一个响应式的数据,但是获取的时候要 .value 才能获取定义的值
- isRef() 判断一个值是否为一个 ref 对象。
- unref() 如果参数是一个 ref 则返回它的 value,否则返回参数本身。
unref():是 val = isRef(val) ? val.value : val 的语法糖。 - reactive() 函数,用来创建响应式的数据对象。
当需要大量数据的时候reactive()是一个很好的选择,不需要重复声明。
定义完data数据之后,可以使用es6语法将其导出,对reactive返回的对象进行解构获取值,但是这样的话数据就会失去响应式,这样的话就需要使用toRefs(),toRefs()使用在下面。
const obj=reactive({name:'xwl',age:18,x,info:{school:'secret'}})
const {name,age,info}=obj; //name,age,info都不是响应式的
const obj=reactive({name:'xwl',age:18,x,info:{school:'secret'}})
const {name,age,info}=toRefs(obj); //name,age,info与ref.value建立链接,均为可响应式对象
toRefs(),toRefs()的区别只是将一个或多个值转换为响应式对象,数量上的区别罢了
TS兼容写法
<script setup lang="ts">
写法总结
更多推荐
所有评论(0)