Giter Site home page Giter Site logo

divlab.github.io's Introduction

aa

divlab.github.io's People

Contributors

divlab avatar

Watchers

 avatar  avatar

divlab.github.io's Issues

关于rem的计算问题

我的理解
给最大宽度max定基数,例如100px(也可以是任意值,常见的是:设计稿/10 ),如果小了, 到底小多少呢,client/max * 100px 这就是html font-size
当计算的时候 w/这个基数(rem) =x,也即是 x*基数 rem 为真实的设计稿的width

使用的时候 设计稿/得出值,想问为什么?因为xrem X得出值,就是真实的渲染尺寸

设置npm的registry

1.原npm地址

npm config set registry http://registry.npmjs.org

2.设置国内镜像

a.通过config命令

npm config set registry https://registry.npm.taobao.org
npm info underscore (如果上面配置正确这个命令会有字符串response)

b.命令行指定

npm --registry https://registry.npm.taobao.org info underscore

c.编辑 ~/.npmrc 加入下面内容

registry = https://registry.npm.taobao.org

3.使用nrm管理registry地址

a.下载nrm

npm install -g nrm

b.添加registry地址

nrm add npm http://registry.npmjs.org

nrm add taobao https://registry.npm.taobao.org

c.切换npm registry地址

nrm use taobao

nrm use npm

对称加密 /非对称加密

对称加密:通信双方使用相同的密钥进行加密。特点是加密速度快,但是缺点是需要保护好密钥,如果密钥泄露的话,那么加密就会被别人破解。常见的对称加密有 AES,DES 算法。

非对称加密:它需要生成两个密钥:公钥(Public Key)和私钥(Private Key)。

公钥顾名思义是公开的,任何人都可以获得,而私钥是私人保管的,相信大多程序员已经对这种算法很熟悉了。

我们提交代码到 Github 的时候,就可以使用 SSH key:在本地生成私钥和公钥,私钥放在本地 .ssh 目录中,公钥放在 Github 网站上。

这样每次提交代码,不用麻烦的输入用户名和密码了,Github 会根据网站上存储的公钥来识别我们的身份。公钥负责加密,私钥负责解密;或者,私钥负责加密,公钥负责解密。

这种加密算法安全性更高,但是计算量相比对称加密大很多,加密和解密都很慢。

常见的非对称算法有 RSA。SSL/TLS 是利用了对称加密和非对称加密的特点。

git push --set-upstream origin dev

用git在本地新建一个分支后,需要做远程分支关联。如果没有关联,git会在下面的操作中提示你显示的添加关联。

关联目的是在执行git pull, git push操作时就不需要指定对应的远程分支,你只要没有显示指定,git pull的时候,就会提示你。

git branch --set-upstream-to=origin/remote_branch your_branch

其中,origin/remote_branch是你本地分支对应的远程分支;your_branch是你当前的本地分支。

Git 2.0版本之前,默认采用matching方法,现在改为默认采用simple方式。

simple方式:如果当前分支只有一个追踪分支,那么git push origin到主机时,可以省略主机名。

matching方式:如果当前分支与多个主机存在追踪关系,那么git push --set-upstream origin master(省略形式为:git push -u origin master)将本地的master分支推送到origin主机(--set-upstream选项会指定一个默认主机),同时指定该主机为默认主机,后面使用可以不加任何参数使用git push。

原因:
1.可能是因为当前版本小于2.0
2.若不是版本问题则是因为当前分支与多个主机存在对应关系,而当前未指定对应关系
此时检查一下项目下.git的.config文件中是否缺少该分支指定信息

解决:
1.按照提示在命令行中输入git push --set-upstream origin dev

FETCH_HEAD //当前节点 git pull 同步

如何使用Git来撤销(几乎)任何事情

任何一个版本控制系统中,最有用的特性之一莫过于 “撤销(undo)”操作。在Git中,“撤销”有很多种含义。

当你完成了一次新的提交(commit),Git会及时存储当前时刻仓库(repository)的快照(snapshot);你能够使用Git将项目回退到任何之前的版本。

下文中,我将列举几个常见的、需要“撤销”的场景,并且展示如何使用Git来完成这些操作。

一、撤销一个公共修改 Undo a "public" change

场景:你刚刚用git push将本地修改推送到了GitHub,这时你意识到在提交中有一个错误。你想撤销这次提交。

使用撤销命令:git revert

发生了什么:git revert将根据给定SHA的相反值,创建一个新的提交。如果旧提交是“matter”,那么新的提交就是“anti-matter”——旧提交中所有已移除的东西将会被添加进到新提交中,旧提交中增加的东西将在新提交中移除。

这是Git最安全、也是最简单的“撤销”场景,因为这样不会修改历史记录——你现在可以git push下刚刚revert之后的提交来纠正错误了。

二、修改最近一次的提交信息 Fix the last commit message

场景:你只是在最后的提交信息中敲错了字,比如你敲了git commit -m "Fxies bug #42",而在执行git push之前你已经意识到你应该敲"Fixes bug #42"。

