0%

项目前期运维工具没有铺开的时候很多时候都是直接上服务器敲命令,这里记录一下source命令,也是某段时间很常用的一个命令。

我用source命令主要是为了刷新环境变量。刚好前段时间看到一篇文章

https://linuxhandbook.com/source-command/,对source命令有了更深入的了解,在这搬运一下。

Linux中的源命令是什么?

source命令的是从当前shell中的文件执行。它也可以用于刷新环境变量,通常source命令的主要用途是刷新环境变量。

1
source filename [options]

您也可以使用.(点)代替这样的源命令:

1
. filename [options]

源命令如何工作?

该命令的语法很简单,但是要理解它,需要更深入地了解一些Linux概念。

在继续之前,我先对变量给出一个简短的解释。

变量概述

您可以打开任何bash终端并创建新变量。变量可以被视为占位符,可用于将系统指向一条信息(字母,数字或符号)。

让我们来看一个例子。我将创建一个名为name的新变量,并将为值Christopher赋值。

在bash中,这是使用公式完成的:variable_name = your_variable。请勿在**=**符号和您的文本之间添加任何空格。

1
2
3
christopher@linuxhandbook:~$ name=Christopher
christopher@linuxhandbook:~$ echo $name
Christopher

如果我只输入变量名会怎样?

1
2
christopher@linuxhandbook:~$ echo name
name

如果您忘记了该符号,bash将返回您输入的文本。在这里,我告诉它回显或打印“名称”。没有$符号,bash无法识别您要使用已创建的变量。

您的变量将被插入到调用它的位置。因此,我也可以将其包含在这样的句子中:

1
2
christopher@linuxhandbook:~$ echo "Hello, $name. $name is a great name. It's good to meet you."
Hello, Christopher. Christopher is a great name. It's good to meet you.

您可以使用变量做很多事情,但是我希望该入门足以使任何阅读本文的人都能理解它们的工作原理。

环境变量与shell(本地)变量

对于理解源命令的下一个关键,让我们谈谈持久性。这是考虑shell变量和环境变量之间差异的简便方法。您可能还根据上下文根据“可移植性”来考虑它。

简而言之,如果在终端shell中创建变量,则退出该shell后该变量将丢失。

相反,环境变量在您的操作系统中具有持久性。这些变量通常使用所有大写字母来区分自己。

例如,您的用户名被操作系统称为$ USER。

1
2
christopher@linuxhandbook:~$ echo $USER
christopher

好的,因此您花了很多时间来研究环境变量和shell变量之间的差异。这与source有什么关系?如果不了解这块则,运行sourcebash不会有任何区别。

Source vs Bash

如果您使用Linux已有一段时间,那么您可能会遇到这些命令,并认为它们做了相同的事情。毕竟,这两个命令都可以用来执行脚本。

source在当前shell中工作,与运行bash会创建一个新shell不同。这并不明显,因为没有显示新窗口。

比如编写一个非常简单的脚本(将其称为echo.sh),如下所示:

1
2
3
4
#! bin/bash

echo $USER
echo $name

在终端中执行其他任何操作之前,请将值分配给变量名称。

1
christopher@linuxhandbook:~$ name=chris

接下来,我将向您展示在分配变量的同一终端中尝试所有3条命令时会发生什么。

1
2
3
4
5
6
7
8
9
christopher@linuxhandbook:~$ bash echo.sh 
christopher

christopher@linuxhandbook:~$ source echo.sh
christopher
chris
christopher@linuxhandbook:~$ . echo.sh
christopher
chris

如您所见,通过bash执行脚本时,无法识别本地变量。

使用source命令刷新环境变量

Source也可以用于更新当前Shell中的环境变量。此任务的常见应用程序是在当前shell中更新bash配置文件。

用户可能想要修改其bash配置文件以创建别名。通常,一旦保存了配置,您将需要打开一个新的终端窗口以进行更改。

1
christopher@linuxhandbook:~$ source .bashrc 

运行此命令将刷新当前外壳程序中的设置,而不会强制您打开新终端。

通过几个迭代后,前端代码变得越来越臃肿越来越乱。所以编码规范就排上日程。

结合这段时间我们的经验加上从网上搜集到的实用的编码规范,做一个简单整理。

前端编码规范意见稿

  • 统一ESLint文件
    • 比如用Airbnb,随着积累可以在其基础上进行扩展。
  • React组件
    • 如果组件需要维护自己的state或者使用其生命周期方法则用class,除此以外都用function。
  • Redux
    • 除了纯渲染组件(没有复杂的交互、逻辑),其余都用redux
              redux使代码结构更加清晰,可读性较强便于维护(倒逼组件或者模块拆的更加合理)。
      
        redux可当成全局内存库来用,当没有更新state时,无论何时何地都能到一样的数据,便于通用组件的开发
        redux可以减少数据的传递,不用依次往下传,随用随取,特别是针对组件层级比较深的情况
  • 统一格式化插件(如果要用格式化插件)
    • 比如VSCode的prettier或者beauty,千万避免多人用多套格式化插件的情况

JS开发规范

对象

  • 不要使用关键字作为key或者属性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    var user = {
    private: true
    };

    // good
    var user = {
    hidden: true
    };

数组

  • 如果你不知道数组的长度,使用push

    1
    2
    3
    4
    5
    6
    7
    8
    var userArr = [];


    // bad
    userArr[index] = 'gamehu';

    // good
    userArr.push('gamehu');
  • 当你需要拷贝数组时

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    var len = users.length,
    usersCopy = [],
    i;

    // bad
    for (i = 0; i < len; i++) {
    usersCopy[i] = items[i];
    }

    // good
    usersCopy = users.slice();

    //good ES6

    usersCopy = [...users];

    usersCopy=Array.from(users);


  • 将类数组的对象转成数组.

    1
    2
    3
    4
    5
    let args = [].slice.apply(users);

    // ES6
    let args=Array.from(users);

