The way vue2 vue3 components pass values

How to pass values ​​between components

Summarize

  • Parent component to child component
    • Parent components pass values ​​by setting properties when using child components, and child components use props to receive
  • Child component to parent component
    • Custom event The parent component passes the custom event to the child component, and the child component emit accepts

What is unidirectional data flow

  1. What is one-way data flow
    ? Data flow refers to the flow of data between components. One-way data flow means that data can only be passed from the parent component to the child component. The child component cannot change the props of the parent component . If you want to modify it, there are other ways.
  2. Why can't it be two-way? When
    the data of the parent component changes, it will notify the child component to update automatically through props. Prevent data confusion when multiple child components try to modify the state of the parent component

Benefits of Unidirectional Data Flow

  • One-way data flow makes all state changes recordable and traceable, and the source is easy to trace;
  • There is only one copy of all data, and only one entry and exit for component data, which makes the program more intuitive and easier to understand, and is conducive to the maintainability of the application.

Pass value from parent component to child component

Method 1: props

General instructions
1. props is a read-only property, and subcomponents cannot modify the passed in value.
2. The value passed in the props method is a shallow copy

Transfer method
1. Use child components on the parent component to pass values ​​through the properties of the child components.
2. The child component propsaccepts the value passed from the parent component.

options API writing method

  • Subcomponents propsreceive data through parameters, and the data is first set on the vc component (vc.data)
    • If the subcomponent is not used , the passed attribute will be stored in propsthe subcomponent , and will not be stored on the vc instance of the subcomponent.$attrs
    • If the subcomponent is used props, the passed attribute will be directly stored on the vc instance of the subcomponent, and $attrswill not be stored in the subcomponent
//1.父组件通过属性传值
<Student :name=name :sex=sex :age=age/>
//option API
//写法1:子组件接收值,简单接收
props:['name','sex','age']

//写法2:子组件接收值,设置传来值的类型
props:{
    
    
	name:String,
	sex:String,
	age:Number	
}

//写法3: 子组件接收值,完整写法
props:{
    
    
	name:{
    
    
		type:String,
		required:true //是否必须
		 default: "table" 
	},
	age:{
    
    
		type:Number,//类型
		default:99 //默认值
	},
	 rowClick: {
    
    
      type: Function,
      default: () =>{
    
    }
    },
     columns: {
    
    
      type: Array,
      default:() =>[]
    },
    api: {
    
    
      type: Object,
      default:()=>({
    
    })
    },
}
default default value

1. When using default to define the default value, if the parent component has a passed value, it will be rendered with the parent value. If the parent component does not pass a value, the default value will be used.
2. When no default value is defined, if the parent component has a value, it will be rendered with the parent value. If the parent component does not pass a value, the default value of that type is used.

String ''
Number 0
Array []
Object {
    
    }

The default value of props will only be read when no parameters are passed in. If an incomplete object is passed in, the default value will not be filled for the properties of the object that have no value

insert image description here

All props default values ​​(vue2 and vue3) need to be written
Basic data type: direct assignment
Object data type: use function assignment()=>{}

If {} and [] are used directly, when multiple parent components using this component do not pass props and use the default value. Assuming that one of the parent components modifies the default value (not recommended, a warning will be reported), other parent components will also change because they point to the reference address in the same memory.
Use the form of a function to return, to ensure that each time the function is executed, a new object is returned.

composition API | defineProps compile macro

defineProps()It can be used directly without definition and reference
Return value: returns a proxy object composed of accepted properties

illustrate

  • definePropsand are compiler macros that defineEmitscan only be used in . <script setup>They don't need to be imported, and will <script setup>be compiled along with the processing of .
  • definePropsor defineEmitsEither use a runtime declaration or a type declaration. Using both declaration methods at the same time will cause compilation errors.
  • When used in templates, props can be omitted .
//第一种,简单接收
const props = defineProps(["name"]);
//第二种设置接收的类型
defineProps({
    
    
  page:Number
});
//第三种设置接收的类型和默认值
defineProps({
    
    
  page:{
    
    
    type:Number,
    default:2
  }
});
//第四种设置传来值的多种类型
defineProps({
    
    
  page:[String,Number]
})

The runtime declaration refers to the declaration of the type of props. This kind of declaration cannot be detected and given by IED. The prompt will only be given after running. The props API belongs to the runtime declaration.
Type declaration Here type declaration refers to ts-based type checking, type constraints on props, and support for IDE type inference and checking. Therefore, to use type declarations, one needs to be based on ts, i.e. <script setup lang="ts">

