0%

“NoSQL” database

非关系型数据库,不适用SQL作为查询语言,不使用数据表格存放数据。

优势:

  • Scalability 可扩展性: by default, non-relational databases are split (or “shared”) across many systems instead of only one. This makes it easier to improve performance at a lower cost.
  • Flexibility 灵活性: new datasets and properties can be added to a document without the need to make a new table for that data.
  • Replication 备用性: copies of the database run in parallel so if one goes down, one of the copies becomes the new primary data source.

MongoDB Atlas

a cloud MongoDB service

1
mongodb+srv://qqs:<password>@clusteraws-vcbnj.mongodb.net/test?retryWrites=true&w=majority

Project with MongoDB

package:

  • mongodb
  • mongoose
    1
    2
    var mongoose = require('mongoose');
    mongoose.connect(process.env.MONGO_URI);
    Mongoose Docs
  • Schema: 一种以文件形式存储的数据库模型骨架,不具备数据库的操作能力
  • Model: 由Schema发布生成的模型,具有抽象属性和行为的数据库操作对
  • Entity : 由Model创建的实体,他的操作也会影响数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mongoose.connect(process.env.MONGO_URI);           //数据库连接

var Schema = mongoose.Schema;
var personSchema = new Schema({ //定义Schema
name: String,
age: Number,
favoriteFoods: Array
})

var Person = mongoose.model('Person',personSchema); //发布Model

var document = new Person({ //创建Entity
name:'David',
age:18,
favoriteFoods:'ice cream',
})

document.save(function(error,data){ //调用实例save方法
console.log('Person document saved') //以回调函数作为最后一个参数
})

如果是Entity,使用save方法,如果是Model,使用create方法,参数是json + 回调函数

1
2
3
4
5
6
7
Person.create({
name:'Ellar',
age:18,
favoriteFoods:'banana'
},callback)

Person.create(arrayOfPeople,callback); //批量创建

Model.find()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// example find all documents whose name is David
Person.find({name:'David'},function(error,data){
if(!error){
console.log(data)
}else{
console.log(error)
}
})

// find方法在非完全匹配情况下的应用,会复杂很多,需要借助不同的方法及其选项,这些方法以$开头
// example find whose name contains 'White'
Person.find(
{
name:
{
'$regex':'White',
'$options':'i'
}
},findcallback
)
// example find whose favoriteFood Array contains 'ice creame'
Person.find(
{
favoriteFood:{
$in:['ice cream']
},
},findcallback
)

// update
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var findAndUpdate = function(personName, done) {
var ageToSet = 20;
Person.findOneAndUpdate(
{name:personName}, /* find filter*/
{$set:{age:ageToSet}}, /* set options*/
{new:true}, /* return option*/
(error,data)=>{
if(error)
done(error);
done(null,data)
}
)
//done(null/*, data*/);
};

// findByIdAndRemove
1
Person.findByIdAndRemove('5d4a500e994a2154010dc67f',function(){})

实现自增长字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var WebSiteSchema = mongoose.Schema({
"original_url":String,
"short_url":Number
})
// model
var Website = mongoose.model('Website', CounterSchema)
// counter
var CounterSchema = Schema({
_id: {type: String, required: true},
seq: { type: Number, default: 0 }
});
var counter = mongoose.model('counter', CounterSchema);

WebSiteSchema.pre('save', function(){
var self = this;
counter.findByIdAndUpdate(
{_id: 'entityId'},
{$inc:{req:1}},
function(err,data){
if(err){
return next(err)
}
self.short_url = data.seq;
next();
}
)
})

pre是前置中间件操作,相当于其他语境的拦截器,在保存WebSite实例前调用计数器counter的findByIdAndUpdate。pre作用在Schema级别上,因此要在使用Schema生成model前定义才会生效。

区间条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
* collection{
* userId:String,
* from:Date,
* to:Date,
* limit:Number
*}
*/
var getLog = function(collection,callback){
let queryCondition = {userId:collection.userId}
if(collection.from){
queryCondition.date = (queryCondition.date || {});
queryCondition.date['$gte'] = collection.from;
}
if(collection.to){
queryCondition.date = (queryCondition.date || {});
queryCondition.date['$lt'] = collection.to;
}
console.log('query conditions:',queryCondition)
let query = Exercise.find(queryCondition)
if(collection.limit){
query = query.sort({'date': -1}).limit(collection.limit)
}
query.exec(callback)
}

关联查询 population (存目)

Please keep learning —> Linux工具快速教程

测试网络连接

linux 上一般是不装ping的

1
2
3
4
telnet 8.8.8.8 80
curl 8.8.8.8:80
ssh -v -p qqs@8.8.8.8
wget 8.8.8.8:80

拷贝目录

将工作空间目录下的test文件夹拷到labhome下面