字符串

  • 对字符串使用单引号 ‘’
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // bad
    var name = "Gamehu";

    // good
    var name = 'Gamehu';

    // bad
    var fullName = "Gamehu " + this.lastName;

    // good
    var fullName = 'Gamehu ' + this.lastName;

函数

  • 绝对不要把参数命名为 arguments, 将覆盖作用域内传过来的 arguments 对象.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    function nope(name, options, arguments) {
    // ...stuff...
    }

    // good
    function yup(name, options, args) {
    // ...stuff...
    }
  • 当函数的参数特别多的时候用对象封装起来再传递.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    //bad

    export const resourceStoreDynamicFormForEdit = (group, data, index, form, editableIndex, sortItems, formItemLayout, validatorMap, dataMap, ciType, selectedCiEditable, showAlarmSlowStrategy) => {}

    // good

    const params={};

    params={
    group:group, xxx
    }

    export const resourceStoreDynamicFormForEdit=(params) ={
    }

  • 函数应该只在一个抽象层次上做一件事.

    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
    30

    // bad
    function getUserRouteHandler (req, res) {
    const { userId } = req.params
    // inline SQL query
    knex('user')
    .where({ id: userId })
    .first()
    .then((user) => res.json(user))
    }

    // good
    // User model (eg. models/user.js)
    const tableName = 'user'
    const User = {
    getOne (userId) {
    return knex(tableName)
    .where({ id: userId })
    .first()
    }
    }

    // route handler (eg. server/routes/user/get.js)
    function getUserRouteHandler (req, res) {
    const { userId } = req.params
    User.getOne(userId)
    .then((user) => res.json(user))
    }


  • 更高层次的函数在低层次函数的前面,便于阅读.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // bad
    // "I need the full name for something..."
    function getFullName (user) {
    return `${user.firstName} ${user.lastName}`
    }

    function renderEmailTemplate (user) {
    // "oh, here"
    const fullName = getFullName(user)
    return `Dear ${fullName}, ...`
    }

    // good
    function renderEmailTemplate (user) {
    // "I need the full name of the user"
    const fullName = getFullName(user)
    return `Dear ${fullName}, ...`
    }

    // "I use this for the email template rendering"
    function getFullName (user) {
    return `${user.firstName} ${user.lastName}`
    }
  • 声明函数时最好设置默认值.

    1
    2
    3
    //es6
    function (a=1, b=1) { // function code }

  • 如果想避免var变量造成的命名冲突,不存在特殊场景时可考虑使用立即执行函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    立即执行函数,即Immediately Invoked Function Expression (IIFE),正如它的名字,
    就是创建函数的同时立即执行。它没有绑定任何事件,也无需等待任何异步操作:
    (function() {
    // 代码
    // ...
    })();
    function(){…}是一个匿名函数,包围它的一对括号将其转换为一个表达式,
    紧跟其后的一对括号调用了这个函数。立即执行函数也可以理解为立即调用一个匿名函数。
    立即执行函数最常见的应用场景就是:将var变量的作用域限制于你们函数内,这样可以避免命名冲突。


  • 当有场景需要使用私有属性时,使用闭包定义私有变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    function Product() {

    var name;

    this.setName = function(value) {
    name = value;
    };

    this.getName = function() {
    return name;
    };
    }

    var p = new Product();
    p.setName("Fundebug");

    console.log(p.name); // 输出undefined
    console.log(p.getName()); // 输出Fundebug
    代码中,对象p的的name属性为私有属性,使用p.name不能直接访问。


变量

  • 总是使用 let、const、var来声明变量,如果不这么做将导致产生全局变量,我们要避免污染全局命名空间。

    1
    2
    3
    4
    5
    // bad
    superPower = new SuperPower();

    // good
    var superPower = new SuperPower();
  • 使用一个 let 以及新行声明多个变量,缩进4个空格。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    let items = getItems();
    let goSportsTeam = true;
    let dragonball = 'z';

    // good
    let items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';
  • 最后再声明未赋值的变量,当你想引用之前已赋值变量的时候很有用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // bad
    var i, len,hidden = true,
    items = getItems();

    // bad
    var i, items = getItems(),
    hidden = true,
    len;

    // good
    var items = getItems(),
    hidden = true,
    length,
    i;
  • 在作用域顶部声明变量,避免变量声明和赋值引起的相关问题。

    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    // bad
    function() {
    test();
    console.log('doing stuff..');

    //..other stuff..

    var name = getName();

    if (name === 'test') {
    return false;
    }

    return name;
    }

    // good
    function() {
    var name = getName();

    test();
    console.log('doing stuff..');

    //..other stuff..

    if (name === 'test') {
    return false;
    }

    return name;
    }

    // bad
    function() {
    var name = getName();

    if (!arguments.length) {
    return false;
    }

    return true;
    }

    // good
    function() {
    if (!arguments.length) {
    return false;
    }

    var name = getName();

    return true;
    }
  • 在声明变量时初始化变量。

    1
    2
    3
    4
    5
    6
    7
    8
    var firstName = "",
    lastName = "",
    price = 0,
    discount = 0,
    fullPrice = 0,
    myArray = [],
    myObject = {};

  • 在声明变量时别用对象。

    1
    2
    3
    4
    5
    6
    7
    8
    Use {} instead of new Object()
    Use "" instead of new String()
    Use 0 instead of new Number()
    Use false instead of new Boolean()
    Use [] instead of new Array()
    Use /()/ instead of new RegExp()
    Use function (){} instead of new Function()

  • 用===代替==,因为==会在比较之前进行类型转换。

    1
    2
    3
    4
    5
    6
    7
    8
    0 == "";        // true
    1 == "1"; // true
    1 == true; // true

    0 === ""; // false
    1 === "1"; // false
    1 === true; // false

  • 注意数字和字符串之间的转换。

    1
    2
    3
    4
    5
    6
    7
    8
    var x = 5 + 7;       // x.valueOf() is 12,  typeof x is a number
    var x = 5 + "7"; // x.valueOf() is 57, typeof x is a string
    var x = "5" + 7; // x.valueOf() is 57, typeof x is a string
    var x = 5 - 7; // x.valueOf() is -2, typeof x is a number
    var x = 5 - "7"; // x.valueOf() is -2, typeof x is a number
    var x = "5" - 7; // x.valueOf() is -2, typeof x is a number
    var x = 5 - "x"; // x.valueOf() is NaN, typeof x is a number

  • 在for循环的每次迭代中都不要让JavaScript读取数组的长度。 将长度值存储在另一个变量中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //bad
    var names = ['George',
    'Ringo',
    'Paul',
    'John'];
    for(var i=0;i<names.length;i++){
    doSomethingWith(names[i]);
    }

    //good
    var names = ['George',
    'Ringo',
    'Paul',
    'John'];
    for(var i=0,j=names.length;i<j;i++){
    doSomethingWith(names[i]);
    }

条件表达式和等号

  • 合理使用 === 和 !== 以及 == 和 !=.

  • 合理使用表达式逻辑操作运算

  • 条件表达式的强制类型转换遵循以下规则:

  • switch时一定要用default结束

    1
    2
    3
    4
    5
    6
    7
    1: 对象 被计算为 true
    2: Undefined 被计算为 false
    3: Null 被计算为 false
    4: 布尔值 被计算为 布尔的值
    5: 数字 如果是 +0, -0, or NaN 被计算为 false , 否则为 true
    6: 字符串 如果是空字符串 '' 则被计算为 false, 否则为 true

  • 使用快捷方式

    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    // bad
    if (name !== '') {
    // ...stuff...
    }

    // good
    if (name) {
    // ...stuff...
    }

    // bad
    if (collection.length > 0) {
    // ...stuff...
    }

    // good
    if (collection.length) {
    // ...stuff...
    }

    //bad
    if(v){
    var x = v;
    } else {
    var x =10;
    }

    //good
    var x = v || 10;


    //bad
    var direction;
    if(x > 100){
    direction = 1;
    } else {
    direction = -1;
    }

    //good
    var direction = (x > 100) ? 1 : -1;


  • 给所有多行的块使用大括号
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // bad
    if (test)
    return false;

    // good
    if (test) return false;

    // good
    if (test) {
    return false;
    }

    // bad
    function() { return false; }

    // good
    function() {
    return false;
    }

注释

  • 使用 /** … */ 进行多行注释,包括描述,指定类型以及参数值和返回值

    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
    // make() returns a new element
    // based on the passed in tag name
    //
    // @param <String> tag
    // @return <Element> element
    function make(tag) {

    // ...stuff...

    return element;
    }

    // good
    /**
    * make() returns a new element
    * based on the passed in tag name
    *
    * @param <String> tag
    * @return <Element> element
    */
    function make(tag) {

    // ...stuff...

    return element;
    }
  • 使用 // 进行单行注释,在评论对象的上面进行单行注释,注释前放一个空行.

    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
     // bad
    var active = true; // is current tab

    // good
    // is current tab
    var active = true;

    // bad
    function getType() {
    console.log('fetching type...');
    // set the default type to 'no type'
    var type = this._type || 'no type';

    return type;
    }

    // good
    function getType() {
    console.log('fetching type...');

    // set the default type to 'no type'
    var type = this._type || 'no type';

    return type;
    }
  • 如果你有一个问题需要重新来看一下或如果你建议一个需要被实现的解决方法的话需要在你的注释前面加上 FIXME 或 TODO 帮助其他人迅速理解

    1
    2
    3
    4
    5
    6
    7
     function Calculator() {

    // FIXME: shouldn't use a global here
    total = 0;

    return this;
    }
    1
    2
    3
    4
    5
    6
    7
     function Calculator() {

    // TODO: total should be configurable by an options param
    this.total = 0;

    return this;
    }

空白

  • 缩进、格式化能帮助团队更快得定位修复代码BUG.

  • 将tab设为4个空格

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // bad
    function() {
    ∙∙var name;
    }

    // bad
    function() {
    ∙var name;
    }

    // good
    function() {
    ∙∙∙∙var name;
    }
  • 大括号前放一个空格

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    {
    // bad
    function test(){
    console.log('test');
    }

    // good
    function test() {
    console.log('test');
    }

    // bad
    dog.set('attr',{
    age: '1 year',
    breed: 'Bernese Mountain Dog'
    });

    // good
    dog.set('attr', {
    age: '1 year',
    breed: 'Bernese Mountain Dog'
    });
    }
  • 在做长方法链时使用缩进.

    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
    // bad
    $('#items').find('.selected').highlight().end().find('.open').updateCount();

    // good
    $('#items')
    .find('.selected')
    .highlight()
    .end()
    .find('.open')
    .updateCount();

    // bad
    var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true)
    .attr('width', (radius + margin) * 2).append('svg:g')
    .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
    .call(tron.led);

    // good
    var leds = stage.selectAll('.led')
    .data(data)
    .enter().append('svg:svg')
    .class('led', true)
    .attr('width', (radius + margin) * 2)
    .append('svg:g')
    .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
    .call(tron.led);

