前端编码规范之Vue规范

  • Post author:
  • Post category:vue


vue大部分规范需参照HTML、CSS、JavaScript规范,在此不再强调。


  • 组件名应该始终是多个单词的

    根组件

    App

    以及

    <transition>



    <component>

    之类的 Vue 内置组件除外。

    这样做可以避免跟现有的以及未来的 HTML 元素相冲突,因为所有的 HTML 元素名称都是单个单词的。



    – Example –

    -js
    // 不推荐
    Vue.component('todo', {
      // ...
    })
    export default {
      name: 'Todo',
      // ...
    }
    // 推荐
    Vue.component('todo-item', {
      // ...
    })
    
    export default {
      name: 'TodoItem',
      // ...
    }
    

  • 组件的

    data

    必须是一个函数

    当在组件中使用 data property 的时候 (除了 new Vue 外的任何地方),它的值必须是返回一个对象的函数。



    – Example –

    -js
    // 不推荐
    Vue.component('some-comp', {
      data: {
        foo: 'bar'
      }
    })
    export default {
      data: {
        foo: 'bar'
      }
    }
    
    // 推荐
    Vue.component('some-comp', {
      data: function () {
        return {
          foo: 'bar'
        }
      }
    })
    
    // In a .vue file
    export default {
      data () {
        return {
          foo: 'bar'
        }
      }
    }
    
    // 在一个 Vue 的根实例上直接使用对象是可以的,
    // 因为只存在一个这样的实例。
    new Vue({
      data: {
        foo: 'bar'
      }
    })
    


  • Prop

    定义应该尽量详细

    至少需要指定其类型(type)。



    – Example –

    -js
    // 不推荐
    // 这样做只有开发原型系统时可以接受
    props: ['status']
    
    // 推荐
    props: {
      status: String
    }
    
    // 更好的做法!
    props: {
      status: {
        type: String,
        required: true,
        validator: function (value) {
          return [
            'syncing',
            'synced',
            'version-conflict',
            'error'
          ].indexOf(value) !== -1
        }
      }
    }
    



  • v-for

    设置键值

    总是用

    key

    配合

    v-for



    – Example –

    -html
    <!-- 不推荐 -->
    <ul>
      <li v-for="todo in todos">
        {{ todo.text }}
      </li>
    </ul>
    
    <!-- 推荐 -->
    <ul>
      <li v-for="todo in todos" :key="todo.id">
        {{ todo.text }}
      </li>
    </ul>
    

  • 避免

    v-if



    v-for

    用在一起

    一般我们在两种常见的情况下会倾向于这样做:

    1. 为了过滤一个列表中的项目 (比如 v-for=“user in users” v-if=“user.isActive”)。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),让其返回过滤后的列表。
    2. 为了避免渲染本应该被隐藏的列表 (比如 v-for=“user in users” v-if=“shouldShowUsers”)。这种情形下,请将 v-if 移动至容器元素上 (比如 ul、ol)。



    – Example –

    -html
    <!-- 不推荐 -->
    <ul>
      <li v-for="user in users" v-if="user.isActive" :key="user.id">
        {{ user.name }}
      </li>
    </ul>
    <ul>
      <li v-for="user in users" v-if="shouldShowUsers" :key="user.id">
        {{ user.name }}
      </li>
    </ul>
    
    <!-- 推荐 -->
    <ul>
      <li v-for="user in activeUsers" :key="user.id">
        {{ user.name }}
      </li>
    </ul>
    <ul v-if="shouldShowUsers">
      <li v-for="user in users" :key="user.id">
        {{ user.name }}
      </li>
    </ul>
    

  • 为组件样式设置作用域

    对于应用来说,顶级

    App

    组件和布局组件中的样式可以是全局的,但是其它所有组件都应该是有作用域的。

    设置样式作用域方法:

    1. scoped attribute;

    2. CSS Modules

      (基于 class 的类似 BEM 的策略)



    – Example –

    -html
    <!-- 不推荐 -->
    <template>
      <button class="btn btn-close">X</button>
    </template>
    
    <style>
    .btn-close {
      background-color: red;
    }
    </style>
    
    <!-- 推荐 -->
    
    <!-- 使用 `scoped` attribute -->
    <template>
      <button class="button button-close">X</button>
    </template>
    
    <style scoped>
    .button {
      border: none;
      border-radius: 2px;
    }
    
    .button-close {
      background-color: red;
    }
    </style>
    
    <!-- 使用 CSS Modules -->
    <template>
      <button :class="[$style.button, $style.buttonClose]">X</button>
    </template>
    
    <style module>
    .button {
      border: none;
      border-radius: 2px;
    }
    
    .buttonClose {
      background-color: red;
    }
    </style>
    
    <!-- 使用 BEM 约定 -->
    <template>
      <button class="c-Button c-Button--close">X</button>
    </template>
    
    <style>
    .c-Button {
      border: none;
      border-radius: 2px;
    }
    
    .c-Button--close {
      background-color: red;
    }
    </style>
    

  • 私有

    property


    使用模块作用域保持不允许外部访问的函数的私有性。如果无法做到这一点,就始终为插件、混入等不考虑作为对外公共 API 的自定义私有

    property

    使用

    $_

    前缀。并附带一个命名空间以回避和其它作者的冲突 (比如

    $_yourPluginName_

    )。



    – Example –

    -js
    // 不推荐
    var myGreatMixin = {
      // ...
      methods: {
        update: function () {
          // ...
        }
      }
    }
    
    var myGreatMixin = {
      // ...
      methods: {
        _update: function () {
          // ...
        }
      }
    }
    
    var myGreatMixin = {
      // ...
      methods: {
        $update: function () {
          // ...
        }
      }
    }
    
    var myGreatMixin = {
      // ...
      methods: {
        $_update: function () {
          // ...
        }
      }
    }
    
    // 推荐
    var myGreatMixin = {
      // ...
      methods: {
        $_myGreatMixin_update: function () {
          // ...
        }
      }
    }
    
    // 甚至更好!
    var myGreatMixin = {
      // ...
      methods: {
        publicMethod() {
          // ...
          myPrivateFunction()
        }
      }
    }
    
    function myPrivateFunction() {
      // ...
    }
    
    export default myGreatMixin
    

  • 组件文件

    只要有能够拼接文件的构建系统,就把每个组件单独分成文件。



    – Example –

    -js
    // 不推荐
    Vue.component('TodoList', {
      // ...
    })
    
    Vue.component('TodoItem', {
      // ...
    })
    
    -
    // 推荐
    components/
    |- TodoList.js
    |- TodoItem.js
    
    components/
    |- TodoList.vue
    |- TodoItem.vue
    

  • 单文件组件文件的大小写

    单文件组件的文件名应该:

    1. 始终是单词大写开头 (PascalCase);

    2. 始终是横线连接 (kebab-case)



    – Example –

    -
    // 不推荐
    components/
    |- mycomponent.vue
    
    components/
    |- myComponent.vue
    
    // 推荐
    components/
    |- MyComponent.vue
    
    components/
    |- my-component.vue
    

  • 基础组件名

    应用特定样式和约定的基础组件 (也就是展示类的、无逻辑的或无状态的组件) 应该全部以一个特定的前缀开头,比如

    Base



    App



    V



    这些组件为你的应用奠定了一致的基础样式和行为。它们可能只包括:

    1. HTML 元素;
    2. 其它基础组件;
    3. 第三方 UI 组件库.

    好处:

    1. 基础组件会全部列在一起,这样更容易识别;
    2. 可以避免你在包裹简单组件时随意选择前缀 (比如 MyButton、VueButton);
    3. 因为这些组件会被频繁使用,所以你可能想把它们放到全局而不是在各处分别导入它们。



    – Example –

    -
    // 不推荐
    components/
    |- MyButton.vue
    |- VueTable.vue
    |- Icon.vue
    
    // 推荐
    components/
    |- BaseButton.vue
    |- BaseTable.vue
    |- BaseIcon.vue
    
    components/
    |- AppButton.vue
    |- AppTable.vue
    |- AppIcon.vue
    
    components/
    |- VButton.vue
    |- VTable.vue
    |- VIcon.vue
    

  • 单例组件名

    只应该拥有单个活跃实例的组件应该以 The 前缀命名,以示其唯一性。

    这不意味着组件只可用于一个单页面,而是每个页面只使用一次。



    – Example –

    -
    // 不推荐
    components/
    |- Heading.vue
    |- MySidebar.vue
    
    // 推荐
    components/
    |- TheHeading.vue
    |- TheSidebar.vue
    

  • 紧密耦合的组件名

    和父组件紧密耦合的子组件应该以父组件名作为前缀命名。

    不推荐 在其父组件命名的目录中嵌套子组件。



    – Example –

    -
    // 不推荐
    components/
    |- TodoList.vue
    |- TodoItem.vue
    |- TodoButton.vue
    
    components/
    |- SearchSidebar.vue
    |- NavigationForSearchSidebar.vue
    
    // 推荐
    components/
    |- TodoList.vue
    |- TodoListItem.vue
    |- TodoListItemButton.vue
    
    components/
    |- SearchSidebar.vue
    |- SearchSidebarNavigation.vue
    

  • 组件名中的单词顺序

    组件名应该以高级别的 (通常是一般化描述的) 单词开头,以描述性的修饰词结尾。

    如果是一组组件,建议以 高级别的 (通常是一般化描述的) 单词 为名创建一个文件夹,统一放置相关组件文件。



    – Example –

    -
    // 不推荐
    components/
    |- ClearSearchButton.vue
    |- ExcludeFromSearchInput.vue
    |- LaunchOnStartupCheckbox.vue
    |- RunSearchButton.vue
    |- SearchInput.vue
    |- TermsCheckbox.vue
    
    // 推荐
    components/
    |- SearchButtonClear.vue
    |- SearchButtonRun.vue
    |- SearchInputQuery.vue
    |- SearchInputExcludeGlob.vue
    |- SettingsCheckboxTerms.vue
    |- SettingsCheckboxLaunchOnStartup.vue
    
    components/
    |- search/
      |- ButtonClear.vue
      |- ButtonRun.vue
      |- InputQuery.vue
      |- InputExcludeGlob.vue
    |-setting/
      |- CheckboxTerms.vue
      |- CheckboxLaunchOnStartup.vue
    

  • 自闭合组件

    在单文件组件、字符串模板和 JSX 中没有内容的组件应该是自闭合的——但在 DOM 模板里永远不要这样做。



    – Example –

    -html
    <!-- 不推荐 -->
    
    <!-- 在单文件组件、字符串模板和 JSX 中 -->
    <MyComponent></MyComponent>
    
    <!-- 在 DOM 模板中 -->
    <my-component/>
    
    <!-- 推荐 -->
    
    <!-- 在单文件组件、字符串模板和 JSX 中 -->
    <MyComponent/>
    
    <!-- 在 DOM 模板中 -->
    <my-component></my-component>
    

  • 自模板中的组件名大小写

    对于绝大多数项目来说,在单文件组件和字符串模板中组件名应该总是 PascalCase 的——但是在 DOM 模板中总是 kebab-case 的。


    在所有地方使用 kebab-case



    – Example –

    -html
    <!-- 不推荐 -->
    
    <!-- 在单文件组件和字符串模板中 -->
    <mycomponent/>
    
    <!-- 在单文件组件和字符串模板中 -->
    <myComponent/>
    
    <!-- 在 DOM 模板中 -->
    <MyComponent></MyComponent>
    
    <!-- 推荐 -->
    
    <!-- 在单文件组件和字符串模板中 -->
    <MyComponent/>
    
    <!-- 在 DOM 模板中 -->
    <my-component></my-component>
    
    <!-- 在所有地方 -->
    <my-component/>
    

  • JS/JSX 中的组件名大小写


    JS/JSX

    中的组件名应该始终是 PascalCase 的,尽管在较为简单的应用中只使用

    Vue.component

    进行全局组件注册时,可以使用 kebab-case 字符串。



    – Example –

    -js
    // 不推荐
    Vue.component('myComponent', {
      // ...
    })
    
    import myComponent from './MyComponent.vue'
    export default {
      name: 'myComponent',
      // ...
    }
    export default {
      name: 'my-component',
      // ...
    }
    
    // 推荐
    Vue.component('MyComponent', {
      // ...
    })
    
    Vue.component('my-component', {
      // ...
    })
    
    import MyComponent from './MyComponent.vue'
    
    export default {
      name: 'MyComponent',
      // ...
    }
    

  • 完整单词的组件名

    组件名应该倾向于完整单词而不是缩写。



    – Example –

    -
    // 不推荐
    components/
    |- SdSettings.vue
    |- UProfOpts.vue
    
    // 推荐
    components/
    |- StudentDashboardSettings.vue
    |- UserProfileOptions.vue
    


  • Prop

    名大小写

    在声明 prop 的时候,其命名应该始终使用 camelCase,而在模板和 JSX 中应该始终使用 kebab-case。



    – Example –



    不推荐:

    -js
    props: {
      'greeting-text': String
    }
    
    -html
    <welcome-message greetingText="hi"/>
    

    推荐:

    -js
    props: {
      greetingText: String
    }
    
    -html
    <welcome-message greeting-text="hi"/>
    

  • 多个

    attribute

    的元素

    多个

    attribute

    的元素应该一行撰写,超出 80 字符按照

    attribute

    元素进行换行。



    – Example –

    -html
    <!-- 不推荐 -->
    <img
      src="https://vuejs.org/images/logo.png"
      alt="Vue Logo"
    >
    
    <my-component
      foo="a"
      bar="b"
      baz="c"
    />
    
    <!-- 推荐 -->
    <img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
    
    <my-component foo="a" bar="b" baz="c"/>
    

  • 模板中简单的表达式

    组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。


    {

    {


    后要空一格,

    }}

    前要空一格



    – Example –

    -html
    <!-- 不推荐 -->
    <div>
      {{
        fullName.split(' ').map(function (word) {
          return word[0].toUpperCase() + word.slice(1)
        }).join(' ')
      }}
    </div>
    
    <!-- 推荐 -->
    <div>{{ normalizedFullName }}</div>
    
    -js
    // 复杂表达式已经移入一个计算属性
    computed: {
      normalizedFullName: function () {
        return this.fullName.split(' ').map(function (word) {
          return word[0].toUpperCase() + word.slice(1)
        }).join(' ')
      }
    }
    

  • 简单的计算属性

    应该把复杂计算属性分割为尽可能多的更简单的 property。



    – Example –

    -js
    // 不推荐
    computed: {
      price: function () {
        var basePrice = this.manufactureCost / (1 - this.profitMargin)
        return (
          basePrice - basePrice * (this.discountPercent || 0)
        )
      }
    }
    
    // 推荐
    computed: {
      basePrice: function () {
        return this.manufactureCost / (1 - this.profitMargin)
      },
      discount: function () {
        return this.basePrice * (this.discountPercent || 0)
      },
      finalPrice: function () {
        return this.basePrice - this.discount
      }
    }
    

  • 带引号的

    attribute


    非空 HTML attribute 值应该始终带双引号 。



    – Example –

    -html
    <!-- 不推荐 -->
    <input type=text>
    <app-sidebar :style={width:sidebarWidth+'px'}>
    
    <!-- 推荐 -->
    <input type="text">
    <app-sidebar :style="{ width: sidebarWidth + 'px'}">
    

  • 指令缩写

    指令缩写 (用 : 表示 v-bind:、用 @ 表示 v-on: 和用 # 表示 v-slot:)



    – Example –

    -html
    <!-- 不推荐 -->
    <input v-bind:value="newTodoText" :placeholder="newTodoInstructions">
    
    <input v-on:input="onInput" @focus="onFocus">
    
    <template v-slot:header>
      <h1>Here might be a page title</h1>
    </template>
    
    <!-- 推荐 -->
    <input :value="newTodoText" :placeholder="newTodoInstructions">
    
    <input @input="onInput" @focus="onFocus">
    
    <template #header>
      <h1>Here might be a page title</h1>
    </template>
    

  • 组件/实例的选项的统一顺序

    1. 副作用 (触发组件外的影响);(el)
    2. 全局感知 (要求组件以外的知识);(name/parent)
    3. 组件类型 (更改组件的类型);(functional)
    4. 模板修改器 (改变模板的编译方式);(delimiters/comments)
    5. 模板依赖 (模板内使用的资源);(components/directives/filters)
    6. 组合 (向选项里合并 property);(extends/mixins)
    7. 接口 (组件的接口);(inheritAttrs/model/props/propsData)
    8. 本地状态 (本地的响应式 property);(data/computed)
    9. 事件 (通过响应式事件触发的回调);(watch/生命周期钩子)

      • 生命周期钩子顺序:beforeCreate / created / beforeMount / mounted / beforeUpdate / updated / activate d /deactivated / beforeDestroy / destroyed
    10. 非响应式的 property (不依赖响应系统的实例 property);(methods)
    11. 渲染 (组件输出的声明式描述)。(template/render/renderError)
    • 最常用组件/实例顺序

      1. 全局感知 (要求组件以外的知识);(name/parent)
      2. 模板修改器 (改变模板的编译方式);(delimiters/comments)
      3. 模板依赖 (模板内使用的资源);(components/directives/filters)
      4. 接口 (组件的接口);(inheritAttrs/model/props/propsData)
      5. 本地状态 (本地的响应式 property);(data/computed)
      6. 事件 (通过响应式事件触发的回调);(watch/生命周期钩子)

        • 生命周期钩子顺序:beforeCreate / created / beforeMount / mounted / beforeUpdate / updated / activate d /deactivated / beforeDestroy / destroyed
      7. 非响应式的 property (不依赖响应系统的实例 property)。(methods)

  • 元素

    attribute

    的顺序

    1. 定义 (提供组件的选项);(is)
    2. 列表渲染 (创建多个变化的相同元素);(v-for)
    3. 条件渲染 (元素是否渲染/显示);(v-if/v-else-if/v-else/v-show/v-cloak)
    4. 渲染方式 (改变元素的渲染方式);(v-pre/v-once)
    5. 全局感知 (需要超越组件的知识);(id)
    6. 唯一的

      attribute

      (需要唯一值的

      attribute

      );(ref/key)
    7. 双向绑定 (把绑定和事件结合起来);(v-model)
    8. 事件 (组件事件监听器);(v-on)
    9. 内容 (覆写元素的内容);(v-html/v-text)
    10. 其它

      attribute

      (所有普通的绑定或未绑定的

      attribute

      )。

  • 组件/实例选项中的空行

    一组

    property

    之间,空行隔开



    – Example –

    -js
    // 推荐
    props: {
      value: {
        type: String,
        required: true
      },
    
      focused: {
        type: Boolean,
        default: false
      },
    
      label: String,
      icon: String
    },
    
    data() {
      return {
        advertInfo:[],
        advertConfig:{},
        
        other1:'',
        other2:'',
      }
    },
    
    computed: {
      formattedValue: function () {
        // ...
      },
    
      inputClasses: function () {
        // ...
      }
    }
    

  • 单文件组件的顶级元素的顺序



    – Example –

    -html
    <!-- 推荐 -->
    <template>...</template>
    
    <script>/* ... */</script>
    
    <style>/* ... */</style>
    

  • 避免在

    scoped

    中使用元素选择器



    – Example –

    -html
    <!-- 不推荐 -->
    <template>
      <button>X</button>
    </template>
    
    <style scoped>
    button {
      background-color: red;
    }
    </style>
    
    <!-- 推荐 -->
    <template>
      <button class="btn btn-close">X</button>
    </template>
    
    <style scoped>
    .btn-close {
      background-color: red;
    }
    </style>
    

  • 隐性的父子组件通信

    应该优先通过

    prop

    和事件进行父子组件之间的通信,而不是

    this.$parent

    或变更

    prop


  • 非 Flux 的全局状态管理

    应该优先通过 Vuex 管理全局状态,而不是通过 this.$root 或一个全局事件总线。


  • Vue Router 规范

    • 页面跳转数据传递使用路由参数。



      – Example –

      -js
      let id = '123';
      this.$router.push({ name: 'userCenter', query: { id: id } });
      
    • router 中的命名规范。

      • path、childrenPoints 命名规范采用kebab-case命名规范;
      • path必须以

        /

        开头,

        children

        里的path也要以

        /

        开头;
      • name 命名规范采用KebabCase命名规范且和component组件名保持一致。



版权声明:本文为qq_42988102原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。