How to write composition API runtime declaration


const props = defineProps({
    
    
foo: String,
bar: {
    
    
  type: Number,
  required: true
}
})
</script>

How to write composition API type declaration

<script setup lang='ts'>
interface List {
    
    
    id: number,
    content: string
}

const props = defineProps<{
    
    
  foo?: string
  list: List // 接口
}>()
</script>
Default value for props type declaration | widthDefaults compiled macro
<script setup lang="ts">
interface Props {
    
    
        msg?: string
        labels?: string[]
 }
 const props = withDefaults(defineProps<Props>(), {
    
    
       msg: 'hello',
       labels: () => ['one', 'two']
 })
</script>

Method 2: Attributes and events on components

vue2 $attrs + $listeners

When multi-layer nested components pass data, you can use this if you just pass the data without intermediate processing, such as when the parent component passes data to the grandchildren component

  • $attrs: Contains a collection of non- properties in the parent scope except classand . By getting all the qualified attribute collections in the parent scope, and then continuing to pass them to other components inside the child component, you can passstylepropsthis.$attrsv-bind="$attrs"
  • $listeners: Contains .nativea collection of listening events except in the parent scope. If you want to continue to pass to other components inside the subcomponent, you can passv-on="$linteners"

Vue3 useAttrs method

useAttrs(): Get the properties and events on the component. Only non- property collections except classand in the parent scope can be accepted . Both native DOM events and custom events can be accepted.styleprops

<template>
	<el-button :="$attrs"></el-button>
</template>

<script setup lang="ts">
import {
    
     useAttrs } from 'vue';
let $attrs  = useAttrs(); //返回的attrs是一个响应式代理对象 
</script>

:="$attrs"The meaning of the writing method is to deconstruct $attrsthe object, keyto be the attribute name, and valueto be the attribute value

<h1 v-bind="{a:1,b:2}">123</h1>
//页面中显示
<h1 a="1" b="2">123</h1>

Method 3: $parent + defineExpose Compilation macro | Get the parent component instance inside the child component

$parentThe instance of the parent component can be obtained inside the child component

//子组件
<button @click="handler($parent)"></button>

//父组件暴露
defineExpose({
    
    })

Components using script setup are closed by default , and the instance of the component obtained through template ref or $parent chain will not expose any data declared in script setup.
Need to use defineExpose compiler macro to expose data

Method 4: Slot Structure Parent -> Child

After the component is referenced by default, the parent component template uses the component, and the structure inside the component's opening and closing tags will not be rendered. If you want to render it, that is, pass the html structure from the parent component to the child component, you can use slots.

Slot function : Allow the parent component to insert the html structure to the specified position of the child component

Type of slot

  • Default slot: The parent component passes the html structure to the child component (equivalent to the child component leaving an html pit, and the parent component passes the html to fill the pit).
  • Named slot (named by the name attribute of the slot tag): The parent component decides which slot to pass the html structure to.
  • Scoped slots: The parent component passes the html structure to the child component, and the child component passes data back to the parent component. The data is in the child component, but the structure generated according to the data needs to be determined by the parent component

Default slots
There can be multiple default slots for the same component

//Parent.vue
<Child>
<div>默认插槽</div>
</Child>

//Child.vue
<slot></slot> //默认插槽 <div>默认插槽</div>
<slot></slot>

Named slot
Use <slot name="xxx">to name the slot, and the parent component chooses which slot to insert (①②both are written in vue3)
①Use <template v-slot:xxx>
②abbreviated form<template #xxx>

There can be multiple title slots of the same component

//Child.vue
<slot name="a"></slot>

//Parent.vue
<template #a>
    <div>我是填充具名插槽a位置结构</div>
</template>

Scope slot
Scope slot: data: child -> parent structure: parent -> child
Usage scenario: the component where the data is in the slot, but the structure generated according to the data needs to be determined by the parent component.

The data is in the child component (scope), and the structure is passed by the parent component.

Step
1. <slot :xxxx="yyy">Pass the data to the parent component, the fixed way of writing the slot. Pass an object, the key is xxx, and the value is yyy.
2. The parent component uses the role slot <template v-slot="{xxxx}">, which needs to be deconstructed here

<Child :todos="todos">
	//v-slot接受子组件传递回来的数据
   <template v-slot="{ row, index }">
     <p :style="{ color: row.done ? 'green' : 'red' }">
      {
    
    {
    
    row.title }}--{
    
    {
    
     index }}
     </p>
	</template>