逗号

  • 不要将逗号放前面

    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
    // bad
    var once
    , upon
    , aTime;

    // good
    var once,
    upon,
    aTime;

    // bad
    var hero = {
    firstName: 'Bob'
    , lastName: 'Parr'
    , heroName: 'Mr. Incredible'
    , superPower: 'strength'
    };

    // good
    var hero = {
    firstName: 'Bob',
    lastName: 'Parr',
    heroName: 'Mr. Incredible',
    superPower: 'strength'
    };
  • 不要加多余的逗号,这可能会在IE下引起错误,同时如果多一个逗号某些ES3的实现会计算多数组的长度。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // bad
    var hero = {
    firstName: 'Kevin',
    lastName: 'Flynn',
    };

    var heroes = [
    'Batman',
    'Superman',
    ];

    // good
    var hero = {
    firstName: 'Kevin',
    lastName: 'Flynn'
    };

    var heroes = [
    'Batman',
    'Superman'
    ];

分号

  • 语句结束一定要加分号
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // bad
    (function() {
    var name = 'Skywalker'
    return name
    })()

    // good
    (function() {
    var name = 'Skywalker';
    return name;
    })();

    // good
    ;(function() {
    var name = 'Skywalker';
    return name;
    })();

类型转换

  • 在语句的开始执行类型转换.

  • 字符串

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //  => this.reviewScore = 9;

    // bad
    var totalScore = this.reviewScore + '';

    // good
    var totalScore = '' + this.reviewScore;

    // bad
    var totalScore = '' + this.reviewScore + ' total score';

    // good
    var totalScore = this.reviewScore + ' total score';
  • 对数字使用 parseInt 并且总是带上类型转换的基数.,如parseInt(value, 10)

    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
    var inputValue = '4';

    // bad
    var val = new Number(inputValue);

    // bad
    var val = +inputValue;

    // bad
    var val = inputValue >> 0;

    // bad
    var val = parseInt(inputValue);

    // good
    var val = Number(inputValue);

    // good
    var val = parseInt(inputValue, 10);

    // good
    /**
    * parseInt was the reason my code was slow.
    * Bitshifting the String to coerce it to a
    * Number made it a lot faster.
    */
    var val = inputValue >> 0;
  • 布尔值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var age = 0;

    // bad
    var hasAge = new Boolean(age);

    // good
    var hasAge = Boolean(age);

    // good
    var hasAge = !!age;

