Git Product home page Git Product logo

blog's Introduction

blog's People

Contributors

chinawzc avatar

Watchers

James Cloos avatar  avatar

blog's Issues

React Hooks

R H

1.1 React Hooks

React Hooks 就是用函数的形式代替原来的继承类的形式,并且使用预函数的形式管理state,有Hooks可以不再使用类的形式定义组件了。---现在所有的组件都可以用函数来声明了。

react 官方文档:https://reactjs.org/docs/hooks-intro.html

react 中文文档: https://reactjs.bootcss.com/docs/hooks-intro.html

主要通过demo来讲

有可能翻车。。。

useState、useEffect、useContext、useReducer...

(useState, useEffect, useContext, useReducer, useMemo, useRef, useImperativeHandle, useDebugValue, useLayoutEffect, useCallback )

1.2 对比

Before:

import React, { Component } from 'react';
export default class Example extends Component {
    state = {
      count:0
    }
    addCount = () => {
        this.setState({
          count:this.state.count + 1
        })
    }
    render() { 
        return (
            <div>
                <p> You clicked {this.state.count} times </p>
                <button onClick={this.addCount}> Chlick me </button>
            </div>
        );
    }
}

After:

import React, { useState } from 'react';
export default () => {
    const [ count , setCount ] = useState(0);
    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={()=>{setCount(count+1)}}>click me</button>
        </div>
    )
}
1.3 demo
2.1 useState

​ (1.2中的对比示例)

  • useState 作用是用来声明状态变量。

    const [state, setState] = useState(initialState);
    

    声明:

const [ count , setCount ] = useState(0);

useState 这个函数接收的参数是状态的初始值(initialState),它返回的是一个数组,一个 state,一个更新 state 的函数。

所以上面的代码的意思就是声明了一个状态变量为count,并把它的初始值设为0,同时提供了一个可以改变 count 的状态值的方法函数setCount。

​ 改变 count 的值:

<button onClick={()=>{setCount(count+1)}}>click me</button>

直接调用setCount函数,这个函数接收的参数是修改过的新状态值。接下来 React 会重新渲染组件。

看demo

2.2 useEffect
  • 代替常用生命周期函数

可以把 useEffect Hook 看做 componentDidMountcomponentDidUpdatecomponentWillUnmount 这三个生命周期函数的组合。之前在生命周期里经常处理的比如 ajax请求数据,添加监听,手动修改DOM等等,可以放到useEffect处理。

useEffect(() => { 
		effect
    return () => {
        cleanup
    }
}, [input])

看demo

React首次渲染和之后的每次渲染都会调用 useEffect 函数,而之前要用两个生命周期函数分别表示首次渲染(componentDidMonut)和更新导致的重新渲染(componentDidUpdate)。

componentWillUnmount?

实现 componentWillUnmount生命周期函数:需要用到 useEffect 的一个可选的清除机制-返回一个函数,将添加和清除的逻辑放在一起。

看demo

1.每次状态发生变化,useEffect 都进行清除

2.useEffect的第二个参数[],它是一个数组,数组中可存入很多状态对应的变量,当状态值发生变化时,才进行清除。但是参数为空数组[]时,就是只有当组件将被销毁时才进行清除。另外默认情况下,effect 将在每轮渲染结束后执行,但你可以选择让[input]中的某个/某些state变化时它才执行。

这时用useState和useEffect 已经可以实现大部分的业务逻辑, 就可以愉快的编写组件了...

2.3 useContext

(get context anywhere)

在用类声明组件时,父子组件的传值是通过组件属性和 props 进行的。

react中,context 可以帮助我们跨越组件层级直接传递变量,实现共享; 现在react的hooks也提供了 useContext 这个方便的Hook。 useContext 有一个参数: context 对象本身。

const value = useContext(MyContext);

另外,和 useReducer 配合使用,可以实现类似 Redux 的作用(next)

demo

以上3个被称为是基础hook,其他的称为额外的hook

2.4 useReducer
const [state, dispatch] = useReducer(reducer, initialState, init)

useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。

Demo

结合useContext

Demo

2.5 end。。。

例子,可以去项目中找...

性能优化笔记

  • 1、 CDN

  • CDN(Content Delivery Network,即内容分发网络)指的是一组分布在各个地区的服务器

  • 的核心点有两个,一个是缓存,一个是回源。“缓存”就是说我们把资源 copy 一份到 CDN 服务器上这个过程,“回源”就是说 CDN 发现自己没有这个资源(一般是缓存的数据过期了),转头向根服务器(或者它的上层服务器)去要这个资源的过程。

  • CDN 往往被用来存放静态资源,CDN 服务器则像一个仓库,它只充当资源的“栖息地”和“搬运工”。所谓“静态资源”,就是像 JS、CSS、图片等不需要业务服务器进行计算即得的资源。而“动态资源”,顾名思义是需要后端实时动态生成的资源,较为常见的就是 JSP、ASP 或者依赖服务端渲染得到的 HTML 页面。“非纯静态资源”是指需要服务器在页面之外作额外计算的 HTML 页面。

  • CDN 的域名选取。同一个域名下的请求会不分青红皂白地携带 Cookie,而静态资源往往并不需要 Cookie 携带什么认证信息。把静态资源和主页面置于不同的域名下,完美地避免了不必要的 Cookie 的出现!

