github多人协作流程

Git多人开发流程

clone远程仓库到本地

1
git clone https://github.com/johncheunghust/test-git-teamwork.git

在本地创建自己的分支并对应到远程分支

1
2
3
git checkout -b dev1 //创建并转到本地分支
git push origin dev1 //将本地分支推送到远程分支
git branch --set-upstream-to=origin/dev1 //将本地分支关联到远程分支dev1

提交代码

1
git push

合并:提交Pull request或merge request

一些坑

文件夹的大小写的问题解决方案

git默认对大小写不敏感,解决方案为

1
2
git config --global core.ignorecase false
git config --local core.ignorecase false

出现分支冲突时的处理

1
2
3
git fetch origin //拉取最新的远程主分支
git rebase master //将当前分支rebase到最新的主分支上,并解决分支冲突
git push //将解决冲突后的分支push到仓库,提PR不再有冲突

关于Git一些解决冲突方式的方法

图解4种git合并分支方法 - 颜海镜的文章 - 知乎

React生命周期

React16.8+生命周期分为三个阶段,分别为挂载、更新和卸载

挂载

  • constructor: 构造函数,最先被执行,通常在构造函数中初始化state对象或者给自定义方法绑定this
  • static getDerivedStateFromProps(nextProps, prevState)。静态方法,当接收到新的属性想去修改state时,可以使用。
  • render()
  • componentDidMount():组件装载之后调用,此时可以获取到DOM节点并操作,比如对canvas,svg的操作,服务器请求,订阅都可以写在这里面,在componentWillUnmount中取消订阅。

更新阶段

  • static getDerivedStateFromProps() 此方法在更新整个挂在阶段都可能会调用。
  • shouldComponentUpdate(nextProps, nextState) 有两个参数,表示新的属性和变化之后的state,返回一个Boolean, true表示会触发重新渲染,false表示不会触发重新渲染,默认返回true,常利用此生命周期来优化React程序性能。
  • render()
  • getSnapshotBeforeUpdate(prevProps, prevState) 函数的返回值,作为第三个参数传给componentDidUpdate, 如果不想要返回值,可以返回null,此生命周期必须与componentDidUpdate搭配使用
  • componentDidUpdate(prevProps, prevState, snapshot) 该方法该方法在getSnapshotBeforeUpdate方法之后被调用,有三个参数prevProps,prevState,snapshot,表示之前的props,之前的state,和snapshot。第三个参数是getSnapshotBeforeUpdate返回的,如果触发某些回调函数时需要用到 DOM 元素的状态,则将对比或计算的过程迁移至 getSnapshotBeforeUpdate,然后在 componentDidUpdate 中统一触发回调或更新状态。

卸载

  • componentWillUnmount() 当我们的组件被卸载或者销毁了就会调用,我们可以在这个函数里去清除一些定时器,取消网络请求,清理无效的DOM元素等垃圾清理工作

lifecycle

Diffenrences between require/exports & import/export

遵循的模块化规范不同

require/exports是commonjs规范;import/export是ES6规范

形式不同

require/exports的用法只有以下三种

1
2
3
const fs = require('fs')
exports.fs = fs
module.exports = fs

import/export的写法多种多样

1
2
3
4
5
6
7
8
9
10
11
12
import fs from 'fs'
import {default as fs} from 'fs'
import * as fs from 'fs'
import {readFile} from 'fs'
import {readFile as read} from 'fs'
import fs, {readFile} from 'fs'

export default fs
export const fs
export function readFile
export {readFile, read}
export * from 'fs'

本质差别

  1. CommonJs & ES6 Module 输出都可以看成是一个具备多个属性或者方法的对象。
  2. default是ES6 Module独有的关键字,export default fs输出默认的接口对象,import fs from ‘fs’可以直接导入这个对象。
  3. ES6 Module中导入的属性或者方法是强绑定的,包括基础类型;而CommonJS是普通的值传递或者引用传递。
require import
动态评估 静态评估
运行时报错 解析时报错
非关键词 关键词
1
2
3
4
5
6
7
8
9
// dep.js
export let a = 1
setTimeout(()=> a+=1, 500)