命名约定

  • 避免单个字符名,让你的变量名有描述意义。

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

    // good
    function query() {
    // ..stuff..
    }
  • 当命名对象、函数和实例时使用驼峰命名规则

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // bad
    var OBJEcttsssss = {};
    var this_is_my_object = {};
    var this-is-my-object = {};
    function c() {};
    var u = new user({
    name: 'Bob Parr'
    });

    // good
    var thisIsMyObject = {};
    function thisIsMyFunction() {};
    var user = new User({
    name: 'Bob Parr'
    });
  • 当命名构造函数或类时使用驼峰式大写

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // bad
    function user(options) {
    this.name = options.name;
    }

    var bad = new user({
    name: 'nope'
    });

    // good
    function User(options) {
    this.name = options.name;
    }

    var good = new User({
    name: 'yup'
    });
  • 命名私有属性时前面加个下划线 _

    1
    2
    3
    4
    5
    6
    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';

    // good
    this._firstName = 'Panda';
  • 模块开发的常量定义必须包含“完整的模块名称“

    1
    2
    3
    4
    5
    6
    // bad
    export const GET_ASSET_LIST = 'GET_ASSET_LIST';

    // good
    export const GET_ASSET_LIST = 'ALARM_GET_ASSET_LIST';

  • 望名知意,建议驼峰命名(函数也适用)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    // bad
    let fItem;

    // good
    let formItem;

    //bad
    let yUnit = unitObj.unit;

    //good
    let yAxisUnit = unitObj.unit;

存取器

  • 属性的存取器函数不是必需的
  • 如果你确实有存取器函数的话使用getVal() 和 setVal(‘hello’),java getter、setter风格或者jQuery风格
  • 如果属性是布尔值,使用isVal() 或 hasVal()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    if (!dragon.age()) {
    return false;
    }

    // good
    if (!dragon.hasAge()) {
    return false;
    }
  • 可以创建get()和set()函数,但是要保持一致
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function Jedi(options) {
    options || (options = {});
    var lightsaber = options.lightsaber || 'blue';
    this.set('lightsaber', lightsaber);
    }

    Jedi.prototype.set = function(key, val) {
    this[key] = val;
    };

    Jedi.prototype.get = function(key) {
    return this[key];
    };