Git -- 修改commit信息

修改commit信息

场景:刚提交了一个代码,发现有几个字写错了,怎么办,当场再写一个修复这几个错别字的 commit?或者是commit信息写错了?

git commit --amend

在提交时,如果加上 --amend 参数,git 不会在当前 commit 上增加 commit,而是会把当前 commit 里的内容和暂存区(stageing area)里的内容合并起来后创建一个新的 commit,用这个新的 commit 把当前 commit 替换掉。所以 commit --amend 做的事就是它的字面意思:**对最新一条 commit **进行修正。

git add . (若有新修改)
git commit --amend

然后我们会看到提交信息编辑界面:

image

之后,insert 进入编辑状态,编辑 commit 信息后退出即可。通过 git log 可以验证查看。

需要注意的是:commit --amend 并不是直接修改原 commit 的内容,而是生成了一条新的 commit。


commit --amend 可以修复最新 commit 的错误,但如果是倒数第二个 commit 写错了,怎么办?

rebase -i

rebase -i 是 rebase --interactive 的缩写形式,意为"交互式 rebase"--让用户编辑完成合并操作 (其实 git rebase -i 的完整命令为:git rebase -i [startpoint] [endpoint],如果不指定[endpoint],则该区间的终点默认是当前分支HEAD所指向的commit。<有关rebase的详细操作--待写>,rebase的作用简要概括为:可以对某一段线性提交历史进行编辑、删除、复制、粘贴) 。

如下图,当我提交commit信息“g”之后发现上一次提交信息应该为“f”,但我写成了“ff”:

image

这时候我们就可以使用 git rebase -i 命令了。

git rebase -i HEAD~2

执行后跳到一个新界面:

image

由远及近列出了两个commit,前面pick代表的默认使用该提交commit,按i进入编辑模式,修改该字段值,经常使用的如下:

  1. pick:简写p,启用该commit;
  2. reword:简写r,使用该commit,但是修改提交信息,修改后可以继续编辑后面的提交信息;
  3. edit:简写e,使用commit,对其进行修改;
  4. squash:简写s,使用该commit,并将该commit并入前一commit;
  5. drop:简写d,移除该commit;

这里把第一个pick改为edit:

image

然后退出(按esc键退出编辑模式,然后输入:wq,保存当前编辑)

image

下面的步骤同上面的commit --amend,执行:

git commit --amend

修改完成之后,执行 rebase --continue (git也已经给了提示)来继续 rebase 过程,把后面的 commit 直接应用上去:

git rebase --continue

最后我们可以 git log 查看验证一下


参考链接:
Git提交历史的修改删除合并
Git 实战手册
Git 原理详解及实用指南

Git -- 撤销

撤销

reset

一般我们在最新的 commit 写错时,可以用 reset --hard 来把 commit 撤销

git reset --hard HEAD^

reset 这个指令虽然可以用来撤销 commit ,但它的实质行为并不是撤销,而是移动 HEAD。而 reset --hard HEAD^ 之所以起到了撤销 commit 的效果,是因为它把 HEAD 移动到了当前 commit 上,从而起到了「撤销」的效果。所以reset的本质实际上是移动HEAD。那么 reset 后面跟着的那个 --hard 是什么意思呢?除了--hard 还有什么其他参数呢?

举个例子,当前的操作情况如图:

image

在此举例要 reset 到的目标 commit 为e。当前缓存区和工作目录都有代码,也有已经commit的提交f(为了便于查看效果,当前把 index0.js 的修改commitindex1.js的修改放进缓存区,index2.js 的修改还放在工作区)。我们给 reset 不同的参数,来看看 reset 之后的代码如何:

index0.js 对应已经 commit 过的(有commit信息:f
index1.js 对应已经 add . 过的 (已放进缓存区)
index2.js 对应修改的(处于工作区)

reset --hard:重置工作目录

就是说,未提交的修改会被全部擦掉。例如你在上次 commit 之后又对文件做了一些改动,然后,你执行了 reset 并附上了 --hard 参数(git reset --hard HEAD^),然后你工作目录里的新改动也一起全都消失了,不管它们是否被放进暂存区。如下图:

image

reset --soft:保留工作目录

reset --soft 会在重置 HEAD 时,工作区中的修改还是放进工作区、已经 commit 的和已经在暂存区的都放进暂存区。也可以说是,工作区的还在工作区,commit的只是取消了commit但还是已经add了。如下图:

image

reset --mixed:保留工作目录,并清空暂存区

reset 如果不加参数,那么默认使用 --mixed 参数。它的行为是:工作区的修改、暂存区的内容以及已经commit 的修改,会被放进工作区。简而言之,就是「把所有差异都混合(mixed)放在工作区中」。如下图:

image

reset --merge:保留工作目录,清空暂存区或commit过的

--hard类似,只不过如果在执行reset命令之前你有改动一些文件并且未提交,merge会保留你的这些修改,hard则不会。总之它的行为就是:工作区的修改还在工作区、暂存区的内容以及已经commit 的修改,会被清除。如下图:

image

reset --keep:保留工作区并丢弃一些之前的提交

--hard类似,执行reset之前改动文件如果是分支修改了的,会提示你修改了相同的文件,不能合并。如果不是分支修改的文件,会移除缓存区。git status还是可以看到保持了这些修改。总之它的行为就是:工作区的修改还在工作区、暂存区的内容也被放在了工作区,已经commit 的修改则被清除了。如下图:

image

来个表总结对比下

命令 commit 暂存区 工作区
reset --hard 清除 清除 清除
reset --soft 暂存区 暂存区 工作区
reset --mixed 工作区 工作区 工作区
reset --merge 清除 清除 工作区
reset --keep 清除 工作区 工作区

checkout

如第一张图,当我们 git status 时,git 会提示使用git checkout丢弃修改:

image

git checkout -- filePath

丢弃工作区某个文件的改动

git checkout -- .

丢弃工作区所有文件的改动

如图所示,使用 checkout 会撤销工作区的改动:

image


more

git reset --hard origin/branchName?

将本地分支代码与远程分支同步,和git reset --hard commit_id 效果一样(commit_id为远程分支最新的一次commit_id),是一个快捷操作。hard对代码的影响同上,一样 hard 也可以改为softmixed(省略)

已经push到远程仓库的commit不允许reset?

git reset是会丢弃掉commit的,如果commit已经被push到远程仓库上了,也就意味着其他开发人员就可能基于这个commit形成了新的commit,这时你去reset,就会造成其他开发人员的提交历史莫名其妙的丢失,或者其他灾难性的后果。当然了,没有其他开发人员基于此的话是可以的,比如说自己的分支,此时必须使用强制推送覆盖远程分支,否则无法推送到远程分支,reset之后需要执行 git push origin branch --force 来同步远程。

常用命令?

reset到上一次:git reset HEAD^reset到上上次:git reset HEAD^^...,也可以:git reset HEAD~2
配合git loggit refloggit reset [commit_id]

不想回滚所有的文件?

git reset [commit_id] [file]

reset 之后又反悔了?

git reflog 得到 resetcommit_id,再 reset 回到未来

回滚之前务必先提交自己的代码?

因为你如果使用--hard的,同时你又没有提交的话,真的回不去了=_=

但是,reset --hard 时真的没有commit怎么办?

git reset时共有三种情况:1.之前的修改进行了 commit 提交(可以回去,同上);2.之前的修改未进行commit提交,但是进行了 git add 操作;3.之前的修改未进行 commit 提交,也未进行git add 操作
第二种情况,我们可以用 git fsck 命令,我们执行 git fsck --lost-found ,然后找到目录'.git/lost-found',经过一顿复杂的查找(一个一个文件的查看)可以找到之前的add但未commit的提交
第三种情况,git 命令是暂时无解了,此时我们就需要感谢IDE提供了一个强大的插件: Local History(vscode)。文件修改后修就会在这里生成新的记录(要保存),我们一个个对比选择好要还原的版本,再右键 -> Restore 就好了
后两种情况操作如此复杂,所以 reset 之前最好要 commit 一下

push远程了,并且小伙伴已经基于此开发了,又需要reset?

git操作:。。。
简单暴力点,不复杂的话还是辛苦手动重新改吧

reset --hard之后我们想应用某一个被 reset 掉的commit

此时我们可以使用git cherry-pick命令,git cherry-pick commit_id,会将commit_id应用到分支上


Note

reset有风险,使用需谨慎,记得commit


参考链接:
Git 实战手册
git reset回滚代码
远程仓库版本回退方法

Git -- rebase

Git -- rebase

git rebase

git rebase -i

修改commit

同之前章节

rebase -i

rebase -i 是 rebase --interactive 的缩写形式,意为"交互式 rebase"--让用户编辑完成合并操作 (其实 git rebase -i 的完整命令为:git rebase -i [startpoint] [endpoint],如果不指定[endpoint],则该区间的终点默认是当前分支HEAD所指向的commit。<有关rebase的详细操作--待写>,rebase的作用简要概括为:可以对某一段线性提交历史进行编辑、删除、复制、粘贴) 。

如下图,当我提交commit信息“g”之后发现上一次提交信息应该为“f”,但我写成了“ff”:

image

这时候我们就可以使用 git rebase -i 命令了。

git rebase -i HEAD~2

执行后跳到一个新界面:

image

由远及近列出了两个commit,前面pick代表的默认使用该提交commit,按i进入编辑模式,修改该字段值,经常使用的如下:

  1. pick:简写p,启用该commit;
  2. reword:简写r,使用该commit,但是修改提交信息,修改后可以继续编辑后面的提交信息;
  3. edit:简写e,使用commit,对其进行修改;
  4. squash:简写s,使用该commit,并将该commit并入前一commit;
  5. drop:简写d,移除该commit;

这里把第一个pick改为edit:

image

然后退出(按esc键退出编辑模式,然后输入:wq,保存当前编辑)

image

下面的步骤同上面的commit --amend,执行:

git commit --amend

修改完成之后,执行 rebase --continue (git也已经给了提示)来继续 rebase 过程,把后面的 commit 直接应用上去:

git rebase --continue

最后我们可以 git log 查看验证一下

合并commit

在上面修改commit信息方法中,执行git rebase -i后进入编辑界面前面的值可以为squash,我们将pick改为edit是修改commit信息,改成squash就是合并commit了。

git log

image

我们把commit:h、g、e合并为一个: 合并的f。

我们执行``后,进入编辑模式,将pick修改为s(squash的缩写)

合并三个,将其中两个改为squash即可

image

保存退出编辑模式,会进入下面的界面,

image

默认的合并之后的commit信息就是红框中的内容,可以对它进行编辑,改成我们想要的commit,这里改为:“合并e g h的commit”,再去 git log 查看,e、g、h合并为了一个commit

image

某一段commit粘贴到另一个分支上 (cherry-pick)

git rebase --onto [branchName] [startpoint] [endpoint]
// startpoint 第一个 commit id, endpoint 最后一个 commit id,branchName 就是目标分支了。

如有冲突解决冲突, 执行git add来标记已解决(无需提交commit),之后执行 git rebase --continue 继续,

此时HEAD应用了要复制的那一段 commit,git log查看,记录下最近的一次 commit_id,切换到分支 [branchName],执行:
git reset --hard [commit_id],如果想要回到rebase之前的状态,可以执行:git rebase --abort

note: 如果要复制某一次的commit至某分支,可以使用git cherry-pick命令,简单快捷。(cherry-pick的解决冲突等)

变基

image

当远程分支有更新时

两个人A、B在一个分支开发时,A提交了代码,远端有了更新:

image

B需要拉一下代码

若执行 git pull,会产生一个merge branch的commit信息,log查看提交历史分叉了

image

想要提交历史是一条线?执行 git pull --rebase,会发现原本分叉的提交现在变成一条直线了

image

rebase操作的特点:把分叉的提交历史“整理”成一条直线,看上去更直观,获得更清晰的项目历史记录,消除了不必要的git merge产生的merge commit。缺点是rebase会丢失merge commit提供的上下文 - 你无法看到上游更改何时合并到功能中

git pull 相当于 git fetch + git merge
git pull --rebase 相当于 git fetch + git rebase

合并分支

git merge和git rebase都可以用来合并分支,都是将一个分支的更改并入另一个分支,只不过方式有些不同,merge是将commit合并后生成一个新的commit。
例:将br1分支合并到master分支上,用git merge操作如下:

切换到master分支
执行:git merge br1

用rebase操作:

切换到br1分支
执行: git rebase master
切换到master分支
执行:git merge br1

修整历史,将分支历史并入主线

最重要的是了解什么时候不使用它。git rebase的黄金法则是永远不要在公共分支使用它。

天天赶进度的业务项目中,平时做的也基本上是最佳实践了。

git merge forked_lib/new_branch --squash?

PS:git cherry-pick

git cherry-pick commit_id

有冲突:git add后直接使用git cherry-pick --continue

不想git cherry-pick自动进行提交: git cherry-pick -n,git status查看

要中断这次cherry-pick:使用git cherry-pick --quit,这种情况下当前分支中未冲突的内容状态将为modified

要取消这次cherry-pick:使用git cherry-pick --abort,这种情况下当前分支恢复到cherry-pick前的状态,没有改变

发布订阅模式

eventerCenter

简单的事件发布/订阅~

代码地址

引入

import EventCenter from './EventCenter'

API

on

  • 添加一个事件监听
  • handler优化了下,只能传函数,以免传对象造成不可控

eventCenter.on(type, handler)

once

  • 添加一个只能触发一次的事件监听

eventCenter.once(type, handler)

emit

  • 触发事件

eventCenter.emit(type, ...params)

off

  • 取消一个事件监听

eventCenter.off(type, handler)

clear

  • 取消所有监听

eventCenter.clear()

---

Git -- 合并commit

1.git rebase -i

(同 git rebase 中的)

使用 git rebase -i 命令

git rebase -i HEAD~2

执行后跳到一个新界面:

image

在之前修改commit信息方法中,执行git rebase -i后进入编辑界面前面的值可以为squash,我们将pick改为edit是修改commit信息,改成squash就是合并commit了。

git log

image

我们把commit:h、g、e合并为一个: 合并的f。

我们执行``后,进入编辑模式,将pick修改为s(squash的缩写)

合并三个,将其中两个改为squash即可

image

保存退出编辑模式,会进入下面的界面,

image

默认的合并之后的commit信息就是红框中的内容,可以对它进行编辑,改成我们想要的commit,这里改为:“合并e g h的commit”,再去 git log 查看,e、g、h合并为了一个commit

image

2. git reset

执行git reset --soft commit_id(commit_id为远程仓库最新的commit_id,一般也是切出此feature分支时的commit_id),之前在工作区的代码依然在工作区,之前提交过的会在暂存区,执行git add(如有需要),执行git commit -m "msg",提交一次commit信息。
原理就相当于,把切分支之后所有的commit,通过reset撤销掉,期间修改的代码会保留在暂存区和工作区,工作区有代码则git add,没有直接git commit

例:
合并commit:e、f、g 为一个新的commit:efg

image

执行 git reset --soft d7e5d51(d7e5d51为对应commit提交信息为d的commit_id),查看状态可知e、f、g的修改现在都在暂存区

image

此时git commit -m "efg",通过 git log 查看验证

image

3. git merge --squash

(gitlab上的squash?)
例:master合并br1分支代码,br1已经有多次commit了。

image

切换分支到master: git checkout master
git merge --squash br1, 此时git status查看可知,之前branch1多次commit的是修改都处在暂存区中
// --squash 会暂停commit提交,要不然一个merge会自动提交commit
git commit -m "squash_msg"
通过git log查看

image

over

LeetCode

1.两数之和:给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

var twoSum = function(nums, target) {
    let obj = {};
    nums.forEach((v, i) => {
        obj[v] = obj[v] || i;
    });
    for (let i = 0; i < nums.length - 1; i++) {
        const ti = obj[target - nums[i]];
        if (ti && ti !== i) {
            return [i, ti];
        }
    }
};

2.无重复字符的最长子串:给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

var lengthOfLongestSubstring = function(s) {
   let max = 0;
   const arr = s.split('');
   const arrLen = arr.length;
   arr.forEach((item, j) => {
       let nmax = 0;
       for (let i = j + max + 1; i <= arrLen; i++) {
           let narr = arr.slice(j, i);
           let len = Array.from(new Set(narr)).length;
           if (narr.length !== len || len === nmax) {
               break;
           } else if (nmax < len) {
               nmax = len;
           }
       }
       max = Math.max(nmax, max);
   });
   return max;
};

Git -- 储藏

有一种场景,当你我们正在一个分支开发某个功能时(还未完成),突然需要我们切换到其他分支上处理一些事情(比如修改bug),直接切换是不可以的,一种方法是我们可以将目前的改动提交,而我们此时并不想commit,我们就可以使用stash将代码暂时储藏起来。

=== stash 储藏

git stash : 将全部未保存的代码添加到储藏


基本命令:

保存当前代码:
  1. git stash: 快速储藏代码,默认储藏名称为"WIP on <branch_name> : <latest_commit_id> <latest_commit_message>"
  2. git stash save "message": 执行储藏时,添加message信息,注明储藏名称,方便查找

查看stash代码:
  1. git stash list: 查看stash列表
  2. git stash show: 查看第一个储藏做了哪些改动(并不是改动详情)
  3. git stash show stash@{num}: 查看其他储藏做了哪些改动
  4. git stash show -p: 查看第一个储藏的改动
  5. git stash show -p stash@{num}: 查看其他储藏做的改动
应用stash代码:
  1. git stash apply: 应用第一个储藏,但不会删除
  2. git stash apply stash@{num}: 应用列表中的储藏

  3. git stash pop: 应用第一个储藏并删除
  4. git stash pop stash@{num}: 应用stash list中的储藏并删除,例如应用并删除第二个:git stash pop stash@{1}
删除stash代码:
  1. git stash drop: 删除以一个储藏
  2. git stash drop stash@{num}: 删除stash list中的储藏,例如删除第二个:git stash drop stash@{1}
  3. git stash clear: 删除所有储藏的stash
以stash储藏创建一个分支:
  1. git stash branch branchName stash@{1}: 创建新分支branchName,并切换到此分支,分支的状态与stash储藏时的状态一致,此时新分支应用的stash代码进了暂存区。

如图,展示了几个常用的stash命令:

image


more

git stash信息存储到哪了?

项目路径下的.git文件中存储着版本管理的所有信息,在文件 .git/log/refs/stash 中可以看到全部的 stash 记录信息

在有新增文件(不是新增代码)时,git stash并不会储藏新增的文件?

也就是说 没有在git 版本控制中的文件,是不能被git stash 存起来的 ,此时需要先执行下git add 将新增文件加到git版本控制中,然后再git stash就可以了

只保存某些文件,其他的不变我继续工作?

总共分3步:(1)add 那些你不想备份的文件:git add file1.js, file2.js;(2)用git stash --keep-index:只会保存那些没有被add的文件;(3)git reset HEAD,将暂存区的修改重置到工作区继续coding

git stash save -u ?

(待确认。。。)

git stash push

git stash push 可以用来代替 git stash savegit stash push 等同于 git stash savegit stash push -m "message" 等同于 git stash save "message";另外 git stash push dir/file.js 可以指定储藏路径(即上面的问题-只保存某些文件,git stash save 无此功能)


参考链接

  1. git stash用法详解
  2. git-stash简单用法
  3. 使用 git stash
  4. git stash save与git stash push区别

Git WorkFlow & Git Commit

Git之WorkFlow(工作流)

I: WorkFlow

WorkFlow 即工作流程。项目中涉及到很多问题,例如版本迭代,版本发布,bug 修复等,为了更好的管理代码,需要制定一个工作流程,这就是工作流

  • 分支管理策略
  • 一个规则

一般常见三种工作流

  • Git Flow
  • GitHub Flow
  • GitLab Flow

共同点

需求是开发的起点,先有需求再有功能分支(feature branch)或者补丁分支(hotfix branch)。完成开发后,该分支就合并到主分支,然后被删除。

一、Git Flow

分支结构

一共有5种分支:
2种长期分支

  • 主分支(master)
  • 开发分支(develop)

master用于存放对外发布的版本,是稳定的分布版;develop用于日常开发,存放最新的开发版
当 develop 上的代码达到一个稳定的状态,可以发布版本的时候,develop上这些修改会被合并到 master 分支上,然后标记上对应的版本标签

3种暂时分支

  • 功能分支(feature branch)
  • 补丁分支(hotfix branch)
  • 预发分支(release branch)

特点
1.一旦完成开发,暂时分支就会被合并进develop或master
2.合并后被删除无私奉献 ):

