{
"window.zoomLevel": 0, // 窗口大小比例
"workbench.colorTheme": "Default Dark+",
"diffEditor.wordWrap": "on",
"editor.wordWrap": "on",
"editor.fontSize": 14,
"editor.tabSize": 2,
"editor.formatOnSave": true, // eslint保存格式化
"eslint.enable": true,
"eslint.run": "onType",
"eslint.options": {
"extensions": [
".js",
".vue",
".jsx",
".tsx"
]
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"tabnine.experimentalAutoImports": true,
"editor.minimap.autohide": true,
"eslint.codeActionsOnSave.rules": null,
"editor.detectIndentation": false,
"settingsSync.ignoredExtensions": []
}
快速浏览 ES2015、ES2016、ES2017、ES2018 及以后的 JavaScript 新特性
function fn () {
let x = 0
if (true) {
let x = 1 // 只在这个`if`里面
}
}
const a = 1
let
是新的 var
。 常量(const
) 就像 let
一样工作,但不能重新分配。
请参阅:Let 和 const
const message = `Hello ${name}`
const str = `
hello
world
`
模板和多行字符串。 请参阅:模板字符串
let bin = 0b1010010
let oct = 0o755
请参阅:二进制和八进制文字
const byte = 2 ** 8
// 同: Math.pow(2, 8)
"hello".repeat(3)
"hello".includes("ll")
"hello".startsWith("he")
"hello".padStart(8) // " hello"
"hello".padEnd(8) // "hello "
"hello".padEnd(8, '!') // hello!!!
"\u1E9B\u0323".normalize("NFC")
Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false
Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
// 返回一个真实的数组
Array.from(document.querySelectorAll("*"))
// 类似于 new Array(...),但没有特殊的单参数行为
Array.of(1, 2, 3)
请参阅: 新方法
class Circle extends Shape {
constructor (radius) {
this.radius = radius
}
getArea () {
return Math.PI * 2 * this.radius
}
expand (n) {
return super.expand(n) * Math.PI
}
static createFromDiameter(diameter) {
return new Circle(diameter / 2)
}
}
原型的语法糖。 请参阅: 类
javascript 默认字段是公共的(public
),如果需要注明私有,可以使用(#
)
class Dog {
#name;
constructor(name) {
this.#name = name;
}
printName() {
//只能在类内部调用私有字段
console.log(`你的名字是${this.#name}`)
}
}
const dog = new Dog("putty")
//console.log(this.#name)
//Private identifiers are not allowed outside class bodies.
dog.printName()
class ClassWithPrivate {
static #privateStaticField;
static #privateStaticFieldWithInitializer = 42;
static #privateStaticMethod() {
// …
}
}
new Promise((resolve, reject) => {
if (ok) { resolve(result) }
else { reject(error) }
})
用于异步编程。 请参阅:Promises
promise
.then((result) => { ··· })
.catch((error) => { ··· })
promise
.then((result) => { ··· })
.catch((error) => { ··· })
.finally(() => {
/* 独立于成功/错误的逻辑 */
})
当承诺被履行或被拒绝时,处理程序被调用
Promise.all(···)
Promise.race(···)
Promise.reject(···)
Promise.resolve(···)
async function run () {
const user = await getUser()
const tweets = await getTweets(user)
return [user, tweets]
}
async
函数是使用函数的另一种方式。
请参阅:异步函数
const [first, last] = ['Nikola', 'Tesla']
let {title, author} = {
title: 'The Silkworm',
author: 'R. Galbraith'
}
支持匹配数组和对象。 请参阅:解构
const scores = [22, 33]
const [math = 50, sci = 50, arts = 50] = scores
// Result:
// math === 22, sci === 33, arts === 50
可以在解构数组或对象时分配默认值
function greet({ name, greeting }) {
console.log(`${greeting}, ${name}!`)
}
greet({ name: 'Larry', greeting: 'Ahoy' })
对象和数组的解构也可以在函数参数中完成
function greet({ name = 'Rauno' } = {}) {
console.log(`Hi ${name}!`);
}
greet() // Hi Rauno!
greet({ name: 'Larry' }) // Hi Larry!
function printCoordinates({ left: x, top: y }) {
console.log(`x: ${x}, y: ${y}`)
}
printCoordinates({ left: 25, top: 90 })
此示例将 x
分配给 left
键的值
for (let {title, artist} of songs) {
···
}
赋值表达式也在循环中工作
const { id, ...detail } = song;
使用 rest(...)
运算符单独提取一些键和对象中的剩余键
const options = {
...defaults,
visible: true
}
const options = Object.assign(
{}, defaults,
{ visible: true })
对象扩展运算符允许您从其他对象构建新对象。 请参阅:对象传播
const users = [
...admins,
...editors,
'rstacruz'
]
const users = admins
.concat(editors)
.concat([ 'rstacruz' ])
扩展运算符允许您以相同的方式构建新数组。 请参阅:扩展运算符
function greet (name = 'Jerry') {
return `Hello ${name}`
}
function fn(x, ...y) {
// y 是一个数组
return x * y.length
}
fn(...[1, 2, 3])
// 与 fn(1, 2, 3) 相同
Default(默认), rest, spread(扩展)。 请参阅:函数参数
setTimeout(() => {
···
})
readFile('text.txt', (err, data) => {
...
})
arr.map(n => n*2)
// 没有花括号 = 隐式返回
// 同: arr.map(function (n) { return n*2 })
arr.map(n => ({
result: n*2
}))
// 隐式返回对象需要在对象周围加上括号
类似函数,但保留了 this
。
请参阅:箭头函数
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
function foo({x, y = 5} = {}) {
console.log(x, y);
}
foo() // undefined 5
function foo() {}
foo.name // "foo"
function foo(a, b){}
foo.length // 2
module.exports = { hello, bye }
同下:
module.exports = {
hello: hello, bye: bye
}
请参阅:对象字面量增强
const App = {
start () {
console.log('running')
}
}
// 同: App = { start: function () {···} }
请参阅:对象文字增强
const App = {
get closed () {
return this.status === 'closed'
},
set closed (value) {
this.status = value ? 'closed' : 'open'
}
}
请参阅:对象字面量增强
let event = 'click'
let handlers = {
[`on${event}`]: true
}
// 同: handlers = { 'onclick': true }
请参阅:对象字面量增强
const fatherJS = { age: 57, name: "张三" }
Object.values(fatherJS)
// [57, "张三"]
Object.entries(fatherJS)
// [["age", 57], ["name", "张三"]]
import 'helpers'
// 又名: require('···')
import Express from 'express'
// 又名: const Express = require('···').default || require('···')
import { indent } from 'helpers'
// 又名: const indent = require('···').indent
import * as Helpers from 'helpers'
// 又名: const Helpers = require('···')
import { indentSpaces as indent } from 'helpers'
// 又名: const indent = require('···').indentSpaces
import
是新的 require()
。
请参阅:Module imports
export default function () { ··· }
// 又名: module.exports.default = ···
export function mymethod () { ··· }
// 又名: module.exports.mymethod = ···
export const pi = 3.14159
// 又名: module.exports.pi = ···
const firstName = 'Michael';
const lastName = 'Jackson';
const year = 1958;
export { firstName, lastName, year };
export * from "lib/math";
export
是新的module.exports
。
请参阅:Module exports
as
关键字重命名import {
lastName as surname // 导入重命名
} from './profile.js';
function v1() { ... }
function v2() { ... }
export { v1 as default };
// 等同于 export default v1;
export {
v1 as streamV1, // 导出重命名
v2 as streamV2, // 导出重命名
v2 as streamLatestVersion // 导出重命名
};
button.addEventListener('click', event => {
import('./dialogBox.js')
.then(dialogBox => {
dialogBox.open();
})
.catch(error => {
/* Error handling */
})
});
ES2020提案 引入 import()
函数
const main = document.querySelector('main')
import(`./modules/${someVariable}.js`)
.then(module => {
module.loadPageInto(main);
})
.catch(err => {
main.textContent = err.message;
});
ES2020 为 import
命令添加了一个元属性 import.meta
,返回当前模块的元信息
new URL('data.txt', import.meta.url)
Node.js 环境中,import.meta.url
返回的总是本地路径,即 file:URL
协议的字符串,比如 file:///home/user/foo.js
import json from "./package.json" assert {type: "json"}
// 导入 json 文件中的所有对象
const json =
await import("./package.json", { assert: { type: "json" } })
function* idMaker () {
let id = 0
while (true) { yield id++ }
}
let gen = idMaker()
gen.next().value // → 0
gen.next().value // → 1
gen.next().value // → 2
情况很复杂。 请参阅:Generators
let fibonacci = {
[Symbol.iterator]() {
let pre = 0, cur = 1;
return {
next() {
[pre, cur] = [cur, pre + cur];
return { done: false, value: cur }
}
}
}
}
for (var n of fibonacci) {
// 在 1000 处截断序列
if (n > 1000) break;
console.log(n);
}
用于迭代生成器和数组。 请参阅:For..of iteration
var gen = {};
gen[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...gen] // => [1, 2, 3]
Generator
函数赋值给 Symbol.iterator
属性,从而使得 gen
对象具有了 Iterator
接口,可以被 ...
运算符遍历了
function* gen() { /* some code */ }
var g = gen();
g[Symbol.iterator]() === g // true
gen
是一个 Generator
函数,调用它会生成一个遍历器对象g
。它的 Symbol.iterator
属性,也是一个遍历器对象生成函数,执行后返回它自己
创建一个新的本地存储库
$ git init [项目名称]
克隆存储库(代码仓库)
$ git clone <git_url>
将存储库克隆到指定目录
$ git clone <git_url> 指定目录
将存储库克隆到指定目录,并指定分支
$ git clone <git_url> -b <分支名称> 指定目录
在工作目录中显示修改后的文件,为您的下一次提交暂存
$ git status
暂存文件,准备提交
$ git add [file]
暂存所有更改的文件,准备提交
$ git add .
将所有暂存文件提交到版本化历史记录
$ git commit -m "commit message"
将所有跟踪的文件提交到版本化历史记录
$ git commit -am "commit message"
取消暂存文件,保留文件更改
$ git reset [file]
将所有内容恢复到最后一次提交
$ git reset --hard
已更改但未暂存内容的差异
$ git diff
已 commited 但尚未提交的内容的差异
$ git diff --staged
在指定分支之前应用当前分支的任何提交
$ git rebase [branch]
设置将附加到您的提交和标签的名称
$ git config --global user.name "name"
设置将附加到您的提交和标签 tags 的电子邮件地址
$ git config --global user.email "email"
启用 Git 输出的一些着色
$ git config --global color.ui auto
在文本编辑器中编辑全局配置文件
$ git config --global --edit
显示本地 repo
配置设置
$ git config --list
删除全局设置
$ git config --global --unset <entry-name>
列出所有本地分支
$ git branch
列出所有分支,本地和远程
$ git branch -av
切换到 my_branch
,并更新工作目录
$ git checkout my_branch
创建一个名为 new_branch
的新分支
$ git checkout -b new_branch
删除名为 my_branch
的分支
$ git branch -d my_branch
将分支 A
合并到分支 B
$ git checkout branchB
$ git merge branchA
标记当前提交
$ git tag my_tag
从远程分支中创建并切换到本地分支
$ git checkout -b <branch-name> origin/<branch-name>
<!--rehype:className=wrap-text-->
# 保存已修改和分阶段的更改
$ git stash
# 列出隐藏文件更改的堆栈顺序
$ git stash list
# 从存储堆栈顶部编写工作
$ git stash pop
# 丢弃存储堆栈顶部的更改
$ git stash drop
# 回到某个 stash 的状态
$ git stash apply <stash@{n}>
# 删除所有的 stash
$ git stash clear
显示当前活动分支的提交历史
$ git log
显示 branchA 上不在 branchB 上的提交
$ git log branchB..branchA
显示更改文件的提交,即使跨重命名
$ git log --follow [file]
显示 branchA 中的内容与 branchB 中的内容的差异
$ git diff branchB...branchA
以人类可读的格式显示 Git 中的任何对象
$ git show [SHA]
文件 .gitignore
指定了 Git
应该忽略的 未跟踪的 文件
:- | :-
:- | :-
行首 #
| 全行注释,不支持行尾类注释 (转义 \#
)
行首 !
| 否定模式 (转义 \!
)
**
| 匹配任意路径
*
| 匹配任意多个字符
?
| 匹配任意一个字符
doc/**
| 匹配 doc
文件夹下的全部内容
doc/**/a
| 匹配任意深度路径下的 a
文件或文件夹
/
| 表示路径分隔符,不区分操作系统
/
结尾 | 仅会匹配文件夹,否则会匹配文件和文件夹
空行 | 不匹配任何文件
行尾空格 | 默认被忽略,可使用\
进行转义
行首空格 | 被正常处理,不会被忽略
当前 .gitignore
文件定义规则的优先级高于上级路径 .gitignore
定义规则的优先级;后定义的规则优先级高于前面定义规则的优先级
# 忽略当前目录logs文件夹下的全部内容
/logs/
/logs/*
/logs/**
# 上述几条规则等效
# 忽略 Mac 系统文件,包括任意子路径下的同名文件(夹)
.DS_store
# 忽略 node_modules 文件夹,包括任意子路径下的同名文件夹
node_modules/
# 忽略任意子路径下build、target文件夹,
# 但不忽略src/main、src/test下的build、target文件夹
build/
!**/src/main/**/build/
!**/src/test/**/build/
target/
!**/src/main/**/target/
!**/src/test/**/target/
# 使用 ! 重新包含指定文件(夹)
!logs/.gitkeep
<!--rehype:className=wrap-text-->
# 从工作目录中删除文件并暂存删除
git rm <filename>
# 从版本控制中删除文件但在本地保留文件
git rm --cached <filename>
# 更改文件名并准备提交
git mv <filename-orig> <filename-renamed>
从该 Git 远程获取所有分支
$ git fetch [alias]
将远程分支合并到当前分支以使其保持最新状态
$ git merge [alias]/[branch]
# 没有快进
$ git merge --no-ff [alias]/[branch]
# 仅快进
$ git merge --ff-only [alias]/[branch]
将本地分支提交传输到远程存储库分支
$ git push [alias] [branch]
从跟踪远程分支获取并合并任何提交
$ git pull
将另一个分支的一个特定提交合并到当前分支
$ git cherry-pick [commit_id]
添加一个 git URL 作为别名
$ git remote add [alias] [url]
显示您设置的远程存储库的名称
$ git remote
显示远程存储库的名称和 URL
$ git remote -v
删除远程存储库
$ git remote rm [remote repo name]
更改 git repo 的 URL
$ git remote set-url origin [git_url]
从项目中删除文件并暂存删除以进行提交
$ git rm [file]
更改现有文件路径并暂存移动
$ git mv [existing-path] [new-path]
显示所有提交日志,并指示任何移动的路径
$ git log --stat -M
$ cat ~/.ssh/config
Host gitlab.com
# 直接使用 sh**socks 提供的 socks5 代理端口
ProxyCommand nc -X 5 -x 127.0.0.1:1080 %h %p
Host github.com
ProxyCommand nc -X 5 -x 127.0.0.1:1080 %h %p
<!--rehype:className=wrap-text-->
# 设置默认行为,以防人们没有设置 core.autocrlf
* text=auto
# 明确声明您希望始终规范化并在结帐时
# 转换为本机行结尾的文本文件
*.c text
*.h text
# 声明在结帐时始终以 CRLF 行结尾的文件
*.sln text eol=crlf
# 表示所有真正二进制且不应修改的文件
*.png binary
*.jpg binary
# 标记或取消标记要根据存储库的语言统计数据而
# 忽略或默认隐藏差异的路径
search/index.json linguist-generated=true
# 以下属性统计 SQL 文件
*.sql linguist-detectable=true
# 从统计信息中排除
docs/formatter.rb linguist-documentation=false
# 将它们从统计信息中排除
special-vendored-path/* linguist-vendored
# 将所有 .rb 文件检测为 Java 文件
*.rb linguist-language=Java
重命名为new
$ git branch -m <new>
$ git branch -m <old> <new> #重命名分支
推送并重置
$ git push origin -u <new>
删除远程分支
$ git push origin --delete <old> #方法1
$ git push origin :oldBranchName #方法2
按内容搜索更改
$ git log -S ' term in the source'
显示特定文件随时间的变化
$ git log -p <file_name>
打印出很酷的日志可视化
$ git log --pretty=oneline --graph --decorate --all
<!--rehype:className=wrap-text-->
列出所有分支及其上游
$ git branch -vv
快速切换到上一个分支
$ git checkout -
只获取所有远程分支
$ git branch -r
从另一个分支签出单个文件
$ git checkout <branch> -- <file>
删除本地存在远程不存在的分支
git remote prune origin
$ git commit -v --amend
重写最后的提交信息
git config core.fileMode false
不再将文件的权限变化视作改动
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status
# 查看git 的设置
$ git config --get core.ignorecase
# 设置大小写敏感
$ git config core.ignorecase false
# 远程有俩相同目录,通过这种方式清除掉,然后提交记录
$ git rm -r --cached <目录/文件>
$ git rebase -i HEAD~3
# 表示要修改当前版本的倒数第三次状态
# 将要更改的记录行首单词 pick 改为 edit
pick 96dc3f9 提交 commit 描述内容 1
pick f1cce8a 提交 commit 描述内容 2
pick 6293516 提交 commit 描述内容 3
# Rebase eeb03a4..6293516 onto eeb03a4
# (3 commands)
#
# Commands:
# p, pick = 使用提交
# r, reword = 使用提交,但编辑提交消息
# e, edit = 使用提交,但停止修改
# s, squash = 使用提交,但融合到先前的提交中
# f, fixup = 像 squash,但丢弃此提交的日志消息
# x, exec = 使用 shell 运行命令(该行的其余部分)
# d, drop = 删除提交
保存并退出,会弹出下面提示
# 您现在可以修改提交,使用
#
# git commit --amend
#
# 对更改感到满意后,运行
#
# git rebase --continue
#
# 1. 通过这条命令进入编辑更改 commit,保存退出
$ git commit --amend
# 2. 保存退出确认修改,继续执行下面命令,
$ git rebase --continue
# 如果修改多条记录反复执行上面两条命令直到完成所有修改
# 最后,确保没有人提交进行推送,最好不要加 -f 强制推送
$ git push -f origin master
# 撤销一条记录
$ git reset --hard HEAD~1
# 强制同步到远程仓库
$ git push -f origin HEAD:master
# 如果有的修改以及加入暂存区的话
$ git reset --hard
# 还原所有修改,不会删除新增的文件
$ git checkout .
# 下面命令会删除新增的文件
$ git clean -xdf
$ git rev-parse HEAD # e10721cb8859b2c
# 获取短 hash
$ git rev-parse --short HEAD # e10721c
$ git branch --merged master | grep -v '^\*\| master' | xargs -n 1 git branch -d
<!--rehype:className=wrap-text-->
# 切换到 B 分支
$ git checkout <B>
# 将 A 分支 <hash-id> 的内容 pick 到 B 分支
$ git cherry-pick <hash-id>
$ git fetch --all && git reset --hard origin/master
<!--rehype:className=wrap-text-->
抛弃本地所有的修改,回到远程仓库的状态
$ git update-ref -d HEAD
把所有的改动都重新放回工作区,并清空所有的 commit,这样就可以重新提交第一个 commit
了
$ git diff --name-only --diff-filter=U
输出工作区和暂存区的 different (不同)。
$ git diff
还可以展示本地仓库中任意两个 commit 之间的文件变动:
$ git diff <commit-id> <commit-id>
git diff --cached
$ git config --global core.quotepath false
$ git diff HEAD
输出工作区、暂存区 和本地最近的版本(commit)的different(不同)。
$ git branch --merged master | grep -v '^\*\| master' | xargs -n 1 git branch -d
<!--rehype:className=wrap-text-->
$ git branch -u origin/mybranch
或者在 push
时加上 -u
参数
git push origin/mybranch -u
关联之后,git branch -vv
就可以展示关联的远程分支名了, 同时推送到远程仓库直接:git push
,不需要指定远程仓库
$ git remote show origin
$ git describe --tags --abbrev=0
$ git blame <file-name>
blame
的意思为责怪
,你懂的。
$ git commit --amend --author='Author Name <email@address.com>'
<!--rehype:className=wrap-text-->
$ git remote set-url origin <URL>
$ git remote add origin <remote-url>
<!--rehype:className=wrap-text-->
$ git remote -v
$ git whatchanged --since='2 weeks ago'
$ git checkout <stash@{n}> -- <file-path>
<!--rehype:className=wrap-text-->
$ git ls-files -t
$ git ls-files --others
$ git ls-files --others -i --exclude-standard
<!--rehype:className=wrap-text-->
$ git bundle create <file> <branch-name>
$ git clone repo.bundle <repo-dir> -b <branch-name>
<!--rehype:className=wrap-text-->
新建一个分支,分支内容就是上面 git bundle create
命令导出的内容
$ git rebase --autostash
$ git fetch origin pull/<id>/head:<branch-name>
<!--rehype:className=wrap-text-->
$ git diff --word-diff
$ git clean -X -f
$ git status --ignored
$ git log Branch1 ^Branch2
$ git log --show-signature
$ git checkout --orphan <branch-name>
相当于保存修改,但是重写 commit 历史
$ git show <branch-name>:<file-name>
# 查看代理
$ git config --global http.proxy
$ git config --global https.proxy
$ git config --global socks.proxy
# 设置代理
# 适用于 privoxy 将 socks 协议转为 http 协议的 http 端口
$ git config --global http.proxy http://127.0.0.1:1080
$ git config --global https.proxy http://127.0.0.1:1080
$ git config --global socks.proxy 127.0.0.1:1080
# 取消代理
$ git config --global --unset http.proxy
$ git config --global --unset https.proxy
$ git config --global --unset socks.proxy
# 只对 github.com 设置代理
$ git config --global http.https://github.com.proxy socks5://127.0.0.1:1080
$ git config --global https.https://github.com.proxy socks5://127.0.0.1:1080
# 取消 github.com 代理
$ git config --global --unset http.https://github.com.proxy
$ git config --global --unset https.https://github.com.proxy
$ git clone --depth=1 https://github.com/user/repo.git
只会 clone
最近一次提交,将减少 clone
时间
关闭 track 指定文件的改动,也就是 Git 将不会在记录这个文件的改动
git update-index --assume-unchanged path/to/file
<!--rehype:className=wrap-text-->
恢复 track 指定文件的改动
git update-index --no-assume-unchanged path/to/file
<!--rehype:className=wrap-text-->
git for-each-ref --sort=-committerdate --format='%(refname:short)' refs/heads
最新的放在最上面
git log --all --grep='<given-text>'
通过 grep 查找,given-text: 所需要查找的字段
git reset <file-name>
不添加参数,默认是 -mixed
# 对于使用 git@ 协议的,可以配置 socks5 代理
# macOS 系统编辑 ~/.ssh/config 文件,添加这几行,设置 github 代理
Host github.com
ProxyCommand nc -X 5 -x 127.0.0.1:1080 %h %p
<!--rehype:className=wrap-text-->
username
需要改成自己的git log --author="username" --pretty=tformat: --numstat | awk \
'{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -
git log --format='%aN' | sort -u |\
while read name; do echo -en "$name\t";\
git log --author="$name" --pretty=tformat: --numstat | awk \
'{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -; done
这里是排名前十,也可以更改排名
git log --pretty='%aN' | sort | uniq -c | sort -k1 -n -r | head -n 10
git log --oneline | wc -l
<type>(<scope>): <short summary>
│ │ │
│ │ └─⫸ 紧凑简短的描述,无需大写,也不需要用句号结尾
│ │
│ └─⫸ Commit 范围: animations|bazel|benchpress|common|compiler|compiler-cli|core|
│ elements|forms|http|language-service|localize|platform-browser|
│ platform-browser-dynamic|platform-server|router|service-worker|
│ upgrade|zone.js|packaging|changelog|docs-infra|migrations|ngcc|ve|
│ devtools....
│
└─⫸ Commit 类型: build|ci|doc|docs|feat|fix|perf|refactor|test
website|chore|style|type|revert
| 类型 | 描述 |
| ----------|------------ |
| feat:
| 新特性 |
| fix(scope):
| 修复 scope 中的 Bug |
| feat!:
/ feat(scope)!:
| breaking change / 重构 API |
| chore(deps):
| 更新依赖 |
| 类型 | 描述 |
| ----------|------------ |
| build:
| 变更影响的是构建系统或者外部依赖 (如: gulp, npm) |
| ci:
| 修改了 CI 配置文件或脚本 (如: Github Action, Travis) |
| chore:
| 【重要】 变更不影响源代码或测试(如更新了辅助工具、库等) |
| docs:
| 只修改了文档 |
| feat:
| 【重要】 一个新特性 |
| fix:
| 【重要】 修复了一个 Bug |
| perf:
| 增强性能的代码变更 |
| refactor:
| 并非修复 Bug 或添加新特性的代码变更 |
| revert:
| 回退代码 |
| style:
| 变更不影响一些有意义的代码 (如: 删除空格、格式化代码、添加分号等) |
| test:
| 添加测试代码或修正已有的测试 |
import React, { useReducer, useContext } from 'react';
// 创建一个 Context
const MyContext = React.createContext({});
// 定义一个 reducer
const reducer = (state:any, action:any) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + action.payload };
case 'DECREMENT':
return { count: state.count - action.payload };
default:
return state;
}
};
// 定义一个 HOC
function withCounter<T>(WrappedComponent:any) {
// 返回一个新组件
const NewComponent = (props:T) => {
// 使用 useReducer 定义状态和更新函数
const [state, dispatch] = useReducer(reducer, { count: 0 });
// 将状态和更新函数通过 Context 共享给子组件
return (
<MyContext.Provider value={{ state, dispatch }}>
<WrappedComponent {...props} />
</MyContext.Provider>
);
};
return NewComponent;
};
export { withCounter, MyContext };
import React, { useContext } from 'react';
import { withCounter, MyContext } from './withCounter';
const Counter = () => {
// 从 Context 中获取状态和更新函数
const { state , dispatch } = useContext(MyContext);
// console.log(state);
return (
<>
<div>Count: {state?.count}</div>
<button onClick={() => dispatch({ type: 'INCREMENT', payload: 1 })}>
Increment
</button>
<button onClick={() => dispatch({ type: 'DECREMENT', payload: 1 })}>
Decrement
</button>
</>
);
};
// 使用 withCounter 包装 Counter 组件
export default withCounter(Counter);
// Define a function to recursively get object properties
function getObjectProperty(obj, prop) {
// Split the property string into an array of nested properties
const props = prop.split('.');
// Get the first property in the array
const firstProp = props.shift();
// Get the value of the first property
const value = obj[firstProp];
// If there are no more nested properties, return the value
if (props.length === 0) {
return value;
}
// If the value is an object, recursively call the function with the remaining properties
if (typeof value === 'object' && value !== null) {
return getObjectProperty(value, props.join('.'));
}
// If the value is not an object or null, return undefined
return undefined;
}
// Call the function with an object and a property string to get the value of the property
const obj = { a: { b: { c: 'value' } } };
const prop = 'a.b.c';
const value = getObjectProperty(obj, prop);
console.log(value); // Output: 'value'
class Single {
static instance = null;
data = [];
constructor() {
if (!Single.instance) {
Single.instance = this;
}
return Single.instance;
}
addData(item) {
this.data.push(item);
}
getData() {
return this.data;
}
}
// Usage:
const single1 = new Single();
const single2 = new Single();
console.log(single1 === single2); // true
single1.addData('foo');
single2.addData('bar');
console.log(single1.getData()); // ['foo', 'bar']
console.log(single2.getData()); // ['foo', 'bar']
import {Directive, HostListener} from '@angular/core';
import {ActivatedRoute} from "@angular/router";
import {UpdateListPageService} from "../services/update-list-page.service";
import {IndexPageComponent} from "../pages/index-page/index-page.component";
@Directive({
selector: '[appScrollToBottom]'
})
export class ScrollToBottomDirective {
constructor( private route:ActivatedRoute,
private getList:UpdateListPageService,
private indexPageComponent:IndexPageComponent
) { }
@HostListener('window:scroll',['$event'])
onScroll(event:any){
let windowHeight = "innerHeight" in window ? window.innerHeight : document.documentElement.offsetHeight;
let body = document.body, html = document.documentElement;
let docHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
let windowBottom = windowHeight + window.pageYOffset;
const id = Number(this.route.snapshot.queryParamMap.get('id'));
if (windowBottom >= docHeight) {
// 已经滚动到底部
const pageInfo = this.indexPageComponent.pageInfo;
if(pageInfo.pageIndex !== pageInfo.pageTotal){
this.getList.getData(id,++pageInfo.pageIndex)
}
// console.log('到底了', id,pageInfo, this.indexPageComponent)
} else {
// 没有滚动到底部
// console.log('没到底了', id)
}
}
}
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} />;
}
FancyInput = forwardRef(FancyInput);
父组件使用
<FancyInput ref={inputRef} />
inputRef.current.focus()
加入更多第三方的JavaScript或CSS,例如加入jQuery和bootstrap,可以先使用npm进行下载
npm install --save jquery npm install --save bootstrap
接着调整angular-cli.json里面app.styles和app.scripts设定
"styles": [
"../node_modules/bootstrap/dist/css/bootstrap.css",
"styles.css"
],
"scripts": [
"../node_modules/jquery/dist/jquery.js",
"../node_modules/bootstrap/dist/js/bootstrap.js"
],
如果 ng serve指令正在执行中的话,要先关掉然后重新执行,才会读到新的angular-cli.json的设定档
接着就可以在画面上自由加入bootstrap的样式
import {Injectable} from '@angular/core';
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
import {Observable} from "rxjs";
import {apiListProps, AppList, methodEnum} from "./apiList";
@Injectable({
providedIn: 'root'
})
export class HttpRequestService {
constructor(private http: HttpClient) {
}
getData(url: string, params?: any, headers?: any): Observable<any> {
const httpHeaders = new HttpHeaders(headers);
return this.http.get(url, {params, headers: httpHeaders});
}
postData(url: string, data: any, headers?: any): Observable<any> {
const httpHeaders = new HttpHeaders(headers);
return this.http.post<any>(url, data, {headers: httpHeaders});
}
public fetch(api: apiListProps, data?: any, type?: string): Observable<any> {
const params: any = new HttpParams({fromObject: data});
const currentOptions = {
params: api.method === methodEnum.get ? params : '',
body: params,
headers: {
'Content-type': type === 'form' ? 'application/x-www-form-urlencoded':'application/json',
}
}
return this.http.request(api.method, api.url, currentOptions);
}
}
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class UtilService {
constructor() { }
// 生成指定范围内的随机整数
getRandomInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// 生成指定长度的不重复随机字符串
generateUniqueString(length: number): string {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
// 防抖函数
debounce(fn: Function, delay: number): Function {
if (typeof fn !== 'function') {
throw new TypeError('Expected a function');
}
let timer: any = null;
return function (this:any) {
const context = this;
const args = arguments;
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, args);
}, delay);
};
}
// 节流函数
throttle(fn: Function, delay: number): Function {
if (typeof fn !== 'function') {
throw new TypeError('Expected a function');
}
let timer: any = null;
let startTime = Date.now();
return function (this:any) {
const context = this;
const args = arguments;
const curTime = Date.now();
const remaining = delay - (curTime - startTime);
clearTimeout(timer);
if (remaining <= 0) {
fn.apply(context, args);
startTime = Date.now();
} else {
timer = setTimeout(() => {
fn.apply(context, args);
}, remaining);
}
};
}
}
目前 NestJS 支持两个 Node HTTP 平台:Express 和 Fastify。从技术上讲,一旦创建了适配器,Nest 便可以使用任何 Node HTTP 框架
### platform-express
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { NestExpressApplication } from '@nestjs/platform-express'
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule)
await app.listen(3000)
}
bootstrap()
### platform-fastify
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { NestFastifyApplication } from '@nestjs/platform-fastify'
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(AppModule)
app.enableCors() // 开启Cors
app.register(fastifyCsrf)
await app.listen(4000, '0.0.0.0')
console.log(`Application is running on: ${await app.getUrl()}`)
}
bootstrap()
├── src # 源代码目录
│ ├── app.module.ts # 应用程序的根模块
│ ├── app.controller.spec.ts # 控制器的单元测试
│ ├── app.controller.ts # 单个路由的基本控制器
│ ├── app.service.ts # 具有单一方法的基本服务
│ └── main.ts # 应用程序的入口文件
│ # 它使用核心函数 NestFactory 来创建 Nest 应用程序的实例
└── test # 测试目录
├── app.e2e-spec.ts
└── jest-e2e.json
NestJS 需要 Node.js >= 12
$ npm i -g @nestjs/cli
$ nest new project-name
Nest CLI 是一个命令行界面工具,可以帮助你初始化、开发和维护你的Nest应用程序,安装依赖并启动开发服务器
$ cd <your-project-name>
$ npm run start
当你准备将应用发布到生产环境时,请运行:
$ npm run build
此命令会在 ./dist 文件夹中为你的应用创建一个生产环境的构建版本
命令 别名 描述
new n 使用模板快速创建应用
generate g 自动生成Controller、Providers 和 Modules
build 打包并输出./dist目录
start 打包并运行
add 安装一个符合Nest的库,同npm install
info i 输出系统信息、CLI版本和Nest Package信息
CHAR String (0 - 255)
VARCHAR String (0 - 255)
TINYTEXT String (0 - 255)
TEXT String (0 - 65535)
BLOB String (0 - 65535)
MEDIUMTEXT String (0 - 16777215)
MEDIUMBLOB String (0 - 16777215)
LONGTEXT String (0 - 4294967295)
LONGBLOB String (0 - 4294967295)
ENUM One of preset options
SET Selection of preset options
DATE yyyy-MM-dd
TIME hh:mm:ss
DATETIME yyyy-MM-dd hh:mm:ss
TIMESTAMP yyyy-MM-dd hh:mm:ss
YEAR yyyy
TINYINT x Integer (-128 to 127)
SMALLINT x Integer (-32768 to 32767)
MEDIUMINT x Integer (-8388608 to 8388607)
INT x Integer (-2147483648 to 2147483647)
BIGINT x Integer (-9223372036854775808 to 9223372036854775807)
FLOAT Decimal (precise to 23 digits)
DOUBLE Decimal (24 to 53 digits)
DECIMAL "DOUBLE" stored as string
创建备份
mysqldump -u user -p db_name > db.sql
导出不带架构的数据库
mysqldump -u user -p db_name --no-data=true --add-drop-table=false > db.sql
恢复备份
mysql -u user -p db_name < db.sql
数据库 Database
CREATE DATABASE db ; 创建数据库
SHOW DATABASES; 列出数据库
USE db; 切换到数据库
CONNECT db ; 切换到数据库
DROP DATABASE db; 删除数据库
表 Table
SHOW TABLES; 列出当前数据库的表
SHOW FIELDS FROM t; 表的列表字段
DESC t; 显示表格结构
SHOW CREATE TABLEt; 显示创建表sql
TRUNCATE TABLEt; 删除表中的所有数据
DROP TABLEt; 删除表格
Proccess
show processlist; 列出进程
kill pid; 杀死进程
# 默认用户名<root>,-p 是密码,
# 参数后面不需要空格
mysql -h 127.0.0.1 -u <用户名> -p<密码>
mysql -D 数据库名 -h 主机名 -u 用户名 -p
mysql -h <host> -P <端口号> -u <user> -p [db_name]
mysql -h <host> -u <user> -p [db_name]
# 显示当前mysql的version的各种信息
mysql> status;
# 显示当前mysql的version信息
mysql> select version();
# 查看 MySQL 端口号
mysql> show global variables like 'port';
可以在类、类方法、访问器、属性和方法参数上使用装饰器。
import { Syncable, triggersSync, preferCache, required } from "mylib"
@Syncable
class User {
@triggersSync()
save() { ... }
@preferCache(false)
get displayName() { ... }
update(@required info: Partial<User>) { ... }
}
// 确保类符合一组接口或类型 ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈╮
// 子类这个类 ┈┈┈┈┈┈┈┈↘ ┈┈┈┈┈┈┈┈┈┈┈┈┈┴┈┈┈┈┈┈┈
class User extends Account implements Updatable, Serializable {
id: string; // 一个字段
displayName?: boolean; // 可选字段
name!: string; // '相信我,它在哪里'字段
#attributes: Map<any, any>; // 私人字段
roles = ["user"]; // 具有默认值的字段
readonly createdAt = new Date() // 具有默认值的只读字段
// 代码调用“new”
constructor(id: string, email: string) {
super(id);
// 在 `strict: true` 中,会根据字段检查此代码以确保其设置正确
this.email = email;
// ....
};
// 描述类方法(和箭头函数字段)的方式
setName(name: string) { this.name = name }
verifyName = (name: string) => { /* ... */ }
// 具有 2 个重载定义的函数
sync(): Promise<{ ... }>
sync(cb: ((result: string) => void)): void
sync(cb?: ((result: string) => void)): void | Promise<{ ... }> {}
// Getters 和 setters
get accountID() { }
set accountID(value: string) { }
// 私有访问只是对这个类,受保护的允许子类。 仅用于类型检查,public 是默认值。
private makeRequest() { ... }
protected handleRequest() { ... }
// 静态字段/方法
static #userCount = 0;
static registerUser(user: User) { ... }
// 用于设置静态变量的静态块。 ‘this’指的是静态类
static { this.#userCount = -1 }
}
type JSONResponse = {
version: number; // 字段
/** In bytes */ // 附加文档
payloadSize: number;
outOfStock?: boolean; // 可选的
update: (retryTimes: number) => void; // 箭头函数字段
update(retryTimes: number): void; // 函数
(): JSONResponse // 类型是可调用的
[key: string]: number; // 接受任何索引
new (s: string): JSONResponse; // new 对象
readonly body: string; // 只读属性
}
GOOS
编译系统
GOARCH
编译arch
GO111MODULE
gomod开关
GOPROXY
go代理 https://goproxy.io https://goproxy.cn https://mirrors.aliyun.com/goproxy/
GOSSAFUNC
生成 SSA.html 文件,展示代码优化的每一步 GOSSAFUNC=func_name go build
go mod init
初始化当前文件夹,创建go.mod文件
go mod download
下载依赖的module到本地
go mod tidy
增加缺少的module,删除无用的module
go mod vendor
将依赖复制到vendor下
文件 go.mod
依赖列表和版本约束
文件 go.sum
记录 module 文件 hash 值,用于安全校验
go command [参数] go 命令 [参数]
go build 编译包和依赖包
go clean 移除对象和缓存文件
go doc 显示包的文档
go env 打印go的环境变量信息
go bug 报告bug
go fix 更新包使用新的api
go fmt 格式规范化代码
go generate 通过处理资源生成go文件
go get 下载并安装包及其依赖
go install 编译和安装包及其依赖
go list 列出所有包
go run 编译和运行go程序
go test 测试
go tool 运行给定的go工具
go version 显示go当前版本
go vet 发现代码中可能的错误
A function can return any number of results.
The swap function returns two strings. It is multiple results.
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
func outer() (func() int, int) {
outer_var := 2
inner := func() int {
outer_var += 99
return outer_var
}
inner()
return inner, outer_var
}
inner, val := outer()
fmt.Println(val)
// => 101
fmt.Println(inner())
// => 200,这里涉及到golang中闭包和内存逃逸的概念,inner()实际上执行了两次,outer()中一次,fmt又一次,
//但为什么是200呢,编译器不能确定outer_var在后续会不会使用,
//所以outer_var不会随着outer()结束而释放它的栈(Stack)空间,
//而会‘逃逸到’堆(Heap)上,那么第二次的inner()中outer_var就会是101。
package main
import (
"fmt"
"os"
)
type point struct {
x, y int
}
func main() {
p := point{1, 2}
fmt.Printf("%v\n", p) // => {1 2}
fmt.Printf("%+v\n", p) // => {x:1 y:2}
fmt.Printf("%#v\n", p) // => main.point{x:1, y:2}
fmt.Printf("%T\n", p) // => main.point
fmt.Printf("%t\n", true) // => TRUE
fmt.Printf("%d\n", 123) // => 123
fmt.Printf("%b\n", 14) // => 1110
fmt.Printf("%c\n", 33) // => !
fmt.Printf("%x\n", 456) // => 1c8
fmt.Printf("%f\n", 78.9) // => 78.9
fmt.Printf("%e\n", 123400000.0) // => 1.23E+08
fmt.Printf("%E\n", 123400000.0) // => 1.23E+08
fmt.Printf("%s\n", "\"string\"") // => "string"
fmt.Printf("%q\n", "\"string\"") // => "\"string\""
fmt.Printf("%x\n", "hex this") // => 6.86578E+15
fmt.Printf("%p\n", &p) // => 0xc00002c040
fmt.Printf("|%6d|%6d|\n", 12, 345) // => | 12| 345|
fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45) // => | 1.20| 3.45|
fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45) // => |1.20 |3.45 |
fmt.Printf("|%6s|%6s|\n", "foo", "b") // => | foo| b|
fmt.Printf("|%-6s|%-6s|\n", "foo", "b") // => |foo |b |
s := fmt.Sprintf("a %s", "string")
fmt.Println(s)
fmt.Fprintf(os.Stderr, "an %s\n", "error")
}
Go语言中不允许隐式转换,所有类型转换必须显式声明(强制转换),而且转换只能发生在两种相互兼容的类型之间。
i := 90
f := float64(i)
u := uint(i)
// 将等于字符Z
s := string(i)
如何获取int字符串?
i := 90
// 需要导入“strconv”
s := strconv.Itoa(i)
fmt.Println(s) // Outputs: 90
update daily1 set price= 100 * (rand()*10) *id + floor(RAND()*100) - 3.14 where id=1;
update daily1 set date= now() - interval floor(RAND()*10) day where id%2!=0;
update daily1 set username = concat('用户',floor(rand()*100)) where id%2!=0;
update daily1 set username = concat('用户',floor(rand()*100)) where id%2!=0;