1
cp -ri /home/Workspace/test /var/labhome/

  • -r 递归
  • -i 询问是否覆盖

    查找文件

    find [path] -name [filename]
    1
    find / -name filename

    tail

    1
    tail -f filename
    tail命令读取文件内容到标准输出,-f 循环读取,上述命令查找文件中最尾部的内容显示在屏幕上,并且不断刷新,使你看到最新的文件内容

    filename 可以是多个文件 以空格分隔即可

tar

tar 通常是GNU tar,而libarchive库集成了bsdtar,不知道为什么我的windows默认的tar.exe是后者执行下述脚本

1
tar -c * | gzip > Agt.tgz

报异常:tar.exe: Failed to open ‘\.\tape0’
该路径是缺省的设备起始位置(?),因为bsdtar要求不能缺省输出参数 应为
1
tar -cf - * | gzip > Agt.tgz

后台运行

后台运行命令

1
anycommand $

将前台命令放到挂起(stopped)

Ctrl + Z
查看后台工作项目
1
jobs -l

操作后台工作项目

  • 终止后台工作项目 kill jobNumber
  • 调至前台继续运行 fg jobNumber
  • 在后台执行挂起的工作项目 bg jobNumber

    vi查找

    在命令模式下敲斜杆( / )这时在状态栏(也就是屏幕左下脚)就出现了 “/” 然后输入你要查找的关键字敲回车就可以了。
    如果你要继续查找此关键字,敲字符 n 就可以继续查找了。
    敲字符N(大写N)就会向前查询;

    重命名rename

    个人理解: 语法其一 rename 原字符串 新值 范围

    将run.sh重命名为run_bak.sh
    1
    rename run.sh run_bak.sh run.sh
    语法其二 rename ‘option/原字符串模式/新字符串模式/‘ 搜索范围

    统一删除.bak后缀名:
    1
    rename 's/\.bak$//' *.bak
    其中/ /之间是正则表达式,后缀名中的.需要转义

    批量修改文件名为全部小写
    1
    rename 'y/A-Z/a-z/' *

    软件

    1
    2
    3
    sudo npm cache clean -f #----- 先清除 npm cache
    sudo apt-get update #------ 更新源
    sudo apt-get upgrade #------ 更新已安装的包
    对于nodejs npm需要使用n模块升级到最新稳定版本
    1
    2
    3
    sudo apt-get install nodejs npm
    npm install -g n
    sudo n stable
    Nov 25th 遭遇 issue 27711 需将nodejs从v12降至v10
    1
    sudo n 10.16.0
    RPM
    1
    2
    3
    4
    wget https://example.com/file.rpm
    sudo yum localinstall file.rpm
    sudo rpm –ivh file.rpm
    sudo rpm –ivh https://example.com/file.rpm

    查看系统版本

    1
    2
    3
    cat /etc/issue 查看发行版
    cat /etc/redhat-release 查看CentOS版本
    uname -r 查看内核版本

    查看硬件信息

    1
    2
    3
    4
    5
    cat /proc/cpuinfo |more
    cat /proc/meminfo |more
    // 磁盘
    cat /proc/partitions
    fdisk -l

    查看服务列表

    1
    service --status-all

    异常:键入命令不显示(回显关闭)

    1
    2
    3
    4
    // 回显关闭
    stty -echo
    // 回显打开
    stty echo

    定时任务

    查看服务状态
    1
    service cron status
    crontab
    1
    2
    3
    4
    // 列出该用户的计时器设置
    crontab -l
    // 编辑该用户的计时器设置
    crontab -u username -e
    不指定username 则以root用户身份执行

    计划任务分为系统任务调度用户任务调度两类

    系统任务调度:系统周期性所要执行的工作,比如写缓存数据到硬盘、日志清理等 见/etc/crontab时日周月计划。

    用户任务调度:用户可以使用 crontab 工具来定制自己的计划任务。所有用户定义的crontab文件都被保存在/var/spool/cron目录中。其文件名与用户名一致

实际上,crontab文件被设计为不允许(存疑)用户直接编辑,而是通过crontab -e管理
计划的格式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed

  • * 表示匹配该域的任意值。假如在Minutes域使用*, 即表示每分钟都会触发事件。
  • / 表示起始时间开始触发,然后每隔固定时间触发一次。例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次.
  • - 表示范围。例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次
  • , 表示列出枚举值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。

  • ? 只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 ?, 其中最后一位只能用?,而不能使用,如果使用*表示不管星期几都会触发,实际上并不是这样。

  • L 表示最后,只能出现在DayofWeek和DayofMonth域。如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。

  • W 表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份 。

  • LW 这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。

  • # 用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。

例子:

20 6 pwd 每天的 6:20 执行pwd命令

20 6 8 6
pwd 每年的6月8日6:20执行pwd命令

20 6 0 pwd 每星期日的6:20执行pwd命令

20 3 10,20 pwd 每月10号及20号的3:20执行pwd命令