Git Flow 示意图

image

图中画了 Git Flow 的五种分支,master,develop,feature ,release , hotfix,其中 master 和 develop 字体被加粗代表主要分支。master 分支每合并一个分支,无论是 hotfix 还是 release ,都会打一个版本标签。通过箭头可以清楚的看到分支的开始和结束走向,例如 feature 分支从 develop 开始,最终合并回 develop ,hoxfix 从 master 检出创建,最后合并回 develop 和 master,master 也打上了标签。

每一个新功能的开发都应该各自使用独立的分支。在创建新的功能开发分支时,父分支应该选择develop(而不是master)。当功能开发完成时,改动的代码应该被合并(merge)到develop分支。功能开发永远不应该直接牵扯到master。

一旦develop分支积聚了足够多的新功能(或者预定的发布日期临近了),你可以基于develop分支建立一个用于产品发布的分支。这个分支的创建意味着一个发布周期的开始,也意味着本次发布不会再增加新的功能——在这个分支上只能修复bug,做一些文档工作或者跟发布相关的任务。在一切准备就绪的时候,这个分支会被合并入master,并且用版本号打上标签。另外,发布分支上的改动还应该合并入develop分支——在发布周期内,develop分支仍然在被使用(一些开发者会把其他功能集成到develop分支)。