// app.js
import {a} from 'dep'
setTimeout(function() {
console.log(a)
}, 1000)

输出为2.而commonjs为1。

module.exports和exports区别

首先,导出的永远为module.exports对象。而exports对象仅为module.exports的引用。

github-demo-summary

同构

HTML空内容,较长的等待时间,加载的HTML中不显示内容,导致无法进行SEO优化。

利用React在客户端和服务端渲染数据同步是难点。

在服务端渲染出HTML内容返回给用户了,浏览器端展示了这部分内容之后,同样也要加载react和代码,需要绑定事件,处理响应。还需要重新渲染,这会用到在服务端渲染时用到的数据,而这部分数据在99%的情况下是一样。需要在生命周期中通过Ajax请求一遍服务端。一次http请求,会耗费资源以及等待时间。这部分数据又在服务端请求时被用过,是否复用,是开发同构应用非常重要的考虑。

nextjs提供的getInitialProps方法,帮我们建立了这一套逻辑。在服务端渲染时会使用这个方法,并得到一个return的返回,作为页面的return被接收到。这部分数据会被nextjs处理收集,在服务端返回html的时候,会插入到html的script标签当中,以HTML的一部分返回此标签,作为react在客户端第一次渲染时的数据,而不需要再去调用getInitialProps重新请求数据。

在项目中我们利用getInitialProps获取缓存,同步redux数据。

OAuth

在实际应用中,构建自己的用户体系是复杂而高要求的。而接入第三方可以避免。

通过cookie确定用户,通过session存储信息。

Hooks

React 生态周边

Redux

在服务端和客户端都要生成Redux store,这两个store之间同步。

Three-column Layout

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
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.left{
float: left;
height: 200px;
width: 100px;
background-color: red;
}
.right {
float: right;
height: 200px;
width: 100px;
background-color: blue;
}
.main {
margin: 50px 120px;
height: 200px;
background-color: #000;
}
/* 流体布局 */
</style>
</head>
<body>
<div class="container">
<div class="left"></div>
<div class="right"></div>
<div class="main"></div>
</div>
</body>
</html>

缺点 : 主要内容无法最先加载,会影响用户体验。

II. BFC三栏布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.left {
float: left;
width: 100px;
height: 200px;
margin-right: 20px;
background-color: red;
}

.right {
width: 200px;
height: 200px;
float: right;
margin-left: 20px;
background: blue;
}

.main {
height: 200px;
overflow: hidden;
background-color:green;
}
/* BFC布局,BFC区域,不会与浮动元素重叠 */

缺点同上。

III. 双飞翼布局

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
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.content {
float: left;
width: 100%;
}

.main {
height: 200px;
margin-left: 200px;
margin-right: 220px;
background-color: green;
}

.left {
float: left;
height: 200px;
width: 190px;
margin-left: -100%;
background-color: red;
}

.right {
width: 200px;
height: 200px;
float: right;
margin-left: -200px;
background-color: blue;
}

/* 双飞翼布局,主体内容可以优先加载,html代码稍复杂 */

</style>
</head>
<body>
<div class="content">
<div class="main"></div>
</div>
<div class="left"></div>
<div class="right"></div>
</body>
</html>

通过对margin-left赋负值,使得.left能够前于.main显示,.right能够后于.main显示。占用.content的空间。

IV. 圣杯布局

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
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.container {
margin-left: 200px;
margin-right: 220px;
}
.main {
float: left;
background-color: red;
height: 200px;
width: 100%;
}
.left {
float: left;
background-color: blue;
width: 180px;
margin-left: -100%;
height: 200px;
position: relative;
left: -200px;
}
.right {
float: right;
background-color: green;
width: 200px;
height: 200px;
position: relative;
right: -220px;
margin-left: -200px;
}

</style>
</head>
<body>
<div class="container">
<div class="main"></div>
<div class="left"></div>
<div class="right"></div>
</div>
</body>
</html>
<!-- 圣杯布局 -->