构造器

  • 给对象原型分配方法,而不是用一个新的对象覆盖原型,覆盖原型会使继承出现问题。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    function Jedi() {
    console.log('new jedi');
    }

    // bad
    Jedi.prototype = {
    fight: function fight() {
    console.log('fighting');
    },

    block: function block() {
    console.log('blocking');
    }
    };

    // good
    Jedi.prototype.fight = function fight() {
    console.log('fighting');
    };

    Jedi.prototype.block = function block() {
    console.log('blocking');
    };
  • 方法可以返回 this 帮助方法可链。

    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
    // bad
    Jedi.prototype.jump = function() {
    this.jumping = true;
    return true;
    };

    Jedi.prototype.setHeight = function(height) {
    this.height = height;
    };

    var luke = new Jedi();
    luke.jump(); // => true
    luke.setHeight(20) // => undefined

    // good
    Jedi.prototype.jump = function() {
    this.jumping = true;
    return this;
    };

    Jedi.prototype.setHeight = function(height) {
    this.height = height;
    return this;
    };

    var luke = new Jedi();

    luke.jump()
    .setHeight(20);
  • 可以写一个自定义的toString()方法,但是确保它工作正常并且不会有副作用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function Jedi(options) {
    options || (options = {});
    this.name = options.name || 'no name';
    }

    Jedi.prototype.getName = function getName() {
    return this.name;
    };

    Jedi.prototype.toString = function toString() {
    return 'Jedi - ' + this.getName();
    };

事件

  • 当给事件附加数据时,传入一个哈希而不是原始值,这可以让后面维护时加入更多数据到事件数据里而不用找出并更新那个事件的事件处理器
    1
    2
    3
    4
    5
    6
    7
    8
    // bad
    $(this).trigger('listingUpdated', listing.id);

    ...

    $(this).on('listingUpdated', function(e, listingId) {
    // do something with listingId
    });
  • 更好:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // good
    $(this).trigger('listingUpdated', { listingId : listing.id });

    ...

    $(this).on('listingUpdated', function(e, data) {
    // do something with data.listingId
    });

模块

  • 文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致

参考

一个小迭代结束之后,我们前端做了一个几次code revew,其中有个问题就是什么时候应该redux,因为redux其实有些重,但是某些场景其实很简单当让组件也很简单,有观点觉得没有必要上redux。

这个问题需要有个解法。

我觉得需要根据现有的环境来几个方面来考虑。

  1. 现在我们产品前端组件类别:基础组件、业务通用组件、定制化组件。
  2. 产品的体量,我们产品是ToB产品,功能比较多,代码量比较大。
  3. 业务场景的复杂度,因为是ToB,所以场景覆盖比较全,同时定制化需求稍微比较多,所以复杂度比较高。

好,接着我们再看看Redux,要讨论是否需要用它或者什么时候用它我们得搞清楚它的作用是什么。

首先咱们得确定一点React本身其实就是一个UI(View)层它不是一个前端框架, 如果是个复杂的产品,前端光负责View很显然是不够的,所以这几年出现了很多前端架构,比如MVVM架构下的Angular就是一套前端框架.

正因为这样,所以FaceBook提出了Flux的架构,就是为了能推出以React为基础的一套前端框架,让React不再孤单.

至于什么是Flux,阮老师说的很清楚: http://www.ruanyifeng.com/blog/2016/01/flux.html

看完之后你会发现,redux其实就是Flux的一种实现,主要解决两个问题:

  1. 清晰的代码层次结构,让复杂的前端变得更加健壮和易维护(action、dispatch、reducer等).
  2. 让React组件之间通信变得更有效率和更方便(通过store能在任何组件访问想要的数据,不管是横向的还是纵向的层级,而不用依次传递经过根本不需要该数据的组件,真正做到按需使用)

所以说到这儿我们可以简单列一下使用Redux的好处

  • 大多数情况下可以用reducer友好的把state从组件里分离出来,使组件更加的轻量化也增加了代码可读性.
  • Redux的store可以当成缓存使用,并且是全局的,你可以在任何地方使用它,只要不更新state,无论何时何地获取的数据都是一样的,使数据共享变得容易.
  • Redux友好的把state统一放到了store里,通过action、dispatch、reducer,让state的变化变得可预测,让数据流变得清晰明朗,易于维护.
  • Redux的store让数据的传递变得更有效率,无需依次进行数据传递(哪怕该组件根本不需要这个数据),可以满足需要的时候再获取.
  • Redux更有利于公共组件的开发,公共组件无论在哪儿使用,可以通过dispatch触发其行为.

试着回答一下

我觉得除非已经明确的定义某个组件只是一个纯UI类组件,不会存在其它业务逻辑或者行为,那我觉得没有必要用Redux,比如我们产品中的一些展示组件,这些组件一个最大的特点就是,只需要给它数据,它渲染就完事了不会有额外的一些操作,那完全就一个jsx就搞定了.除此以外都建议用Redux,虽然它复杂了些,但是你会慢慢发现他的好.

简单记录一下我平时使用搜索套路。

浏览medium的时候发现了一位大佬,继而发现了她的博客,继而读到了一篇关于“动力”的文章。觉得说的很到位,很契合我现在正努力改变而采用的方式,那就是先动起来。

这儿做个搬运工也做个自我的审视。

每天,无数人都在为动力而苦苦挣扎,或者说,缺乏动力。他们渴望做得更多,做得更好,但他们根本没有这样做的动力。

结果,他们最终过着平庸的生活,因为他们知道自己有“潜力”,但他们从未选择追求这种潜力。他们安顿在相同的套路中,发现自己希望得到比他们所收到的更多的东西。

必须有一个更好的解决方案,对不对?

我对动机问题的回答

我知道我知道。您现在可能会问我:“ 您如何保持动力?” 。我的答案很简单:

我不会试图简单地“保持积极性”。我采取行动,动机随之而来。

人们似乎认为这是相反的,我们必须等待动力来使我们采取行动。但这离事实还远!

动机不是一种神奇的能量,它只是神奇地独自出现。这是必须通过行动创造的力量。因此,真正有动力的唯一方法就是行动。如果您所做的只是将时间浪费在如意算盘上,那么您寻找的动力就永远不会到来。

