vue3 setup 实用知识点与相关项目实践问题解决

一、 vue3 <script setup> 写法

  • Option Api (与vue2写法相同)
  • Composition API (setup()函数写法)
  • <script setup> (Composition API 的语法糖)

<script setup>就是vue3新出的一个语法糖,使用方法就是在书写script标签的时候在其后面加上一个setup修饰。, 因为这种写法比较简洁,推荐使用这种写法

1.ref, reactive, toRef, toRefs 的区别

  • ref 可以把基本数据类型数据,转成响应式对象
  • ref 返回的对象,重新赋值成对象也是响应式的

1.1 ref

用于为数据添加响应式的状态,它的本质就是将基本数据或对象通过 new Proxy 创建成为一个响应式的对象,所以通过它创建的基本数据 其实是个对象 它的值就放在了该对象的value属性中

//可以理解为这样
const a = 1
// a = 1
const a = ref(1)
//a = {
//value:1
//}

所有通过ref创建的数据 需要通过xxx.value取它的值
通过ref创建一个响应式对象 其实它的内部还是通过reactive对该对象进行处理
所以声明响应式对象 **推荐使用ref统一声明**

<script setup>
import { ref } from 'vue'
const a = ref(1)
console.log(a.value) // 1
<script>

1.2 reactive

reactive主要为对象添加响应式对象,接收一个对象作为参数,可以用于为表单等数据做统一响应式处理

  • 取值时不需要加.value
  • reactive 返回的对象,重新赋值丢失响应式
  • reactive 返回的对象不可以解构
const form = reactive({
  name:'aa',
  age:20
})

console.log(from.name) // aa

const {name,age} = form //通过解构 此时name,age会丢失响应 要想让它具有响应式 需要通过toRefs处理
const {name,age} = toRefs(form) //此时name,age具有响应

1.3 toRefs

toRefs 用于将响应式对象转换为结果对象,其中结果对象的每个属性都是指向原始对象相应属性的ref。
常用于es6的解构赋值操作。
主要解决 对一个响应式对象直接解构时解构后的数据将不再有响应式

const props = defineProps({
  msg:{
    type:String,
    default:''
  },
  info:{
    type:String,
    default:''
  }
})
const {msg,info} = toRefs(props) //msg,与info就具有响应式了 指针指向props下面对应的属性值
msg.value
info.value

1.4 toRef

toRef 用于为源响应式对象上的属性新建一个ref,从而保持对其源对象属性的响应式连接。
接收两个参数:源响应式对象和属性名,返回一个ref数据
例如使用父组件传递的props数据时,要引用props的某个属性且要保持响应式连接时就很有用

const props = defineProps({
  msg:{
    type:String,
    default:'aa'
  },
  info:{
    type:String,
    default:''
  }
})
const msg = toRef(props,'msg') 
msg.value //aa

1.5 总结

  • 通过带有ref相关的方法处理后的数据 都需要通过.value获取
  • 推荐使用 ref 与 toRefs 方式处理数据
    说明下为什么不用reactive
    通常来讲,reactive 适用于复杂的数据类型,如数组,对象。但发觉 使用时,不如ref的方便
    1.需要通过toRefs来解构赋值,数据才具有响应性
    2.和ref获取值不一样,导致需要区分是否要.value

2.Props使用

  • 注意:Props的值无法直接修改,要通过emit("update:xxx",val)进行修改
<template>

<h1>{{ msg }}</h1>
<h1>{{ props.msg }}</h1>

</template>

<script setup lang="ts">
import { ref} from 'vue'
// defineProps 不需要引入
const props =  defineProps({
  msg:{
    type: String,
    default:''
  }
})

console.log(props.msg)

</script>

3.Emits

<script setup lang="ts">
//defineEmits  无需引入
const emit = defineEmits(['add'])

const add = () => {
  emit('add')
}

</script>

4.store

<script lang="ts" setup>
  import { useStore } from 'vuex'
  const store = useStore()
  console.log(store.state.xxx)
</script>

5.router

<script lang="ts" setup>
  import { useRouter } from 'vue-router'
  const router= useRouter ()
  console.log(router.currentRoute.value)
</script>

6.computed

<script lang="ts" setup>
  import { computed } from 'vue'
  const name= computed (() => { 
    return 'xxx'
  })
</script>

7.computed

computed 计算属性 可以对当前实例上的数据进行计算,并缓存下来,只有当依赖的响应式数据改变时,才重进计算,否则每次取到的计算属性数据都是从缓存中取到的(>1)

