Vue学习

参考文章:

🎯Vue学习笔记

VUE3 学习笔记(目录)——官方文档的学习路线梳理

Vue3官方文档

Vue前置知识

Html5+CSS+CSS3+JS+ES6

JavaScript

ES6-11新特性

原型/原型链

Node.js

Vue2

引入vue2

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16"></script>

<body>
<div id="app">
{{message}}
</div>

<script>
const vm=new Vue({
// el:'#app',
//data函数返回需要的数据
data(){
return{
message:'hello vue'
}
}
})
vm.$mount('#app') //用el:'#app'和这个都可以,但是这个更灵
//活些,比如可以满足某个条件再挂载
</script>

MVVM 介绍

**MVVM**(Model-View-ViewModel)是一种软件架构模式,广泛应用于前端开发,尤其是用于构建用户界面。在 Vue 和其他现代框架中,MVVM 模式帮助开发者实现了清晰的职责分离,使得界面和逻辑更加易于管理和维护。
MVVM 各个部分的定义
  • Model(模型) :表示应用的数据层,负责存储应用的状态数据。通常是一个 JavaScript 对象,包含应用的核心数据。

  • View(视图) :表示应用的用户界面,用户能看到并与之交互的部分。视图展示数据(由 Model 提供),并响应用户的输入。

  • ViewModel(视图模型) :位于 View 和 Model 之间,是一个桥梁。它负责从 Model 获取数据,并将数据传递给 View。同时,它还可以将 View 中的用户操作(如按钮点击、输入框改变)反向传递到 Model,更新数据。

    MVVM 的关键特性是 双向数据绑定,即 View 和 Model 之间的同步更新。它通过 ViewModel 来进行数据和视图之间的自动同步。

Vue 中如何实现 MVVM
Vue 采用了 **MVVM** 模式,并通过以下几个机制来实现这一模式:
双向数据绑定
Vue 的双向数据绑定是实现 MVVM 的核心部分之一。Vue 的双向绑定是通过 **数据劫持**(使用​`Object.defineProperty`​ 或 `Proxy`​)和 **观察者模式**(Observer)来实现的。这使得 Vue 可以自动同步 View 和 Model 中的变化。
  1. Model(数据层) :Vue 使用响应式数据模型来管理数据。通过 reactive​ 或 ref​ API,Vue 可以将数据对象变成响应式对象,当数据发生变化时,相关的视图会自动更新。
  2. View(视图层) :Vue 通过模板(template)和指令(如 v-bind​、v-model​ 等)来描述界面。视图会根据模型中的数据动态渲染和更新。
  3. ViewModel(视图模型) :Vue 中的 实例对象(Vue 实例) 就是 ViewModel,它充当了 View 和 Model 之间的桥梁。它通过监听数据变化、处理用户输入和更新数据,完成视图的渲染与同步。
数据劫持(Observer)
Vue 会通过 **数据劫持**(在 Vue 2.x 中是通过 `Object.defineProperty`​,在 Vue 3.x 中是通过 `Proxy`​)来监听数据变化。一旦数据发生变化,Vue 会通知相关的视图组件进行更新。
// 示例:响应式数据
const state = Vue.reactive({
count: 0
});

在这个例子中,state.count​ 是响应式的,任何对 count​ 的修改都会触发视图的更新。

总结:Vue 如何实现 MVVM

Vue 通过以下方式实现了 MVVM 模式:

  • View(视图) :通过模板和指令,显示数据,并响应用户交互。
  • ViewModel(视图模型) :通过 Vue 实例(Vue 组件)来连接视图和数据。Vue 实例管理视图的更新,处理事件,提供计算属性和方法等。
  • Model(模型) :Vue 的响应式数据系统使得数据成为动态可变的。当数据变化时,视图会自动更新。

Vue 的响应式系统(通过数据劫持、依赖收集、双向数据绑定)和模板渲染机制共同作用,实现了数据和视图的同步更新,从而提供了 MVVM 模式的完整实现。

(重要)数据代理(实现数据的响应式)

`Object.defineProperty`​ 是 JavaScript 中定义对象属性的一种方法,可以自定义属性的行为。Vue 的数据代理机制在 Vue2 中大量使用了 `Object.defineProperty`​ 实现响应式系统。

Object.defineProperty​ 允许定义对象的属性,并控制其可写性、可枚举性和可配置性,还能定义 getter 和 setter 函数。

const obj = {}; 

Object.defineProperty(obj, 'property', {
value: 42, // 属性的值
writable: true, // 是否可以修改值
enumerable: true, // 是否可以通过for...in或Object.keys()枚举
configurable: true // 是否可以删除属性或修改属性描述符
});

console.log(obj.property); // 输出:42

使用 getter​ 和 setter​ 实现数据拦截。getter​ 和 setter​ 是 JavaScript 中用于对象属性的一种方法,主要用于控制属性的读取和写入行为。

const user = {
_name: 'Alice'
};

Object.defineProperty(user, 'name', {

//getter 会在访问属性时被触发,并返回值作为此属性的值
get() {
console.log('获取 name 属性');
return this._name;
},
//setter:当代理数据 name 被赋新值时触发
// 将传入的新值赋给 this._name,更新实际的 name 值
set(newValue) {
console.log('设置 name 属性');
this._name = newValue;
}
});

console.log(user.name); // 获取 name 属性 -> 输出:Alice
user.name = 'Bob'; // 设置 name 属性
console.log(user.name); // 获取 name 属性 -> 输出:Bob

应用场景

  1. 数据监听:Object.defineProperty​ 可以通过自定义 getter​ 和 setter​ 函数来监听数据变化,从而实现对数据的拦截。
  2. 响应式实现:Vue2 中利用 Object.defineProperty​ 实现数据的响应式,将属性代理到 Vue 实例上,捕获数据的变化并更新视图。
  3. 防止属性修改:可以通过设置 writable: false​ 来使某些属性变为只读。
  4. 属性保护:通过设置 enumerable: false​ 隐藏属性,防止它在 for...in​ 或 Object.keys()​ 中被遍历到。

Vue基础知识

插值语法

<template>
<!-- 插值语法主要用于在模板中显示动态数据。 -->

<!-- 文本插值 -->
<p>{{ message }}</p>

<!-- v-bind属性插值 -->
<img v-bind:src="imageSrc" alt="图片描述" />
<!-- 简写 -->
<img :src="imageSrc" alt="图片描述" />

<!-- JavaScript 表达式 -->
<p>{{ message.toUpperCase() }}</p>

<!-- 三元运算符 -->
<p>{{ isActive ? 'Active' : 'Inactive' }}</p>

<!-- 使用过滤器(Vue3中使用方法替代)-->
<p>{{ formattedDate() }}</p>
</template>

<script>
export default {
data() {
return {
message: 'Hello, Vue!',
imageSrc: 'https://example.com/image.png',
isActive: true,
};
},
methods: {
formattedDate() {
return new Date().toLocaleDateString();
},
},
};
</script>

