news 2026/9/15 7:35:07

Vue的基础原理和使用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue的基础原理和使用

一、Vue

1、前言

前端的基础知识参考:javaweb前端基础(HTML、CSS、Javascript、vue3、ajax/axios)-CSDN博客文章浏览阅读225次,点赞6次,收藏3次。本项目综合运用了三大核心技术,实现了一个完整的四段式页面布局(导航栏 → 搜索区 → 表格区 → 页脚),是前端入门的经典综合练习案例。https://blog.csdn.net/m0_47518801/article/details/163595469?spm=1001.2014.3001.5501

依赖环境是node.js,需先下载node.js,安装及配置参考:Node.js 下载安装与环境配置全流程(保姆级详解)| 图文详解,快速上手_node安装教程图文详解-CSDN博客文章浏览阅读10w+次,点赞818次,收藏1.3k次。本文是我在学习 Node.js 过程中整理的笔记,详细记录了 Node.js 的下载安装、环境配置、全局模块安装、镜像加速设置等操作步骤。虽然是学习笔记,但内容非常全面、细致,适合初学者和有需要的朋友参考。_node安装教程图文详解https://blog.csdn.net/Natsuago/article/details/145567734?spm=1001.2014.3001.5506

2、vue创建、启动

也可以参考如下连接创建快速上手 | Vue.jsVue.js - 渐进式的 JavaScript 框架https://cn.vuejs.org/guide/quick-start.html

各选项分析:
• JSX 支持:一般初学者不需要,除非你要用 JSX 语法
• Router:单页面应用路由,做项目基本都需要
• Pinia:状态管理,中大型项目需要
• Vitest:单元测试,初学者可以暂时不选
• 端到端测试:初学者不需要
• Linter(ESLint):代码检查,建议选上
• Prettier:代码格式化,建议选上
1. 对于初学者/学习目的,推荐选择:
• Router ✅(做项目基本必须)
• Pinia ✅(状态管理,常用)
• Linter ✅(帮助规范代码) - Prettier ✅(格式化代码)
2. 如果是最简单的学习项目,可以只选 Router。

安装成功的版本查看方式

当创建成功的时候,直接按照提示做就ok了(npm install主要是安装node_modules依赖)

2.1 bug_1:当输入 npm create vue@latest 时,报如下错误。

核心错误:EPERM: operation not permitted, open 'D:\nodejs\node_cache\_cacache\tmp\***'
npm 在写入缓存目录时没有权限

方式一以管理员身份运行 CMD,然后 cd /d D:\MicrosoftVSCodeLocation\vue(进入自己对应的目录)

方式二清除 npm 缓存(缓存文件可能已损坏或被锁定)

npm cache clean --force

方式三关闭占用文件的程序

以下程序可能锁定了缓存文件:
• VS Code(尤其是打开了相关项目时)
• 杀毒软件(如 360、火绒、Windows Defender)
• 其他终端/IDE
操作:
1. 关闭所有 VS Code 窗口
2. 暂时关闭杀毒软件
3. 关闭其他终端
4. 重新执行命令

方式四手动修改缓存目录权限
1. 打开文件资源管理器,进入 D:\nodejs\node_cache
2. 右键 → 属性 → 安全 → 编辑
3. 给当前用户添加 完全控制 权限
4. 勾选 "替换所有子对象权限"
5. 确定后重试

方式五更换缓存目录(推荐长期方案)
将缓存目录改到用户目录下,避免权限问题:

npm config set cache "C:\Users\你的用户名\.npm_cache" --global

方法六:删除缓存目录后重试(你自己对应的目录)

# 手动删除缓存目录 rd /s /q D:\nodejs\node_cache\_cacache

然后重新执行:npm create vue@latest就可以了

2.2 bug_2:npm install 依赖冲突

npm install 依赖冲突的错误原因
oxlint@1.74.0 ← 项目实际安装的版本
eslint-plugin-oxlint@1.73.0 要求 peer oxlint@~1.73.0 ← 期望的版本
create-vue 脚手架模板中两个包的版本号没对齐,属于脚手架的小 bug。

方法一:加 --legacy-peer-deps,跳过 peer 依赖的严格检查,直接安装。

## 优点:简单安全,不影响功能,缺点:每次 install 都要加参数 npm install --legacy-peer-deps ## 如果不想每次都加参数,可以全局设置:(可选) npm config set legacy-peer-deps true

方法二:加 --force(强制安装,忽略冲突。)

## 优点:简单,缺点:可能掩盖其他依赖问题 npm install --force

方法三:手动修复版本号。(推荐 ✅)

## 优点:彻底解决,缺点:需要手动操作 ## 打开 package.json,找到这两行: "oxlint": "~1.74.0", "eslint-plugin-oxlint": "~1.73.0" ## 改为版本一致: "oxlint": "~1.73.0", "eslint-plugin-oxlint": "~1.73.0"