重点:计算属性不能对依赖数据进行赋值、修改,不能操作DOM,只能说是依赖于某个值得到某个值,其他的都不行
在vue3中,计算属性默认传输一个参数,就是数据的getter函数,如下图所示

  • 简单写法
import { computed  } from "vue";

 const message = computed(()=>{
   return "无法进入此页面...";
 }) 
  • 复杂写法
import { computed  } from "vue";

const newName=computed({
  get:()=>{
    console.log(`我是get函数值=======${name.value}`)
    return name.value
  },
  set:(newValue)=>{
    console.log(`我是set函数值======${newValue}`)
    name.value = newValue
  }
})

8.watch

import { watch } from "vue";
<script setup lang="ts" name="UpdateBase">
let dialogVisible = ref<boolean>(false);//弹框

watch(() => dialogVisible.value, (newValue,oldValue)=> {
    //注意:此时的watch第一个参数是一个箭头函数。
    //监听对象中的某个属性
    console.log(newValue);
});
</script>

9.父子组件通信

9.1 父组件调用子组件

  • 父组件
<template>
  <Son ref="RefSon"></Son>
</template>

//父组件调用子组件
const RefSon = ref();
RefSon.value.submit();
RefSon.value.count
  • 子组件
const cout = ref(1);
const submit = ()=>{
 console.log("提交");
}
//暴露方法和值
defineExpost({submit,count});

9.2 子组件调用父组件

  • 子组件
// 选择跳转到第几页
const handleCurrentChange =(index:number) => {
    emit("handleCurrentChange", index);
};
// 选择一页有条数据
const  handleSizeChange = (index:number) => {
    emit("handleSizeChange", index);
};
//修改父组件值
const changePageSize = (pageSize:number)=>{
  emit("update:pageSize":pageSize)
}
//子组件调用父组件方法及更新父组件值
const emit = defineEmits(["handleCurrentChange","handleSizeChange","update:pageSize"]);
  • 父组件
<Son 
   v-model:pageSize = "pageSize"
   @handleCurrentChange="pageChange"
   @handleSizeChange="pageSizeChange"/>

<script setup lang="ts" name="Parent">
    const pageSize = ref(10);
    const pageChange =(index:number)=> {
        console.log("调用父组件方法成功");
    }
</script>

10.定义组件name属性

<script setup lang="ts" name="Layout">

</script>

11.定义全局变量

1.provide/inject 【推荐】

1.新建config.ts文件

//全局常量
export default {
	// 分页每页条数切换的配置
	pageSizeList: [5, 30, 50, 100],
	// 分页配置
	pageConfig: {
		elapsedMilliseconds: null, //耗时
		pageSize: 30, // 每页显示数
		pageIndex: 1, // 当前页
		orderField: 'CREATEDATE', // 排序字段
		ascending: false, // 排序类型true升序 false降序
		total: 0, //  总条数
		totalPage: 0, //  总页数
	},
	// 全局表格配置
	tableConfig: {
		border: false, //全局表格边框是否展示
		highlightRow: true, //全局表格高亮是否开启
		loading: true, //全局表格加载是否开启
		height: 200, //全局表格初始高度
	},
}

2.main.ts文件引入

import config from '@/libs/config'

//分页等常量设定为全局变量
app.provide('$config', config)

3.使用

import { inject } from "vue";

const $config:any = inject("$config");
const tableConfig = ref($config.tableConfig);

12.静态文件引入(图片)

由于vue2项目之前是用的 webpack 所以我们引入本地图片会用 require(xxxxx)
Vue3 是使用vite构建的,所以引入方式如下:

import noFoundImgUrl from "@/assets/404_images/404.png";

<img class="pic-404__parent" :src="noFoundImgUrl"  alt="404"/>

二、setup 与vue2的区别

1.自动注册子组件

vue2:父组件调用子组件使用components注册组件
vue3:不用写,引入子组件即可

2. 属性和方法无需返回

之前说composition API写起来有点繁琐的原因在于需要手动返回模板需要使用的属性和方法。而在setup script中可以省略这一步。

  • composition API
<template>
  <div>
    <h2 @click="ageInc">{{ name }} is {{ age }}</h2>
  </div>
</template>
 
<script>
import { defineComponent, ref } from 'vue';
 
export default defineComponent({
  setup() {
    const name = ref('CoCoyY1')
    const age = ref(18) 
    const ageInc = () => {
      age.value++
    } 
    return {
      name,
      age, 
      ageInc
    }
  }
})
</script>
  • setup
<template>
  <div>
    <h2 @click="ageInc">{{ name }} is {{ age }}</h2>
  </div>
</template>
 