数据绑定之单向绑定和双向绑定

  • 单向绑定( v-bind​ ):数据从数据源单向流向视图。应用场景:用于将数据绑定到HTML元素的属性。
  • 双向绑定( v-model​ ):数据在数据源和视图之间相互绑定,同步更新。应用场景:用于表单元素(如 <input>​, <textarea>​, <select>​)与数据源之间的双向绑定。
<template>
<!-- 单向绑定 v-bind -->
<p>{{ message }}</p> <!-- 使用插值语法展示数据 -->
<img :src="imageSrc" alt="图片描述" /> <!-- 使用 v-bind 将属性绑定到数据 -->
<p :class="isActive ? 'active' : 'inactive'"></p> <!-- 使用表达式控制属性 -->

<!-- 双向绑定 v-model -->
<input v-model="inputText" /> <!-- 使用 v-model 实现双向绑定 -->
<select v-model="selectedOption"> <!-- 在 select 中使用 v-model 绑定 -->
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
</select>
</template>

<script>
export default {
data() {
return {
message: 'Hello, Vue!',
imageSrc: 'https://example.com/image.png',
isActive: true,
inputText: '', // 双向绑定到 input 元素
selectedOption: 'option1' // 双向绑定到 select 元素
};
}
};
</script>

Vue事件和事件修饰符

<template>
<!-- 1. 基本事件绑定 -->
<button @click="clickHandler">点击我</button>

<!-- 2. 事件修饰符 -->
<!-- .stop:阻止事件冒泡 -->
<div @click="outerClick">
<button @click.stop="innerClick">点击阻止冒泡</button>
</div>

<!-- .prevent:阻止默认行为 -->
<a href="https://example.com" @click.prevent="clickHandler">不要跳转</a>

<!-- .capture:使用事件捕获模式 -->
<div @click.capture="captureClick">使用捕获模式点击</div>

<!-- .self:仅在自身元素上触发 -->
<div @click.self="selfClick">
<p>点击此区域不会触发事件</p>
</div>

<!-- .once:只触发一次的事件 -->
<button @click.once="clickOnce">只触发一次</button>

<!-- .passive:提升滚动性能 -->
<div @scroll.passive="onScroll">滚动区域</div>

<!-- 3. 键盘事件修饰符 -->
<!-- .enter:监听 Enter 键 -->
<input @keyup.enter="submitForm" placeholder="按 Enter 键提交" />

<!-- 自定义按键修饰符:监听特定组合键 -->
<input @keyup.ctrl="save" placeholder="按 Ctrl 键保存" />

<!-- .exact:精确匹配修饰符组合 -->
<button @click.exact="exactClick">仅在没有组合键时触发</button>

<!-- 4. 组合修饰符:多个修饰符同时使用 -->
<button @click.stop.once="combinedClick">组合修饰符</button>
</template>

<script>
export default {
methods: {
clickHandler() {
console.log("按钮被点击了!");
},
outerClick() {
console.log("外层被点击");
},
innerClick() {
console.log("内层被点击");
},
captureClick() {
console.log("捕获阶段触发");
},
selfClick() {
console.log("只在自身元素上触发");
},
clickOnce() {
console.log("该事件只触发一次");
},
onScroll() {
console.log("滚动事件");
},
submitForm() {
console.log("表单提交");
},
save() {
console.log("保存操作");
},
exactClick() {
console.log("仅在没有按其他组合键时触发");
},
combinedClick() {
console.log("组合修饰符示例");
}
}
}
</script>

Vue计算属性(computed)

计算属性用于对数据进行动态计算,并能响应式地更新。计算属性常用于数据依赖关系复杂的场景,以提升性能。
<template>
<div>
<!-- 1. 基本计算属性 -->
<p>全名:{{ fullName }}</p>

<!-- 2. 带依赖的计算属性 -->
<p>折扣价格:{{ discountedPrice }}</p>

<!-- 3. 计算属性中的 Getter 和 Setter -->
<p>年龄:{{ age }}</p>
<button @click="increaseAge">增加年龄</button>
</div>
</template>

<script>
export default {
data() {
return {
firstName: '张',
lastName: '三',
price: 100,
discount: 0.9,
birthYear: 1990
}
},
computed: {
// 1. 基本计算属性:通过依赖 firstName 和 lastName 计算全名
fullName() {
return `${this.firstName} ${this.lastName}`;
},

// 2. 带依赖的计算属性:折扣价格,根据 price 和 discount 计算
discountedPrice() {
return this.price * this.discount;
},

// 3. 带 Getter 和 Setter 的计算属性
// Getter:从 birthYear 计算年龄
// Setter:通过设置年龄更新 birthYear
age: {
get() {
const currentYear = new Date().getFullYear();
return currentYear - this.birthYear;
},
set(newAge) {
const currentYear = new Date().getFullYear();
this.birthYear = currentYear - newAge;
}
}
},
methods: {
increaseAge() {
this.age += 1; // 通过 Setter 更新 age 属性,birthYear 也会自动更新
}
}
}
</script>

总结

  1. 基本计算属性:

    • 直接使用 computed​ 定义一个方法,返回计算结果。
    • 示例:fullName​ 通过 firstName​ 和 lastName​ 计算得出。
  2. 依赖数据的计算属性:

    • 计算属性依赖的任何响应式数据发生变化时,计算属性会自动重新计算。
    • 示例:discountedPrice​ 依赖 price​ 和 discount​。
  3. 计算属性的 Getter 和 Setter:

    • 计算属性不仅可以是只读的,还可以定义 Getter 和 Setter。
    • Getter 用于计算值,Setter 用于响应数据的更改。
    • 示例:age​ 通过 birthYear​ 计算并返回值,通过设置 age​ 更新 birthYear​。
  4. 计算属性 vs 方法:

    • 计算属性有缓存,只有在依赖的数据变化时才会重新计算。
    • 方法则每次调用都会重新计算,适合不需要缓存的情况。

Vue监视属性(watch)

export default {
data() {
return {
count: 0,
name: 'Vue',
user: {
firstName: 'John',
lastName: 'Doe'
}
};
},

// 使用 watch 监听数据变化
watch: {
// 1. 基础用法:监听简单数据属性
count(newValue, oldValue) {
console.log(`count changed from ${oldValue} to ${newValue}`);
},

// 2. 字符串路径:监听嵌套对象的属性
'user.firstName': function(newValue, oldValue) {
console.log(`user.firstName changed from ${oldValue} to ${newValue}`);
},

// 3. 深度监听:用于监听对象、数组内部的变化
user: {
handler(newValue, oldValue) {
console.log('user object changed:', newValue);
},
deep: true
},

// 4. immediate 选项:立即执行回调
name: {
handler(newValue, oldValue) {
console.log(`name changed from ${oldValue} to ${newValue}`);
},
immediate: true
}
}
// 5.手动创建一个侦听器
created(){
this.$watch('question', (newQuestion) => {
// ...
})
}
// 6.停止侦听器
const unwatch = this.$watch('question', callback)

// ...当该侦听器不再需要时
unwatch()
};