25 8-10 pwd 每天8-10点的第25分钟执行pwd命令
/15 pwd 每15分钟执行一次pwd命令

20 6 /10 * pwd 每个月中,每隔10天6:20执行一次pwd命令

开启系统cron日志

sudo vim /etc/rsyslog.d/50-default.conf 取消注释

1
cron.log

即可在/var/log/syslog中看到关于CRON的log

ISSUE: No MTA installed, discarding output

cron默认将console output用邮件发送,task有控制台输出但未配置邮件则记如上日志,可以将console output重定向到文件

1
echo "" > /home/QQs/corn.log

若task执行中间过程有报错,报错信息仍会以Email形式发送,非安装邮件服务器不能解决
安装postfix

注意,crontab task 若以root权限执行(syslog中可以看到),root若无重定向日志文件的写权限,仍然会报 “No MTA installed, discarding output”

重载CRON服务

1
sudo service cron reload

启动/关闭
1
sudo service cron start/stop

Ubuntu timezone

显示时间:

1
date -R

某年月日,测试scheduled tasks,总是无法在期望时间触发,经查系统时间为‘Universal Time’(即格林威治时间时间)
输入
1
tzselect 

选择本地时间(shanghai)

永久覆盖本地时间设置
1
cp /usr/share/zoneinfo/Asia/Shanghai  /etc/localtime

设置时间
1
date -s '2020/3/1 18:20:30'

Linux 以一定时间间隔更新内核时间,做上述调整后立即重启,可以将设置写入内核,否则会被更新回来(QQs尚未验证重启操作)

异常:刚启动正常,很短时间后,所有端口无法连接

事实证明,Linux与Windows发生IP冲突,Linux无法竞争过Windows,(反之未验证),现象是刚刚启动时连接正常,很短时间后无法连接。
再次遭遇此问题,Linux虚拟机IP被抢,virtualbox桥接,宿主计算机访问虚拟机无碍,其他计算机可以ping通,telnet不通(目标ip实际上是windows机器)

user

1
su -l USERNAME

chmod

Linux/Unix 的文件调用权限分为三级 : 文件所有者(Owner)、用户组(Group)、其它用户(Other Users)。

例如 owner - RWX, group - RX, other - R_

如上,u 表示该文件的拥有者,g 表示与该文件的拥有者属于同一个群体(group)者,o 表示其他以外的人,a 表示这三者皆是。

