软件开发 --- vue之初体验
渐进式JavaScript 框架
一个易于上手、性能卓越、生态丰富的构建Web用户界面的轻量级js框架。
- 渐进式(Progressive): 意味着你可以根据需求逐步采用Vue的特性。可以只使用Vue的核心功能,也可以使用它的全家桶。
- 易于上手(Approachable): Vue的设计注重直观性和简单性,学习曲线平缓,对初学者友好。
- 性能卓越(Performant): Vue采用虚拟DOM和响应式系统,确保高效的页面渲染和更新。
- 生态丰富(Versatile): Vue提供了丰富的功能和生态系统,可以用于构建各种类型的Web应用。
表达式
html中一嵌入的一段js就是表达式
<div id="app"> <p>I have a {{ product }}</p> <p>{{ product + 's' }}</p> <p>{{ isWorking ? 'YES' : 'NO' }}</p> <p>{{ product.getSalePrice() }}</p> </div>
指令
指令是特殊的 HTML 属性,以v-
开头,用于在模板中给html元素附加特殊的行为
根据真值(truthiness)来插入/移除元素
v-if
v-for

类似于后端的if else 这也是后端的语法特性
切换display: none CSS属性:
<script setup> const showProductDetails = ref(false) </script> <template> <p v-show="showProductDetails">产品详情内容</p> <button @click="showProductDetails = !showProductDetails"> 切换显示 </button> </template>
这是一个条件显示的指令,具体解释:
v-show是Vue的一个内置指令
它通过切换元素的CSS display属性来控制元素的显示和隐藏
showProductDetails是一个布尔值变量:
当showProductDetails为true时,元素会显示
当showProductDetails为false时,元素会隐藏(display: none)
与v-if的区别:
v-show只是切换CSS display属性,元素始终会被渲染
v-if则是真正的条件渲染,元素会被完全添加或移除
使用场景:
如果需要频繁切换,建议使用v-show(性能更好)
如果条件很少改变,建议使用v-if
双向数据绑定
<input v-model="message"> <p>你输入的内容是:{{ message }}</p>
这里,v-model
实现了 input
的双向绑定,输入框的值会更新 message
,而 message
的值变化时,输入框也会同步更新。
<input v-model.lazy="message">
- 默认情况下,v-model在每次输入字符时都会更新数据
- 添加.lazy后,只有在输入框失去焦点或按回车时才会更新数据
<input v-model.number="age">
- 自动将用户输入的值转换为数字类型
<input v-model.trim="username">
去除首尾空格
列表渲染
1. 基础列表渲染
<li v-for="item in items" :key="item.id"> {{ item }} </li>
说明,等价于多个li标签自动生成
这是最基本的列表渲染方式
:key 绑定是 Vue 强烈推荐的做法,用于优化虚拟 DOM 的更新性能
item.id 应该是唯一的标识符
2. 带索引的数组遍历
<li v-for="(item, index) in items"> <!-- 可访问 item 和 index --> </li>
说明:
- index 是当前项的索引,从 0 开始
- 参数顺序固定:第一个是当前项,第二个是索引
3. 对象遍历
<li v-for="(value, key) in object"> <!-- 可访问 value 和 key --> </li>
说明:
value 是对象的值
key 是对象的键名
可以遍历对象的所有可枚举属性
4. 组件列表渲染
<CartProduct v-for="item in products" :product="item" :key="item.id" />
说明:
- 用于渲染组件列表
- :product 通过 props 向子组件传递数据
- 每个组件实例都需要有唯一的 :key
数据绑定
1. 链接地址绑定
<!-- 完整写法 --> <a v-bind:href="url">链接</a> <!-- 简写方式 --> <a :href="url">链接</a>
说明:v-bind 可以简写为 :,两种方式效果完全相同
2. 按钮禁用状态绑定
<button :disabled="isButtonDisabled">按钮</button>
说明:
- 当 isButtonDisabled 为 true 时,按钮会被禁用
- 当 isButtonDisabled 为 false 时,disabled 属性会被移除
data() { return { isButtonDisabled: true // 按钮将被禁用 } }
3. 类名动态绑定
<div :class="{ active: isActive }">内容</div>
说明:
- 使用对象语法绑定类名
- 当 isActive 为 true 时,会添加 active 类
data() { return { isActive: true // 元素会有 active 类 } }
4. 内联样式绑定
<div :style="{ color: activeColor }">文本</div>
说明:
- 直接绑定到内联样式
- activeColor 是一个数据属性
data() { return { activeColor: 'red' // 文本将变为红色 } }
实例
就是DOM处理器。一个 Vue 实例通过 el
绑定一个根 DOM 元素,用于管理html视图。
通常情况下,Vue 推荐使用 单一根实例,并通过 组件化 来管理不同的功能模块。多个 Vue 实例的做法虽然可以实现,但会让代码变得难以维护和扩展,尤其在复杂的应用中。通常,使用 Vue Router 和 Vuex 来管理应用状态和视图切换,是更加推荐的方式。
模板
模板使得html具有后端语言的某些特性。vue模板和jsp模板或者模板引擎是一个概念。就是动态化的html。比如jsp模板可以在html中编写java对象,html只显示执行后的结果。
<div id="app"> <p>{{ message }}</p> <!-- 数据绑定,显示 message --> </div>
message 就是一个html中的变量,html中没有变量的概念,但是模板支持了后端语言特性。
message 值来自于js代码
数据
Vue 会监听数据的变化,并根据数据自动更新视图
data() { return { message: 'Hello Vue!' }; }
message
是组件中的一个数据,当它改变时,Vue 会自动更新视图中的 {{ message }}
。
方法
方法是 Vue 实例中的函数,通常用于响应用户的事件或执行一些操作。方法通常不具备计算属性的缓存特性,每次调用都会执行。
methods: { greet() { alert('Hello, ' + this.name); } }
greet
是一个方法,当触发某个事件时,它会执行,并弹出提示框。
生命周期钩子
Vue 实例在创建过程中会经历一系列的生命周期阶段,例如创建、挂载、更新、销毁等。生命周期钩子是指在这些不同阶段自动调用的函数。
比如:
created
:vue创建完成但为挂载到DOM上时会执行mounted
:组件挂载DOM完成后,DOM 已渲染。时会执行destroyed
:组件销毁时调用。
created() { console.log('Vue 实例已创建'); }
插槽
简单来说,插槽就是一种在子组件中预留的位置,父组件可以把自己的标签内的内容插入到这些预留的位置中。
通俗解释:
想象你在做一个模板(比如一个卡片组件),这个卡片组件有一个固定的外框,但卡片的内容是灵活的,可以由父组件来决定。插槽就像是卡片上的“空白区域”,父组件可以在这个区域放置自己想要的内容。
1. 单个插槽(Single Slot)
<!-- ChildComponent.vue --> <template> <div> <h2>I'm a title</h2> <slot> <!-- 默认内容,只有在没有提供插槽内容时才显示 --> Only displayed if no slot content </slot> </div> </template>
使用组件:
<!-- ParentComponent.vue -->
<template>
<MyComponent>
<p>This will go in the slot</p>
</MyComponent>
</template>
单个插槽
<template> <div> <h2>I'm a title</h2> <!-- 定义一个插槽 --> <slot> Only displayed if no slot content </slot> </div> </template> <script> export default { name: 'MyTitle' } </script>
然后创建一个使用该组件的父组件:
<template> <div> <!-- 使用方式1: 不传入内容,将显示默认内容 --> <MyTitle /> <!-- 使用方式2: 传入内容 --> <MyTitle> <p>This will go in the slot</p> </MyTitle> </div> </template> <script> import MyTitle from './MyTitle.vue' export default { name: 'ParentComponent', components: { MyTitle } } </script>
多个插槽
<template> <div class="container"> <!-- 页头插槽 --> <header class="header"> <slot name="header"> <!-- 默认页头内容 --> <h1>默认页头</h1> </slot> </header> <!-- 主要内容插槽 --> <main class="main"> <slot> <!-- 默认主要内容 --> <p>默认内容区域</p> </slot> </main> <!-- 页脚插槽 --> <footer class="footer"> <slot name="footer"> <!-- 默认页脚内容 --> <p>默认页脚</p> </slot> </footer> </div> </template> <script> export default { name: 'AppLayout', // 添加组件选项 created() { console.log('AppLayout组件已创建') // 添加日志 }, mounted() { console.log('AppLayout组件已挂载') // 添加日志 } } </script> <style scoped> .container { display: flex; flex-direction: column; min-height: 100vh; } .header { padding: 1rem; background-color: #f8f9fa; } .main { flex: 1; padding: 2rem; } .footer { padding: 1rem; background-color: #f8f9fa; text-align: center; } </style>
然后创建一个使用这个布局的HomePage组件:
<template> <AppLayout> <!-- 使用具名插槽 header --> <template v-slot:header> <h1>首页标题</h1> <nav>导航菜单</nav> </template> <!-- 默认插槽内容 --> <div class="content"> <h2>欢迎访问首页</h2> <p>这是主要内容区域</p> </div> <!-- 使用具名插槽 footer --> <template v-slot:footer> <p>版权所有 © 2024</p> </template> </AppLayout> </template> <script> import AppLayout from './AppLayout.vue' export default { name: 'HomePage', components: { AppLayout }, created() { console.log('HomePage组件已创建') // 添加日志 } } </script>
组件
vue最重要的特性之一。组件是 Vue 中可重用的、封装的独立单元。每个组件有自己的模板、数据和方法,组件可以嵌套在其他组件中,构建更复杂的界面。、
// 定义一个组件 Vue.component('my-component', { template: '<p>这是一个组件</p>' }); // 使用组件 <my-component></my-component>
举例一个复杂一点的组件
<template> <ComponentName --自定义组件名称 message="欢迎使用" --自定义组件参数 :product="{ id: 1, name: '示例商品', price: 99 }" --定义一个产品对象传入组件参数 email="user@example.com" /> </template>
<script setup> // 导入核心功能和子组件 import { ref, watch, computed } from 'vue' import Product from './Product.vue' //引入其他组件 import Review from './Review.vue' // 定义组件接收的参数 const props = defineProps({ // 消息提示文本 message: String, // 商品对象 product: Object, // 邮箱(带验证) email: { type: String, required: true, default: 'none', validator: (value) => { // 邮箱格式验证 return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) //[^\s@]+ - 最后一次匹配一个或多个非空白字符和非@字符 } } }) // 响应式数据 const firstName = ref('Vue') const lastName = ref('Mastery') // 计算属性:完整名称 const fullName = computed(() => { return firstName.value + ' ' + lastName.value }) // 监听 firstName 变化 watch(firstName, (value, oldValue) => { console.log(`名字从 ${oldValue} 变为 ${value}`) // 可以在这里执行一些副作用,比如更新其他相关数据 }) // 示例方法 const updateName = (newFirstName) => { firstName.value = newFirstName } const handleProductSelect = (selectedProduct) => { console.log('选择的商品:', selectedProduct) } const submitReview = (reviewData) => { console.log('提交评论:', reviewData) } </script> <template> <div class="component-container"> <!-- 显示消息 --> <span class="message">{{ message }}</span> <!-- 显示姓名 --> <div class="name-section"> <h3>姓名信息</h3> <p>全名:{{ fullName }}</p> <input v-model="firstName" placeholder="输入名字" > <input v-model="lastName" placeholder="输入姓氏" > </div> <!-- 商品组件 --> <Product v-if="product" :product-data="product" @select="handleProductSelect" /> <!-- 评论组件 --> <Review :email="email" @submit="submitReview" /> </div> </template>
事件
事件处理用于响应用户的操作(如点击按钮、输入文本等)。Vue 通过 v-on
指令绑定事件,或者使用简写 @
。
基础点击事件:
<template> <button @click="handleClick">点击我</button> </template> <script> export default { methods: { handleClick() { console.log('按钮被点击了') } } } </script>
带参数的事件:
<template> <button @click="addToCart(product, quantity)"> 添加到购物车 </button> </template> <script> export default { methods: { addToCart(product, quantity) { console.log(`添加${quantity}个${product.name}`) } } } </script>
表单提交
<template> <form @submit.prevent="handleSubmit"> <input type="text" v-model="username"> <button type="submit">提交</button> </form> </template> <script> export default { data() { return { username: '' } }, methods: { handleSubmit() { console.log('表单提交') } } } </script>
组合按键
<template> <input @keyup.ctrl.s="saveData"> <!-- ctrl+s --> </template> <script> export default { methods: { saveData() { console.log('保存数据') } } } </script>
仅一次触发
<template> <img @mouseover.once="showImage" src="/image.jpg"> </template> <script> export default { methods: { showImage() { console.log('这个提示只会显示一次') } } } </script>
.stop - 阻止事件冒泡
<template> <div @click="handleOuter"> 外层 <button @click.stop="handleInner"> 内层按钮 </button> </div> </template> <script> export default { methods: { handleOuter() { console.log('外层点击') }, handleInner() { console.log('只触发内层点击,不会冒泡到外层') } } } </script>
.self - 只在事件在元素本身(而不是子元素)触发时才触发处理函数
<template> <div @click.self="handleClick"> 父元素 <button @click="handleButtonClick"> 子元素按钮 </button> </div> </template> <script> export default { methods: { handleClick() { console.log('只有点击父元素才会触发,点击子元素不会触发') }, handleButtonClick() { console.log('点击按钮') } } } </script>
按键修饰符(Key Modifiers):
<!-- 常用按键修饰符 --> .tab - Tab 键 .delete - Delete 和 Backspace 键 .esc - Esc 键 .space - 空格键 .up - 上箭头 .down - 下箭头 .left - 左箭头 .right - 右箭头 .ctrl - Ctrl 键 .alt - Alt 键 .shift - Shift 键 .meta - Windows/Command 键
<template> <!-- 按 Enter 键提交 --> <input @keyup.enter="submit"> <!-- 按 Esc 键关闭 --> <div @keyup.esc="close"> <!-- Ctrl + S 保存 --> <input @keyup.ctrl.s="save"> </template>
鼠标修饰符(Mouse Modifiers):
.left - 鼠标左键 .right - 鼠标右键 .middle - 鼠标中键
<template> <!-- 鼠标右键点击 --> <div @click.right="showMenu"> <!-- 鼠标左键点击 --> <button @click.left="handleClick"> <!-- 鼠标中键点击 --> <div @click.middle="handleMiddleClick"> </template>
自定义事件
数据流向:
- Props:父 → 子(数据向下传递)
- Events:子 → 父(数据向上传递)
这种机制确保了组件之间清晰的数据流动方向,便于维护和调试。
1. 子组件 (ButtonCounter.vue)
<script setup> // 定义要触发的事件 const emit = defineEmits(['incrementBy']) // 点击按钮时触发事件 const handleIncrement = () => { emit('incrementBy', 5) // 向父组件发送数值 5 } </script> <template> <div class="counter-button"> <button @click="handleIncrement" class="increment-btn" > 增加 5 </button> </div> </template> <style scoped> .increment-btn { padding: 10px 20px; background: #42b983; color: white; border: none; border-radius: 4px; cursor: pointer; } .increment-btn:hover { background: #3aa876; } </style>
父组件 (ParentComponent.vue)
<script setup> import { ref } from 'vue' import ButtonCounter from './ButtonCounter.vue' // 计数器的值 const count = ref(0) // 处理子组件发送的增量 const incWithVal = function(toAdd) { count.value += toAdd console.log(`计数增加了 ${toAdd},现在是 ${count.value}`) } </script> <template> <div class="counter-container"> <h2>计数器示例</h2> <div class="count-display"> 当前计数:{{ count }} </div> <!-- 使用子组件并监听其事件 --> <ButtonCounter v-on:incrementBy="incWithVal" /> <!-- 也可以使用简写方式 --> <ButtonCounter @incrementBy="incWithVal" /> <!-- 显示操作记录 --> <div class="log" v-if="count > 0"> 已经增加了 {{ count }} 次 </div> </div> </template> <style scoped> .counter-container { padding: 20px; max-width: 400px; margin: 0 auto; text-align: center; } .count-display { font-size: 24px; margin: 20px 0; padding: 15px; background: #f5f5f5; border-radius: 8px; } .log { margin-top: 20px; color: #666; font-style: italic; } </style>
使用方式
<template> <div class="app"> <h1>Vue 事件示例</h1> <ParentComponent /> </div> </template>
生命周期钩子函数
<script setup> import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onActivated, onDeactivated, ref } from 'vue' const count = ref(0) // 挂载前 onBeforeMount(() => { console.log('1. 组件即将挂载') // 可以进行数据准备 }) // 挂载后 onMounted(() => { console.log('2. 组件已挂载') // 可以访问 DOM // 可以调用 API fetchData() }) // 更新前 onBeforeUpdate(() => { console.log('3. 组件即将更新') // 可以获取更新前的 DOM 状态 }) // 更新后 onUpdated(() => { console.log('4. 组件已更新') // 可以访问更新后的 DOM }) // 卸载前 onBeforeUnmount(() => { console.log('5. 组件即将卸载') // 清理工作开始 }) // 卸载后 onUnmounted(() => { console.log('6. 组件已卸载') // 清理工作完成 }) // 被 keep-alive 缓存的组件激活时 onActivated(() => { console.log('组件被激活') // 可以重新获取数据 }) // 被 keep-alive 缓存的组件停用时 onDeactivated(() => { console.log('组件被停用') // 可以暂存数据 }) // 示例方法 const fetchData = async () => { try { const response = await fetch('https://api.example.com/data') const data = await response.json() console.log('数据获取成功', data) } catch (error) { console.error('数据获取失败', error) } } </script> <template> <div class="component"> <h2>生命周期示例</h2> <button @click="count++"> 点击次数:{{ count }} </button> </div> </template>
SPA
Vue.js 非常适合构建单页应用(SPA)。在 SPA 中,整个应用通常只加载一个 HTML 页面,后续的页面内容和视图都通过 AJAX 请求动态加载,页面不会完全刷新,用户体验更加流畅。
在单页应用中,Vue 的特点包括:
- Vue Router:Vue Router 是 Vue.js 的官方路由管理器,允许你在单页应用中实现页面导航。当你在应用中点击链接时,Vue Router 会加载不同的视图。
- Vuex:用于管理全局状态,在不同页面之间共享数据。
- 组件化:所有的视图和逻辑都通过组件进行管理,使得 SPA 更加模块化、可维护
MPA
Vue.js 也可以用于构建传统的多页应用(MPA)。在 MPA 中,每个页面都会重新加载整个 HTML 页面,页面之间的切换需要进行完整的刷新。Vue 在这种情况下可以作为每个页面的增强工具,在页面中逐个加载 Vue 实例来控制每个页面的局部视图和交互。
- Vue 并不强制要求是 SPA。它是一个渐进式的框架,可以根据需求选择在单页应用或多页应用中使用。
- 对于 SPA,Vue 提供了 Vue Router 和 Vuex 等功能来帮助构建流畅的前端应用。
- 对于 MPA,Vue 可以作为增强工具,用于在某些页面中提升交互性和动态功能,而不会影响整个应用的结构。
因此,Vue 既能用来构建现代化的 SPA,也能用来增强传统的 MPA。选择哪种方式取决于你的项目需求和架构。
计算属性
属性值的自动计算器,当依赖的数据发生变化时,计算属性会自动更新属性值。比如
将时间戳转换为可读日期格式。
computed: { formattedDate() { const date = new Date(this.timestamp); return date.toLocaleDateString(); } }
根据用户输入的价格和折扣计算折后价格。
computed: { discountedPrice() { return this.price * (1 - this.discount); // 基于 price 和 discount 计算折后价格 } }
根据某些数据动态修改元素的类名或样式。
computed: { buttonClass() { return { 'btn-active': this.isActive, 'btn-disabled': !this.isActive }; } }
计算布局和尺寸
computed: { columnCount() { return this.windowWidth > 800 ? 4 : 2; // 根据窗口宽度决定列数 } }
复杂的逻辑处理
computed: { isEligibleForDiscount() { return this.age > 18 && this.membershipLevel === 'gold'; } }
处理嵌套对象或数组
computed: { fullName() { return `${this.user.firstName} ${this.user.lastName}`; } }
库
1. create-vue
create-vue
是一个命令行工具,用于快速搭建 Vue.js 项目。它帮助开发者快速初始化一个 Vue 应用,并提供一个最小化的配置。使用 create-vue
命令,你可以立即创建一个 Vue 项目并开始开发,类似于 create-react-app
在 React 开发中的作用。
3. Vue Router
Vue Router 是 Vue.js 的官方路由库,用于在 单页面应用(SPA) 中实现页面间的导航。Vue Router 允许你将不同的 URL 路径映射到对应的 Vue 组件,用户在不同的路径之间切换时,浏览器不会刷新页面,而是动态渲染对应的组件。
例如:
在这个例子中,访问 /
路径会渲染 Home
组件,而访问 /about
路径会渲染 About
组件。
4. Vue DevTools
Vue DevTools 是一个浏览器扩展(支持 Chrome 和 Firefox),为 Vue.js 应用提供强大的调试工具。使用 Vue DevTools,你可以:
- 检查 Vue 组件树
- 查看组件的数据和状态
- 跟踪应用性能
- 观察 Vuex 中的事件和状态变化(如果你使用了 Vuex)
它是调试和优化 Vue 应用程序的一个非常有用的工具。
5. Nuxt.js
Nuxt.js 是一个基于 Vue.js 的 框架,用于简化开发过程,提供一些开箱即用的功能,如:
- 服务器端渲染(SSR):让应用可以在服务器端渲染页面,从而提高首屏加载速度和 SEO 优化。
- 代码分割:自动拆分 JavaScript 代码,按需加载,提高页面加载性能。
- 热重载:在开发过程中,自动刷新页面而不丢失状态。
- 静态生成:支持将应用生成静态文件,适用于博客、文档等不需要频繁动态变化的应用。
Nuxt.js 简化了 Vue.js 开发中的许多配置工作,使开发者能够更加专注于应用功能的实现。
这些工具和库大大提高了 Vue.js 开发的效率和可维护性。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
2020-11-30 我们如何打造一个近乎完美的密码字典