使用撤销命令:git commit –amend或git commit --amend -m "Fixes bug #42"

发生了什么:git commit –amend将使用一个包含了刚刚错误提交所有变更的新提交,来更新并替换这个错误提交。由于没有staged的提交,所以实际上这个提交只是重写了先前的提交信息。

三、撤销本地更改 Undo "local" changes

场景:当你的猫爬过键盘时,你正在编辑的文件恰好被保存了,你的编辑器也恰在此时崩溃了。此时你并没有提交过代码。你期望撤销这个文件中的所有修改——将这个文件回退到上次提交的状态。

使用撤销命令:git checkout --

发生了什么:git checkout将工作目录(working directory)里的文件修改成先前Git已知的状态。你可以提供一个期待回退分支的名字或者一个确切的SHA码,Git也会默认检出HEAD——即:当前分支的上一次提交。

注意:用这种方法“撤销”的修改都将真正的消失。它们永远不会被提交。因此Git不能恢复它们。此时,一定要明确自己在做什么!(或许可以用git diff来确定)

四、重置本地修改 Reset "local" changes

场景:你已经在本地做了一些提交(还没push),但所有的东西都糟糕透了,你想撤销最近的三次提交——就像它们从没发生过一样。

使用撤销命令:git reset或git reset --hard

发生了什么:git reset将你的仓库纪录一直回退到指定的最后一个SHA代表的提交,那些提交就像从未发生过一样。默认情况下,git reset会保留工作目录(working directory)。这些提交虽然消失了,但是内容还在磁盘上。这是最安全的做法,但通常情况是:你想使用一个命令来“撤销”所有提交和本地修改——那么请使用--hard参数吧。

五、撤销本地后重做 Redo after undo "local"

场景:你已经提交了一些内容,并使用git reset –hard撤销了这些更改(见上面),突然意识到:你想还原这些修改!

使用撤销命令:git reflog和git reset, 或者git checkout

发生了什么:git reflog是一个用来恢复项目历史记录的好办法。你可以通过git reflog恢复几乎任何已提交的内容。

你或许对git log命令比较熟悉,它能显示提交列表。git reflog与之类似,只不过git reflog显示的是HEAD变更次数的列表。
image

一些说明:

  1. 只有HEAD会改变。当你切换分支时,用git commit提交变更时,或是用git reset撤销提交时,HEAD都会改变。但当你用git checkout --时, HEAD不会发生改变。(就像上文提到的情形,那些更改根本就没有提交,因此reflog就不能帮助我们进行恢复了)

  2. git reflog不会永远存在。Git将会定期清理那些“不可达(unreachable)”的对象。不要期望能够在reflog里找到数月前的提交记录。

  3. reflog只是你个人的。你不能用你的reflog来恢复其他开发者未push的提交。

因此,怎样合理使用reflog来找回之前“未完成”的提交呢?这要看你究竟要做什么:

  1. 如果你想恢复项目历史到某次提交,那请使用git reset --hard

  2. 如果你想在工作目录(working direcotry)中恢复某次提交中的一个或多个文件,并且不改变提交历史,那请使用git checkout--

  3. 如果你想确切的回滚到某次提交,那么请使用git cherry-pick。

六、与分支有关的那些事 Once more, with branching

场景:你提交了一些变更,然后你意识到你正在master分支上,但你期望的是在feature分支上执行这些提交。

使用撤销命令:git branch feature, git reset --hard origin/master, 和 git checkout feature

发生了什么:你可能用的是git checkout -b来建立新的分支,这是创建和检出分支的便捷方法——但实际你并不想立刻切换分支。git branch feature会建立一个叫feature的分支,这个分支指向你最近的提交,但是你还停留在master分支上。

git reset --hard将master回退至origin/master,并忽略所有新提交。别担心,那些提交都还保留在feature上。

最后,git checkout将分支切换到feature,这个分支原封不动的保留了你最近的所有工作。

七、事半功倍处理分支 Branch in time saves nine

场景:你基于master新建了一个feature分支,但是master分支远远落后与origin/master。现在master分支与origin/master同步了,你期望此刻能在feature下立刻commit代码,并且不是在远远落后master的情况下。

使用撤销命令:git checkout feature和git rebase master

发生了什么:你也许已经敲了命令:git reset(但是没用--hard,有意在磁盘上保存这些提交内容),然后敲了git checkout -b,之后重新提交更改,但是那样的话,你将失去本地的提交记录。不过,一个更好的方法:

使用git rebase master可以做到一些事情:

1.首先,它定位你当前检出分支和master之间的共同祖先节点(common ancestor)。

2.然后,它将当前检出的分支重置到祖先节点(ancestor),并将后来所有的提交都暂存起来。

3.最后,它将当前检出分支推进至master末尾,同时在master最后一次提交之后,再次提交那些在暂存区的变更。

八、批量撤销/找回 Mass undo/redo

场景:你开始朝一个既定目标开发功能,但是中途你感觉用另一个方法更好。你已经有十几个提交,但是你只想要其中的某几个,其他的都可以删除不要。