1
chmod [ugoa...][[+-=][rwxX]

栗子
1
2
chmod ugo+r file1.txt //将文件 file1.txt 设为所有人皆可读
chmod a+r file1.txt // 同上

其他参数

  • -R 对目录递归变更权限

    查看端口使用

    1
    sudo lsof -i:8080

    curl

    即client url tool curl 的用法指南

dos2unix

windows&linux交叉编译环境下脚本结尾标记可能出错

Bash syntax error: unexpected end of file

使用dos2unix工具转换脚本文件

1
dos2unix task.sh

LoopBack 是一个可扩展的开源Node.js 框架。它可以让我们

  • 无需写任何代码(或少量的代码)来创建REST API
  • 访问任意数据库中的数据甚至是外部的REST API
  • 可以在API上定义关系型数据模型和访问限制(ACL)
  • 在移动APP中使用地理位置,文件访问和推送消息
  • 提供 Android, iOS 和 JavaScript SDKs快速创建有数据支持的应用程序
  • 方便的应用部署,无论在云上还是自己的服务器
1
npm install -g @loopback/cli
1
lb4 app

昨天法国人发我一资料,前置工作参照一教程安装nodejs环境,使用apt-get install完了,node -v 一看,是4.x.x的版本

实际上,这种情况下应该先更新软件的源

1
2
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
sudo apt-get install -y nodejs

dpkg was interrupted, you must manually run ‘sudo dpkg —configure -a’ to correct the problem

之前调用apt-get install时出现上述异常
需要

1
2
3
cd /var/lib/dpkg/updates
sudo rm *
sudo apt-get update

这里提一下update和upgrade的区别:

update 是更新软件库列表

upgrade 是在上述基础上将本地软件安装升级

处理控制台进程无响应

Ctrl C无法终结,按Ctrl Z将进程转到后台执行,然后ps -ef查看进程列表,kill无响应的进程

ubuntu lts 的IP设置

1
sudo nano /etc/netplan/01-xxxx.yaml

这个配置文件内容如下例

1
2
3
4
5
6
7
8
9
10
network:
version: 2
renderer: NetworkManager
ethernets:
enp0s3:
addresses:
- 192.168.1.100/24
gateway4: 192.168.1.1
nameservers:
addresses: [8.8.8.8, 4.4.4.4]

enp0s3为配置的ethernet(以太网)网络接口 用 ip link show 命令显示网络接口列表
1
2
3
# netplan try 若配置正确 这个命令会应用上面的配置 并提示是否退回之前的设置
sudo netplan apply
# sudo netplan --debug apply

查看发行版本号

1
cat /etc/issue

一切可以用JavaScript实现的,终将用Javascript来实现

Node.js

官方定义

Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js’ package ecosystem, npm, is the largest ecosystem of open source libraries in the world.

Node 是js的一种新的运行环境,基于Chrome V8 js引擎开发,以事件驱动和无阻塞IO模型实现轻量和高效. npm是Node包管理生态系统,目前是世界最大的开源库。

关于CommonJS

CommonJS规范————阮一峰
CommonJS规范是旨在解决Javascript的作用域问题,其规定每个文件就是一个模块,有其自己的作用域,一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见(例外的文件之间分享使用global全局变量)模块必须通过 module.exports 导出对外的变量或接口,通过 require() 来导入其他模块的输出到当前模块作用域中。

require是同步执行的,只有加载完成,才能执行后面的操作 浏览器端一般遵循异步模块定义(asynchronous module definition, AMD)协议

module

上文所述,每个文件就是一个模块,在每个模块/文件内部,都有一个module对象,该对象存在以下属性

  • module.id 模块的识别符,通常是带有绝对路径的模块文件名。
  • module.filename 模块的文件名,带有绝对路径。
  • module.loaded 返回一个布尔值,表示模块是否已经完成加载。
  • module.parent 返回一个对象,表示调用该模块的模块。(可以判断是否为应用入口)
  • module.children 返回一个数组,表示该模块要用到的其他模块。
  • module.exports 表示模块对外输出的值。

Express.js

Express 是一个简洁而灵活的 node.js Web 应用程序框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。

server.js

1
2
3
4
5
6
7
8
9
10
const express = require('express');
const app = express();

app.get('/',function(req, res){
res.send('hello express')
});

const listener = app.listen(8080, function(){
console.log('express app is running on port '+listener.address().port)
})

插一句,require和import

vscode 建议我将上面第一行代码改为‘import express from ‘express’’

Require是CommonJS的语法,CommonJS的模块是对象,输入时必须查找对象属性。
1
2
3
4
declare module.fs{
function stat(){}
//...
}

1
2
3
4
5
6
7
let { stat, exists, readFile } = require('fs');

// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;

ES6模块不是对象,而是通过export命令显示指定输出代码,再通过import输入。import的可以是对象定义或表达式等
express封装了http method 和 router
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/** A first working Express Server */
app.get('/',function(req, res){
res.send('Hello Express')
})

/** Serve an HTML file */
app.get('/views/index.html',function(req,res){
let absolutePath = __dirname + '/views/index.html'
res.sendFile(absolutePath)
})

/** Serve static assets */
app.use('/public', express.static( __dirname + '/public'))
// 内置中间件函数,访问静态资源文件

/** params add a '?' if the parameter is omissible */
app.get("/api/timestamp/:date_string/:addr_string?",function(req,res){
res.json(req.params.date_string)
})
/** Request Headers */
app.get("/api/whoami", function (req, res) {
var ip = req.header('x-forwarded-for') || req.connection.remoteAddress;
var lang = req.header('Accept-Language');
var software = req.header('User-Agent');
console.log({"ip":ip,"language":lang,"software":software})
res.json({"ip":ip,"language":lang,"software":software});
});

请求参数的获取方式

  • path中的变量,形如/api/user/:userId, 用req.params.userId
  • url参数如?org=dw001&type=1,将直接结构化为req.query对象
  • post请求的RequestBody,使用bodyParser中间件,添加到req.body中
  • req.param(parameterName)方法

中间件middleware

Express是一个自身功能极简,完全是路由和中间件构成一个web开发框架:从本质上来说,一个Express应用就是在调用各种中间件。

1
2
3
4
5
6
7
8
app.get('/now', function(req, res, next){
let now = new Date().toString();
req.time = now;
next();
},
function(req, res){
res.json({time: req.time})
})

大致是express().[method]([path],[middleware],(req,res)=>{…})

可以引用第三方中间件函数

body-parse

将post body内容编码并放入req.body

1
2
3
4
5
6
7
8
var bodyParser = require('body-parser');

app.use(bodyParser.urlencoded({ extended: false }));

/** Get data form POST */
app.post('/name',function(req, res){
res.json({name: req.body.first + ' ' + req.body.last})
})

1
2
3
4
5
6
7
const cookieparser = require('cookie-parser')
const util =require('util')

app.use(cookieparser())
app.get('/getcookie',function(req, res){
res.send(util.inspect(req.cookies))
});

util.inspect类似于JSON.stringify将json对象属性以{key}={value};的字符串格式输出

multer

文件上传

Multer 会添加一个 body 对象 以及 file 或 files 对象 到 express 的 request 对象中。 body 对象包含表单的文本域信息,file 或 files 对象包含对象表单上传的文件信息。 GitHub:multer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var _fs = require('fs') 
var multer = require('multer')

const upload = multer({dest:'/tmp'})
app.post('/files/upload', upload.single('file'), function(req,res){ // image是input [type='file'] 的name属性 或 formdata的field名
console.log(req.files[0])
var des_file = __dirname + '/tmp/' +req.files[0].originalname;
_fs.readFile(req.files[0].path, function(err, data){
_fs.writeFile(des_file, data, function(err){
var response={}
if(err){
console.log(err)
}else{
response={
message:'File uploaded successfully',
filename:req.files[0].originalname
}
}
console.log(response);
res.end(JSON.stringify(response))
})
})
})

Multer 接受一个 options 对象,其中最基本的是 dest 属性,这将告诉 Multer 将上传文件保存在哪。如果你省略 options 对象,这些文件将保存在内存中,永远不会写入磁盘。 关于options

环境变量

服务端口号变量控制

1
2
3
4
// listen for requests
const listener = app.listen(process.env.PORT, function() {
console.log('Your app is listening on port ' + listener.address().port);
});

unix shell prompt:
1
2
3
4
export PORT=1234
echo env|PORT

unset PORT

windows CMD
1
2
3
4
# 设置
set PORT=1234
# 移除
set PORT=

windows powershell
1
2
3
$env:PORT = 1234

del env:PORT

cross-env

从package.json获取版本作为环境变量

1
cross-env REACT_APP_VERSION=$(node -p 'require(\"./package.json\").version')

关于Node.js的系统学习

Node.js的实现的学习才应该是你要学的Node.js本身,而不是无尽的工具和第三方库。

参考官方文档

外部服务访问静态文件也会有跨域问题, 解决方法:

1
2
3
4
5
6
let options = {
setHeaders: function (res, path, stat) {
res.set('Access-Control-Allow-Origin', '*')
}
}
app.use(express.static('public', options))

application performance

使用chrome devtoolProfile和Memory
Easy-Monitor
[阿里Node.js性能平台](https://cn.aliyun.com/product/nodejs)

to be continued…

Tips

path.resolve vs path.join

express-async-errors

Limit repeat requests

AES加密算法 CSC模式:通过密钥和salt(起扰乱作用)按固定算法(md5)产生key和iv。然后用key和iv(初始向量,加密第一块明文)加密(明文)和解密(密文)。

对称加密 Symmetric Encryption

共享密钥加密 私钥加密算法。加密和解密时使用相同密钥,或是使用两个可以简单相互推算的密钥。事实上,这组密钥成为双方或多个成员之间的共同秘密,以便维持专属的通讯联系。

要求雙方取得相同的密鑰是對稱密鑰加密的主要缺點之一
对称加密的速度比公钥加密快很多,在很多场合都需要对称加密。

進階加密标准(Advanced Encryption Standard,AES),对称加密中最流行的算法之一。

非对称加密

公开密钥密码,公钥加密算法。
要两个密钥,一个是公开密钥,另一个是私有密钥;一个用作加密,另一个则用作解密。使用其中一个密钥拿明文加密后所得密文,只能用相对应个另一个密钥才能解密得到原本明文;甚至连最初用来加密个密钥也不能解。

RSA是最具影响力的非对称加密算法,三个字母是三位创始人的姓氏首字母。RSA算法基于一个十分简单的数论事实:将两个大质数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。

阮一峰的文章

RSA算法原理1

RSA算法原理2

非对称加密另有一个用途是数字签名

Digital Signature
网站对敏感内容使用私钥生成摘要信息,该信息使用公钥解密后可以证实为由私钥所生成(?),且反映内容是否已被篡改,故可作为数字签名

为避免不怀好意的第三方冒充网站,提供公钥并发送密文给客户,应运而生”证书中心”(certificate authority,CA)。CA使用其私钥为官方公钥及其他信息加密,生成数字证书(Digital Certificate)。客户使用CA提供的公钥从数字证书中获取真实的网站公钥。

https协议

客户端向服务器发出加密请求。

服务器用自己的私钥加密网页以后,连同本身的数字证书,一起发送给客户端。

客户端(浏览器)的”证书管理器”,有”受信任的根证书颁发机构”列表。客户端会根据这张列表,查看解开数字证书的公钥是否在列表之内。

如果数字证书记载的网址,与你正在浏览的网址不一致,就说明这张证书可能被冒用,浏览器会发出警告”此网站的安全证书有问题 单击此处关闭该网页 继续浏览此网站(不推荐)”。

author

description

license

keywords

dependencies 和 devDependencies, peerDependencies, bundledDependencies, optionalDependencies

  • npm install 命令 不加 —save 不会修改package.json
  • npm install 命令 —save参数将package记入dependencies,—save-dev参数将package记入devdependencies
  • 开发工具如glup,angular脚手架自动安装的jasmine、karma、tslint、chai等,以致于@angular/cli自身,@angular/compiler-cli等工具,以及用于编译器做类型识别的各种@type/XXX 应该放devdependencies
  • 运行环境如Express,前端框架@angular/core以及可选包forms、animation等,弥补浏览器内核、版本等差异的腻子如core.js,应为dependencies 参考Angular Doc: npm packages
  • 封装node.js api第三方工具如操作xml、mail、excel等,是业务不可或缺的,应属dependencies
  • 重新编译electron的如node-ffi私以为其实并不会进入构建结果,应为dependencies

    当有人准备在自己的程序中下载使用你的模块时,估计他们不想下载构建你使用的外部测试或者文档框架,为此宜将这些依赖项放在devDependencies里 npm doc

peerDependencies: peer意为同等地位的人,同龄人。peerDependencies将该模块的树形的依赖关系摊平到宿主的依赖环境中

bundledDependencies: 将依赖的包与当前模块绑定在一起, 如发布一模块做如下配置,npm pack该模块将获得包含”renderized”和”super-streams”的awesome-web-framework-1.0.0.tgz,package version另需在dependencies中指定

1
2
3
4
5
6
7
{
"name": "awesome-web-framework",
"version": "1.0.0",
"bundledDependencies": [
"renderized", "super-streams"
]
}

optionalDependencies 可选的依赖,有的依赖模块依赖于特定的运行环境,因此optionalDependencies的依赖项在安装失败的情况下不会影响整个安装结果

语义版本控制(Semantic Versioning)

MAJOR version when you make incompatible API changes,

MINOR version when you add functionality in a backwards-compatible manner, and

PATCH version when you make backwards-compatible bug fixes.

Use the tilde-character (~) to prefix the version of moment in your dependencies and allow npm to update it to any new PATCH release(补丁).

Use the caret-character (^) to prefix the version of moment in your dependencies and allow npm to update it to any new MINOR release(小版本).

Git做版本管理及CHANGELOG

本地依赖

1
"qqsmodule": "file:../CustomModules/qqsmodule"

package-lock

Node Docs: package-lock

  • 记录整个依赖树的具体版本, 即包括了依赖的依赖,
  • 须提交package-lock 在npm install时安装指定的版本
  • 每次npm update时依据package.json中的升级版本设置修改package-lock.json

version lens

VS Code extension 鼠标悬停在dependency的项上,可查看最新版本和依赖项目链接

关于版本号和构建编号

The standard build numbering convention makes use of a fourth numerical indicator which is appended
to the release number, where the fourth indicator is the build number.

  • For verification builds, the build number starts with a “1”, and increments with each successive
    build. For each successive release, the build indicator starts again with zero’s.
  • For development builds, the build number starts with “20001” and increments with each
    successive build. For each successive release, the build indicator starts again with zero’s.

scripts

QQs:TS相比ES————静态类型,代码的可读性和可维护性
TS相比ES不只是静态类型,还有Class Interface Generics(泛型) Enum等
辩证地看,也有它地缺点如学习成本,搭框架地额外成本,与js库的兼容性,额外的编译过程等
官方Doc
在线编译器

调试

方法一

npm install typescript

add tsconfig.json

1
2
3
4
5
6
7
8
9
10
11
12
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"noImplicitAny": true,
"outDir": "./dist",
"sourceMap": true
},
"include": [
"src/**/*"
]
}