</Child>
let todos = ref([
  {
    
     id: 1, title: "ranran", done: true },
  {
    
     id: 2, title: "ranran1", done: false },
  {
    
     id: 3, title: "ranran2", done: true },
  {
    
     id: 4, title: "ranran3", done: false },
]);


//Child.vue
//子组件回传数据给父组件
<ul>
   <li v-for="(item, index) in todos" :key="item.id">
    <!--作用域插槽:可以讲数据回传给父组件-->
    <slot :row="item" :index="index"></slot>
   </li>
</ul>

<script setup lang="ts">
//通过props接受父组件传递数据
defineProps(["todos"]);
</script>

Passing from child component to parent component

Custom events and native DOM events

// 在vue2中,这种写法为自定义事件
// 在vue3中,这种写法为原生DOM事件
<Child @click=""></Child>
  • When a component binds an event, vue2 binds a custom event by default. In vue2, .nativeidentifiers can be used, for example @click.native, to turn custom events into native DOM events and bind events to the root node of the subcomponent.
  • Vue3's native DOM events are native events no matter they are placed on tags or components.
    Although vue3's native DOM event binding is also a native event on the component, as long as the child component accepts defineEmitsthe function passed by the parent component, the event will be triggered as a custom event.

Method 1: custom event/native DOM event + emit method

options API writing method | $emit()

  1. Bind the custom event to the instance vc of the child component, the callback function is in the parent component, and the parameters are received through the callback function
  • Writing method 1: Use in the parent component @or v-on:bind the callback function to the vc of the child component@自定义事件=’事件回调'
  • this.$refs.xxxWriting method 2: Obtain the instance of the child component in the parent component , and use $on(‘事件名’,回调函数)the binding custom event
  1. The subcomponent $emit()triggers a custom event and passes parameters, and the subcomponent this.$off()unbinds the custom event
//父组件
<template>
  <div>
    <h1>我是父组件</h1>
    <Son :info="info" @change="fn"></Son>
  </div>
</template>

<script>
import Son from "./Son.vue";
export default {
    
    
  data() {
    
    
    return {
    
    
      info: "我是父组件中的数据",
    };
  },
  components: {
    
    
    Son,
  },
  methods: {
    
    
    fn(info) {
    
    
      this.info = info +  "我是父组件中点击修改后的数据";
    },
  },
};
</script>

//子组件
<template>
  <div>
    <h2>我是子组件</h2>
    <p>{
    
    {
    
     info }}</p>
    <button @click="fn">修改数据(子)</button>
  </div>
</template>

<script>
export default {
    
    
  props: ["info"], //父传子
  methods: {
    
    
    fn() {
    
    
      //这种直接赋值prop是不可取的,vue会直接报错
      //this.info=this.info+"子组件直接赋值prop"
      // 修改数据
      this.$emit('change',this.info + ",我现在被子组件emit了"); //触发自定义事件并传值,父组件自定义事件的回调函数触发
    },
  },
};
</script>

composition API writing method | defineEmits compile macro

defineEmits accepts the custom function passed by the parent component
Return value: (event, ...args) => instance.emit(event, ...args)returns a function, the first parameter is the name of the custom event, and the second parameter begins with the parameter passed to the custom function.

How to write a runtime statement

//子组件
<template>
  <button @click="butFn">改变page值:{
    
    {
    
    page}}</button>
</template>


<script setup>
import {
    
     defineEmits } from "vue";
const emit = defineEmits(["pageFn"]);   //定义一个变量来接收父组件传来的方法
const butFn=()=>{
    
    
    emit("pageFn",5)//触发该方法,5为传递的参数
}
</script>

How to write composition API type declaration