//deep:设置为 true 时,进行深度监听,适用于对象和数组。
//immediate:设置为 true 时,立即触发回调,而不是等待属性第一次变化。

应用场景

  1. 监视表单输入的变化:例如,监听用户输入的表单内容并实时进行验证。
  2. 监视对象或数组的深度变化:当需要监听对象内部某些嵌套的属性时,使用 deep: true​ 来实现深度监听。
  3. 执行异步操作:监视某个属性并在属性变化时进行 API 请求或异步操作,比如监听搜索关键词并触发搜索请求。
  4. 立即执行初始化逻辑:通过 immediate: true​ 实现立即调用回调,通常用于初始化时需执行的逻辑。

计算属性和监视属性的选择

特性 计算属性 (computed) 监视属性 (watch)
是否缓存 是(依赖不变则不会重复计算)
适合的数据类型 适合同步计算且结果频繁使用的数据 适合执行异步操作或有副作用的监听逻辑
依赖数据变化时触发 自动基于依赖数据变化触发更新 手动设置需要监听的数据
常见应用场景 数据的组合、格式化和转换 异步请求、定时操作、深度监听对象
异步处理 不适用 支持(适合处理异步任务)

Vue动态绑定class和style

//--------------------------------- 动态绑定class ---------------------------------------
//使用v-bind进行绑定 'v-bind: class',简写 ' :class '
<!-- 使用对象语法:根据条件添加类名 -->
//对象语法:{ active: isActive, 'text-large': isLarge }
// isAcitve为真时,将 active 类应用到元素上,当isLarge 为 true 时,将text-large 类应用到元素上。
<div :class="{ 'active': isActive, 'text-large': isLarge }"></div>

export default {
data() {
return {
isActive: true,
isLarge: false,
};
}
};

<!-- 使用数组语法:添加多个类 -->
//数组语法:[classA, classB] 将 classA 和 classB 添加到 class 中。
<div :class="[classA, classB]"></div>

export default {
data() {
return {
classA: 'blue',
classB: 'bold'
};
}
};
//--------------------------------- 动态绑定 style ---------------------------------------
//对象语法:
<div :style="{ color: textColor, fontSize: fontSize + 'px' }"></div>

export default {
data() {
return {
textColor: 'red',
fontSize: 14
};
}
};
//数组语法
<div :style="[baseStyles, overridingStyles]"></div>

export default {
data() {
return {
baseStyles: {
color: 'blue',
fontSize: '14px'
},
overridingStyles: {
fontSize: '18px'
}
};
}
};
//--------------------------------- 结合 class 和 style 一起使用 ---------------------------------------

<div :class="{ 'active': isActive }" :style="{ color: textColor }"></div>


应用场景

<template>
<div>
<!-- 1. 条件样式:根据状态切换样式 -->
<button :class="{ 'active-button': isActive }">Click Me</button>
<!-- isActive 为 true 时应用 'active-button' 类 -->

<!-- 2. 响应用户交互:鼠标悬停时改变样式 -->
<div @mouseover="isHovered = true" @mouseleave="isHovered = false" :class="{ 'hover-effect': isHovered }">
Hover over me
</div>
<!-- isHovered 为 true 时应用 'hover-effect' 类 -->

<!-- 3. 条件样式组合:根据多个条件组合多个样式 -->
<div :class="[isActive ? 'active' : '', hasError ? 'error' : '']">
Status message
</div>
<!-- 当 isActive 为 true 应用 'active' 类,hasError 为 true 时应用 'error' 类 -->

<!-- 4. 动态样式值:根据数据动态调整样式 -->
<div :style="{ width: progress + '%' }" class="progress-bar">
Loading...
</div>
<!-- 根据 progress 值动态设置进度条的宽度 -->

<!-- 5. 动画和过渡效果:基于条件触发动画 -->
<transition name="fade">
<div v-if="showModal" class="modal">
Modal Content
</div>
</transition>
<!-- 当 showModal 为 true 时,模态框以 'fade' 动画淡入淡出 -->

<!-- 6. 主题切换:根据配置更改主题样式 -->
<div :class="theme">
Themed Content
</div>
<!-- 根据 theme 的值(如 'dark' 或 'light')动态应用主题样式 -->

<!-- 7. 响应式布局:动态调整样式实现响应式 -->
<div :style="{ fontSize: isMobile ? '14px' : '18px' }">
Responsive Text
</div>
<!-- isMobile 为 true 时字体为 14px,否则为 18px -->
</div>
</template>

<script>
export default {
data() {
return {
isActive: false,
isHovered: false,
hasError: false,
progress: 50, // 进度条宽度的百分比
showModal: false,
theme: 'dark', // 主题可以是 'dark' 或 'light'
isMobile: true // 根据屏幕尺寸设置,模拟响应式
};
}
};
</script>

<style>
.active-button {
background-color: blue;
color: white;
}
.hover-effect {
background-color: lightgrey;
}
.error {
border: 1px solid red;
}
.progress-bar {
height: 10px;
background-color: green;
}
.modal {
background-color: white;
border: 1px solid black;
padding: 20px;
}
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active for <2.1.8 */ {
opacity: 0;
}
.dark {
background-color: #333;
color: white;
}
.light {
background-color: white;
color: black;
}
</style>

条件渲染(v-if,v-else,v-else-if,v-show)

<template>
//v-if,v-else-if,v-else可以配合使用,但是结构不能被打断,必须为同意元素类型,比如div。
//v-if,v-else-if.v-else 如果在页面交互时为假后,会直接从dom里移除,不能获取。
//v-show只是将display设置成none了,并不是直接从dom中移除
<div>
<!-- 使用 v-if 和 v-else 实现简单的条件渲染 -->
<div v-if="isLoggedIn">
{{ welcomeMessage() }}
</div>
<div v-else>
{{ loginPrompt() }}
</div>

<!-- 使用 v-else-if 和 v-else 进行多条件判断 -->
<div v-if="getScoreCategory(score) === 'excellent'">
成绩优异
</div>
<div v-else-if="getScoreCategory(score) === 'pass'">
及格
</div>
<div v-else>
需要努力
</div>

<!-- 使用 v-show 控制元素的显示隐藏 -->
<div v-show="isVisible">
这个元素是可见的
</div>

<!-- 使用 template 和 v-if 实现不额外生成 DOM 元素的条件渲染 -->
<template v-if="isAdmin">
<p>{{ adminWelcomeMessage() }}</p>
<button @click="goToAdminPanel">管理后台</button>
</template>
</div>
</template>