<script setup>
import { ref } from 'vue'; 
const name = ref('CoCoyY1')
const age = ref(18) 
const ageInc = () => {
  age.value++
}
 
</script>

3.setup 数据的监听为什么不生效

  • 我这里是因为监听的值dialogVisible写出了dialogVisible,应该写dialogVisible.value才能监听成功
watch(() => dialogVisible.value, (newValue,oldValue) => {
   console.log("监听");
});

三、报错集合

1.Vue typescript ref:Cannot create property 'value' on boolean 'false'

Vue typescript引用:不能在布尔值false上创建属性“value”

  • 原因:参数赋值错误
  • 解决方案:去掉value即可
    (updateBaseRef.value as any).dialogVisible = true;
  • 说明:updateBaseRef 是子组件 ref的值,通过父组件调用子组件updateBaseRef 的参数dialogVisible(const dialogVisible = ref(false)) 打开弹框

2. Object is of type 'unknown'

场景:父传子值,值的类型为数组套对象的形式

const props = defineProps({
    UpdateForm:{
        type:Array,
        default:()=>[]
    }
})

//然后实现动态表单,item会报错为 Object is of type 'unknown
<el-form class="form-inline" label-width="120px" ref="submitRef"  :model="submitReq" >
    <template v-for="(item,key) in UpdateForm">
        <el-form-item :label="item.label" :prop="item.prop">
            <el-input v-model="submitReq[item.prop]" :placeholder="item.pleaseholder" />
        </el-form-item>
    </template>
    <!-- 优先级 -->
    <el-form-item label="优先级" prop="priority">
          <el-input v-model="submitReq.priority" placeholder="请输入优先级" />
    </el-form-item>
</el-form>
  • 百度查了下原因:因为Array里可以放任意类型,所以需要Array<类型>,而Vue 本身已经提供了包装类型 PropType

  • PropType官网

  • 在这里插入图片描述

  • 解决方案:使用PropType

interface IUpdateBase {
	label: string
	prop: string
	pleaseholder: string
	type: string
}
const props = defineProps({
    UpdateForm:{
        type:Array as PropType<IUpdateBase[]>,
        default:()=>[]
    }
})

3.type check failed for prop "modelValue". Expected Boolean, got Undefined

根据报错提示,modelValue”的类型检查失败。预期布尔值,得到未定义,并且可以追溯到是哪个页面

  • 原本以为是表单model属性未定义类型,但还是有报错,
  • 通过预期为布尔类型可知,猜测为弹框的v-model="dialogVisible"属性为设定初始值,导致的
    解决方案:let dialogVisible:Ref<Boolean> = ref(false);解除警告

在这里插入图片描述

4.Generic type 'Array<T>' requires 1 type argument(s).ts

意思是要确定数组内部的属性:
pageSizeList: Array === > pageSizeList: Array<number>

5. 使用provide/inject时报错:Object is of type 'unknown'

//全局变量
export type GlobalVariableType = {
	pageSizeList: Array<number>
	pageConfig: IReq
	tableConfig: {
		border: Boolean
		highlightRow: Boolean
		loading: Boolean
		height: Number
	}
}
const $config = inject<GlobalVariableType>("$config");
const tableConfig = ref(($config as GlobalVariableType).tableConfig);

6.Type '{ valueOf: () => boolean; }' is not assignable to type 'boolean | undefined'.

在这里插入图片描述

:loading = "tableConfig.loading" ===> :loading = "Boolean(tableConfig.loading)" 强制转换

7.Object is possibly 'undefined'

在这里插入图片描述![
定义type类型为Array<string>
实际值为:type:[“success”,“info”]

解决方案::type="item.type![1]"(加!即可 ,告诉ts 一定有值)

8.Property 'value' does not exist on type 'Object'

在这里插入图片描述
selectList定义类型:Array<Object> 修改为 Array<{value:string,label:string}>

9.Argument of type 'string | null' is not assignable to parameter of type 'string'.

在这里插入图片描述
JSON.parse(localStorage.getItem(name))修改为JSON.parse(localStorage.getItem(key)|| '0')

10."undefined" is not valid JSON at JSON.parse || JSON.parse(JSON.stringify(undefined))报错

最终发现是问题是JSON.parse(),它是无法对undefinedor''进行解析的
JSON.parse()内容必须符合JSON格式规范,具体可以查看菜鸟和w3c等

return JSON.parse(localStorage.getItem(name) || "0"); ==>localStorage.getItem(name) 值为undefined 不可转为json

解决方案:

if (localStorage.getItem(name) !== "undefined") {
	return JSON.parse(localStorage.getItem(name) || "0");
} else {
	return "";
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值