每个人(即使是最成功的人)在完成任务时也有同样的恐惧感。其实我们也知道有要做的事情,但是却因为自己不想做而挣扎。

不要想太多

在很多时候不想动的时,你得遵从你的内心而不是你的大脑,你的大脑总是会让你不动,因为它就是你的潜意识,但是从内心你知道你是应该要动起来的。所以这个时候遵从自己的内心赶紧挪动屁股动起来。

要点:采取行动的人有动力

动机是当我们渴望采取行动,而我们实际上遵循这一愿望时发生的事情。如果您想获得动力,则必须采取行动。我意识到这是一个自我实现的预言,但这正是我要提出的重点。

当您采取行动时,就会积蓄动力。动量推动着你前进,你想采取更多的行动。该行动将产生更多动力,依此类推。这是一个良性循环:正反馈回路。

img

如果发现自己缺乏动力,请看一下自己的行为。如果您不采取行动,是时候开始努力了。不久之后,您会发现自己比想知道的要有更多的动力!

另一方面,零作用意味着零动量。如果您拒绝采取行动,那么您正在创建一个充满负面事物的空间。通常,这个空间充满思想:关于您应该如何或为什么或何时采取行动的想法。您的大脑过度分析了您想要完成的工作。您会发现自己正在思考完成动作所需的所有方式以及可能会失败的所有方式。

恐惧和焦虑开始发作。在这一点上,您实际上已经产生了消极的动力,并且您一开始就害怕要完成的一切。您开始下滑。

您坐下来等待动力的时间越长,就会发现的越少。

当然,并非每种情况都要求您立即投入工作而不考虑应该采取的行动。例如,假设您需要与一个浪漫的伴侣进行艰难的谈话,以结束您的恋爱关系。您害怕伤害他们,害怕伤害自己。这种恐惧削弱了您的动力,使您无意继续进行对话,即使这是100%必要的。

你该怎么办?您是否应该让这种对话像暴风雨云一样继续笼罩着您,直到您对它感到厌倦为止,只是在没有真正思考的情况下跳入对话?

没有!

在这种情况下,思考如何最好地进行对话实际上是有帮助的。有远见的人会使您免于伤害任何人的感情。考虑如何完成一项动作与考虑实际完成所述动作之间存在巨大差异。您可能需要花费数周,数月甚至数年的时间思考实际做某事,却一无所获。您必须意识到自己的想法:如果您发现自己偏离批判性思维而进入可怕的领域,那么该是停止停顿并采取行动的时候了!

让我们回到眼前的场景。设置日期和您计划与伴侣进行对话的时间。设定时间和地点会让您负责。不要让自己对这一承诺退缩-放弃只是意味着欺骗自己。

在您履行了这一承诺之后,然后-仅那时!-您是否应该开始考虑动作本身。

通过提前计划并做出承诺,您实质上改变了您的思考过程。您现在不再“呃,我不想进行对话”,而是以“好吧,我在对话中实际上会说些什么?”的心态。。这是非生产性思维与生产性思维的问题。

看到不同?😊

结论

动机的“秘密”就是行动。如果您发现自己动力不足,则可能是因为无所作为。如果您想找到动力,就需要全身心投入并开始行动!

大小无关紧要。实际上,我建议您将其缩小。我们经常想尽可能地“完成很多工作”,但这是一个不好的方法,因为它耗尽了我们的意志力。取而代之的是,您应该采取足够的措施来增强动力,但要在压力开始感到沉重之前就停止。这样,您便有意愿回来以后再做更多的事情。

现在是您采取行动的时候了。

产品1.1时,有较大需求的调整,映射到代码则意味着很多功能需要改造或者新增,此时才发现前期由于只求快,代码的可维护性太差,导致要改造模块功能太麻烦,所以就想着边改造边对代码进行重构,一步步提升性能和可维护性。

这个过程我顺便用Lighthouse跑了一下我们的页面,结果挺崩溃的。

我相当不能接受这个结果,遂列出了优化行动项

  1. 深入学习React
    1. 官网再详细看一遍
    2. 极客时间上React学完
    3. medium.com上找对应的React、Redux文章
  2. 尽可能少的渲染
    1. 掌握react的条件渲染规则
    2. 辨识什么情况下用Redux
    3. 父子组件、公共组件的定义和搭配
  3. code review
    1. 代码逻辑
    2. 代码可维护性
    3. 渲染次数
    4. 第三方组件风险

React的条件渲染

官方的文章说的很详细,我很舒服。

https://reactjs.org/docs/reconciliation.html

这篇文章虽然老了点但是讲的通俗易懂,编于更深入的理解React的Reconciliation

https://tylermcginnis.com/react-elements-vs-react-components/

然后我们再谈怎么阻止多余的渲染

官方是这么说的:

Preventing Component from Rendering
In rare cases you might want a component to hide itself even though it was rendered by another component. To do this return null instead of its render output.

所以最简单的方式就是当不需要某个模块输出是那就return null,从而避免渲染;

因为我们是React+Redux,梳理代码后得出如下有效技巧:

  1. 子组件添加state判断
  2. 合理记录state用于条件判断是否需要渲染
  3. 为元素设置key,选择不会动态变化得作为key,比如id之类的,杜绝使用index
  4. 必要的时候设置shouldComponentUpdate
  5. 卸载组件后记住重置状态
  6. 利用devtool调试并提升代码性能

参考:

背景