<script>
export default {
data() {
return {
isLoggedIn: false, // 用户是否登录
score: 75, // 用户的分数
isVisible: true, // 控制元素显示隐藏
isAdmin: false // 用户是否为管理员
};
},
methods: {
// 返回登录提示信息
loginPrompt() {
return "请登录";
},

// 返回欢迎信息
welcomeMessage() {
return "欢迎回来!";
},

// 根据分数获取成绩类别
getScoreCategory(score) {
if (score > 90) return "excellent";
if (score > 60) return "pass";
return "fail";
},

// 管理员欢迎信息
adminWelcomeMessage() {
return "欢迎管理员";
},

// 跳转至管理后台
goToAdminPanel() {
console.log("跳转至管理后台");
}
}
};
</script>

Vue列表渲染(v-for)

<template>
<!-- 基础列表渲染 -->
<!-- v-for 指令用于遍历数组 items,生成多个 li 元素。 -->
<!-- :key 属性用于确保每个元素唯一性,建议用 item 的唯一标识 (如 id) 而不是索引 index,以提高渲染性能 -->
<!-- 没有 key 时,当列表的数据发生改变(如增删项)时,可能会出现渲染错误,导致页面内容和数据不一致。 -->
<!-- key 的使用建议
唯一性:通常使用唯一标识符(如数据库的 id),而不是索引 index,因为索引在插入和删除时会变化,无法保证唯一性。
简洁的类型:推荐使用简单数据类型,如字符串或数字,便于快速比较和查找。 -->
<!-- v-for不止能遍历列表,还能遍历对象的属性和字符串 -->
<ul>
<li v-for="(item, index) in items" :key="item.id">
{{ item.name }}
</li>
</ul>

<!-- 条件渲染与列表渲染结合 -->
<!-- v-if 判断 items 是否有内容,如果数组为空则显示提示文字“无数据” -->
<!-- 适用于条件渲染列表或根据情况展示不同内容的场景,避免浪费渲染资源 -->
<ul v-if="items.length > 0">
<li v-for="(item, index) in items" :key="item.id">
{{ item.name }}
</li>
</ul>
<p v-else>无数据</p>

<!-- 动态绑定class和style -->
<!-- 结合 :class 和 :style 指令,根据数据条件动态应用样式,比如根据 item.active 改变显示状态 -->
<!-- 应用于状态高亮、动态变化样式等场景 -->
<ul>
<li
v-for="(item, index) in items"
:key="item.id"
:class="{ active: item.active }"
:style="{ color: item.color }"
>
{{ item.name }}
</li>
</ul>

<!-- 列表项组件化 -->
<!-- 将列表项封装成组件,每个 item 传递给子组件,并通过 @remove 自定义事件实现删除功能 -->
<!-- 适用于复杂列表项结构或重复渲染同一结构的场景,提高代码复用性和维护性 -->
<ItemComponent
v-for="(item, index) in items"
:key="item.id"
:item="item"
@remove="removeItem(item.id)"
/>

<!-- 过滤和排序 -->
<!-- 通过 computed 属性筛选或排序列表项,并将过滤后的结果渲染 -->
<!-- 常用于需要动态排序、筛选的场景,如搜索结果或分类列表 -->
<ul>
<li v-for="(item, index) in filteredItems" :key="item.id">
{{ item.name }}
</li>
</ul>

<!-- 使用 transition-group 添加过渡动画 -->
<!-- 使用 transition-group 包裹列表项,实现添加、删除或重新排序的过渡动画效果 -->
<!-- 适用于需要动态展示动画效果的列表,如购物车、待办事项列表等 -->
<transition-group name="list" tag="ul">
<li v-for="(item, index) in items" :key="item.id" class="list-item">
{{ item.name }}
</li>
</transition-group>
</template>

<script>
export default {
data() {
return {
items: [
{ id: 1, name: "Item 1", active: true, color: "red" },
{ id: 2, name: "Item 2", active: false, color: "blue" },
{ id: 3, name: "Item 3", active: true, color: "green" },
],
};
},
computed: {
// 过滤或排序后的列表,计算属性
filteredItems() {
return this.items.filter(item => item.active).sort((a, b) => a.name.localeCompare(b.name));
},
},
methods: {
// 删除特定项
removeItem(id) {
this.items = this.items.filter(item => item.id !== id);
},
},
components: {
ItemComponent: {
props: ["item"],
template: `
<li>
{{ item.name }}
<button @click="$emit('remove')">删除</button>
</li>
`,
},
},
};
</script>

<style>
/* transition-group 动画样式 */
.list-enter-active,
.list-leave-active {
transition: opacity 0.5s;
}
.list-enter, .list-leave-to {
opacity: 0;
}
.active {
font-weight: bold;
}
</style>

列表过滤和列表排序

在 Vue 中,列表过滤和排序通常结合计算属性 `computed`​ 或直接在渲染前操作数据来实现。这两种操作都可以用来根据用户的输入或其他条件动态调整列表的显示内容和顺序。在vue中还可以用 `watch`​ 属性实现,但是比较繁琐,所以通常用 `computed`​ 来实现。
1. 列表过滤(filter)
列表过滤通常通过 JavaScript 数组的 `filter`​ 方法实现。

filter​ 方法的工作原理:

filter​ 方法会遍历数组中的每个元素,并将每个元素传递给回调函数:

  • 如果回调函数返回 true​,该元素会包含在新的数组中。
  • 如果回调函数返回 false​,该元素会被排除在新数组之外。
// 基本语法:array.filter(callback(element, index, array), thisArg)

// 参数说明:
// - `callback`: 用于测试每个元素的函数,接受以下参数:
// - `element`: 当前正在处理的元素
// - `index`: 当前元素的索引(可选)
// - `array`: 调用 filter 的数组本身(可选)
// - `thisArg`: 可选参数。指定 callback 内部的 this 值。

// 返回值:返回一个新数组,包含所有通过测试的元素(即回调函数返回 true 的元素)。

// 示例
const numbers = [1, 2, 3, 4, 5, 6];
const evenNumbers = numbers.filter((number) => number % 2 === 0);

console.log(evenNumbers); // 输出: [2, 4, 6]

通过过滤操作,显示满足特定条件的列表项。以下代码实现了一个按用户输入的关键字来筛选文章标题的过滤功能。

<template>
<div>
<!-- 搜索框,绑定关键字输入 -->
<input type="text" v-model="searchQuery" placeholder="输入关键字搜索文章" />

<!-- 通过 v-for 渲染过滤后的文章列表 -->
<ul>
<li v-for="article in filteredArticles" :key="article.id">
{{ article.title }}
</li>
</ul>
</div>
</template>

<script>
export default {
data() {
return {
searchQuery: "", // 绑定搜索输入
articles: [ // 示例文章列表
{ id: 1, title: "Vue 指南" },
{ id: 2, title: "深入理解 Vue 响应式原理" },
{ id: 3, title: "Vue CLI 快速上手" },
{ id: 4, title: "Vue 组件之间的通信" },
],
};
},
computed: {
// 过滤后的文章列表
filteredArticles() {
// 返回与搜索关键字匹配的文章
return this.articles.filter((article) =>
article.title.toLowerCase().includes(this.searchQuery.toLowerCase())
);
},
},
};
</script>

