VSCode does not supprt negative regular expression (‘!’) on user settings files.exclude
.
But we could have work around to set file exclusion rule in file explorer.
For example, if we only want to display node_modules/@project
folder and hide all other folders, the setting could be like this:
1 | // File explorer |
This could work perfectly. Yep.
]]>axios.js
,发现可以考虑开始使用新的类库了。调用fetch api拿到response之后,通常我们需要两步来解析response中的数据。
1 | const url = 'some_api_url'; |
response中的数据有status,还有body。但会发现body的数据类型是一个ReadableStream
而不是一个正常的json或者string。我们需要用response.json()
来进行转换。注意这个方法是返回一个promise对象,所以要继续调用then
来解析。
1 | const url = 'some_api_url'; |
这时的data就是我们需要的json或者string数据。
$ npm install --save axios
1 | // React - ES6 |
使用时和fetch类似。
1 | axios.get(url).then(response => console.log(response)); |
可以看到response的数据结构和jquery接口很像,包含了status和data。status是请求返回的状态码,data就是response的数据主体。跟fetch对比起来要少一步promise解析。
遇到错误处理,通常是使用catch方法来进行捕捉,但是使用fetch时,这种方法会失效。
1 | const url = 'wrong_api_url'; |
使用一个错误的api url,但是看结果会发现还是进入了then的回调函数中。只是状态码变成了400。
使用axios时,catch中的回调函数就会被调用到。
1 | const url = 'wrong_api_url'; |
打出的error log是:
BAD ERROR: Request failed with status code 400
可以看出在这两方面中,axios确实是比fetch好用很多。
参考来源:
]]>create-react-app
搭建起来的react应用。只是突发奇想页面渲染过程中,如何能把图片保存在server端。直接在React代码中使用保存文件的api目前还没有找到,所以只能寄希望于nodejs
端来解决保存问题。
大概在网上搜了一下,目前普遍的做法跟自己想的比较类似,就是在nodejs
自定义一个保存图片的api,然后React代码中发送api请求,nodejs端捕获,并且保存对应的图片到server文件系统中。
实践起来还是比较复杂的,中间遇到的问题也不少。
使用express
迅速搭建一个server,端口号暂定为5000。(Create React App默认的Webpack Server端口为3000)
server/server.js:
1 | const express = require('express'); |
接下来先模拟写一个图片下载的服务。目前项目里抓取的都是twitter上的图片,所以拿一个例子来说明。
图片:”https://pbs.twimg.com/media/DUWPzZ7W4AExatH.jpg"
1 | http.get ({ |
报错:Error: connect ECONNREFUSED
这个错误是说明无法与服务器连接。debug很久才发现,图片是存放在twitter网站的静态资源,需要翻墙使用代理才能下载到。加上公司的proxy,这个图片才算是能正常下载下来。
1 | http.get ({ |
使用mkdirp
是因为需要自动创建下载目录。fs.writeFileSync
不负责创建路径。事实上,如果路径不存在,会直接报错导致程序中断。
图片的写入需要创建一个fileStream来实现。给repsonse上绑一个data的监听事件,一旦是有数据返回就往stream上写数据流直到response返回结束。
至此,nodejs api编写完成。
在React中,在componentDidMount
中发送请求,这样就不会阻塞页面的加载渲染。
1 | componentDidMount() { |
直接使用5000端口的API端点,前端会直接报错。这里存在一个跨域请求的访问,出于安全考虑,浏览器会禁止这类访问,并将请求状态置为500。所以我们只能使用3000端口。但如何把端口从3000映射到nodejs的5000端口上?
此时需要在package.json
里增加一行:
1 | { |
关于这个proxy的定义,只在网上找到这么一句解释:
A proxy to use for outgoing https requests. If the HTTPS_PROXY or https_proxy or HTTP_PROXY or http_proxy environment variables are set, proxy settings will be honored by the underlying request library.
大概就是如果使用了proxy
,所有HTTP / HTTPS的请求都会被代理到proxy设定的域名下。这样我们就可以调用localhost:5000下的服务了。
前后端相应的配置都已经完成,还剩最后一步:启动两个服务器。
1 | { |
很遗憾,这样写并不能启动我们自定义的服务器。node是单线程,启动了create-react-app的服务器之后,就会一直监听在那里,不会再执行第二步命令。所以需要concurrently
库来帮助。
concurrently
可以“同时”执行多个命令。
1 | { |
参数kill-others-on-fail
是当某一个命令失败后,同时结束其他命令。这样我们就可以保证两个服务器同时启动。
参考来源:
]]>1 | module.exports.hello = function() { |
Nodejs遵循的是CommonJS规范,使用require
方法来加载模块,而ES6中是使用import
来加载模块。参考这个图片:
1 | function (exports, require, module, __filename, __dirname) { |
可以看出require
规范下module
和exports
都是内置的变量。但是exports
是被module.exports
引用的,所以给任意其一赋值都是可行的。
1 | // working |
这种情况下,模块导出的是包含两个方法的对象。但是对比下面的声明:
1 | // working |
这种情况下,第二个声明失效,因为module.exports
已经被直接赋值,exports
会被忽略。
另外,因为exports
只是一个变量,所以直接给其赋值也是无意义的。
1 | // working |
require
方法无法识别export
导出语句,会直接报错。因此Nodejs
环境下只能使用module.exports
或者exports
语法。
1 | // module.js |
import
语句可以识别module.exports
、exports
和export
语法。
1 | // module.js |
可以猜想到,import
方法中,export default
和exports.default
是被认为相似的声明,而且不会强制认为必须是相同的导出导入名称匹配。
这种互通,在Server Side Rendering
技术中会有更好的体现。在服务器渲染技术中,我们需要找到一种声明在ES6模块(比如React代码)和Nodejs中都可以被识别,这种情况下,使用module.exports
/ exports
顶替export
会是更好的选择。
以height
为例:
1 | is a measurement which includes the element borders, the element vertical padding, the element horizontal scrollbar (if present, if rendered) and the element CSS height. |
offsetHeight = 可看到的元素高度 + 纵向padding + 边框高度 + 横向滚动条高度
1 | returns the inner height of an element in pixels, including padding but not the horizontal scrollbar height, border, or margin |
clientHeight = 可看到的元素高度 + 纵向padding - 横向滚动条高度
1 | is a measurement of the height of an element's content including content not visible on the screen due to overflow |
scrollHeight = 可看到的元素高度 + 看不到的元素高度 + 纵向padding
参考文章来源:
The properties of offsetTop, clientTop, scrollTop in the JS
What is offsetHeight, clientHeight, scrollHeight?
[Error] QuotaExceededError (DOM Exception 22): The quota has been exceeded.
The root cause is that: storage api, like localStorage
/ sessionStorage
is not supported in safari incognito mode.
According to the MDN wiki, the localStorage is set as an object with a quota of zero, which will make it unusable.
A way is required to detect whether the storage api is available under current browser session.
1 | function storageAvailable(type) { |
这篇文章主要介绍require方法在nodejs开发中的一些应用技巧。除此之外,模块化开发也会有一些涉及。
1 | const config = require('/path/to/config'); |
针对require()
方法,Node的执行步骤主要分以下5步。
在这一步中,require方法会将文件的路径转为绝对路径。
1 | require('find-me'); |
Node会查找所有module.paths
指定的所有路径下的find-me.js
文件。可以通过module.paths方法查看当前查找路径的优先级顺序。
$ node> module.paths[ '/Users/username/stubhub/techspace/my-react-story-book/repl/node_modules','/Users/username/stubhub/techspace/my-react-story-book/node_modules','/Users/username/stubhub/techspace/node_modules','/Users/username/stubhub/node_modules','/Users/username/node_modules','/Users/node_modules','/node_modules','/Users/username/.node_modules','/Users/username/.node_libraries','/usr/local/lib/node' ]
如果这个文件没有找到,则会报错:cannot find module error.
。
相对应的,在项目目录下创建一个node_modules
文件夹,其中包含一个find-me.js
。这样也能require到。
$ mkdir node_modules$ echo console.log('I am not lost');" > node_modules/find-me.js$ node> require('find-me');I am not lost
模块化概念中,一个模块不仅仅只是一个文件,还有可能是一个文件夹。可以在node_modules
下创建一个find-me
文件夹,然后再在其下创建index.js
文件,这样也能require到。
$ mkdir -p node_modules/find-me$ echo "console.log('Found again.');" > node_modules/find-me/index.js$ node> require('find-me');Found again.
index.js
是默认的模块入口文件。也可以通过在package.json
中配置对应的入口文件名来更改。
$ echo "console.log('I rule');" > node_modules/find-me/start.js$ echo '{ "name": "find-me-folder", "main": "start.js" }' > node_modules/find-me/package.json$ node> require('find-me');I rule
当我们需要查找确定某一个模块能否被require到(并非需要真去加载执行这个模块),可以用require.resolve(moduleName)
来检查。如果模块存在,返回模块的路径;如果不存在,则与require
方法一样报错。这对于去引用在package.json
中optionalDependencies
下定义的模块引用很有用处。
$ node> require.resolve('find-me');'/Users/samer/learn-node/node_modules/find-me/start.js'> require.resolve('not-there');Error: Cannot find module 'not-there'
定义模块路径方面,相对路径以./
或者../
作为开头,而绝对路径以/
作为开头。
创建一个lib/util.js
文件和index.js
文件,并输出当前module
,可以看出node对循环引用的支持。
$ mkdir lib$ echo "console.log('In util', module);" > lib/util.js$ echo "console.log('In index', module); require('./lib/util');" > index.js
执行index.js
文件,可以看到下列输出。
$ node index.jsIn index Module { id: '.', exports: {}, parent: null, filename: '/Users/samer/learn-node/index.js', loaded: false, children: [], paths: [ ... ] }In util Module { id: '/Users/samer/learn-node/lib/util.js', exports: {}, parent: Module { id: '.', exports: {}, parent: null, filename: '/Users/samer/learn-node/index.js', loaded: false, children: [ [Circular] ], paths: [...] }, filename: '/Users/samer/learn-node/lib/util.js', loaded: false, children: [], paths: [...] }
可以看到,在module util中,指出了其父模块为index
,但是父模块的介绍中,children
的属性被标为[Circular]
,否则会陷入无限循环。
这就引申出了另外一个模块循环引用的问题:如果util
模块又引用了index
模块?
对之前两个模块添加exports
输出。
1 | // Add the following line at the top of lib/util.js |
这时,module的输出就变成:
$ node index.jsIn index Module { id: '.', exports: { id: 'index' }, loaded: false, ... }In util Module { id: '/Users/samer/learn-node/lib/util.js', exports: { id: 'lib/util' }, parent: Module { id: '.', exports: { id: 'index' }, loaded: false, ... }, loaded: false, ... }
通常定义module的输出会使用下列语法:
1 | exports.id = 1; |
可以看一下node module包装之后的代码,理解会更加深刻:
1 | function (require, module, __filename, __dirname) { |
直接给exports
赋值则不会生效,因为这只是改变了exports
这个变量,但是输出的module.exports
没有任何变化。如果是给exports
添加更多的属性,则会生效。
loaded
关键字标示模块是否已经加载完成。可以通过setImmediate
方法来检查这个标志。setImmediate
方法是在当次event loop结束之后立刻执行。对于加载一个模块来说,event loop就是一次require/load模块的过程,所以下列两种代码执行的效果是一样的。
1 | // module A |
同样,当模块加载完成时,exports
对象也立刻执行完毕,所以想通过异步来改变模块输出的数据,是不会成功的。
1 | fs.readFile('/etc/passwd', (err, data) => { |
现在再来看循环引用的案例。创建两个模块:lib/moduleA
和lib/moduleB
。
1 | // lib/moduleA.js |
执行node moduleA.js
时,会得到下列输出:
$ node lib/module1.jsModule1 is partially loaded here { a: 1 }
可以看出node对循环引用的支持,依据的原则就是:在加载过程中创建exports
对象。如果在加载结束前引用了模块,则只能得到当前执行之后的模块输出。
通过一句语句就能看出node是对模块进行了封装后再加载:
$ node> require('module').wrapper[ '(function (exports, require, module, __filename, __dirname) { ','\n});' ]
这五个参数会经常在模块中用到。exports
是对module.exports
的引用,require
和module
是执行模块代码必用的函数。__filename
和__dirname
是封装的模块的文件名和绝对路径。
也可以通过arguments
来输出这几个参数:
$ echo "console.log(arguments)" > index.js$ node index.js{ '0': {},'1':{ [Function: require] resolve: [Function: resolve], main: Module { id: '.', exports: {}, parent: null, filename: '/Users/username/index.js', loaded: false, children: [], paths: [Object] }, extensions: { '.js': [Function], '.json': [Function], '.node': [Function] }, cache: { '/Users/username/index.js': [Object] } },'2':Module { id: '.', exports: {}, parent: null, filename: '/Users/username/index.js', loaded: false, children: [], paths: [ '/Users/username/node_modules', '/Users/node_modules', '/node_modules' ] },'3': '/Users/username/index.js','4': '/Users/username' }
]]>flex
layout based on this post A complete guide to Flexbox.Right beginning: display: flex
only affects the items inside the container, but of course for the container, it is a block
element. So the container will possess one line of the page.
Also, it is a 1-dimensional layout, mainly focuses on line layout. Really friendly to responsive design.
For comparison, there is another layout style named grid
. It is covered by another post A complete guide to Grid. Will discuss that in future posts.
1 | .container { |
inline-flex
will only affect the container to be an inline
element. The items inside the container are placed as inline
elements no matter it is flex
or inline-flex
.
1 | .container { |
1 | .item { |
Place items in the sequence of flex-order
.
1 | .item { |
If all items flex-grow
is 0, no extra space will be distributed to all items.
1 | .item { |
nowrap
will put all items in one line.
1 | .item { |
Combination for flex-direction
and flex-wrap
.
1 | .container { |
1 | .item { |
length
would be a real valid number like 80px
.
If it is a valid length value, it means the item should use this as its width.
If it is 0
, the extra space around content in an item is not counted in when distributing the rest space in line of flex container.
If it is auto
, the extra space around content in an item is distributed based on its flex-grow
value.
See this picture to figure out its usage.
1 | .container { |
Combination for flex-grow
, flex-shrink
and flex-basis
.
1 | .item { |
flex-shrink
and flex-basis
are optional. By default, 0 1 auto
.
Note that float
, clear
and vertical-align
is not working under flex
layout.
Issue blog: What’s the difference between “super()” and “super(props)” in React when using es6 classes?
1 | // if you want to access `this.props` in constructor |
vs
1 | // If you do not want to access `this.props` in constructor |
SomeComponent.displayName
is just to identify the name of current module.
No functional use, just to help the debug to be clear.
Issue blog: What is React component ‘displayName’ is used for?
React source code during debugging: Debug code in react
1 | function warnIfInvalidElement(Component, element) { |
For the following code snippet:
1 | class IncreaserPanel extends React.Component { |
Tips: Using onClick={() => this.increaser()}
will cause that every time on rendering function, this function will be recreated. Then especially with the case that it is used as a props to child react element, it will lead to rerendering of the child element. This is a waste of performance.
In ES6 class function grammer, the export
could be like this:1
2
3
4
5
6
7
8
9
10
11
12
13import React from 'react';
class MyComponent extends React.Component {
//...
}
export default MyComponent; // import MyComponent from 'myComponent.js'
// or
/**
* export {
* MyComponent, // import {MyComponent} from 'myComponent.js'
* MySecondComponent,
* //...
* }
*/
In CMD guideline, the export could be written like this:1
2
3
4
5
6
7import React from 'react';
class MyComponent extends React.Component {
//...
}
module.exports = {
MyComponent: MyComponent // import {MyComponent} from 'myComponent.js'
};
Not much difference, but if we use CMD guideline, it is easier to use in Server side render (SSR), because node.js supports CMD guideline.
]]>Host
、Domain
、Server
。有点混淆不清,所以简单查了一下。DNS(Domain Name Service)可以准确翻译某个指定的IP地址,找到对应的计算机,反之亦然。
IP地址比较难以记忆,所以Internet允许给网络中的计算机指定一个字符串定义的名称。比如IP地址为18.72.0.3
就被指定为bitsy.mit.edu
。整个字符串就是这台计算机的host name
。第一部分的bitsy
是机器名,其余的mit.edu
是其domain name
。
另外还有一张图能够说明host name
和domain name
的关系。
Domain name
和SubDomain name
比较好区分:
example.com是一个域名;
tools.example.com是example.com
上的子域名。
来源:
]]>Yarn
。主要的优势就是分析包之间的关联关系,并缓存已下载的内部包依赖。Yarn的实现方法主要有三步。
registry
。node_modules
目录下。Yarn已在Facebook的生成环境中使用,证明其工作的有效性。
$ npm install -g yarn
相关命令:
$ npm install$ npm install --save <name>
可以替换成:
$ yarn$ yarn add <name>
我也在本地试验了一下,找了一个最基本的项目,这是项目中目前的包依赖,package.json
文件。
1 | { |
总共只有这几个包依赖,但是如果使用npm
进行安装。
$ npm install
这一条命令总共执行了3分钟。但如果换成yarn
命令。
$ yarn
这一条命令分析出了380+个包依赖,优化后总共只执行了不到80秒。速度提升一倍以上,确实是很实用。
以后继续分析yarn的其他功能。
最后感叹一下,Facebook真是的前端技术领域里的担当啊。
]]>For example, there is a repo named remote/repo
. Then you fork it within your own github as you/repo
.
To keep the develop
branch in you/repo
with the develop
in remote/repo
, the following steps may be needed.
Checkout two develop branches.
$ git checkout -b remote-develop remote/develop$ git checkout -b develop you/develop
If there are commits after the lastest sync, the remote-develop
will be ahead of develop
with several changes. Then you need to keep the develop
with remote-develop
.
$ git checkout develop$ git merge remote-develop
Then you have updated the local branch develop
with lastest code. Finally the update for you/repo
should be made to keep the two repos sync.
$ git push you develop
]]>$ git remote -vorigin git@github.corp.ebay.com:yuren/app-browse.git (fetch)origin git@github.corp.ebay.com:yuren/app-browse.git (push)upstream git@github.corp.ebay.com:Stubhub/app-browse.git (fetch)upstream git@github.corp.ebay.com:Stubhub/app-browse.git (push)$ git checkout -B develop upstream/developfatal: Cannot update paths and switch to branch 'develop' at the same time.Did you intend to checkout 'upstream' which can not be resolved as commit?
Here listed a correct answer from stackoverflow. It works for me.
$ git remote update$ git checkout -B develop upstream/develop
]]>Here is a test code snippet for shallow clone or deep clone.
1 | /** |
For testing array code part, I am really amazed that the cloned array is independent from the original array.
Maybe there is no deep or shallow clone difference in Javascript array objects.
However, for testing object code part, the difference does exist.
In this place, a type check should be added. This version is simplified.
Among objects, there are mainly three sub types that need attention.
1 | typeof [1,2,3]; // object |
Therefore, for trap 1 case, an instanceof check should be added, to decide whether the result structure should be array or object.
]]>Q1:Given an array of integers, print all subsets of size kE.g. A = [1 2 4 2], printSubsets(A, 1) should output124printSubsets(A, 2) should output1 21 42 4printSubsets(A, 3) should output1 2 4
I am not gooed at algorithm, even bad at it. So the only solution I could thought of is Recurrsion.
Yeah, that sucks. However, it really works in this question.
I just need to use recurrsion to reuse the output of subsets with fewer integers, and then combine the subsets with original array to make a Cartesion product.
During this solution thinking, I forgot several things that should be focused or paid attention to. Fortunately, these missings has been notified by the kind interviewee.
Here the solution is attached.
1 | var printSubsets = function(array, num) { |
I have pointed out the four traps.
Rewrite the push method so that the new element will be checked first, and then decided whether to be pushed into the result array.
The order of new element should be considerred. [1, 2] and [2, 1] should be regarded as same element, and one of two elements should not be pushed into result array if the other has already been pushed.
When checking two arrays which contain the same elements, the method toString
could be used.
[1,2] == [1,2] // false[1,2].toString() == [1,2].toString() // true
In each loop for array elements, the result should remain same. Therefore, we need to ensure that the original result array should not be poluted.
Then what bothers me is that the two function: cloneArray and deepCloneArray. Apparently, cloneArray is just copying arrays in a shallow layer, while deepCloneArray can copy arrays with deepest layer.
However, run the below code, the output realy amazes me.
var source = [[1], [2], [3]];var testClone = cloneArray(source);var testDeepClone = deepCloneArray(source);testClone; // [[1], [2], [3]]testDeepClone; // [[1], [2], [3]]source[0] = [1,2];testClone; // [[1], [2], [3]]testDeepClone; // [[1], [2], [3]]
Really not the same as what I thought it would be.
Another post is waiting for me to explore the copy issue.
]]>1 | // ES5 |
注意,不用加function
关键字,并且方法之间不需要加分号。
类的数据类型就是函数,类本身就指向构造函数。
1 | typeof Point // "function" |
类的实例上调用方法,其实就是调用原型上的方法。
1 | var point = new Point(); |
类的内部定义的所有方法,都是不可枚举的。
1 | class Point { |
通过new
命令生成对象实例时,自动调用该方法。默认返回实例对象(this),但也可以指定返回其他对象。
1 | class FooTestString { |
首先还是要安装一些最基本的应用。
ssh远程命令行操作的神器:xterm。
操作文件系统的应用:winscp。
首先去Node官网下载Linux版本的安装文件,下载下来是一个tar.xz
文件。这种文件就是Linux下面的压缩包后缀。
使用winscp
将安装包拖拽到远程目录中,我放到的是/root/Downloads
下。之后使用解压缩命令。
$ tar xvfJ [node-install-file.tar.xz] -C [/tmp/node]
命令的含义是将压缩包解压缩到/tmp/node
文件夹下。
之后将node文件夹移动到用户的目录下。
$ sudo su -c "chown -R root:root /tmp/node*"$ sudo mv /tmp/node* /usr/local/node
这里可以任意指定node的安装目录,我是放到了/usr/local/node
下。
这时访问node命令发现不会识别,主要是因为环境命令还没有设置。
设置环境命令有多个地方。
可以在顶级目录下的
/etc/bash.bashrc
文件中追加PATH
的定义。这种方式会使所有的用户都获得这种命令的执行权。
在当前用户下的
.bashrc
文件中追加PATH
的定义。这种方式只会让当前用户获得这种命令的执行权。我选择的是这一种方法。
首先查看当前环境变量的定义。
$ cd /root$ cat .bashrc
可以看到里面的定义。此时利用vim编辑器编辑文件,追加PATH
的定义。
$ vi .bashrc
进入编辑器模式,填写内容:
export PATH="$PATH:[node-directory]/bin"
这里将node的安装目录下的bin
文件夹设置进来。加两个vim的命令。
注意,其一,Linux的环境变量是通过冒号分隔的;其二,安装目录路径一定要是绝对路径。
ESC: 从编辑模式切换到命令模式a: 当前光标位置追加文本wq: 保存文件并退出
保存时可能会遇到readonly option is set
的错误。这时输入命令
:set noreadonly
之后再输入wq
就可以保存退出了。
这时重新开一个session
,就能使用node
命令和npm
命令了。
补充一点,cd ~
会直接进入当前用户的文件夹目录。所以cnpvg50830120:/
和cnpvg50830120:~
的区别就在于,前者是绝对路径,是顶级目录,后者是当前用户所在的文件夹目录。
首先查看当前Linux的版本信息再决定用哪个安装。
$ cat /etc/SuSE-release
我的那个server是SUSE Linux Enterprise 11 SP3,所以用zypper
命令安装Git
。
$ zypper search git // 搜索当前REPOSITORY的安装列表里是否有git安装文件$ zypper install git
REPOSITORY是负责定义安装那些工具的来源。这些来源可以通过命令来看。
$ zypper lr // 列出所有的源$ zypper ar [options] <URI> <alias> // 添加源
定义源其实就是定义一些安装时下载的来源,所以使用zypper install [name]
时需要联网。
$ curl www.baidu.com // 检查是否能联网访问baidu.com
其他的一些命令。
$ zypper mr // 删除源$ zypper rr // 导入导出源
安装好之后,就可以使用Git
了。
在服务器上新建一个项目目录,用于clone项目代码。
$ mkdir [WORKSPACE_PATH/PROJECT_NAME]$ git clone [GIT_URL] [PROJECT_PATH]
这时可能会报错,说Could not read from remote repository
。
这个错有可能是因为当前机器上的ssh key没设定或者没有添加到Github账号中做关联。
$ ssh-keygen -t rsa -C "USER_NAME" // 注意。USER_NAME为Github用户名
之后可能会问.ssh
要生成到哪个文件夹下,我全部使用默认。
生成ssh key
之后,将id_rsa.pub
文件内容添加到Github
账号中。之后就可以正常的clone git repository。
每次更新代码时,执行git命令。
$ cd [PROJECT_PATH]$ git fetch origin$ git rebase [BRANCH_NAME]
]]>回调函数
事件监听
发布 / 订阅
Promise对象
一个任务分成几段之后,可以先执行第一段,然后转而执行其他任务,等准备好之后再执行第二段。
一个任务分成几段之后,段与段之间不能插入其他任务,必须连续执行,这段时间其他任务只能干等着。
任务分成几段之后,第一段中的function定义了第二段函数的参数,这时第二个参数就是回调函数。
new Promise().then().then()...
多个线程互相协作,完成异步任务。
1 | function *asnycJob() { |
奥妙就在于yield命令的使用。
看一个案例。
1 | var fetch = require('node-fetch'); |
例子里用Generator函数实现了异步执行,但是连续调用g.next()
显得流程管理上不是那么方便。
传值调用还是传名调用。传值调用明显更通俗易懂,而且简单实用。而传名调用可能会有性能上的优化。
1 | function f(a, b){ |
传名调用实现就是将参数放到一个临时函数中,再将这个临时函数传入函数体。这个临时函数就是Thunk函数。
1 | function f(m){ |
Javascript是执行的传值调用。Thunk将多参数函数替换成单参数版本,只接受回调函数作为参数。
1 | // 正常版本的readFile(多参数版本) |
一个简单的Thunk函数转换器。
1 | var Thunk = function(fn){ |
正常情况来说,Generator函数封装的异步操作可以如下。
1 | var fs = require('fs'); |
repository
之后,我是把项目的ssh url
发出来给的大家,这其中遇到了一些问题。开发人员使用ssh的url进行clone的时候,发现会报错,然后换成https的url就可以。
网上查了下发现,使用https url克隆对初学者来说会比较方便,复制https url然后到git Bash里面直接用clone命令克隆到本地就好了,但是每次fetch和push代码都需要输入账号和密码,这也是https方式的麻烦之处。而使用SSH url克隆却需要在克隆之前先配置和添加好SSH key,因此,如果你想要使用SSH url克隆的话,你必须是这个项目的拥有者。否则你是无法添加SSH key的,另外ssh默认是每次fetch和push代码都不需要输入账号和密码,如果你想要每次都输入账号密码才能进行fetch和push也可以另外进行设置。
使用https的时候可能会遇到unable to get local issuer certificate
错误,这时需要设置
git config --global http.sslVerify false
这个有多种解决方案。
这种方式感觉不太适合协同开发
Github
上建立一个repository
供大家协同开发,有人提出要可以进行code review
,之前在项目中用到过Gerrit
,但是这种小型项目感觉用Gerrit
有点大材小用。总之,先尝试了一下Fork的使用。
USERA
在Github
上新建一个repository
,之后USERB
去Fork
这个repository
。然后clone
到本地。
$ git clone git@github.wdf.sap.corp:USERB/RepositoryName.git
这里用的是ssh url。
Clone
下来之后,USERB
可以进行编辑,比如新建一个err.md
文件,并填写一些内容。
$ git add filename$ git commit
之后,USERB
进行push
操作,注意,此时push
是到了USERB
的repository
。
如果想要让项目的原作者注意到,USERB
需要发起一个pull request
。
创建一个pull request
,base
是远程的USERA的repository
,head
是USERB
的repository
。
填写comment
,这时就可以等待原作者的同意并并入到原作者的项目中去了。
此时大家可能会注意到一个问题,我们如何保持我们fork出来的项目和原项目同步呢?
$ git fetch [origin]
这条命令没有错,但是只是从USERB
的repository
的远程来拉代码,并不能解决同步问题。
这时我们需要设定一个新的remote
源。
$ git remote -v
查看当前的remote
。
origin git@github.wdf.sap.corp:USERB/RepositoryName.git (fetch)origin git@github.wdf.sap.corp:USERB/RepositoryName.git (push)
新加一个新的remote
。
$ git remote add upstream git@github.wdf.sap.corp:USERA/RepositoryName.git
此时我们看到,remote
变成了四个。
origin git@github.wdf.sap.corp:USERB/RepositoryName.git (fetch)origin git@github.wdf.sap.corp:USERB/RepositoryName.git (push)upstream git@github.wdf.sap.corp:USERA/RepositoryName.git (fetch)upstream git@github.wdf.sap.corp:USERA/RepositoryName.git (push)
之后,我们如果要保持代码同步的话,可以直接输入命令。
$ git fetch upstream
参考文章: Fork and Pull, Fork a Repo
]]>