如何建项目目录、写好JavaScript,请查收

写在开头

  • 本文主要整理一些平常开发使用频率较多的规范,例如:文件结构、命名。仅为做者我的观点。

文件结构

  • 在平常项目开发中,大部分的开发人员是不须要去关注整个项目的结构目录的,由于项目的负责人,或者其余的架构人员已经把整个项目文件结构弄好了。开发人员只须要在本身负责的模块目录去编写对应的模块便可。那若是须要你去搭建一个项目,你应该怎么去设计文件结构呢?

react项目为例

  • 项目名称为detanx-react
入口
  • detanx-react/index.js文件,项目的入口文件。
配置目录
  • detanx-react/config文件夹,用来存放项目的一些配置文件,例如咱们使用webpack的打包配置以及其余好比将react-routerreact-dom等经过gulp打包单独的包等配置。
打包分析目录
  • detanx-react/analyz文件夹,用来存放经过webpack打包后,分析打包过程的文件。
生产目录
  • detanx-react/build或者detanx-react/dist文件夹,用来存放项目开发完成后,须要部署上线的全部生产文件。
数据目录
  • detanx-react/mock文件夹,在项目开发时,后端开发的进度可能没有前端快,致使接口没法调试,因此咱们能够本身配置mock数据进行开发和调试。这个文件夹就用来存放全部的mock数据。
公共目录
  • detanx-react/public文件夹,打包会须要基本的html模版,内嵌的一些好比cssfavicon等咱们就能够放到这个文件夹下面。
引用目录
  • detanx-react/src文件夹,这个文件夹是咱们项目开发变化最多的,咱们写的大多数代码都在这个文件夹下面,针对不一样项目,它下面的结构也不同。
    1. detanx-react/src/components文件夹,存放项目开发的公共组件。
    2. detanx-react/src/hooks文件夹,存放开发过程当中封装的一些新的hook。
    3. detanx-react/src/request文件夹,存放项目请求的封装。
    4. detanx-react/src/router文件夹,存放项目的路由相关设置,配置也能够写到detanx-react/config文件夹下。
    5. detanx-react/src/view文件夹,存放项目开发的全部页面内容,根据每一个模块划分。
      • 每一个模块中又能够分为index文件(模块入口)、components文件夹(存放模块的抽离组件)等。
    6. detanx-react/src/common文件夹,存放项目的公共内容,例如:公共的方法(判断空、数据类型等)、常量(会多个(2个及以上)不一样模块用到的常量)文件等。
静态目录
  • detanx-react/static文件夹,这个文件夹也能够放在detanx-react/src下面,用于存放一些图片、字体、CSSLESSSASS)等文件。
其余
  • 根据本身的项目需求在任意目录下能够适当添加其余文件夹。

命名

  • 命名部分主要包括了函数命名、变量命名、模块命名、class命名(CSS命名)。开发项目时,尤为在多人合做的时候,若是没有一个好的命名规范而且也不喜欢写代码注释。当你离职或者其余缘由须要项目交接时,别人可能就是在遍看代码边问候你的家人了,可能还会打电话问你,浪费彼此不少时间。