2. 列表排序(sort)
列表排序通常通过 JavaScript 数组的 `sort`​ 方法来实现。`sort`​ 方法会根据提供的比较函数对数组进行排序,并返回排序后的数组。Vue 中,可以在计算属性或方法中使用 `sort`​ 使列表根据条件动态排序。

sort​ 方法会对数组的每两个元素进行比较,返回值决定了元素的顺序:

  • 如果比较函数返回值小于 0​,则将第一个元素放在第二个元素之前(升序)。
  • 如果比较函数返回值大于 0​,则将第一个元素放在第二个元素之后(降序)。
  • 如果返回 0​,则两个元素的相对位置保持不变。
// 基本语法:array.sort((a, b) => { 比较逻辑... })

// - `a` 和 `b` 是数组中相邻的两个元素。
// - `return` 值:决定排序顺序。
// - `a - b`:升序排列(从小到大)。
// - `b - a`:降序排列(从大到小)。
// - 除此之外,比较逻辑还可以自己实现其他比较复杂的逻辑。
// 示例1
const numbers = [10, 5, 20, 15];

// 升序排序
numbers.sort((a, b) => a - b);
console.log(numbers); // 输出: [5, 10, 15, 20]

// 降序排序
numbers.sort((a, b) => b - a);
console.log(numbers); // 输出: [20, 15, 10, 5]

//示例2
const users = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 },
{ name: "Charlie", age: 35 }
];

// 按年龄升序排序
users.sort((a, b) => a.age - b.age);
console.log(users);
// 输出: [
// { name: "Bob", age: 25 },
// { name: "Alice", age: 30 },
// { name: "Charlie", age: 35 }
// ]

// 按年龄降序排序
users.sort((a, b) => b.age - a.age);
console.log(users);
// 输出: [
// { name: "Charlie", age: 35 },
// { name: "Alice", age: 30 },
// { name: "Bob", age: 25 }
// ]

//示例3
const users = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 },
{ name: "Charlie", age: 25 },
{ name: "David", age: 35 }
];

users.sort((a, b) => {
// 先按年龄排序(升序)
if (a.age !== b.age) {
return a.age - b.age;
}
// 如果年龄相同,按姓名字母顺序排序
return a.name.localeCompare(b.name);
});

console.log(users);
// 输出:
// [
// { name: "Bob", age: 25 },
// { name: "Charlie", age: 25 },
// { name: "Alice", age: 30 },
// { name: "David", age: 35 }
// ]

排序可以通过条件对列表中的元素进行升序或降序排列。以下代码实现了根据用户选择的排序方式对文章列表按标题或发布日期进行排序。
<template>
<div>
<!-- 排序选择框 -->
<label for="sort">排序方式:</label>
<select id="sort" v-model="sortOrder">
<option value="title">按标题排序</option>
<option value="date">按日期排序</option>
</select>

<!-- 通过 v-for 渲染排序后的文章列表 -->
<ul>
<li v-for="article in sortedArticles" :key="article.id">
{{ article.title }} - {{ article.date }}
</li>
</ul>
</div>
</template>

<script>
export default {
data() {
return {
sortOrder: "title", // 默认按标题排序
articles: [ // 示例文章列表
{ id: 1, title: "Vue CLI 快速上手", date: "2024-09-10" },
{ id: 2, title: "Vue 指南", date: "2024-10-05" },
{ id: 3, title: "深入理解 Vue 响应式原理", date: "2024-07-15" },
{ id: 4, title: "Vue 组件之间的通信", date: "2024-08-22" },
],
};
},
computed: {
// 排序后的文章列表
sortedArticles() {
// 根据 sortOrder 动态排序文章列表
return [...this.articles].sort((a, b) => {
if (this.sortOrder === "title") {
// 按标题字母顺序排序
return a.title.localeCompare(b.title);
} else if (this.sortOrder === "date") {
// 按日期从新到旧排序
return new Date(b.date) - new Date(a.date);
}
});
},
},
};
</script>

3.过滤和排序结合使用
可以将过滤和排序结合到一个计算属性中,实现同时满足搜索和排序的需求。以下是将两个功能合并的示例。
<template>
<div>
<!-- 搜索框 -->
<input type="text" v-model="searchQuery" placeholder="输入关键字搜索文章" />

<!-- 排序选择框 -->
<label for="sort">排序方式:</label>
<select id="sort" v-model="sortOrder">
<option value="title">按标题排序</option>
<option value="date">按日期排序</option>
</select>

<!-- 渲染符合条件的文章列表 -->
<ul>
<li v-for="article in filteredAndSortedArticles" :key="article.id">
{{ article.title }} - {{ article.date }}
</li>
</ul>
</div>
</template>

<script>
export default {
data() {
return {
searchQuery: "", // 搜索关键字
sortOrder: "title", // 默认按标题排序
articles: [ // 示例文章列表
{ id: 1, title: "Vue CLI 快速上手", date: "2024-09-10" },
{ id: 2, title: "Vue 指南", date: "2024-10-05" },
{ id: 3, title: "深入理解 Vue 响应式原理", date: "2024-07-15" },
{ id: 4, title: "Vue 组件之间的通信", date: "2024-08-22" },
],
};
},
computed: {
// 同时进行过滤和排序后的文章列表
filteredAndSortedArticles() {
// 先过滤文章
const filteredArticles = this.articles.filter((article) =>
article.title.toLowerCase().includes(this.searchQuery.toLowerCase())
);

// 再根据排序条件排序过滤后的文章列表
return filteredArticles.sort((a, b) => {
if (this.sortOrder === "title") {
return a.title.localeCompare(b.title);
} else if (this.sortOrder === "date") {
return new Date(b.date) - new Date(a.date);
}
});
},
},
};
</script>

vue监测(对象和数组)数据的原理

Vue 使用JavaScript的函数创建**构造函数**,并为其创建对象实例,通过 `Object.defineProperty()`​ 来劫持对象的属性,然后创建 getter 和 setter 方法。当访问对象的属性时,会触发 getter;当修改对象属性时,会触发 setter。
function defineReactive(obj, key, val) {
// 递归处理对象的嵌套属性
observe(val);

// 定义 getter 和 setter
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
console.log(`Getting ${key}: ${val}`);
// 依赖收集:假设存在一个全局的Watcher来收集依赖
if (Dep.target) {
dep.depend(); // dep 是每个属性对应的依赖管理器
}
return val;
},
set(newVal) {
console.log(`Setting ${key}: ${newVal}`);
if (newVal !== val) {
val = newVal;
// 触发视图更新
dep.notify();
}
}
});
}

function observe(obj) {
if (typeof obj !== 'object' || obj === null) {
return; // 只有对象才能被观察
}

// 遍历对象的每个属性并进行响应式处理
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key]);
});
}

const data = {
name: 'Vue',
age: 4
};