在实际工作时,碰到的情形:比如你正在开发某一项功能,这时来了一个产品经理告诉你:线上的产品出现问题了,有个功能需要调整。此时你就可以先把目前的工作提交至一个feature分支,然后切换到线上版本所在的master分支,然后基于该分支生成一个hotfix的新分支,等测试通过后,你可以合并至线上的master分支上,修复产品经理提出的问题。最后,你再切换回先前的feature分支,合一下master的修改,继续原来的开发,完成工作,测试通过后,merge回develop分支,同时关闭掉该feature分支。

这个模式是基于"版本发布"的,目标是一段时间以后产出一个新版本。(但是,很多网站项目是"持续发布",代码一有变动,就部署一次)

当 Git Flow 暴露的问题:
Hotfix 和 Release 分支在需要版本快速迭代的项目中,几乎用不到,因为刚开发完就直接合并到 master 发版,出现问题 develop 就直接修复发布下个版本了;
Hotfix 和 Release 分支,一个从 master 创建,一个从 develop 创建,使用完毕,需要合并回 develop 和 master。而且在实际项目管理中,很多开发者会忘记合并回 develop 或者 master。

二、GitHub Flow

Github flow 是Git flow的简化版,专门配合"持续发布"。它是 Github.com 使用的工作流程。只有一个主分支 master,团队成员们的分支代码通过 pull Request 来合并到 master 上。

  1. 只有一个长期分支 master ,而且 master 分支上的代码,永远是可发布状态,一般 master 会设置 protected 分支保护,只有有权限的人才能推送代码到 master 分支。
  2. 如果有新功能开发,可以从 master 分支上检出新分支。
  3. 在本地分支提交代码,并且保证按时向远程仓库推送。
  4. 当你需要反馈或者帮助,或者你想合并分支时,可以发起一个 pull request。
  5. 当 review 或者讨论通过后,代码会合并到目标分支。
  6. 一旦合并到 master 分支,应该立即发布。