产线当前在代码库管理遇到了如下挑战:

  1. 职责权限不明确,关键操作未收口,分支管理效率不理想。
  2. 多功能同时开发时,有功能之间互相Block、提测受影响、新分支建立困难的问题。
  3. 无法应对后续应对多客户定制化开发场景的需要。
  4. 测试团队人力紧张。

针对以上问题,本模型在旧模型的基础上进行改造,做出以下改变:

  1. 分支和tag建立收口到专人负责,不用再互相等待。
  2. 多feature并行开发测试,减少开发阶段的耦合。
  3. 设立定制化feature机制应对后期多客户的场景。
  4. 功能测试收束至develop分支进行。

实操

搬运一下我们团队的分支管理办法,大体的思路还是遵循git flow的规范

develop和release

当数个feature开发并提测后,进入多feature集成测试阶段。在develop分支进行多feature集成测试,完成后转入实验局测试。完成实验局阶段后,产品团队决定发版,这时从develop拉出release分支并根据版本号命名。此时会做最终的功能集成测试和回归测试,验证功能间是否有冲突导致的BUG和遗漏BUG,测试完成后合并至master和develop分支。

  • 步骤1

    当新工程建立时,配置管理员从master分支拉出develop分支,设置保护权限,关闭develop分支所有人员提交代码权限,完成后邮件通知全体研发。

  • 步骤2

    当功能提测后,研发经理将feature分支合并至develop分支。若某feature分支未成功提测,则略过该分支。当全部feature分支合并完毕后开放develop权限。

    每次测试部署时,由配置管理员建立tag,然后根据tag部署。

  • 步骤3

    重复BUG修复过程直至符合发布要求。

  • 步骤4

    当多feature集成测试阶段结束,配置管理员邮件通知全员即将锁定的分支,然后设置develop保护权限,建立tag进行实验局部署。

  • 步骤5

    实验局阶段将对bug进行整理,非block级bug将在后续feature中进行规划和修改。

    注意事项:block级bug将视紧急程度开放权限给指定人员

  • 步骤6

    当产品团队确定发版,配置管理员从develop分支拉出release分支,邮件通知全员。

  • 步骤7

    集成测试部署时,配置管理员邮件通知全员即将锁定的分支,然后设置release保护权限,锁定release权限。(设置锁定的目的是防止转测阶段有人提交代码出现BUG,导致tag不可用)

  • 步骤8

    配置管理员在release上打测试tag,然后解除锁定,邮件通知全员。

    研发下载release代码,准备修改bug。

    测试经理基于测试tag,启动集成测试流程。

    研发修复BUG并提交至release分支。

    步骤7重复多次直到符合发布要求

    角色 职责 通知机制
    研发经理 feature分支向develop分支合并 release分支向master分支合并 release分支向develop分支合并 feature分支的合并需要通知配置管理和Scrum Master release分支的合并需要通知配置管理
    配置管理 develop分支的建立 release分支的建立、tag建立、锁定、删除 master分支的tag建立 feature分支的建立、tag建立、锁定、删除 develop分支建立需要邮件通知全体人员 release分支的tag建立需要通知测试,release分支的建立、锁定、解锁需要邮件通知全体人员 master分支的tag建立需要通知负责安装包的研发人员 feature分支的tag建立需要通知测试, feature分支的建立、锁定、删除需要通知相关研发人员
    研发 开发、bugfix、安装包生成
    测试经理 release分支的测试、部署 feature分支的测试、部署 develop分支的测试、部署 feature分支验收测试和多feature集成测试结果需要邮件通知对应研发和研发经理

hotfix分支场景

hotfix分支用于产品稳定版及现场问题的修正。当一线端反馈了BUG并且判定需要作为hotfix修复时,从master拉出hotfix分支。分支修复完成后,重新合并入master和develop分支。若hotfix可能影响定制化feature的场景,由ScrumMaster判断是否需要进行合入。

  • 步骤1

    当master发布完成后,当有现场问题需要修正时,配置管理员从master的发布tag拉hotfix分支,邮件通知全员。

    研发从hotfix分支下载代码,修改缺陷。

    研发在实验局(建议)或专用环境验证缺陷是否修改完成。

    研发将修改提交,然后推送到hotfix。

    研发修改故障单状态,提交测试。

  • 步骤2

    在hotfix整体送测日,配置管理员锁定hotfix权限。

    配置管理员在hotfix上打转测tag ,邮件通知全员。

    测试经理基于转测试tag,启动回归测试流程。

    如果回归测试不通过,配置管理员开放hotfix权限,重复步骤2直到回归测试通过。

  • 步骤3

    回归测试通过,测试经理确认基于hotfix的哪个tag发布,邮件通知全员。

    执行步骤4、5、6,配置管理确定步骤完成后删除分支

  • 步骤4

    研发经理基于hotfix的发布tag,向master合并,完成后通知配置管理员。

    配置管理员确认后,给master打发布tag,转升级包生成流程。

  • 步骤5

    研发经理基于hotfix的发布tag向develop合并,完成后通知配置管理员。

  • 步骤6

    配置管理员通知研发判断hotfix是否影响定制化分支,若影响,则通知研发经理基于hotfix的发布tag向定制化feature合并,完成后通知配置管理员和Scrum Master。

角色 职责 通知机制
研发经理 hotfix发布tag向develop分支合并 hotfix发布tag向master分支合并 hotfix发布tag向定制化feature分支合并 hotfix tag向develop和master的合并需要通知配置管理 hotfix tag向定制化feature的合并需要通知配置管理和Scrum Master
配置管理 hotfix分支的建立、锁定、删除 hotfix分支的tag建立 判断hotfix是否向定制化feature合并 hotfix分支的建立、锁定、解锁需要邮件通知全体人员 hotfix分支的tag建立需要通知测试经理 hotfix需要向定制化feature合并需要通知研发经理
研发 hotfix分支的日常实验局部署和bugfix 升级包生成
测试经理 hotfix分支的测试、部署 hotfix分支发布 hotfix分支发布需要邮件通知全体人员