observe(data); // 将对象变为响应式

// 测试响应式效果
data.name = 'Vue.js'; // 控制台输出 "Setting name: Vue.js"
console.log(data.name); // 控制台输出 "Getting name: Vue.js"

对于数组,Vue 会对数组的变更方法(例如 `push`​, `pop`​, `shift`​, `unshift`​, `splice`​ 等)做“劫持”。这些方法被重写,以便能够通知视图进行更新。
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);

// 重写部分数组方法
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(method => {
arrayMethods[method] = function(...args) {
const result = arrayProto[method].apply(this, args);
console.log(`Array method ${method} called, updating view.`);
// 通知视图更新,可以通过触发观察者的机制实现
return result;
};
});

function observeArray(arr) {
// 修改数组的原型链
arr.__proto__ = arrayMethods;
}

const data = {
items: [1, 2, 3]
};

observeArray(data.items); // 让数组变为响应式

// 测试响应式效果
data.items.push(4); // 控制台输出 "Array method push called, updating view."
console.log(data.items); // [1, 2, 3, 4]

Vue 3 使用了 **Proxy** 来实现响应式,它比 `Object.defineProperty`​ 更加强大,能够直接代理整个对象及其嵌套对象,并且支持拦截更多的操作(比如 `delete`​ 和 `in`​ 等)。这使得 Vue 3 在性能和灵活性上有所提高。
const data = {
name: 'Vue 3'
};

// 使用 Proxy 实现响应式
const handler = {
get(target, prop) {
console.log(`Accessing ${prop}`);
return target[prop];
},
set(target, prop, value) {
console.log(`Setting ${prop} to ${value}`);
target[prop] = value;
return true;
}
};

const proxyData = new Proxy(data, handler);

proxyData.name = 'Vue 3 Proxy'; // Setting name to Vue 3 Proxy
console.log(proxyData.name); // Accessing name

Vue表单数据收集

绑定表单数据时注意点
//1.type类型的输入框
账号:<input type="type" v-model = "userInfo.account"> <br/><br/>
密码:<input type="type" v-model = "userInfo.possword"> <br/><br/>

<!-- vue实例中的data数据 -->
data:{
userInfo:{
account:'',
possword:''
}
}
//2.radio类型的单选框
//和上面的type类型不一样,因为上面的text类型有输入的value,而这种类型的数据没有输入值,只有单选值。
//因此需要在这个标签内部加上这个value值,这样才能获取到选择的值。
性别:
男<input type="radio" name="sex" v-model = "userInfo.sex" value="male">
女<input type="radio" name="sex" v-model = "userInfo.sex" value="female"> <br/><br/>

<!-- vue实例中的data数据 -->
data:{
userInfo:{
sex:''
}
}
//3.checkbox类型的复选框
//由于也和这个单选框一样,只有选择,没有具体的输入这个value值,因此需要在标签内部加上这个value值
//除了需要在这个标签内部加上这个value值之外,还需要在vue实例的data中,通过数组的方式接收这个复选框所选择的值。
爱好:
学习<input type="checkbox" v-model = "userInfo.hobby" value="study">
运动<input type="checkbox" v-model = "userInfo.hobby" value="sport">
打游戏<input type="checkbox" v-model = "userInfo.hobby" value="games"> <br/><br/>

<!-- vue实例中的data数据 -->
data:{
userInfo:{
//数组接收复选框的值
hobby:[]
}
}
//如果复选框没有这个value值,那么勾选的本质就是一个布尔属性,
//如需要在勾选注意事项的时候,是可以不需要这个value值的,并且在data中接收到的这个值也是一个布尔值。
<input type="checkbox" v-model = 'userInfo.agree'>接受并订阅

<!-- vue实例中的data数据 -->
data:{
userInfo:{
agree:''
}
}
//4.select类型的下拉框
//这个由于也是选择框,没有输入具体的数据,因此需要在option中配置value值。并且在vue实例中的data数据用字符串接收即可。
所属地区:
<select v-model="userInfo.city">
<option value="">请选择所属地区</option>
<option value="江西">江西</option>
<option value="广东">广东</option>
<option value="贵州">贵州</option>
<option value="贵州">湖南</option>
</select>

<!-- vue实例中的data数据 -->
data:{
userInfo:{
city:''
}
}

v-model的三个修饰符
//1.Lazy
//指的是失去焦点再收集数据,如下在文本框中输入数据时
//不实时的根据用户的输入或者删除将内容进行数据的收集,而是在点击其他组件,失去焦点的时候再去收集全部的数据
<textarea v-model.lazy="userInfo.other"></textarea>
//2.number
//输入字符串转化为有效的数字。
//比如在输入用户的年龄或者电话号码的时候,如果后端没有做这个是否为全部数字的校验的时候,那么就可以通过这个指令进行校验了。
//这个指令一般搭配这个输入框的number类型一起使用
年龄:<input type="number" v-model.number = "userInfo.age">
//3.trim
//对输入的数据进行收尾的去除空格。
//这个在实际开发中,如果后端没有及时的对前端传过来的数据进行一个字符串的去空格的话
//也是可以通过前端的这个指令进行一个数据的去重的。
账号:<input type="type" v-model.trim = "userInfo.account">

Vue指令大全

Vue指令
  • v-model​:双向数据绑定。
  • v-bind​ **(**​ :) :动态绑定属性。
  • v-on​ **(**​ @) :绑定事件监听器。
  • v-if / v-else-if / v-else​:条件渲染。
  • v-show​:显示或隐藏元素。
  • v-for​:列表渲染。
  • v-bind:class v-bind:style​:动态绑定类名和样式。
  • v-once​:仅渲染一次,不会更新。
  • v-pre​:跳过编译,直接输出原始内容。
  • v-cloak​:防止模板闪现,需配合 CSS 使用。
  • v-html​:解析和插入 HTML 字符串。
  • v-slot​:定义插槽内容(用于子组件)。
<template>
<div>
<!-- v-model: 双向数据绑定,适用于表单元素 -->
<input v-model="name" placeholder="Enter your name">

<!-- v-bind: 动态绑定属性,例如绑定元素的样式或属性 -->
<img v-bind:src="imageUrl" alt="Image">

<!-- 缩写 : 表示 v-bind 的简写 -->
<img :src="imageUrl" alt="Image">

<!-- v-on: 绑定事件监听器,例如点击、输入等事件 -->
<button v-on:click="handleClick">Click me</button>

<!-- 缩写 @ 表示 v-on 的简写 -->
<button @click="handleClick">Click me</button>

<!-- v-if: 条件渲染,满足条件时渲染元素 -->
<p v-if="isVisible">This text is visible</p>

<!-- v-else-if / v-else: 结合 v-if 使用的条件判断 -->
<p v-else-if="isAnotherCondition">Another condition met</p>
<p v-else>Default case</p>

<!-- v-show: 控制元素的显示隐藏,通过设置 display 样式 -->
<p v-show="isVisible">This text is conditionally visible</p>