使用撤销命令:git rebase -i

发生了什么:-i将rebases设置为“交互模式(interactive mode)”。rebase开始执行的操作就像上文讨论的一样,但是在重新执行某个提交时,它会暂停下来,让你修改每一次提交。

rebase –i将会打开你的默认文本编辑器,然后列出正在执行的提交,就像这样:
image

前两列最关键:第一列是选择命令,它会根据第二列中的SHA码选择相应的提交。默认情况下,rebase –i会认为每个更改都正通过pick命令被提交。

要撤销一个提交,直接在编辑器删除对应的行就可以了。如果在你的项目不再需要这些错误的提交,你可以直接删除上图中的第1行和3-4行。

如果你想保留提交但修改提交信息,你可以使用reword命令。即,将命令关键字pick换成reword(或者r)。你现在可能想立刻修改提交消息,但这么做不会生效——rebase –i将忽略SHA列后的所有东西。现有的提交信息会帮助我们记住0835fe2代表什么。当你敲完rebase –i命令后,Git才开始提示你重写那些新提交消息。

如果你需要将2个提交合并,你可以用squash或者fixup命令,如下图:
image

squash和fixup都是“向上”结合的——那些用了这些合并命令(编者按:指squash、fixup)的提交,将会和它之前的提交合并:上图中,0835fe2和6943e85将会合并成一个提交,而38f5e4e和af67f82将会合并成另一个提交。

当你用squash时,Git将会提示是否填写新的提交消息;fixup则会给出列表中第一个提交的提交信息。在上图中,af67f82是一个“Ooops”信息,因为这个提交信息已经同38f5e4e一样了。但是你可以为0835fe2和6943e85合并的新提交编写提交信息。

当你保存并退出编辑器时,Git将会按照从上到下的顺序执行你的提交。你可以在保存这些提交之前,修改提交的执行顺序。如果有需要,你可以将af67f82和0835fe2合并,并且可以这样排序:
image

九、修复早先的提交 Fix an earlier commit

场景:之前的提交里落下了一个文件,如果先前的提交能有你留下的东西就好了。你还没有push,并且这个提交也不是最近的提交,因此你不能用commit –amend。

使用撤销命令:git commit --squash和git rebase --autosquash -i

发生了什么:git commit –squash将会创建一个新的提交,该提交信息可能像这样“squash! Earlier commit”。(你也可以手写这些提交信息,commit –squash只是省得让你打字了)。

如果你不想为合并的提交编写信息,也可以考虑使用命令git commit --fixup。这种情况下,你可能会使用commit --fixup,因为你仅希望在rebase中使用之前的提交信息。

rebase --autosquash –i将会启动rebase交互编辑器,编辑器会列出任何已完成的squash!和fixup!提交,如下图:

当使用--squash和–fixup时,你或许记不清你想修复的某个提交的SHA码——只知道它可能在一个或五个提交之前。你或许可以使用Git的^和操作符手动找回。HEAD^表示HEAD的前一次提交。HEAD4表示HEAD前的4次提交,加起来总共是前5次提交。

十、停止跟踪一个已被跟踪的文件 Stop tracking a tracked file

场景:你意外将application.log添加到仓库中,现在你每次运行程序,Git都提示application.log中有unstaged的提交。你在.gitignore中写上”*.log”,但仍旧没用——怎样告诉Git“撤销”跟踪这个文件的变化呢?

使用撤销命令:git rm --cached application.log

发生了什么:尽管.gitignore阻止Git跟踪文件的变化,甚至是之前没被跟踪的文件是否存在,但是,一旦文件被add或者commit,Git会开始持续跟踪这个文件的变化。类似的,如果你用git add –f来“强制”add,或者覆盖.gitignore,Git还是会继续监视变化。所以以后最好不要使用–f来add .gitignore文件。

如果你希望移除那些应当被忽略的文件,git rm –cached可以帮助你,并将这些文件保留在磁盘上。因为这个文件现在被忽略了,你将不会在git status中看到它,也不会再把这个文件commit了。

关于npm包管理相关

所有npm模块都有name,有的模块的name还有scope。scope的命名规则和name差不多,同样不能有url非法字符或者下划线点符号开头。scope在模块name中使用时,以@开头,后边跟一个/ 。package.json中,name的写法如下:

"name": "@somescope/somepackagename"
scope是一种把相关的模块组织到一起的一种方式,也会在某些地方影响npm对模块的处理。npm公共仓库支持带有scope的的模块,同时npm客户端对没有scope的模块也是向后兼容的,所以可以同时使用两者。

安装带有scope的模块
带有scope的模块安装在一个子目录中,如果正常的模块安装在node_modules/packagename目录下,那么带有scope的模块安装在node_modules/@myorg/packagename目录下,@myorg就是scope前面加上了@符号,一个scope中可以包含很多个模块。

安装一个带有scope的模块

npm install @myorg/mypackage
在package.json中写明一个依赖:

"dependencies": {
"@myorg/mypackage": "^1.3.0"
}
如果@符号被省略,那么npm会尝试从github中安装模块,在npm install命令的文档中有说明
https://docs.npmjs.com/cli/install