feature分支场景

feature分支用于进行新功能开发和上个阶段实验局的缺陷修复。产线管理团队需要规划好功能的相关性和相互依赖,避免把相互依赖的功能放到不同的feature中去。在feature规划完成后,需要建立分支,并在feature分支上完成工程开发、提测阶段。提测成功后,feature分支将被合并至develop分支。

多个feature分支将在develop进行合并测试,若测试前有feature不满足提测条件,为了不影响其它feature的发布,可以将这个分支延迟合并。

注意事项:common包等不参与部署过程的公共模块,在产线管理团队规划时可采用其它的分支管理策略,例如多个虚拟团队公用一个feature分支。

  • 步骤1

    当需求确定时,研发经理确定feature规划。feature规划一般根据敏捷小组进行,也会受到功能关联性的影响。feature规划确定后需要邮件通知研发小组和配置管理员。

  • 步骤2

    Scrum Master邮件发起feature分支建立申请,然后配置管理员从develop分支拉出feature分支,通知小组成员和研发经理。研发人员开始功能开发。

  • 步骤3

    准备提测,配置管理员锁定feature分支,通知小组成员、测试经理和研发经理。研发部署锁定的feature进行提测,不论提测通过或未通过,Scrum Master都解除分支锁定,通知小组成员和研发经理。

  • 步骤4

    若提测失败,配置管理员重新打开分支权限。

  • 步骤5

    研发继续在feature分支进行bugfix。当bugfix完成后,配置管理员重新锁定分支。

  • 步骤6

    提测通过,研发经理merge分支至develop。确定merge成功后,删除feature分支,通知小组成员和配置管理员。

  • 步骤7

    当第二个feature或者后续feature需要提测时,需要先从develop反向合并然后进行检查,该步骤是为了防止代码冲突或者功能被覆盖。

注意事项:若合并分支时发现基础代码有冲突,研发需要给测试团队提供冲突列表,帮助测试团队着重验证冲突功能。

角色 职责 通知机制
研发经理 确定feature规划 feature分支向develop分支合并 develop分支向feature分支合并 feature规划需要通知小组成员 feature分支的合并需要通知小组成员和配置管理员
配置管理员 feature分支的建立、tag建立、锁定、删除 develop的tag建立 feature分支的建立、tag建立、锁定、删除需要通知小组成员和研发经理
研发 feature分支的日常开发、部署和bugfix develop分支的实验局部署、多feature集成测试阶段bug修复
Scrum Master 发起feature建立申请 feature分支建立向配置管理员申请

定制化feature分支场景

当存在定制化需求时,需要建立定制化feature分支。该分支用于定制化客户的功能开发、测试、打包等。在定制化开发过程中,若有影响该分支的release和hotfix出现,则需要合入定制化feature分支。

  • 步骤1

    定制化需求确定,配置管理员决定从哪个tag创建定制化feature分支。

  • 步骤2

    配置管理员从develop分支拉出定制化feature分支,通知相关小组成员和研发经理。

  • 步骤3

    当有release分支发布时,配置管理员决定是否合并至定制化feature分支,然后通知研发经理。

    研发经理从release分支合并代码至定制化feature分支。

  • 步骤4

    当有hotfix分支发布时,配置管理员决定是否合并至定制化feature分支,然后通知研发经理。

    研发经理从hotfix分支发布tag合并代码至定制化feature分支。

  • 步骤5

    准备提测,配置管理员锁定定制化feature分支,通知小组成员和测试经理。

  • 步骤6

    研发部署锁定的定制化feature进行提测,提测未通过,配置管理员解除分支锁定,通知小组成员。

  • 步骤7

    研发继续在定制化feature分支进行bugfix。当bugfix完成后,配置管理员重新锁定分支并且提测,通知小组成员、测试经理。

  • 步骤6

    提测通过,配置管理员对定制化feature分支建立tag,转安装包流程。

角色 职责 通知机制
研发经理 release分支向定制化feature分支合并 hotfix发布tag向定制化feature分支合并 定制化feature分支合并需要通知配置管理员
配置管理员 定制化feature分支的建立、锁定、解锁 定制化feature分支的tag建立 定制化feature分支的建立、锁定、解锁需要通知小组成员和研发经理
研发 定制化feature分支的日常开发、部署和bugfix
测试 定制化feature分支的日常测试活动

感谢:

前几天,组织上让我参加了一下咱们产品系统架构评估方面的一个会议,虽然内容较少但是我受益颇多。主持人是刚进入没多久的架构师,议题是现系统架构中一个很大的问题:现有架构(没做集群,因为大多数情况下客户现场只会给到2台服务器)吃不下每秒15000条syslog(网络交易日志),问题在于我们加的中间件kafka。

所以最终议题为:什么时候需要中间件—即为什么我们要拿掉kafka。

架构师抛了这几个问题:

  1. 解耦 怎么解?

    比如 redis 用于做数据的交换,这个数据是不能丢的。

    比如kafka,不保证时间,不保证顺序性。。。

  2. 如果去掉这个中间层(件)会怎样?

特别是第二个问题,要随时问自己,如果去掉了会怎样?别盲目解读在我们领域,不是什么是加一层不能解决的这句话。

本文引用的内容,如有侵权请联系我删除,给您带来的不便我很抱歉。