const emit = defineEmits<{
    
    
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()

// 3.3+:另一种更简洁的语法
const emit = defineEmits<{
    
    
  change: [id: number] // 具名元组语法
  update: [value: string]
}>()

Method 2 ref + defineExpose compile macro | parent component gets value/method of child component

refYou can get the real DOM node, and you can also get the instance of the subcomponent VC
$parentcan get the instance of the parent component inside the subcomponent

Get the instance of the child component in the setup
1. Set the child component property ref in the parent component
2. Accept the variable with the same name

//父组件
<Son ref='son'> </son>

<script setup lang="ts">
const son= ref(null);
</script>

There is a problem: refthe subcomponent instance can be obtained, but the subcomponent data cannot be obtained.
Reason: the internal data of the component is closed to the outside world.
Solution: the component defineExpose()exposes the value or method to the outside world

//子组件暴露
defineExpose({
    
    
  money,
})
//父组件使用
<Son ref='son'> </son>

<script setup lang="ts">
const son= ref(null);
conselo.log(son.value);
</script>

Parent-child component data synchronization

The principle of vue2 v-model

v-model grammatical sugar = props (parent to child) + binding event listener @input/trigger event listener emit (child to father)

Implementation principle of v-model
The attribute passed by default is value, and the custom event name is input

<!--v-model简写-->
<Son v-model="msg" />
<!--原始写法-->
<Son :value="msg" @input= "val => msg=val "/>

text and textarea elements use value property and input event;
checkbox and radio use checked property and change event;
select field use value property and change event.

Vue2 sync modifier principle

sync modifier = props (parent to child) + binding event listener @update: property name/trigger event listener emit (child to father)

.syncModifier can achieve v-modelthe same function as and, and it is v-modelmore flexible than.
v-modelA component can only use one .syncand can have multiple.

xxx.syncPrinciple
①The parent component passes props to the child component: property name
②A custom event is bound to the current child component, the event name is the value update:属性名that the event will updatexxx

// 正常父传子: 
<son :info="str" :title="str2"></son>

// 加上sync之后父传子(.sync没有数量限制): 
<son :info.sync="str" .title.sync="str2"></son> 

// 它等价于
<son
:info="str" @update:info="val=>str=val"
:title="str2" @update:title="val=>str2=val">
</son> 

//子组件emit触发事件
this.$emit('update:info',this.info + ",我现在被子组件emit了")

vue3 v-model principle

The principle of vue3 v-modelis the principle of modifiers in vue2 sync, and the characteristics are the same. One component can have multiple v-model.

Bind multiple v-models: v-model:value = 值, each v-model does not affect each other.

<Child1 v-model:pageNo="pageNo" v-model:pageSize="pageSize"></Child1>

Ancestor components pass values ​​to descendant components

provide/inject

  • provideProvide data: specify the data or methods you want to provide to descendant components
  • injectinjectInject data into components: Use the received data or methods in any descendant components provide, no matter how deep the components are nested, they can be used directly

Use in vue2

injectdata/propsInitialize before, initialize provideafter data/props, when injecting content, inject the content into the current vc, __provideconfigure
injectthe key to read the content in the current component first ( __provide), if it cannot be read, read it from its parent component, find the final content and save it to the current instance (vc), so that the content injected by inject can be read directly through this.

Vue does not respond to variables in provide. Therefore, in order for the variables accepted by inject to be responsive, the variables provided by provide themselves need to be responsive.

// 父组件
export default {
    
    
  provide: {
    
    
    name: "父组件数据",
    say() {
    
    
      console.log("say say say");
    },
  },
  // 当需要用到this的时候需要使用函数形式,调用时this指向当前组件
  provide() {
    
    
    return {
    
    
      todoLength: this.todos.length
    }
  },
}

// 子组件
<template>
  <div>
    <div>provide inject传递过来的数据: {
    
    {
    
     name }}</div>
    <div>provide inject传递过来的方法<button @click="say">say</button></div>
  </div>
</template>

<script>
export default {
    
    
  inject: ['name', 'say'],
  },
}
</script>

Use in vue3

  • provide()method, the first parameter needs to be a unique identifier (the same name as the variable inside the component is not allowed), and the second parameter is the data (value/method) provided by the ancestor component
  • inject()method, the first data is the identity, and the data provided by the ancestor component is obtained through the identity, and the second parameter is the default value. If the identity data is not obtained from the ancestor component, the default value will be used.
//祖先组件
<script setup lang="ts">
import {
    
    provide] from 'vue'
const age = ref(18);
provide("keyName",age )
</script>

//孙子组件
<script setup lang="ts">
import {
    
    inject} from 'vue';
let car = inject('keyName'); // car为ref引用对象
console.log(car.value)
const updateCar = ()=>{
    
    
   car.value  = '自行车';
}
</script>

Explanation
1. In the above case, the data received in the grandchild component and the data injected by the ancestor component point to the same ref reference object . So modifying the car in the grandson component will also modify the data with the same name in the ancestor component. Modify the value in the ancestor component, and the grandson component will also respond.

2. Used in the template, you can directly use the parameter identification

<script setup lang="ts">
import {
    
    inject} from 'vue';
</script>
<template>
	<span>{
    
    {
    
    keyName}}</span>
</template>

Guess you like

Origin blog.csdn.net/qq_41370833/article/details/132484031