【笔记-node】《imooc-nodejs入门到企业web开发中的应用》

目录课程名备注
入门必学nodejs入门到企业web开发中的应用
框架与工具node.js+koa2+mysql打造前后端分离精品项目《旧岛》
项目实战

20190317-20200720:《imooc-nodejs入门到企业web开发中的应用》

第01章 课程内容介绍

01-01 导学

一、课程安排
1、nodejs核心api
2、静态资源服务器 && http协议
3、项目代码构建、打包
4、api 单元测试&ui测试
5、headless爬虫
6、回顾总结
二、nodejs创建项目
1、cd Desktop
2、express mockData
3、cd mockData
4、npm i
5、npm start
(aSuncat) 三、node热更新
1、npm install --sava-dev nodemon
2、项目根目录新增文件nodemon.json

{
  "restartable": "rs",
  "ignore": [
    ".git",
    ".svn",
    "node_modules/**/node_modules"
  ],
  "verbose": true,
  "execMap": {
    "js": "node --harmony"
  },
  "watch": [
	"routes/"
  ],
  "env": {
    "NODE_ENV": "development"
  },
  "ext": "js json"
}

watch: routes/指的是监控routes文件夹下的所有文件
ext:js json指的是监控后缀为js json的文件
3、package.json
scripts添加
"hotStart": "nodemon"
4、浏览器输入localhost:3000就能访问到网页了。
四、如果是创建最简单的服务
1、创建一个js,server.js

var http = require('http');

http.createServer((req, res) => {
  console.log(`request come:${req.url}`)
  res.end('123');
}).listen('8080');

console.log('请求成功了');

2、node server.js
3、浏览器输入localhost:8080,就能看到结果了。

第02章 nodejs是什么,为什么偏爱Nodejs

02-01 nodejs是什么

一、nodejs
1、非阻塞I/O
input output,计算机输入输出,键盘,显示机,打印机,都是I/O设备,读写磁盘,网络操作
2、阻塞:I/O时进程休眠等待I/O完成后进行下一步
3、非阻塞:I/O时函数立即返回,进程不等待I/O完成
4、cpu:计算机1秒钟执行30亿条指令
二、事件驱动
1、I/O等异步操作结束后的通知
2、观察者模式

02-02 nodejs究竟好在哪里

一、nodejs好处/nodejs适用场景
1、处理高并发、I/O密集的web场景性能优势明显
二、cpu密集 vs I/O密集
cpu密集:压缩、解压、加密、解密(计算、逻辑判断)
I/O密集:文件操作、网络操作、数据库
三、web常见场景
1、静态资源获取
2、数据库操作
3、渲染页面:读取模板文件,生成html
四、高并发应对之道(高并发:单位时间内访问量特别大)
1、增加机器数
2、增加每台机器的cpu数-多核
五、进程
1、进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位
2、多进程:启动多个进程,多个进程可以一块执行多个任务。
3、单进程,一个cpu只开一个进程,一个进程只开一个线程。
4、单线程只是针对主线程,I/O操作系统底层多线程调度。
5、单线程并不是单进程
六、主进程 event loop
七、单线程
1、单线程的好处:
(1)多线程占用内存高
(2)多线程间切换使得CPU开销大
(3)多线程由内存同步开销
(4)编写单线程程序简单
(5)线程安全
2、单线程的劣势:
(1)CPU密集型任务占用CPU时间长
(2)无法利用CPU的多核
(3)单线程抛出异常使得程序停止
八、常用场景
1、web server
2、本地代码构建
3、使用工具开发

第03章 环境&调试

03-01 commonjs1

一、环境
1、安装nodejs
nodejs.org进入官网
二、commonjs
1、commonjs是nodejs使用的模块规范
2、没有window这个全局对象,有window
3、process
4、每个文件是一个模块,有自己的作用域
5、在模块内部module变量代表模块本身
6、module.exports属性代表模块对外接口
三、node
1、 node test.js // 运行test.js
2、node --inspect-brk test.js 调试test.js,在chrome浏览器的调试工具f12的sources的test.js中就能看到调试信息

(function(exports, require, module, _filename, __dirname) { // module代表模块本身
	console.log('this is a test');
}
);

3、chrome打开inspects

03-02 commonjs2

一、require规则
1、/表示绝对路径,./表示相对于当前文件的路径。
2、支持js、json、node扩展名,不写依次尝试
3、不写路径则认为是build-in模块或者各级node_modules内的第三方模块
二、require特性
1、module被加载的时候执行,加载后缓存(当我们加载一个模块时,这个模块里的内容都会被执行)
2、一旦出现某个模块被循环加载(A require B, B require A),就只输出已经执行的部分,还有未执行的部分不会输出。

03-04 引用第三方模块

一、异步I/O,执行之后立即返回,不在乎结果是什么
二、buffer:缓冲,二进制数据处理
三、fs是用来操作二进制流的
四、npm install… 的时候,会创建node_modules,把相关依赖下载到node_mobules;
五、npm root -g 得到全局的node_modules依赖

03-05 imports与exports的区别

一、exports是module.exports的快捷方式

exports.test = 1; // 等同于module.exports.test = 1;

二、不能改变exports的指向

exports = { // exports修改了指向,不再生效了
	a: 1,
	b: 2,
	test: 3
}
module.exports = { // 应该写成module.exports,这是exports的指向仍是module,所以会生效
	a: 1,
	b: 2,
	test: 3
}

03-06 global变量

一、global自带属性方法
1、CommonJS
2、Buffer、 process、 console
3、timer:setTimeout()、setInterval()、clearTimeout()、clearInterval()、setImmediate
二、global特性
1、直接在模块中写的是局部变量。
2、模块中写了global会变成全局变量

testStr = '1'; // 局部变量
global.testStr = '2'; // 全局变量

03-07 progress进程

一、process 进程
uncaughtException :异常的捕获
1、process属性

const {argv, argv0, execArgv, execPath} = process; // argv:启动process时所有的参数; argv0:保存了argv第一个值的引用(不常用);execArgv:调用node传入的特殊参数(写在文件名之前的参数是不会进入argv的统计的); execPath:调用它的脚本的路径
argv.forEach(item => { // node安装的路径,当前文件路径
  console.log(item);
});

2、process方法
process.cwd():当前process执行的路径

// 1
setImmediate(() => {console.log('setImmediate')}) // 等下一个事件队列, 与时间无关。放在下一队列的队首。比setTimeout()慢
// 2
process.nextTick(() => { // 当前事件队列中的东西执行完了,再执行它。比setImmediate执行得早。放在当前队列的队尾。比setTimeout() 快
	console.log('nextTick');
})