V. flex布局

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
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.container {
display: flex;
}
.main {
flex-grow: 1;
height: 300px;
background-color: red;
}
.left {
order: -1;
flex: 0 1 200px;
margin-right: 20px;
height: 300px;
background-color:blue;
}
.right {
flex: 0 1 100px;
margin-left: 20px;
height: 300px;
background-color: green;
}
</style>
</head>
<body>
<div class="container">
<div class="main"></div>
<div class="left"></div>
<div class="right"></div>
</div>
</body>
</html>

VI. Table布局

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
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.container {
display: table;
width: 100%;
}
.left, .main, .right {
display: table-cell;
}
.left {
width: 200px;
height: 300px;
background-color: red;
}
.main {
background-color: blue;
}
.right {
width: 100px;
height: 300px;
background-color: green;
}
</style>
</head>
<body>
<div class="container">
<div class="left"></div>
<div class="main"></div>
<div class="right"></div>
</div>
</body>
</html>
<!-- 无法设置栏间距 -->

VII. 绝对定位布局

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
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.container {
position: relative;
}
.main {
height: 400px;
margin: 0 120px;
background-color: green;
}
.left {
position: absolute;
width: 100px;
height: 300px;
left: 0;
top: 0;
background-color: red;
}
.right {
position: absolute;
width: 100px;
height: 300px;
background-color: blue;
right: 0;
top: 0;
}
</style>
</head>
<body>
<div class="container">
<div class="main"></div>
<div class="left"></div>
<div class="right"></div>
</div>
</body>
</html>

Restful

REST

RESTful

REST:在网络中client和server的一种交互形式;REST本身不实用,实用的是如何设计 RESTful API(REST风格的网络接口)

Web:分布式信息系统为超文本文件和其它对象(资源)提供访问入口

资源引出三个操作:

  • 识别(identify)。uri(url+urn)识别资源。
  • 表示(represent)。html, xml, 图片,视频。
  • 交互(interact with)。http, ftp等协议。
  1. 客户端服务器分离. 提高界面便携性,简化服务器提高可伸缩性(高性能低成本)允许组件分别优化
  2. 无状态。从客户端的每个请求要包含服务器所需要的所有信息。优点:提高可见性、可靠性、可扩展性。
  3. 缓存。服务器返回信息必须被标记是否可以缓存。优点:减少交互次数、平均延迟。
  4. 分层系统。避免系统复杂,提高可扩展性
  5. 统一接口。提高交互的可见性,鼓励单独改善组件。
  6. 支持按需代码。提高可扩展性。

看Url就知道要什么
看http method就知道干什么
看http status code就知道结果如何

ajax

什么是Ajax

异步JavaScript和XML。

用于创建快速动态网页的技术。

通过在后台与服务器进行少量数据交换,Ajax可以实现网页异步更新。

readyState五种状态的含义

0:请求未初始化

1:服务器连接已建立

2:请求已接收

3:请求处理中

4:请求已完成,且响应已就绪

innerText与innerHTML区别

前者将对象的文本内容设置或返回为指定解点及其所有子节点的纯文本。

而后者讲获取和设置元素中的纯文本或HTML内容。与innerText不同,innerHTML允许您使用HTML格式的文本,并且不会自动对文本进行编码和解码。

另外outerHTML包括对象节点本身的标签。

Generator

概念

Generator 函数是协程在ES6的实现,最大的特点就是可以交出函数的执行权(即暂停执行)。

1
2
3
4
function* gen(x) {
var y = yield x + 2;
return y;
}

generator函数是一个封装的异步任务。执行:

1
2
3
let g = gen(1);
g.next()// {value: 3, done: false}
g.next()// {value: undefined, done: true}

调用generator函数会返回一个iterator,其next方法会返回一个对象,表示当前阶段的信息,包括value属性(yield语句后面表达式的值),done表示是否执行完毕。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
var fetch = require('node-fetch');

function* gen() {
var url = 'https://api.github.com/user/github';
var result = yield fetch(url);//return a Promise
console.log(result.bio);
}

var g = gen();
var result = g.next();
result.value.then(function(data){
return data.json();
}).then(function(data))

promise

What is Promise

Promise是JavaScript的异步操作的解决方案,为异步操作提供统一接口。

