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({ data(){ return{ message:'hello vue' } } }) vm.$mount('#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 中的变化。
- Model(数据层) :Vue 使用响应式数据模型来管理数据。通过
reactive 或 ref API,Vue 可以将数据对象变成响应式对象,当数据发生变化时,相关的视图会自动更新。
- View(视图层) :Vue 通过模板(template)和指令(如
v-bind、v-model 等)来描述界面。视图会根据模型中的数据动态渲染和更新。
- 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, configurable: true });
console.log(obj.property);
|
使用 getter 和 setter 实现数据拦截。getter 和 setter 是 JavaScript 中用于对象属性的一种方法,主要用于控制属性的读取和写入行为。
const user = { _name: 'Alice' };
Object.defineProperty(user, 'name', {
get() { console.log('获取 name 属性'); return this._name; },
set(newValue) { console.log('设置 name 属性'); this._name = newValue; } });
console.log(user.name); user.name = 'Bob'; console.log(user.name);
|
应用场景
- 数据监听:
Object.defineProperty 可以通过自定义 getter 和 setter 函数来监听数据变化,从而实现对数据的拦截。
- 响应式实现:Vue2 中利用
Object.defineProperty 实现数据的响应式,将属性代理到 Vue 实例上,捕获数据的变化并更新视图。
- 防止属性修改:可以通过设置
writable: false 来使某些属性变为只读。
- 属性保护:通过设置
enumerable: false 隐藏属性,防止它在 for...in 或 Object.keys() 中被遍历到。
Vue基础知识
插值语法
<template>
<p>{{ message }}</p>
<img v-bind:src="imageSrc" alt="图片描述" /> <img :src="imageSrc" alt="图片描述" />
<p>{{ message.toUpperCase() }}</p>
<p>{{ isActive ? 'Active' : 'Inactive' }}</p>
<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> <p>{{ message }}</p> <img :src="imageSrc" alt="图片描述" /> <p :class="isActive ? 'active' : 'inactive'"></p>
<input v-model="inputText" /> <select v-model="selectedOption"> <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: '', selectedOption: 'option1' }; } }; </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> <p>全名:{{ fullName }}</p>
<p>折扣价格:{{ discountedPrice }}</p>
<p>年龄:{{ age }}</p> <button @click="increaseAge">增加年龄</button> </div> </template>
<script> export default { data() { return { firstName: '张', lastName: '三', price: 100, discount: 0.9, birthYear: 1990 } }, computed: { fullName() { return `${this.firstName} ${this.lastName}`; },
discountedPrice() { return this.price * this.discount; },
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; } } } </script>
|
总结
基本计算属性:
- 直接使用
computed 定义一个方法,返回计算结果。
- 示例:
fullName 通过 firstName 和 lastName 计算得出。
依赖数据的计算属性:
- 计算属性依赖的任何响应式数据发生变化时,计算属性会自动重新计算。
- 示例:
discountedPrice 依赖 price 和 discount。
计算属性的 Getter 和 Setter:
- 计算属性不仅可以是只读的,还可以定义 Getter 和 Setter。
- Getter 用于计算值,Setter 用于响应数据的更改。
- 示例:
age 通过 birthYear 计算并返回值,通过设置 age 更新 birthYear。
计算属性 vs 方法:
- 计算属性有缓存,只有在依赖的数据变化时才会重新计算。
- 方法则每次调用都会重新计算,适合不需要缓存的情况。
Vue监视属性(watch)
export default { data() { return { count: 0, name: 'Vue', user: { firstName: 'John', lastName: 'Doe' } }; }, watch: { count(newValue, oldValue) { console.log(`count changed from ${oldValue} to ${newValue}`); }, 'user.firstName': function(newValue, oldValue) { console.log(`user.firstName changed from ${oldValue} to ${newValue}`); },
user: { handler(newValue, oldValue) { console.log('user object changed:', newValue); }, deep: true },
name: { handler(newValue, oldValue) { console.log(`name changed from ${oldValue} to ${newValue}`); }, immediate: true } } created(){ this.$watch('question', (newQuestion) => { }) }
const unwatch = this.$watch('question', callback)
unwatch() };
|
应用场景
- 监视表单输入的变化:例如,监听用户输入的表单内容并实时进行验证。
- 监视对象或数组的深度变化:当需要监听对象内部某些嵌套的属性时,使用
deep: true 来实现深度监听。
- 执行异步操作:监视某个属性并在属性变化时进行 API 请求或异步操作,比如监听搜索关键词并触发搜索请求。
- 立即执行初始化逻辑:通过
immediate: true 实现立即调用回调,通常用于初始化时需执行的逻辑。
计算属性和监视属性的选择
| 特性 |
计算属性 (computed) |
监视属性 (watch) |
| 是否缓存 |
是(依赖不变则不会重复计算) |
否 |
| 适合的数据类型 |
适合同步计算且结果频繁使用的数据 |
适合执行异步操作或有副作用的监听逻辑 |
| 依赖数据变化时触发 |
自动基于依赖数据变化触发更新 |
手动设置需要监听的数据 |
| 常见应用场景 |
数据的组合、格式化和转换 |
异步请求、定时操作、深度监听对象 |
| 异步处理 |
不适用 |
支持(适合处理异步任务) |
Vue动态绑定class和style
<!-- 使用对象语法:根据条件添加类名 -->
<div :class="{ 'active': isActive, 'text-large': isLarge }"></div>
export default { data() { return { isActive: true, isLarge: false, }; } };
<!-- 使用数组语法:添加多个类 -->
<div :class="[classA, classB]"></div>
export default { data() { return { classA: 'blue', classB: 'bold' }; } };
<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' } }; } };
<div :class="{ 'active': isActive }" :style="{ color: textColor }"></div>
|
应用场景
<template> <div> <button :class="{ 'active-button': isActive }">Click Me</button>
<div @mouseover="isHovered = true" @mouseleave="isHovered = false" :class="{ 'hover-effect': isHovered }"> Hover over me </div>
<div :class="[isActive ? 'active' : '', hasError ? 'error' : '']"> Status message </div>
<div :style="{ width: progress + '%' }" class="progress-bar"> Loading... </div>
<transition name="fade"> <div v-if="showModal" class="modal"> Modal Content </div> </transition>
<div :class="theme"> Themed Content </div>
<div :style="{ fontSize: isMobile ? '14px' : '18px' }"> Responsive Text </div> </div> </template>
<script> export default { data() { return { isActive: false, isHovered: false, hasError: false, progress: 50, showModal: false, theme: 'dark', 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 { 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> <div> <div v-if="isLoggedIn"> {{ welcomeMessage() }} </div> <div v-else> {{ loginPrompt() }} </div>
<div v-if="getScoreCategory(score) === 'excellent'"> 成绩优异 </div> <div v-else-if="getScoreCategory(score) === 'pass'"> 及格 </div> <div v-else> 需要努力 </div>
<div v-show="isVisible"> 这个元素是可见的 </div>
<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>
<ul> <li v-for="(item, index) in items" :key="item.id"> {{ item.name }} </li> </ul>
<ul v-if="items.length > 0"> <li v-for="(item, index) in items" :key="item.id"> {{ item.name }} </li> </ul> <p v-else>无数据</p>
<ul> <li v-for="(item, index) in items" :key="item.id" :class="{ active: item.active }" :style="{ color: item.color }" > {{ item.name }} </li> </ul>
<ItemComponent v-for="(item, index) in items" :key="item.id" :item="item" @remove="removeItem(item.id)" />
<ul> <li v-for="(item, index) in filteredItems" :key="item.id"> {{ item.name }} </li> </ul>
<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>
.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,该元素会被排除在新数组之外。
const numbers = [1, 2, 3, 4, 5, 6]; const evenNumbers = numbers.filter((number) => number % 2 === 0);
console.log(evenNumbers);
|
通过过滤操作,显示满足特定条件的列表项。以下代码实现了一个按用户输入的关键字来筛选文章标题的过滤功能。
<template> <div> <input type="text" v-model="searchQuery" placeholder="输入关键字搜索文章" />
<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,则两个元素的相对位置保持不变。
const numbers = [10, 5, 20, 15];
numbers.sort((a, b) => a - b); console.log(numbers);
numbers.sort((a, b) => b - a); console.log(numbers);
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);
users.sort((a, b) => b.age - a.age); console.log(users);
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);
|
排序可以通过条件对列表中的元素进行升序或降序排列。以下代码实现了根据用户选择的排序方式对文章列表按标题或发布日期进行排序。
<template> <div> <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 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() { 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);
Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { console.log(`Getting ${key}: ${val}`); if (Dep.target) { dep.depend(); } 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'; console.log(data.name);
|
对于数组,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); console.log(data.items);
|
Vue 3 使用了 **Proxy** 来实现响应式,它比 `Object.defineProperty` 更加强大,能够直接代理整个对象及其嵌套对象,并且支持拦截更多的操作(比如 `delete` 和 `in` 等)。这使得 Vue 3 在性能和灵活性上有所提高。
const data = { name: 'Vue 3' };
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'; console.log(proxyData.name);
|
Vue表单数据收集
绑定表单数据时注意点
账号:<input type="type" v-model = "userInfo.account"> <br/><br/> 密码:<input type="type" v-model = "userInfo.possword"> <br/><br/>
<!-- vue实例中的data数据 --> data:{ userInfo:{ account:'', possword:'' } }
性别: 男<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:'' } }
爱好: 学习<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:[] } }
<input type="checkbox" v-model = 'userInfo.agree'>接受并订阅
<!-- vue实例中的data数据 --> data:{ userInfo:{ agree:'' } }
所属地区: <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的三个修饰符
<textarea v-model.lazy="userInfo.other"></textarea>
年龄:<input type="number" v-model.number = "userInfo.age">
账号:<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> <input v-model="name" placeholder="Enter your name">
<img v-bind:src="imageUrl" alt="Image">
<img :src="imageUrl" alt="Image">
<button v-on:click="handleClick">Click me</button>
<button @click="handleClick">Click me</button>
<p v-if="isVisible">This text is visible</p>
<p v-else-if="isAnotherCondition">Another condition met</p> <p v-else>Default case</p>
<p v-show="isVisible">This text is conditionally visible</p>
<ul> <li v-for="(item, index) in items" :key="index">{{ item }}</li> </ul>
<div :class="{ active: isActive }" :style="{ color: textColor }"> Styled Text </div>
<p v-once>Rendered only once</p>
<p v-pre>{{ rawContent }}</p>
<p v-cloak>This content is cloaked until Vue compiles it</p>
<div v-html="htmlContent"></div>
<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; } </style>
|
自定义指令
Vue.directive('focus', function(el) { el.focus(); });
Vue.directive('color', { bind(el, binding) { el.style.color = binding.value; }, update(el, binding) { el.style.color = binding.value; } });
export default { directives: { focus: function(el) { el.focus(); }, color: { bind(el, binding) { el.style.color = binding.value; }, update(el, binding) { el.style.color = binding.value; } } } };
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() { console.log('beforeCreate: 实例初始化,数据和事件尚未初始化'); }, created() { console.log('created: 数据和事件初始化完成,可访问 data 和 methods'); },
beforeMount() { console.log('beforeMount: 模板编译完成,尚未挂载到 DOM'); }, mounted() { console.log('mounted: 组件已挂载到 DOM,可操作 DOM'); },
beforeUpdate() { console.log('beforeUpdate: 响应式数据将更新,尚未重新渲染 DOM'); }, updated() { console.log('updated: 数据已更新,DOM 已重新渲染'); },
beforeDestroy() { console.log('beforeDestroy: 实例即将销毁,可清理事件监听或定时器'); }, destroyed() { console.log('destroyed: 实例已销毁,数据和事件不可访问'); } };
|
Vue的Render渲染函数
Vue3
VUE3 学习笔记(目录)——官方文档的学习路线梳理
ref() 和reactive()实现响应式数据
Vue组件化编程
组件基础知识
概念
Vue 组件是独立的、可复用的代码单元,每个组件封装了其自身的模板、逻辑和样式。组件可以嵌套使用,并且可以进行数据的传递,从而形成具有逻辑关系的模块化页面结构。
优势
- 复用性:可以将逻辑和 UI 封装在组件中,在不同页面或项目中重复使用。
- 模块化:将页面划分成多个组件,可以独立开发、调试和维护。
- 可维护性:组件使代码更易于理解和管理,降低了代码的复杂度。
- 隔离性:每个组件可以有自己的样式、逻辑,互不影响,便于样式和功能的单独控制。
创建和使用组件的基本方法
- 创建组件:创建一个
.vue 文件,定义模板、逻辑和样式。
- 注册组件:在父组件中引入该组件,并在
components 对象中注册。
- 使用组件:在父组件模板中通过自定义标签的方式来引用该子组件。
示例
假设创建一个名为 HelloWorld.vue 的组件,并在根组件 App.vue 中使用它。
<template> <div class="hello"> <h1>{{ message }}</h1> </div> </template>
<script> export default { name: 'HelloWorld', props: { message: String } }; </script>
<style scoped> .hello { color: blue; } </style>
|
<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 接收数据。
<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>
<template> <div> <p>{{ message }}</p> </div> </template>
<script> export default { name: 'ChildComponent', props: { message: String } }; </script>
|
子组件通过 $emit 向父组件传递事件
- 子组件通过
$emit 向父组件发送事件,父组件通过 v-on 监听该事件并执行相应的操作。
<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>
<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 在后代组件中接收数据。
<template> <div> <ChildComponent /> </div> </template>
<script> export default { name: 'Ancestor', provide() { return { message: 'Hello from Ancestor' }; } }; </script>
<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详细配置)
npm install pinia
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() } });
import { createApp } from 'vue'; import App from './App.vue'; import { createPinia } from 'pinia'; const app = createApp(App);
app.use(createPinia()); app.mount('#app');
<template> <div> <h1>{{ message }}</h1> <button @click="changeMessage">Change Message</button> <p>{{ upperCaseMessage }}</p> </div> </template>
<script> import { useStore } from './stores/useStore';
export default { setup() { const store = useStore();
const changeMessage = () => { store.updateMessage('Hello from Updated Pinia!'); };
return { message: store.message, upperCaseMessage: store.upperCaseMessage, changeMessage }; } }; </script>
<style>
</style>
|
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> 来匹配展示组件。