一般情况下用setImmediate()
如果nextTick()循环调用,后面正常的异步就没办法执行了。

03-08 debug1

一、调试
1、inspector
进入官网,node-docs-inspector,可以看到api
控制台执行node --inspect-brk test.js
2、vscode:编辑器

第04章 nodejs基础api

04-01 path1

一、path:处理和路径相关的一切
path.join()也会调用nomalize
path.resolve():根据相对路径,得到绝对路径
const {basename, dirname, extname} = require('path'); // basename:文件名;dirname:所在路径;extname:扩展名
process.env.PATH可以看到path
二、nodejs.cn:nodejs中文网站

04-02 path2

一、path
1、__dirname、__filename总是返回文件的绝对路径
2、process.cwd()总是返回执行node命令所在文件夹
二、./
1、在require方法中总是相对当前文件所在文件夹
2、在其他地方和process.cwd()一样,相对Node启动文件夹

04-03 buffer1

一、常用的:file和网络
二、buffer三个要点
1、buffer用于处理二进制数据流
2、实例类似整数数组,大小固定
3、c++代码在v8堆外分配物理内存
三、nodejs跑的代码并不纯粹是用javascript跑的,还有一部分是c++
四、buffer是一个全局对象

04-04 buffer2

一、buffer静态属性方法(类的方法)

Buffer.byteLength();
Buffer.isBuffer();
Buffer.concat();

二、实例常用的属性和方法

buf.legnth
buf.toString() // 默认是utf-8,buf.toString('base64')
buf.fill()
buf.equals()
buf.indexOf()
buf.copy()
04-05 buffer3

一、中文乱码问题

const StringDecoder = require('string_decode');
const decoder = new StringDecoder('utf8');

const buf = Buffer.from('中文字符串'); // 3个字符表达一个汉字
for (let i = 0; i < buf.legnth; i += 5) {
  const b = Buffer.allocUnsafe(5);
  buf.copy(b, 0, i);
  // console.log(b.toString()); // 乱码
  console.log(decoder.write(b)); // 正常
}
04-06 event1

一、events

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
myEmitter.on('error', (err, time) => {
  console.log('触发事件', err);
  console.log('时间戳:', time);
});
myEmitter.emit('error', new Error('test error!'), Date.now());
04-07 event2

一、removeListener('error', fn1);
removeAllListeners('error');

04-08 fs1

一、

const fs = require('fs');
const content = Buffer.from('this is a test.');

fs.writeFile('./test', content, err => {
	if (err) throw err;
	console.log('done');
});

二、可以用stat判断文件是否存在

const fs = require('fs');
fs.stat(filePath, (err, stats) => {
	if (err) {
		res.statusCode = 404;
		res.setHeader('Content-Type', 'text/plain');
		res.end(`${filePath} is not a directory or file`);
		return;
	}
	if (stats.isFile()) {
		res.statusCode = 200;
		res.setHeader('Content-Type',  'text/plain');
		fs.createReadStream(filePath).pipe(res);
	} else if (stats.isDirectory()) {
		fs.readdir(filePath, (err,files) => {
			res.statusCode = 200;
			res.setHeader(res.join(','));
		})	
	}
})

三、建议大家不要用同步的方式去判断,如果在高并发情况下,会阻塞其他用户。
四、

const fs = require('fs');
fs.rename('./text', 'test.txt', err => {
	if(err) throw err;
	console.log('done!');
});

五、watch可以watch任何内容,watchFile只能watch一个文件
六、recursive美 /rɪˈkɜːrsɪv/ adj, 递归的,循环的

第06章 项目实战-静态资源服务器

06-02 静态资源服务器 02

一、node热更新,supervisor,只能监听js文件的变化,不能监听tpl文件的变化。
npm i -g supervisor

06-05 静态资源服务 05

一、模板引擎 handlebars
1、handlebars官网文档:https://www.handlebarsjs.cn/
2、安装

npm i handlebars

3、使用

const Handlebars = require('handlebars');

二、不同目录下启动,得到的相对路径是不同的。
eg:有个文件,路径为~/Users/imooc/test/routes/node01.js,这个文件里写了这段代码

const source = fs.readFileSync('../template/dir.tpl');

其他它想调用的文件路径是~/Users/imooc/test/template/dir.tpl
以下列举不同的两种情况
1、终端: cd ~/imooc/test
node node01.js
此时路径为 ~/Users/imooc/template/dir.tpl
2、终端:cd ~/imooc/test/routes
node node01.js
此时路径为~/Users/imooc/test/template/dir.tpl
三、解决方案:path,不同目录下启动,用相对路径获取文件

const path = require('path');
const tplpath = path.join(__dirname, '../template/dir.tpl');
const source = fs.readFileSync(tplPath);
06-06 静态资源服务器 06

一、compile接收的字符串,readFileSync读出来的是buffer,以下两种处理方式,推荐使用2,因为2更快。
1、utf-8

const source = fs.readFileSync(tplPath, 'utf-8'); // 读出来的是buffer,加上'utf-8',把buffer转换成字符串
const template = Handlebars.compile(source);

2、toString()

const source = fs.readFileSync(tplPath);
const template = Handlebars.compile(source.toString())
06-07 静态资源服务器 07

一、config/defaultConfig.js

module.exports = {
	root: process.cwd();  // 项目根目录
	hostname: '127.0.0.1',
	port: 9527
}

二、如果使用的是require,可以放心地使用相对路径。
三、Content-Type

06-08 压缩文件

一、

compress: /\.(html|js|css|md)/

二、压缩的文件,浏览器支持的压缩方式request的accept-encoding,告诉浏览器我们使用的压缩方式
Accept-Encoding
Content-Encoding

module.exports = (rs, req, res) => {
	const acceptEncoding = req.headers['accept-encoding'];
	if (!acceptEncoding || !acceptEncoding.match(/\b(gzip|deflate)\b/)) {
		return rs;	
	} else if () {
		res.setHeader('Content-Encoding', 'gzip');
	}
}
06-09 range范围请求

一、range: bytes=[start]-[end]
Accept-Ranges: bytes
Content-Range: bytes start-ent/total
二、

module.export = (totalSize, req, res) => {
	const sizes = range.match(/bytes=(\d*)-(\d*)/);
}
06-10 缓存

一、缓存
在这里插入图片描述
二、缓存header
1、Exprires, Cache-Control
2、If-Modified-Since / Last-modified
3、If-None-Match / Etag
三、