add .vscode/tasks.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "typescript",
"tsconfig": "tsconfig.json",
"problemMatcher": [
"$tsc"
],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

Terminal—Run Task—Choose tsconfig.json

add .vscode/launch.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"version": "0.2.0",
"configurations": [
{
"name": "launch",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/dist/main.js",
"args": [],
"cwd": "${workspaceRoot}",
"protocol": "inspector"
}
]
}

Run Debugging(Choose ‘launch’, the name definited in the launch.json)
方法二

npm i typescript node-ts

add tsconfig.js
1
2
3
4
5
6
7
8
9
10
11
12
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"noImplicitAny": true,
"outDir": "./dist",
"sourceMap": true
},
"include": [
"src/**/*"
]
}

add .vscode/launch.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"version": "0.2.0",
"configurations": [
{
"name": "Current TS File",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/node_modules/ts-node/dist/_bin.js",
"args": [
"${relativeFile}"
],
"cwd": "${workspaceRoot}",
"protocol": "inspector"
}
]
}

基本类型

  1. number
  2. boolean
  3. string
  4. []
  5. enum
  6. any
  7. void
  8. null 和 undefined
  9. never

关于枚举
定义一组常量

1
2
3
4
5
6
enum Direction {
Up = "↑",
Down = "↓",
Left = "←",
Right = "→",
}