它起到代理作用(proxy), 充当异步操作与回调函数之间的中介。Promise可以让异步操作写起来,就像在写同步操作的流程,而不必一层层地嵌套回调函数。

Promise是一个对象,也是一个构造函数。

1
2
3
4
5
function f1(resolve, reject) {
//异步代码
}
var p1 = new Promise(f1);//p1是一个构造函数实例
p1.then(f2);// f1异步操作完成就会执行f2

传统方法与Promise方法对比:

1
2
3
4
5
6
7
8
step1(function(v1){
step2(v1, function(v2){
// ...
})
})

new Promise(step1)
.then(step2);

States of Promise

共三种状态:

  • pending
  • fulfilled
  • rejected

fulfille为resolved.

这三种状态的变化途径只有两种:

  • 从”未完成”到”成功”

  • 从”未完成”到”失败”

1
2
3
4
5
6
7
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}

timeout(100)

then 用法解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 写法一
f1().then(function () {
return f2();
});

// 写法二
f1().then(function () {
f2();
});

// 写法三
f1().then(f2());

// 写法四
f1().then(f2);

Promise优缺点

优点:

  • 统一异步API,fetch API基于promise的:
1
2
3
fetch(url)
.then(request=>request.test())
.then(str=>...)

Promise与事件对比:

  • 更适合处理一次性的结果,不能使用Promise

Promise与回调函数相比:

  • 更干净的函数参数,回调函数的场景,主函数既有输入参数,又有输出参数。

缺点:

  • 多次触发的事件
  • 数据流
  • 不能取消执行
  • 无法获取当前执行的进度信息(比如,要在用户界面展示进度条)(Q Promise)

简单地实现Promise,并支持异步链式调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Promise(fn) {
//promise resolve时的回调函数集
this.cbs = [];
//传递给Primise处理函数的resolve
//这里直接往实例上挂个data
//然后把onReolvedCallbakc数组里的函数依次执行一边
const resolve = (value) =>{
//注意promise的then函数需要异步执行
setTimeout(()=>{
this.data = value;
this.cbs.forEach((cb)=>cb(value));
});
}

//执行用户传入的函数
//并把resolve方法交给用户执行
fn(resolve.bind(this));
}

分开来看,fn是用户传的函数,这个函数内部调用了resolve函数后,就会把promise实例上的cbs全部执行一遍,那么cbs数组中的函数从哪来呢?

1
2
3
4
5
6
7
8
9
10
Promise.prototype.then = function(onResolved) {
return new Promise((resolve)=>{
const result = onResolved(this.data);
if(result instanceof Promise) {
result.then(resolve);
} else {
resolve(result)
}
})
}

异步操作封装成Promise,实现对Ajax的封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function pajax({url=null, method='GET', dataType:'JSON', async='true'}) {
return new Promise((resolve, reject)=>{
let xhr = new XMLHttpRequest();
xhr.open(method, url, async);
xhr.responseType = dataType;
xhr.onreadystatechange = () =>{
if(xhr.readyState===4 && xhr.status===200) {
let result = xhr.responseText;
resolve(result);
}
}
xhr.onerror = (err) =>{
reject(err);
}
xhr.send();
})
}

pajax({
url: './test.json',
}).then(console.log)
.catch(console.err)

Promise then的第二个参数和catch的区别

主要区别就是,如果在then的第一个函数里抛出了异常,后面的catch能捕获到,而then的第二个函数捕获不到。

Promise的catch是个语法糖,还是通过then来处理的,类似于:

1
2
3
Promise.prototype.catch = function(fn) {
return this.then(null, fn);
}

捕获错误信息的时候会就近原则。

js single thread and event-loop

Pros and Cons for single threaded javasript

由浏览器决定,JavaScript主要用途是操作DOM,决定了它只能是单线程,否则会带来复杂的同步问题。

Pros:

  1. 适合高并发
  2. 适合I/O密集型应用

Cons:

  1. 不适合CPU密集型应用。如果有长时间运行的计算(大循环),将会导致CPU时间片不能释放,使得后续I/O无法发起。