GitHub Flow 示意图

image

Github flow 的最大优点就是简单,是适于"持续发布"的流程。
最大的特色就是 Pull Request 的提出。

GitHub Flow 的出现,非常大程度上简化了 Git Flow ,因为只有一个长期分支 master,然而在一些实际问题面前,仅仅使用 master 分支显然有点力不从心,例如:
版本的延迟发布(例如APP应用审核到通过中间,可能也要在 master 上推送代码);
不同环境的部署 (例如:测试环境,预发环境,正式环境);
不同版本发布与修复 (是的,只有一个 master 分支真的不够用,另外新建一个production分支跟踪线上版本)。

GitLab Flow

GitLab 既支持 Git Flow 的分支策略,也有 GitHub Flow 的 Pull Request( Merge Request ) 和 issue tracking。

image

Gitlab flow 的最大原则叫做"上游优先"(upsteam first),即只存在一个主分支master,它是所有其他分支的"上游"。只有上游分支采纳的代码变化,才能应用到其他分支。

开发分支是预发分支的"上游",预发分支又是生产分支的"上游"。代码的变化,必须由"上游"向"下游"发展。比如,生产环境出现了bug,这时就要新建一个功能分支,先把它合并到master,确认没有问题,再cherry-pick到pre-production,这一步也没有问题,才进入production。