类似map的用法
1
2
3
4
5
6
switch(key){
case Direaction.Up:
console.log('direction is up');
break;
...
}

类似interface的用法, 如 function Foo(direct: Direaction)

“类型谓词”

1
2
3
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}

定义类型保护函数isFish用以区分一个联合类型(Fish | Bird)的变量,依据是Fish类型存在swim属性
其意义无非就是把下列代码
1
2
3
4
5
6
if ((<Fish>pet).swim) {
(<Fish>pet).swim();
}
else {
(<Bird>pet).fly();
}

改为
1
2
3
4
5
6
if (isFish(pet)) {
pet.swim();
}
else {
pet.fly();
}

多数情况下还是用 typeof 和 instanceof

let 和 const 同es6

let const 声明的变量只在当前代码块中有效

1
2
for (var i = 0; i < 10; i++) {}
console.log(i); //10
1
2
for(let j = 0; j < 10; j++) {}
console.log(j);// Error: j is not define

以前需要立即执行表达式(IIFE)解决的问题

1
2
3
4
5
6
7
for (var k = 0; k < 5; k++) {
(function (k) {
setTimeout(function () {
console.log(k); //输出0,1,2,3,4
},0);
})(k);
}

1
2
3
4
5
for (let j = 0; j < 5; j++) {
setTimeout(function () {
console.log(j); //输出0,1,2,3,4
},0);
}