if (expires) {
	res.setHeader('Expires', (new Date(Date.now() + maxAge * 1000).toUTCString()))
}
if (cacheControl) {
	res.setHeader('Cache-Control', `public, max-age=${maxAge}`);
}
if (lastModified) {
	res.setHeader('Last-Modified', stats.mtime.toUTCString());
}
if (etag) {
	res.setHeader('Etag', `${stats.size}-${stats.mtime}`);
}
06-11 --cli

一、处理process.args,如-p --port是同种东西,就直接处理成相同的
1、commander
2、yargs
npm i yargs
二、使用

const yargs = require('yargs');
const argv = yargs
	.usage('anywhere [options]')
	.option('p', {
		alias: 'port',
		describe: '端口号',
		default: 9527
	})

chmod +x bin/anydoor 修改执行权限

ll bin/anydoor

三、
1、代码提交代码到gitlab
2、版本号x.y.z
1.2.*和~1.2.0是一样的

06-13 --cli

一、自动打开浏览器
openUrl.js

const { exec } = require('child_process');

module.exports = url => {
	switch (process.platform) {
		case 'darwin': // mac
			exec(`open ${url}`);
			break;
		case 'win32': // windows
			exec(`start ${url}`);
			break;
	}
}

app.js

const openUrl = require('./helper/openUrl');
server.iisten(port, hostname, () => {
	const addr = `http://${hostname}:${port}`;
	console.info(addr);
	openUrl(addr);
});

二、包
1、上传包
(1)登录npm官方站点,注册账号
(2)终端执行
npm login
npm publish
3、使用
npm i -g anydoor
anydoor -p 9906 -d /usr

第07章 本地构建

一、nodejs在前端的用法
1、web server
2、前端开发周边工具
二、npm install gulp -g -D
-D 代表–save-dev
三、如果别人的gulp装在全局,自己把别人的项目download下来后,
在gulpfile.js文件中引入gulpimport gulp from 'gulp'
terminal运行程序时报错:command not found: gulp
1、原因:自己本地没有全局的gulp,只是在本项目中安装了gulp
2、解决方案:
(1)全局安装
(2)方法2:利用npm 的scripts解决
定义一个任务gulp
pacakge.json中添加

"scripts": {
	"gulp": "gulp",
}

node_modules中的.bin目录暴露了自己安装的包的”可执行的引用“,类似于帮我们手工写了快捷方式。
四、*匹配任意个字符
?匹配一个字符
[…]匹配范围内字符
![pattern1|pattern2] 匹配取反
?(pattern1|pattern2) 匹配0或1个
+(pattern1|pattern2) 匹配1或多个
(a|b|c) 匹配任意个
@(pattern|pat
|pat?erN) 匹配特定的一个
** 任意层级匹配

07-02 gulp2

一、rm -rf buld && gulp
二、npm包:del可以删除一个或多个文件

const del = require('del');
gulp.task('clean', () => {
	del.sync('build');
})

三、css自动添加前缀
gulp-autoprefixer
四、css压缩
gulp-clean-css
五、watch就会占用终端,不会退出
1、

gulpfile.js

gulp.task('watch', () => {
	const watcher = gulp.watch('src/**/*', ['default'])
	watch.on('change', event => {})
})

2、启动
(1)方法1:npm run gulp watch
(2)方法2:
package.json

"gulp": "gulp",
"gulp-watch": "gulp watch",
07-03 babel

一、babel
babel is a javaScript compiler
二、
npm install --save-dev babel-presets-env
三、"presets": ["env"] 指的是可以把es6/ es7的语法转换成ecmascript2015
四、babel的plugins可以找到除了"presets": "env"外的其他presets
五、babel在webpack中的使用
1、

loader: "babel-loader",
options: {
	
}

可以在webpack.config.js中使用options,也可以用.bablerc文件

07-06 webpack-module

一、css单独拎出来,extract-text-webpack-plugin
引用import、实例化new、应用plugins
二、externals,
防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。

externals: {
  jquery: 'jQuery'
}

这样就剥离了那些不需要改动的依赖模块.
三、如果不是单页面应用,多个文件都需要用到react, react-dom,则使用vendor,能复用

entry: {
	index: '',
	vendor: ['react', 'react-dom']
},
plugins: [
	new webpack.optimize.CommonsChunkPlugin({
		name: ['vendor', 'runtime']
	})
]

四、运行时,webpack的模块系统,能单独拎出来。
五、tree-shaking,没有用到的方法不被打包进去。比如,lodash中只用到了部分方法,那么没有被使用的方法function就不要打包进去。

plugins = [
	new UglifyJSPlugin()
]

.babelrc需要配置"modules": false

{
	"presets": [["env", {"modules": false}], "react"],
}

第08章

播放不出来,下次再试试

第09章 UI测试常用工具

09-01 UI测试1

一、react组件ui测试,jest
1、和mocha语法非常像
2、.babelrc需要配置
二、dom测试
Enzyme或react的testutils。
三、sinon,测试方法被调用几次
四、selenium-webdriver
1、自动测试
2、可以通过api来模拟用户行为

第10章 案例项目-headless爬虫

10-01 爬虫与反爬虫简介

