Skip to main content

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;
}

效果

image-20211014131106957

[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)专项答题

规则:

答案:查看提示=>> 识别红色文字

image-20211015114938140
  • 选择:

    image-20211015115028171

  • 输入:

    image-20211015114855188

每日至少 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_IdOrderDateOrderPriceCustomer
12008/12/291000Bush
22008/11/231600Carter
32008/10/05700Bush
42008/09/28300Bush
52008/08/062000Adams
62008/07/21100Carter

查询结果

CustomerSUM(OrderPrice)
Carter1700

[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代替。

  • 联查

  • 内联和左联

  • 分页

  • 加锁

  • 子查询