不存在变量提升
1
2
3
4
5
console.log(foo); // 输出undefined
console.log(bar); // 报错ReferenceError

var foo = 2;
let bar = 2;

不允许重复声明

暂时性死区

即不允许在声明位置之前调用该变量

const 声明引用值不允许修改,然而const的对象内部状态是可以修改的。区分声明只读类型关键字readonly

1
readonly GIPX = 0x1a;

解构

1

接口

鸭子类型

1
2
3
4
5
6
7
8
9
10
interface LabelledValue	
{
label: string;
}
function printLabel(labelledObj:LabelledValue)
{
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

可见,参数对象并非实现接口,只需对外表现接口的特性

另外,接口里的属性可以定义为非必须的

1
2
3
4
interface SquareConfig { 
color?: string;
width?: number;
}

定义可索引的类型

1
2
3
4
5
6
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];

如上,是一个接口的定义,符合该接口的属性可以number类型为索引。

另,索引亦可为string类型。注意当同时使用两种类型的索引,数字索引的返回值必须是字符串索引返回值类型的子类型。 这是因为当使用 number 来索引时,JavaScript会将它转换成 string 然后再去索引对象。 就是说用 100 (一个 number )去索引等同于使用 “100” (一个 string )去 索引,因此两者需要保持一致。

interface 和 type:
如上所述,interface是接口,是一种规范,简单的对功能的抽象。接口可以用extends扩展(栗子略了)
type是类型, 多次声明的接口定义是扩展叠加的关系

1
2
3
4
5
6
7
8
9
10
interface WidgetProps{
click:any
}
interface WidgetProps{
drag:any
}
CanWidgetProperty : WidgetProps = {
click: handleClick;
drag: handleDrag;
}

type顾名思义是类型,可以是基础类型(或其组合,如用逻辑|的组合)的别名。与interface相比似乎更加固定、安全(不似interface灵活)

实现多个接口:

1
let person: Person & AdditionalProperties

实现接口..

es6是没有constructor的,可以再琢磨下js原型篇。
ts中,类是具有两个类型:静态部分的类型和实例的类型。静态部分是类定义本身,实例部分就是生成的类的对象
constructor 存在于类的静态部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick();
}

function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
return new ctor(hour, minute);
}

class DigitalClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("beep beep");
}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("tick tock");
}
}

let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);

这个是官方示例代码。为了实现定义一个符合ClockInterface接口规范的createClock方法。
而且应将符合ClockConstructor接口规范的类型作为返回值得类型

泛型

1
2
3
4
function identity<T>(arg: T): T 
{
return arg;
}

在es中class本质是函数,故class也可将泛型作为构造方法参数,并加以类型约束

1

infer

1
type ParamType<T> = T extends (param: infer P) => any ? P : T;

infer表示P是待推断的参数类型,如T

never

不会返回结果的类型,一直是while(true)的函数,或者一定会抛出异常的函数

模块

export 和 import

namespace(存目)

从js迁移

参考TS Doc

元组

与数组的唯一区别是依次逐项指定了类型
适用于特定的数据元结构,比较现实的场景如csv文件的row

1
const newRow:[number,string,boolean] = [1,'老王',true]

装饰器 Decorator

装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息作为参数传入。

1
2
3
4
5
6
7
8
9
10
11
import "reflect-metadata";

const formatMetadataKey = Symbol("format");

function format(formatString: string) {
return Reflect.metadata(formatMetadataKey, formatString);
}

function getFormat(target: any, propertyKey: string) {
return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
}

这里用到了反射

1
2
3
4
5
6
7
8
9
10
11
12
class Greeter {
@format("Hello, %s")
greeting: string;

constructor(message: string) {
this.greeting = message;
}
greet() {
let formatString = getFormat(this, "greeting");
return formatString.replace("%s", this.greeting);
}
}

