全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2024-10-09_【.】浅读Vue3代码10万行,总结出30个代码规范

您的位置:首页 >> 新闻 >> 行业资讯

【.】浅读Vue3代码10万行,总结出30个代码规范 点击关注公众号,“技术干货”及时达! 不管是Vue.js源码,还是UI组件库Element-plus,只要有多人协同开发,代码规范上多多少少都会有一些"百花齐放"。即使像Vue.js源码,不同开发者在function、enum、variable命名上也会附带个人风格。 Vue.js部分枚举定义, 三个大牛,三种不同的风格。 // Type结尾enum TargetType { INVALID = 0, COMMON = 1, COLLECTION = 2,}// Types结尾enum OptionTypes { PROPS = 'Props', DATA = 'Data', COMPUTED = 'Computed', METHODS = 'Methods', INJECT = 'Inject',} // 枚举项使用pascalCaseenum BooleanFlags { shouldCast, shouldCastTrue,}通过对Vue、Element-plus源码浅读分析,罗列出30个代码规范建议,前半部分和Vue相关,后半部分和Javascript相关。如果表述有误,欢迎指正。 需要说明的是:代码规范没有标准答案,只有适合、不适合。其目的是让项目代码保持风格统一, 提升易读性, 降低上手成本。 代码规范往期介绍: 浅读Vue3代码10万行,总结出30个代码规范200+收藏的Vue3规范,如何配置eslint、prettier、editorconfigVue3黑神话:悟空版 eslint: eslint-plugin-wukong这样的Git规范,Leader看了都说好!目录规范模块命名采用kebab-case命名方式,多个noun单词用短横线"-"分割,一般不超过2个单词,命名采用模块-子模块方式,这样module会按模块级别归类排序。 如vue的编译器compiler分core、dom、ssr、sfc模块,因此命名为compiler-core,将根模块放在第一个单词。 //BADcompilerCorecompilerDomcompilerSSR // GOODvuevue-compat compiler-corecompiler-domcompiler-ssrcompiler-sfc runtime-coreruntime-domruntime-test 文件夹命名文件夹命名需要特别注意,windows系统对大小写不敏感,如components和Components在windows系统下会认为是同一个,因此把名字由Components改为components,git是识别不到差异。由于文件夹大小写重命名导致在linux环境编译报错的问题屡见不鲜。 和module类似,采用kebab-case规则,以noun单词命名。如果为同类功能的文件夹,则一般以复数形式结尾,常用的有components、utils等等。 layoutscomponentsdirectiveshooksutilspatchesscriptstransformsservices组件文件夹命名一个复杂组件通常会拆分为多个文件夹,每个文件夹以kebab-case方式命名。 dropdowndropdown-itemdropdown-menu checkboxcheckbox-buttoncheckbox-groupassets目录assets存放静态资源,images, styles, icons、svgs等静态目录以复数形式结尾,静态资源文件以kebab-case形式命名。 - assets - images - icons - styles - svgs - ant-design-vue.svg组件规范组件结构化组件编写大部分事件聚焦逻辑,因此将script放在顶层,文件结构可按sript、template、style顺序布局。 script setup lang="ts".../scripttemplate.../templatestyle lang="less" scoped.../stylescript可以按props、emits、ref、 computed、watch、methods、events顺序排列代码。为你而是每个文件顺序统一,可以使用vscode定义代码片段,按以上顺序添加注释,开发组件直接在对应注释下添加对应代码。 script lang="ts" setup/** imports */import { computed, ref, useSlots } from 'vue'import { ElIcon } from '@element-plus/components/icon'import { TypeComponents, TypeComponentsMap } from '@element-plus/utils'import { useNamespace } from '@element-plus/hooks'import { alertEmits, alertProps } from './alert' /** props */const props = defineProps(alertProps) /** emits */const emit = defineEmits(alertEmits) /** refs */const visible = ref(true) /** computed */const iconComponent = computed(() = TypeComponentsMap[props.type])const iconClass = computed(() = [ ns.e('icon'), { [ns.is('big')]: !!props.description || !!slots.default },])const withDescription = computed(() = { return { 'with-description': props.description || slots.default }}) /** methods */const close = (evt: MouseEvent) = { visible.value = false emit('close', evt)}/script 组件中单引号、双引号html中、vue的template中标签属性使用双引号 component :is="tag" ref="_ref" v-bind="_props" :class="buttonKls" :style="buttonStyle" @click="handleClick" 所有js中的字符串使用单引号 ns.is('disabled', _disabled.value), ns.is('loading', props.loading), ns.is('plain', props.plain), ns.is('round', props.round),所有js中的代码行换行,要么统一使用分号";",要么统一不使用分号,不能混着用。 // BADimport { buttonProps } from './button';import type { ExtractPropTypes } from 'vue' // GOODimport { buttonProps } from './button'import type { ExtractPropTypes } from 'vue'组件名命名规范采用kebab-case,一个项目必须保持统一,组件名一般不超过2 到 3 个单词。 checkbox-button.vuecheckbox-group.vue 组件以高优单词开头组件命名以高优先单词开头,以描述性单词结尾,重要单词放前面可实现有序排列。例如查询组件,使用Search前缀,输入组件命名为SearchInputXXX,而按钮组件使用SearchButtonXXX。 components/|- search-button.vue|- search-button-clear.vue|- search-input.vue|- search-input-query.vue|- settings-checkbox.vue|- settings-checkbox-terms.vue父、子组件命名和父组件紧密相关的子组件应该以父组件名作为前缀命名,例如: components|- todo-list.vue|- todo-list-item.vue|- todo-list-item-button.vue组件名应使用完整单词组件命名不能使用缩写,而应该使用完整单词组成的名称。因为时间一长,或者换个人,可能完全看不出SdSettings组件为何意。由于文件引入一般都有智能提示,因此也不容易出现拼写类错误。 BADcomponents/|- SdSettings.vue|- UProfOpts.vue GOODcomponents/|- student-dashboard-settings.vue|- user-profile-options.vue组件Props命名组件props定义使用lowerCamelCase命名,在template中使用组件时,使用kebab-case规则。例如定义greetingText属性,模板使用方式为greeting-text=""。 // BAD// componentconst props = defineProps({ 'greeting-text': String})// for in-DOM templateswelcome-message greetingText="hi"/welcome-message // GOODcomponentconst props = defineProps({ greetingText: String})WelcomeMessage greeting-text="hi"/组件事件命名规则事件命名需要注意定义和template使用。 事件定义:通常使用一个verb定义的事件名居多,例如open、close、click、change、focus、blur、select。对于状态类事件定义一般采用noun + "-" + verb形式,例如state-change、active-change。生命周期类通常采用prep + verb形式表示事件,例如before-enter、after-enter。或者是标识动作的事件,采用verb + "-" + noun。 // verbdefineEmits(['open'])defineEmits(['close'])defineEmits(['focus']) // noun + "-" + verbdefineEmits(['state-change']) // verb + "-" + noundefineEmits(['open-menu']) // 生命周期'before-enter''before-leave''after-enter''after-leave'事件定义推荐使用对象的形式,而不是简写。对象形式能够直观看到每一个事件入参和返回值类型。 export const cascaderEmits = { focus: (evt: FocusEvent) = evt instanceof FocusEvent, blur: (evt: FocusEvent) = evt instanceof FocusEvent, clear: () = true, visibleChange: (val: boolean) = isBoolean(val), expandChange: (val: CascaderValue) = !!val, removeTag: (val: CascaderNode['valueByOption']) = !!val,} const emit = defineEmits(cascaderEmits)// template中使用组件时,注册事件使用kebab-case形式。 el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"/template中组件属性设置单独占用一行在使用属性时,每个属性独占一行可提升代码的可读性。 // BADmy-component foo="a" bar="b" baz="c"/ // GOODmy-component foo="a" bar="b" baz="c"/组件模板应仅包含简单表达式,复杂的应提取到computed中在template中避免使用复杂的表达式,包含计算逻辑的值应提取到computed。 // BAD div :style="{ height: '30rem', width: '100%', transition: '.3s ease-out all', transform: `rotateX(${parallax.roll}deg) rotateY(${parallax.tilt}deg)`, }" /div // GOODdiv :style="cardStyle" const cardStyle = computed(() = ({ height: '30rem', width: '100%', transition: '.3s ease-out all', transform: `rotateX(${parallax.roll}deg) rotateY(${parallax.tilt}deg)`,}))复杂的computed应提取为多个简单计算包含多个值计算的computed应当通过拆分,简化逻辑。 // BADconst price = computed(() = { const basePrice = manufactureCost.value / (1 - profitMargin.value) return basePrice - basePrice * (discountPercent.value || 0)}) // GOODconst basePrice = computed( () = manufactureCost.value / (1 - profitMargin.value)) const discount = computed( () = basePrice.value * (discountPercent.value || 0)) const finalPrice = computed(() = basePrice.value - discount.value)属性赋值规则template中属性值使用引号(equoted)包裹。如果值为匿名对象则需要增加空格,保证值可读性。 // BADinput type=textAppSidebar :style={width:sidebarWidth+'px'} // GOODinput type="text"AppSidebar :style="{ width: sidebarWidth + 'px' }"directive指令统一使用简写,不能简写、全称混用。directive统一使用简写::forv-bind:,@forv-on:and#forv-slot. // BAD// v-bind全称和":"混用input v-bind:value="newTodoText" :placeholder="newTodoInstructions"// v-on和@混用input v-on:input="onInput" @focus="onFocus" // v-slot和#混用template v-slot:headerh1Here might be a page title/h1/templatetemplate #footerpHere's some contact info /template // GOODinput :value="newTodoText" :placeholder="newTodoInstructions" input @input="onInput" @focus="onFocus" template #header Here might be a page title/h1/templatetemplate #footer Here's some contact info/template组件属性定义添加校验在定义组件属性时,如果属性值是必填、类型明确,则可添加type、required、validator限定值的有效性。 // BADconst props = defineProps({ status: String }) // GOODconst props = defineProps({ status: { type: String, required: true, validator: (value) = { return ['created','loading', 'loaded'].includes( value ) } }})Javascript 规范boolean类型变量命名boolean变量、属性命名可添加is、has前缀,可以使用 is + Noun、is + Adjectiveis + Adjective + Nounis + Noun + Adjectivehas + X按统一前缀规范,代码中只要看到is、has前缀的,则可初步判断为boolean类型。 isString // is + NounisStatic // is + NounisSlot // is + NounisMemberExpressionBrowser // is + Noun, 为表达清楚变量意义,可多个名词链接isSimpleIdentifier // is + Adjective + NounisCompatEnabled // is + AdjectiveisVBindisVOnisFromSetupisUsedInTemplate hasFallback // has + NounhasVnodeHook // has + NounhasStyleBinding // has + NounhasDynamicKeys // has + NounhasText // has + NounhashPrefix // has + NounhasCommas // has + NounhasName // has + NounhasAttrsChanged // has + Noun + AdjectivehasCloned // has + Adjectiveboolean类型参数命名函数、方法、构造函数的参数,如果为boolean类型,命名: is + Nounis + Adjectiveadjectiveadvverb + Nounallow + Verb不管使用那种方式,前提是直观上能推断是boolean类型。 isLocal // is + NounisReference is + Nouninline // advinheritAttrs // verb + Noun.optimized // adjectivechecked // adjectiveallowRecurse // allow + Verbforce?: boolean // verbsync?: boolean // noun function genModulePreamble( genScopeId: boolean, inline?: boolean,) {} export function findProp( dynamicOnly: boolean = false, allowEmpty: boolean = false,) {} export interface WatchOptionsImmediate = boolean extends WatchOptionsBase { immediate?: Immediate // adjective deep?: boolean // adjective once?: boolean // adv} boolean类型方法命名boolean类型方法命名,一般以is、has、should、include、exclude、check等为前缀, 以Exists、Enabled、Equal等为后缀。 // is前缀isInDestructureAssignment(...)isInNewExpression(...)isFragmentTemplate(...)isTagStartChar(...)isSameKey(...)// has前缀hasScopeRef(...)hasForwardedSlots(...)hasPropsChanged(...) // 表示状态变化的hasMultipleChildren(...)hasExplicitCallback(...)hasCSSTransform(...)shouldSkipAttr(...)shouldReloadHmr(...)includeBooleanAttr(...)checkCompatEnabled(...)// Exists、Equal后缀fileExists(...)looseEqual(...)Function:业务方法命名使用lowerCamelCase小驼峰,以verb作为前缀,少数情况以描述性noun作为前缀。常用动词前缀: create、init、gen、walk、to、process、extract、unwrap、patch。createObjectMatcher(obj: Recordstring, any) {}createElementWithCodegen(...)genFlagText(...)parseWithForTransform(...)createConditionalExpression(...)convertToBlock(...)walkFunctionParams(...) // work + NounwalkBlockDeclarations(...) // 遍历Block定义extractIdentifiers(...)unwrapTSNode(...)patchEvent(...)patchDOMProp(...)patchStyle(...)// 描述性noun作为前缀ssrRender(_ctx, _push, _parent, _attrs)Function:事件方法命名注册事件的回调方法,经常纠结前缀要不要加"on",后缀要不要加"ed",错误示范:onChanged。 如果是交互类事件的函数定义,通常采用on + Verb + Noun?形式,如onClick、onConfirm、onClose、onChange等等。 el-button type="primary" size="small" @click="onConfirm"el-button @click="onDelete"Delete Item/el-button// on + Verb + Nounel-button v-if="!isAdding" @click="onAddOption"如果是逻辑处理类事件,通常采用handle+Verb,handle + Noun + Verb,例如: el-icon class="el-input__icon" @click="handleIconClick" edit //el-icon el-cascader v-model="value" :options="options" :props="props" @change="handleChange"/ el-autocomplete @select="handleSelect" 如果是处理单一业务逻辑,则可直接调用function,不需要专门定义事件方法,例如: Transition name="el-fade-in" @enter="lock" @after-leave="cleanup" el-button type="primary" plain @click="updateServiceWorker()"如果是更改单一状态,则可直接在template赋值,例如: el-button plain @click="alwaysRefresh = true" el-button plain @click="needRefresh = false" el-button @click="centerDialogVisible = false"Cancel/el-buttonel-button type="primary" @click="centerDialogVisible = false"Funtion: 数据请求、处理类方法数据查询通常采用get或fetch作为前缀,数据提交通常采用post、send、upload前缀。命名规则采用前缀 + Noun + Noun?.例如: getBookData(id)fetchBookData(id) postBookData(data)uploadBookFile(file)Class命名Class采用PascalCase方式命名,命名可使用 多个NounAdjective + NounAdjective + Adjective + NounNoun + Verb + Noun。// Noun + Nounexport class TypeScope { } // Adjective + Adjective + Nounexport class BaseReactiveHandler {}// Adjective + Adjective + Noun export class ReadonlyReactiveHandler {} // Adjective + Noun + Nounexport class ComputedRefImpl {} // Noun + Verb + Nounexport class ScriptCompileContext {}Class私有方法、私有属性命名TS官方是极力反对Class私有方法或属性使用下划线"_"前缀,由于有private标识私有,所以私有成员使用lowerCamelCase命名即可。 Vue.js源码在早年前的Class偏向于使用_lowerCamleCase命名,现在也逐渐使用loweCamelCase。 // BADexport class VueElement extends BaseClass { _instance: ComponentInternalInstance | null = null private _connected = false private _resolved = false private _numberProps: Recordstring, true | null = null private _styles?: HTMLStyleElement[] private _ob?: MutationObserver | null = null } // GOODexport default class Tokenizer { /** The current state the tokenizer is in. */ private state = State.Text /** The read buffer. */ private buffer = '' private stateInterpolation(c: number): void { ... } private stateInterpolationClose(c: number) { ... }}常量命名常量统一放到一个文件定义,例如定义constants.ts文件,每个常量命名使用大写字母加下划线。组成可使用: 多个NOUNADJECTIVE_NOUNVERB_NOUNconst PURE_ANNOTATION = `/*#__PURE__*/`export const KEEP_ALIVE = Symbol(__DEV__ ? `KeepAlive` : ``)export const BASE_TRANSITION = Symbol(__DEV__ ? `BaseTransition` : ``)export const OPEN_BLOCK = Symbol(__DEV__ ? `openBlock` : ``) const DAYS_IN_WEEK = 7; const MONTHS_IN_YEAR = 12; const MAX_DOG_WEIGHT = 150;枚举命名枚举名称使用PascalCase命名规则,如果是集合类的枚举,通常以复数s形式结尾,表示复数类的后缀常有Types、Levels、Tags、Codes、Hooks等。如果是表示动作类状态枚举,则使用Verb + Noun。 枚举值和常量定义规则一致,采用全大写模式, 多个单词用下划线"_"分割。 // Typesexport enum Namespaces { HTML, SVG, MATH_ML,}// Codesexport enum ErrorCodes { ABRUPT_CLOSING_OF_EMPTY_COMMENT, CDATA_IN_HTML_CONTENT, DUPLICATE_ATTRIBUTE, END_TAG_WITH_ATTRIBUTES, END_TAG_WITH_TRAILING_SOLIDUS,}// Flagsexport enum ReactiveFlags { SKIP = '__v_skip', IS_REACTIVE = '__v_isReactive', IS_READONLY = '__v_isReadonly', IS_SHALLOW = '__v_isShallow', RAW = '__v_raw',}// Hooksexport enum LifecycleHooks { BEFORE_CREATE = 'bc', CREATED = 'c', BEFORE_MOUNT = 'bm',} // Verb + Nounexport enum MoveType { ENTER, LEAVE, REORDER,}总结代码规范重要吗? 重要!优秀的代码规范,能够让别人在短时间能对你有正向的评价。特别在笔试时,优雅的代码也能让面试官眼前一亮,为后续流程降低门槛。 如何保持代码规范? 客观上:可以借助代码检查eslint、prettier、StyleLint、HTMLLint、SonarQube等工具辅助检查。主观上:要有工匠精神,个人认为正确的代码规范就得持续保持,"自成一派",不能潜移默化地受其他风格影响。我是前端下饭菜,原创不易,各位看官动动手,帮忙关注、点赞、收藏、评轮! 点击关注公众号,“技术干货”及时达! 阅读原文

上一篇:2022-11-20_一周资讯|电通组建全新领导架构;麦肯任命全球首席传播官 下一篇:2022-07-22_直播预告丨江亿:我国能源系统的低碳未来和转型路径

TAG标签:

17
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价