函数命名

  • 命名方式能够用大驼峰、小驼峰。通常建议使用小驼峰。
  • 咱们在写一个函数的时候,咱们首先应该肯定该函数的一个功能,好比判断一个值的类型、经过请求获取一个某个表格的数据、显示或隐藏某个页面等。因此根据不一样的功能咱们能够用不一样的单词开始,例如:
    1. can 判断是否可执行某个动做(canGetUserName
    2. has 判断是否含有某个值(hasUserName
    3. is 判断是否为某个值(isEmpty
    4. get 获取某个值(getUserName
    5. set 设置某个值(setUserName
    6. load 加载某些数据(loadUserInfo
  • 这几个确定不可能完整的覆盖项目中全部的场景,当咱们函数命名不能直接看出在作什么的时候,咱们能够经过给函数添加注释来讲明函数的做用。
  • 每一个函数应该都是一个独立的功能,下降函数之间的耦合。
  • 每一个函数的大小不宜超过200行,超过应该尝试抽离部分逻辑为一个新的函数。实在没法抽离应该在复杂逻辑部分添加适当的注释。
  • 私有方法能够在项目开发的时候,开发人员之间作一个规范,例如使用_$开头的函数为私有。

变量命名

  • 在项目开发的时候,不少开发人员为了省事,就对一个变量随便取名。例如:可能只是用来接收一个函数的返回值,就取个a,反正下面只会用到一次;再者一个是某个计数变量就直接一个i或者其余字母。当你这个模块全身这种变量时,别人看你的代码就是一个灾难。
  • 普通变量咱们可使用var或者let去声明,在命名一个变量时,咱们应该经过变量的类型或者它的使用场景去命名,尽可能作到明确语义。例如:
    1. 布尔类型:isShowModal(是否显示弹窗)
    2. 数组类型:userLists(用户列表)
    3. 对象类型:userInfoObj(用户信息对象,虽然咱们通常知道userInfo就是一个对象,我这里只是表示一个若是看不出来的命名,咱们能够在后面加一个标识)
    4. Symbol类型:userNameSymSymbol类型的用户名)
    5. 等等
  • 变量命名必须以字母、下划线_或者$为开头,一般开发时约定_或者$开头的为私有变量。
  • 变量命名时应该尽可能保证命名中不出现数字,例如:list1list2等等这种变量名称。
  • 常量应该所有大些而且每一个单词如下划线链接(WARNING_DUATION_TIME:警告显示时间)。

模块命名

  • react模块为例,咱们每一个模块的命名应该使用大驼峰(UserConfig),而且模块名称应该语义化,表示模块的内容,例如(UserConfig)表示用户的配置模块。

class命名(CSS命名)

  • CSS命名咱们通常是使用 BEM(块(block)、元素(element)、修饰符(modifier)) 命名规范。
    .block {}
    .block__element {}
    .block--modifier {}
    复制代码
    • block 表明了更高级别的抽象或组件。
    • block__element 表明 .block 的后代,用于造成一个完整的 .block 的总体。
    • block--modifier 表明 .block 的不一样状态或不一样版本。
  • 使用两个连字符和下划线而不是一个,是为了让你本身的块能够用单个连字符来界定。
    .sub-block__element {}
    .sub-block--modifier {}
    复制代码
  • 优缺点:
    1. 优势:能够得到更多的描述和更加清晰的结构,从其名字能够知道某个标记的含义。
    2. 缺点:当嵌套太深,class名称会特别长,咱们能够经过其余约定解决,例如:缩减层级等。
  • 使用
    1. 时机:须要明确关联性的模块关系时。
    2. 处理:经过 LESS/SASS 等预处理器语言来编写 CSS

代码规范

  • ES6-编程风格
  • 在开发中,若是没有使用一些代码规范的插件,多个开发人员写得代码就几个风格,因此协同开发时,咱们须要对代码的规范进行约定,例如js经过eslint去约束,ts能够经过tslint约束,咱们只须要一我的去配置相应的约束的配置文件。
  • 但有些代码的编写这些约束也作不到,只是实现的好坏而已。下面举一些代码上的规范(不必定是约束没法作到的)。

ESLint 的使用示例

  • ESLint 是一个语法规则和代码风格的检查工具,能够用来保证写出语法正确、风格统一的代码。
  • 安装 ESLint
$ npm i -g eslint
复制代码
  • 安装 Airbnb 语法规则,以及 importa11yreact 插件。
$ npm i -g eslint-config-airbnb
$ npm i -g eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react
复制代码
  • 在项目的根目录下新建一个.eslintrc文件,配置 ESLint
{
  "extends": "eslint-config-airbnb"
}
复制代码
  • vscode中安装eslint插件便可边写边检查。

引号

  • 引号分为单引号(')和双引号(")以及ES6新增的模版字符串使用 `` 表示,可能在开发时大部分都是随便去使用这个引号,怎么输入着方便怎么来。通常状况下,.js.ts.jsx.tsx.vue等能够写js的文件中,通常是使用单引号('),在html中(标签的属性等)使用双引号(')。``能够代替之前的连续字符串链接操做。
export default class extends PureComponent {
    constructor() {
        this.state = {
          value: 'detanx',
          list: null
        }
      }
    render() {
        const { value, list } = this.state
        return (<>
            <div className="detanx">{value}</div>
        </>)
    }
}

// ``使用
let user = 'my name is'
const NAME = 'detanx'; // 简单示例一下

// es5 
user = user + ' ' + NAME;

// es6
let user = `${user} ${NAME}`
复制代码

变量

  • 变量命名规范在上面说过了,这里说一下变量的使用。咱们如今编写代码应该尽可能的使用letconst去声明变量,而不是用var
  • 全部变量都应该先声明再使用。
  • 超过2处使用的某个值,咱们应该使用const在当前使用模块的最顶层去声明,多个模块使用的应该在文件的公共模块的常量文件中声明。
  • 声明一个变量应该给它赋一个初值。
// bad
let i
for(i = 0; i < 10; i ++) { ... }

// good
let i = 0; 
for(; i < 10; i ++) { ... }

for (let i = 0; i < 10; i ++) { ... }
复制代码
  • 使用const连续声明多个值时,可使用数组解构赋值的方式。
// bad
var a = 1, b = 2, c = 3;

// good
const a = 1;
const b = 2;
const c = 3;

// best
const [a, b, c] = [1, 2, 3];
复制代码

解构赋值

  • 可使用解构赋值的地方优先使用解构赋值。例如上面的多个const声明。
    1. 数组成员对变量赋值时,优先使用解构赋值。
    const arr = [1, 2, 3, 4];
    
    // bad
    const first = arr[0];
    const second = arr[1];
    
    // good
    const [first, second] = arr;
    复制代码
    1. 函数的参数若是是对象的成员,优先使用解构赋值。
    // bad
    function getFullName(user) {
      const firstName = user.firstName;
      const lastName = user.lastName;
    }
    
    // good
    function getFullName(obj) {
      const { firstName, lastName } = obj;
    }
    
    // best
    function getFullName({ firstName, lastName }) {
    }
    复制代码
    1. 若是函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。这样便于之后添加返回值,以及更改返回值的顺序。
    // bad
    function processInput(input) {
      return [left, right, top, bottom];
    }
    
    // good
    function processInput(input) {
      return { left, right, top, bottom };
    }
    
    const { left, right } = processInput(input);
    复制代码

对象

  • 单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾。
// bad
const a = { k1: v1, k2: v2, };
const b = {
  k1: v1,
  k2: v2
};

// good
const a = { k1: v1, k2: v2 };
const b = {
  k1: v1,
  k2: v2,
};
复制代码
  • 对象尽可能静态化,一旦定义,就不得随意添加新的属性。若是添加属性不可避免,要使用Object.assign方法。
// bad
const a = {};
a.x = 3;

// if reshape unavoidable
const a = {};
Object.assign(a, { x: 3 });

// good
const a = { x: null };
a.x = 3;
复制代码
  • 若是对象的属性名是动态的,能够在创造对象的时候,使用属性表达式定义。
// bad
const obj = {
  id: 5,
  name: 'San Francisco',
};
obj[getKey('enabled')] = true;

// good
const obj = {
  id: 5,
  name: 'San Francisco',
  [getKey('enabled')]: true,
};
复制代码
  • 上面代码中,对象obj的最后一个属性名,须要计算获得。这时最好采用属性表达式,在新建obj的时候,将该属性与其余属性定义在一块儿。这样一来,全部属性就在一个地方定义了。css

  • 对象的属性和方法,尽可能采用简洁表达法,这样易于描述和书写。html

var ref = 'some value';
// bad
const atom = {
  ref: ref,
  value: 1,
  addValue: function (value) {
    return atom.value + value;
  },
};
// good
const atom = {
  ref,
  value: 1,
  addValue(value) {
    return atom.value + value;
  },
};
复制代码

数组

使用扩展运算符(...)拷贝数组。前端

// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i++) {
  itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
复制代码
  • 使用 Array.from 方法,将相似数组的对象转为数组。
const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
复制代码

函数

  • 匿名函数看成参数的场合,尽可能用箭头函数代替。
// 当即执行函数
(() => {
  console.log('Welcome to the Internet.');
})();
复制代码
  • 全部配置项都应该集中在一个对象,放在最后一个参数,布尔值不能够直接做为参数。
// bad
function divide(a, b, option = false ) { ... }
// good
function divide(a, b, { option = false } = {}) { ... }
复制代码
  • 不要在函数体内使用 arguments 变量,使用 rest 运算符(...)代替。由于 rest 运算符显式代表你想要获取参数,并且 arguments 是一个相似数组的对象,而 rest 运算符能够提供一个真正的数组。
// bad
function concatenateAll() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}
// good
function concatenateAll(...args) {
  return args.join('');
}
复制代码
  • 使用默认值语法设置函数参数的默认值。
// bad
function handleThings(opts) {
  opts = opts || {};
}
// good
function handleThings(opts = {}) { ... }
复制代码

Map 结构

  • 注意区分 ObjectMap,只有模拟现实世界的实体对象时,才使用 Object。若是只是须要key: value的数据结构,使用 Map 结构。由于 Map 有内建的遍历机制。
let map = new Map(arr);
for (let key of map.keys()) { console.log(key); }

for (let value of map.values()) { console.log(value); }

for (let item of map.entries()) { console.log(item[0], item[1]); }
复制代码

模块

  • 使用import取代require
// bad
const moduleA = require('moduleA');
const func1 = moduleA.func1;
const func2 = moduleA.func2;

// good
import { func1, func2 } from 'moduleA';
复制代码
  • 使用export取代module.exports
// commonJS的写法
const React = require('react');
const Breadcrumbs = React.createClass({
  render() {
    return <nav />;
  }
});
module.exports = Breadcrumbs;

// ES6的写法
import React from 'react';
class Breadcrumbs extends React.Component {
  render() {
    return <nav />;
  }
};
export default Breadcrumbs;
复制代码
  • 若是模块只有一个输出值,就使用export default,若是模块有多个输出值,就使用exportexport default与普通的export不要同时使用。vue

  • 不要在模块输入中使用通配符。由于这样能够确保你的模块之中,有一个默认输出(export default)。node

// bad
import * as myObject from './importModule';

// good
import myObject from './importModule';
复制代码
  • 模块默认输出一个函数,函数名的首字母应该小写。默认输出一个对象,对象名的首字母应该大写。
function makeStyleGuide() { ... }
export default makeStyleGuide;

const StyleGuide = {
  es6: {
  }
};
export default StyleGuide;
复制代码
相关文章
相关标签/搜索