<!-- v-for: 循环渲染列表,遍历数组或对象 -->
<ul>
<li v-for="(item, index) in items" :key="index">{{ item }}</li>
</ul>

<!-- v-bind:class 和 v-bind:style: 动态绑定类名和样式 -->
<div :class="{ active: isActive }" :style="{ color: textColor }">
Styled Text
</div>

<!-- v-once: 只渲染元素和组件一次,之后不再更新 -->
<p v-once>Rendered only once</p>

<!-- v-pre: 跳过这个元素的编译过程,直接输出原始内容 -->
<p v-pre>{{ rawContent }}</p>

<!-- v-cloak: 防止未编译的模板内容闪现,通常结合 CSS 使用 -->
<p v-cloak>This content is cloaked until Vue compiles it</p>

<!-- v-html: 解析和插入 HTML 字符串,注意避免 XSS 安全问题 -->
<div v-html="htmlContent"></div>

<!-- v-slot: 用于定义插槽内容 -->
<custom-component>
<template v-slot:header>
Header Content
</template>
</custom-component>
</div>
</template>

<script>
export default {
data() {
return {
name: '',
imageUrl: 'https://example.com/image.jpg',
isVisible: true,
isAnotherCondition: false,
isActive: true,
textColor: 'red',
items: ['Item 1', 'Item 2', 'Item 3'],
rawContent: '{{ This will not be compiled }}',
htmlContent: '<strong>This is bold text</strong>'
};
},
methods: {
handleClick() {
console.log('Button clicked');
}
}
};
</script>

<style>
[v-cloak] {
display: none; /* 用于 v-cloak 指令的隐藏效果 */
}
</style>

自定义指令
// 1.函数式自定义指令

// 注册一个全局函数式自定义指令 v-focus,使元素自动聚焦
Vue.directive('focus', function(el) {
el.focus();
});

//2. 对象式自定义指令

// 注册一个全局对象式自定义指令 v-color,用于设置文本颜色
Vue.directive('color', {
// 绑定时设置初始颜色
bind(el, binding) {
el.style.color = binding.value;
},
// 数据更新时修改颜色
update(el, binding) {
el.style.color = binding.value;
}
});

//3.在组件内注册局部自定义指令
export default {
directives: {
// 局部函数式自定义指令 v-focus
focus: function(el) {
el.focus();
},
// 局部对象式自定义指令 v-color
color: {
bind(el, binding) {
el.style.color = binding.value;
},
update(el, binding) {
el.style.color = binding.value;
}
}
}
};
// 4.多单词命名用 - 连接,不要用大小写区分
Vue.directive('focus-element', function(el) {
el.focus();
});

Vue生命周期

  • 创建阶段

    • beforeCreate​:实例刚创建,数据和事件未初始化。
    • created​:数据和事件初始化完成,可以访问 data​ 和 methods​。
  • 挂载阶段

    • beforeMount​:模板已编译,尚未挂载到 DOM。
    • mounted​:实例已挂载到 DOM,可以操作渲染的 DOM 元素。
  • 更新阶段

    • beforeUpdate​:响应式数据更改,重新渲染前调用。
    • updated​:数据更改导致 DOM 更新完成。
  • 销毁阶段

    • beforeDestroy​:实例销毁前,可做清理工作。
    • destroyed​:实例已销毁,不再可访问数据和事件。
export default {
// 创建阶段
beforeCreate() {
// 实例初始化后调用,数据和事件尚未初始化,无法访问 `data` 和 `methods`
console.log('beforeCreate: 实例初始化,数据和事件尚未初始化');
},
created() {
// 数据和事件初始化完成,可访问 `data` 和 `methods`
// 常见场景:页面加载时自动获取用户信息、商品列表等。
console.log('created: 数据和事件初始化完成,可访问 data 和 methods');
},

// 挂载阶段
beforeMount() {
// 在模板编译完成、挂载到 DOM 之前调用,此时还未渲染到页面上
console.log('beforeMount: 模板编译完成,尚未挂载到 DOM');
},
mounted() {
// 组件挂载到 DOM 后调用,可以操作已渲染的 DOM 元素
// 常见场景:图表初始化、DOM 元素动画、手动聚焦输入框等。
// 使用 mounted 添加事件监听器,在 beforeDestroy 中移除它们,防止内存泄漏。
// 常见场景:监听窗口大小、滚动事件、全局事件等。
// 在 mounted 中初始化第三方插件或库(如图表、富文本编辑器),在 beforeDestroy 中卸载它们,以便在组件的生命周期内安全管理插件的状态。
// 常见场景:富文本编辑器、地图库、图表库等插件的管理。
console.log('mounted: 组件已挂载到 DOM,可操作 DOM');
},

// 更新阶段
beforeUpdate() {
// 响应式数据更新,重新渲染之前调用,未更新 DOM
console.log('beforeUpdate: 响应式数据将更新,尚未重新渲染 DOM');
},
updated() {
// 数据更改导致 DOM 更新后调用,可访问更新后的 DOM
// 常见场景:更新表格或图表的数据、处理动画等。
console.log('updated: 数据已更新,DOM 已重新渲染');
},

// 销毁阶段
beforeDestroy() {
// 实例销毁前调用,仍可访问实例数据和方法,适合清理工作
// beforeDestroy 钩子中常用于清理未完成的异步任务(如 setInterval、setTimeout 等),确保组件销毁后不会继续执行任务,防止内存泄漏。
// 常见场景:定时刷新数据、轮询任务等。
console.log('beforeDestroy: 实例即将销毁,可清理事件监听或定时器');
},
destroyed() {
// 实例销毁后调用,无法访问 `data` 和 `methods`
console.log('destroyed: 实例已销毁,数据和事件不可访问');
}
};

Vue的Render渲染函数

Vue3

VUE3 学习笔记(目录)——官方文档的学习路线梳理

ref() 和reactive()实现响应式数据

Vue组件化编程

组件基础知识

概念
Vue 组件是独立的、可复用的代码单元,每个组件封装了其自身的模板、逻辑和样式。组件可以嵌套使用,并且可以进行数据的传递,从而形成具有逻辑关系的模块化页面结构。

优势

  • 复用性:可以将逻辑和 UI 封装在组件中,在不同页面或项目中重复使用。
  • 模块化:将页面划分成多个组件,可以独立开发、调试和维护。
  • 可维护性:组件使代码更易于理解和管理,降低了代码的复杂度。
  • 隔离性:每个组件可以有自己的样式、逻辑,互不影响,便于样式和功能的单独控制。
创建和使用组件的基本方法
  • 创建组件:创建一个 .vue​ 文件,定义模板、逻辑和样式。
  • 注册组件:在父组件中引入该组件,并在 components​ 对象中注册。
  • 使用组件:在父组件模板中通过自定义标签的方式来引用该子组件。

示例

假设创建一个名为 HelloWorld.vue​ 的组件,并在根组件 App.vue​ 中使用它。

