正则的三个应用场景
主讲人
- 李喆明
- 360前端技术专家
- 奇舞团
从5各方面介绍
- 什么是 Node.js
- Node.js 基础
- NPM
- 基于 Node.js 的 Web 开发
- Node.js 的调试
1.什么是 Node.js
基于jsv8引擎的js运行时
以js区别:
- 基于异步 I/O 相关接口
- 基于 node_modules 和 require 的模块依赖
- 提供 C++ addon API 与系统交互
语法一样,提供的接口的差别
- Node.js 可以干什么
- Web 服务端:Web Server、爬虫
- CLI 命令行脚本:webpack
- GUI 客户端软件:VSCode、网易云音乐
- 树莓派IoT, 图像处理, 实时通讯,加密货币...
例:
nodejs爬虫
const puppeteer = require('puppeteer');
const url = 'https://movie.douban.com/subject/26794435';
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url);
const film = await page.evaluate(() => {
const title = $('h1 > span:first-child').text();
const poster = $('#mainpic img').attr('src');
const desc = $('span[property="v:summary"]').text().trim();
return {title, poster, desc};
});
console.log(JSON.stringify(film, null, ' '));
await browser.close();
})();
-
Node.js 基础
nodejs 脚本运行
node index.js
读写文件
const fs = require('fs');
fs.readFile('test.txt', (err, data) => {
console.log(data);
});//异步操作
console.log('read file content');
模块
主要有
- 内置模块:编译进 Node 中,例如 http fs net process path 等
- 文件模块:原生模块之外的模块,和文件(夹)一一对应的形式
使用内部文件
const fs = require('fs');
fs.readFile('a.text', (err, buffer) => {
console.log(buffer);
})
const {readFile} = require('fs');
readFile('a.txt', (err, buffer) => {
console.log(buffer);
})
使用文件模块
var circle = require('./circle.js');
console.log('半径为4的圆面积是:' + circle.area(4));
定义模块
const pi = Math.PI;
exports.area = function (r) {
return pi * r * r;
};
exports.circumference = function (r) {
return 2 * pi * r;
};
模块加载形式:
require('/foo/bar/a.js');
// 加载相对路径文件
require('../a.js');
// 加载无后缀的文件
require('../a');
// 加载外部模块
require('pkg-name');//从node_modules文件依次查找
模块类型
- .js
- .json
- .node (使用c++形式写完之后,使用node gulp 编译成的二进制文件)
- .ejs node 新出的基于esmodule 形式 相应的 cjs commonjs导入导出
模块路径查找
- 绝对路径
- 相对路径
- 和当前路径处理为绝对路径
- 模块/文件夹
- 原生模块,直接读取缓存
+[$NODE_PATH, ~/.node_modules, ./node_modules, ../node_modules, ...]
- 解析 package.json,查找 main 属性,没有则使用 index.js
- 如果未找到,则报错
js模块解析
const circle = require('./circle.js');
+ require 并不是全局变量
+ 定义的变量 circle 会污染其他文件么?
- 通过 fs.readFileSync 同步拿到文件内容
对内容包装,形成闭包 不会影响全局
(function (exports, require, module, __filename, __dirname) {
var circle = require('./circle.js');
console.log('The area is ' + circle.area(4));
});
- 通过 vm.runInThisContext 执行 虚拟沙盒环境
- 获取 module 对象的值作为模块的返回值
模块缓存 - 模块加载后会将返回值缓存起来
- 下次加载时直接读取缓存结果,避免文件 I/O 和解析时间
-
导出对象缓存在 Module._cache 对象上
npm 包管理器
npm最多
包管理
- 一个package.json文件应该存在于包顶级目录下
- 二进制文件应该包含在bin目录下
- JavaScript代码应该包含在lib目录下
- 文档应该在doc目录下
- 单元测试应该在test目录下
例:
npm init -y
{
"name": "star-plan",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
meta 信息:
- main:定义 入口文件
- npm scripts :运行脚本
- dependencies
- devDependencies
- peerDependencies
- bundledDependencies
- optionalDependencies
dependencies": {
"accepts": "^1.2.2",
"content-disposition": "~0.5.0",
"cookies": "~0.7.0",
"debug": "*",
"delegates": "^1.0.0",
"escape-html": "~1.0.1",
"fresh": "^0.5.2",
"only": "0.0.2",
"parseurl": "^1.3.0",
"statuses": "^1.2.0",
"type-is": "^1.5.5",
"vary": "^1.0.0"
* ^ ~区别:
^ 可以中版本小版本更新
~小版本跟新
*所有更新到最新
不带
其他形式:
+ 1.0.0 Must match version exactly
+ >1.0.0 Must be greater than version
+ >=1.0.0 <1.0.0 <=1.0.0
+ ~1.0.0 "Approximately equivalent to version"
+ ^1.0.0 "Compatible with version"
+ 1.2.x 1.2.0, 1.2.1, etc., but not 1.3.0
+ * Matches any version
+ version1 - version2 Same as >=version1 <=version2.
- bin 指定key-value 在系统中创建alias 直接执行属性名对应文件
- registry 原代理
semver version : 版本信息,
依次: - 小版本修复,bug更新
- 种版本 特性的增加
- 大版本整体重构
npm问题(现在被github收购)
速度问题 官网在国内不好
-
安全问题
解决:- 查看源码看 Star
- https://snyk.io/ 外部的检测服务
- npm audit (快速检测漏洞)
基于 Node.js 的Web 开发
用到http模块
const http = require("http")
const server = http.createServer((req, res) => {
res.end('Hello World');
});
server.listen(3000);
koa框架
const Koa = require('koa');
const app = new Koa();
// response
app.use(ctx => {
ctx.body = 'Hello Koa';
});
app.listen(3000);
module.exports = class Application extends Emitter {
...
listen() {
debug('listen');
const server = http.createServer(this.callback());
return server.listen.apply(server, arguments);
}
use(fn) {
this.middleware.push(fn);
return this;
}
callback() {
const fn = compose(this.middleware);
if (!this.listeners('error').length) this.on('error', this.onerror);
const handleRequest = (req, res) => {
res.statusCode = 404;
const ctx = this.createContext(req, res);
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
onFinished(res, onerror);
return fn(ctx).then(handleResponse).catch(onerror);
};
return handleRequest;
}
createContext(req, res) {
const context = Object.create(this.context);
const request = context.request = Object.create(this.request);
const response = context.response = Object.create(this.response);
context.app = request.app = response.app = this;
context.req = request.req = response.req = req;
context.res = request.res = response.res = res;
request.ctx = response.ctx = context;
request.response = response;
response.request = request;
context.originalUrl = request.originalUrl = req.url;
context.cookies = new Cookies(req, res, {
keys: this.keys,
secure: request.secure
});
request.ip = request.ips[0] || req.socket.remoteAddress || '';
context.accept = request.accept = accepts(req);
context.state = {};
return context;
}
}
中间件繁多,质量参差不齐,选择困难
- 逻辑分层
- 路由处理
- 数据解析、校验
- 权限校验
- Session、Cache
- 数据库、Redis
- 安全
- ...
Koa 无规范约束,不利于企业级团队开发
thinkjs:
基于koa 进行企业级封装 thinkjs
├─src //源码
│ ├─bootstrap 用户启动脚本
│ ├─config //配置文件
│ │ ├─config.js
│ │ └─adapter.js //日志等
│ ├─controller 路由
│ │ ├─index.js //对应着controller 参数和权限
│ ├─logic //
│ │ ├─index.js
│ └─model 数据库操作
├─view 模板文件
│ ├─index_index.html
└─www 静态资源
│ └─static
│ ├─css
│ ├─img
│ └─js
├─development.js 启动文件
├─production.js启动文件
├─package.json
// src/controller/index.js
export default class extends think.Controller {
indexAction() {
return this.display();
}
}
// view/index_index.html
<h3>Hello World</h3>
统一目录
模块统一 模范快速开发,和学习成本小
TODO项目实战
- 功能列表
- TODO List 的页面
- API
- 获取 TOO 列表
- 增加 TODO
- 删除 TODO
- 更新 TODO 状态
- 数据表设计
CREATE TABLE `todo` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`desc` varchar(255) NOT NULL DEFAULT '',
`status` tinyint(11) NOT NULL DEFAULT '0' COMMENT '0 是未完成,1是已完成',
`createdAt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- 安装 thinkjs
npm install -g think-cli
创建项目
$ thinkjs new todo
$ cd todo
$ npm install