版本发布
image

对于"版本发布"的项目,建议的做法是每一个稳定版本,都要从master分支拉出一个分支,比如2-3-stable、2-4-stable等等。如果发现问题,就从对应版本分支创建修复分支。

II: Git Commit - git提交规范

每次提交,Commit message 都包括三个部分:Header,Body 和 Footer。
其中,Header 是必需的,Body 和 Footer 可以省略。

(最少情况就是Header:type:subject)

type: (scope): subject
// 空一行
body
// 空一行
footer

Header

Header部分只有一行,包括两个字段:type(必需)和subject(必需)。

type用于说明 commit 的类别,只允许使用下面8个标识;scope用于说明 commit 影响的范围,比如数据层、控制层、视图层等等;subject是 commit 目的的简短描述。

type--
feat:新功能(feature)
fix:修补bug
docs:文档(documentation)
style: 格式(不影响代码运行的变动)
refactor(ref):重构(即不是新增功能,也不是修改bug的代码变动)
test:增加测试
chore:构建过程或辅助工具的变动
tag: 针对与每次版本的提交
revert: 撤销,版本回退
perf: 性能优化

Body

Body 部分是对本次 commit 的详细描述

Footer

不兼容变动/关闭 bug、需求

flex布局-子容器六大属性

flex子容器属性

在flex布局中,父元素设置了flex之后,子容器相关的属性一共有6个,我们最经常使用的就是flex,例如flex:1。

  1. order: 子容器的排列顺序
  2. flex-grow: 子容器剩余空间的拉伸比例
  3. flex-shrink: 子容器超出空间的压缩比例
  4. flex-basis: 子容器在不伸缩情况下的原始尺寸
  5. flex: flex 属性是 flex-grow,flex-shrink 和 flex-basis 的简写,默认值为0 1 auto,flex:1 就相当于 flex: 1 1 0%,后两个属性是可选的
  6. align-self: 定义单个项目自身在交叉轴上的排列方式,可以覆盖掉容器上的align-items属性

1:order

属性定义项目的排列顺序。数值越小,排列越靠前,默认为 0

    .ele {
        order: <number>; /* default 0 */
    }
    /*数值越小,越排前,默认为0*/

如图所示:蓝色div设置order:-1后排在了最前面,红色div设置order:1后排在了左后面。

例:order

2:flex-grow

子元素宽度的和小于、大于父元素的宽度,分别对应了flex-grow和flex-shrink属性生效的情况,也就是说当子元素的宽度的和小于父元素的宽度值时flex-grow生效,反之flex-shrink生效

属性定义子容器的伸缩比例。将剩余空间按照该比例给子容器。

    .ele {
        flex-grow: <number>; /* default 0 */
    }
    /默认0(即如果有剩余空间也不放大,值为1则放大,2是1的双倍大小,以此类推)*/

如图所示:父元素宽度为200px,红色、蓝色div宽度设为50px。

上面一组为原始状态,flex-grow默认值为0,都不分配剩余空间,红色、蓝色div宽度都为50px

中间一组中的蓝色div设置flex-grow: 1后,其分配了剩余空间,红色div宽度还是50px,蓝色div分得了剩余的100px宽度,宽度变为了150px

下面一组中的红色div设置flex-grow: 1,蓝色div设置flex-grow: 2,两个div按比例1:2分配了剩余空间,红色div分得的宽度为1001/(1+2) = 33.33px,蓝色div分得1002/(1+2) = 66.67px,原始宽度加上分配剩余空间得到的宽度后,红色、蓝色div的宽度变为了83.33px、116.67px