<!-- HelloWorld.vue -->
<template>
<div class="hello">
<h1>{{ message }}</h1>
</div>
</template>

<script>
export default {
name: 'HelloWorld', // 组件名
props: {
message: String // 接受父组件传递的 message 属性
}
};
</script>

<style scoped>
.hello {
color: blue;
}
</style>

<!-- App.vue -->
<template>
<div id="app">
<!-- 使用组件并传递数据 -->
<HelloWorld message="Welcome to Vue!" />
</div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'; // 导入组件

export default {
name: 'App',
components: {
HelloWorld // 注册组件
}
};
</script>

<style>
#app {
font-family: Arial, sans-serif;
}
</style>

export default 的使用
在 Vue 组件中,`export default`​ 用于导出组件配置对象,它使组件可以在其他文件中被引入并使用。组件的配置对象定义了模板、数据、方法、生命周期等内容。
// MyComponent.vue
<template>
<div>
<p>This is my custom component</p>
</div>
</template>

<script>
export default {
name: 'MyComponent', // 导出组件的配置对象
data() {
return {
example: 'Hello, Vue!'
};
},
methods: {
greet() {
console.log(this.example);
}
}
};
</script>

<style scoped>
/* 组件的样式 */
</style>

组件通信

Vue 提供了两种方式来实现父子组件之间的通信:通过 `props`​ 让父组件向子组件传递数据,通过 `$emit`​ 让子组件向父组件传递事件。

总结:**父子组件通信**:通过 `props`​ 和 `$emit`​ 来实现数据和事件的传递。
父组件通过 props向子组件传递数据
  • 父组件使用 props​ 向子组件传递数据,子组件通过 props​ 接收数据。
<!-- 父组件代码(App.vue) -->
<template>
<div>
<ChildComponent :message="parentMessage" />
</div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
name: 'App',
data() {
return {
parentMessage: 'Hello from Parent'
};
},
components: {
ChildComponent
}
};
</script>
<!-- 子组件代码(ChildComponent.vue) -->
<template>
<div>
<p>{{ message }}</p> <!-- 使用 props 接收父组件传递的数据 -->
</div>
</template>

<script>
export default {
name: 'ChildComponent',
props: {
message: String // 定义接收父组件传递的 props
}
};
</script>
子组件通过 $emit向父组件传递事件
  • 子组件通过 $emit​ 向父组件发送事件,父组件通过 v-on​ 监听该事件并执行相应的操作。
<!-- 父组件代码(App.vue) -->
<template>
<div>
<ChildComponent @childEvent="handleChildEvent" />
</div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
name: 'App',
methods: {
handleChildEvent(payload) {
console.log('Event received from child:', payload);
}
},
components: {
ChildComponent
}
};
</script>

<!-- 子组件代码(ChildComponent.vue) -->
<template>
<div>
<button @click="sendEventToParent">Send Event to Parent</button>
</div>
</template>

<script>
export default {
name: 'ChildComponent',
methods: {
sendEventToParent() {
this.$emit('childEvent', 'Hello from Child'); // 触发事件并传递数据
}
}
};
</script>

使用 provide inject跨层级传递数据

provide​ 和 inject​ 允许跨越多层组件进行数据传递。provide​ 在祖先组件中定义数据,inject​ 在后代组件中接收数据。

<!-- 祖先组件代码(App.vue) -->
<template>
<div>
<ChildComponent />
</div>
</template>

<script>
export default {
name: 'Ancestor',
provide() {
return {
message: 'Hello from Ancestor'
};
}
};
</script>

<!-- 孙子组件代码(ChildComponent.vue) -->
<template>
<div>
<p>{{ injectedMessage }}</p>
</div>
</template>

<script>
export default {
inject: ['message'], // 接收祖先组件提供的数据
computed: {
injectedMessage() {
return this.message;
}
}
};
</script>

使用Vuex或Pinia来进行全局状态管理

Vue3如何使用Pinia详细介绍、pinia持久化存储(pinia-plugin-persistedstate详细配置)

// 1.安装Pinia
npm install pinia
// 2.创建一个 store(例如 useStore.js)
// state:用于定义存储的数据。
// actions:用于修改 state 的方法。
// getters:用于获取计算后的数据。
import { defineStore } from 'pinia';

export const useStore = defineStore('main', {
state: () => ({
message: 'Hello from Pinia'
}),
actions: {
updateMessage(newMessage) {
this.message = newMessage;
}
},
getters: {
upperCaseMessage: (state) => state.message.toUpperCase()
}
});

// 3.在应用中使用 Pinia
// 创建一个 Pinia 实例并在 Vue 应用中注册
// 在应用的入口文件(如 main.js 或 main.ts)中,使用 createPinia 创建 Pinia 实例,并将其挂载到 Vue 应用中。

import { createApp } from 'vue';
import App from './App.vue';
import { createPinia } from 'pinia';
const app = createApp(App);
// 创建 Pinia 实例并挂载到应用
app.use(createPinia());
app.mount('#app');

// 4.在组件中使用 Pinia store
//您可以在组件中通过 useStore 方法访问和修改 Pinia store 中的状态。
//app.vue
<template>
<div>
<h1>{{ message }}</h1>
<button @click="changeMessage">Change Message</button>
<p>{{ upperCaseMessage }}</p>
</div>
</template>

<script>
import { useStore } from './stores/useStore'; // 导入 Pinia store

export default {
setup() {
const store = useStore(); // 获取 store 实例

const changeMessage = () => {
store.updateMessage('Hello from Updated Pinia!');
};
//访问状态:通过 store.message 访问 store 中的 message。
//调用 action:通过 store.updateMessage() 调用定义的 action 来修改状态。
//使用 getter:通过 store.upperCaseMessage 使用 store 中定义的 getter。
return {
message: store.message, // 直接访问 state 中的属性
upperCaseMessage: store.upperCaseMessage, // 使用 getter
changeMessage // 调用 action
};
}
};
</script>

<style>
/* 样式 */
</style>

// 5.Pinia 的状态持久化
// Pinia 本身没有内建的状态持久化功能,但可以使用插件来持久化状态。
//最常见的做法是结合 pinia-plugin-persistedstate 插件来实现。

Vue七大对象

1.el

2.data

3.method

4.template

5.render

6.computed

7.watch

Vue使用注意

注册组件父子嵌套和Router路由

我们在使用Vue组件时,展示Vue组件可以分两种方式,一种是父子组件嵌套,另一种是使用Router渲染。我们在一个组件里注册使用另一个组件时,属于**组件嵌套。**

在Vue中,直接注册组件和使用 `Router`​ 来管理组件显示有一些重要区别:

组件嵌套:

  • 在一个组件里注册另一个组件后, 我们使用 <嵌套的子组件名/>​ 进行组件的展示。

Router:

  • 当我们配置好路由后,使用 <router-view name="header"></router-view>​ 来匹配展示组件。