第一次使用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兼容写法
Logo

前往低代码交流专区

更多推荐