使用带有scope的模块
在代码中require一个含有scope的模块:

require(‘@myorg/mypackage’)
nodejs在解析socpe模块的时候,并没有把它当做一个有什么蹊跷的东西来处理,仅仅是按照路径去找@myorg目录下的mypackage模块。

发布带有scope的模块
带有scope的模块可以被发布到任意支持socpe模块的npm仓库,包括npm公共仓库,公共仓库从2015-04-19就开始支持带有scope的模块了。

如果有必要,可以把某个scope关联到某个仓库,见下面的说明。

如果要发布一个公共socpe模块,你必须在最开始发布的时候指定–access public。这样会让模块能被公开使用,就像在publish之后运行了 npm access public命令一样。

如果要发布私有模块,那么你必须有一个npm私有模块账户,可以选择自己搭建一个npm服务,或者直接使用官方的。发布私有模块的命令:

npm publish

或者

npm publish –access restricted
即发布socpe模块时,默认就是restricted的。这些在npm publish文档里边可以看到详细说明。 https://docs.npmjs.com/cli/publish

将一个scope和一个仓库关联
scope可以和一些自己搞的npm仓库关联起来。这样你就可以同时使用npm公共仓库和一些其他的私有仓库中的模块,例如企业npm。可以用npm login把scope关联到一个仓库:

npm login –registry=http://reg.example.com –scope=@myco
scope和仓库可以是一个多对一的关系:一个仓库里边可以放多个scope,但是一个scope同时只能放在一个仓库中。也可以用npm config把scope关联到一个仓库:

npm config set @myco:registry http://reg.example.com
当一个scope关联到一个私有仓库之后,该scope下的模块在npm install的时候都会从它关联的仓库中获取模块,而不是npm配置的仓库,发布的时候也是同样的道理,会发布到它关联的仓库而不是npm配置的仓库。

作者:wavesnow
链接:https://www.jianshu.com/p/ac5b5f65320b
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

浏览器中如何实现拷贝到剪贴板功能

在一些博客系统,如掘金的博客中,可以复制代码,它是如何实现的

它一般可以使用第三方库 clipboard-copy 来实现,源码很简单,可以读一读

长按识别二维码查看原文
https://github.com/feross/clipboard-copy/blob/master/index.js
标题:clipboard-copy

目前最为推荐的方式是使用 Clipboard API 进行实现

navigator.clipboard.writeText(text)
而对于一些不支持 Clipboard API 的浏览器,使用以下 API 进行复制

选中: Selection API

复制: document.execCommand (已被废弃)

选中: Selection API/Range API
选中主要利用了 Selection API 与 Range API

长按识别二维码查看原文

https://developer.mozilla.org/en-US/docs/Web/API/Selection
标题:Selection API
图片
选中的代码如下

const selection = window.getSelection();
const range = document.createRange();

// RangeAPI: 制造区域
range.selectNodeContents(element);

// Selection: 选中区域
selection.addRange(range);

selectedText = selection.toString();
取消选中的代码如下

window.getSelection().removeAllRanges();
它有现成的第三方库可以使用: select.js

长按识别二维码查看原文

https://github.com/zenorocha/select
标题:select.js
图片
复制: execCommand
复制就比较简单了,execCommand

document.execCommand('copy')

查看自己本机的ssh

想要查看自己本机的ssh到底是多少
输入下面的命令就可以:

cat .ssh/id_rsa.pub
1
查看完ssh还想复制ssh内容,一个简单的命令就可以搞定,
这个命令是可以复制本机的ssh到剪切板中,在其他地方com+v就可以粘贴ssh了

cat .ssh/id_rsa.pub | pbcopy
————————————————
版权声明:本文为CSDN博主「smile&$」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/smile_68/article/details/112470696

ssh-keygen -t rsa -b 4096 -C "邮箱"

ssh-keygen -t rsa -b 4096 -C "邮箱":这条命令的目的是为了让本地机器ssh登录远程机器上的GitHub账户无需输入密码。将这条命令分解:
1、ssh-keygen:
SSH 为 Secure Shell 的缩写,SSH 为建立在应用层基础上的安全协议。SSH 是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。

从客户端来看,SSH提供两种级别的安全验证:

    第一种级别(基于口令的安全验证):只要你知道自己帐号和口令,就可以登录到远程主机。所有传输的数据都会被加密,但是不能保证你正在连接的服务器就是你想连接的服务器。可能会有别的服务器在冒充真正的服务器,也就是受到“中间人”这种方式的攻击。

    第二种级别(基于密匙的安全验证)ssh-keygen:需要依靠密匙,你必须为自己创建一对密匙,并把公用密匙放在需要访问的服务器上。如果你要连接到SSH服务器上,客户端软件就会向服务器发出请求,请求用你的密匙进行安全验证。服务器收到请求之后,先在该服务器上你的主目录下寻找你的公用密匙,然后把它和你发送过来的公用密匙进行比较。如果两个密匙一致,服务器就用公用密匙加密“质询”(challenge)并把它发送给客户端软件。客户端软件收到“质询”之后就可以用你的私人密匙解密再把它发送给服务器。用这种方式,你必须知道自己密匙的口令。但是,与第一种级别相比,第二种级别不需要在网络上传送口令。第二种级别不仅加密所有传送的数据,而且“中间人”这种攻击方式也是不可能的(因为他没有你的私人密匙)。但是整个登录的过程可能需要10秒 。

    ssh-keygen有很多的参数,比如这里的-t -b -C都是他的一些参数。