然后重新执行:npm install

3、vue的项目结构

4、vue的两者API风格

5、案例

在vue项目中基于组合式API完成用户列表渲染

<script setup> import { ref } from 'vue' // ========== 职位映射表 ========== const positionMap = { '1': '班主任', '2': '讲师', '3': '学工主管', '4': '教研主管', '5': '咨询师' } // ========== 原始完整数据(不会被修改,作为查询的数据源) ========== const allEmpList = [ { id: 1, name: '令狐冲', gender: '1', avatar: 'https://api.dicebear.com/7.x/adventurer/svg?seed=linghu', position: '2', hireDate: '2020-08-08', lastOpTime: '2024-12-20 14:30:00' }, { id: 2, name: '任盈盈', gender: '2', avatar: 'https://api.dicebear.com/7.x/adventurer/svg?seed=renyingying', position: '1', hireDate: '2022-08-01', lastOpTime: '2024-12-19 09:15:00' }, { id: 3, name: '岳不群', gender: '1', avatar: 'https://api.dicebear.com/7.x/adventurer/svg?seed=yuebuqun', position: '4', hireDate: '2021-05-20', lastOpTime: '2024-12-18 16:45:00' } ] // ========== 使用 ref 创建响应式数组 ========== // ref() 将数据包裹为响应式引用 // 在 JS 中通过 .value 访问,在模板中自动解包无需 .value // 表格展示的数据(查询后会被筛选赋值) const empList = ref([...allEmpList]) // 搜索条件:姓名(v-model 双向绑定) const searchName = ref('') // 搜索条件:性别(v-model 双向绑定) const searchGender = ref('') // 搜索条件:职位(v-model 双向绑定) const searchPosition = ref('') // ========== 查询功能 ========== /** * 查询按钮点击事件 * 根据姓名、性别、职位三个条件从 allEmpList 中筛选数据 * 条件为空则不过滤该字段(即"全部") * * 用户输入 → v-model → searchName/searchGender/searchPosition ↓ 点击【查询】按钮 ↓ handleSearch() 执行 filter ↓ empList.value = 筛选结果 ↓ 视图自动更新(响应式) */ const handleSearch = () => { empList.value = allEmpList.filter(item => { // 姓名条件:模糊匹配(包含即可),忽略大小写 const nameMatch = !searchName.value || item.name.includes(searchName.value) // 性别条件:精确匹配,为空则不过滤 const genderMatch = !searchGender.value || item.gender === searchGender.value // 职位条件:精确匹配,为空则不过滤 const positionMatch = !searchPosition.value || item.position === searchPosition.value // 三个条件同时满足才返回 true(AND 逻辑) return nameMatch && genderMatch && positionMatch }) } // ========== 清空/重置功能 ========== /** * 清空按钮点击事件 * 重置所有搜索条件,并恢复显示全部数据 */ const handleReset = () => { // 清空搜索条件 searchName.value = '' searchGender.value = '' searchPosition.value = '' // 恢复显示全部数据 empList.value = [...allEmpList] } /** * 根据职位编号获取职位名称 * @param {string} code - 职位编号 * @returns {string} - 职位名称 */ const getPositionText = (code) => { return positionMap[code] || '未知' } /** * 编辑按钮点击事件 * @param {object} item - 当前行数据 */ const handleEdit = (item) => { alert(`正在编辑:${item.name}`) } /** * 删除按钮点击事件 * 注意:操作 ref 数组时,需要通过 .value 访问 * @param {object} item - 当前行数据 */ const handleDelete = (item) => { if (confirm(`确定要删除「${item.name}」吗?`)) { // 从展示列表中删除 const index = empList.value.findIndex(emp => emp.id === item.id) if (index !== -1) { empList.value.splice(index, 1) } // 同时从原始数据中删除(保持数据一致性) const allIndex = allEmpList.findIndex(emp => emp.id === item.id) if (allIndex !== -1) { allEmpList.splice(allIndex, 1) } } } </script> <template> <!-- ==================== 第二部分:搜索表单区域 ==================== --> <div class="search-form"> <!-- @submit.prevent 阻止表单默认提交行为,改为调用 handleSearch --> <form @submit.prevent="handleSearch"> <!-- 使用 v-model 双向绑定搜索关键字,双向绑定即数据模型与视图同步更新 --> <label for="name">姓名:</label> <input type="text" id="name" name="name" v-model="searchName" placeholder="请输入姓名"> <label for="gender">性别:</label> <select id="gender" name="gender" v-model="searchGender"> <option value="">请选择</option> <option value="1">男</option> <option value="2">女</option> </select> <label for="position">职位:</label> <select id="position" name="position" v-model="searchPosition"> <option value="">请选择</option> <option value="1">班主任</option> <option value="2">讲师</option> <option value="3">学工主管</option> <option value="4">教研主管</option> <option value="5">咨询师</option> </select> <!-- 查询按钮:type="submit" 触发表单提交,由 @submit.prevent 拦截并调用 handleSearch --> <button type="submit" class="btn-primary">查询</button> <!-- 清空按钮:type="button" 避免触发表单提交,调用 handleReset --> <button type="reset" class="btn-default" @click="handleReset">清空</button> </form> </div> <!-- ==================== 第三部分:表格展示区(Vue挂载区域) ==================== --> <div class="table-container"> <table> <!-- 表头 --> <thead> <tr> <th>序号</th> <th>姓名</th> <th>性别</th> <th>头像</th> <th>职位</th> <th>入职日期</th> <th>最后操作时间</th> <th>操作</th> </tr> </thead> <!-- 表体:使用 v-for 循环渲染响应式数据 --> <tbody> <!-- 空数据提示:当列表为空时显示 --> <tr v-if="empList.length === 0"> <td colspan="8" class="empty-tip">暂无数据</td> </tr> <tr v-for="(item, index) in empList" :key="item.id"> <!-- 序号:索引+1 --> <td>{{ index + 1 }}</td> <!-- 姓名 --> <td>{{ item.name }}</td> <!-- 性别:动态绑定class和文字 --> <td> <span :class="['gender-tag', item.gender === '1' ? 'gender-male' : 'gender-female']"> {{ item.gender === '1' ? '男' : '女' }} </span> </td> <!-- 头像:使用 :src 动态绑定 --> <td> <img :src="item.avatar" :alt="item.name" class="avatar"> </td> <!-- 职位:映射为文字 --> <td> <span class="position-tag">{{ getPositionText(item.position) }}</span> </td> <!-- 入职日期 --> <td>{{ item.hireDate }}</td> <!-- 最后操作时间 --> <td>{{ item.lastOpTime }}</td> <!-- 操作按钮 --> <td> <button class="btn btn-edit" @click="handleEdit(item)">编辑</button> <button class="btn btn-delete" @click="handleDelete(item)">删除</button> </td> </tr> </tbody> </table> </div> </template> <style scoped> /* ==================== 全局样式重置 ==================== */ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: "Microsoft YaHei", Arial, sans-serif; background-color: #f5f5f5; display: flex; flex-direction: column; min-height: 100vh; } /* ==================== 第一部分:顶部导航栏 ==================== */ .header { display: flex; justify-content: space-between; align-items: center; background-color: #d0d0d0; padding: 15px 20px; } .header .title { font-size: 28px; font-weight: bold; color: #333; } .header .logout { color: #555; text-decoration: none; font-size: 16px; padding: 8px 16px; border-radius: 4px; transition: all 0.3s ease; } .header .logout:hover { background-color: #ff4d4f; color: #fff; box-shadow: 0 2px 6px rgba(255, 77, 79, 0.4); } /* ==================== 第二部分:搜索表单区域 ==================== */ .search-form { padding: 20px; background-color: #fff; border-bottom: 1px solid #e0e0e0; } .search-form form { display: flex; align-items: center; gap: 15px; flex-wrap: wrap; } .search-form label { font-size: 14px; color: #555; margin-right: 5px; } .search-form input[type="text"], .search-form select { height: 32px; padding: 0 10px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; min-width: 120px; } .search-form input[type="text"]:focus, .search-form select:focus { border-color: #4a90d9; outline: none; } .search-form button { height: 32px; padding: 0 20px; font-size: 14px; border-radius: 4px; border: 1px solid #ccc; cursor: pointer; transition: all 0.3s; } .btn-primary { background-color: #1890ff; color: #fff; border-color: #1890ff; } .btn-primary:hover { background-color: #40a9ff; box-shadow: 0 2px 8px rgba(24, 144, 255, 0.4); } .btn-default { background-color: #fff; color: #666; border: 1px solid #d9d9d9; } .btn-default:hover { color: #1890ff; border-color: #1890ff; } /* ==================== 第三部分:表格展示区 ==================== */ .main-content { flex: 1; } .table-container { margin: 20px; background-color: #fff; border-radius: 6px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); padding: 20px; } .table-container table { width: 100%; border-collapse: collapse; font-size: 14px; } .table-container thead { background-color: #f0f5fa; } .table-container thead th { padding: 12px 15px; text-align: left; font-weight: bold; color: #333; border-bottom: 2px solid #4a90d9; white-space: nowrap; } .table-container tbody td { padding: 12px 15px; border-bottom: 1px solid #e8e8e8; color: #555; vertical-align: middle; } /* 偶数行浅灰色背景 */ .table-container tbody tr:nth-child(even) { background-color: #f5f7fa; } .table-container tbody tr:hover { background-color: #e6f7ff; } .table-container .avatar { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; border: 2px solid #e0e0e0; } /* 操作按钮通用样式 */ .table-container .btn { border: none; padding: 5px 12px; font-size: 12px; border-radius: 4px; cursor: pointer; transition: all 0.3s; margin-right: 8px; } .table-container .btn-edit { background-color: #52c41a; color: #fff; } .table-container .btn-edit:hover { background-color: #73d13d; box-shadow: 0 2px 6px rgba(82, 196, 26, 0.4); } .table-container .btn-delete { background-color: #ff4d4f; color: #fff; } .table-container .btn-delete:hover { background-color: #ff7875; box-shadow: 0 2px 6px rgba(255, 77, 79, 0.4); } /* 性别标签样式 */ .gender-tag { display: inline-block; padding: 2px 10px; border-radius: 10px; font-size: 12px; } .gender-male { background-color: #e6f3ff; color: #4a90d9; } .gender-female { background-color: #fff0f0; color: #e74c3c; } /* 职位标签样式 */ .position-tag { display: inline-block; padding: 2px 8px; border-radius: 3px; font-size: 12px; background-color: #f0f5ff; color: #2f54eb; } /* 空数据提示样式 */ .empty-tip { text-align: center; padding: 40px; color: #999; font-size: 16px; } /* ==================== 第四部分:页脚版权区域 ==================== */ .footer { background-color: #555; color: #fff; text-align: center; padding: 20px 0; line-height: 1.8; font-size: 13px; } .footer .company-name { font-size: 14px; letter-spacing: 1px; } .footer .copyright { font-size: 12px; opacity: 0.8; margin-top: 5px; } </style>

