IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    前端代码规范

    冷石发表于 2022-08-29 09:01:22
    love 0

    良好的代码规范有利于提高项目开发效率以及减少阅读代码的困难,本规范结合了(chaoxi)多个流行的代码规范以及前人的开发经验而成,参考规范进行开发提高代码质量

    前言

    良好的代码规范有利于提高项目开发效率以及减少阅读代码的困难,本规范结合了(chaoxi)多个流行的代码规范以及前人的开发经验而成,参考规范进行开发提高代码质量

    HTML 规范

    设置标准模式的文档类型

    1
    2
    3
    4
    <!DOCTYPE html>
    <html>
    <head> </head>
    </html>

    设置页面语言

    1
    <html lang="zh-CN"></html>

    设置页面字符编码

    1
    <meta charset="UTF-8" />

    使用语义化的标签准确地描述网页的内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <main>
    <article>
    <header>
    <h1>title</h1>
    <p>published: <time datetime="2015-02-21">21st Feb, 2015</time></p>
    </header>
    <p>content</p>
    </article>
    <aside>...</aside>
    <footer>...</footer>
    </main>

    确保页面可访问性

    • 给 img 标签添加正确的 alt 属性
    • 使用 label 标签对表单进行控制

    使用字符实体替代特殊字符

    1
    2
    3
    <span>&gt;</span>
    <span>&copy;</span>
    <span>&lt;</span>

    使用两个空格缩进

    1
    2
    3
    <section>
    <h1></h1>
    </section>

    按特定顺序书写标签属性

    1. class
    2. id, name
    3. data-*
    4. src, for, type, href, value
    5. title, alt
    6. role, aria-*
    1
    2
    3
    4
    5
    6
    <!-- 示例 -->
    <a class="link" id="link" data-toggle="modal" href="#">link</a>

    <input class="form-control" type="text" />

    <img src="example.jpg" alt="dog" />

    不对布尔属性赋值

    1
    2
    3
    4
    5
    6
    7
    8
    <!-- 示例 -->
    <input type="text" disabled />

    <input type="checkbox" value="1" checked />

    <select>
    <option value="1" selected>1</option>
    </select>

    CSS 规范

    全局设置盒子模型

    1
    2
    3
    * {
    box-sizing: border-box;
    }

    使用简洁的 class 命名

    class 的命名应该尽量简洁、明确,以字母开头命名,且全部字母为小写,单词之间使用连字符 “-” 连接。
    每个模块使用一个模块命名,子元素使用模块名 + 元素作用名名

    1
    2
    3
    4
    <!-- 示例 -->
    <header class="header">
    <h1 class="header-title"></h1>
    </header>

    使用常用的 class 命名

    包裹类: container, wrapper, outer, inner, box, header, footer, main, content, aside, page, section, block

    状态类: primary, secondary, success, danger, warning, info, error, link, light, dark, disabled, active, checked, loading

    尺寸类: large, middle, small, bigger, smaller

    组件类: card, list, picture, carousel, swiper, menu, navs, badge, hint, modal, dialog

    位置类: first, last, current, prev, next, forward, back

    文本类: title, desc, content, date, author, category,label,tag

    人物类: avatar, name, age, post, intro

    Class Name含义
    about关于
    account账户
    arrow箭头图标
    article文章
    aside边栏
    audio音频
    avatar头像
    bg,background背景
    bar栏(工具类)
    branding品牌化
    crumb,breadcrumbs面包屑
    btn,button按钮
    caption标题,说明
    category分类
    chart图表
    clearfix清除浮动
    close关闭
    col,column列
    comment评论
    community社区
    container容器
    content内容
    copyright版权
    current当前态,选中态
    default默认
    description描述
    details细节
    disabled不可用
    entry文章,博文
    error错误
    even偶数,常用于多行列表或表格中
    fail失败(提示)
    feature专题
    fewer收起
    field用于表单的输入区域
    figure图
    filter筛选
    first第一个,常用于列表中
    footer页脚
    forum论坛
    gallery画廊
    group模块,清除浮动
    header页头
    help帮助
    hide隐藏
    hightlight高亮
    home主页
    icon图标
    info,information信息
    last最后一个,常用于列表中
    links链接
    login登录
    logout退出
    logo标志
    main主体
    menu菜单
    meta作者、更新时间等信息栏,一般位于标题之下
    module模块
    more更多(展开)
    msg,message消息
    nav,navigation导航
    next下一页
    nub小块
    odd奇数,常用于多行列表或表格中
    off鼠标离开
    on鼠标移过
    output输出
    pagination分页
    pop,popup弹窗
    preview预览
    previous上一页
    primary主要
    progress进度条
    promotion促销
    rcommd,recommendations推荐
    reg,register注册
    save保存
    search搜索
    secondary次要
    section区块
    selected已选
    share分享
    show显示
    sidebar边栏,侧栏
    slide幻灯片,图片切换
    sort排序
    sub次级的,子级的
    submit提交
    subscribe订阅
    subtitle副标题
    success成功(提示)
    summary摘要
    tab标签页
    table表格
    txt,text文本
    thumbnail缩略图
    time时间
    tips提示
    title标题
    video视频
    wrap容器,包,一般用于最外层
    wrapper容器,包,一般用于最外层

    不使用 ID 选择器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    #title {
    color: blue;
    }

    // good
    .title {
    color: blue;
    }

    不要为 0 值设置单位

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    .content {
    margin-top: 0px;
    }

    // good
    .content {
    margin-top: 0;
    }

    遵循以下的样式书写顺序

    1. 布局定位属性:position / display / float / clear / visibility / overflow
    2. 盒属性:width / height / margin / padding / border / background
    3. 文本属性:color / font / text-decoration / text-align / vertical-align / white- space / break-word
    4. 视觉属性:background-color / border / border-radius / box-shadow
    5. 其他属性:content / cursor / text-shadow / background:linear-gradient
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    .box {
    /* Positioning */
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 100;

    /* Box-model */
    display: block;
    float: right;
    width: 100px;
    height: 100px;

    /* Typography */
    font: normal 13px 'Helvetica Neue', sans-serif;
    line-height: 1.5;
    color: #333;
    text-align: center;

    /* Visual */
    background-color: #f5f5f5;
    border: 1px solid #e5e5e5;
    border-radius: 3px;

    /* Misc */
    opacity: 1;
    }

    嵌套选择器的深度不要超过 3 层

    1
    2
    3
    4
    5
    6
    7
    .container {
    .content {
    .profile {
    /* STOP! */
    }
    }
    }

    不要使用 @import

    1
    2
    /* bad */ @import url("other.css"); /* good */
    <link rel="stylesheet" href="other.css" />

    将媒体查询语句放在靠近相关规则最近的位置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    .element {
    /* ... */
    }
    .element-avatar {
    /* ... */
    }
    .element-selected {
    /* ... */
    }

    @media (min-width: 480px) {
    .element {
    /* ... */
    }
    .element-avatar {
    /* ... */
    }
    .element-selected {
    /* ... */
    }
    }

    使用简短的申明语句

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // bad
    .content {
    padding-top: 10px;
    padding-bottom: 10px;
    padding-left: 20px;
    padding-right: 20px;
    }

    // good
    .content {
    padding: 10px 20px;
    }

    使用 stylelint 检查样式代码

    https://github.com/shinnn/vscode-stylelint

    JS 规范

    使用 const 优于 let,弃用 var

    1
    2
    3
    4
    5
    6
    7
    // bad
    var name = 'jack'

    let myVariable = 'myVariable'

    // good
    const FIRST_US_PRESIDENT = 'George Washington'

    使用字面量创建对象

    1
    2
    3
    4
    5
    // bad
    const a = new Object{}

    // good
    const obj = {}

    函数名或方法名使用动宾短语

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    function userInfo() {
    ...
    }

    // good
    function getUserInfo() {
    ...
    }

    命名所有的方法包含闭包和回调, 避免匿名方法

    命名方法和函数可以让你在代码运行出错时更快的找到错误

    布尔值变量使用 is 开头的词组

    1
    2
    3
    4
    5
    // bad
    const finished = false

    // good
    const isFinished = false

    避免重复的描述对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // bad
    const car = {
    carMake: 'Honda',
    carModel: 'Accord',
    carColor: 'Blue'
    }

    // good
    const Car = {
    make: 'Honda',
    model: 'Accord',
    color: 'Blue'
    }

    使用有意义可读性好的变量名

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // bad
    const yyyymmdstr = moment().format('YYYY/MM/DD')

    for (var i = 0; i < 525600; i++) {
    runCronJob()
    }

    // good
    const yearMonthDay = moment().format('YYYY/MM/DD')

    const MINUTES_IN_A_YEAR = 525600
    for (var i = 0; i < MINUTES_IN_A_YEAR; i++) {
    runCronJob()
    }

    将函数参数限制 2 个以内

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // bad
    function createMenu(title, body, buttonText, cancellable) {
    ...
    }

    // good
    const menuConfig = {
    title: 'Foo',
    body: 'Bar',
    buttonText: 'Baz',
    cancellable: true
    }

    function createMenu(menuConfig) {
    ...
    }

    封装判断条件

    将多个条件的判断结果赋值给一个变量或使用一个函数返回

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // bad
    if (fsm.state === 'fetching' && isEmpty(listNode)) {
    /// ...
    }

    // good
    function shouldShowSpinner(fsm, listNode) {
    return fsm.state === 'fetching' && isEmpty(listNode)
    }

    if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
    // ...
    }

    避免否定情况的判断

    尽可能只使用正向的判断

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    if(!isLoading) {
    ...
    }

    // good
    if(isLoading) {
    ...
    }

    删除无效的代码

    及时删除不再被调用的代码,避免给阅读代码带来困扰

    使用 try/catch 语句捕获可能出现的错误

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    try {
    functionThatMightThrow()
    } catch (error) {
    // 不单单只是使用 console
    console.error(error)
    // 更多的错误处理
    notifyUserOfError(error)
    // 报告错误
    reportErrorToService(error)
    }

    只注释业务逻辑复杂性的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function hashIt(data) {
    var hash = 0
    var length = data.length

    for (var i = 0; i < length; i++) {
    var char = data.charCodeAt(i)
    hash = (hash << 5) - hash + char

    // Convert to 32-bit integer
    hash = hash & hash
    }
    }

    Vue 规范

    将组件名命名为多个单词

    1
    2
    3
    4
    5
    // bad
    todo.vue

    // good
    todo-item.vue

    使用 PascalCase 或者 kebab-case 命名组件

    1
    2
    3
    4
    5
    <!-- bad  -->
    components/ |- mycomponent.vue components/ |- myComponent.vue

    <!-- good -->
    components/ |- MyComponent.vue components/ |- my-component.vue

    详细定义组件的 Prop

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // bad
    props: ['status']

    // good
    props: {
    status: {
    type: String,
    required: true
    }
    }

    在 v-for 循环上加 key

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!-- bad -->
    <li v-for="todo in todos">
    {{ todo.text }}
    </li>

    <!-- good -->
    <li v-for="todo in todos" :key="todo.id">
    {{ todo.text }}
    </li>

    不要把 v-if 和 v-for 同时用在同一个元素上

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!-- bad -->
    <ul>
    <li v-for="user in users" v-if="shouldShowUsers" :key="user.id">
    {{ user.name }}
    </li>
    </ul>

    <!-- good -->
    <ul v-if="shouldShowUsers">
    <li v-for="user in users" :key="user.id">
    {{ user.name }}
    </li>
    </ul>

    为组件样式设置作用域

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <!-- bad -->
    <template>
    <button class="btn btn-close">X</button>
    </template>

    <style>
    .btn-close {
    background-color: red;
    }
    </style>

    <!-- good -->
    <template>
    <button class="c-button c-button--close">X</button>
    </template>

    <!-- 使用 BEM 约定 -->
    <style>
    .c-button {
    border: none;
    border-radius: 2px;
    }

    .c-button--close {
    background-color: red;
    }
    </style>

    组件和实例的选项使用统一的顺序

    1. 副作用(触发组件外的影响)
    • el
    1. 全局感知(要求组件以外的知识)
    • name
    • parent
    1. 组件类型(更改组件的类型)
    • functional
    1. 模板修改器(改变模板的编译方式)
    • delimiters
    • comments
    1. 模板依赖(模板内使用的资源)
    • components
    • directives
    • filters
    1. 组合(向选项里合并属性)
    • extends
    • mixins
    1. 接口(组件的接口)
    • inheritAttrs
    • model
    • props/propsData
    1. 本地状态(本地的响应式属性)
    • data
    • computed
    1. 事件(通过响应式事件触发的回调)
    • watch
    • 生命周期钩子 (按照它们被调用的顺序)
      • beforeCreate
      • created
      • beforeMount
      • mounted
      • beforeUpdate
      • updated
      • activated
      • deactivated
      • beforeDestroy
      • destroyed
    1. 非响应式的属性(不依赖响应系统的实例属性)
    • methods
    1. 渲染(组件输出的声明式描述)
    • template/render
    • renderError

    元素和组件的特性使用统一的顺序

    1. 定义(提供组件的选项)
    • is
    1. 列表渲染(创建多个变化的相同元素)
    • v-for
    1. 条件渲染(元素是否渲染/显示)
    • v-if
    • v-else-if
    • v-else
    • v-show
    • v-cloak
    1. 渲染方式(改变元素的渲染方式)
    • v-pre
    • v-once
    1. 全局感知(需要超越组件的知识)
    • id
    1. 唯一的特性(需要唯一值的特性)
    • ref
    • key
    • slot
    1. 双向绑定(把绑定和事件结合起来)
    • v-model
    1. 其它特性 (所有普通的绑定或未绑定的特性)
    2. 事件(组件事件监听器)
    • v-on
    1. 内容(覆写元素的内容)
    • v-html
    • v-text

    保证一个组件专注于解决一个问题

    确保一个组件是独立的、可复用的、微小的和可测试的

    将行内表达式转为 computed

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // bad
    <template>
    <h1>
    {{ (new Date()).getUTCFullYear() }}
    </h1>
    </template>

    // good
    <template>
    <h1>
    {{ year }}
    </h1>
    </template>
    <script type="text/javascript">
    export default {
    computed: {
    year() {
    return new Date().getUTCFullYear()
    }
    }
    }
    </script>

    尽可能使用 mixins

    Mixins 封装可重用的代码,避免了重复。如果两个组件共享有相同的功能,则可以使用 mixin

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    const HelloMixin = {
    methods: {
    hello() {
    console.log('hello')
    }
    }
    }

    // CompA
    <script>
    import HelloMixin from './HelloMixin'

    export default {
    name: 'CompA',
    mixins: [HelloMixin]
    }
    </script>

    // CompB
    <script>
    import HelloMixin from './HelloMixin'

    export default {
    name: 'CompB',
    mixins: [HelloMixin]
    }
    </script>

    Node 规范

    require 文件夹,而不是文件

    在一个文件夹中开发库/模块,放置一个文件 index.js 暴露模块的内部

    使用 .npmrc 锁住依赖版本

    代码必须在所有的环境中是相同的,但是 Npm 默认情况下会拿包的最新版本,配置 .npmrc 锁定依赖版本

    1
    save-exact=true

    在 Node 外管理前端资源

    使用专门的中间件(nginx,S3,CDN)服务前端内容,这是因为在处理大量静态文件的时候,由于 node 的单线程模型,它的性能很受影响

    使用中间件限制并发请求

    DOS 攻击非常流行而且相对容易处理。使用外部服务,比如 cloud 负载均衡, cloud 防火墙, nginx, 或者(对于小的,不是那么重要的 app)一个速率限制中间件(比如express-rate-limit),来实现速率限制。否则应用程序可能受到攻击, 导致拒绝服务, 在这种情况下, 真实用户会遭受服务降级或不可用。

    验证传入的 JSON schemas

    验证传入请求的 body payload,并确保其符合预期要求, 如果没有, 则快速报错。为了避免每个路由中繁琐的验证编码, 您可以使用基于 JSON 的轻量级验证架构,比如 jsonschema or joi,否则您疏忽和宽松的方法大大增加了攻击面, 并鼓励攻击者尝试许多输入, 直到他们找到一些组合, 使应用程序崩溃。

    使用非 root 用户运行 Node.js

    Node.js 作为一个具有无限权限的 root 用户运行,这是一种普遍的情景。例如,在 Docker 容器中,这是默认行为。建议创建一个非 root 用户,并保存到 Docker 镜像中(下面给出了示例),或者通过调用带有”-u username” 的容器来代表此用户运行该进程,否则在服务器上运行脚本的攻击者在本地计算机上获得无限制的权利 (例如,改变 iptable,引流到他的服务器上)

    其它规范

    使用 jest 进行单元测试

    通常只要求对一些基础/底层的组件、函数进行测试, 视情况考虑是否要测试业务代码

    使用 Prettier 格式化代码


    所有代码格式相关的工作都可以交给 Prettier 来做

    合理的图片命名

    图片业务 + 图片功能类别 + 图片模块名称 + 图片精度

    参考资料

    • Code Guide
    • O2 前端规范文档
    • JavaScript 风格指南
    • Vue 风格指南
    • Vue.js 组件编码规范
    • Node.js 最佳实践


沪ICP备19023445号-2号
友情链接