Object.defineProperty()
对数据进行劫持结合发布订阅模式来实现proxy
对数据代理,通过 reactive() 函数给每一个对象都包一层 proxy,通过 proxy 监听属性的变化,从而实现对数据的监控对象新增、删除属性没有响应式
,数组新增、删除元素没有响应式;通过下标修改某个元素没有响应式;通过.length改变数组长度没有响应式
。只有实例创建时 data 中有的数据实例创建后才是响应式的,给已创建好的 vue 实例 data 对象中添加属性时,数据虽然会更新,但视图不会更新,不具有响应式
this.$forceUpdate()
强制更新视图和数据(不推荐)[Vue | this].$set(object,key,value),实例中添加响应式属性;
[Vue | this].$delete(object,key),实例中删除属性;
Object.assign(),将多个对象属性合并到目标对象中,具有响应式;
Object.freeze(),将对象冻结,防止任何改变。使得对象成为只读,无法添加、删除或更新;
Object.keys(),返回对象的所有属性;
Object.values(),返回对象的所有属性值;
Object.entries(),返回对象的所有键值对;
pop(),尾部删除元素;
push(),尾部添加元素;
unshift(),首部添加元素;
shift(),首部删除元素;
sort(),排序;
reverse(),翻转;
splice(index[必填,位置],howmany[必填,要删除的数量],item1...itemx[可选,向数组中添加的新元素]);
【补充】清空对象,置空数组操作
this.form = {}
this.$refs.form.resetFields()
this.form.name = ""
this.arrayList = []
this.arrayList.splice(0,this.arrayList.length)
// this.arrayList.length = 0 不具有响应式,无法实现
proxy性能整体上优于Object.defineProperty
vue3支持更多数据类型的劫持
(vue2只支持Object、Array;vue3支持Object、Array、Map、WeakMap、Set、WeakSet)vue3支持更多时机来进行依赖收集和触发通知
(vue2只在get时进行依赖收集,vue3在get/has/iterate时进行依赖收集;vue2只在set时触发通知,vue3在set/add/delete/clear时触发通知),所以vue2中的响应式缺陷vue3可以实现
vue3做到了“精准数据”的数据劫持
(vue2会把整个data进行递归数据劫持,而vue3只有在用到某个对象时,才进行数据劫持,所以响应式更快并且占内存更小)vue3的依赖收集器更容易维护
(vue3监听和操作的是原生数组;vue2是通过重写的方法实现对数组的监控)vue2 | vue3 | 说明 |
---|---|---|
beforeCreate | setup() | 组件创建之前,执行初始化任务 |
created | setup() | 组件创建完成,访问数据、获取接口数据 |
beforeMount | onBeforeMount | 组件挂载之前 |
mounted | onMounted | 组件挂载完成,DOM已创建,访问数据或DOM元素,访问子组件 |
beforeUpdate | onBeforeUpdate | 未更新,获取更新前所有状态 |
updated | onUpdated | 已更新,获取更新后所有状态 |
beforeDestroy | onBeforeUnmount | 组件销毁之前,清空定时器,取消订阅消息 |
destroyed | onUnmounted | 组件销毁之后 |
activated | onActivated | keep-alive包含,组件被激活时 |
deactivated | onDeactivated | keep-alive包含,发生组件切换,组件消失时 |
<script setup>
const getList = () => {}
getList()
onMounted(() => {
getList()
}),
onBeforeMount(() => {
getList()
}),
</script>
<script>
export default {
mounted() {
// 开启定时器
let timer = setInterval(() => {
console.log('---定时器在触发---')
}, 1000)
//这下面的代码不可以省略
this.$on('hook:activated', () => {
if (timer === null) { // 避免重复开启定时器
timer = setInterval(() => {
console.log('setInterval')
}, 1000)
}
})
this.$on('hook:deactivated', () => {
clearInterval(timer)
timer = null
})
}
}
<script>
<script setup>
import { onBeforeUnmount, onDeactivated } from 'vue'
// 组件卸载前,对应 Vue2 的 beforeDestroy
onBeforeUnmount(() => {
clearTimeout(timer)
window.removeAddEventListener('...')
})
// 退出缓存组件,对应 Vue2 的 deactivated
onDeactivated(() => {
clearTimeout(timer)
window.removeAddEventListener('...')
})
</script>
setup()
在解析其他组件选项(data、methods、computed 等都没解析)之前调用,在beforeCreate()之前执行,所以this指向undefined,vue3中不能通过this进行访问<script setup>
import { getCurrentInstance } from "vue";
// proxy 为当前组件实例;global 为全局组件实例
const { proxy, appContext } = getCurrentInstance();
const global = appContext.config.globalProperties;
</script>
基本类型
生成 RefImpl
实例;定义复合类型
生成 Proxy
实例.value
调用const count = ref(0)
const user = ref({
name:'falcon',
age:20
})
const addCount = () => count.value++
const addAge = () => user.value.age++
Proxy
实例shallowReactive
生成非递归响应数据,只监听第一层数据的变化const stu = reactive({
name:'falcon',
major:'Chinese',
score:80
})
const addScore = () => stu.score++
toRef()
,单个转化为响应式toRefs()
,多个转化为响应式unref()
,是 val = isRef(val) ? val.value : val
的语法糖;如果参数是一个ref就返回其 value,否则返回参数本身【注】针对一个响应式对象(reactive封装)的prop(属性)创建一个ref,且保持响应式
const stu = reactive({
name:'falcon',
age:20,
major:'Chinese',
score:80
})
const age = toRef(stu,'age')
const {name,major,score} = toRefs(stu)
readonly
,创建只读对象(递归只读)isReadonly
,判断是否是readonly对象shallowReadonly
,只对最外层响应式只读,深层次不转换let status = readonly(true);
const changeStatus = () => (status = !status);
let info = reactive({
username: "falcon",
password: "123456",
role: {
roleId: 123,
roleName: "系统管理员",
},
});
info = shallowReadonly(info);
const changeRole = () => {
info.role.roleId++;
};
只能有一个根节点
,因为vdom是一颗单根树,patch方法在遍历的时候从根节点开始,所以要求template只有一个根元素可以有多个根节点
,因为如果template不只有一个根元素时,就会添加一个fragment组件将多个根组件包起来<template>
<div>demo1 text</div>
<h2>h2 text</h2>
<p>p text</p>
</template>
<template>
<div class="app-container">
<el-button type="primary" @click="showToast">打开弹框</el-button>
</div>
<teleport to="body">
<div v-if="visible" class="modal_class">
A man who has not climbed the granted wall is not a true man
<el-button
style="width: 50%; margin-top: 20px"
type="primary"
@click="closeToast"
>关闭弹框</el-button
>
</div>
</teleport>
</template>
<script setup>
import { ref } from "vue";
const visible = ref(false);
const showToast = () => {
visible.value = true;
};
const closeToast = () => {
visible.value = false;
};
</script>
<style scoped>
.modal_class {
position: absolute;
width: 300px;
height: 200px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 1px solid #ccc;
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
padding: 30px;
}
</style>
#default
初始化模板组件;#fallback
异步请求中处理的ui<template>
<div class="app-container">
<Suspense>
<template #default>
<SyncApi />
template>
<template #fallback>
<h3 style="color: blue">数据加载中...h3>
template>
Suspense>
div>
template>
<script setup>
import SyncApi from "./SyncApi.vue";
script>
// SyncApi 组件内容
<template>
<div v-for="(people, index) in peoples.results" :key="index">
{{ people.name }} {{ people.birth_year }}
div>
template>
<script setup>
const peoples = ref({
results: [],
});
const headers = { "Content-Type": "application/json" };
const fetchPeoples = await fetch("https://swapi.dev/api/people", {
headers,
});
peoples.value = await fetchPeoples.json();
script>
<template>
<Table />
</template>
<script setup>
import Table from "@/components/Table";
</script>
<template>
<el-form ref="formRef">el-form>
template>
<script setup>
// 1. 变量名和 DOM 上的 ref 属性必须同名,自动形成绑定
const formRef = ref(null)
console.log(formRef.value)
// 2. 通过当前组件实例来获取DOM元素
const { proxy } = getCurrentInstance()
proxy.$refs.formRef.validate((valid) => { ... })
script>
// vue2 中用法
watch:{
// 第一种
flag(newValue,oldValue){},
// 第二种
user:{
handler(newValue,oldValue){},
immediate:true,
deep:true
}
}
// vue3 中用法
<script setup>
const count = ref(0)
const status = ref(false)
// 监听一个
watch(count,(newValue,oldValue) => {})
// 监听多个
watch([count,status],([newCount,oldCount],[newStatus,oldStatus]) => {})
const user = reactive({
name:'falcon',
age:20,
sex:'female',
hobbies:[]
})
// 监听一个
watch(() => user.age,(newValue,oldValue) => {})
// 监听多个
watch([() => user.name,() => user.sex],(newValue,oldValue) => {})
// 添加配置参数
watch(() => user.hobbies,(newValue,oldValue)=> {},{
immediate:true,
deep:true,
// 回调函数的执行时机,默认在组件更新之前执行,更新之后执行参数为‘post’
flush:'pre'
})
</script>
// 正常情况组件销毁自动停止监听
watchEffect(() => {})
// 异步方式手动停止监听
const stopWatch = watch(() => user.hobbies,(newValue,oldValue)=>{},{deep:true})
setTimeout(() => {
stopWatch()
},3000)
const stopWatchEffect = watchEffect(() => {})
setTimeout(() => {
stopWatchEffect()
},3000)
对传入的一个或多个值进行监听
,触发时会返回新值和旧值
,且默认第一次不会执行
默认第一次会执行,且不需要传入监听的内容,会自动收集函数内的数据源作为依赖
,当依赖发生变化时会重新执行函数(类似computed),并且不会返回旧值
异步方式
例如setTimeout里创建的监听需要手动停止具有响应性
,利用其set()
方法回调函数必须return
,结果是计算结果数据项发生变化时,重新计算,具有缓存性
不能执行异步操作
const names = reactive({
firstName:'',
lastName:'',
fullName:''
})
// 通过此种方式定义的fullName,想要修改的时候后台警告:Write operation failed: computed value is readonly;想要修改fullName,通过set()方法
const fullName = computed(() => {
return names.firstName + " " + names.lastName;
});
const fullName = computed({
get(){
return names.firstName + " " + names.lastName
},
set(value){
}
})
使用hooks代替 vue2 中的mixin
。hooks 约定用驼峰命名法,并以“use”作为开头;将可复用的功能抽离为外部js文件;引用时将定义的属性和方法响应式地解构暴露出来;避免了vue2的碎片化,实现低内聚高耦合先执行mixin中生命周期函数;后执行组件内部代码
,mixin中的data数据和组件中的data数据冲突时,组件中的data数据会覆盖mixin中数据
// useCount.js
const useCount = (initValue = 1) => {
const count = ref(initValue)
const increase = (delta) => {
if(typeof delta !== 'undefined'){
count.value += delta
}else{
count.value++
}
}
const multiple = computed(() => count.value * 2)
const decrease = (delta) => {
if(typeof delta !== 'undefined'){
count.value -= delta
}else{
count.value--
}
}
const reset = () => count.value = initValue
return {
count,
multiple,
increase,
decrease,
reset
}
}
export default useCount
<template>
<p>{{count}}p>
<p>{{multiple}}p>
<el-button @click="addCount">count++el-button>
<el-button @click="subCount">count--el-button>
<el-button @click="resetCount">resetel-button>
template>
<script setup>
import useCount from "@/hooks/useCount"
const {count,multiple,increase,decrease,reset} = useCount(10)
const addCount = () => increase()
const subCount = () => decrease()
const resetCount = () => reset()
script>
// Demo.vue
<template>
<div>异步加载组件的内容div>
template>
// ErrorComponent.vue
<template>
<div>Warning:组件加载异常div>
template>
// LoadingComponent.vue
<template>
<div>组件正在加载...<div>
template>
<template>
<AsyncDemo />
template>
<script setup>
import LoadingComponent from './LoadingComponent.vue'
import ErrorComponent from './ErrorComponent.vue'
const time = (t,callback = () => {}) => {
return new Promise((resolve) => {
setTimeout(() => {
callback()
resolve()
},t)
})
}
const AsyncDemo = defineAsyncComponent({
// 要加载的组件
loader:() => {
return new Promise((resolve) => {
async function(){
await time(3000)
const res = await import("./Demo.vue")
resolve(res)
}
})
},
// 加载异步组件时使用的组件
loadingComponent:LoadingComponent,
// 加载失败时使用的组件
errorComponent:ErrorComponent,
// 加载延迟(在显示loadingComponent之前的延迟),默认200
delay:0,
// 超时显示组件错误,默认永不超时
timeout:5000
})
script>
slot='插槽名称'
,vue3 中使用v-slot:插槽名称
slot-scope="data"
从子组件获取数据,vue3 中在父组件中使用#data
或者#default="{data}"
获取<template>
<div>
<slot />
<slot name="slotName" />
<slot :data="user" name="propsSlot" />
div>
template>
<script>
const user = reactive({
name:'falcon',
age:20
})
script>
<template>
<Son>
<template #default><div>默认插槽内容div>template>
<template #slotName><div>具名插槽内容div>template>
<template #propsSlot="scope">
<div>
作用域插槽内容:name,{{scope.data.name}};age,{{scope.data.age}}
div>
template>
Son>
template>
<script setup>
import Son from './Son.vue'
<script>
// main.js
app.directive("focus",{
mounted(el,bingings,vnode,preVnode){
el.focus()
}
})
<template>
<div>
<input type="text" v-focus />
div>
template>
<script setup>
const vFocus = {
mounted:(el) => el.focus()
}
script>
.sync
和v-model
都是语法糖,都可以实现父子组件中数据的双向通信v-model="num"
,:num.sync="num"
;v-model:@input+value,:num.sync:@update:numv-model只能用一次,.sync可以有多个
vue3中v-model可以有多个
<template>
<p>name:{{name}}p>
<p>age:{{age}}p>
<Son v-model:name="name" v-model:age="age" />
template>
<script setup>
import Son from './Son.vue'
const user = reactive({
name:'falcon',
age:20
})
const {name,age} = toRefs(user)
script>
<template>
<input type="text" :value="name" @input="onNameInput" />
<input type="number" :value="age" @change="onAgeInput" />
template>
<script setup>
defineProps({
name:{
type:String,
default:() => ""
},
age:{
type:String,
default:() => ""
}
})
const emit = defineEmits(["update:name"],["update:age"])
const onNameInput = (e) => emit("update:name",e.target.value)
const onAgeInput = (e) => emit("update:age",e.target.value)
script>
不建议 v-for 与 v-if 一起使用
vue2 中优先级v-for 高于 v-if
。如果执行过滤列表项操作,配合computed;如果条件判断来显隐循环列表,将v-if提前,包裹v-forvue3 中优先级v-if 高于 v-for
<template>
<div v-if="flag">
<div v-for="item in dataList" :key="item.id">{{item.id}} - {{item.label}}div>
div>
template>
<script setup>
const flag = ref(true)
const dataList = reactive([
{
id:1,
label:'list-01'
},
{
id:2,
label:'list-02'
}
])
script>
单独声明优先
,并且重复定义会出发出警告就近原则
<template>
<div>
<input type="text" v-bind:disabled="false" :disabled="disabled" />
<input type="text" :disabled="disabled" v-bind:disabled="false" />
div>
template>
<script setup>
const disabled = ref(true)
script>
父组件传值,子组件通过props接受;子组件想改变父组件中数值,通过$emit调用父组件中方法
<template>
<Child :count="count" :name="name" :age="age" @add="add" @sub="sub" />
template>
<script setup>
import Child from "./Child.vue";
const count = ref(0);
const user = reactive({
name: "falcon",
age: 20,
});
const add = () => count.value++;
const sub = () => count.value--;
const { name, age } = toRefs(user);
script>
<template>
<p>接受到的参数为:name,{{ name }},age,{{ age }},count,{{ count }}p>
<el-button type="primary" size="small" @click="add">count++el-button>
<el-button type="primary" size="small" @click="sub">count--el-button>
template>
<script setup>
defineProps({
name: {
type: String,
default: () => "",
},
age: {
type: Number,
default: () => 0,
},
count: {
type: Number,
default: () => 0,
},
});
const emits = defineEmits(["add", "sub"]);
const add = () => emits("add");
const sub = () => emits("sub");
script>
传递属性或方法给子组件下级组件,传递子组件中没有被props定义的属性,传递子组件中没有被emits定义的方法
<template>
<Child :count="count" :name="name" :age="age" @add="add" @sub="sub" />
template>
<script setup>
import Child from "./Child.vue";
const count = ref(0);
const user = reactive({
name: "falcon",
age: 20,
});
const add = () => count.value++;
const sub = () => count.value--;
const { name, age } = toRefs(user);
script>
<template>
<p>子组件接收:{{ count }}p>
<el-button type="primary" size="small" @click="add">count++el-button>
<GrandChild v-bind="$attrs" />
template>
<script setup>
import GrandChild from "./GrandChild.vue";
defineProps({
count: {
type: Number,
default: () => 0,
},
});
const emits = defineEmits(["add"]);
const add = () => emits("add");
script>
<template>
<p>孙组件接受:name,{{ name }},age,{{ age }}p>
<el-button type="primary" size="small" @click="sub">count--el-button>
template>
<script setup>
defineProps({
name: {
type: String,
default: () => "",
},
age: {
type: Number,
default: () => 0,
},
});
const emits = defineEmits(["sub"]);
const sub = () => emits("sub");
script>
<template>
<Child v-model:name="name" v-model:count="count" v-model:salary="salary" />
template>
<script setup>
import Child from "./Child.vue";
const name = ref("falcon");
const count = ref(0);
const salary = ref(3000);
script>
<template>
<p>
子组件接受到的v-model参数:name,{{ name }},count,{{ count }},salary,{{
salary
}}
p>
<el-button type="primary" size="small" @click="changeCount"
>count++el-button
>
<el-button type="primary" size="small" @click="changeSalary"
>salary1000+el-button
>
template>
<script setup>
const props = defineProps({
name: {
type: String,
default: () => "",
},
count: {
type: Number,
default: () => "",
},
salary: {
type: Number,
default: () => "",
},
});
const emits = defineEmits(["update:count", "update:salary"]);
const changeCount = () => emits("update:count", props.count + 1);
const changeSalary = () => emits("update:salary", props.salary + 1000);
script>
通过ref获取指定的DOM元素或组件,结合defineExpose暴露出来的属性和方法实现通信
<template>
<div>title:{{ title }}div>
<Child ref="child" />
<el-button type="primary" size="small" @click="add">count++el-button>
<el-button type="primary" size="small" @click="sub">count--el-button>
<el-button type="primary" size="small" @click="receive"
>receive msgel-button
>
template>
<script setup>
import Child from "./Child.vue";
const child = ref(null);
const title = ref("暂无数据");
const add = () => child.value.add();
const sub = () => child.value.sub();
const receive = () => (title.value = child.value.msg);
script>
<template>
<p>子组件:count,{{ count }}p>
template>
<script setup>
const count = ref(0);
const msg = "expose message";
const add = () => count.value++;
const sub = () => count.value--;
defineExpose({
msg,
add,
sub,
});
script>
祖先向下级传递参数,无论层级多深,都可以传递
<template>
<Child />
template>
<script setup>
import Child from "./Child.vue";
const user = reactive({
name: "falcon",
age: 20,
});
provide("user", user);
script>
<template>
<p>子组件接受:name,{{ user.name }}p>
<GrandChild />
template>
<script setup>
import GrandChild from "./GrandChild.vue";
const user = inject("user");
script>
<template>
<p>孙组件接受:age,{{ user.age }}p>
template>
<script setup>
const user = inject("user");
script>
不建议使用,建议使用可组合函数完成组件间通信和复用
const count = ref(20);
const name = ref("falcon");
const addCount = () => count.value++;
const subCount = () => count.value--;
export default { count, name, addCount, subCount };
<template>
<p>name:{{ name }},count:{{ count }}p>
<el-button @click="addCount" type="primary" size="small">count++el-button>
<el-button @click="subCount" type="primary" size="small">count--el-button>
template>
<script setup>
import mixins from "./mixin";
const { count, name, addCount, subCount } = mixins;
script>
vue3 中废除api:$on
、$once
、$off
;不再支持Event Bus,选用替代方案mitt.js,原理还是Event Bus
import mitt from 'mitt';
export default mitt()
<template>
<Brother1 />
<Brother2 />
template>
<script setup>
import Brother1 from "./Brother1.vue";
import Brother2 from "./Brother2.vue";
script>
<template>
<p>brother1 发送事件p>
<el-button type="primary" size="small" @click="handleClick">发送事件el-button>
template>
<script setup>
import mybus from './bus.js';
const handleClick = () => {
mybus.emit("title",{title:"hello world"});
mybus.emit("user",{user:{name:"falcon",age:20}})
}
script>
<template>
<p>brother2 接受事件p>
<p>title:{{title}}p>
<p>user:name,{{name}};age,{{age}}p>
template>
<script setup>
import mybus from './bus.js'
const title = ref("")
const user = reactive({
name:"",
age:null
})
mybus.on("title",(data) => {
title.value = data.title
})
mybus.on("user",(data) => {
user.name = data.user.name
user.age = data.user.age
})
script>
pinia 是 vue 的存储库,允许跨组件/跨页面共享状态
。具有以下优点:
去除Mutation,Actions支持同步和异步
store仅在需要时才自动注册
支持模块热更新
import { createPinia } from 'pinia'
const store = createPinia()
export default store
// 引入store定义函数
import { defineStore } from 'pinia'
// 定义store实例并导出
// 第一个参数,字符串类型,唯一不可重复,作为库id来区分不同库
// 第二个参数,以对象形式配置存储库的state、getters、actions
export const useStore = defineStore('useCount',{
/**
state,存储全局状态
必须是箭头函数:为了在服务器端渲染的时候避免交叉请求导致数据状态污染
*/
state:() => {
return {
count:0
}
},
/**
getters,封装计算属性
具有缓存功能,类似于computed;需要传入state才能拿到数据;不能传递任何参数,但是可以返回一个函数接受任何参数
*/
getters:{
doubleCount:(state) => state.count * 2,
powCount(){
return this.doubleCount ** 2
}
},
/**
actions,编辑业务逻辑
类似于methods,支持同步和异步;获取state里的数据不需要传入直接使用this
*/
actions:{
addCount(){
this.count++
},
subCount(){
this.count--
}
},
/**
配置数据持久化需要进行的操作
*/
persist:{}
})
<template>
<p>{{useStoreDemo.count}}<p>
<p>{{useStoreDemo.doubleCount}}</p>
<p>{{useStoreDemo.powCount}}</p>
<el-button @click="toAdd">count++</el-button>
<el-button @click="toSub">count--</el-button>
</template>
<script setup>
import {useStore} from '../store'
const useStoreDemo = useStore()
// 也可以解构出来想要使用的count,但直接解构不具有响应式,想要具有响应式,可以执行如下操作:
const {count} = storeToRefs(useStore())
const toAdd = () => useStoreDemo.addCount()
const toSub = () => useStoreDemo.subCount()
<script>
pinia的数据是存储在内存中的,页面刷新后数据会丢失;可以支持扩展插件,实现数据持久化
npm i pinia-plugin-persist
,默认使用sessionStoragepersist:{
enabled:true,
strategies:[
{
storage:localStorage,
paths:["num","user"]
}
]
}
params中配置path无效
query传参刷新页面数据不会消失,params传参刷新页面数据消失
动态参数会显示在地址栏中,且刷新页面数据不会消失
严格区分大小写
router.go(1)
、后退router.go(-1)
、刷新router.go(0)
<template>
<el-button @click="TransByQuery">通过query传参</el-button>
<el-button @click="TransByParams">通过params传参</el-button>
<el-button @click="TransByDynamic">动态传递参数</el-button>
</template>
<script setup>
const queryParams = reactive({
name:'falcon',
age:20
})
const id = ref('2023')
const router = useRouter()
const TransByQuery = () => {
router.push({
path:'/basic/querydemo',
query:queryParams
})
}
const TransByParams = () => {
router.push({
name:'ParamsDemo',
params:queryParams
})
}
const TransByDynamic = () => {
router.push({
name:'DynamicDemo',
params:{id:id.value}
})
}
<script>
const route = useRoute()
console.log(route.query.name,route.query.age)
const route = useRoute()
console.log(route.params.name,route.params.age)
const route = useRoute()
console.log(route.params.id)
{
name: "QueryDemo",
path: "querydemo",
redirect: null,
component: "basic/userouter/querydemo",
hidden: true,
meta: {
title: "query样例",
icon: null,
},
},
{
name: "ParamsDemo",
path: "paramsdemo",
redirect: null,
component: "basic/userouter/paramsdemo",
hidden: true,
meta: {
title: "params样例",
icon: null,
},
},
{
name: "DynamicDemo",
path: "dynamicdemo/:id",
redirect: null,
component: "basic/userouter/dynamicdemo",
hidden: true,
meta: {
title: "dynamic样例",
icon: null,
},
},
>>> className
,less /deep/ className
, scss ::v-deep className
:deep(className)
<template>
<div class="name">falcondiv>
template>
<script setup>
const str = ref('#f00')
script>
<style lang="scss" scoped>
.name{
background-color:v-bind(str)
}
style>