2、-t rsa:t是type的缩写
-t即指定密钥的类型,密钥的类型有两种,一种是RSA,一种是DSA:

    RSA:RSA加密算法是一种非对称加密算法,是由三个麻省理工的牛人弄出来的,RSA是他们三个人姓的开头首字母组合。

    DSA:Digital Signature Algorithm (DSA)是Schnorr和ElGamal签名算法的变种。

为了让两个linux机器之间使用ssh不需要用户名和密码。所以采用了数字签名RSA或者DSA来完成这个操作。ssh-keygen默认使用rsa密钥,所以不加-t rsa也行,如果你想生成dsa密钥,就需要加参数-t dsa。

3、-b 4096:b是bit的缩写
-b 指定密钥长度。对于RSA密钥,最小要求768位,默认是2048位。命令中的4096指的是RSA密钥长度为4096位。

DSA密钥必须恰好是1024位(FIPS 186-2 标准的要求)。

4、-C "邮箱":C是comment的缩写
-C表示要提供一个新注释,用于识别这个密钥,所以“”里面不一定非要填邮箱,可以是任何内容,邮箱仅仅是识别用的key

总结:当你创建ssh的时候:-t 表示密钥的类型 ,-b表示密钥的长度,-C 用于识别这个密钥的注释 ,这个注释你可以输入任何内容,很多网站和软件用这个注释作为密钥的名字

git pull 默认拉取远端其他分支问题

今天工作中遇见了一个问题:执行git pull 命令时,默认合并了远端的某个分支,经过查阅资料发现是git的配置问题。

如图所示:

git 查看远端主机详细配置信息

git remote show origin

通过查看配置信息发现:我的本地分支在执行git pull命令的时候默认拉取的远端的develop分支,导致pull命令合并了远端的develop分支

本地关联远程分支命令:

git branch --set-upstream

修改关联

git branch --set-upstream-to origin/分支名

再次执行 git remote show origin 发现pull配置信息已成功修改

mac下安装rar解压工具

第一、下载安装包 RAR for Mac OS X,下载地址

第二、解压压缩包,cd Downloads/rar 。

第三、使用以下命令安装 rar 和 unrar:

1、sudo install -c -o $USER rar /usr/local/bin
2、sudo install -c -o $USER unrar /usr/local/bin
第四、命令:

解压:unrar x fileName.rar(files with full path) dirName
压缩:rar a fileName.rar(files with full path) dirName

作者:懶仁
链接:https://www.jianshu.com/p/e001591f5366
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

JavaScript 模块化方案和工具

模块化是大型前端项目的必备要素。JavaScript 从诞生至今,出现过各种各样的模块化方案,让我们一起来盘点下吧。

IIFE 模块

默认情况下,在浏览器宿主环境里定义的变量都是全局变量,如果页面引用了多个这样的 JavaScript 文件,很容易造成命名冲突。

// 定义全局变量
let count = 0;
const increase = () => ++count;
const reset = () => {
    count = 0;
    console.log("Count is reset.");
};

// 使用全局变量
increase();
reset();

为了避免全局污染,可以用匿名函数包裹起来,这就是最简单的 IIFE 模块(立即执行的函数表达式):

// 定义 IIFE 模块
const iifeCounterModule = (() => {
    let count = 0;
    return {
        increase: () => ++count,
        reset: () => {
            count = 0;
            console.log("Count is reset.");
        }
    };
})();

// 使用 IIFE 模块
iifeCounterModule.increase();
iifeCounterModule.reset();

IIFE 只暴露了一个全局的模块名,内部都是局部变量,大大减少了全局命名冲突。

每个 IIFE 模块都是一个全局变量,这些模块通常有自己的依赖。可以在模块内部直接使用依赖的全局变量,也可以把依赖作为参数传给 IIFE:

// 定义带有依赖的 IIFE 模块
const iifeCounterModule = ((dependencyModule1, dependencyModule2) => {
    let count = 0;
    return {
        increase: () => ++count,
        reset: () => {
            count = 0;
            console.log("Count is reset.");
        }
    };
})(dependencyModule1, dependencyModule2);

一些流行的库在早期版本都采用这模式,比如大名鼎鼎的 jQuery(最新版本也开始用 UMD 模块了,后面会介绍)。

还有一种 IIFE,在 API 声明上遵循了一种格式,就是在模块内部提前定义了这些 API 对应的变量,方便 API 之间互相调用:

// Define revealing module.
const revealingCounterModule = (() => {
    let count = 0;
    const increase = () => ++count;
    const reset = () => {
        count = 0;
        console.log("Count is reset.");
    };

    return {
        increase,
        reset
    };
})();

// Use revealing module.
revealingCounterModule.increase();
revealingCounterModule.reset();

CommonJS 模块(Node.js 模块)

CommonJS 最初叫 ServerJS,是由 Node.js 实现的模块化方案。默认情况下,每个 .js 文件就是一个模块,模块内部提供了一个moduleexports变量,用于暴露模块的 API。使用 require 加载和使用模块。下面这段代码定义了一个计数器模块:

// 定义 CommonJS 模块: commonJSCounterModule.js.
const dependencyModule1 = require("./dependencyModule1");
const dependencyModule2 = require("./dependencyModule2");

let count = 0;
const increase = () => ++count;
const reset = () => {
    count = 0;
    console.log("Count is reset.");
};

exports.increase = increase;
exports.reset = reset;
// 或者这样:
module.exports = {
    increase,
    reset
};

使用这个模块:

// 使用 CommonJS 模块
const { increase, reset } = require("./commonJSCounterModule");
increase();
reset();
// 或者这样:
const commonJSCounterModule = require("./commonJSCounterModule");
commonJSCounterModule.increase();
commonJSCounterModule.reset();

在运行时,Node.js 会将文件内的代码包裹在一个函数内,然后通过参数传递exportsmodule变量和require函数。

// Define CommonJS module: wrapped commonJSCounterModule.js.
(function (exports, require, module, __filename, __dirname) {
    const dependencyModule1 = require("./dependencyModule1");
    const dependencyModule2 = require("./dependencyModule2");

    let count = 0;
    const increase = () => ++count;
    const reset = () => {
        count = 0;
        console.log("Count is reset.");
    };

    module.exports = {
        increase,
        reset
    };

    return module.exports;
}).call(thisValue, exports, require, module, filename, dirname);

// Use CommonJS module.
(function (exports, require, module, __filename, __dirname) {
    const commonJSCounterModule = require("./commonJSCounterModule");
    commonJSCounterModule.increase();
    commonJSCounterModule.reset();
}).call(thisValue, exports, require, module, filename, dirname);

AMD 模块(RequireJS 模块)

AMD(异步模块定义)也是一种模块格式,由 RequireJS 这个库实现。它通过define函数定义模块,并接受模块名和依赖的模块名作为参数。

// 定义 AMD 模块
define("amdCounterModule", ["dependencyModule1", "dependencyModule2"], 
      (dependencyModule1, dependencyModule2) => {
    let count = 0;
    const increase = () => ++count;
    const reset = () => {
        count = 0;
        console.log("Count is reset.");
    };

    return {
        increase,
        reset
    };
});

也用 require加载和使用模块:

require(["amdCounterModule"], amdCounterModule => {
    amdCounterModule.increase();
    amdCounterModule.reset();
});

跟 CommonJS 不同,这里的 requrie接受一个回调函数,参数就是加载好的模块对象。

AMD 的define函数还可以动态加载模块,只要给它传一个回调函数,并带上 require参数:

// Use dynamic AMD module.
define(require => {
    const dynamicDependencyModule1 = require("dependencyModule1");
    const dynamicDependencyModule2 = require("dependencyModule2");

    let count = 0;
    const increase = () => ++count;
    const reset = () => {
        count = 0;
        console.log("Count is reset.");
    };

    return {
        increase,
        reset
    };
});

AMD 模块还可以给define传递moduleexports,这样就可以在内部使用 CommonJS 代码:

// 定义带有 CommonJS 代码的 AMD 模块
define((require, exports, module) => {
    // CommonJS 代码
    const dependencyModule1 = require("dependencyModule1");
    const dependencyModule2 = require("dependencyModule2");

    let count = 0;
    const increase = () => ++count;
    const reset = () => {
        count = 0;
        console.log("Count is reset.");
    };

    exports.increase = increase;
    exports.reset = reset;
});

// 使用带有 CommonJS 代码的 AMD 模块
define(require => {
    // CommonJS 代码
    const counterModule = require("amdCounterModule");
    counterModule.increase();
    counterModule.reset();
});

UMD 模块

UMD(通用模块定义),是一种支持多种环境的模块化格式,可同时用于 AMD 和 浏览器(或者 Node.js)环境。

兼容 AMD 和浏览器全局引入:

((root, factory) => {
    // 检测是否存在 AMD/RequireJS 的 define 函数
    if (typeof define === "function" && define.amd) {
        // 如果是,在 define 函数内调用 factory
        define("umdCounterModule", ["deependencyModule1", "dependencyModule2"], factory);
    } else {
        // 否则为浏览器环境,直接调用 factory
        // 导入的依赖是全局变量(window 对象的属性)
        // 导出的模块也是全局变量(window 对象的属性)
        root.umdCounterModule = factory(root.deependencyModule1, root.dependencyModule2);
    }
})(typeof self !== "undefined" ? self : this, (deependencyModule1, dependencyModule2) => {
    // 具体的模块代码
    let count = 0;
    const increase = () => ++count;
    const reset = () => {
        count = 0;
        console.log("Count is reset.");
    };

    return {
        increase,
        reset
    };
});

