vue_day04

目录

1day04课程介绍

2scoped样式冲突

问题:

3data是一个函数

4组件通信

问题:

5props详解

问题:

6小黑记事本组件版-拆分组件

问题:

7小黑记事本组件版-渲染&添加删除

问题:

8小黑记事本组件版-底部合计-清空-持久化存储

9非父子通信-数据总线

10非父子通信-provide-inject

11v-model详解

问题:

12sync修饰符

[13ref和ref获取dom和组件](#13ref和ref获取dom和组件)

问题:

[14vue异步更新和nextTick](#14vue异步更新和nextTick)

15总结


1day04课程介绍

2scoped样式冲突

问题:

scoped原理?

上图

3data是一个函数

所以它返回的都是一个个实例,也就是一个个对象

4组件通信

问题:

<script>标签中的data()和methods有什么区别?

5props详解

问题:

.join('·')作用?

给原数组展示时每个元素添加分隔符

6小黑记事本组件版-拆分组件

问题:

使用组件化标签的前提是先在components里面注册元素

7小黑记事本组件版-渲染&添加删除

问题:

渲染功能->需要渲染的数据应该放在哪里?->公共父组件App.vue中,再通过父传子即可渲染数据

8小黑记事本组件版-底部合计-清空-持久化存储

9非父子通信-数据总线

10非父子通信-provide-inject

11v-model详解

问题:

e.target?获取绑定事件的事件源

12sync修饰符

13ref和$ref获取dom和组件

问题:

ref和$refs是干什么用的?

14vue异步更新和$nextTick

15总结

深入浅出 Vue 核心知识点:从组件通信到异步更新的全方位解析Vue 作为前端领域主流的渐进式框架,凭借其易用性、灵活性和高性能,成为众多开发者构建界面的首选工具。本文将围绕 Vue 的核心知识点,从组件通信的多种模式、props 的深入理解、v-model 的原理、sync 修饰符的妙用,到 ref 的 DOM 操作与 nextTick 的异步更新机制,进行全方位、深层次的剖析,帮助开发者夯实 Vue 技术栈,应对复杂业务场景的开发挑战。一、组件通信:Vue 组件间数据流转的桥梁在 Vue 的组件化开发中,组件通信是核心环节之一。无论是简单的父子组件,还是复杂的非父子组件,高效的通信机制是保障应用功能完整、逻辑清晰的关键。1. 父子组件通信:props 与 emit 的经典组合props是父组件向子组件传递数据的官方通道。它允许父组件将数据以属性的形式传递给子组件,子组件通过在props选项中声明接收的属性,即可在自身模板和逻辑中使用这些数据。vue<!-- 父组件 Parent.vue -->

<template>

<div>

<Child :message="parentMsg" @child-event="handleChildEvent" />

</div>

</template>

<script>

import Child from './Child.vue';

export default {

components: { Child },

data() {

return {

parentMsg: '来自父组件的问候'

};

},

methods: {

handleChildEvent(data) {

console.log('接收到子组件的事件:', data);

}

}

};

</script>

<!-- 子组件 Child.vue -->

<template>

<div>

<p>{{ message }}</p>

<button @click="sendEvent">向父组件发送事件</button>

</div>

</template>

<script>

export default {

props: {

message: {

type: String,

required: true,

default: '默认消息'

}

},

methods: {

sendEvent() {

this.$emit('child-event', '子组件的反馈数据');

}

}

};

</script>

在上述代码中,父组件通过:message将parentMsg传递给子组件,子组件在props中声明message的类型、是否必填等规则,确保数据的合法性。而子组件通过$emit触发自定义事件child-event,并携带数据,父组件通过@child-event监听该事件,实现了父子组件间的双向通信。props 的验证规则是保障数据质量的重要手段。除了type和required,还可以使用validator函数进行自定义验证:javascript运行props: {

status: {

type: String,

validator: function(value) {

return ['success', 'warning', 'error'].includes(value);

}

}

}

这种验证在多人协作开发或大型项目中,能有效避免因数据格式错误导致的 bug。2. 非父子组件通信:数据总线与 provide/inject在复杂的组件树中,非父子组件(如兄弟组件、跨层级组件)的通信需要更灵活的机制。** 数据总线(Event Bus)** 是一种简单高效的实现方式。它通过创建一个空的 Vue 实例作为中央事件枢纽,实现组件间的事件触发与监听。javascript运行// 新建一个bus.js文件

import Vue from 'vue';

export default new Vue();

// 组件A:触发事件

import bus from './bus.js';

export default {

methods: {

sendData() {

bus.$emit('custom-event', '需要传递的数据');

}

}

};

// 组件B:监听事件

import bus from './bus.js';

export default {

mounted() {

bus.$on('custom-event', (data) => {

console.log('接收到非父子组件的消息:', data);

});

},

beforeDestroy() {

bus.$off('custom-event');

}

};

需要注意的是,在使用数据总线时,要及时在组件销毁前取消事件监听($off),避免内存泄漏。provide/inject则是 Vue 提供的另一种跨层级通信方式,适用于祖先组件向其所有子孙组件注入数据,无论层级多深。vue<!-- 祖先组件 -->

<template>

<div>

<DescendantComponent />

</div>

</template>

<script>

import DescendantComponent from './DescendantComponent.vue';

export default {

provide() {

return {

ancestorData: this.ancestorMsg,

ancestorMethod: this.ancestorMethod

};

},

data() {

return {

ancestorMsg: '祖先组件的注入数据'

};

},

methods: {

ancestorMethod() {

console.log('这是祖先组件的方法');

}

},

components: { DescendantComponent }

};

</script>

<!-- 深层子孙组件 -->

<template>

<div>

<p>{{ ancestorData }}</p>

<button @click="ancestorMethod">调用祖先方法</button>

</div>

</template>

<script>

export default {

inject: ['ancestorData', 'ancestorMethod']

};

</script>

provide选项可以是一个对象或一个返回对象的函数,使用函数可以确保注入的数据是响应式的(当祖先组件的数据变化时,子孙组件能及时更新)。inject选项声明了组件需要注入的属性或方法,这种方式在开发如主题切换、权限控制等全局功能时非常实用。二、props 详解:组件数据传递的严谨之道props 作为 Vue 组件通信的基础,其背后的设计理念和细节值得深入探究。1. props 的响应式原理Vue 对 props 的处理是深度响应式的。当父组件传递的 props 数据发生变化时,子组件会自动更新对应的视图和逻辑。这是因为 Vue 在初始化组件时,会将 props 转换为响应式数据,与组件自身的data属性类似,都依赖于 Vue 的响应式系统。但需要注意的是,如果父组件传递的是一个对象或数组,子组件对其属性或元素的修改会直接影响父组件的数据,这在某些场景下可能导致意外的副作用。因此,Vue 官方建议单向数据流:父组件负责传递数据,子组件仅负责展示或基于 props 派生新数据(可通过computed属性实现),避免直接修改 props。vue<template>

<div>

<p>子组件接收的列表:{{ list }}</p>

<p>子组件派生的列表:{{ filteredList }}</p>

</div>

</template>

<script>

export default {

props: {

list: {

type: Array,

required: true

}

},

computed: {

filteredList() {

return this.list.filter(item => item.status);

}

}

};

</script>

在上述代码中,子组件通过computed属性filteredList对父组件传递的list进行过滤,既保证了数据的派生需求,又遵循了单向数据流的原则。2. props 的高级用法与常见问题默认值为对象或数组时:必须使用工厂函数返回,否则所有组件实例将共享同一个对象 / 数组引用。javascript运行props: {

config: {

type: Object,

default: () => ({

theme: 'light',

size: 'medium'

})

}

}

props 的类型校验:type可以是原生构造函数(String、Number、Boolean、Array、Object、Date、Function、Symbol),也可以是自定义的构造函数(通过instanceof校验)。javascript运行function Person(firstName, lastName) {

this.firstName = firstName;

this.lastName = lastName;

}

props: {

author: {

type: Person

}

}

props 与 data、computed 的优先级:props的优先级高于data,因为data是基于props的默认值或当前值进行初始化的;而computed则可以基于props和data进行派生。三、v-model:双向绑定的语法糖v-model 是 Vue 中实现双向数据绑定的便捷方式,它本质上是v-bind:value和v-on:input的语法糖。1. v-model 的基本原理以输入框为例,以下两种写法是等价的:vue<!-- 语法糖形式 -->

<input v-model="message" />

<!-- 等价的完整形式 -->

<input :value="message" @input="message = $event.target.value" />

这种语法糖极大简化了表单元素的双向绑定操作,让开发者可以更专注于业务逻辑。2. 自定义组件的 v-model在自定义组件中,我们可以通过model选项自定义 v-model 的 prop 和事件,实现更灵活的双向绑定。vue<template>

<div>

<input :value="currentValue" @input="handleInput" />

</div>

</template>

<script>

export default {

model: {

prop: 'value',

event: 'change'

},

props: {

value: {

type: String,

default: ''

}

},

computed: {

currentValue: {

get() {

return this.value;

},

set(val) {

this.$emit('change', val);

}

}

},

methods: {

handleInput(e) {

this.currentValue = e.target.value;

}

}

};

</script>

<!-- 父组件中使用 -->

<CustomInput v-model="parentValue" />

在这个自定义输入框组件中,通过model选项指定了 prop 为value,事件为change,因此父组件的v-model会自动绑定到该 prop 和事件上。这种方式让自定义组件拥有了与原生表单元素一致的 v-model 使用体验。3. v-model 的修饰符v-model 还支持多种修饰符,以满足不同的业务需求:.lazy:将输入事件由input改为change,即失去焦点或按下回车键时才更新数据。vue<input v-model.lazy="message" />

.number:自动将输入的字符串转换为数字。vue<input v-model.number="age" type="number" />

.trim:自动过滤输入内容的首尾空格。vue<input v-model.trim="username" />

这些修饰符大大增强了 v-model 的实用性,减少了开发者手动处理数据格式的工作量。四、sync 修饰符:父子组件双向绑定的优雅实现在 Vue 2.3.0 + 中,引入了sync修饰符,它是一种简化父子组件间双向绑定的语法糖,用于解决父组件向子组件传递数据后,子组件需要修改该数据并同步回父组件的场景。1. sync 修饰符的原理sync修饰符的本质是自动为子组件的事件添加update:前缀。例如:vue<!-- 父组件 -->

<Child :title.sync="parentTitle" />

<!-- 等价于 -->

<Child :title="parentTitle" @update:title="parentTitle = $event" />

<!-- 子组件中触发更新 -->

this.$emit('update:title', '新的标题');

这种方式让父子组件的双向绑定更加清晰、简洁。2. sync 修饰符的使用场景sync修饰符常用于弹窗组件的显隐控制、表单组件的状态同步等场景。vue<!-- 弹窗组件 Popup.vue -->

<template>

<div v-show="visible" class="popup">

<div class="popup-content">

<slot></slot>

<button @click="close">关闭</button>

</div>

</div>

</template>

<script>

export default {

props: {

visible: {

type: Boolean,

default: false

}

},

methods: {

close() {

this.$emit('update:visible', false);

}

}

};

</script>

<!-- 父组件中使用 -->

<template>

<div>

<button @click="showPopup = true">显示弹窗</button>

<Popup :visible.sync="showPopup">

<p>这是弹窗内容</p>

</Popup>

</div>

</template>

<script>

import Popup from './Popup.vue';

export default {

components: { Popup },

data() {

return {

showPopup: false

};

}

};

</script>

在这个例子中,父组件通过sync修饰符将showPopup与弹窗组件的visible prop 双向绑定,弹窗组件内部通过emit('update:visible', false)即可关闭弹窗并同步父组件的状态,逻辑清晰且代码简洁。五、ref 与 refs:DOM 与组件的直接操作在 Vue 中,通常推荐通过数据驱动视图,但在某些特殊场景下,需要直接操作 DOM 元素或组件实例,这时就需要用到ref和refs。1. ref 的基本用法ref可以添加在 DOM 元素或组件上,用于注册一个引用信息到父组件的refs对象中。vue<template>

<div>

<input ref="inputRef" type="text" />

<ChildComponent ref="childRef" />

<button @click="handleClick">操作引用</button>

</div>

</template>

<script>

import ChildComponent from './ChildComponent.vue';

export default {

components: { ChildComponent },

methods: {

handleClick() {

// 操作DOM元素

this.$refs.inputRef.focus();

// 操作组件实例

this.$refs.childRef.childMethod();

}

}

};

</script>

需要注意的是,refs不是响应式的,它是在组件渲染完成后才填充的,因此不能在模板或计算属性中依赖refs。2. refs 的高级应用在一些复杂的交互场景中,refs可以发挥重要作用,例如:集成第三方 DOM 库:如操作轮播图、图表等需要直接操作 DOM 的库时,通过ref获取 DOM 节点后传入库中。父子组件间的直接方法调用:当子组件暴露了一些公共方法时,父组件可以通过$refs直接调用这些方法,实现更灵活的交互。vue<!-- 子组件 ChartComponent.vue -->

<template>

<div ref="chartDom"></div>

</template>

<script>

import ECharts from 'echarts';

export default {

mounted() {

this.initChart();

},

methods: {

initChart() {

this.chart = ECharts.init(this.$refs.chartDom);

this.chart.setOption({

// 图表配置

});

},

updateChart(data) {

this.chart.setOption({

series: [{

data: data

}]

});

}

},

beforeDestroy() {

this.chart.dispose();

}

};

</script>

<!-- 父组件 -->

<template>

<div>

<ChartComponent ref="chartRef" />

<button @click="updateChartData">更新图表数据</button>

</div>

</template>

<script>

import ChartComponent from './ChartComponent.vue';

export default {

components: { ChartComponent },

methods: {

updateChartData() {

const newData = [/* 新的图表数据 */];

this.$refs.chartRef.updateChart(newData);

}

}

};

</script>

在这个例子中,父组件通过refs直接调用子组件的updateChart方法,实现了图表数据的更新,这种方式在与第三方库集成时非常高效。六、Vue 异步更新与 nextTick:理解视图更新的时机Vue 的视图更新是异步的,这是为了提高性能,将多次数据变更合并为一次 DOM 更新。理解这一机制对于处理一些需要在 DOM 更新后执行的操作至关重要,而$nextTick就是专门用于处理这种场景的工具。1. Vue 异步更新的原理当我们修改 Vue 实例的数据时,Vue 会将这些修改收集起来,然后在一个微任务(Microtask)中批量更新 DOM。这意味着,数据变更后,DOM 不会立即更新,而是会等待当前同步代码执行完毕后再进行更新。vue<template>

<div>

<p ref="msgRef">{{ message }}</p>

<button @click="changeMessage">修改消息</button>

</div>

</template>

<script>

export default {

data() {

return {

message: '初始消息'

};

},

methods: {

changeMessage() {

this.message = '新的消息';

// 此时DOM尚未更新,$refs.msgRef的innerText还是"初始消息"

console.log(this.$refs.msgRef.innerText); // 输出:初始消息

this.$nextTick(() => {

// DOM已更新,$refs.msgRef的innerText变为"新的消息"

console.log(this.$refs.msgRef.innerText); // 输出:新的消息

});

}

}

};

</script>

在上述代码中,点击按钮后,message被修改,但立即打印refs.msgRef的内容时,DOM 还未更新,因此输出的是旧值。而在nextTick的回调中,DOM 已经更新,输出的是新值。2. nextTick 的使用场景nextTick主要用于以下场景:操作更新后的 DOM:当需要基于新的 DOM 状态进行操作(如获取元素尺寸、位置,操作焦点等)时,必须使用nextTick。在 created 钩子中操作 DOM:created钩子在 DOM 渲染之前执行,因此在created中操作 DOM 需要使用nextTick。vue<template>

<div ref="container">

<!-- 动态渲染的内容 -->

</div>

</template>

<script>

export default {

created() {

this.$nextTick(() => {

// 此时DOM已渲染,可以安全操作

const container = this.$refs.container;

console.log(container.offsetHeight);

});

}

};

</script>

确保数据更新后执行回调:当需要在数据更新后立即执行某个回调函数,且该函数依赖于 DOM 更新时,nextTick是最佳选择。3. nextTick 的实现原理Vue 的nextTick内部优先使用原生的Promise.then(微任务),如果环境不支持 Promise,则降级使用MutationObserver、setImmediate,最后使用setTimeout(宏任务)。这种设计保证了nextTick回调的执行时机在 DOM 更新之后。七、总结:Vue 核心知识点的体系化理解通过对组件通信、props、v-model、sync 修饰符、ref 与\(refs、异步更新与\)nextTick 等核心知识点的深入解析,我们可以构建起对 Vue 框架的体系化理解:组件通信是 Vue 组件化开发的核心,不同的通信方式(props/emit、数据总线、provide/inject)适用于不同的场景,开发者需根据业务需求选择合适的方式。props作为数据传递的基础,其响应式原理和校验规则是保障组件稳定性的关键,遵循单向数据流原则能有效避免数据混乱。v-model 和 sync 修饰符是双向绑定的语法糖,它们简化了表单和组件状态的同步操作,提高了开发效率。ref 与 refs提供了直接操作 DOM 和组件实例的能力,在与第三方库集成或处理特殊交互时非常有用,但需注意其非响应式的特性。** 异步更新与\(nextTick**是Vue性能优化的体现,理解这一机制并熟练使用`\)nextTick` 能帮助开发者解决许多 DOM 更新时机相关的问题。掌握这些核心知识点,不仅能让开发者更高效地使用 Vue 进行开发,还能深入理解 Vue 的设计理念和底层原理,为应对复杂业务场景和性能优化奠定坚实的基础。在实际开发中,应将这些知识点灵活运用,结合具体业务需求,打造出高效、可维护的 Vue 应用。

相关推荐
明远湖之鱼2 小时前
浅入理解跨端渲染:从零实现 React DSL 跨端渲染机制
前端·react native·react.js
悟忧3 小时前
规避ProseMirror React渲染差异带来的BUG
前端
小皮虾3 小时前
小程序云开发有类似 uniCloud 云对象的方案吗?有的兄弟,有的!
前端·javascript·小程序·云开发
QuantumLeap丶3 小时前
《uni-app跨平台开发完全指南》- 05 - 基础组件使用
vue.js·微信小程序·uni-app
Android疑难杂症3 小时前
鸿蒙Notification Kit通知服务开发快速指南
android·前端·harmonyos
T___T3 小时前
全方位解释 JavaScript 执行机制(从底层到实战)
前端·面试
阳懿3 小时前
meta-llama-3-8B下载失败解决。
前端·javascript·html
Qinana3 小时前
🌊 深入理解 CSS:从选择器到层叠的艺术
前端·css·程序员
IT_陈寒3 小时前
Python 3.12新特性实测:10个让你的代码提速30%的隐藏技巧 🚀
前端·人工智能·后端