Vue3 写法示例与规范详细指南
作者:木易 士心
一、项目结构规范
1.推荐目录结构
src/ ├── assets/ # 静态资源(图片、字体等) ├── components/ # 公共组件(PascalCase命名) │ ├── Button.vue │ └── UserCard.vue ├── composables/ # 组合式函数(逻辑复用) │ └── useFetch.ts ├── router/ # 路由配置 │ └── index.ts ├── store/ # 状态管理(Pinia) │ └── user.ts ├── utils/ # 工具函数 │ └── format.ts ├── views/ # 页面级组件 │ └── Home.vue ├── App.vue # 根组件 └── main.ts # 入口文件
2.命名规则
组件文件:PascalCase(如 UserProfile.vue)
文件夹:复数形式(如 components/ 而非 component/)
组合式函数:useXxx 前缀(如 useAuth.ts)
二、代码编写规范
1. Script 部分(Composition API)
setup 语法糖
<script setup lang="ts"> import { ref, computed, onMounted } from 'vue'; import { fetchUser } from '@/api/user'; // 响应式数据 const count = ref(0); const user = ref<UserType | null>(null); // 计算属性 const doubleCount = computed(() => count.value * 2); // 生命周期 onMounted(async () => { user.value = await fetchUser(1); }); // 类型定义 interface UserType { id: number; name: string; } </script>
Props/Emits 类型化
const props = defineProps<{ modelValue: string; disabled?: boolean; }>(); const emit = defineEmits<{ (e: 'update:modelValue', value: string): void; (e: 'submit'): void; }>();
2. Template 部分
语义化标签
<template> <article class="article-card"> <header class="article-header"> <h2>{{ title }}</h2> </header> <main class="article-content"> <p>{{ content }}</p> </main> </article> </template>
条件渲染与列表
<template> <!-- 避免 v-if + v-for 同级 --> <template v-if="users.length"> <ul> <li v-for="user in users" :key="user.id"> {{ user.name }} </li> </ul> </template> <p v-else>No users found</p> </template>
3. Style 部分
Scoped CSS
<style scoped> .article-card { border: 1px solid #eee; transition: all 0.3s ease; } .article-card:hover { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } </style>
CSS 命名规范(BEM 示例)
.article-card {} .article-card__header {} .article-card__header--active {}
三、高级特性规范
1. 组合式函数(Composables)
// composables/useCounter.ts import { ref } from 'vue'; export function useCounter(initialValue = 0) { const count = ref(initialValue); const increment = () => count.value++; const decrement = () => count.value--; return { count, increment, decrement }; }
2. 组件通信
1.父子组件通讯
1. 父传子:使用 props
父组件通过属性(Props)向子组件传递数据。
父组件 (Parent.vue): <template> <div> <ChildComponent :title="pageTitle" :user="currentUser" /> </div> </template> <script setup> import { ref, reactive } from 'vue' import ChildComponent from './ChildComponent.vue' const pageTitle = ref('欢迎来到我的应用') const currentUser = reactive({ name: '张三', id: 123 }) </script>
子组件 (ChildComponent.vue): <template> <div> <h2>{{ title }}</h2> <p>用户: {{ user.name }} (ID: {{ user.id }})</p> </div> </template> <script setup> // 定义接收的 props const props = defineProps({ title: { type: String, required: true }, user: { type: Object, default: () => ({}) } }) // 在模板中直接使用 props.title 和 props.user // 注意:在 <script setup> 中,props 是一个对象,需要通过 props.xxx 访问 </script>
2. 子传父:使用 emit
子组件通过触发自定义事件向父组件传递数据。
子组件 (ChildComponent.vue): <template> <div> <button @click="handleClick">点击我通知父组件</button> </div> </template> <script setup> // 定义组件可以触发的事件 const emit = defineEmits(['updateUser', 'customEvent']) const handleClick = () => { // 触发事件,并传递数据 emit('updateUser', { id: 456, name: '李四' }) // 也可以触发不带参数的事件 emit('customEvent') } </script>
父组件 (Parent.vue): <template> <div> <ChildComponent @updateUser="handleUserUpdate" @customEvent="handleCustomEvent" /> <p>当前用户: {{ user.name }}</p> </div> </template> <script setup> import { ref } from 'vue' import ChildComponent from './ChildComponent.vue' const user = ref({ name: '王五', id: 789 }) const handleUserUpdate = (newUser) => { // 接收子组件传递过来的数据 user.value = newUser console.log('用户已更新:', newUser) } const handleCustomEvent = () => { console.log('自定义事件被触发了') } </script>
2.跨层级组件:provide/inject
当组件嵌套层级很深,逐层传递 props 会非常繁琐时,可以使用 provide 和 inject。
使用 provide / inject
祖先组件提供数据,后代组件(无论多深)注入并使用。
祖先组件 (App.vue 或 Layout.vue):
<template> <div> <DeeplyNestedChild /> </div> </template> <script setup> import { provide, ref } from 'vue' import DeeplyNestedChild from './components/DeeplyNestedChild.vue' // 提供一个响应式数据 const theme = ref('dark') const appConfig = { apiUrl: 'https://api.example.com', version: '1.0.0' } // 第一个参数是 key (推荐使用 Symbol 或字符串),第二个参数是提供的值 provide('THEME', theme) provide('APP_CONFIG', appConfig) </script>
后代组件 (DeeplyNestedChild.vue):
浅色版本 <template> <div :class="theme"> <p>当前主题: {{ theme }}</p> <p>API 地址: {{ config.apiUrl }}</p> </div> </template> <script setup> import { inject } from 'vue' // 注入数据,第二个参数是默认值(可选但推荐) const theme = inject('THEME', 'light') const config = inject('APP_CONFIG', {}) </script>
注意
如果 provide 的是 ref 或 reactive 对象,inject 得到的数据会保持响应性。
为避免命名冲突,建议使用 Symbol 作为 key:
浅色版本 // keys.js export const THEME_KEY = Symbol() export const CONFIG_KEY = Symbol() // 在 provide 和 inject 时使用这些 Symbol
3.其他通信方式
- $refs (子 -> 父): 父组件可以通过 ref获取子组件的实例,从而直接调用子组件的方法或访问其数据。这种方式会增加耦合度,应谨慎使用。
- 插槽 (Slots): 主要用于内容分发,但结合作用域插槽 (Scoped Slots),可以实现父组件向子组件传递数据,同时由父组件决定如何渲染。常用于列表、表格等组件。
- v-model: 是 props 和 emit 的语法糖,用于在表单组件上创建双向绑定。
3. 性能优化
虚拟滚动:长列表使用 vue-virtual-scroller
计算属性缓存:避免在模板中执行复杂计算
响应式优化:对大型对象使用 shallowRef
四、工具链配置
1. ESLint 配置(.eslintrc.js)
module.exports = { extends: [ 'plugin:vue/vue3-recommended', '@vue/typescript/recommended' ], rules: { 'vue/multi-word-component-names': 'off', // 允许单文件组件名 '@typescript-eslint/no-explicit-any': 'warn' } };
2. Prettier 配置(.prettierrc)
{ "semi": false, "singleQuote": true, "trailingComma": "es5", "printWidth": 100 }
3. Git 规范
提交信息格式:<_type>: <_subject>
强制检查:使用 Husky + Commitlint
feat: 添加用户登录功能
fix: 修复表单验证错误
chore: 更新依赖版本
五、反模式警示
避免混用 Options API 和 Composition API
<script> export default { data() { return { count: 0 } } // 不要与 setup() 混用 } </script> <script setup> const count = ref(0); // 冲突 </script>
禁止直接修改 Props
<script setup> const props = defineProps(['count']); // 错误方式 props.count++; // 正确方式:通过 emits const emit = defineEmits(['update:count']); emit('update:count', props.count + 1); </script>
避免深层响应式
// 性能开销大 const state = reactive({ user: { profile: { name: 'Alice' } } }); // 优化:对大型对象使用 shallowRef const largeData = shallowRef({ /* ... */ });
遵循以上规范可显著提升代码可维护性和团队协作效率,建议结合 Vue Style Guide 官方文档使用。
到此这篇关于Vue3 写法示例与规范指南的文章就介绍到这了,更多相关vue规范指南内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!