Vue3.x 新特性总结

在这里插入图片描述

Vue3.x 官网


Vue3 与 Vue2

Vue3相较于Vue2在多个方面都有所改进和提升,主要优势:

  1. 更好的性能

    • Vue3通过重写响应式系统,使用Proxy替代了Vue2的Object.defineProperty。这使得Vue3在数据响应式处理上更加高效,特别是在处理复杂数据结构和嵌套属性时,性能提升更为显著。(Vue双向绑定原理

    • Vue3还通过静态标记树摇(tree-shaking) 优化等手段来减小打包体积,从而提高了页面加载速度。这意味着在构建大型应用时,Vue3能够提供更快的启动时间和更流畅的用户体验。

      • 静态标记:主要目的是将模板中的静态节点(不会改变的节点)与动态节点(可能会改变的节点)进行区分。通过区分静态和动态节点,Vue 3 可以在渲染过程中跳过静态节点的重新渲染,从而避免不必要的计算和 DOM 操作,提高渲染性能。
      • 树摇(tree-shaking):未使用的代码可以被自动排除,仅打包需要的,从而减小最终的打包体积。
        • Vue 2使用的是CommonJS,不支持tree-shaking
        • Vue 3被重写为使用ES Modules,支持Tree Shaking
          .
  2. 更简洁的API设计

    • Vue3引入了Composition API,通过setup函数来组织组件的逻辑。这使得代码更加模块化,易于理解和维护。例如,你可以将组件的状态、计算属性和方法都放在setup函数中,通过返回的对象暴露给模板使用。
      .
  3. 引入组合API

    • 组合API允许你更灵活地组织和复用代码。例如,你可以使用refreactive来创建响应式数据,使用computed来创建计算属性,使用watchEffect来监听响应式数据的变化。这些API使得你可以更加直观地处理组件的逻辑,提高代码的可读性和可维护性。
      .
  4. 更好的TypeScript支持

    • Vue3从一开始就以TypeScript编写,在底层实现了更好的TypeScript支持,提供了类型定义文件和类型推导,更好的TypeScript支持,减少了类型错误,并提供了更好的类型推导。这意味着你可以在Vue3组件中使用TypeScript来编写代码,享受类型安全和自动补全等特性。这有助于减少类型错误,提高代码质量。
      .
  5. 支持Fragment和Teleport

    • Fragment允许组件有多个根节点,这使得你可以更灵活地组织模板结构。例如,你可以在一个组件中返回多个并列的元素,而不需要将它们包裹在一个额外的父元素中。
    • Teleport可以将组件的部分模板“传送”到DOM结构的最外层,这对于实现如模态框、通知等需要脱离当前组件层级显示的功能非常有用。
      .
  6. 自定义渲染器API

    • Vue3提供了自定义渲染器API,允许开发者根据需求创建自己的渲染逻辑。这可以用于实现特殊的渲染效果、优化性能或集成第三方库等场景,增加了开发的灵活性和扩展性。
      .
  7. 更好的模块化

    • Vue3引入tree-shaking对内部模块进行了更好的分离,可以将无用模块“剪辑”,仅打包需要的,使打包的整体体积变小了。这意味着在构建应用时,未使用的代码可以被自动排除,从而减小最终的打包体积。这有助于减少不必要的代码和资源加载,提高应用的加载速度和性能。

一、Composition API

Vue2 时的方式在代码很少的时候,逻辑结构还是蛮清晰的,但是随着组件功能越来越多,代码量越来越大,整体内容全部放在其中肯定会显得臃肿。因为每个功能模块的代码会散落分布在各个位置,让整个项目的内容难以阅读和维护。如下图:

在这里插入图片描述

而到了 Vue3,它会根据逻辑功能来进行组织,把同一个功能的不同代码都放在一起,或者把它们单独拿出来放在一个函数中,所以 Composition API 又被称为基于函数组合的API

在这里插入图片描述
相同功能的代码编写在一块,而不像options API那样,各个功能的代码混成一块

下面举个简单例子,将处理count属性相关的代码放在同一个函数

function useCount() {
    let count = ref(10);
    let double = computed(() => {
        return count.value * 2;
    });

    const handleConut = () => {
        count.value = count.value * 2;
    };

    console.log(count);

    return {
        count,
        double,
        handleConut,
    };
}

组件中使用count

export default defineComponent({
    setup() {
        const { count, double, handleConut } = useCount();
        return {
            count,
            double,
            handleConut
        }
    },
});

可以看到,整个数据来源清晰了,即使去编写更多的 hook 函数,也不会出现像mixin命名冲突的问题


1. setup 函数

setup 函数是 Vue3 中新增的函数,它是我们在编写组件时,使用 Composition API 的入口。

同时它也是 Vue3 中新增的一个生命周期函数,会在 beforeCreate 之前调用。此时组件的 datamethods 还没有初始化,因此在 setup 中是不能使用 this。所以 Vue 为了避免我们错误的使用,它直接将 setup 函数中的 this 修改成了 undefined 。并且,只能同步使用setup函数,不能用async将其设为异步。

setup 函数接收两个参数 propscontext, 语法为:setup(props,context){}

props

props 里面包含父组件传递给子组件的所有数据。在子组件中使用 props 进行接收。

props 是响应式的, 当传入新的 props 时,会及时被更新。
由于是响应式的, 所以 props不可以直接使用 ES6 解构,解构会消除它的响应式。 可以使用toRefs来解构

父组件:

<template>
	<!-- 父组件向子组件传递数据 -->
	<Sub :name="name" :age="age" />
</template>

<script>
import { ref } from 'vue'
import Sub from './Sub.vue'
export default {
	setup () {
		const name = ref('张三');
		const age = ref(20)

		return { name, age }
	},
	components: { Sub },
}
</script>

子组件(Sub.vue):

<template>
	<div>{{name}}{{age}}</div>
</template>

<script>
export default {
	props: {
		name: String,
		age: Number
	},
	mounted () {
        // vue2.x 的写法
		console.log(this.name); // 张三
		console.log(this.age); // 20
	},
	setup (props) {
        // vue3.x 的写法
		console.log(props.name); // 张三
		console.log(props.age); // 20
		
		// let { name ,age } = props;  // 不能直接解构,会消除它的响应式
		let { name, age } = toRefs(props); // 使用toRefs来解构
		console.log(name.value, age.value); // 张三 20
	}
}
</script>

context

context 里面包含 attrs, slots, emit 等数据方法:

  • attrs:获取组件上的属性
  • slots:获取 slot 插槽的节点
  • emit :emit 方法(子组件向父组件传递数据

父组件:

<template>
	<Sub subData="some other data" @subClick='subClick'>parent</Sub>
</template>

<script>
import Sub from './Sub.vue'
export default {
	setup () {

		function subClick (e) {
			console.log(e); // 接收子组件传递过来的数据
		}

		return { subClick }
	},
	components: { Sub },
}
</script>

子组件(Sub.vue):

<template>
	<!-- 父组件向子组件传递数据 -->
	<div @click="handleClick">Child</div>
</template>

<script>
export default {
	mounted () {
        // vue2.x 获取组件上的属性
        console.log(this.$attrs.subData);  // 'some other data'
        
		// vue2.x 获取slot插槽的节点
		console.log(this.$slots);

	},
	methods: {
		// vue2.x emit方法(子组件向父组件传递数据)
		handleClick () {
			this.$emit('subClick', 'vue2.x - this is subData')
		},
	},
	setup (props, context) {
		let { attrs, slots, emit } = context;

		// vue3.x 获取组件上的属性
		console.log(attrs.subData);  // 'some other data'

		// vue3.x 获取slot插槽的节点
		console.log(slots.default());

		// vue3.x emit方法(子组件向父组件传递数据)
		function handleClick () {
			emit('subClick', 'vue3.x - this is subData');
		}

		return { handleClick }
	}
}
</script>

<script setup> 语法糖

新版vue3的setup语法糖更为简洁,详见 Vue3 script setup 语法糖,超爽体验

  • defineProps:用于接收父组件传递的数据
  • defineEmits:用于子组件向父组件传递数据
  • defineExpose:用于显式地对外暴露数据、方法
<script setup>

	// defineProps:用于接收父组件传递的数据
	const props = defineProps({
	    name: {
	        type: String,
	        default: ''
	    },
	    age: {
	        type: Number,
	        default: 20
	    }
	});


	// defineEmits:用于子组件向父组件传递数据
	const emit = defineEmits(['updateData']);  
	function updateValue(newValue) {  
	  emit('updateData', newValue);  
	}  


	// defineExpose:用于显式地对外暴露数据、方法
	const count = ref(0);
	const exposeCount = () => {
		console.log('外部组件可以直接调用此方法')
		// 在父组件中使用:const getCount = () => refSetupOperation.value.exposeCount ();
	};
	defineExpose({ count ,moreShow });
	
</script>

2. 生命周期

Vue3.x 生命周期

setup 函数是 Vue3 中新增的一个生命周期函数

  • setup 函数会在 beforeCreate 之前调用,因为此时组件的 datamethods 还没有初始化,因此在 setup 中是不能使用 this
  • 所以 Vue 为了避免我们错误的使用,它直接将 setup 函数中的 this 修改成了undefined
  • setup函数,只能是同步的不能是异步
Vue2.x (Option API)Vue3.x (Composition API)
beforeCreatesetup
createdsetup
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyonBeforeUnmount
destroyedonUnmounted
activatedonActivated
deactivatedonDeactivated
errorCapturedonErrorCaptured
- -onRenderTracked
- -onRenderTriggered

初始化加载顺序:

setup => beforeCreate => created => onBeforeMount => onMounted

<script>
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onRenderTracked, onRenderTriggered } from 'vue'
export default {
	setup () {
		console.log('setup');
		
		// 生命周期钩子(没有beforeCreate和created)
		onBeforeMount(() => { console.log('onBeforeMount'); })
		onMounted(() => { console.log('onMounted'); })
		onBeforeUpdate(() => { console.log('onBeforeUpdate'); })
		onUpdated(() => { console.log('onUpdated'); })
		onBeforeUnmount(() => { console.log('onBeforeUnmount'); })
		onUnmounted(() => { console.log('onUnmounted'); })

		// 新增的debug钩子  生产环境中会被忽略
		onRenderTracked(() => { console.log('onRenderTracked'); }) // 每次渲染后重新收集响应式依赖,在onMounted前触发,页面更新后也会触发
		onRenderTriggered(() => { console.log('onRenderTriggered'); }) // 每次触发页面重新渲染时的自动执行,在onBeforeUpdate之前触发
	},
	beforeCreate () {
		console.log('beforeCreate');
	},
	created () {
		console.log('created');
	}
}
</script>

3. 返回值

setup 函数中返回一个对象,可以在模板中直接访问该对象中的属性和方法。

<template>
	<div @click="handleClick">{{name1}} - {{name2}}</div>
</template>

<script>
import { ref, provide } from 'vue'
export default {
	setup () {
		const name1 = ref('张三');
		
		return {
			name1,
			name2: 'zhangsan',
			handleClick () {
				console.log(name1.value); // 张三
				console.log(this.name2); // zhangsan
			}
		}
	}
}
</script>

使用<script setup> 语法糖无需通过 return 语句暴露给模板:

<template>
	<div @click="handleClick">{{name1}} - {{name2}}</div>
</template>

<script setup>
import { ref } from 'vue'

const name1 = ref('张三');
const name2 = ref('zhangsan');
const handleClick  = ()=>{
	console.log(name1.value); // 张三
}
</script>

4. ref 与 reactive

创建一个响应式数据

  • ref:任意类型(建议基本类型)数据的响应式引用(设置、获取值时需要加.value)。
    ref 的本质是拷贝,修改数据是不会影响到原始数据。

  • reactive:则主要用于对象和数组等复杂类型的数据,将其转化为响应式对象。
    使用 reactive 创建的响应式对象,可以直接访问其属性或调用其方法。

设计理念:

  • ref 的设计理念主要是为了解决单一元素或数据的响应式问题。
  • reactive 的设计则更侧重于解决 JavaScript 对象和数组等复杂数据结构的响应式问题。

总结:

reactive + 增强 => ref

  • 在 Vue3 中,如果是把对象类型的数据弄成响应式,reactive 和 ref 都可以。reactive 能做的 ref 也能做,并且还是用 reactive 做的(ref 内部是通过reactive 来支持的);

  • 简单来说 ref 是在 reactive 上在进行了封装进行了增强,个人理解ref是reactive的语法糖,当 ref 的参数为对象时,用的就是 reactive 方法,如:ref(1) 就等价于 reactive({value: 1})

  • 平时项目ref一把梭,是可以的,问题也不大;

Vue 3 官方建议使用 ref() 作为声明响应式状态的主要 API,尤其是在处理基本类型数据和单一对象时。
然而,在处理复杂数据结构时,如对象和数组,reactive 可能是更合适的选择。

<template>
	<ul>
		<li>ref 基本类型:{{name1}}</li>
		<li>ref 复杂类型:{{name2.name}}</li>
		<li>reactive 复杂类型:{{name3.name}}</li>
	</ul>
</template>

<script>
import { ref, reactive } from 'vue'
export default {
	setup () {
		let nameStr = '张三';
		let name1 = ref(nameStr); // ref为基本数据类型添加响应式状态
		setTimeout(() => {
			name1.value = 'zhangsan';

			console.log(name1.value); // 'zhangsan'
			console.log(nameStr); // '张三'  =>  不会影响到原始数据
		}, 1000)


		let nameObj2 = { name: '张三' };
		let name2 = ref(nameObj2); // ref为复杂数据类型添加响应式状态(不建议)
		setTimeout(() => {
			// 设置值需要加.value
			name2.value.name = 'zhangsan';

			console.log(name2.value.name); // 'zhangsan'  =>  获取值需要加.value
			console.log(nameObj2); // {name: "zhangsan"}  =>  会影响到原始数据
		}, 2000)


		let nameObj3 = { name: '张三' };
		const name3 = reactive(nameObj3);// reactive只能为复杂数据类型添加响应式状态
		setTimeout(() => {
			name3.name = 'zhangsan';

			console.log(name3.name); // 'zhangsan'
			console.log(nameObj3); // {name: "zhangsan"}  =>  会影响到原始数据
		}, 3000)


		return { name1, name2, name3 }
	}
}
</script>

5. toRef 与 toRefs

也可以创建一个响应式数据

  • toRef:用来给抽离响应式对象中的某一个属性,并把该属性包裹成 ref 对象,使其和原对象产生链接。
    toRef 的本质是引用,修改响应式数据会影响原始数据。

  • toRefs:用来把响应式对象转换成普通对象,把对象中的每一个属性,包裹成 ref 对象。
    toRefs 就是 toRef 的升级版,只是toRefs 是把响应式对象进行转换,其余的特性和 toRef 无二

<template>
	<ul>
		<li>{{name1}}</li>
		<li>{{name2.name.value}}</li>
		<li>{{name}}</li>
	</ul>
</template>

<script>
import { toRef, toRefs, reactive } from 'vue'
export default {
	setup () {
		let nameObj1 = reactive({ name: '张三' });
		let name1 = toRef(nameObj1, 'name');
		let age1 = toRef(nameObj1, 'age '); // age不存在
		setTimeout(() => {
			name1.value = 'zhangsan';
			age1.value = 20; // 即便age不存在也能正常响应式赋值
		}, 1000)


		let nameObj2 = reactive({ name: '张三' });
		let age2 = toRef(nameObj3, 'age '); // age不存在
		let name2 = toRefs(nameObj2);
		setTimeout(() => {
			name2.name.value = 'zhangsan';
			age2.value = 20; // age不存在,赋值无反应
		}, 2000)


		let nameObj3 = reactive({ name: '张三' });
		setTimeout(() => {
			nameObj3.name = 'zhangsan';
		}, 3000)
		let { name } = toRefs(nameObj3); // 解构后仍需保持响应式(重要)

		return { name1, name2, name }
	}
}
</script>

6. readonly 只读属性

表示响应式对象不可修改

<template>
	<ul>
		<li>{{nameObj.name}}</li>
	</ul>
</template>

<script>
import { reactive, readonly } from 'vue'
export default {
	setup () {
		let nameObj = reactive({ name: '张三' });
		let readonlyObj = readonly(nameObj); // 对nameObj响应式对象设置成只读,不可修改

		setTimeout(() => {
			readonlyObj.name = 'zhangsan'; // 无法设置属性  =>  Set operation on key "name" failed: target is readonly. Proxy {name: "张三"}
		}, 1000)

		return { nameObj }
	}
}
</script>

7. method方法

<template>
  <!-- 调用方法 -->
  <button @click='changeName'>按钮</button>  
</template>

<script setup>
import { reactive } from 'vue'
export default {
	setup () {
		const state = reactive({ name: '张三' });
		
		// 声明method方法
		const changeName = () => {
		    state.name = 'zhangsan';
		}  
		
		return { state , changeName }
	}
}
</script>


8. computed 计算属性

<template>
	<div>
		<button @click="add">+</button>
		<p>{{addCount}}</p>
	</div>
</template>

<script>
import { ref, computed } from 'vue'
export default {
	setup () {
		const num = ref(0);
		const add = () => {
			num.value++
		}
		// 计算属性
		const addCount = computed(() => {
			return num.value * 2;
		})
		return { add, addCount }
	}
}
</script>

9. watch 与 watchEffect 监听属性

  • watch 函数:
    用来侦听特定的数据源,并在回调函数中执行副作用。
    默认情况是惰性的,也就是说仅在侦听的源数据变更时才执行回调。
  • watchEffect 函数:
    1.立即执行、立即监听(immediate)
    2.自动会感知代码依赖(自动收集依赖),不需要传递监听的内容(不需要像 watch 一样手动传入依赖)
    3.无法获得变化前的值(oldVal)
<template>
	<div>
		<p>{{name}}</p>
		<p>{{nameObj.name}}</p>
	</div>
</template>

<script>
import { ref, reactive, watch, watchEffect } from 'vue'
export default {
	setup () {

		// 一、监听基本类型
		const name = ref('张三');
		setTimeout(() => {
			name.value = '李四';
		}, 1000);
		
		watch(name, (newVal, oldVal) => {
			console.log(newVal, oldVal);
		}, { immediate: true }) // 立即执行


		// 二、监听复杂类型
		const nameObj = reactive({ name: 'zhangsan' })
		setTimeout(() => {
			nameObj.name = 'lisi';
		}, 2000);
		
		// 1.复杂数据无法直接监听、惰性
		watch(() => nameObj, (newVal, oldVal) => {
			console.log(newVal, oldVal); // 不会触发
		})
		
		// 2.需要深度监听、不惰性
		watch(() => nameObj, (newVal, oldVal) => {
			console.log(newVal, oldVal); // newVal、oldVal具有响应式
		}, { 
			deep: true, // 深度监听
			immediate: true, // 立即监听
		})
		
		// 3.也可以直接监听对象的属性
		watch(() => nameObj.name, (newVal, oldVal) => {
			console.log(newVal, oldVal);
		})


		// 三、同时监听多个对象
		watch([() => nameObj.name, () => nameObj.lastName], ([newName, newLastName], [oldName, oldLastName]) => {
			console.log(newName, oldName, newLastName, oldLastName);
		})



		const stop = watchEffect(() => {
			console.log(name);
			console.log(nameObj.name);
		})

		// 3秒后停止监听
		setTimeout(()=>{  
			stop();   
		},3000)
		
		
		return { name, nameObj }
	}
}
</script>

二、获取DOM节点

<template>
	<input type="text" value="张三" ref="hello">
</template>

<script>
import { ref, onMounted } from 'vue'
export default {
	setup () {
		const helloRef = ref(null); // 获取组件中ref="hello"的真实dom元素
		
		onMounted(() => {
			console.log(helloRef.value); // <input type="text">
			console.log(helloRef.value.value); // 张三
		})
		
		return { hello }
	}
}
</script>

三、provide 与 inject

Vue组件通信方式总结

Vue 提供了依赖注入的 API,通过 provide 和 inject 可以在祖先组件和后代组件之间建立一个依赖注入关系。

provide 和 inject 是 Vue.js 框架中用于实现跨组件层级状态传递的一对组合函数。它们通常用于开发者希望避免 props 的逐层传递(也称为 “prop drilling”)的场景。

父组件向(孙)子组件传递数据与方法:

  1. Vue2.x中:

    // 父组件
    export default {  
      data() {  
        return {  
          msg: 'Hello from Parent!'  
        };  
      },  
      provide() {    // provide来提供数据或方法
        return {  
          parentMsg: this.msg  
        };  
      }  
    }
    
    // (孙)子组件
    export default {  
      inject: ['parentMsg'],   // inject来接收父组件提供的数据或方法
      mounted() {  
        console.log(this.parentMsg); // "Hello from Parent!"  
      }  
    }
    
  2. Vue3.x中:

    // 父组件
    import { ref, provide } from 'vue'
    export default {
    	setup () {
    		const name = ref('张三');
    
    		// provide(别名, 要传递的数据和方法)
    		provide('myName', name)
    		provide('handleClick', () => {
    			name.value = 'zhangsan';
    		})
    	}
    }
    
    // (孙)子组件
    import { inject } from 'vue'
    export default {
    	setup () {
    		//调用 inject 方法,通过指定的别名,接收到父级共享的数据和方法
    		const name = inject('myName');
    		const handleClick = inject('handleClick');
    
    		return { name, handleClick }
    	}
    }
    

四、Teleport 传送门

Teleport 翻译过来是传送的意思,就像是哆啦 A 梦中的 「任意门」 ,任意门的作用就是可以将人瞬间传送到另一个地方。

举例:
我们希望 Dialog 渲染的 dom 和顶层组件是兄弟节点关系, 在 App.vue 文件中定义一个供挂载的元素:

<template>
	<router-view />
	<div id="model"></div> <!-- 挂载点 -->
</template>

定义一个 Dialog 组件 Dialog.vue , 留意 to 属性, 与上面的 id选择器 一致:

<template>
	<teleport to="#model"> 
		<!-- 挂载内容 -->
		<div>title</div>
        <div>I'am a Dialog.</div>
	</teleport>
</template>

DOM 渲染效果如下:

在这里插入图片描述

我们使用 teleport 组件,通过 to 属性,指定该组件渲染的位置在 App.vue 中,但是 Dialog 又是完全由 Dialog.vue 组件控制。


五、Suspense

Suspense是一个用于处理异步组件的特殊组件。其主要目标是简化异步操作的状态管理和展示。
当等待异步组件加载时,Suspense允许你展示一个备用内容,这对于优化用户体验、处理懒加载组件或异步数据获取时非常有用。

简而言之:Suspense组件用于在等待某个异步组件解析时显示备用内容

在 Vue2.x 中经常遇到这样的场景:

<template>
<div>
    <div v-if="!loading">
    	<!-- 异步渲染 -->
        <AsyncComponent />  
    </div>
    <div v-if="loading">
        加载中...
    </div>
</div>
</template>

在异步数据没加载前,一般我们都会提供一个加载中( loading )的动画,当数据返回时配合 v-if 来控制数据显示。

现在 Vue3.x 新出的内置组件 Suspense , 它提供两个template slot, 刚开始会渲染一个 fallback 状态下的内容, 直到到达某个条件后才会渲染 default 状态的正式内容, 通过使用 Suspense 组件进行展示异步渲染就更加的简单。

Suspense 组件 只是一个带插槽的组件,只是它的插槽指定了 defaultfallback 两种状态。

<template> 
	<Suspense>
	      <template #default>
		  		<!-- 异步渲染 -->
		        <AsyncComponent />  
	      </template>
	      <template #fallback>
	          <div>
	              Loading...
	          </div>
	      </template>
	</Suspense>
</template>  

<script>  
import { Suspense, defineAsyncComponent } from 'vue';  

// 使用defineAsyncComponent来定义异步组件  
const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'));   // 在 AsyncComponent 被实际渲染到 DOM 时才会被异步加载。

export default {  
  components: {  
    Suspense,  
    AsyncComponent
  }  
};  
</script>

AsyncComponent 在加载时,用户会看到 Loading... 的提示。一旦这个组件都加载完成,它们将替换加载提示并正常渲染。

在使用Suspense组件时,你需要注意以下几点:

  • Suspense组件只能包含一个异步组件,否则会抛出错误。
  • fallback插槽中的内容只能是一个单独的元素,否则会抛出错误。
  • 异步组件必须通过 defineAsyncComponent 方法定义,否则无法使用Suspense组件。

六、碎片化节点 Fragment

在 Vue2.x 中, template 中只允许有一个根节点:

<template>
    <div>
        <span></span>
        <span></span>
    </div>
</template>

但是在 Vue3.x 中,可以有多个根元素,也可以有把文本作为根元素

<template>
    <span></span>
    <span></span>
</template>

七、插槽与作用域插槽

Vue之slot插槽和作用域插槽

1. 插槽

父组件:

<template>
	<Child>
		<!-- Vue2.x写法
		<div slot="parent">
            <div>父组件</div>
		</div>
		 -->
		<template v-slot:parent>
            <div>父组件</div>
        </template>
	</Child>
</template>

子组件(Child.vue):

<template>
	<slot name='parent'>子组件</slot>
</template>

2. 作用域插槽

作用域插槽是一种特殊类型的插槽,用作一个 (能被传递数据的) 可重用模板,来代替已经渲染好的元素。

  • 在子组件中,只需将数据传递到插槽,就像你将 prop 传递给组件一样
  • 在父组件中,通过 slot-scope="scoped" 的形式,获取子组件传递过来的数据,该数据对象的别名为 scoped。这就是作用域插槽的模板。

父组件:

<template>
	<Child>
		<!-- <template slot="content" slot-scope="scoped">  -->
		<template v-slot:content="scoped">
			<!-- 2. 接收myName数据,scoped 为 { "myName": "猫老板的豆" } -->
			<div>{{scoped.myName}}</div>
		</template>
	</Child>
</template>

子组件:

<template>
	<slot name="content" :myName="myName"></slot>  <!-- 1. 对外抛出 myName 数据 -->
</template>

<script>

import { ref } from 'vue'
export default {
	setup () {

		let myName = ref("猫老板的豆")

		return { myName }
	},
}
</script>
  • 12
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猫老板的豆

你的鼓励将是我创作的最大动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值