看起来很复杂,其实就是个 IIFE。代码注释写得很清楚了,可以看看。
下面来看兼容 AMD 和 CommonJS(Node.js)模块的 UMD:

(define => define((require, exports, module) => {
    // 模块代码
    const dependencyModule1 = require("dependencyModule1");
    const dependencyModule2 = require("dependencyModule2");

    let count = 0;
    const increase = () => ++count;
    const reset = () => {
        count = 0;
        console.log("Count is reset.");
    };

    module.export = {
        increase,
        reset
    };
}))(// 判断 CommonJS 里的 module 变量和 exports 变量是否存在
    // 同时判断 AMD/RequireJS 的define 函数是否存在
    typeof module === "object" && module.exports && typeof define !== "function"
        ? // 如果是 CommonJS/Node.js,手动定义一个 define 函数
            factory => module.exports = factory(require, exports, module)
        : // 否则是 AMD/RequireJS,直接使用 define 函数
            define);

同样是个 IIFE,通过判断环境,选择执行对应的代码。

ES 模块(ES6 Module)

前面说到的几种模块格式,都是用到了各种技巧实现的,看起来眼花缭乱。终于,在 2015 年,ECMAScript 第 6 版(ES 2015,或者 ES6 )横空出世!它引入了一种全新的模块格式,主要语法就是 importepxort关键字。来看 ES6 怎么定义模块:

// 定义 ES 模块:esCounterModule.js 或 esCounterModule.mjs.
import dependencyModule1 from "./dependencyModule1.mjs";
import dependencyModule2 from "./dependencyModule2.mjs";

let count = 0;
// 具名导出:
export const increase = () => ++count;
export const reset = () => {
    count = 0;
    console.log("Count is reset.");
};
// 默认导出
export default {
    increase,
    reset
};

浏览器里使用该模块,在 script标签上加上type="module",表明引入的是 ES 模块。在 Node.js 环境中使用时,把扩展名改成 .mjs

// Use ES module.
//浏览器: <script type="module" src="esCounterModule.js"></script> or inline.

// 服务器:esCounterModule.mjs
import { increase, reset } from "./esCounterModule.mjs";
increase();
reset();
// Or import from default export:
import esCounterModule from "./esCounterModule.mjs";
esCounterModule.increase();
esCounterModule.reset();

浏览器如果不支持,可以加个兜底属性:

<script nomodule>
    alert("Not supported.");
</script>

ES 动态模块(ECMAScript 2020)

2020 年最新的 ESCMA 标准11版中引入了内置的 import函数,用于动态加载 ES 模块。import函数返回一个 Promise,在它的then回调里使用加载后的模块:

// 用 Promise API 加载动态 ES 模块
import("./esCounterModule.js").then(({ increase, reset }) => {
    increase();
    reset();
});

import("./esCounterModule.js").then(dynamicESCounterModule => {
    dynamicESCounterModule.increase();
    dynamicESCounterModule.reset();
});

由于返回的是 Promise,那肯定也支持await用法:

// 通过 async/await 使用 ES 动态模块
(async () => {
    // 具名导出的模块
    const { increase, reset } = await import("./esCounterModule.js");
    increase();
    reset();
    // 默认导出的模块
    const dynamicESCounterModule = await import("./esCounterModule.js");
    dynamicESCounterModule.increase();
    dynamicESCounterModule.reset();
})();

各平台对importexport和动态import的兼容情况如下:

image.pngimage.png

image.pngimage.png

System 模块

SystemJS 是一个 ES 模块语法转换库,以便支持低版本的 ES。例如,下面的模块是用 ES6 语法定义的:

// 定义 ES 模块
import dependencyModule1 from "./dependencyModule1.js";
import dependencyModule2 from "./dependencyModule2.js";
dependencyModule1.api1();
dependencyModule2.api2();

let count = 0;
// Named export:
export const increase = function () { return ++count };
export const reset = function () {
    count = 0;
    console.log("Count is reset.");
};
// Or default export:
export default {
    increase,
    reset
}

如果当前的运行环境(比如旧浏览器)不支持 ES6 语法,上面的代码就无法运行。一种方案是把上面的模块定义转换成 SystemJS 库的一个 API, System.register