例:flex-grow

3:flex-shrink

属性定义了子容器弹性收缩的比例。把各自的空间按比例分配出去。flex-shrink的默认值为1,flex-shrink的值为0时,不缩放。

    .ele {
        flex-shrink: <number>; /* default 1 */
    }
    /*默认1(如果空间不足则会缩小,值为0不缩小)*/

如图所示:父元素宽度为200px,红色、蓝色div宽度设为150px(子元素宽度和为300px,大于父元素宽度200px)

上面一组为原始状态,lex-shrink默认为1,红色和蓝色div按1:1的比例让出自己的空度(50px)后,两个div的宽度都为100px

第二组中的蓝色div设置flex-shrink: 0后,其不出让自己的空间,红色div让出空间100px,宽度变为50px

第三组中的红色div设置flex-shrink:1,蓝色div设置flex-shrink: 2,红色和蓝色div按1:2的比例让出自己的宽度,红色div让出100/3=33.33px,蓝色div让出100*2/3=66.67px,原始宽度减去让出宽度后红色、蓝色div的宽度变为了66.67px、33.33px

值得注意的是第四组的情况,红色div没有设置宽度,当其没有被撑满时,蓝色块(flex-shrink为默认)宽度正常,当红色块被撑满大于150px时:如第五组,蓝色块会被压缩,这是由于其flex-shrink默认为1的缘故,所以这种情况未避免蓝色块被压缩,我们应该为其设置flex-shrink为0(小程序如果是引入的组件,不会被压缩... 😨)

例:flex-shrink

4:flex-basis

属性定义了项目占据的主轴空间,规定的是子元素的基准值。浏览器根据这个属性,计算主轴多余空间或不足空间的大小。它的默认值为auto,即项目的本来大小。取值可以为绝对单位或是百分比(可用于栅格效果)。

    .ele {
        flex-basis: <length> | auto; /* default auto */
    }
    /*默认为auto,可以设置px值,也可以设置百分比大小*/

项目的原始长度由flex-basis和width属性控制。flex-basis的优先级高于width属性,如果只设置了width属性,flex-basis为auto,则项目的原始长度等于width;而如果同时设置了width和flex-basis,则项目的原始长度等于flex-basis。因此上文中用到width的地方可以使用flex-basis来代替。flex-basis/width影响了元素是flex-grow还是flex-shrink的表现。

例:flex-basis

5:align-self

属性指定元素自身在副轴上的对齐方式,可以用来覆盖父元素align-items属性的值对自身设置的效果。

    .ele {
        align-self: auto | flex-start | flex-end | center | baseline | stretch
    }
    /*自动(默认) | 顶部对齐 | 底部对齐 | 居中对齐 | 上下对齐并铺满 | 文本基线对齐*/

如图所示:父元素设置了align-items:stretch,红色div设置了align-self:flex-end,则红色div的表现形式和其他的有所不同了。

例:align-self

6:flex

复合属性,是flex-grow 、 flex-shrink 和 flex-basis 的简写属性,用来指定Flex子项如何分配空间。平时常用到的flex: 1,其实是flex: 1 1 0%的简写属性,即伸缩比例都是1。

    .ele {
        flex: <number> <number> <number> | auto; /* default 0 1 auto */
    }
    /*默认值为0 1 auto,*/

flex不同的取值实际的复合属性如下:

    none:0 0 auto;
    auto:1 1 auto;
    2:2 1 0%; (2为flex-grow的值,其他为默认);
    100px: 1 1 100px(100px为flex-basis 值,其他为默认);
    1 2: 1 2 0% (1、2分别为flex-grow和flex-shrink 的值,flex-basis取默认值);
    1 100px: 1 1 100px (1、100px分别为flex-grow 和flex-basis的值,flex-shrink取默认值1)
    0 auto 或 initial:0 1 auto 即初始值;

参数默认情况为:

    flex-grow:默认值为0,若省略则被默认为1;
    flex-shrink:默认值为1,省略时默认为1;
    flex-basis:默认值为auto,省略时默认为0%;

一个例子: flex和width同时赋值时的影响(主要是flex-basis)

主要代码如下:

    .flex {
        display: flex;
        width: 600px;
    }
    .flex > div {
        height: 100px;
    }
    .red {
        width: 200px;
        flex: 2 1 0%;
    }
    .blue {
        width: 100px;
        flex: 2 1 auto;
    }
    .yellow {
        flex: 1 1 200px;
    }

如图所示:三个div的宽度实际分别为: 120px、220px、260px。 flex-basis值为0%时会覆盖width的值200px,所以红色div基准宽度为0;蓝色div的flex-basis值为auto时,会以width的值100px为基准宽度;黄色div的flex-200px,其基准宽度即为200px。再按照flex-grow的比例进行剩余空间(600-100-200=300px)的分配。

例:flex-basis & width

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.