运行时变量属性未在类型声明中

使typeScript 识别未声明的属性 除了parameter:any 有以下几种方法更好的做法

  • 重新声明中变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // types.d.ts
    import 'some-library';

    declare module 'some-library' {
    export interface Person {
    [key: string]: any; // 添加索引签名
    email?: string; // 添加可选属性
    }
    }
  • 类型断言
    1
    2
    3
    4
    5
    let personWithTypeAssertion: Person = {
    name: "John Doe",
    age: 30,
    additionalProperty: "Some value" // TypeScript 不会报错
    } as Person & { additionalProperty: string };

    typescript-eslint

    Typescript-ESLint

面试必备

typescript的特点

  • 提供面向对面编程(OOP)的特性 如 类,接口,模块
  • 静态类型检查
  • ES6特性 箭头函数 变量声明等
  • 可选参数
  • 内置类型

优点和技巧

  • 静态类型
  • 扩展名为.d.ts的Definition文件提供对现有JavaScript库(如Jquery,D3.js等)的支持。

troubleshooting

fork-ts-checker-webpack-plugin error in undefined(undefined,undefined)

该插件与typescript类型检查有关 该报错原因难以定位 修改typescript到旧版本解决
场景:

1
2
3
4
"@craco/craco": "^6.0.0",
"@types/node": "^14.14.16",
"react": "^17.0.1",
"typescript": "4.1.3",

该职位期望招聘高级软件开发人员 以完成基础软件开发 具备独立搭建框架能力和较丰富的前后端开发经验

1介绍一下原型链

2变量提升

  • js中变量和函数的声明都会被提升到函数最顶部
  • 这使得变量和函数在coding时可以先调用在声明

    2属性绑定实现视图更新的原理

    Angular的双向绑定原理:

React的虚拟dom和diff算法
虚拟dom是轻量化的js对象 用于描述真实的dom 当状态改变时 react通过比较虚拟dom的差异 最小化改动真实dom 从而提升性能

3编写单元测试用例

4只执行一次的生命周期钩子 constructor init之间进行了什么

钩子 目的

ngAfterContentInit | 在Angular将外部内容放到视图内之后。
ngAfterContentChecked | 在Angular检测放到视图内的外部内容的绑定后。
ngAfterViewInit | 在Angular创建了组件视图之后。
ngAfterViewChecked | 在Angular检测了组件视图的绑定之后。

constructor 依赖注入

5依赖注入 反射

6面向对象的核心——抽象

封装 继承 多态是面向对象的三个核心特征 对应的目的性是是代码具备可重用性、可扩展性,即可减少重复代码,进而使程序增加可维护性

面向对象未必是先进的,编程模式要根据需求而定,避免脱裤子放屁,近年流行的函数式编程就是与面向对象相斥的,在函数式编程中减少使用保存状态的‘对象’

7docker

8持续集成

前端表格1000w行数据流畅渲染

deepseek —— 前端

一、技术深度扩展能力
核心原理掌握

深入解释DOM树渲染机制(参考论文提及的节点树形结构抽象能力)

阐述AJAX异步通信的底层实现(如XHR对象与Fetch API差异)

展示CSS渲染层合成原理(如重排/重绘优化策略)

性能优化体系

优化维度 具体措施 考核要点
网络层面 HTTP/2协议应用、CDN加速策略 减少RTT时间
资源加载 按需加载、Tree Shaking 首屏加载时间优化
代码执行 Web Workers多线程优化 主线程阻塞规避

二、工程化实践能力
架构设计思维

展现B/S架构系统设计经验(参考论文的三层架构设计)

演示模块化开发能力(如Web Components应用)

数据库优化方案(MySQL索引策略、NoSQL选型)

质量保障体系

单元测试覆盖率(Jest/Mocha)

E2E测试实施(Cypress/Puppeteer)

持续集成部署(Jenkins/Travis CI)

三、软性能力表现
沟通演示技巧

使用语音模拟系统进行技术方案阐述(参考论文的语音面试模块)

白板编码时标注关键路径时间复杂度

职业形象管理

VR试衣系统准备的职业着装认知(参考论文的虚拟试衣功能)

简历制作突出开源项目贡献(GitHub Star/Fork数据)

四、前沿技术敏感度
新兴技术栈

WebAssembly在性能敏感场景的应用

Serverless架构的BFF层实践

WebGL/WebXR三维交互开发经验

标准演进跟踪

CSS Houdini底层API实践

ECMAScript提案阶段特性预研

W3C新规范解读(如WebGPU)

建议候选人准备3-5个典型优化案例,量化展示性能提升指标(如首屏加载从2.1s优化至780ms),并携带移动端适配、跨平台框架(Electron/Flutter)等扩展能力的实证材料。