// Define SystemJS module.
System.register(["./dependencyModule1.js", "./dependencyModule2.js"], 
                function (exports_1, context_1) {
    "use strict";
    var dependencyModule1_js_1, dependencyModule2_js_1, count, increase, reset;
    var __moduleName = context_1 && context_1.id;
    return {
        setters: [
            function (dependencyModule1_js_1_1) {
                dependencyModule1_js_1 = dependencyModule1_js_1_1;
            },
            function (dependencyModule2_js_1_1) {
                dependencyModule2_js_1 = dependencyModule2_js_1_1;
            }
        ],
        execute: function () {
            dependencyModule1_js_1.default.api1();
            dependencyModule2_js_1.default.api2();
            count = 0;
            // Named export:
            exports_1("increase", increase = function () { return ++count };
            exports_1("reset", reset = function () {
                count = 0;
                console.log("Count is reset.");
            };);
            // Or default export:
            exports_1("default", {
                increase,
                reset
            });
        }
    };
});

这样,import/export关键字就不见了。Webpack、TypeScript 等可以自动完成这样的转换(后面会讲)。

SystemJS 也支持动态加载模块:

// Use SystemJS module with promise APIs.
System.import("./esCounterModule.js").then(dynamicESCounterModule => {
    dynamicESCounterModule.increase();
    dynamicESCounterModule.reset();
});

Webpack 模块(打包 AMD,CJS,ESM)

Webpack 是个强大的模块打包工具,可以将 AMD、CommonJS 和 ES Module 格式的模块转换并打包到单个 JS 文件。

Babel 模块

Babel 是也个转换器,可将 ES6+ 代码转换成低版本的 ES。前面例子中的计数器模块用 Babel 转换后的代码是这样的:

// Babel.
Object.defineProperty(exports, "__esModule", {
    value: true
});
exports["default"] = void 0;
function _interopRequireDefault(obj) 
         { return obj && obj.__esModule ? obj : { "default": obj }; }

// Define ES module: esCounterModule.js.
var dependencyModule1 = _interopRequireDefault(require("./amdDependencyModule1"));
var dependencyModule2 = _interopRequireDefault(require("./commonJSDependencyModule2"));
dependencyModule1["default"].api1();
dependencyModule2["default"].api2();

var count = 0;
var increase = function () { return ++count; };
var reset = function () {
    count = 0;
    console.log("Count is reset.");
};

exports["default"] = {
    increase: increase,
    reset: reset
};

引入该模块的index.js将会转换成:

// Babel.
function _interopRequireDefault(obj) 
         { return obj && obj.__esModule ? obj : { "default": obj }; }

// Use ES module: index.js
var esCounterModule = _interopRequireDefault(require("./esCounterModule.js"));
esCounterModule["default"].increase();
esCounterModule["default"].reset();

以上是 Babel 的默认转换行为,它还可以结合其他插件使用,比如前面提到的 SystemJS。经过配置,Babel 可将 AMD、CJS、ES Module 转换成 System 模块格式。

TypeScript 模块

TypeScript 是 JavaScript 的超集,可以支持所有 JavaScript 语法,包括 ES6 模块语法。它在转换时,可以保留 ES6 语法,也可以转换成 AMD、CJS、UMD、SystemJS 等格式,取决于配置:

{
    "compilerOptions": {
        "module": "ES2020", // None, CommonJS, AMD, System, UMD, ES6, ES2015, ES2020, ESNext.
    }
}

TypeScript 还支持 modulenamespace关键字,表示内部模块。

module Counter {
    let count = 0;
    export const increase = () => ++count;
    export const reset = () => {
        count = 0;
        console.log("Count is reset.");
    };
}

namespace Counter {
    let count = 0;
    export const increase = () => ++count;
    export const reset = () => {
        count = 0;
        console.log("Count is reset.");
    };
}

都可以转换成 JavaScript 对象:

var Counter;
(function (Counter) {
    var count = 0;
    Counter.increase = function () { return ++count; };
    Counter.reset = function () {
        count = 0;
        console.log("Count is reset.");
    };
})(Counter || (Counter = {}));

总结

以上提到的各种模块格式是在 JavaScript 语言演进过程中出现的模块化方案,各有其适用环境。随着标准化推进,Node.js 和最新的现代浏览器都开始支持 ES 模块格式。如果要在旧环境中使用模块化,可以通过 Webpack、Babel、TypeScript、SystemJS 等工具进行转换。

HTTPS 方式接入github保存密码的方式

https 方式每次都要输入密码,按照如下设置即可输入一次就不用再手输入密码的困扰而且又享受 https 带来的极速

设置记住密码(默认15分钟):

git config --global credential.helper cache
如果想自己设置时间,可以这样做:

git config credential.helper 'cache --timeout=3600'
这样就设置一个小时之后失效

长期存储密码:

git config --global credential.helper store
增加远程地址的时候带上密码也是可以的。(推荐)

http://yourname:[email protected]/name/project.git

//深文件无法删除
git config —system core.longpaths true

git仓库删除所有提交历史记录,成为一个干净的新仓库

把旧项目提交到git上,但是会有一些历史记录,这些历史记录中可能会有项目密码等敏感信息。如何删除这些历史记录,形成一个全新的仓库,并且保持代码不变呢?

1.切换到新的分支
git checkout --orphan latest_branch

缓存所有文件(除了.gitignore中声名排除的)

git add -A

提交跟踪过的文件(Commit the changes)

git commit -am "commit message"

删除master分支(Delete the branch)

git branch -D master

5.重命名当前分支为master(Rename the current branch to master)
git branch -m master

6.提交到远程master分支 (Finally, force update your repository)
git push -f origin master

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.