目录
七、使用node原生http模块实现购物车所有接口(不需要装包,不需要中间件)
八、node原生写接口,搭建静态web服务器,处理前端history路由
Node.js是单线程的,基于事件循环,非阻塞 IO的。事件循环中使用一个事件队列,在每个时间点上,系统只会处理一个事件,即使电脑有多个CPU核心,也无法同时并行的处理多个事件。因此,node.js适合处理I/O型的应用,不适合那种CPU运算密集型的应用。在I/O型的应用中,给每一个输入输出定义一个回调函数,node.js会自动将其加入到事件轮询的处理队列里,当I/O操作完成后,这个回调函数会被触发,系统会继续处理其他的请求。
一、什么是npm
npm是javascript的包管理工具,是前端模块化下的一个标志性产物。简单地地说,就是通过npm下载模块,复用已有的代码,提高工作效率。
- 1.从社区的角度:把针对某一特定问题的模块发布到npm的服务器上,供社区里的其他人下载和使用,同时自己也可以在社区里寻找特定的模块的资源,解决问题
- 2.从团队的角度:有了npm这个包管理工具,复用团队既有的代码也变的更加地方便
新建一个项目,cd进去,然后执行npm init
来初始化项目的配置。
在执行npm init
之前,有两点需要我们注意一下:
- 包名不能重复
- npm对包名的限制:不能有大写字母/空格/下划线
npm init 一路回车
或者:npm init -y
生成的package.json文件:
name和version组成唯一标识。每次发包时都要修改版本号。
description:描述
main:入口,别人安装你的npm包后,import时自动找到这个文件
scripts: 脚本 npm run test或者yarn test
keywords:关键字。放简介,字符串。方便别人查找。
author: 作者
license: 许可证
ISC许可证:ISC许可证_百度百科
MIT许可证:MIT许可证_百度百科
files:files是一个包含项目中的文件的数组。如果命名了一个文件夹,那也会包含文件夹中的文件。
{
"name": "xu-20191024",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
{
"name": "xu-20191024",
"version": "1.0.2",
"description": "1705E,项目实战",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": ["1705E","1706E"],
"author": "徐同保",
"license": "ISC"
}
检查包名是否存在:
https://www.npmjs.com/package/xu-20191025
通过keywords找:
repository:npm和git关联
"repository": {
"type": "git",
"url": "https://github.com/xutongbao"
},
homepage: 项目官网
"homepage": "https://blog.csdn.net/xutongbao",
dependencies与devDependencies的区别:
在发布npm包的时候,本身dependencies下的模块会作为依赖,一起被下载;devDependencies下面的模块就不会自动下载了;但对于项目而言,npm install 会自动下载devDependencies和dependencies下面的模块。
当别人使用我们的插件时,peerDependencies就会告诉明确告诉使用方,你需要安装该插件哪个宿主版本:
"dependencies": {
"axios": "^0.19.0"
},
"devDependencies": {
"element-ui": "^2.12.0"
},
"peerDependencies": {
"react": "^16.11.0"
},
"optionalDependencies": {
"redux": "^4.0.4"
}
参考链接:
https://blog.csdn.net/yq_oxygen/article/details/90040392
如何解决NPM 的UNMET PEER DEPENDENCY问题? - 问答 - 云+社区 - 腾讯云
optionalDependencies:
可选依赖,如果有一些依赖包即使安装失败,项目仍然能够运行或者希望npm继续运行,就可以使用optionalDependencies。另外optionalDependencies会覆盖dependencies中的同名依赖包,所以不要在两个地方都写。
bin字段:
参考链接:
package.json bin的作用_feng98ren的专栏-CSDN博客
src/index.js:
请确保你的index.js文件里面最开头写上 #!/usr/bin/env node,否则文件里的脚本不会再Node环境下执行
#!/usr/bin/env node
console.log(1)
"bin": {
"myapp": "./src/index.js"
},
全局安装:
常用命令:
npm config list
npm config ls -l
安装npm包,安装到dependencies:
npm install commander --save-prod
npm install commander --save
npm install commander -S
npm install commander
npm add commander
npm i commander
安装npm包,安装到devDependencies:
npm install commander --save-dev
npm install commander -D
卸载npm包:
npm uninstall commander
npm unlink commander
npm remove commander
npm rm commander
npm un commander
npm r commander
安装到全局:
npm install create-react-app -g
从全局删除:
npm un create-react-app -g
二、命令行程序
console.log('hello world!')
命令行参数:
1)
console.log('hello world!', process.argv[2])
2)
console.log('hello world!', process.argv[1])
三、commander.js
使用.option()
方法定义commander
的选项options
短标志可以作为单个arg传递,例如-abc相当于-a -b -c。
多词组成的选项,像“--template-engine”会变成 program.templateEngine 等
<>代表必填,[]代表选填,选填可以设置默认值
.version('0.0.1') 使用node app -V查版本
.version('0.0.1', '-v, --version') 使用node app -v或node app --version查版本
使用node app -h或node app --help查看帮助
program.parse方法用于解析process.argv,解析后可以program.xxx使用
const program = require('commander');
program
.version('0.0.1') //node app -V
//.version('0.0.1', '-v, --version') //node app -v
.option('-d, --debug', 'output extra debugging')
.option('-s, --small', 'small pizza size')
.option('-p, --pizza-type <type>', 'flavour of pizza');
program.parse(process.argv);
if (program.debug) console.log(program.opts());
console.log('pizza details:');
if (program.small) console.log('- small pizza size');
if (program.pizzaType) console.log(`- ${program.pizzaType}`);
求和:
const program = require('commander');
program
.version('0.0.1')
.option('-a, --my-a, <a>', '第一个值')
.option('-b, --my-b, <b>', '第二个值')
.parse(process.argv);
console.log(program.myA)
console.log(program.myB)
console.log(program.myA*1 + program.myB*1)
求和二:
const program = require('commander');
program
.version('0.0.1')
.option('-a, --add', '求和')
.parse(process.argv);
if (program.add) {
let sum = 0
program.args.forEach(item => {
sum += item * 1
})
console.log(sum)
}
阶乘:
const program = require('commander');
program
.version('0.0.1')
.option('-a, --add', '求和')
.option('-f, --factorial <num>', '阶乘')
.parse(process.argv);
const factorial = (num) => {
if (num < 0) {
return -1
} else if (num === 0 || num === 1) {
return 1
} else {
return num * factorial(num - 1)
}
}
if (program.add) {
let sum = 0
program.args.forEach(item => {
sum += item * 1
})
console.log(sum)
} else if (program.factorial) {
let result = factorial(program.factorial)
console.log(result)
}
多单词形式:
const program = require('commander');
program
.version('0.0.1')
.option('-a, --my-add', '求和,多单词形式')
.parse(process.argv);
//驼峰
if (program.myAdd) {
let sum = 0
program.args.forEach(item => {
sum += item * 1
})
console.log(sum)
}
以--no形式开头的选项,代表后面紧跟单词的相反面:
const program = require('commander');
program
.version('0.0.1')
.option('-a, --no-add', '求和,以--no形式开头的选项,代表后面紧跟单词的相反面')
.parse(process.argv);
console.log(program)
if (program.add) {
let sum = 0
program.args.forEach(item => {
sum += item * 1
})
console.log(sum)
} else {
console.log('取反')
}
command方法,自定义命令
description方法, 命令的描述性语句
action方法,定义命令的回调函数
const program = require('commander');
program
.version('1.0.0')
.command('my-add <num>')
.option('-a, --add, <num>', '加法')
.action((num, cmd) => {
console.log(num, cmd.add)
})
program
.option('-u, --upadte', '更新')
.description('描述信息!!!')
program.parse(process.argv)
参考链接:
四、npm包管理
发布npm包:
1.先注册一个npm账号
2.在终端登录npm账号:npm login 回车输入用户名密码和邮箱
3.新建一个文件夹,cd到新创建的文件夹,使用npm init 生成package.json
4.使用npm publish上传npm包,你会收到一封邮件,在npm官网可以看到刚上传的npm包
yarn更新包:
yarn upgrade
五、node提供一个链接可以下载图片
const fs = require('fs')
const request = require('request')
const program = require('commander')
program
.option('-d, --down <url>', '下载')
.parse(process.argv)
let url = program.down
const name = url.slice(url.lastIndexOf('/') + 1)
request(url).pipe(fs.createWriteStream('./' + name));
//node app -d https://n3-q.mafengwo.net/s15/M00/16/18/CoUBGV2xnO6ALntcAB_DZLkVUnY568.png
//node app -d https://p4-q.mafengwo.net/s15/M00/B3/B1/CoUBGV2wYYmAAByNACD9lHJSPKY794.png
//node app --down https://n2-q.mafengwo.net/s15/M00/D0/E4/CoUBGV2vBYGAbzADAB1W_rqrlCM012.png
六、使用node原生http模块写接口
跨域:
所以ajax跨域请求附带自定义响应头时,被请求服务器除了添加Access-Control-Allow-Origin响应头,还得注意注意添加Access-Control-Allow-Headers响应头为对应的自定义请求头的名称,多个自定义请求头用英文状态下逗号分开。
//跨域
res.setHeader('Access-Control-Allow-Origin', '*') //可以把 * 改成 http://localhost:3000 避免xss攻击
//res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,PATCH,DELETE,HEAD,OPTIONS') //放行的方法
res.setHeader('Access-Control-Allow-Headers', 'content-type') //放行的请求头
res.setHeader('Access-Control-Max-Age', 1800) //隔30分钟才发起预检请求,1800秒
url.parse:
url.parse('http://localhost:3000/api/list?id=0') :
url.parse('http://localhost:3000/api/list?id=0', true) :
204状态码:
请求收到,但返回信息为空。
请求执行成功,但是没有数据,浏览器不用刷新页面.也不用导向新的页面。常用于跨域请求。
跨域请求:
OPTIONS是一种“预检请求”,浏览器在处理跨域访问的请求时如果判断请求为复杂请求,则会先向服务器发送一条预检请求,根据服务器返回的内容浏览器判断服务器是否允许该请求访问。如果web服务器采用cors的方式支持跨域访问,在处理复杂请求时这个预检请求是不可避免的。
跨域不可避免,预检请求也不可避免,那我们能做的,就是减少预检请求,处理办法就是设置跨域的有效期Access-Control-Max-Age,这样就只会跨域预检一次了。
浏览器的同源策略,就是出于安全考虑,浏览器会限制从脚本发起的跨域HTTP请求(比如异步请求GET, POST, PUT, DELETE, OPTIONS等等),所以浏览器会向所请求的服务器发起两次请求,第一次是浏览器使用OPTIONS方法发起一个预检请求,第二次才是真正的异步请求,第一次的预检请求获知服务器是否允许该跨域请求:如果允许,才发起第二次真实的请求;如果不允许,则拦截第二次请求。
Access-Control-Max-Age用来指定本次预检请求的有效期,单位为秒,,在此期间不用发出另一条预检请求。
例如:
res.setHeader('Access-Control-Max-Age', 1800) 表示隔30分钟才发起预检请求。也就是说,发送两次请求
七、使用node原生http模块实现购物车所有接口(不需要装包,不需要中间件)
const http = require('http')
const fs = require('fs')
const url = require('url')
const { bookNavData, bookMallData, userList } = require('./data')
let bookList = []
const server = http.createServer((req, res) => {
//跨域
res.setHeader('Access-Control-Allow-Origin', '*') //可以把 * 改成 http://localhost:3000 避免xss攻击
//res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,PATCH,DELETE,HEAD,OPTIONS') //放行的方法
res.setHeader('Access-Control-Allow-Headers', 'content-type') //放行的请求头
res.setHeader('Access-Control-Max-Age', 1800) //隔30分钟才发起预检请求,1800秒
let { pathname } = url.parse(req.url, true)
console.log(req.method, url.parse(req.url, true))
console.log(pathname)
if (req.url === '/') { //hello world!
res.writeHead(200, { 'Content-Type': 'text/html' })
res.write('hello world!')
res.end()
} else if (req.url === '/home') { //路由
res.writeHead(200, { 'Content-Type': 'text/html' })
const home = fs.readFileSync('./index.html') //读文件
res.end(home)
} else if (req.url === '/banner01') { //图片
//res.writeHead(200, { 'Content-Type': 'image/jpg' })
const banner01 = fs.readFileSync('./images/banner01.jpg') //读图片
res.end(banner01)
} else if (req.method == 'OPTIONS') { //跨域,处理options请求
res.writeHead(204) //204 无内容
res.end()
} else if (req.method === 'POST' && pathname === '/api/login') { //登录
let body = ''
// 通过req的data事件监听函数,每当接受到请求体的数据,就累加到body变量中
req.on('data', (chunk) => {
body += chunk
})
// 在end事件触发后,通过JSON.parse将body解析为真正的POST请求格式
req.on('end', () =>{
body = JSON.parse(body)
let { username, password } = body
let user = userList.find(item => item.username === username)
res.writeHead(200, { 'Content-Type': 'application/json' })
if (user) {
if (user.password === password) {
res.write(JSON.stringify({
code: 200,
data: {
username
},
message: '登录成功'
}))
} else {
res.write(JSON.stringify({
code: 400,
message: '密码错误'
}))
}
} else {
res.write(JSON.stringify({
code: 400,
data: body,
message: '用户不存在'
}))
}
res.end()
})
} else if (pathname === '/api/nav') { //导航
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookNavData,
message: '导航'
}))
} else if (pathname === '/api/list') { //列表
let { id } = url.parse(req.url, true).query
let list = bookMallData.find(item => item.id == id).list
list.forEach(item => {
if (bookList.findIndex(book => book.id === item.id) >= 0) {
item.is_in_my_book = true
} else {
item.is_in_my_book = false
}
})
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: list,
message: '列表'
}))
} else if (pathname === '/api/get_book_list') { //书包
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '书包'
}))
} else if (pathname === '/api/add') { //添加到书包
let body = ''
req.on('data', (chunk) => {
body += chunk
})
req.on('end', () => {
body = JSON.parse(body)
let { item } = body
bookList.push(item)
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '添加成功'
}))
})
} else if (pathname === '/api/detail') { //详情
let { id } = url.parse(req.url, true).query
let detail
bookMallData.forEach(listItem => {
listItem.list.forEach(book => {
if (book.id == id) {
detail = book
}
})
})
if (bookList.find(book => book.id === detail.id)) {
detail.is_in_my_book = true
} else {
detail.is_in_my_book = false
}
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: detail,
message: '详情'
}))
} else if (pathname === '/api/delete') { //删除
let body = ''
req.on('data', (chunk) => {
body +=chunk
console.log('chunk:', chunk)
})
req.on('end', () => {
body = JSON.parse(body)
let { ids } = body
bookList = bookList.filter(item => !ids.find(id => id === item.id))
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '删除成功'
}))
})
} else if (pathname === '/api/update') {
let body = ''
req.on('data', (chunk) => {
body += chunk
})
req.on('end', () => {
body = JSON.parse(body)
let { bookListNew } = body
bookList = bookListNew
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '更新成功'
}))
})
} else { //404
res.writeHead(404, { 'Content-Type': 'text/html' })
res.end('404')
}
})
server.listen(9999, () => {
console.log(9999)
})
八、node原生写接口,搭建静态web服务器,处理前端history路由
参考链接:
connect-history-api-fallback - npm
项目上线啦:
http://39.97.238.175/index/home
const http = require('http')
const url = require('url')
const path = require('path')
const fs = require('fs')
const connect = require('connect')
const history = require('connect-history-api-fallback')
const { bookNavData, bookMallData, userList } = require('./data')
let bookList = []
//使原生http模块可以使用中间件功能
const app = connect()
//处理react前端路由(BrowserRoute),vue前端路由(mode:history)
app.use(history())
//跨域,静态web服务器
app.use((req, res, next) => {
//跨域
res.setHeader('Access-Control-Allow-Origin', '*') //可以把 * 改成 http://localhost:3000 避免xss攻击
//res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,PATCH,DELETE,HEAD,OPTIONS') //放行的方法
res.setHeader('Access-Control-Allow-Headers', 'content-type') //放行的请求头
res.setHeader('Access-Control-Max-Age', 1800) //隔30分钟才发起预检请求,1800秒
let { pathname } = url.parse(req.url, true)
let extName = path.extname(pathname)
console.log(pathname, extName)
if (pathname === '/') {
pathname = '/index.html'
}
if (pathname.indexOf('/api') >= 0) {
next()
} else {
fs.readFile(`./public/${pathname}`, (err, data) => {
if (err) {
res.writeHead(404, {'Content-Type': 'text/html' })
res.end('404')
} else {
if (extName === '.css') {
res.writeHead(200, {'Content-Type': 'text/css'})
}
res.end(data)
}
})
}
})
//接口
app.use((req, res) => {
let { pathname } = url.parse(req.url, true)
if (req.method == 'OPTIONS') { //跨域,处理options请求
res.writeHead(204) //204 无内容
res.end()
} else if (req.method === 'POST' && pathname === '/api/login') { //登录
let body = ''
// 通过req的data事件监听函数,每当接受到请求体的数据,就累加到body变量中
req.on('data', (chunk) => {
body += chunk
})
// 在end事件触发后,通过JSON.parse将body解析为真正的POST请求格式
req.on('end', () =>{
body = JSON.parse(body)
let { username, password } = body
let user = userList.find(item => item.username === username)
res.writeHead(200, { 'Content-Type': 'application/json' })
if (user) {
if (user.password === password) {
res.write(JSON.stringify({
code: 200,
data: {
username
},
message: '登录成功'
}))
} else {
res.write(JSON.stringify({
code: 400,
message: '密码错误'
}))
}
} else {
res.write(JSON.stringify({
code: 400,
data: body,
message: '用户不存在'
}))
}
res.end()
})
} else if (pathname === '/api/nav') { //导航
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookNavData,
message: '导航'
}))
} else if (pathname === '/api/list') { //列表
let { id } = url.parse(req.url, true).query
let list = bookMallData.find(item => item.id == id).list
list.forEach(item => {
if (bookList.findIndex(book => book.id === item.id) >= 0) {
item.is_in_my_book = true
} else {
item.is_in_my_book = false
}
})
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: list,
message: '列表'
}))
} else if (pathname === '/api/get_book_list') { //书包
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '书包'
}))
} else if (pathname === '/api/add') { //添加到书包
let body = ''
req.on('data', (chunk) => {
body += chunk
})
req.on('end', () => {
body = JSON.parse(body)
let { item } = body
bookList.push(item)
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '添加成功'
}))
})
} else if (pathname === '/api/detail') { //详情
let { id } = url.parse(req.url, true).query
let detail
bookMallData.forEach(listItem => {
listItem.list.forEach(book => {
if (book.id == id) {
detail = book
}
})
})
if (bookList.find(book => book.id === detail.id)) {
detail.is_in_my_book = true
} else {
detail.is_in_my_book = false
}
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: detail,
message: '详情'
}))
} else if (pathname === '/api/delete') { //删除
let body = ''
req.on('data', (chunk) => {
body +=chunk
console.log('chunk:', chunk)
})
req.on('end', () => {
body = JSON.parse(body)
let { ids } = body
bookList = bookList.filter(item => !ids.find(id => id === item.id))
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '删除成功'
}))
})
} else if (pathname === '/api/update') { //更新
let body = ''
req.on('data', (chunk) => {
body += chunk
})
req.on('end', () => {
body = JSON.parse(body)
let { bookListNew } = body
bookList = bookListNew
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '更新成功'
}))
})
} else { //404
res.writeHead(404, { 'Content-Type': 'text/html' })
res.end('404')
}
})
const server = http.createServer(app)
server.listen(9998)
console.log(9998)
九、安装FileZilla服务端
下载filezilla客户端和服务端:
FileZilla - The free FTP solution
安装server端:
十、安装FileZilla客户端
十一、阿里云配置支持FTP
十二、colors
装包:
yarn add colors
node代码:
const color = require('colors')
console.log('hello world!'.green)
console.log('hello world!'.underline.red)
console.log('hello world!'.inverse)
console.log('hello world!'.rainbow)
效果:
十三、express脚手架
装包:
yarn global add express-generator
运行:
express --view=pug m-express-demo
cd m-express-demo
yarn
yarn start
十三、npm view指令
显示npm包的相关信息:
npm view axios
npm show axios
npm info axios
npm v axios
查询npm包的所有版本号:
npm view axios versions
查询npm包的所有版本号和所有依赖:
npm view axios versions dependencies
十四、String
let a = 'hello'
let b = 'hello'
let c = new String('hello')
console.log(a === b) //true
console.log(a === c) //false
console.log(typeof a) //string
console.log(typeof c) //object
console.log(a instanceof String) //false
console.log(c instanceof String) //true
十五、node是单线程
let start = Date.now()
console.log(start)
setTimeout(() => {
console.log(Date.now() - start) //1000左右
for (let i = 0; i < 5000000000; i++) {
}
}, 1000)
setTimeout(() => {
console.log(Date.now() - start) //大于2000,具体大多少,取决于上面for循序的次数
}, 2000)
十六、错误处理
错误堆栈:
test.js:
const a = () => {
console.log(obj.name)
}
const b = () => {
a()
}
b()
在异步函数中,堆栈信息将丢失:
const a = () => {
setTimeout(() => {
console.log(obj.name)
}, 10)
}
const b = () => {
a()
}
b()
uncaughtException捕获异常(丢失了错误发生位置的上下文):
process.on('uncaughtException', (error) => {
console.error("xu:", error)
})
const a = () => {
console.log(obj.name)
}
const b = () => {
a()
}
b()
domain模块:
当res是上下文时,可以把错误信息返回给前端!
const domain = require('domain')
const d = domain.create()
let name = 'tom'
d.on('error', (error) => {
console.log('上下文环境:', name)
console.log('domain捕获到的异常信息:', error.stack)
})
d.run(() => {
console.log(obj.name)
})
十七、process.nextTick(callback)
在事件循环的下一次循环中调用 callback 回调函数。
console.log(1)
process.nextTick(() => {
console.log(2)
})
console.log(3)
十八、根据下标打印动物
const program = require('commander')
const fs = require('fs')
const packageInfo = require('./package.json')
program.version(packageInfo.version)
.option('-i, --index <type>', "下标")
program.parse(process.argv)
console.log(program.index)
fs.readFile('./animals.txt', 'utf-8', (err, data) => {
if (err) {
return
}
let animalsArr = data.split('===============++++SEPERATOR++++====================')
console.log(animalsArr[program.index])
})
动物数据:
链接:百度网盘 请输入提取码
提取码:g1sv
十九、node通过网页读取文件目录
const program = require('commander')
const fs = require('fs')
const http = require('http')
const { exec } = require('child_process')
const path = require('path')
const packageInfo = require('./package.json')
program.version(packageInfo.version)
.option('-p, --port <port>', "set port")
program.parse(process.argv)
let PORT = program.port || 8000
const app = http.createServer((req, res) => {
let rootPath = process.cwd()
if (req.url === '/favicon.ico') {
res.end()
return
}
let myPath = path.join(rootPath, req.url)
console.log('a', myPath)
if (fs.statSync(myPath).isFile()) {
fs.readFile(myPath, 'utf8', (err, data) => {
res.end(data)
})
} else {
let list = fs.readdirSync(myPath).map(filePath => {
return `<div>
<a href="${path.join(req.url, filePath)}">${filePath}</a>
</div>`
}).join('')
let html = fs.readFileSync(__dirname + '/public/index.html', 'utf8')
html = html.replace("{{list}}", list)
res.end(html)
}
})
app.listen(PORT, () => {
//exec(`start http://localhost:${PORT}`)
})
二十、重命名文件或文件夹
const fs = require('fs')
const path = require('path')
let target = process.argv[2]
let rename = process.argv[3]
let rootPath = process.cwd()
target = path.join(rootPath, target)
if (fs.existsSync(target)) {
fs.renameSync(target, path.join(rootPath, rename))
} else {
console.log('文件或文件夹不存在')
}
二十一、js区分对象函数和数组
const fs = require('fs')
const path = require('path')
let obj = {}
console.log(obj instanceof Object) //true
console.log(typeof obj) //object
console.log(Object.prototype.toString.call(obj)) //[object Object]
let fun = () => {}
console.log(fun instanceof Function) //true
console.log(fun instanceof Object) //true
console.log(typeof fun) //function
console.log(Object.prototype.toString.call(fun)) //[object Function]
let arr = []
console.log(arr instanceof Array) //true
console.log(arr instanceof Object) //true
console.log(typeof arr) //object
console.log(Object.prototype.toString.call(arr)) //[object Array]
二十二、事件触发器
const EventEmitter = require('events')
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter()
myEmitter.on('myEventName', (a, b) => {
console.log(a, b)
})
myEmitter.emit('myEventName', 1, 2)
myEmitter.emit('myEventName', 1, 2)
myEmitter.once('myOnce', () => {
console.log('只触发一次')
})
myEmitter.emit('myOnce')
myEmitter.emit('myOnce')
myEmitter.on('error', (err) => {
console.error(err)
})
myEmitter.emit('error', new Error('错误'))
二十三、手动封装事件
class MyEmitter {
constructor() {
this.events = {}
}
on(eventName, callback) {
if (this.events[eventName]) {
this.events[eventName].push(callback)
} else {
this.events[eventName] = [callback]
}
}
emit(eventName, ...arg) {
let callbackArr = this.events[eventName]
callbackArr && callbackArr.forEach(item => {
if (Object.prototype.toString.call(item) === '[object Function]') {
item(...arg)
} else if (Object.prototype.toString.call(item) === '[object Object]') {
if (item.once) {
item.callback(...arg)
item.callback = () => {}
}
}
})
}
once(eventName, callback) {
if (this.events[eventName]) {
this.events[eventName].push({
once: true,
callback
})
} else {
this.events[eventName] = [{
once: true,
callback
}]
}
}
}
const myEmitter = new MyEmitter()
module.exports = myEmitter
二十四、父子进程通信
app.js:
const child_process = require('child_process')
const child = child_process.fork('./compute.js')
child.send({ type: 'start' })
child.on('message', (action) => {
if (action.type === 'sum') {
console.log('子进程计算出来的结果:', action.sum)
process.exit()
}
})
process.on('exit', () => {
console.log('主进程结束')
})
console.log('运行到这里')
compute.js:
const computeSum = () => {
let sum = 0
for (let i = 0; i < 1000000; i++) {
sum += i
}
return sum
}
process.on('message', (action) => {
if (action.type === 'start') {
let sum = computeSum()
process.send({
type: 'sum',
sum
})
}
})
二十五、docker
docker:码头工人
doctor:医生
docker toolbox下载链接:Releases · docker-archive/toolbox · GitHub
docker toolbox 国内下载地址:Index of /docker-toolbox/windows/docker-toolbox/
docker官网:Empowering App Development for Developers | Docker
docker hub官网:Docker Hub
docker菜鸟教程:Docker 教程 | 菜鸟教程
安装:
下载最新版的 boot2docker.iso 放在本地缓存文件夹里,否则启动时会联网下载最新版的,切很难下载下来,会报错
可以去百度云盘下载:
链接:百度网盘 请输入提取码
提取码:esp0
也可以去github下载:
https://github.com/boot2docker/boot2docker/releases
hello world:
docker run ubuntu:15.10 /bin/echo "hello world"
运行交互式的容器:
docker run -i -t ubuntu:15.10 /bin/bash
ctrl + D 或者 exit 退出
docker run -d ubuntu:15.10 /bin/sh -c "while true; do echo hello; sleep 5;done"
docker ps
docker logs determined_meitner
进入容器:
docker attach determined_meitner
二十六、koa
koa:框架
koa-router:路由
koa-bodyparser:解析post
koa2-cors:跨域
koa-static:静态资源
koa-logger:日志
@koa/multer multer:上传文件
koa:
const Koa = require('koa')
const app = new Koa()
const { bookNavData } = require('./data')
app.use(ctx => {
ctx.body = {
code: 200,
data: bookNavData,
message: '导航'
}
})
app.listen(84)
console.log(84)
koa-router:
const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa()
const router = new Router()
const { bookNavData } = require('./data')
router.get('/api/nav', ctx => {
ctx.body = {
code: 200,
data: bookNavData,
message: '导航'
}
})
app.use(router.routes())
app.listen(84)
console.log(84)
koa-bodyparser:
const Koa = require('koa')
const Router = require('koa-router')
const bodyParser = require('koa-bodyparser')
const app = new Koa()
const router = new Router()
const { bookNavData } = require('./data')
//用户列表
const userList = [{
id: '001',
username: 'admin',
password: '123456'
}, {
id: '002',
username: 'xu',
password: '123'
}, {
id: '003',
username: 'a',
password: '123456'
}]
//登录
router.post('/api/login', ctx => {
let { username, password } = ctx.request.body
let user = userList.find(item => item.username === username)
if (user) {
if (user.password === password) {
ctx.body = {
code: 200,
data: {
username
},
message: '登录成功'
}
} else {
ctx.body = {
code: 400,
message: '密码错误'
}
}
} else {
ctx.body = {
code: 400,
message: '用户不存在'
}
}
})
//导航
router.get('/api/nav', ctx => {
ctx.body = {
code: 200,
data: bookNavData,
message: '导航'
}
})
app.use(bodyParser())
app.use(router.routes())
app.listen(84)
console.log(84)
koa-compose:
const Koa = require('koa')
const Router = require('koa-router')
const bodyParser = require('koa-bodyparser')
const cors = require('koa2-cors')
const static = require('koa-static')
const logger = require('koa-logger')
const compose = require('koa-compose')
const app = new Koa()
const router = new Router()
const { bookNavData } = require('./data')
//用户列表
const userList = [{
id: '001',
username: 'admin',
password: '123456'
}, {
id: '002',
username: 'xu',
password: '123'
}, {
id: '003',
username: 'a',
password: '123456'
}]
//登录
router.post('/api/login', ctx => {
let { username, password } = ctx.request.body
let user = userList.find(item => item.username === username)
if (user) {
if (user.password === password) {
ctx.body = {
code: 200,
data: {
username
},
message: '登录成功'
}
} else {
ctx.body = {
code: 400,
message: '密码错误'
}
}
} else {
ctx.body = {
code: 400,
message: '用户不存在'
}
}
})
//导航
router.get('/api/nav', ctx => {
ctx.body = {
code: 200,
data: bookNavData,
message: '导航'
}
})
// //跨域
// app.use(cors())
// //日志
// app.use(logger())
// //解析post请求
// app.use(bodyParser())
// //静态资源
// app.use(static(__dirname + '/public'))
// //路由
// app.use(router.routes())
const middlewares = compose([cors(), logger(), bodyParser(), static(__dirname + '/public'), router.routes()])
app.use(middlewares)
app.listen(84)
console.log(84)
GitHub - koajs/compose: Middleware composition utility
koa-router:
const Router = require('koa-router')
const router = new Router()
//导航
router.get('/api/nav', (ctx, next) => {
ctx.body = {
code: 200,
data: bookNavData,
message: '导航'
}
})
//登录
router.post('/api/login', async (ctx, next) => {
let { username, password } = ctx.request.body
ctx.body = {
code: 200,
data: {
username
},
message: '登录成功'
}
})
//路由
app.use(router.routes())
koa-static:
const static = require('koa-static')
//静态资源
//app.use(static('public'))
app.use(static(__dirname + '/public'))
@koa/multer multer(上传单个文件):
const Koa = require('koa')
const Router = require('koa-router')
const bodyParser = require('koa-bodyparser')
const cors = require('koa2-cors')
const static = require('koa-static')
const logger = require('koa-logger')
const compose = require('koa-compose')
const multer = require('@koa/multer')
const app = new Koa()
const router = new Router()
const { bookNavData } = require('./data')
//用户列表
const userList = [{
id: '001',
username: 'admin',
password: '123456'
}, {
id: '002',
username: 'xu',
password: '123'
}, {
id: '003',
username: 'a',
password: '123456'
}]
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, __dirname + '/upload')
},
filename: (req, file, cb) => {
cb(null, `${Date.now()} - ${file.originalname}`)
}
})
const upload = multer({ storage })
//登录
router.post('/api/login', ctx => {
let { username, password } = ctx.request.body
let user = userList.find(item => item.username === username)
if (user) {
if (user.password === password) {
ctx.body = {
code: 200,
data: {
username
},
message: '登录成功'
}
} else {
ctx.body = {
code: 400,
message: '密码错误'
}
}
} else {
ctx.body = {
code: 400,
message: '用户不存在'
}
}
})
//导航
router.get('/api/nav', ctx => {
ctx.body = {
code: 200,
data: bookNavData,
message: '导航'
}
})
//上传文件
router.post('/api/upload', upload.single('img'), ctx => {
ctx.body = {
code: 200,
data: ctx.request.file,
message: '上传成功'
}
})
// //跨域
// app.use(cors())
// //日志
// app.use(logger())
// //解析post请求
// app.use(bodyParser())
// //静态资源
// app.use(static(__dirname + '/public'))
// //路由
// app.use(router.routes())
const middlewares = compose([cors(), logger(), bodyParser(), static(__dirname + '/public'), router.routes()])
app.use(middlewares)
app.listen(84)
console.log(84)
@koa/multer multer(上传多个文件):
const Koa = require('koa')
const Router = require('koa-router')
const bodyParser = require('koa-bodyparser')
const cors = require('koa2-cors')
const static = require('koa-static')
const logger = require('koa-logger')
const compose = require('koa-compose')
const multer = require('@koa/multer')
const app = new Koa()
const router = new Router()
const { bookNavData } = require('./data')
//用户列表
const userList = [{
id: '001',
username: 'admin',
password: '123456'
}, {
id: '002',
username: 'xu',
password: '123'
}, {
id: '003',
username: 'a',
password: '123456'
}]
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, __dirname + '/upload')
},
filename: (req, file, cb) => {
cb(null, `${Date.now()} - ${file.originalname}`)
}
})
const upload = multer({ storage })
//登录
router.post('/api/login', ctx => {
let { username, password } = ctx.request.body
let user = userList.find(item => item.username === username)
if (user) {
if (user.password === password) {
ctx.body = {
code: 200,
data: {
username
},
message: '登录成功'
}
} else {
ctx.body = {
code: 400,
message: '密码错误'
}
}
} else {
ctx.body = {
code: 400,
message: '用户不存在'
}
}
})
//导航
router.get('/api/nav', ctx => {
ctx.body = {
code: 200,
data: bookNavData,
message: '导航'
}
})
//上传文件
router.post('/api/upload', upload.array('img', 9), ctx => {
ctx.body = {
code: 200,
data: ctx.request.files,
message: '上传成功'
}
})
// //跨域
// app.use(cors())
// //日志
// app.use(logger())
// //解析post请求
// app.use(bodyParser())
// //静态资源
// app.use(static(__dirname + '/public'))
// //路由
// app.use(router.routes())
const middlewares = compose([cors(), logger(), bodyParser(), static(__dirname + '/public'), router.routes()])
app.use(middlewares)
app.listen(84)
console.log(84)
二十七、node如何打断点调试
inspect 检查;查看;审视;视察
指令解释:
//在 host:port 上激活检查器
--inspect[=[host:]port]
//在 host:port 上激活检查器,并默认在第一行打断点
--inspect-brk[=[host:]port]
参考链接:
//使用node启动
node --inspect-brk app
//使用nodemon启动
nodemon --inspect-brk app
//默认不在第一行打断点
nodemon --inspect app
--nolazy没有官方文档,可以参考这个链接:
What does the Node.js `--nolazy` flag mean? - Stack Overflow
chrome地址栏里输入:
chrome://inspect/#devices
打断点:
看日志:
二十八、mysql里char和varchar的区别
varchar(100) 100 代表一百个字符
char是固定长度的,而varchar会根据具体的长度来使用存储空间
参考链接:mysql varchar(n) 问题 char与varchar选择问题_超Sir丶的博客-CSDN博客
二十九、mysql语句参考
U
-- 选择数据库
USE demo;
USE test2;
-- 删除数据库
DROP TABLE `admin`;
-- 管理员
CREATE TABLE `admin` (
`uid` INT UNSIGNED AUTO_INCREMENT,
`username` VARCHAR(20) NOT NULL,
`password` VARCHAR(30) NOT NULL,
`create_time` DATETIME NOT NULL,
PRIMARY KEY (`uid`)
);
-- 老师
DROP TABLE `teacher`;
CREATE TABLE `teacher` (
`uid` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '老师的ID',
`username` VARCHAR(20) NOT NULL COMMENT '老师登录平台的用户名',
`password` VARCHAR(30) NOT NULL COMMENT '老师登录平台的密码',
`age` INT UNSIGNED COMMENT '老师的年龄',
`gender` enum('男', '女', '保密') DEFAULT '保密' COMMENT '老师的性别',
`email` VARCHAR(50) COMMENT '邮箱',
`create_time` DATETIME NOT NULL COMMENT '老师加入平台的时间'
);
-- 显示表格各列的信息,包括注释
SHOW FULL COLUMNS FROM teacher;
-- 查
SELECT * FROM `admin`;
SELECT username FROM admin;
SELECT `password`, `username` FROM admin WHERE uid = 1;
-- LIKE 可以做模糊搜索 AND 表示并且 OR 表示或者
SELECT * FROM admin WHERE username LIKE '%admi%';
SELECT * FROM admin WHERE username LIKE '%a%' AND `password` = '123';
SELECT * FROM admin WHERE username LIKE '%a%' OR `password` = '123';
-- 查询以x开头的两位用户名
SELECT * FROM admin WHERE username LIKE 'x_';
-- 查询用户名中不包含a的
SELECT * FROM admin WHERE username NOT LIKE '%a%';
-- 用户名中包含x或者包含u或者包含a或者包含l
SELECT * FROM admin WHERE username RLIKE '[xual]';
-- REGEXP 和 RLIKE 一样
SELECT * FROM admin WHERE username REGEXP '[xa]';
-- 包含a
SELECT * FROM admin WHERE username RLIKE '[a]+';
SELECT * FROM admin WHERE username RLIKE 'a+';
-- a开头
SELECT * FROM admin WHERE username RLIKE '^[a]+';
-- x或a开头
SELECT * FROM admin WHERE username RLIKE '^[xa]+';
-- 查询以x开头的两位用户名, "."匹配单个字符
SELECT * FROM admin WHERE username RLIKE 'x.';
SELECT username, `password`
FROM admin
-- 排序 默认升序, 升序:asc ascend ,降序:desc descend
SELECT * FROM admin ORDER BY username;
SELECT * FROM admin ORDER BY username ASC;
SELECT * FROM admin ORDER BY username DESC;
-- 先用创建时间排序,再用用户名排序
SELECT * FROM admin ORDER BY create_time DESC, username ASC;
-- 只要前两条
SELECT *
FROM admin
LIMIT 2;
-- 分页 第一个数是起始点,从零开始,第二个数是每页的数量
SELECT * FROM admin LIMIT 0, 4;
SELECT * FROM admin LIMIT 4, 1;
-- 和 LIMIT 4, 1相同
SELECT * FROM admin LIMIT 1 OFFSET 4;
-- 排序后分页
SELECT * FROM admin ORDER BY username ASC LIMIT 1, 2;
-- 数量
SELECT COUNT(*) FROM admin;
-- 完全限定的表名
SELECT admin.username FROM admin;
-- 增
INSERT INTO `admin` (`username`, `password`, `create_time`) VALUES ('admin', '123456', '2019-02-20 10:36:06');
-- 增,自动获取系统时间
INSERT INTO `admin` (`username`, `password`, `create_time`) VALUES ('xu', '123', NOW());
INSERT INTO `admin` (`username`, `password`, `create_time`) VALUES ('zhangsan', '123', NOW());
INSERT INTO `admin` (`username`, `password`, `create_time`) VALUES ('lisi', '123', NOW());
INSERT INTO `admin` (`username`, `password`, `create_time`) VALUES ('wangwu', '123', NOW());
-- 1号老师张三 男
INSERT INTO `teacher`
(`username`, `password`, `age`, `gender`, `email`, `create_time`)
VALUES ('zhangsan', 'zhangsan', 30, '男', '1183391880@qq.com', NOW());
-- 2号老师李四 女
INSERT INTO `teacher`
(`username`, `password`, `age`, `gender`, `email`, `create_time`)
VALUES ('lisi', 'lisi123', 31, '女', 'lisi@qq.com', NOW());
-- 3号老师王五 性别保密
INSERT INTO `teacher`
(`username`, `password`, `age`, `email`, `create_time`)
VALUES ('wangwu', 'wangwu123', 32, 'lisi@qq.com', NOW());
-- 4号老师xu 既是老师又是管理员
INSERT INTO `teacher`
(`username`, `password`, `age`, `email`, `create_time`)
VALUES ('xu', 'xu', 30, 'xu@qq.com', NOW());
INSERT INTO `teacher`
(`username`, `password`, `age`, `email`, `create_time`)
VALUES ('lilei', 'lilei', 36, 'lilei@qq.com', NOW());
SELECT * FROM teacher;
SELECT * FROM teacher WHERE age > 30;
SELECT * FROM teacher WHERE age >= 30;
SELECT * FROM teacher WHERE age != 30;
SELECT * FROM teacher WHERE age < 31;
SELECT * FROM teacher WHERE age <= 31;
-- 查询两个表
SELECT * FROM admin, teacher;
-- 关联查询
SELECT * FROM admin, teacher WHERE admin.username = teacher.username;
-- 关联查询,重复名称起个别名
SELECT admin.username as '管理员表里的用户名', teacher.username as '老师表里的用户名'
FROM admin, teacher WHERE admin.username = teacher.username;
-- 两个表里都有username字段,导致报错
SELECT username FROM admin, teacher WHERE admin.username = teacher.username;
-- 别名
SELECT username as `name` FROM admin;
-- 两个表有相同的字段时
SELECT admin.username FROM admin, teacher;
SELECT admin.username as admin_username FROM admin, teacher;
-- 不等于 和 != 一样
SELECT * FROM teacher WHERE age <> 30;
-- 去重
SELECT DISTINCT age from teacher;
-- 把两次查询的结果合并后去重
SELECT username FROM admin
UNION
SELECT username FROM teacher;
-- 把两次查询的结果合并后全部展示
SELECT username FROM admin
UNION ALL
SELECT username FROM teacher;
-- 求和
SELECT SUM(age) FROM teacher;
-- 求平均值
SELECT AVG(age) FROM teacher;
-- 求数量
SELECT COUNT(age) FROM teacher;
-- 按年龄分组,并统计分组的数量
SELECT age, COUNT(age) FROM teacher GROUP BY age;
-- 按年龄分组后求和
SELECT age, SUM(age) FROM teacher GROUP BY age;
-- WITH ROLLUP 汇总,分组后再汇总
SELECT age, COUNT(age) FROM teacher GROUP BY age WITH ROLLUP;
-- 使用COALESCE把null字段变成“汇总”
SELECT COALESCE(age, '汇总'), COUNT(age) FROM teacher GROUP BY age WITH ROLLUP;
-- 删除某个老师
DELETE FROM teacher WHERE uid = 1;
-- 删除某个管理员
DELETE FROM `admin` WHERE uid = 2;
-- 改
UPDATE admin SET `password` = '123456' WHERE uid = 2;
SELECT * FROM admin;
UPDATE admin SET username = 'xu123', `password` = '12' WHERE uid = 2;
-- 删
DELETE FROM admin WHERE uid = 2;
create.sql:
-- 删除数据库
DROP TABLE IF EXISTS `admin`;
-- 管理员
CREATE TABLE IF NOT EXISTS `admin` (
`uid` INT UNSIGNED AUTO_INCREMENT,
`username` VARCHAR(20) NOT NULL,
`password` VARCHAR(30) NOT NULL,
`create_time` DATETIME NOT NULL,
PRIMARY KEY (`uid`)
);
-- 老师
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE IF NOT EXISTS `teacher` (
`uid` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '老师的ID',
`username` VARCHAR(20) NOT NULL COMMENT '老师登录平台的用户名',
`password` VARCHAR(30) NOT NULL COMMENT '老师登录平台的密码',
`age` INT UNSIGNED COMMENT '老师的年龄',
`gender` enum('男', '女', '保密') DEFAULT '保密' COMMENT '老师的性别',
`email` VARCHAR(50) COMMENT '邮箱',
`create_time` DATETIME NOT NULL COMMENT '老师加入平台的时间'
);
insert.sql:
-- 增
INSERT INTO `admin` (`username`, `password`, `create_time`) VALUES ('admin', '123456', '2019-02-20 10:36:06');
-- 增,自动获取系统时间
INSERT INTO `admin` (`username`, `password`, `create_time`) VALUES ('xu', '123', NOW());
INSERT INTO `admin` (`username`, `password`, `create_time`) VALUES ('zhangsan', '123', NOW());
INSERT INTO `admin` (`username`, `password`, `create_time`) VALUES ('lisi', '123', NOW());
INSERT INTO `admin` (`username`, `password`, `create_time`) VALUES ('wangwu', '123', NOW());
INSERT INTO `admin` (`username`, `password`, `create_time`) VALUES ('Abc', '123', NOW());
INSERT INTO `admin` (`username`, `password`, `create_time`) VALUES ('bcd', '123', NOW());
INSERT INTO `admin` (`username`, `password`, `create_time`) VALUES ('Bcd', '123', NOW());
-- 1号老师张三 男
INSERT INTO `teacher`
(`username`, `password`, `age`, `gender`, `email`, `create_time`)
VALUES ('zhangsan', 'zhangsan', 30, '男', '1183391880@qq.com', NOW());
-- 2号老师李四 女
INSERT INTO `teacher`
(`username`, `password`, `age`, `gender`, `email`, `create_time`)
VALUES ('lisi', 'lisi123', 31, '女', 'lisi@qq.com', NOW());
-- 3号老师王五 性别保密
INSERT INTO `teacher`
(`username`, `password`, `age`, `email`, `create_time`)
VALUES ('wangwu', 'wangwu123', 32, 'lisi@qq.com', NOW());
-- 4号老师xu 既是老师又是管理员
INSERT INTO `teacher`
(`username`, `password`, `age`, `email`, `create_time`)
VALUES ('xu', 'xu', 30, 'xu@qq.com', NOW());
INSERT INTO `teacher`
(`username`, `password`, `age`, `email`, `create_time`)
VALUES ('lilei', 'lilei', 36, 'lilei@qq.com', NOW());
INSERT INTO `teacher`
(`username`, `password`, `age`, `email`, `create_time`)
VALUES ('Abc', 'Abc', 36, 'abc@qq.com', NOW());
select.sql:
-- 查
SELECT * FROM `admin`;
SELECT * FROM teacher;
SELECT username FROM admin;
SELECT `password`, `username` FROM admin WHERE uid = 1;
-- LIKE 可以做模糊搜索 AND 表示并且 OR 表示或者
SELECT * FROM admin WHERE username LIKE '%admi%';
SELECT * FROM admin WHERE username LIKE '%a%' AND `password` = '123';
SELECT * FROM admin WHERE username LIKE '%a%' OR `password` = '123';
-- 查询以x开头的两位用户名
SELECT * FROM admin WHERE username LIKE 'x_';
-- 查询用户名中不包含a的
SELECT * FROM admin WHERE username NOT LIKE '%a%';
-- 用户名中包含x或者包含u或者包含a或者包含l
SELECT * FROM admin WHERE username RLIKE '[xual]';
-- REGEXP 和 RLIKE 一样
SELECT * FROM admin WHERE username REGEXP '[xa]';
-- 包含a
SELECT * FROM admin WHERE username RLIKE '[a]+';
SELECT * FROM admin WHERE username RLIKE 'a+';
-- a开头
SELECT * FROM admin WHERE username RLIKE '^[a]+';
-- x或a开头
SELECT * FROM admin WHERE username RLIKE '^[xa]+';
-- 查询以x开头的两位用户名, "."匹配单个字符
SELECT * FROM admin WHERE username RLIKE 'x.';
SELECT username, `password`
FROM admin
-- 区分大小写,只查询A开头的用户名
SELECT * FROM admin WHERE username LIKE BINARY('A%');
SELECT * FROM admin WHERE BINARY username LIKE 'a%';
-- 区分大小写排序 https://www.jianshu.com/p/f8707b8461d3
SELECT * FROM admin ORDER BY username collate utf8mb4_bin;
SELECT * FROM admin ORDER BY username COLLATE utf8mb4_bin DESC;
-- 排序 默认升序, 升序:asc ascend ,降序:desc descend
SELECT * FROM admin ORDER BY username;
SELECT * FROM admin ORDER BY username ASC;
SELECT * FROM admin ORDER BY username DESC;
-- 先用创建时间排序,再用用户名排序
SELECT * FROM admin ORDER BY create_time DESC, username ASC;
-- 只要前两条
SELECT *
FROM admin
LIMIT 2;
-- 分页 第一个数是起始点,从零开始,第二个数是每页的数量
SELECT * FROM admin LIMIT 0, 4;
SELECT * FROM admin LIMIT 4, 1;
-- 和 LIMIT 4, 1相同
SELECT * FROM admin LIMIT 1 OFFSET 4;
-- 排序后分页
SELECT * FROM admin ORDER BY username ASC LIMIT 1, 2;
-- 数量
SELECT COUNT(*) FROM admin;
-- 完全限定的表名
SELECT admin.username FROM admin;
SELECT * FROM teacher;
SELECT * FROM teacher WHERE age > 30;
SELECT * FROM teacher WHERE age >= 30;
SELECT * FROM teacher WHERE age != 30;
SELECT * FROM teacher WHERE age < 31;
SELECT * FROM teacher WHERE age <= 31;
-- 查询两个表
SELECT * FROM admin, teacher;
-- 关联查询
SELECT * FROM admin, teacher WHERE admin.username = teacher.username;
-- 关联查询,重复名称起个别名
SELECT admin.username as '管理员表里的用户名', teacher.username as '老师表里的用户名'
FROM admin, teacher WHERE admin.username = teacher.username;
-- 两个表里都有username字段,导致报错
SELECT username FROM admin, teacher WHERE admin.username = teacher.username;
-- 别名
SELECT username as `name` FROM admin;
-- 两个表有相同的字段时
SELECT admin.username FROM admin, teacher;
SELECT admin.username as admin_username FROM admin, teacher;
-- 不等于 和 != 一样
SELECT * FROM teacher WHERE age <> 30;
-- 去重
SELECT DISTINCT age from teacher;
-- 把两次查询的结果合并后去重
SELECT username FROM admin
UNION
SELECT username FROM teacher;
-- 把两次查询的结果合并后全部展示
SELECT username FROM admin
UNION ALL
SELECT username FROM teacher;
-- 求和
SELECT SUM(age) FROM teacher;
-- 求平均值
SELECT AVG(age) FROM teacher;
-- 求数量
SELECT COUNT(age) FROM teacher;
-- 按年龄分组,并统计分组的数量
SELECT age, COUNT(age) FROM teacher GROUP BY age;
-- 按年龄分组后求和
SELECT age, SUM(age) FROM teacher GROUP BY age;
-- WITH ROLLUP 汇总,分组后再汇总
SELECT age, COUNT(age) FROM teacher GROUP BY age WITH ROLLUP;
-- 使用COALESCE把null字段变成“汇总”
SELECT COALESCE(age, '汇总'), COUNT(age) FROM teacher GROUP BY age WITH ROLLUP;
other.sql:
-- 选择数据库
USE demo;
USE test2;
-- 显示表格各列的信息,包括注释
SHOW FULL COLUMNS FROM teacher;
-- 删除某个老师
DELETE FROM teacher WHERE uid = 1;
-- 删除某个管理员
DELETE FROM `admin` WHERE uid = 2;
-- 改
UPDATE admin SET `password` = '123456' WHERE uid = 2;
SELECT * FROM admin;
UPDATE admin SET username = 'xu123', `password` = '12' WHERE uid = 2;
-- 删
DELETE FROM admin WHERE uid = 2;
-- 显示建表语句
SHOW CREATE TABLE admin;
三十、mysql主键
MySQL主键设计原则:
- MySQL主键应当是对用户没有意义的。
- MySQL主键应该是单列的,以便提高连接和筛选操作的效率(当然复合主键是可以的,只是不建议)
- 永远也不要更新MySQL主键
- MySQL主键不应包含动态变化的数据,如时间戳、创建时间列、修改时间列等
- MySQL主键应当有计算机自动生成。
主键有哪几种:
(1). 自增序列;
(2). UUID()函数生成的随机值;
(3). 用户注册的唯一性帐号名称,字符串类型,一般长度为:40个字符;
(4). 基于一套机制生成类似自增的值,比如序列生成器;
mysql使用自增主键有何优势:
1) 数据库自动编号,速度快,而且是增量增长,聚集型主键按顺序存放,对于检索非常有利。
2) 数字型,占用空间小,易排序,在程序中传递方便。
三十一、安装虚拟机
1)获取软件
链接:百度网盘 请输入提取码
提取码:vi1e
win7、win10等系统镜像获取网址:
2)安装,默认安装就可以
VMware创建虚拟机的时候遇到一个经典报错:
Attempting to start up from:
EFI VMware Virtual SCSI Hard Drive (0.0) … unsuccessful.
EFI VMware Virtual SATA CDROM Drive (1.0) … unsuccessful.
EFI Network...
解决方法:
1. 在虚拟机的安装目录里找到vmx文件
2. 删掉里面的 firmware="efi"
3. 保存重启虚拟机即可正常安装
3)新建虚拟机,并安装win7系统
4)安装VMware tools
1.在虚拟机win7的光驱里放入vmware tools的iso镜像文件
2.启动虚拟机
3.在打开虚拟机菜单,在下拉列表里选择安装VMware tools
4.等一会会弹出对话框,点击运行setup.exe,一步步安装即可
5.安装后重启,以后系统里的文件就可以直接拷贝的虚拟机了
三十二、磨课笔记
npm config list
npm init -y
npm install/i <package name> --save-dev / -D
npm install/i <package name> --save / -S
注册信息:
http://registry.npmjs.org/react
http://registry.npmjs.org/react/latest
更新包(如果已经安装再次安装并不会更新,所以需要update命令)
npm update react
命令行登录npm:
D:\source\m-apps\demo\ts\npm>npm login
Username: xutongbao
Password:
Email: (this IS public) 1183391880@qq.com
Logged in as xutongbao on https://registry.npmjs.org/.
查询镜像源:
D:\source\m-apps\demo\ts\npm>npm get registry
https://registry.npmjs.org/
设置镜像源:
npm set registry 'https://registry.npmjs.org'
通过第三方镜像源管理工具来管理npm的镜像:
npm i nrm -g
nrm ls
* npm -------- https://registry.npmjs.org/
yarn ------- https://registry.yarnpkg.com/
cnpm ------- http://r.cnpmjs.org/
taobao ----- https://registry.npm.taobao.org/
nj --------- https://registry.nodejitsu.com/
npmMirror -- https://skimdb.npmjs.com/registry/
edunpm ----- http://registry.enpmjs.org/
nrm use taobao
nrm use npm
发包:
npm publish
"version": "1.0.2"
大版本,小功能版本,补丁版本
取消发布包:
npm unpublish --force
废弃一个包:
npm deprecate <pkg>