10-16
16 git 查看当前追踪分支
git branch -vv
==>
develop 0b7833b [origin/develop] fix: 修复@别名配置
* master 0b7833b [origin/master] fix: 修复@别名配置
可以看到当前 master 追踪的 origin/master 分支,develop 追踪的 origin/develop
追踪后,git push 或者 git pull,就默认与该追踪分支进行交互
联想:如何设置追踪分支?
设置分支 develop 追踪远端分支的 developxxxxxxxxxx git branch --set-upstream develop origin/develop 前提是本地都有这两个分支检出远端分支,并设置追踪(最常用)xxxxxxxxxx git checkout -b develop origin/develop 前提是有 origin/develop 分支设置当前分支追踪 origin/developxxxxxxxxxx git branch -u origin/develop
[1] git 跟踪远程分支,查看本地分支追踪和远程分支的关系.https://www.cnblogs.com/mafeng/p/10137244.html
17 manifest.json 作用
[Web 应用程序清单](https://developer.mozilla.org/zh-CN/docs/Web/web app manifest)在一个 JSON 文本文件中提供有关应用程序的信息(如名称,作者,图标和描述)。manifest 的目的是将 Web 应用程序安装到设备的主屏幕,为用户提供更快的访问和更丰富的体验。
实例
{
"background_color": "#fff",
"display": "standalone",
"homepage_url": "https://sheepdex.org",
"icons": [
{
"src": "./images/192x192_App_Icon.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "./images/512x512_App_Icon.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
],
"orientation": "portrait",
"name": "SheepDEX",
"short_name": "SheepDEX",
"start_url": ".",
"theme_color": "#0958B4"
}
一些字段说明:
- display:定义开发人员对 Web 应用程序的首选显示模式。
- orientation:定义所有 Web 应用程序顶级的默认方向 browsing contexts.
[1] Web App Manifest.https://developer.mozilla.org/zh-CN/docs/Web/Manifest
联想:如何在社交应用上创建网站预览图?增强 seo
例如:Fackbook、Telegram、Twitter 等
解决方案:添加 og 协议头
<meta property=”og:image” content=”http://g1.ykimg.com/0100641F464A ... 9-76EA-E5E20A1887C4″/>
除了支持 image,还有其他属性
<meta property="”og:type”" content="”video”" />
<meta property="”og:title”" content="”五月天_突然好想你MV现场版”" />
<meta property=”og:image” content=”http://g1.ykimg.com/0100641F464A ... 9-76EA-E5E20A1887C4″/> <meta property=”og:url”
content=”http://v.youku.com/v_show/id_XMTIyMTY5Nz...”/> <meta property=”og:videosrc”
content=”http://player.youku.com/player.p ... AutoPlay=true/v.swf”/>
<meta property="”og:width”" content="”500″" />
<meta property="”og:height”" content="”416″" />
给 telegram 添加预览图
第一步:
<html prefix="og: http://ogp.me/ns#"></html>
第二步:然后再给 telegram 机器人一条刷新指令即可
[1] How to force telegram to update link preview?.https://stackoverflow.com/questions/35268940/how-to-force-telegram-to-update-link-preview
18 一个圆形阴影-css-box-shadow
div {
width: 100px;
height: 100px;
margin: 100px auto;
background-color: #ff8888;
border: 1px solid #000;
border-radius: 50%;
box-shadow: 10px 10px 5px #888888, 10px 10px 5px #888 inset;
}
效果
[1] CSS 边框 轮廓 阴影.https://segmentfault.com/a/1190000003846548
19 Js 深拷贝
Lodash 太大,所以引入一个手写的深拷贝
function deepClone(obj) {
if (obj === null) return null //null 的情况
if (obj instanceof RegExp) return new RegExp(obj) //正则表达式的情况
if (obj instanceof Date) return new Date(obj) //日期对象的情况
if (typeof obj == 'Function') return new (function (obj) {})() //函数的情况
if (typeof obj != 'object') {
//非复杂类型,直接返回 也是结束递归的条件
return obj
}
//[].__proto__.constructor=Array()
//{}.__proto__.constructor=Object()
//因此处理数组的情况时,可以取巧用这个办法来new新对象
var newObj = new obj.__proto__.constructor()
for (var key in obj) {
newObj[key] = deepClone(obj[key])
}
return newObj
}
问题一:__proto__
具有兼容性问题,如何处理?
利用 Object.create
代替 __proto__
⚠️ 问题二:deepClone 中的循环引用是什么导致的?相同引用又是什么?如何解决呢?
引用分为两种:循环引用和相同引用
//循环引用,一个类中一个属性引用自身
const circle = {}
circle.circle = circle
//相同引用,一个类中多个属性引用相同的对象
const obj = {}
const arr = []
obj.arr1 = arr
obj.arr2 = arr
✅ 如何解决深拷贝中的引用问题呢?
思想:用一个对象(或者 map)记录一下出现的所有属性,在每次拷贝之前检查一下在记录中否?如果在,直接取原有的,不在则取当前的。
具体实现:
function deepClone(origin: any) {
const map = new Map()
return baseClone(origin)
function baseClone(origin: any) {
// 1.条件
if (origin === null || origin === undefined) return origin
if (origin instanceof Date) return new Date(origin.getTime())
if (origin instanceof RegExp) return new RegExp(origin)
const type = typeof origin
// if(type === 'function') return new Function('return' + origin.toString())()
// if(type === 'function') return new function(origin){}
// if(type === 'function') return new function(origin){}
// 拷贝函数有问题,如果函数内部有闭包量,那么会失败
// TODO目前函数并未实现深拷贝
if (type !== 'object') return origin
// 2.递归
const keys = Object.keys(origin)
let target = null
target = origin instanceof Array ? [] : Object.create(origin)
//用当前值和origin比较,值是新创建数据本身
map.set(origin, target)
const len = keys.length
for (let i = 0; i < len; ++i) {
const key = keys[i]
const value = origin[key]
if (map.has(value)) {
target[key] = map.get(value)
} else {
target[key] = deepClone(value)
// 注意:数组也可以通过arr['0']的方式去赋值
}
}
return target
}
}
[1] 原生 js 实现深拷贝.https://juejin.cn/post/6844903967923650573
[2] 解决循环引用和相同引用的 js 深拷贝实现(BFS).https://segmentfault.com/a/1190000021682472
[3] JavaScript 拷贝 函数方式.https://blog.csdn.net/Altaba/article/details/103916762
20 uniapp 的云数据库、云储存
❌ 目前先不了解云函数和数据库
描述:可以通过 云函数
去操作云数据库、云储存,而云函数可以绑定域名,就相当于一个接口,例如:https://30784075-9ca0-4ed8-93cb-edd05313f768.bspapp.com/api/register
此域名可以用于微信小程序
下面是使用云数据库的实例:https://segmentfault.com/a/1190000039896915
21 MongoDB
目的:学习 MongoDB,为使用云数据库打基础(都为非关系型文档数据库)
环境:MongoDB 版本为 4.2
学习方式:看阅中文文档为目录,具体阅读英文文档,因为英文文档有在线 shell(可以直接尝试)
中文文档路径:https://docs.mongoing.com/mongo-introduction/documents
英文文档路径:https://docs.mongodb.com/v4.2/tutorial/query-documents/
学习重点
- 增删改查
插入
查询
查询返回字段
db.inventory.insertMany( [
{ item: "journal", status: "A", size: { h: 14, w: 21, uom: "cm" }, instock: [ { warehouse: "A", qty: 5 } ] },
{ item: "notebook", status: "A", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "C", qty: 5 } ] },
{ item: "paper", status: "D", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "A", qty: 60 } ] },
{ item: "planner", status: "D", size: { h: 22.85, w: 30, uom: "cm" }, instock: [ { warehouse: "A", qty: 40 } ] },
{ item: "postcard", status: "A", size: { h: 10, w: 15.25, uom: "cm" }, instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);
db.collection.find(查询条件, 投影文档)
仅返回指定的字段和_id 字段:通过投影文档完成,1 为返回 0 为过滤
db.inventory.find( { status: "A" }, { item: 1, status: 1 } )
将返回_id, item, status 字段,注意默认会返回 id,如果要禁用请指明 id 为 0
返回除了被排除的字段之外的所有字段
db.inventory.find( { status: "A" }, { status: 0, instock: 0 } )
返回嵌入式文档中的特定字段
db.inventory.find( { status: "A" }, { item: 1, status: 1, "size.uom": 1 } )
返回数组中的项目特定数组元素
db.inventory.find( { status: "A" }, { item: 1, status: 1, instock: { $slice: -1 } } )
提供了三个运算符: $elemMatch,$slice 和$
查询数组
- 匹配一个数组
- 查询一个元素
- 多个条件查询元素
- { "tags" : { $gt:10, $lt:15 } } 有一个元素大于 10,另一个元素小于 15
- { "tags" : { $elemMatch : { $gt:10, $lt:15 } } }至少有一个元素在 10-15 之间
- 通过索引查询:{ "tags.1" : "a" }
- 通过长度查询:$size
查询 null
db.inventory.insertMany([
{ _id: 1, item: null },
{ _id: 2 }
])
- 平等过滤器:db.inventory.find( { item: null } )
- 类型检查:db.inventory.find( { item : { $type: 10 } } )
- 存在检查:db.inventory.find( { item : { $exists: false } } )
嵌套查询
db.inventory.insertMany( [
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);
db.inventory.find( { "size.uom": "in" } )
#查找size下uom为in的文档
db.inventory.find( { "size.h": { $lt: 15 } } )
#查找size下h大于15的文档
22 学习强国计划
积分累计规则
(1)登录
(12)文章:进入文章页面至少 5 秒
(6)视频:至少 10 秒
(6)视频时长:1 分/1 分钟
(5)每日答题:需要文字识别
(10)专项答题
规则:
答案:查看提示=>> 识别红色文字
选择:
输入:
每日至少 40 分,每月 30*40=1200
每日最大 60 分,每月最大 60*30=1800
目标 1000 分,需时长:1000/40 = 25 天
目标 5000 分,需时长:5000/40 = 125 天
目标 10000 分,需时长:10000/40 = 250 天
23 Node 端的时间格式化工具库-format
❌ 没看懂
date-fns/format:https://github.com/date-fns/date-fns
date-fns/format 文档地址:https://date-fns.org/
示例
import * as _dateFormat from 'date-fns/format'
export const dateFormat = (date = null, format = 'yyyy-MM-dd HH:mm:ss') => {
if (date === null || date === undefined) {
date = new Date() // eslint-disable-line no-param-reassign
}
const t = date instanceof Date ? date : new Date(date)
// @ts-ignore
return _dateFormat(t, format)
}
24 docker 启动 mysql 方法-docker 创建 mysql
第一步:拉取并运行镜像
docker image pull mysql:latest
# 如果是m1
docker pull mysql/mysql-server
docker run -d --restart=always --name nest-dashboard -p 3308:3306 -v /root/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=LOVEJJ@stung2 mysql:latest
# 如果是m1
docker run -d --restart=always --name my-mysql -p 3040:3306 -v /Users/jiangjin/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root mysql/mysql-server
#密码为root
#-v表示挂载目录
第二步:创建数据库
docker container exec -it nest-dashboard bash;
mysql -u root -p;
CREATE DATABASE `nest-dashboard` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE `hap_vac` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER USER 'root'@'%' IDENTIFIED BY 'root' PASSWORD EXPIRE NEVER;
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';
[1] Docker MySQL 数据持久化.https://www.jianshu.com/p/351b71c3cd5a
数据备份
第一种:通过通过 navcat 应用导入导出数据
第二种:通过命令
#导出
mysqldump -u root -p nest-dashboard record > dump_record_2021-11-02_17:15:03.txt
#导入
mysql -u root -p database_name < dump.txt
#如果两台服务器互通
mysqldump -u root -p database_name \
| mysql -h other-host.com database_name
[1] MySQL 导出数据.https://www.runoob.com/mysql/mysql-database-export.html
25 nestjs 利用 typeorm 连接数据库
注意问题 1:Error: RepositoryNotFoundError: No repository for [Enitity] was found. Looks like this entity is not registered in current "default" connection?
1.检查 user.entity.ts 是否带有 @Entity()
装饰器
2.检查 app.module.ts 中的连接方法 TypeOrmModule.forRoot
是否配置了 entity
3.检查 user.module.ts 中的导入方法 TypeOrmModule.forFeature
是否导入了 entity
第一步:安装依赖
yarn add @nestjs/typeorm typeorm mysql2
第二步:配置数据库,并连接
用 docker 创建数据库,详见【24 docker 启动 mysql 方法】
之后,配置数据库连接
//app.module.ts
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
// host: 'localhost',
host: '192.168.3.42',
port: 3310,
username: 'root',
password: 'root',
database: 'ginlink_test',
entities: [User],
charset: 'utf8mb4',
timezone: '+08:00',
synchronize: true,
}),
UserModule,
],
第三步:创建实体(entity),并配置、注入实体
- 配置到 app.module => TypeOrmModule.forRoot,如上第二步
- 配置到 user.module => TypeOrmModule.forFeature,如下
- 注入到 user.service => constructor,如下
- 配置实体,如下
//user.module.ts
imports: [TypeOrmModule.forFeature([User])],
//user.service.ts
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>,
) {}
//user.entity.ts
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
account: string
@Column()
password: string
@Column({ default: true })
isActive: boolean
}
第四步:书写控制器和服务
//user.controller.ts
@Post('register')
async register(@Body() user: Partial<User>) {
const newUser = await this.userService.createUser(user);
return newUser;
}
//user.service.ts
async createUser(user: Partial<User>) {
const { account, password } = user;
if (!account || !password)
throw new HttpException('请输入用户名或者密码', HttpStatus.BAD_REQUEST);
const existUser = await this.usersRepository.findOne({
where: { account },
});
if (existUser)
throw new HttpException('用户名已存在', HttpStatus.BAD_REQUEST);
const newUser = await this.usersRepository.create(user);
await this.usersRepository.save(newUser);
return newUser;
}
[1] nestjs 连接数据库.https://docs.nestjs.cn/8/techniques?id=%e6%95%b0%e6%8d%ae%e5%ba%93
26 ts 中的 Partial、Pick、Omit、Required
interface User {
name: string;
age: number;
}
//[1]Partial<T>可以让T类型都变为可选的
const a: Partial<User> = {};
=> {name?: string; age?: number}
//[2]Pick<T, seleted>可以选择需要T类型中的哪一个或多个类型
const b: Pick<User, 'age'> = {};
=>> {age: number}
const b: Pick<User, 'age' | 'name'> = {};
=>> {name: string; age: number}
//[3]Omit<T, seleted>可以选择忽略T类型中的哪一个或多个类型
const b: Omit<User, 'age'> = {};
=>> {name: string}
[1] TypeScript Utility Types Part 1: Partial, Pick, and Omit.https://www.dslemay.com/blog/2020/04/27/typescript-utility-types-part-1-partial-pick-and-omit
这篇文档中还有其他好的 Ts 类型文章,例如:
- Part 1: Partial, Pick, and Omit
- Part 2: Record, Readonly, & Required
- Part 3: Extract, Exclude, and NonNullable
Required
interface User {
name: string;
age: number;
height?: string;
}
//Required与Partial正好相反,要求所有参数为必须参数
const c: Required<User> = {}
=>> { name: string; age: number; height: string; }
28 ts 中的装饰器
装饰器是一项 ECMA 提案,只是在 ts 中先实现了。
问题一:ts 中的装饰器与 js 中的装饰器有什么区别?
TODO
问题二:装饰器的种类?
在 typescript 中一共有五类。类、属性、方法、访问器、参数。
修饰类
type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void
//target: 类的构造器。
修饰属性
type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void
//target: 对于静态成员来说是类的构造器,对于实例成员来说是类的原型链。
//propertyKey: 属性的名称。
修饰方法
type MethodDecorator = <T>(
target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>
) => TypedPropertyDescriptor<T> | void
//target: 对于静态成员来说是类的构造器,对于实例成员来说是类的原型链。
//propertyKey: 属性的名称。
//descriptor: 属性的描述器。
修饰访问器:类似于修饰方法,区别在于 descriptor(属性描述符),方法是数据描述符,而访问器是存取描述符
修饰参数
type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) => void
//target: 对于静态成员来说是类的构造器,对于实例成员来说是类的原型链。
//propertyKey: 属性的名称(注意是方法的名称,而不是参数的名称)。
//parameterIndex: 参数在方法中所处的位置的下标。
问题三:装饰器的执行顺序?
属性参数方法-> 访问器-> 静态属性参数方法
-> 类
[1] TypeScript 装饰器完全指南.https://saul-mirone.github.io/zh-hans/a-complete-guide-to-typescript-decorator/
问题四:typescript 的 tsconfig.json 中的 module
配置项与项目有什么关系?
没有太大关系,module 可以配置为 esm
一般后端项目,module 为 commonjs,因为大多数包都还是 commonjs 的,而 esmodule 是兼容 commonjs 的,所有 module 选项一般设置未 commonjs
只是要注意一个问题,ts2.7 的配置中出了一个 esModuleInterop 的配置,可以解决 esmodule 中 impot 只导入 default 属性的问题
import d from 'cjs'
//以前写法
import * as d from 'cjs'
[1] typescript 中的模块引用.https://segmentfault.com/a/1190000019793220
问题五:Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option `to remove this warning.
❌ stackoverflow上有相关问题,但 it not work
介绍:利用 tsc 编译 typescript 文件的时候出现上述错误,但我在 tsconfig.json 中已经配置了 experimentalDecorators
属性,未生效?
✅ 原因:tsc 命令为全局的命令,在默认 tsc 工作路径,是没有配置文件的,所以需要让它感知 tsconfig.json 的存在,在 package.json 创建命令,此时 tsc 的工作路径就是当前
//package.json
"scripts": {
"dev": "tsc -w"
}
29 ECMA
介绍:Ecma 为信息和通信行业制定和发布国际标准。自 1961 年至今,Ecma International 一直在全力推动及时创建广泛的全球信息和通信技术 (ICT) 和消费电子 (CE) 标准。
ECMA-262
ECMA-262 指的是 ECMAScript 通用编程语言规范
,它属于 TC39 技术委员会管理的项目。
截止 2020.6,已经发行到第 11 版,完整的版本见https://www.ecma-international.org/publications-and-standards/standards/ecma-262/
30 TypeORM
问题一:数据库相关知识要掌握到什么程度?
问题二:不用创建表啦?是的,typeorm 自动创建表,应用程序中的模型即是数据库中的表。
问题三:什么是左连接、内连接?
连接都是针对于两个表的
- left join (左连接):返回包括左表中的所有记录和右表中
连接字段
相等的记录。 - right join (右连接):返回包括右表中的所有记录和左表中
连接字段
相等的记录。 - inner join (等值连接或者叫内连接):只返回两个表中
连接字段
相等的行。 - full join (全外连接):返回左右表中所有的记录和左右表中
连接字段
相等的记录。
问题四:什么是 having?
having 类似 where,where 用于分组前,having 用于分组后。原因在于 where 关键字无法与合计函数一起使用,所以才创建 having 关键字。
SELECT Customer,SUM(OrderPrice) FROM Orders
GROUP BY Customer
HAVING SUM(OrderPrice)<2000
O_Id | OrderDate | OrderPrice | Customer |
---|---|---|---|
1 | 2008/12/29 | 1000 | Bush |
2 | 2008/11/23 | 1600 | Carter |
3 | 2008/10/05 | 700 | Bush |
4 | 2008/09/28 | 300 | Bush |
5 | 2008/08/06 | 2000 | Adams |
6 | 2008/07/21 | 100 | Carter |
查询结果
Customer | SUM(OrderPrice) |
---|---|
Carter | 1700 |
[1] SQL Having 的用法.https://blog.csdn.net/u014401141/article/details/53010608
问题五:sql 查询中都有哪些概念?
where 条件查询
createQueryBuilder('user').where('user.name = :name', { name: 'Timber' })
having 条件查询
createQueryBuilder('user').having('user.name = :name', { name: 'Timber' })
order by 排序查询
createQueryBuilder('user').orderBy('user.id', 'DESC')
createQueryBuilder('user').orderBy('user.id', 'ASC')group by 分组查询
createQueryBuilder('user').groupBy('user.id')
limit
createQueryBuilder('user').limit(10)
注意:如果你使用带有连接或子查询的复杂查询,LIMIT 可能无法正常工作。 如果使用分页,建议使用
take
代替。offset
createQueryBuilder('user').offset(10)
注意:如果你使用带有连接或子查询的复杂查询,OFFSET 可能无法正常工作。 如果使用分页,建议使用
skip
代替。联查
内联和左联
分页
加锁
子查询