一、爬虫
1、定义:按照一定规则自动抓取网络信息的程序
二、反爬虫技巧
1、User-Agent:爬虫不是正常的User-Agent, 不过可以伪造
Referer,
验证码:12306
2、单位时间访问次数、访问量
3、关键信息图片混淆
4、异步加载
三、superAgent, 帮我们发送请求,得到html
cheerio,把html的内容转换成像jquery一样的对象
四、cheerio缺点
1、无法绕过反爬虫
五、GoogleChrome/ puppeteer
文档: https://github.com/puppeteer/puppeteer
1、 npm i puppeteer
六、gitignore
1、.gitignore目录一没了,就会报错
所以不能写screenshot/,得写成screenshot/*.png
如果目录是空的,git就不会上传。
2、为了让screenshot文件夹不是空的,在文件夹中放一个空的文件,文件名叫.gitkeeper

10-03 Puppeteer API

一、puppeteer的evaluate很像javascript的el
二、
const browser = await puppeteer.lauch({headless: false}); // default is true
默认打开浏览器

10-05 爬虫代码实现

一、获取页面的元素
page.$(selector)
page.$$(selector)
二、获取页面元素属性
page $eval(selector, pageFunction[, ...args])

10-06 爬虫代码实现2
const { promisify } = require('util');
const writeFile = promisify(fs.writeFile);
const urlToImg = promisify((url, dir, callback) => {
	mod.get(url, res => {
		res.pipe(fs.createWriteStream(file))
			.on('finish', () => {
				callback()
				console.log(file)
			})
	})
})

const base64ToImg = async function (base64Str, dir) {
	// data:image/jpeg;base64,/asdasda
	const matches = base64Str.match(/^data:(.+?);base64,(.+$/))
	const ext = matches[1].split('/')[1]
	const file = path.join(dir, `${Date.now()}.${ext}`)
	await writeFile(file, matches[2], 'base64')
	console.log(file)
}

二、sleep
page.waitFor()

第11章 课程小结

一、nodejs关键技术
1、Stream
2、动态Web framework(express/ koa)
3、child_process & cluster (单线程)
二、深入学习
1、through2
2、express、koa、egg
3、ssr & 同构
4、nodejs源码


20200722-2020:《node.js+koa2+mysql打造前后端分离精品项目《旧岛》》

课程地址:https://coding.imooc.com/class/342.html

第01章 【导学】从0到1手把手教你用nodejs KOA2打造超好用的web框架

01-01 纯正商业级应用Node.js KOA2 开发微信小程序服务号-导学

一、知识点
egg.js think.js
校验器LinValidator
全局异常处理
自动路由注册
koa核心机制
为什么要有洋葱模型
中间件到底怎么用
查找类(class)的属性和方法
异步变成模型
async await
sequelize与MySQL
二、nodejs能力
脱离浏览器运行js
nodejs stream(前端工程化基础)
服务端api
作为中间层
三、CTO
需要设计整个公司架构
需要从全局考虑问题
需要掌握公司最重要的资产:数据(谁掌握数据,谁才有话语权)
四、
双层结构:前端+服务器
三层结构:前端+后端+服务端(返回的数据很纯粹,不关乎业务)

01-02 异步、javascript特性与nodejs

一、服务端语言
1、Python Flask
2、Java SprintBoot
3、NodeJS KOA
二、javascript是基于原型链设计的,越来越向工程化(OO)对齐,用面向对象的方式编程、构建代码。
三、flask django:同步
nodejs是强制用异步的。
四、koa: async await

第02章 【深入理解KOA】koa2的那点事儿与异步编程模型

02-01 软件与环境

一、框架、库
node.js
npm
koa
二、软件、工具
MySQL(xampp)
微信开发者工具
visual studio code
postman
navicat:数据可视化工具
nodemon(自动重启工具,自动重启服务器node server)、pm2(部署程序的工具)
三、长期支持版本 LTS
LTS= Long Term Support 长期支持
四、nvm可以管理不同的node版本

02-02 node一小步,前端一大步

一、vs code 修改语言
1、command + shift + p,打开vs code的命令面板
configue display language,安装自己想要的语言

02-03 koa的精简特性与二次开发必要性

一、数据库
1、悲观锁 乐观锁 事务 脏读 幻读
二、https://github.com/TaleLin,cms框架,vue+koa

02-04 模块加载、es、ts、babel

一、导入包/模块
1、commonJS
2、ES6
import…from
3、AMD
二、javascript新特性制定:https://github.com/tc39
1、es10, babel
2、TS Typescript
(1)比较好的javascript新特性
(2)有特性约束,大型项目中比较好维护
三、koa的内核也是用typescript编写的

02-05 koa的中间件

一、koa 应用程序对象,特点是在对象上有很多中间件

const Koa = require('koa')
const app = new Koa()
app.listen(3001)

二、只有应用程序被阻塞了,才能监听到从前端发过来的http请求
三、将函数注册到对象上,函数就成了中间件
四、发送请求
浏览器
小程序
五、调用服务,可以浏览器地址栏输入以下的任何一种
(1)localhost:3000
(2)127.0.0.1:3000
六、ctx, next是koa内部机制自动传入的参数,不需要开发者管理

app.use(async (ctx, next) => {
	console.log(1)
	await next()
	console.log(2)
})
app.use(async (ctx, next) => {
	console.log(3)
	await next()
	console.log(4)
})

打印 1 3 4 2

02-06 洋葱模型

一、洋葱模型
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

02-08 深入理解async 和await

一、next()的调用结果一定是个promise
const a = next()
const a = await next()

app.use(async (ctx, next) => {
	const a = await next() // 和async配合使用,a是一个promise,即Promise('hello world')
	const b = next() // b得到的是next()直接返回的值,即’hello world‘
})
app.use((ctx, next) => {
	return 'hello world'
})

二、await会阻塞线程,会求值(会计算表达式,包括promise,返回最终值)
三、异步
1、对资源的操作
2、读文件
3、发送http
(1)需安装包:库(axios)
4、操作数据库
(1)需安装包:sequelize
四、总结:需安装的包
axios
sequelize
koa-router

app.use((ctx, next) => {
	const axios = require('axios')
	const res = axios.get('http://7yue.pro')
})

六、如果有async,函数的任何返回值都会被包装成promise
七、面试题:为什么很多中间件函数前面要加async
1、函数用了await,如果不加async,await会报错
八、async, await最早出现在c#语言,异步终极解决方案

02-09 为什么一定要保证洋葱模型

一、怎样保证中间件按洋葱模型执行
使用async, await
二、每个中间件执行的时候,都会被注入ctx
用ctx传值的时候,必须要保证中间件按洋葱模型执行

app.use(async (ctx, next) => {
	await next()
	const r1 = ctx.r3
})
app.use(async (ctx, next) => {
	const axios = require('axios')
	const res = await axios.get('http://7yue.pro')
	ctx.r3 = res
	await next()
})

三、nodejs写的项目,最大并发量比其他框架好

第03章 【让KOA更加好用】 路由系统的改造

03-01 路由

一、koa自己会将数据返回成application/json
二、koa返回数据,ctx.body
三、koa-router使用的3步
1、Router对象实例化
2、router实例对象编写一系列路由函数
3、app.use()注册
四、get post put delete

03-03 多Router拆分

一、版本号携带策略
1、路径 /v1/classic/latest
2、查询参数 /classic/latest?version=v1
3、header
二、修改代码是存在风险的。
开闭原则:编程的时候,对代码的修改是关闭的,对代码的扩展是开放的。
三、对每个版本的api,都写一个路由
四、一旦产生项目循环引用,nodejs是不会报错的。

03-04 nodemon自动重启

一、npm i -g nodemon
二、使用
terminal输入nodemon app.js,实现自动重启
三、全局安装的包是不会出现在项目的package.json里的
四、如果不是全局安装的nodemon,是不能直接使用nodemon app.js的,需要npx nodemon app.js,也可以在packag.json的scripts里面使用

scripts: {
	'start:dev': 'nodemon app.js'
}
03-05 vscode+nodemon调试

一、vscode, 点爬虫,点lauch,点击添加配置,得到launch.json
launch.json里面自定义启动方式

{
	"configurations": [
		{
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen",
      "name": "nodemon",
      "program": "${workspaceFolder}/app.js",
      "request": "launch",
      "restart": true,
      "runtimeExecutable": "nodemon",
      "skipFiles": [
        "<node_internals>/**"
      ],
      "type": "pwa-node"
    },
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "skipFiles": [
        "<node_internals>/**"
      ],
      "program": "${workspaceFolder}/index.js"
    },
    {
      "type": "node",
      "request": "launch",
      "name": "当前文件",
      "program": "${file}"
    }
	]
}
03-06 requireDirectory实现路由自动加载

一、路由自动加载的方式/原理
1、app文件夹中寻找所有的路由模块,自动require进来
2、通过循环的方式,注册在app上
二、require-directory
1、安装
npm i require-directory
2、使用

const requireDirectory = require('require-directory')
const modules = requireDirectory(module, './api/v1')
for (let i in modules) {
	
}

3、Lin CMS可以让module.exports = router和module.exports = { router }都能被处理
四、core/init.js中的requireDirectory(module, '../api/v1', { visit: whenLoadModule }),’…/api/v1’,随着core文件夹放的位置不一样,里面的路径需要随着改变。解决方法
1、path config, 让开发者可以自己配置
2、用绝对路径

const apiDirectory = `${process.cwd()}/app/api`
requireDirectory(module, apiDirectory, {
  visit: whenLoadModule
})

五、如果想得到process.cwd()的值,
调试,当前行process.cwd()打上断点,选中process.cwd(),右键,选择调试求值(evaluate in debug console),就会在控制台打印出结果

第04章 【深入浅出讲异常】异步异常与全局异常处理

04-01 参数获取与LinValidator校验器

一、常见传参方式,参数获取方式
1、url路径
/api/book/detail/:id
(1)获取
ctx.params
2、search
/api/book/detail?id=1
(1)获取
ctx.request.query
3、header
(1)获取
ctx.request.header
4、body
只有post才能在body中传参数
(1)获取
用到npm库koa-bodyparser,中间件

二、浏览器url只能发送get请求,不能发送post请求
三、python 接口参数校验:WTSForms

04-02 异常处理与异常链

一、函数设计
1、判断出异常
return false
return null
2、throw new Error
二、提高写代码的质量:《代码大全2》
三、1/0 ,Infinity
php, python, java, c#都会报错, 0不能为分母
四、ReferenceError是node内置错误
五、如果return false,或者return null会丢失error信息
六、中间件的本质还是一个函数
七、机制:可以监听到任何异常
八、前后端联调的效率不高的很大一个原因是:后端返回的错误信息不够具体

04-03 异步异常处理方案

一、方法内部try{}catch{}是对同步方法有效果,如果方法内有异步方法,有时候try…catch是执行不到的

04-04 全局异常处理中间件编写

一、promise不是通过throw new error返回错误,而是通过reject返回错误信息
二、unhandled promise: promise有未处理的异常,可以用await,因为await能监听到这个异常。
三、全局异常处理,2步
1、全局监听到错误
app.js

const catchError = require('./middlewares/exception')
app.use(catchError)

2、监听到错误之后,输出一段有意义的提示信息
middlewares/exception.js

const catchError = async (ctx, next) => {
  try {
    await next()
  } catch (error) {
    ctx.body = '服务器有点问题,你等一下'
  }
}

module.exports = catchError

四、AOP 面向切面编程

04-05 已知错误与未知错误

一、error信息给前端
1、http status code
2、message
3、error_code:详细,开发者自己定义
4、request_url:当前请求的url
二、错误类型
1、已知型错误
(1)用户传的参数,不符合规则。需要传整型,但是传了字符串。
(2)try… catch
2、未知型错误
(1)程序潜在错误,对开发者来说,是无意识的,或者说开发者根本不知道他出错了
(2)连接数据库,账号、密码输错了

04-06 定义异常返回格式

一、错误返回
app/api/classic.js

  if (!query) {
    const error = new Error('为什么错误')
    error.error_code = 10001
    error.status = 400
    error.request_url = `${ctx.method} ${ctx.path}`
    throw error
  }

二、python在两个单词之间用_, error_code

04-07 HttpException异常基类

一、动态,面向对象方式 一个类
二、继承Nodejs内置的Error对象,因为是Error类型的,所以才能throw error
三、
1、core/http-exception.js

class HttpException extends Error {
  constructor (msg = '服务器错误', errorCode = 1000, code = 400) {
    super()
    this.errorCode = errorCode
    this.code = code
    this.msg = msg
  }
}

module.exports = {
  HttpException
}

2、middlewares/exceptions.js

const { HttpException } = require('../core/http-exception')

const catchError = async (ctx, next) => {
  try {
    await next()
  } catch (error) {
    if (error instanceof HttpException) { // 已知错误
      ctx.body = {
        msg: error.msg,
        errorCode: error.errorCode,
        request: `${ctx.method} ${ctx.path}`,
      }
      ctx.status = error.code
    }
  }
}

module.exports = catchError

3、app/api/classic.js

const Router = require('koa-router')
const router = new Router()
const { HttpException } = require('../../../core/http-exception')

router.post('/v1/:id/classic/latest', (ctx, next) => {
  if (true) {
    // 动态 面向对象方式 一个类
    const error = new HttpException('为什么错误ne', 10001, 400)
    throw error
  }
})

module.exports = router

04-08 特定异常类与global全局变量

一、nodejs有个全局变量, global

第05章 LinValidator校验器与Sequelize Orm生成MySQL数据表

05-01 Lin-Validator使用指南

一、api中如果抛出了一个异常,后续的代码是不会执行的
二、LinValidator的npm包名:lin-mizar
git地址:https://github.com/TaleLin/lin-cms-koa-core/blob/master/lib/validator/lin-validator.ts
三、将校验器都放在同一处,app/validators/validator.js
四、validator.js的使用率很高,校验。
文档:https://github.com/validatorjs/validator.js

05-03 配置文件与在终端显示异常

一、如果在class中,要在constructor中使用this,得要super()
1、没有extends的话,constructor中不用super
二、开发环境需要看到终端异常信息,生产环境不需要看到
config/config.js

module.exports = {
	enviroment: 'env'
}

core/ int.js

const requireDirectory = require('require-directory')
const Router = require('koa-router')

class InitManager {
  static initCore(app) {
    InitManager.app = app
    InitManager.loadConfig()
  }
  static loadConfig(path = '') {
    const configPath = path || process.cwd() + 'config/config.js'
    const config = require('configPath')
    global.config = config
  }
}

module.exports = InitManager

三、原型链作业
1、编写一个函数findMember,是最终输出结果为[‘nameA’, ‘nameB’, ‘nameC’, ‘validateC’, ‘ValidateB’, ‘validateA’]

class A {
  constructor () {
    this.nameA = 'a'
  }
  validateA () {
    console.log('A')
  }
}
class B extends A {
  constructor () {
    super()
    this.nameB = 'b'
  }
  validateB () {
    console.log('B')
  }
}

class C extends B {
  constructor () {
    super()
    this.nameC = 'c'
  }
  validateC () {
    console.log('C')
  }
}

// 编写一个函数findMember

const c = new C()

const members = findMembers(c, 'name', 'validate')
console.log(members) // ['nameA', 'nameB', 'nameC', 'validateC', 'ValidateB', 'validateA']

05-04 关系型数据库与非关系型数据库

一、用户系统
1、通用型
2、针对小程序
二、
注册、登录
三、业务的处理通常是放到Model中处理的
1、基本所有的流程走的都是这个流程
在这里插入图片描述

四、常用数据库分为两大类
1、关系型数据库 SQL
(1)MySQL
(2)SQLServer
(3)Oracle
(4)PostgresSQL
2、非关系型数据库
(1)Redis
主要是用来做缓存的
(2)MongoDB
存储的是一个一个的类似javascript对象数据,也成为文档型数据库
五、持久存储数据,持久化
六、CRUD, 增删改查

05-05 navicat管理MySQL

一、xampp找到自己对应的版本,就可以不用去官网下载mysql
二、MariaDB是MySQL的分支
三、Navicat for MySQL,数据库可视化管理工具
四、navicat修改密码
1、修改密码
选择任意一个数据库,点击用户
在这里插入图片描述
双击后修改

2、是否设置成功
mysql user表,password应该是有值的,如果没有值,则没有设置成功
3、

root@localhost
root@`127.0.0.1`

五、koa中需要设置的用户名/密码,数据库名
数据库名:(自由填写)
默认字符集:utf8mb4
默认排序规则:utf8mb4_general_ci
六、sequelize会将模型自动转换成表

05-06 Sequelize初始化配置与注意事项

一、mysql, sqlserver, postgresdb,oracle这些类型数据库都能使用sequlize

const sequelize = new Sequelize(dbName, user, password, {
   dialect: 'mysql', // 数据库类型
  host,
  port,
  logging: true,
  timezone: '+08:00',
}) // 控制或设置数据库的参数,dbName, user, password, js对象()

二、如果数据库类型是mysql,必须安装mysql的驱动
npm i mysql2
三、如果不设置timezone,会跟北京时间相差8小时。

05-07 Use模型与用户唯一标识设计探讨

一、导入的模块重命名
1、es6
import { sequelize as db } from './core/db'
2、commonjs
const { sequelize : db } = require('./core/db')
二、一个用户对一个小程序来说,是不变,且唯一的, openid
一个用户,对小程序,公众号,都唯一的是unionID
三、主键,不能重复,不能为空
四、不要用字符串做主键,用数字类型,查询效率高很多
五、如果是自己用60001, 600002,自己设计的编号作为主键,不能处理并发情况,很有可能计算重复。
六、接口保护,权限,访问接口, Token令牌
app/models/user.js

const { sequelize : db } = require('../../core/db')
const { Sequelize, Model } = require('sequelize')

class User extends Model {

}

User.init({ // mysql一种类型
  // 主键 关系型数据库
  // 注册 user id 设计 id编号系统 600001 60002
  // 自动增长id编号
  id: {
    type: Sequelize.INTERGER,
    primaryKey: true,
    autoIncrement: true,
  },
  nickname: Sequelize.STRING,
  email: Sequelize.STRING,
  password: Sequelize.STRING,
  openid: {
    type: Sequelize.STRING(64),
    unique: true,
  },
}, { 
sequelize: db,
tableName: 'user'
 })
module.exports = {
  User
}

core/db.js

const Sequelize = require('sequelize')
const { dbName, host, port, user, password } = require('../config/config').database

const sequelize = new Sequelize(dbName, user, password, {
  dialect: 'mysql', // 数据库类型
  host,
  port,
  logging: true,
  timezone: '+08:00',
  define: {
    timestamps: true,
    paranoid: true,
    createdAt: 'created_at',
    updatedAt: 'updated_at',
    deletedAt: 'deleted_at',
    underscored: true,
  },
}) // 控制或设置数据库的参数,dbName, user, password, js对象()

sequelize.sync({
  force: true,
})

module.exports = {
  sequelize
}

05-09 LinValidateor综合应用

一、抛出异常
开发环境,不是httpException

const isHttpException = error instanceof HttpException
const isDev = global.config.enviroment === 'dev'
if (isDev && !isHttpException) {
	throw error
}

第06章 【构建用户身份系统】通用用户系统与小程序用户系统

06-01 用户注册与Sequelize新增数据

一、如果是自定义的校验中带异步验证,则Lin-Validator需要返回promise,这样可以使用await来先获取校验只,校验过了才能进行下一步。

06-02 中间件只在应用程序启动时初始化一次

一、中间件是一种静态的方式。
二、类,每次都是new一个类,将类实例化,每个api请求,都会实例化一个Lin-Validator
三、不要轻易在中间件以类的形式来组织中间件。函数一般来说,不会实例化。函数通常不会用来保存类的状态,校验器这种复杂的功能比较适合用类的方式来组织,不太适合用函数的方式来组织。

06-03 盐与密码加密的小知识

一、密码加密处理:bcryptjs
导入包的时候,npm包通常是放在第一位

npm i bcryptjs

二、

const salt = bcrypt.genSaltSync(10) // 10:计算机在生成盐的时候所花的成本

三、不能明文存储,即使密码相同,加密后也不能相同
四、
app/api/v1/user.js

const bcrypt = require('bcryptjs')
 const salt = bcrypt.genSaltSync(10)
  const psw = bcrypt.hashSync(password, salt)
06-04 模型的set操作

一、app/models

  password: {
    type: Sequelize.STRING,
    set(val) { // set:观察者模式,如果是值改变,则会自动调用这个方法。es6 reflect方法很容易实现观察者模式
      const salt = bcrypt.genSaltSync(10)
      const psw = bcrypt.hashSync(val, salt)
      this.setDataValue('password', psw) // 赋值的时候会自动调用set方法, setDataValue是Model方法,所以能用this
    }
  },
06-05 success操作成功处理

一、全局异常处理throw new global.errs.Success()
core/http-exception.js

class Success extends HttpException {
  constructor(msg, errorCode) {
    super()
    this.code = 201
    this.msg = msg || 'ok'
    this.errorCode = errorCode || 0
  }
}

lib/helper.js

function success(msg, errorCode) {
  throw new global.errs.Success(msg, errorCode)
}

module.exports = {
  success
}

app/api/v1/user.js

router.post('/register', async (ctx) => {
  const { body } = ctx.request
  const { email, password, nickname } = body
  const user = {
    email,
    password,
    nickname,
  }
  User.create(user)
  success()
})

二、ctx.body

router.post('/register', async (ctx) => {
  const { body } = ctx.request
  const { email, password, nickname } = body
  const user = {
    email,
    password,
    nickname,
  }
  User.create(user)
  ctx.body = {
  		code: 201,
		msg: ’这是success的另一种实现方法‘,
		errorCode: 0,
	}
})
06-06 isOption校验

一、身份校验
1、session,考虑状态
2、令牌,无状态
(1)token令牌:一串无意义的随机字符串
(2)jwt令牌:携带数据
二、无状态,有状态
rest:一次请求就能拿到数据(无状态)
websrevice :请求:open, 取数据,close (有状态)
三、asp, jsp:动态网页技术

06-07 模拟枚举

一、javascript对象模拟枚举
lib/enum.js

function isThisType (val) {
  for (let key in this) {
    if (this[key] === val) {
      return true
    }
  }
  return false
}
const LoginType = {
	USER_MINI_PROGRAM: 100,
	USER_EMAIL: 101,
	USER_MOBILE: 102,
	ADMIN_EMAIL: 200,
	isThisType,
}

module.exports = {
  LoginType
}

二、java可以在编译阶段找到很多问题,javascript, python很多时候只能在执行阶段找问题。

06-08 验证用户账号密码

一、async在函数名的前面

第07章 【主流的用户身份识别方式与权限控制】JWT令牌与Auth权限控制中间件

07-01 jsonwebtoken

一、jwt令牌,npm包:jsonwebtoken
npm i jsonwebtoken
二、core /kɔːr/:核心
core/util.js

const jwt = require('jsonwebtoken')

// scope,用来做权限
const generaterToken = function (uid, scope) {
  const secretKey = global.config.security.secretKey
  const expiresIn = global.config.security.expiresIn
  const token = jwt.sign({
    uid,
    scope,
  }, secretKey, {
    expiresIn,
  })
  return token
}

module.exports = {
  generaterToken,
}

app/api/v1/token.js

router.post('/', async (ctx) => {
  const { body } = ctx.request
  const { type } = body
  let token
  switch (type) {
    case LoginType.USER_EMAIL:
      const { account, secret } = body
      token = await emailLogin(account, secret)
      ctx.body = {
        token: token,
      }
  }
})

async function emailLogin(account, secret) {
  const user = await User.verifyEmailPassword(account, secret)
  return generaterToken(user.id, 2)
}

module.exports = router

07-02 httpBasicAuth传递令牌

一、token 过期,不合法
二、中间件自带参数ctx, next
三、token放在 body,还是 header ,取悦于前后端约定
四、HTTP 规定 身份验证机制 HttpBasicAuth
basic auth是最基本的
basic64加密
五、HttpBasicAuth需要npm包:basic-auth
npm i basic-auth
六、ctx.req: node.js原生的request对象
ctx.request: koa基于原生noejs的request封装后的request
七、

const basicAuth = require('basic-auth')

class Auth {
  constructor () {
    
  }

  get m() {
    return async (ctx, next) => {
      const token = basicAuth(ctx.req)
      ctx.body = token
    }
  }
}

module.exports = {
  Auth
}

八、koa router上可以使用多个中间件。先执行的中间件写在前面,前面的中间件能阻止进入后一个中间件。上一个中间件末尾写await next(),才会去执行第二个中间件

router.get('/latest', new Auth().m, (ctx, next) => {} // m后面不需要加括号,m是一个属性,不是一个方法
07-04 API权限分级控制

一、权限 token角色,普通用户、管理员,可以用分级scope来区分

07-05 小程序openid登录系统

一、业务逻辑
1、在api接口编写
2、model,分层
二、业务分层,
1、nodejs: Model, Service
2、Thinkphp: Model Service Logic
3、java: Model DTO
二、MVC,业务逻辑应该写在M中,model
三、code appid appsecret

07-06 微信鉴权、openid与unionId

一、util是Node.js提供的帮助工具,不需要npm i
const util = require('util')
二、调用微信服务,可以用axios库
const result = await axios.get('https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code')

07-07 根据openid创建微信用户

一、uid,用系统的,不用openid,因为openid是比较机密的数据,不能一直在前后端传输
二、
在这里插入图片描述

第08章 使用Lin-UI与在小程序中使用npm

08-01 Lin-UI组件安装

一、微信开发者工具
1、项目设置,选择使用npm模块,调试组件库不要调得太低,尽量调的新的一点
2、开发者工具里的工程根目录,选择“在终端中打开”
在这里插入图片描述
(1)要保证是在项目根目录中,现在是在pages 目录下,所以cd …
(2) npm init,这样就创建了package.json文件
(3) npm i Lin-UI,微信开发者工具中不会显示node_modules文件
(4)工具,构建npm。miniprogram_npm,下面会有lin–ui的所有组件
在这里插入图片描述
二、小程序对cnpm的支持度比较低

08-02 在小程序中登录

一、如果调用的是本地的api
配置,不校验合法域名

08-04 数据库设计的好思路(实体表与业务表)

一、model code first,
要先考虑的不是数据表,而是model模型
二、主题,由粗到细
1、user
2、期刊
movie
sentence
music
(1)实体,表/model ,记录本身相关信息,事务,表
(2)大千世界事务的映射
3、一期一期, model/表,第1期、第2期、第3期
Flow
(1)很难找到实体,是抽象的,记录业务,解决业务问题,
在这里插入图片描述

08-05 Music、Sentence、Movie模型定义

一、classic 共同字段/属性
image
title
pubdate
content
fav_nums
type 代号
(1)url, music特有的
二、共同字段/属性,定义成基类,nodejs中的squelize不能做到直接用类继承,python或java可以
三、
const musicFields = Object.assign({
url: Sequelize.STRING,
}, classicFields)

Music.init(musicFields, {
	sequelize,
	tableName: 'music'
} )
08-06 Flow模型与导入SQL数据

一、where是特定的查询条件
二、order,排序

08-07 在小程序中携带令牌

一、base64.js,是一个npm包,可用于base64加码

npm i js-base64

二、

header: {
	Authorization: this._encode()
}
_encode() {
	// account: password
	// token:
	const token = wx.getStorageSync('token')
	const base64 = Base64.encode(token + ':')
	return base64
}
08-08 Sequeliz模型的序列化

一、序列化,对象转换成json
二、art(类)下的dataValues才会被序列化成json,才会被返回
三、对模型进行属性修改
1、

const flow = await Flow.findOne({
	order: [
		['index', 'DESC']
	]
})
const art = await Art.getData(flow.art_id, flow.type)
art.dataValues.index = flow.index // 直接修改了一个类,没有私有成员的概念
ctx.body = art

2、以上方法不推荐,推荐使用以下方法
用内置方法,更为合理,更为安全

art.setDataValue('index', flow.index)

四、sequelize告诉koa框架要通过dataValues进行序列化。
五、json是由js对象抽象出来的

第09章 点赞业务的实现

第10章 面向对象与MySQL in查询

第11章 MySQL group分组查询与JS并发原理

第12章 KOA、Sequelize多几层JSON序列化

第13章 【无感知刷新、获取令牌、登录等】 前后端对接

13-01 小程序如何实现无感知刷新令牌

一、token令牌可能会过期
app.js

import { Token } from 'models/token.js'
App({
	onLaunch: function () {
		const token = new Token()
		token.verify()
	}
})

二、自动无感知帮助用户重新刷新令牌
退出后,短时间内再进入,不一定会触发onLaunch方法
二次重发机制

13-02 坑!坑!坑!Model中禁止使用构造函数

一、所有的自定义的models中,都不要写constructor
二、参数通过方法参数的方法传过来,而不是通过constrctor传过来

13-03 短评修复

一、实时加载,从onload, 改成onshow,用户切换一下就改变数据

13-04 KOA静态资源

一、静态资源
1、api,读流,返回。
2、不需要api,需要插件(npm包:koa-static)
app.js

const path = require('path')
const static = require('koa-static')
app.use(static(path.join(__dirname, './static')))

二、models处理图片
models/art.js

if (art && art.image) {
	let imgUrl = art.dataValues.image
	art.dataValues.image = global.config.host + imgUrl
}
13-05 image完整路径方案探讨

一、图片拼接,在最源头的地方处理,是最好的
二、以下方法不可行,因为get没办法修改dataValues
models/classic.js

const classicFields = {
	image: {
		type: Sequelize.STRING,
		get() {
			return global.config.host + this.getDataValue('image')
		}	
	}
}

三、

const id = art.get('image') // 获取完整路径
const t = art.image // 获取完整路径
const s = art.getDataValue('image') // 获取到原始值

四、一个模型的dataValues是不受get影响的,存储的都是原始字符串。
五、方案
Model.prototype.toJSON 修改
六、hook钩子,最大的好处的是能解耦代码
九、判断是否以http开头
data[key].startsWith('http')

13-06 静态资源存储方案探讨

一、静态资源的加载,图片,最消耗流量
二、静态资源存储
1、网站目录中,app/static/images
2、静态资源服务器,微服务,带宽足够
3、云服务,阿里云OSS(ECS:,RDS:关系型数据库,OSS:云服务)
OSS可以进行CDN缓存
4、免费的静态资源服务器,github, gitpage。
个人服务器可以用。
三、html, css, js可以打包的,都是静态资源
四、vue/react打包出来的资源也是静态资源
nuxt的ssr不属于静态资源,服务端模板渲染
五、适用场景
1、react, vue
(1) CMS,内部管理系统,不需要SEO,可以用react, vue
(2) webApp, h5
2、next的ssr
(1) SEO,B2C技术

13-07 access_token 和refresh_token双令牌保证无感知登录

一、无感知登录

import { Token } from '../models/token.js'

const tips = {
  1: '抱歉,出现了一个错误',
  1005: 'appKey无效,请前往www.7yue.pro申请'
}

class HTTP {
  request({
           url,
           data = {},
           method = 'GET'
          }) {
    return new Promise((resolve, reject) => {
      this._request(url, resolve, reject, data, method)
    })
  }
  _request(url, resolve, reject, data = {}, method = 'GET', noRefetch = false) {
    wx.request({
      url: config.api_base_url + url,
      method,
      data,
      header: {
        'content-type': 'application/json',
        Authorization: this._encode()
      },
      success: (res) => {
        const code = res.statusCode.toString()
        if (code.startsWith('2')) {
          resolve(res.data)
        } else {
          if (code == '403') {
            if (!noRefetch) {
              this._refetch(
                url,
                resolve,
                reject,
                data,
                method
              )
            }
          } else {
            reject()
            const error_code = res.data.error_code
            this._show_error(error_code)
          }
        }
      },
      fail: (err) => {
        reject()
        this._show_error(1)
      }
    })
  }

  _show_error(error_code) {
    if (!error_code) {
      error_code = 1
    }
    const tip = tips[error_code]
    wx.showToast({
      title: tip ? tip : tips[1],
      icon: 'none',
      duration: 2000
    })
  }

  _refetch(...param) {
    var token = new Token()
    token.getTokenFromServer((token) => {
      this._request(...param, true)
    })
  }

  _encode() {
    const token = wx.getStorageSync('token')
    const base64 = new Base64()
    const result = base64.encode(token + ':')
    return 'Basic' + result
  }
}

二、
在这里插入图片描述
三、app
1、缓存中存储app账号,密码
2、双令牌,access_token, refresh_token
(1)access_token,类似于jwt令牌
(2)access_token过期了,通过refresh_token重新获取access_token, 每次发access_token的时候,都重新发送一个refresh_token令牌
四、oAuth2.0,第3方登录的时候用到

第14章 项目部署指南

14-01 部署指南与小程序云开发探讨

一、 部署不属于技巧性的东西
二、本地电脑没有外网ip,可以本地访问,可以局域网访问,但是不能外部访问。
三、云服务,可以理解成linux电脑
四、购买域名之后一定要备案
五、部署
1、服务器、域名
(1)服务器
(2)域名
域名备案
域名解析:域名和ip绑定起来
2、环境安装
mysql:xampp
node
3、nginx 转发
六、通常来说,一个服务,或者一个项目,都有一个端口
七、80端口不需要加在域名后面
八、小程序:https
1、阿里云,有个https证书可以用一年
(1)免费:lets encrypt,每3个月需要续期一次
九、CMS,通常都是web端
十、结合,云开发,api开发
serverless

14-02 守护进程与MP2

一、启动的不能是常规进程,得是守护进程,是后台进程。
1、node app.js启动的是常规进程,会阻塞,关闭terminal终端,进程就会停止。
二、守护进程pm2, 日志监控,重启,安装的时候需要-g安装
启动:pm2 start app.js
终止:pm2 stop app
三、pm2可代替nodemon

第15章 关于Lin CMS和现代大型Web架构思想

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值