write something,rather i‘m not sure do whtat
1.一个简单的发布订阅模式
2.Git Workflow
3.flex布局-子容器属性
4.Git -- 储藏
5.Git -- 撤销
6.Git -- 修改commit信息
7.Git -- rebase
8.Git -- 合并commit
9.React Hooks
10.性能优化笔记
write what...
write something,rather i‘m not sure do whtat
1.一个简单的发布订阅模式
2.Git Workflow
3.flex布局-子容器属性
4.Git -- 储藏
5.Git -- 撤销
6.Git -- 修改commit信息
7.Git -- rebase
8.Git -- 合并commit
9.React Hooks
10.性能优化笔记
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 )
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.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
可以把 useEffect
Hook 看做 componentDidMount
,componentDidUpdate
和 componentWillUnmount
这三个生命周期函数的组合。之前在生命周期里经常处理的比如 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 已经可以实现大部分的业务逻辑, 就可以愉快的编写组件了...
(get context anywhere)
在用类声明组件时,父子组件的传值是通过组件属性和 props
进行的。
react中,context 可以帮助我们跨越组件层级直接传递变量,实现共享; 现在react的hooks也提供了 useContext
这个方便的Hook。 useContext
有一个参数: context 对象本身。
const value = useContext(MyContext);
另外,和 useReducer
配合使用,可以实现类似 Redux
的作用(next)
demo
以上3个被称为是基础hook,其他的称为额外的hook
const [state, dispatch] = useReducer(reducer, initialState, init)
useState
的替代方案。它接收一个形如 (state, action) => newState
的 reducer,并返回当前的 state 以及与其配套的 dispatch
方法。
Demo
结合useContext
Demo
例子,可以去项目中找...
1、 CDN
CDN(Content Delivery Network,即内容分发网络)指的是一组分布在各个地区的服务器
的核心点有两个,一个是缓存,一个是回源。“缓存”就是说我们把资源 copy 一份到 CDN 服务器上这个过程,“回源”就是说 CDN 发现自己没有这个资源(一般是缓存的数据过期了),转头向根服务器(或者它的上层服务器)去要这个资源的过程。
CDN 往往被用来存放静态资源,CDN 服务器则像一个仓库,它只充当资源的“栖息地”和“搬运工”。所谓“静态资源”,就是像 JS、CSS、图片等不需要业务服务器进行计算即得的资源。而“动态资源”,顾名思义是需要后端实时动态生成的资源,较为常见的就是 JSP、ASP 或者依赖服务端渲染得到的 HTML 页面。“非纯静态资源”是指需要服务器在页面之外作额外计算的 HTML 页面。
CDN 的域名选取。同一个域名下的请求会不分青红皂白地携带 Cookie,而静态资源往往并不需要 Cookie 携带什么认证信息。把静态资源和主页面置于不同的域名下,完美地避免了不必要的 Cookie 的出现!
场景:刚提交了一个代码,发现有几个字写错了,怎么办,当场再写一个修复这几个错别字的 commit?或者是commit信息写错了?
在提交时,如果加上 --amend 参数,git 不会在当前 commit 上增加 commit,而是会把当前 commit 里的内容和暂存区(stageing area)里的内容合并起来后创建一个新的 commit,用这个新的 commit 把当前 commit 替换掉。所以 commit --amend 做的事就是它的字面意思:**对最新一条 commit **进行修正。
git add . (若有新修改)
git commit --amend
然后我们会看到提交信息编辑界面:
之后,insert 进入编辑状态,编辑 commit 信息后退出即可。通过 git log 可以验证查看。
需要注意的是:commit --amend 并不是直接修改原 commit 的内容,而是生成了一条新的 commit。
rebase -i 是 rebase --interactive 的缩写形式,意为"交互式 rebase"--让用户编辑完成合并操作 (其实 git rebase -i 的完整命令为:git rebase -i [startpoint] [endpoint],如果不指定[endpoint],则该区间的终点默认是当前分支HEAD所指向的commit。<有关rebase的详细操作--待写>,rebase的作用简要概括为:可以对某一段线性提交历史进行编辑、删除、复制、粘贴) 。
如下图,当我提交commit信息“g”之后发现上一次提交信息应该为“f”,但我写成了“ff”:
这时候我们就可以使用 git rebase -i 命令了。
git rebase -i HEAD~2
执行后跳到一个新界面:
由远及近列出了两个commit,前面pick代表的默认使用该提交commit,按i进入编辑模式,修改该字段值,经常使用的如下:
这里把第一个pick改为edit:
然后退出(按esc键退出编辑模式,然后输入:wq,保存当前编辑)
下面的步骤同上面的commit --amend,执行:
git commit --amend
修改完成之后,执行 rebase --continue (git也已经给了提示)来继续 rebase 过程,把后面的 commit 直接应用上去:
git rebase --continue
最后我们可以 git log 查看验证一下
一般我们在最新的 commit
写错时,可以用 reset --hard
来把 commit
撤销
git reset --hard HEAD^
reset
这个指令虽然可以用来撤销 commit
,但它的实质行为并不是撤销,而是移动 HEAD。而 reset --hard HEAD^
之所以起到了撤销 commit
的效果,是因为它把 HEAD
移动到了当前 commit
上,从而起到了「撤销」的效果。所以reset
的本质实际上是移动HEAD
。那么 reset
后面跟着的那个 --hard
是什么意思呢?除了--hard
还有什么其他参数呢?
举个例子,当前的操作情况如图:
在此举例要 reset
到的目标 commit
为e。当前缓存区和工作目录都有代码,也有已经commit
的提交f(为了便于查看效果,当前把 index0.js
的修改commit
,index1.js
的修改放进缓存区,index2.js
的修改还放在工作区)。我们给 reset
不同的参数,来看看 reset
之后的代码如何:
index0.js
对应已经commit
过的(有commit
信息:f
)
index1.js
对应已经add .
过的 (已放进缓存区)
index2.js
对应修改的(处于工作区)
reset --hard
:重置工作目录就是说,未提交的修改会被全部擦掉。例如你在上次 commit
之后又对文件做了一些改动,然后,你执行了 reset
并附上了 --hard
参数(git reset --hard HEAD^
),然后你工作目录里的新改动也一起全都消失了,不管它们是否被放进暂存区。如下图:
reset --soft
:保留工作目录reset --soft
会在重置 HEAD 时,工作区中的修改还是放进工作区、已经 commit
的和已经在暂存区的都放进暂存区。也可以说是,工作区的还在工作区,commit
的只是取消了commit
但还是已经add
了。如下图:
reset --mixed
:保留工作目录,并清空暂存区reset
如果不加参数,那么默认使用 --mixed
参数。它的行为是:工作区的修改、暂存区的内容以及已经commit
的修改,都会被放进工作区。简而言之,就是「把所有差异都混合(mixed
)放在工作区中」。如下图:
reset --merge
:保留工作目录,清空暂存区或commit过的和--hard
类似,只不过如果在执行reset
命令之前你有改动一些文件并且未提交,merge
会保留你的这些修改,hard
则不会。总之它的行为就是:工作区的修改还在工作区、暂存区的内容以及已经commit
的修改,都会被清除。如下图:
reset --keep
:保留工作区并丢弃一些之前的提交和--hard
类似,执行reset
之前改动文件如果是分支修改了的,会提示你修改了相同的文件,不能合并。如果不是分支修改的文件,会移除缓存区。git status
还是可以看到保持了这些修改。总之它的行为就是:工作区的修改还在工作区、暂存区的内容也被放在了工作区,已经commit
的修改则被清除了。如下图:
命令 | commit | 暂存区 | 工作区 |
---|---|---|---|
reset --hard |
清除 | 清除 | 清除 |
reset --soft |
暂存区 | 暂存区 | 工作区 |
reset --mixed |
工作区 | 工作区 | 工作区 |
reset --merge |
清除 | 清除 | 工作区 |
reset --keep |
清除 | 工作区 | 工作区 |
checkout
如第一张图,当我们 git status
时,git
会提示使用git checkout
丢弃修改:
git checkout -- filePath
丢弃工作区某个文件的改动
git checkout -- .
丢弃工作区所有文件的改动
如图所示,使用 checkout
会撤销工作区的改动:
more
git reset --hard origin/branchName
?将本地分支代码与远程分支同步,和
git reset --hard commit_id
效果一样(commit_id
为远程分支最新的一次commit_id
),是一个快捷操作。hard
对代码的影响同上,一样hard
也可以改为soft
、mixed(省略)
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 log
、git reflog
:git reset [commit_id]
git reset [commit_id] [file]
reset
之后又反悔了?
git reflog
得到reset
的commit_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
一下
git
操作:。。。
简单暴力点,不复杂的话还是辛苦手动重新改吧
reset --hard
之后我们想应用某一个被 reset
掉的commit
?此时我们可以使用
git cherry-pick
命令,git cherry-pick commit_id
,会将commit_id
应用到分支上
reset
有风险,使用需谨慎,记得commit
参考链接:
Git 实战手册
git reset回滚代码
远程仓库版本回退方法
Git -- rebase
同之前章节
rebase -i
rebase -i 是 rebase --interactive 的缩写形式,意为"交互式 rebase"--让用户编辑完成合并操作 (其实 git rebase -i 的完整命令为:git rebase -i [startpoint] [endpoint],如果不指定[endpoint],则该区间的终点默认是当前分支HEAD所指向的commit。<有关rebase的详细操作--待写>,rebase的作用简要概括为:可以对某一段线性提交历史进行编辑、删除、复制、粘贴) 。
如下图,当我提交commit信息“g”之后发现上一次提交信息应该为“f”,但我写成了“ff”:
这时候我们就可以使用 git rebase -i
命令了。
git rebase -i HEAD~2
执行后跳到一个新界面:
由远及近列出了两个commit,前面pick代表的默认使用该提交commit,按i进入编辑模式,修改该字段值,经常使用的如下:
这里把第一个pick改为edit:
然后退出(按esc键退出编辑模式,然后输入:wq,保存当前编辑)
下面的步骤同上面的commit --amend,执行:
git commit --amend
修改完成之后,执行 rebase --continue (git也已经给了提示)来继续 rebase 过程,把后面的 commit 直接应用上去:
git rebase --continue
最后我们可以 git log 查看验证一下
在上面修改commit信息方法中,执行git rebase -i后进入编辑界面前面的值可以为squash
,我们将pick改为edit是修改commit信息,改成squash就是合并commit了。
git log
我们把commit:h、g、e合并为一个: 合并的f。
我们执行``后,进入编辑模式,将pick修改为s(squash的缩写)
合并三个,将其中两个改为squash即可
保存退出编辑模式,会进入下面的界面,
默认的合并之后的commit信息就是红框中的内容,可以对它进行编辑,改成我们想要的commit,这里改为:“合并e g h的commit”,再去 git log
查看,e、g、h合并为了一个commit
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的解决冲突等)
变基
两个人A、B在一个分支开发时,A提交了代码,远端有了更新:
B需要拉一下代码
若执行 git pull,会产生一个merge branch的commit信息,log查看提交历史分叉了
想要提交历史是一条线?执行 git pull --rebase
,会发现原本分叉的提交现在变成一条直线了
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前的状态,没有改变
简单的事件发布/订阅~
引入
import EventCenter from './EventCenter'
on
eventCenter.on(type, handler)
once
eventCenter.once(type, handler)
emit
eventCenter.emit(type, ...params)
off
eventCenter.off(type, handler)
clear
eventCenter.clear()
Git -- 合并commit
(同 git rebase 中的)
使用 git rebase -i
命令
git rebase -i HEAD~2
执行后跳到一个新界面:
在之前修改commit信息方法中,执行git rebase -i后进入编辑界面前面的值可以为squash
,我们将pick改为edit是修改commit信息,改成squash就是合并commit了。
git log
我们把commit:h、g、e合并为一个: 合并的f。
我们执行``后,进入编辑模式,将pick修改为s(squash的缩写)
合并三个,将其中两个改为squash即可
保存退出编辑模式,会进入下面的界面,
默认的合并之后的commit信息就是红框中的内容,可以对它进行编辑,改成我们想要的commit,这里改为:“合并e g h的commit”,再去 git log
查看,e、g、h合并为了一个commit
执行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
执行 git reset --soft d7e5d51
(d7e5d51为对应commit提交信息为d的commit_id),查看状态可知e、f、g的修改现在都在暂存区
此时git commit -m "efg"
,通过 git log 查看验证
(gitlab上的squash?)
例:master合并br1分支代码,br1已经有多次commit了。
切换分支到master:
git checkout master
git merge --squash br1
, 此时git status查看可知,之前branch1多次commit的是修改都处在暂存区中
// --squash 会暂停commit提交,要不然一个merge会自动提交commit
git commit -m "squash_msg"
通过git log查看
over
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;
};
有一种场景,当你我们正在一个分支开发某个功能时(还未完成),突然需要我们切换到其他分支上处理一些事情(比如修改bug),直接切换是不可以的,一种方法是我们可以将目前的改动提交,而我们此时并不想commit,我们就可以使用stash将代码暂时储藏起来。
git stash
: 将全部未保存的代码添加到储藏
git stash
: 快速储藏代码,默认储藏名称为"WIP on <branch_name> : <latest_commit_id> <latest_commit_message>"git stash save "message"
: 执行储藏时,添加message信息,注明储藏名称,方便查找git stash list
: 查看stash列表git stash show
: 查看第一个储藏做了哪些改动(并不是改动详情)git stash show stash@{num}
: 查看其他储藏做了哪些改动git stash show -p
: 查看第一个储藏的改动git stash show -p stash@{num}
: 查看其他储藏做的改动git stash apply
: 应用第一个储藏,但不会删除git stash apply stash@{num}
: 应用列表中的储藏git stash pop
: 应用第一个储藏并删除git stash pop stash@{num}
: 应用stash list中的储藏并删除,例如应用并删除第二个:git stash pop stash@{1}
git stash drop
: 删除以一个储藏git stash drop stash@{num}
: 删除stash list中的储藏,例如删除第二个:git stash drop stash@{1}git stash clear
: 删除所有储藏的stashgit stash branch branchName stash@{1}
: 创建新分支branchName,并切换到此分支,分支的状态与stash储藏时的状态一致,此时新分支应用的stash代码进了暂存区。如图,展示了几个常用的stash命令:
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 save
,git stash push
等同于git stash save
,git stash push -m "message"
等同于git stash save "message"
;另外git stash push dir/file.js
可以指定储藏路径(即上面的问题-只保存某些文件,git stash save
无此功能)
Git之WorkFlow(工作流)
WorkFlow 即工作流程。项目中涉及到很多问题,例如版本迭代,版本发布,bug 修复等,为了更好的管理代码,需要制定一个工作流程,这就是工作流
需求是开发的起点,先有需求再有功能分支(feature branch)或者补丁分支(hotfix branch)。完成开发后,该分支就合并到主分支,然后被删除。
一共有5种分支:
2种长期分支
master用于存放对外发布的版本,是稳定的分布版;develop用于日常开发,存放最新的开发版
当 develop 上的代码达到一个稳定的状态,可以发布版本的时候,develop上这些修改会被合并到 master 分支上,然后标记上对应的版本标签
3种暂时分支
特点
1.一旦完成开发,暂时分支就会被合并进develop或master
2.合并后被删除无私奉献 ):
图中画了 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 是Git flow的简化版,专门配合"持续发布"。它是 Github.com 使用的工作流程。只有一个主分支 master,团队成员们的分支代码通过 pull Request 来合并到 master 上。
Github flow 的最大优点就是简单,是适于"持续发布"的流程。
最大的特色就是 Pull Request 的提出。
GitHub Flow 的出现,非常大程度上简化了 Git Flow ,因为只有一个长期分支 master,然而在一些实际问题面前,仅仅使用 master 分支显然有点力不从心,例如:
版本的延迟发布(例如APP应用审核到通过中间,可能也要在 master 上推送代码);
不同环境的部署 (例如:测试环境,预发环境,正式环境);
不同版本发布与修复 (是的,只有一个 master 分支真的不够用,另外新建一个production分支跟踪线上版本)。
GitLab 既支持 Git Flow 的分支策略,也有 GitHub Flow 的 Pull Request( Merge Request ) 和 issue tracking。
Gitlab flow 的最大原则叫做"上游优先"(upsteam first),即只存在一个主分支master,它是所有其他分支的"上游"。只有上游分支采纳的代码变化,才能应用到其他分支。
开发分支是预发分支的"上游",预发分支又是生产分支的"上游"。代码的变化,必须由"上游"向"下游"发展。比如,生产环境出现了bug,这时就要新建一个功能分支,先把它合并到master,确认没有问题,再cherry-pick到pre-production,这一步也没有问题,才进入production。
对于"版本发布"的项目,建议的做法是每一个稳定版本,都要从master分支拉出一个分支,比如2-3-stable、2-4-stable等等。如果发现问题,就从对应版本分支创建修复分支。
每次提交,Commit message 都包括三个部分:Header,Body 和 Footer。
其中,Header 是必需的,Body 和 Footer 可以省略。
(最少情况就是Header:type:subject)
type: (scope): subject
// 空一行
body
// 空一行
footer
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 部分是对本次 commit 的详细描述
不兼容变动/关闭 bug、需求
在flex布局中,父元素设置了flex之后,子容器相关的属性一共有6个,我们最经常使用的就是flex,例如flex:1。
- order: 子容器的排列顺序
- flex-grow: 子容器剩余空间的拉伸比例
- flex-shrink: 子容器超出空间的压缩比例
- flex-basis: 子容器在不伸缩情况下的原始尺寸
- flex: flex 属性是 flex-grow,flex-shrink 和 flex-basis 的简写,默认值为0 1 auto,flex:1 就相当于 flex: 1 1 0%,后两个属性是可选的
- align-self: 定义单个项目自身在交叉轴上的排列方式,可以覆盖掉容器上的align-items属性
属性定义项目的排列顺序。数值越小,排列越靠前,默认为 0
.ele {
order: <number>; /* default 0 */
}
/*数值越小,越排前,默认为0*/
如图所示:蓝色div设置order:-1后排在了最前面,红色div设置order:1后排在了左后面。
例:order
子元素宽度的和小于、大于父元素的宽度,分别对应了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-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(小程序如果是引入的组件,不会被压缩... 😨)
属性定义了项目占据的主轴空间,规定的是子元素的基准值。浏览器根据这个属性,计算主轴多余空间或不足空间的大小。它的默认值为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的表现。
属性指定元素自身在副轴上的对齐方式,可以用来覆盖父元素align-items属性的值对自身设置的效果。
.ele {
align-self: auto | flex-start | flex-end | center | baseline | stretch
}
/*自动(默认) | 顶部对齐 | 底部对齐 | 居中对齐 | 上下对齐并铺满 | 文本基线对齐*/
如图所示:父元素设置了align-items:stretch,红色div设置了align-self:flex-end,则红色div的表现形式和其他的有所不同了。
复合属性,是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 {
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)的分配。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.