二、Element Plus

基于 Vue 3,面向设计师和开发者的组件库

一个 Vue 3 UI 框架 | Element PlusA Vue 3 based component library for designers and developershttps://element-plus.org/zh-CN/

1、element plus 快速入门

必须去到对应项目的文件包里下载

npm install element-plus --save

也可以这样下载(cmd)

在 package.json 中在出现如下,就是安装成功

# 安装成功执行这条指令查看版本 npm list element-plus

main.ts(在“指南”→“快速开始”里)

import './assets/main.css' import { createApp } from 'vue' import App from './App.vue' import router from './router' // 引入 Element Plus 组件库 import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' // Element Plus 组件默认使用英文,这里引入中文语言包 import zhCn from 'element-plus/es/locale/lang/zh-cn' // 一次性导入 Element Plus 的全部图标组件(注意:全量注册图标会增大打包体积。生产项目更推荐按需导入) import * as ElementPlusIconsVue from '@element-plus/icons-vue' const app = createApp(App) app.use(router) // 使用 Element Plus 组件库, 并指定使用中文语言包 app.use(ElementPlus, {locale: zhCn}) // 注册 Element Plus 图标组件 for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.mount('#app')

其他的参考 Element plus 官方文档就可以了

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 7:34:55

光伏CAD插件实战指南:从组件排布到施工图全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 7:34:00

