046Vue3的官方推荐的三个组件传值解决方案:props、pinia(状态管理)、provide和inject
props
< script setup >
const props = defineProps ( [ 'foo' ] )
const props = defineProps ( {
foo : String
} )
console. log ( props. foo)
</ script>
< template>
< div> {{ foo }}</ div>
</ template>
props单向数据流
所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。
prop 被用于传入初始值;而子组件想在之后将其作为一个局部数据属性。在这种情况下,最好是新定义一个局部数据属性,从 props 上获取初始值即可:
const props = defineProps ( [ 'initialCounter' ] )
const counter = ref ( props. initialCounter)
需要对传入的 prop 值做进一步的转换。在这种情况中,最好是基于该 prop 值定义一个计算属性:
const props = defineProps ( [ 'size' ] )
const normalizedSize = computed ( ( ) => props. size. trim ( ) . toLowerCase ( ) )
Prop 校验
defineProps ( {
propA : Number,
propB : [ String, Number] ,
propC : {
type : String,
required : true
} ,
propD : {
type : Number,
default : 100
} ,
propE : {
type : Object,
default ( rawProps) {
return { message : 'hello' }
}
} ,
propF : {
validator ( value ) {
return [ 'success' , 'warning' , 'danger' ] . includes ( value)
}
} ,
propG : {
type : Function,
default ( ) {
return 'Default function'
}
}
} )
pinia(状态管理)
import { defineStore } from 'pinia'
export const useTodos = defineStore ( 'todos' , {
state : ( ) => ( {
todos : [ ] ,
filter : 'all' ,
nextId : 0 ,
} ) ,
getters : {
finishedTodos ( state ) {
return state. todos. filter ( ( todo ) => todo. isFinished)
} ,
unfinishedTodos ( state ) {
return state. todos. filter ( ( todo ) => ! todo. isFinished)
} ,
filteredTodos ( state ) {
if ( this . filter === 'finished' ) {
return this . finishedTodos
} else if ( this . filter === 'unfinished' ) {
return this . unfinishedTodos
}
return this . todos
} ,
} ,
actions : {
addTodo ( text ) {
this . todos. push ( { text, id : this . nextId++ , isFinished : false } )
} ,
} ,
} )
pinia的封装与引入
import { toStr } from '@/utils/Json' ;
import { createPinia, type PiniaPluginContext } from 'pinia' ;
import { toRaw } from 'vue' ;
const pinia = createPinia ( ) ;
interface IStorageOption {
key : string;
keeps : {
sessions : string[ ] ;
locals : string[ ] ;
} ;
}
const storageOption : IStorageOption = {
key : '__pinia__' ,
keeps : {
sessions : [ ] ,
locals : [ 'model' , 'model00411' ] ,
} ,
} ;
function setStorage (
key : string,
val : any,
storageType : Storage = localStorage
) {
const obj = { v : val } ;
console. log ( 666.789 , key) ;
storageType. setItem ( storageOption. key + key, toStr ( obj) ) ;
}
function getStorage ( key : string, storageType : Storage = localStorage) {
const val = storageType. getItem ( storageOption. key + key) ;
const obj = val ? JSON . parse ( val as string) : null ;
return obj?. v || null ;
}
function doStorage (
key : string,
store : any,
storageType : Storage = localStorage
) {
const storageResult = getStorage ( key, storageType) ;
if ( storageResult) {
console. log ( 666.7007 , '读取啦' , storageResult, key, toRaw ( store. $state) ) ;
store. $patch ( ( ) => {
store. $state = { ... storageResult } ;
} ) ;
}
store. $subscribe ( ( ) => {
console. log ( 666.7002 , '更新啦' , key, toRaw ( store. $state) ) ;
setStorage ( key, toRaw ( store. $state) , storageType) ;
} ) ;
}
function useStorage ( ) {
return ( ctx : PiniaPluginContext) => {
const { store } = ctx;
const sid = store. $id;
if ( storageOption. keeps. locals. includes ( sid) ) {
doStorage ( sid, store, localStorage) ;
} else if ( storageOption. keeps. sessions. includes ( sid) ) {
doStorage ( sid, store, sessionStorage) ;
}
} ;
}
pinia. use ( useStorage ( ) ) ;
export default pinia;
引入
import { createApp } from 'vue' ;
import App from './App.vue' ;
import pinia from './stores' ;
const app = createApp ( App) ;
app. use ( pinia) ;
app. mount ( '#app' ) ;
项目中使用方法
import { defineStore } from 'pinia' ;
const modelStoreTmp = {
type : 1 ,
data : {
val : 3 ,
item : { a : 1 , b : 2 } ,
} ,
} ;
export default defineStore ( 'piniaName' , {
state : ( ) => {
return {
modelStore : modelStoreTmp,
} ;
} ,
actions : { } ,
} ) ;
import ModelStore from './store' ;
const modelStore = ModelStore ( ) . modelStore;
provide和inject
Provide (提供)
import { provide } from 'vue'
provide ( 'message' , 'hello!' )
Inject (注入)
import { inject } from 'vue'
const message = inject ( 'message' , '这是默认值' )
建议尽可能将任何对响应式状态的变更都保持在供给方组件中
import { provide, ref } from 'vue'
const location = ref ( 'North Pole' )
function updateLocation ( ) {
location. value = 'South Pole'
}
provide ( 'location' , {
location,
updateLocation
} )
< script setup >
import { inject } from 'vue'
const { location, updateLocation } = inject ( 'location' )
</ script>
< template>
< button @click = " updateLocation" > {{ location }}</ button>
</ template>
三种方式的传值,按需使用
父子组件使用props 有层级关系的多个组件provide 跨无关联组件使用pinia