大纲:

  1. 基本知识点,宏任务、微任务…
  2. 事件循环机制过程

浏览器中的事件循环

JavaScript代码执行过程中,除了依靠函数调用栈来搞定函数的执行顺序,还依靠任务队列(task queue)来搞定另外一些代码的执行。整个执行过程,成为事件循环过程。一个线程中,事件循环是唯一的,但任务队列可以拥有多个。任务队列又分为macro-task和micro-task,在最新标准中称为task和jobs。

  • 执行一个宏任务(栈中没有就从事件队列中获取)
  • 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
  • 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
  • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
  • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

event-loop

macro-task大概包括:

  • script(整体代码)
  • setTimeOut
  • setInterval
  • setImmediate
  • I/O
  • UI render
  • DOM

micro-task大概包括:

  • process.nextTick
  • Promise
  • Async/Await(Promise)
  • MutationObserver(h5新特性)

几个例子

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
console.log('script start')

async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()

setTimeout(function() {
console.log('setTimeout')
}, 0)

new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})

console.log('script end')

依次输出为:

script start -> async2 end -> Promise -> script end -> async1 end -> promise1 -> promise2 -> setTimeout

  1. 首先全局匿名函数最先执行,输出”script start”
  2. 然后遇到async1()函数,调用,将该函数压入执行栈,接着执行 “await async2()”。这里await关键字的作用就是await下面的代码只有当await后面的promise返回结果后才可以执行。而await async2() 语句就像执行普通函数一样执行async2(),进入async2输出”async2 end”。
  3. await关键字下面的语句相当于.then(),加入micro-task队列,那么async1函数执行结束,弹出执行栈。
  4. 遇到setTimeout(),加入macro-task队列。
  5. 接着执行Promise中的executor函数,然后.then()被加入micro-task队列。
  6. 然后执行最后的输出,当前宏任务结束。
  7. 此时事件循环机制开始工作:然后从micro-task队列中依次执行微任务。

而当await函数后面跟的是一个异步函数的调用(这里我们强调返回值为异步才为异步函数):

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
console.log('script start')

async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
return Promise.resolve().then(()=>{
console.log('async2 end1')
})
}
async1()

setTimeout(function() {
console.log('setTimeout')
}, 0)

new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})

console.log('script end')

在最新的chrome v8引擎中执行为:

script start
async2 end
Promise
script end
async2 end1
promise1
promise2
async1 end
setTimeout

在这里我们理解为,进入await标记的代码后,将Promise后的首个链式调用注册为micro-task,然后继续执行,直到当前宏任务完成后,微任务也完成后,最后执行await后面的代码,最后再调用其它宏任务。

一个典型的async await的使用为:

1
2
3
4
async function f() {
await p
console.log('ok')
}

我们可以将其简化理解为promise:

1
2
3
4
5
function f() {
return RESOLVE(p).then(() => {
console.log('ok')
})
}

『RESOLVE(p)』接近于『Promise.resolve(p)』,不过有微妙而重要的区别:p 如果本身已经是 Promise 实例,Promise.resolve 会直接返回 p 而不是产生一个新 promise。

这里我们复习一下Ajax原生请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function search(term, onload, onerror) {
var xhr, results, ur
l;
url = `http://example.com/search?q=${term}`;

xhr = new XMLHttpRequest();
xhr.open('GET', url, true);

xhr.onload = function(e) {
if(this.status===200) {
results = JSON.parse(this.responseText);
onload(results);
}
};
xhr.onerror = function(e) {
onerror(e);
};

xhr.send();
}

search('Hello World', console.log, console.error);

如果使用Promise对象,可以写成如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function search(term) {
var url =`http://example.com/search?q=${term}`;
var xhr = new XMLHttpRequest();
var result;
xhr.open('GET', url, true);
var p = new Promise(function(resolve, reject){
xhr.open('GET', url, true); //async or not
xhr.onload = function(e) {
if(this.status===200) {
result = JSON.parse(this.responseText);
onload(results);
}
}
xhr.onerror = function(e) {
reject(e);
}
xhr.send();
});
return p;
}

search("Hello World").then(console.log, console.error)