2026年AI编程实战地图:场景化工具链协同指南

1. 这不是工具清单&#xff0c;而是一份2026年AI编程生产力的实战地图“2026年AI编程工具大全&#xff0c;33个主流工具一次看懂”——看到这个标题&#xff0c;你脑子里浮现的可能是一页密密麻麻的软件名官网链接一句话介绍的表格。但我要坦白告诉你&#xff1a;那种清单&…

作者头像 李华
网站建设 2026/9/15 7:31:24

Hibernate 核心原理与架构详解

Hibernate 核心原理与架构详解 定位&#xff1a;Hibernate 架构分层、启动引导、持久化流程、代理原理、事务连接与类型系统 适用版本&#xff1a;Hibernate ORM 6.x&#xff08;Jakarta Persistence 3.1&#xff09; 目录 整体架构启动引导持久化流程代理生成原理连接与事务集…

作者头像 李华
网站建设 2026/9/15 7:28:15

线程池生产级调优实战:参数计算、拒绝策略与异常处理解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 7:26:30

Unity3D捕鱼游戏开发全流程:对象池、鱼群AI与碰撞交互实战

简介&#xff1a;面向Unity3D初学者的捕鱼游戏期末作业工程&#xff0c;内含完整源程序&#xff0c;可直接运行体验捕鱼玩法。项目覆盖C#游戏逻辑、UI界面、音频管理、难度选择等模块&#xff0c;并配有清晰的代码结构与说明文档&#xff0c;适合课程设计、自学练手或二次开发。…

作者头像 李华