NodeJs 笔记补充
NodeJS知识补充Node js 和 JavaScript区别都基于ECMAScript标准的实现和扩展ECMAScript 定义语法,写js和node都要遵守语法包含了:变量定义、循环、判断、函数、原型、原型链、作用域、闭包、异步。ECMAScript定义了语法,但是它不能操作DOM,不能监听click事件,不能发送ajax请求,不能处理http请求,不能操作文件,只有它,几乎做不了任何实际的
NodeJS
知识补充
Node js 和 JavaScript区别
都基于ECMAScript标准的实现和扩展
ECMAScript 定义语法,写js和node都要遵守
语法包含了:变量定义、循环、判断、函数、原型、原型链、作用域、闭包、异步。
ECMAScript定义了语法,但是它不能操作DOM,不能监听click事件,不能发送ajax请求,不能处理http请求,不能操作文件,只有它,几乎做不了任何实际的项目
JavaScript(完成浏览器端任何操作):ECMAScript + WebAPI
NodeJS(完成server端任何操作): ECMAScript + NodeAPI
CommonJS 模块
node应用由模块组成,采用的commonjs模块规范。
每一个文件就是一个模块,拥有自己独立的作用域,变量,以及方法等,对其他的模块都不可见。
CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。
加载某个模块,其实是加载该模块的module.exports(导出包)属性。require方法用于加载模块(导入包)。
浏览器不兼容CommonJS的根本原因,在于缺少四个Node.js环境的变量。
module
exports
require
global
只要能够提供这四个变量,浏览器就能加载 CommonJS 模块。
特点
所有代码都运行在模块作用域,不会污染全局作用域。
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
模块加载的顺序,按照其在代码中出现的顺序。
加载模块是同步的(只有加载完成,才能执行之后操作)
语法
//定义对象
let obj={
name:'jine',
age:22,
printf:function(){
console.log(this.name)
console.log(this.age)
}
}
//把模块导出
module.exports=obj
-------------------------------------------------------------------------------------
//引入模块
const model=require('./model.js')
//使用模块中内容
model.printf()
-------------------------------------------------------------------------------------
删除模块的缓存 缓存保存在require.cache中,可操作该属性进行删除
// 删除指定模块的缓存
delete require.cache[moduleName];
// 删除所有模块的缓存
Object.keys(require.cache).forEach(function(key){delete require.cache[key];})
浏览器概述
1、人机交互(UI)
2、网络请求部分(Socket)
3、JavaScript引擎(解析执行JavaScript)
4、渲染引擎(渲染HTML,CSS)又叫排版引擎或浏览器内核
5、数据库存储(cookie、HTML5的本地存储Localstorage、SessionStorage)
渲染引擎
主流的渲染引擎有
Chrome浏览器:Blink引擎 (WebKit的一个分支) .
Safari浏览器:WebKit引擎,(windows版本2008年3月18日推 出正式版,但苹果已于2012年7月25日停止开发Windows版的Safari.)
FireFox浏览器: Gecko引擎
Opera浏览器: Blink引擎(早期版使用Presto引擎,后改为Blink引擎)
Internet Explorer浏览器: Trident引擎 。(最后一个版本 IE 11)
Microsoft Edge浏览器: EdgeHTML引擎(Trident的一个分支)
工作原理
1.解析HTML构建Dom树(Document object Model,文档对象模型),DOM 是W3C组织推荐的处理可扩展置标语言的标准编程接口
渲染引擎会将页面所有CSS解析生成CSS规则树
渲染树=DOM树+CSS规则树
2.构建渲染树,渲染树并不等同于Dom树,因为像head标签或display: none这样的元素就没有必要放到渲染树
中了,但是它们在Dom树。(渲染树剔除一些没有必要显示的元素)
3.对渲染树进行布局,定位坐标和大小、确定是否换行、确定position、 overflow- z- index等等, 这个过程叫"layout"(先布局) 或"reflow"(后进行重新调整)
"注意:尽量避免频繁使用layout和reflow,会影响性能"
4.绘制渲染树,调用操作系统底层API进行绘图操作。
解析DOM树---->构建渲染树---->渲染树布局---->绘制渲染树
浏览器访问网站过程
1、浏览器中输入网址
2、浏览器对URL网址请求报文封装
3、浏览器发起DNS解析请求,将域名转为IP地址
4、浏览器将请求报文发送到服务器
5、服务器接收请求报文,并解析
6、服务器处理用户请求,并将处理结果封装成HTTP响应报文。
7、服务器将HTTP响应报文发送给浏览器
8、浏览器接收到服务器响应的HPPT报文,并解析
9、浏览器解析HTML页面并渲染,在解析HTML页面时遇到新的资源需要再次发起请求
10、最终浏览器渲染出完整页面
Node概述
1. node.js 是一个开发平台,就像Java开发平台、.Net开发平台、PHP开发平台、Apple开发平台一样。
何为开发平台?有对应的编程语言、有语言运行时、有能实现特定功能的API (SDK: Software Development Kit)
2.该平台使用的编程语言是JavaScript 语言。
3. node.js 平台是基于Chrome V8 JavaScript 引擎构建。
4.基于node.js 可以开发控制台程序(命令行程序、CLI程序)、桌面应用程序(GUI) (借助node-webkit、 electron等框架实现)、Web 应用程序(网站)
PHP 开发技术栈: LAMP - Linux Apache MySQL PHP
node.js全栈开发技术栈: MEAN-MongoDB Express Angular Node.js
特点
1、Node是一个构建于Chrome V8引擎(只有堆和调用栈)之上的一个Javascript 运行环境
Node是-个运行环境,作用是让js拥有开发服务端的功能
"Chrome V8引擎(只有堆和调用栈)所以说:没有浏览器提供的一些API,例如:DOM、BOM等"
2、Node使用事件驱动(当事件被触发时,执行传递过去的回调函数)、非阻塞IO模型(异步读写,当执行I/0操作时,不会阻塞线单线程)使得它非常的轻量级和高效。
Node中绝大多数API都是异步(类似于ajax) ,目的是提高性能
3、js为单线程(一个调用栈便是一个单线程),node提供多线程处理
4、Node中的NPM是世界上最大的开源库生态系统(类似于github)
5、Node本身就是个web容器(服务器),所以无需要 Apache、IIS等。
node.js 中的单线程-非阻塞IO
补充知识:
代码演示,例如:
console.log("1")
function fun(){
setTimeout(function(){
console.log("2")
},500)
}
function funny(){
setTimeout(function(){
console.log("3")
},200)
}
fun()
funny()
console.log("4")
/*
结果为:1 4 3 2
为什么不是1,2,3,4呢,按理说不是应该顺序执行吗?
这便是node是单线程工作,非阻塞IO模型(异步读写,不会阻塞线单线程)
*/
/*
执行流程:1、首先调用栈先调用入口mian()函数,先将console.log("1")入栈,打印完毕后弹出栈中
2、此时栈中剩余main()函数,再将fun()入栈,再出栈放入到node.js底层开启异步中放入队列中进行等待
3、再将funny()入栈,再出栈也放入到WEB APIS中进行等待
4、将console.log("4")入栈,打印完毕后弹出栈中
5、此时main()函数也出栈,此时栈空
6、再去callback queue中找,依次调用异步成功后的callback(),上面fun()和funny()函数因设置时间不同,所以先将funny()放入第一个callback(),后放入fun()第二个callback()
*/
JavaScript 是单线程工作,这意味着两段脚本不能同时运行,而是必须一个接一个地运行。我们人类是多线程工作。你可以使用多个手指打字,可以一边开车一边与人交谈。唯一会妨碍我们的是打喷嚏,因为当我们打喷嚏的时候,所有当前进行的活动都必须暂停(只是举例,不包括心跳啥的)。JavaScript由于单线程限制,防止阻塞,只能通过异步函数的调用方式,把需要延迟处理的事件放入事件循环队列。到目前为止,回调(callback)是编写和处理JavaScript程序异步逻辑的最常用方式。
可以自己进行编写代码,来去参考浏览器(与node.js运行原理相同)演示动画地址:http://latentflip.com/loupe
REPL介绍
1. REPL 全称: Read- Eval-Print-Loop (交互式解释器)
R读取-读取用户输入,解析输入了Javascript数据结构并存储在内存中。
E执行-执行输入的数据结构
P打印-输出结果
L循环-循环操作以上步骤直到用户两次按下ctrl-c 按钮退出。
2.在REPL中编写程序(类似于浏览器开发人员工具中的控制台功能)
直接在控制台输入、node 命令进入REPL环境
3.按两次Control + C退出REPL界面或者输入、.exit~ 退出REPL界面
按住control 键不要放开,然后按两下c键
Package.json 属性说明
name - 包名。
version - 包的版本号。
description - 包的描述。
homepage - 包的官网 url 。
author - 包的作者姓名。
contributors - 包的其他贡献者姓名。
dependencies - 依赖包列表。如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下。
repository - 包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上。
main - main 字段指定了程序的主入口文件,require('moduleName') 就会加载这个文件。这个字段的默认值是模块根目录下面的 index.js。
keywords - 关键字
Node.JS 模块化
1、require 导入模块
const http=require('http')
2、exports 导出模块
db={name:'jine',age:22}
module.exports=db
内置模块使用
1、导包
const fs=require('fs');
2、调用unlink删除文件方法
fs.unlink('./tmp/hello.txt',(err)=>{
if(err) throw err;
console.log('已成功删除 /tmmp/hello')
})
-----------------------------------------------------------------------------------------
文件读取操作,例如
const fs=require('fs')
fs.readFile('./etc/passwd.txt','utf-8',(err,data)=>{
console.log(err);
//err是一个错误对象,没有就返回一个null
console.log(data);
})
-----------------------------------------------------------------------------------------
文件写操作(如果没有文件会创建一个,若有会覆盖掉),例如:
const fs=require('fs')
const data=`
我是一段文本内容,啦啦啦啦啦啦啦啦啦啦啦~~~
`
fs.writeFile('./etc/content.txt',data,(err)=>{
if(err==null){
console.log("文件保存成功√")
}else{
console.log(err)
}
})
获取文件或目录的路径
console.log(__dirname)
/*获取的是当前文件的所在目录的绝对路径 */
console.log(__filename)
/*获取的是当前文件的绝对路径*/
path 模块
为了避免少些斜杠而导致路径不对,可以引入path模块
const path=require('path')
const filePath=path.join(__dirname,'test','file.txt')
console.log(filePath)
/*
c:\Users\Jine\Desktop\my_code_space\jine\Daily code practice\07、03\test\file.txt
*/
http 模块
//使用内置模块http创建一个服务器
//1、导入http模块
const http=require('http')
//2、创建一个服务器 (返回值代表当前服务器)
const server=http.createServer((request,response)=>{
//3、设置返回给用户看的内容
response.setHeader('Content-Type','text/html;charset=utf-8')
/*setHeader 防止中文乱码,所以要设置响应头 */
response.end("你好,世界~")
})
//4、开启服务器
server.listen(1024,()=>{
console.log("服务器开启~")
})
/*开启服务器后,可以在本地浏览器中访问 127.0.0.1:1024 或 localhost:1024*/
静态资源服务器
//导入模块
const http=require("http")
const fs=require("fs")
const path=require('path')
//创建服务器
const server=http.createServer((request,response)=>{
//设置返回用户内容
//获取用户请求哪一个资源
let userRq=request.url
//获取用户读取的资源对应的路径
const filePath=path.join(__dirname,'web',userRq)
//读取用户请求的资源文件
fs.readFile(filePath,(err,data)=>{
if(err==null){
response.end(data)
}else{
response.end("404 not found")
}
})
})
server.listen(8888,()=>{
console.log("服务器开启成功√")
})
URL 模块 (get)
"get 应用(数据存在url中,相对较小,req.url可以拿到)"
"get 安全性低,一般用于请求数据/获取数据
例如:
//导入模块
const http=require('http')
const url=require('url')
//创建服务器
const server=http.createServer((request,response)=>{
/*通过request.url拿到前端传递过来的参数
* 导入url模块,处理接受到的字符串
* 调用parse方法,(参数1:要处理的url,参数2:如果写true便返回一个对象)
* 返回的对象中有query属性,其也是一个对象,这个属性里面有get传递过来的参数
*/
let URL=url.parse(request.url,true)
// console.log(URL.query)
response.end(JSON.stringify(URL.query))
/*若拿到了get信息对应的数据,便可返回字符串给前端 */
})
//启动服务器
server.listen(8888,()=>{
console.log("服务器已开启√")
})
querystring 模块(post)
"post 应用(数据存在请求体中,相对而言较大,传递到node.js会分小块传送)"
"post 一般用于提交数据"
"测试post接口软件 https://www.postman.com/downloads/"
node.js如何接收:
他也是一小块一小块的接收.
1.首先有一个容器
2.给req对象一个data事件(这个事件会执行很多次)这个事件里面就把这些小块的数据拼接起来
3.给req对象一个end事件(这个事件只会执行一次)表示数据传递完了
4.处理传递过来的数据转为对象 queryString.parse();
例如:
//导入模块
const http=require('http')
const querystring=require('querystring')
//创建服务器
const server=http.createServer((request,response)=>{
//容器
let postData=""
//给request对象一个data事件
//事件处理程序,chunk参数是这次传递过来的一小块内容
request.on('data',(chunk)=>{
//将每此传过来小块的内容放到容器中
postData+=chunk
})
//数据传递完毕执行
request.on('end',()=>{
//打印容器
console.log(postData)
//解析容器中的数据,转为对象
let postObj=querystring.parse(postData)
console.log(postObj)
})
//可以根据post传来的参数,去数据库中判断是否正确
//最后返回前端页面
response.end("over")
})
//启动服务器
server.listen(8888,()=>{
console.log("服务器已开启√")
})
第三方模块
爬虫模块
1、新建文件夹,文件夹名字非中文,且不要和模块名一样
2、在cmd打开这个文件夹,然后输入 npm init -y (初始化)
3、去npm官网,搜索crawler
4、在cmd中运行 npm i crawler(下载爬虫模块)
5、使用爬虫,如下代码
爬取元素内容,例如:
var Crawler = require("crawler");
var c = new Crawler({
maxConnections : 10,
// This will be called for each crawled page
callback : function (error, res, done) {
if(error){
console.log(error);
}else{
var $ = res.$;
// $ is Cheerio by default
//a lean implementation of core jQuery designed specifically for the server
console.log($("title").text());
}
done();
}
});
// Queue just one URL, with default callback
c.queue('http://www.baidu.com');
-----------------------------------------------------------------------------------------
爬取图片或视频,例如:
var Crawler = require("crawler");
var fs = require('fs');
var c = new Crawler({
encoding:null,
jQuery:false,// set false to suppress warning message.
callback:function(err, res, done){
if(err){
console.error(err.stack);
}else{
fs.createWriteStream(res.options.filename).write(res.body);
}
done();
}
});
c.queue({
uri:"https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=false&word=%E7%BE%8E%E5%A5%B3&step_word=&hs=0&pn=7&spn=0&di=35090&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=0&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=undefined&cs=3186997546%2C1762170182&os=86097226%2C93959096&simid=3448572432%2C288122016&adpicid=0&lpn=0&ln=3390&fr=&fmq=1593780721779_R&fm=&ic=undefined&s=undefined&hd=undefined&latest=undefined©right=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&ist=&jit=&cg=girl&bdtype=0&oriquery=&objurl=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201309%2F03%2F20130903141830_Q4Wuc.jpeg&fromurl=ippr_z2C%24qAzdH3FAzdH3F4_z%26e3B17tpwg2_z%26e3Bv54AzdH3Frj5rsjAzdH3F4ks52AzdH3F8bnmndnllAzdH3F1jpwtsAzdH3F&gsm=7&rpstart=0&rpnum=0&islist=&querylist=&force=undefined",
filename:"./mv.jpg",
headers:{'User-Agent':'requests'}
/*让服务器伪装成客户端,这样可以破解防爬虫 */
});
自动重启工具(nodemon)
在编写调试Node.js项目,修改代码后,需要频繁的手动close掉,然后再重新启动,非常繁琐。现在,我们可以使用nodemon这个工具,它的作用是监听代码文件的变动,当代码改变之后,自动重启。
例如:
nodemon app.js
package.json 和 package-lock.json
package.json
默认项目初始化json文件,用来描述项目的信息
1.使用npm5之前的版本,是不会生成package-lock json这个文件的。
2. npm5以后,包括npm5这个版本,才会生成package-lockjson文件
3.当使用npm安装包的时候,npm都会生成或者更新package-lock.json文件。
npm5以后的版本,在安装包的时候,不需要加--save (-S) 参数, 也会自动在package.json中保存依赖项(依赖包)。
当安装包的时候,会自动创建或者更新package-lock json文件。
package-lock.json文件内保存了node_ _modules中所有包的信息,包含这些包的名称、版本号、下载地址。
带来好处是,如果重新npm install的时候,就无需逐个分析包的依赖项,因此会大大加快安装速度。
从package-lock.json文件名来看,lock代表的是“锁定”的意思。
它用来锁定当前开发使用的版本号,防止npm instal的时候自动更新到了更新版本。
因为新版本有可能会更新老的api,导致之前的代码出错。
原来的package.json文件只能锁定大版本,也就是版本号的第一位,并不能锁定后面的小版本,你每次npm install
都是拉取的该大版本下的最新的版本,为了稳定性考虑我们几乎是不敢随意升级依赖包的,这将导致多出来很多工
作量,测试/适配等,所以package-lock.jison文件出来了,当你每次安装一个依赖的时候就锁定在你安装的这个版本
express 模块
1、安装前,先清空缓存,否则会报错。 (npm cache clean -f)
2、安装(npm i express)
使用如下,例如:
//导入模块
const express = require('express')
//调用express()方法创建服务器
const app = express()
//设置返回用户看的内容
app.get('/', function (req, res) {
//若用内置模块http创建的服务器,返回内容用res.end() 响应
//此express模块创建的服务器,用res.send() 响应
//使用express模块返回中文时不用给响应头中加中文转义,默认会转义
res.send(`<h1>Hello World~</h1>
<p>你好呀</p>
`)
})
//开启服务器
app.listen(8888,()=>{
console.log("服务器开启成功√")
})
创建静态资源服务器
访问express中文网的静态文件的创建(https://www.expressjs.com.cn/starter/static-files.html)
例如:
//导入模块
const express = require('express')
//调用express()方法创建服务器
const app = express()
//通过如下代码,便可将web目录下的图片、css文件、JavaScript文件对外开放访问
app.use(express.static('web'))
//开启服务器
app.listen(8888,()=>{
console.log("服务器开启成功√")
})
实现简单的get 接口
/**
* 接口:得到一条随机笑话
* 接口地址:/joke
* 请求方式:get
* 参数:无
* 返回:一条笑话
*/
//导包
const express = require('express')
//创建服务器
const app = express()
//写接口
app.get('/joke',(req,res)=>{
//实际开发是从数据库或其他数据源获取
let arr=['世界是你的','程序员会找到女朋友','你的代码永不报错']
let index=Math.floor(Math.random()*3); //0,1,2
//返回笑话
res.send(`<h1>${arr[index]}</h1>`)
})
//开启服务器
app.listen(8888,()=>{
console.log("服务器已开启√")
})
实现带有参数的get接口
/**
* 接口:查询英雄对应的外号
* 接口地址:/name
* 请求方式:get
* 参数:heroName
* 返回:英雄外号
*/
//导包
const express = require('express')
//创建服务器
const app = express()
//写接口
app.get('/name',(req,res)=>{
//要接受前端,传递过来的参数
let hero=''
//req.query返回的是一个对象
//herName便是get请求的参数
switch(req.query.heroName){
case '提莫':
hero='迅捷斥候'
break;
case '李青':
hero="盲僧"
break;
case '阿狸':
hero="九尾妖狐"
break;
case '猴子':
hero="齐天大圣"
break;
default:
hero="该英雄查询不到";
break;
}
res.send(`<h1 style=color:red;>${hero}</h1>`)
})
//开启服务器
app.listen(8888,()=>{
console.log("服务器已开启√")
})
实现返回json数据的接口
/**
* 接口:返回一个食物
* 接口地址:/food
* 请求方式:get
* 请求参数:无
* 返回值:json
*/
//导包
const express = require('express')
//创建服务器
const app = express()
//写接口
app.get('/food',(req,res)=>{
//1、express模块写法:直接写成对象,这样会自动返回json格式
res.send({
foodName:'宫保鸡丁',
price:22,
description:'肉大,菜香,多吃不腻'
})
// //2、原生内置模块写法:使用内置模块setHeader设置
// res.setHeader('Conten-Type','application/json');
// res.send(`
// foodName:'宫保鸡丁',
// price:22,
// description:'肉大,菜香,多吃不腻'
// `)
})
//开启服务器
app.listen(8888,()=>{
console.log("服务器已开启√")
})
实现简单的post 接口
/**
* 接口:返回默认字符串
* 接口地址:/str
* 请求方式:post
* 参数:无
* 返回:这是一个post接口
*/
//导包
const express = require('express')
//创建服务器
const app = express()
//写接口
app.post('/str',(req,res)=>{
res.send(`<h1>这是一个post接口</h1>`)
})
//开启服务器
app.listen(8888,()=>{
console.log("服务器已开启√")
})
实现带有参数的post接口
导入第三方模块(npm i body-parser)
/**
* 接口:用户登录
* 接口地址:/login
* 请求方式:post
* 参数:username password
* 返回:登录成功/登录失败
*/
//需要使用第三方模块body-parser获取post传递的过来的参数
//安装模块(npm i body-parser)
//导包
const express = require('express')
const bodyParser=require('body-parser')
//创建服务器
const app = express()
//parse application/ x-www-form-urlencoded
//将post请求体中的格式转换为urlencoded格式
app.use(bodyParser.urlencoded({extended:false}))
//写接口
app.post('/login',(req,res)=>{
//实际开发根据数据库中的数据进行判断
//req.body 返回的是post对象,username和password是自定义的参数
if(req.body.username=='admin' && req.body.password=='88888888'){
res.send({
code:200,
msg:"登录成功"
})
}else{
res.send({
code:400,
msg:'账户密码不正确'
})
}
})
//开启服务器
app.listen(8888,()=>{
console.log("服务器已开启√")
})
实现post方式传文本参数的接口
导入第三方模块(npm i multer)
/**
* 接口:用户登录
* 接口地址:/register
* 请求方式:post
* 参数:username password usericon(用户头像/图片文件)
* 返回:注册成功/注册失败
*/
//需要使用第三方模块multer接受post传递过来的文本参数
//安装模块(npm i multerr)
//导包
const express = require('express')
const multer=require('multer')
//用包:创建一个uploads文件夹,接受传递来的文本
let upload=multer({dest:'uploads/'})
//创建服务器
const app = express()
//写接口
app.post('/register',upload.single('usericon'),(req,res,next)=>{
// req.file is the usericon file
//传过来的文件参数名用usericon
// req. body will hold the text fields, if there were any
//一起传过来的文件保存在req.body中
//记录了传递过来的文件的一些信息
console.log(req.file)
//记录了传递过来的post对象和其中的参数
console.log(req.body)
})
//开启服务器
app.listen(8888,()=>{
console.log("服务器已开启√")
})
注册路由
后端路由
注册路由:说白了就是写接口(API)
将多个接口放入一个文件中,只开启一次服务器
例如:
//后端路由
//注册路由:说白了就是写接口(API)
//导包
const express = require('express')
//创建服务器
const app = express()
//注册路由
//1、注册接口
app.post('/register',(req,res)=>{
//逻辑...
res.send("注册成功")
})
//2、登录接口
app.post('/login',(req,res)=>{
//逻辑...
res.send("登录成功")
})
//3、获取所有英雄接口
app.get('/getAllHero',(req,res)=>{
//逻辑...
res.send("获取成功")
})
自定义模块
自定义模块(文件名为:'自定义模块.js'),如下:
let db={
name:'jine',
age:'22',
fun(){
console.log("我是一个方法")
},
funny(){
console.log('我也是一个方法~~')
}
}
//导出,提供接口
module.exports=db
-----------------------------------------------------
这样便可以使用我们自定义的模块如下:
//导包
const myMoudle=require('./自定义模块.js')
console.log(myMoudle.name)
console.log(myMoudle.age)
myMoudle.fun()
myMoudle.funny()
其他补充
服务器重定向
服务器主动修改浏览器地址栏
//导包
const express=require("express")
//创建服务器
const app=express()
//如果访问本服务器中找不到输入的这个页面,就会自动重定向跳转到指定页面
app.use((req,res)=>{
//设置302响应头
//结束响应
res.writeHead(302,{
Location:'https://blog.3xnb.com'
});
res.end("ok")
})
//启动服务器
app.listen(8888,()=>{
console.log("服务器已开启√")
})
中间件
服务器开启之后和路由器响应之前执行的一个函数
这个函数可以操作req,res
next() 执行下一个中间件
例如:
//导包
const express=require("express")
const bodyParser=require("body-parser")
//创建服务器
const app=express()
//中间件
//服务器开启之后和路由器响应之前执行的一个函数
//这个函数可以操作req,res
//next() 执行下一个中间件
app.use((req,res,next)=>{
console.log("LOGGED")
next()
})
//API
app.get('/hello',(req,res)=>{
res.end("Hello World~")
})
//启动服务器
app.listen(8888,()=>{
console.log("服务器已开启√")
})
跨域
报错类型:'Access-Contril-Allow-Origin'
-----------------------------------------------------------------------------------------
什么是跨域?
浏览器使用ajax时,如果请求的接口地址和当前打开页面的地址不同源称之为跨域
同源:协议,地址,端口都相同(反之有一个不满足就为不同源)
-----------------------------------------------------------------------------------------
出于安全考虑,浏览器不允许,页面向不同源的接口请求数据,因为如果接口和网页不同源,浏览器默认认为是俩个不同的服务器。
解决跨域
跨域是前端工作中不可避免的问题:我们经常会出现请求不同源接口的情况,为了能够获取数据,解决跨域的问题方案也有很多,但是常用的就两种
■第一种: CORS(后端操作)
目前的主流方案,也是最简单的方案,直接让后端设置响应头,允许资源共享就ok.
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
例如:
app.get('/login',(req,res)=>{
//设置响应头,允许资源被访问/共享
res.setHeader('Access-Control-Allow-Origin','*')
/*表示所有请求路径都可以请求这个接口*/
})
解决跨域问题,设置响应头的代码,可以在中间件中写,这样可以在所有接口中都会应用到(只在中间件写一次就可以了,不用再多个接口中去写,因为中间件是在服务器开启之后和路由响应之前执行的一个函数)
例如:
app.use((req,res,next)=>{
//在中间件中设置响应头,允许资源被所有接口访问/共享
res.setHeader('Access-Control-Allow-Origin','*')
next()
})
-----------------------------------------------------------------------------------------
或者还可以安装第三方模块(npm i cors)
例如:
//导入包
const cors=require('cors')
//使用
app.use(cors());
/*直接使用这个模块,便可省略在中间件中写的解决跨域问题的代码*/
■第二种:JSONP(前后端配合)
曾经的跨域杀手,专治各种跨域问题。现在慢慢的淡出历史舞台
PS:面试官特别喜欢问这个,因为这个有一定的技术难度,也能体现一个人的实际开发经验
jsonp是前后端来配合使用的.
使用原理:通过动态创建script标签通过script标签的src请求没有跨域限制来获取资源
补充知识:
浏览器页面上使用ajax发请求,当前页面地址和ajax请求的地址不同源,才会有跨域限制
但script、img、link标签中的src属性发的请求都没有跨域限制
使用jsonp原理:
这样便可以用script标签中的src来操作,去访问后端地址的接口时,前后端规定好使用的get请求传入的参数名便可,这样也没有了跨域限制
-----------------------------------------------------------------------------------------
前端页面,例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JSONP</title>
</head>
<body>
<script>
function print(obj){
alert(obj.user)
alert(obj.data)
}
</script>
<!-- 前端页面:访问后端API,传入后端指定get参数,后端处理返回后,接受到返回的调用和实参对象来使用 -->
<script src="http://127.0.0.1:8888/getAll?fun=print"></script>
</body>
</html>
-----------------------------------------------------------------------------------------
后端页面,例如:
//导包
const express=require("express")
//创建服务器
const app=express()
app.get('/getAll',(req,res)=>{
//接受到前端传入的get参数,当作函数调用这个参数,并传入实参对象,最后返回前端
res.send(`${req.query.fun}({"user":"我是jine","data":"成功调用√"})`)
})
//启用服务器
app.listen(8888,()=>{
console.log("服务器成功启动√")
})
-----------------------------------------------------------------------------------------
或者还可以直接在Ajax请求时,加上 dataType:'jsonp'
例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>点击哟</title>
<script src="https://jine.oss-cn-beijing.aliyuncs.com/API/jquery-3.5.0.js"></script>
<script>
$(()=>{
let $btn= $('button')
$btn.on("click",()=>{
$.ajax({
url:'http://127.0.0.1:8888/getAll',
dataType:'jsonp',
success:(backData)=>{
alert(backData.user)
alert(backData.data)
}
})
// 如果访问的接口支持jsonp,那么发送ajax便可以使用jsonp
// ajax加 dataType:'jsonp' 便可使用
// 加的原理是:自动创建一个script标签,用他的src属性去请求服务器
//注意:后端中的参数必须是callback,因为使用ajax开启jsonp,src传递的参数是callback,所以要一致
})
})
</script>
</head>
<body>
<button>点击我哟~</button>
</body>
</html>
"注意:后端中的参数必须是callback,因为使用ajax开启jsonp,src传递的参数是callback,所以要一致"
聚合数据(API)
百度搜索聚合数据,进入官方
便可调用别人提供已经写好的API
只要使用的接口支持jsonp,那么可以用ajax去请求
数据库操作
安装第三方模块(npm i mysql)
查询
查询数据库,使用如下,例如:
//导包
const express=require("express")
const mysql=require("mysql")
//创建服务器
const app=express()
//创建一个和数据库的连接
const connction=mysql.createConnection({
host:'localhost', //地址
user:'root', //账户
password:'xxxxxxx', // 密码
database:'gradem' //数据库名
})
//打开连接
connction.connect();
//执行sql语句
//查询操作
connction.query('select * from student',(error,results,fields)=>{
//error 报错对象,若没有错误就返回null
//results sql语句得到的结果集,若有错返回undefined
//
if(error) throw console.error
// console.log(results[3].sname)
//results返回的是一个数组,数组中包含对象
// console.log(fields)
//拿到字段的信息
});
//关闭连接
connction.end()
//开启服务器
app.listen(8888,()=>{
console.log("服务器已成功开启√")
})
增加
//导包
const express=require("express")
const mysql=require("mysql")
//创建服务器
const app=express()
//创建一个和数据库的连接
const connction=mysql.createConnection({
host:'localhost', //地址
user:'root', //账户
password:'xxxxxxxx', // 密码
database:'gradem' //数据库名
})
//打开连接
connction.connect();
// 数据
let data={
sno:'20200606',
name:'库里',
sex:'男',
saddress:'美国',
sdept:'NBA职业联赛系',
speciality:'三分篮球专业'
}
let addSql=`insert into student(sno,sname,ssex,saddress,sdept,speciality) values('${data.sno}','${data.name}','${data.sex}','${data.saddress}','${data.sdept}','${data.speciality}')`
//执行sql语句
//增加数据
connction.query(addSql,(error,results,fields)=>{
if(error==null){
//返回结果是一个对象
console.log(results)
//受影响的行数,如果大于0,说明新增成功
console.log(results.affectedRows)
//插入这条数据的id
console.log(results.insertId)
}else{}
console.log(error);
});
//关闭连接
connction.end()
//开启服务器
app.listen(8888,()=>{
console.log("服务器已成功开启√")
})
更新
//导包
const express=require("express")
const mysql=require("mysql")
//创建服务器
const app=express()
//创建一个和数据库的连接
const connction=mysql.createConnection({
host:'localhost', //地址
user:'root', //账户
password:'xxxx', // 密码
database:'gradem' //数据库名
})
//打开连接
connction.connect();
let updateSql=`update student set sname='小学生' where sno='20200606' `
//执行sql语句
//更新数据
connction.query(updateSql,(error,results,fields)=>{
if(error==null){
//返回结果是一个对象
console.log(results)
//受影响的行数,如果大于0,说明新增成功
console.log(results.affectedRows)
//插入这条数据的id
console.log(results.insertId)
}else{}
console.log(error);
});
//关闭连接
connction.end()
//开启服务器
app.listen(8888,()=>{
console.log("服务器已成功开启√")
})
删除
//导包
const express=require("express")
const mysql=require("mysql")
//创建服务器
const app=express()
//创建一个和数据库的连接
const connction=mysql.createConnection({
host:'localhost', //地址
user:'root', //账户
password:'xxxxxxxx', // 密码
database:'gradem' //数据库名
})
//打开连接
connction.connect();
let delSql=`delete from student where sno='20050202'`
//执行sql语句
//删除数据
connction.query(delSql,(error,results,fields)=>{
if(error==null){
//返回结果是一个对象
console.log(results)
//受影响的行数,如果大于0,说明新增成功
console.log(results.affectedRows)
//插入这条数据的id
console.log(results.insertId)
}else{}
console.log(error);
});
//关闭连接
connction.end()
//开启服务器
app.listen(8888,()=>{
console.log("服务器已成功开启√")
})
第三方库(数据库简易操作)
const hm=require('mysql-ithm')
//连接数据库
//没有自动创建
hm.connect({
host:"localhost",
port:'3306',
user:'root',
password:'xxx',
database:'game'
})
//创建Model(表格模型,增删改查)
//如果table表格存在则连接,不存在则自动创建
//最好先创建好数据库和表,因为这个hm模块创建出来的表,再存入中文时会报错
//自己创建的数据库和表可以先设置为utf-8
let heroModel=hm.model('hero',{
name:String,
skill:String,
icon:String,
status:String
})
npm发布包
一、发布一个新包
1、进入要发布的项目根目录,初始化为npm包:
npm init
依次按提示填入包名、版本、描述、github地址、关键字、license等
这步完成之后会生成一个package.json文件,上面输入的这些信息可以在该文件中修改
注意:如果你的包引用了第三方包,则需要在package.json文件种增加dependencies节点,写入依赖的包及版本
"dependencies": {
"mysql": "^2.18.1"
/*这个mysql便是依赖包的版本*/
}
2、注册npm用户,有两种方法
方法一、npm官网注册:npm
方法二、使用npm 命令注册:npm adduser()
注意:如果用户名被别人注册过,那么回报如下错误:
Unable to authenticate, need:Basic
注意:用第二种方法注册的用户登录后,发布包时候会报如下错,只能使用方法一,去官网注册
'mypackage1' is not in the npm registry.
3、账号登录
npm login
依次输入第二步中第一种方法注册的用户名、密码和邮箱
4、发布包,上传到npm包服务器
npm publish
注意:如果报错:‘You do not have permission to publish “mypackage1”. Are you logged in as the correct user?’
表示包’mypackage1‘已经在包管理器已经存在被别人用了,需要更该包名称
’+’符合表示发布成功了
可以去自己的npm主页上验证以下,可以看到包mypackage_tao已经在列表中了
注意:如果发布时报错:‘no_perms Private mode enable, only admin can publish this module:’
表示当前不是原始镜像,可能用的是其他镜像,如淘宝镜像,
要切换回原始的npm镜像,命令:
npm config set registry https://registry.npmjs.org/
如果用了nrm工具,使用命令:nrm use npm 切换
淘宝镜像
npm config set registry https://registry.npm.taobao.org
// 配置后可通过下面方式来验证是否成功
npm config get registry
// 或
npm info express
二、更新一个已经发布的包
1、修改包的版本
将package.json文件中的version值修改。
2、重新发布包
npm publish
三、删除包
1、删除指定的版本
npm unpublish 包名@版本号
2、删除整个包
npm unpublish 包名 --force
会有警告提示
抓包入库
爬取王者荣耀英雄
//导入模块
const hm=require('mysql-ithm')
const Crawler = require("crawler");
//1、抓包
var c = new Crawler({
maxConnections : 10,
// This will be called for each crawled page
callback : function (error, res, done) {
if(error){
console.log(error);
}else{
var $ = res.$;
// $ is Cheerio by default
//a lean implementation of core jQuery designed specifically for the server
// console.log(JSON.parse(res.body))
/*王者荣耀所有的英雄,返回的是包含了很多对象的数组 */
//所有的英雄,都要去获取他对应的头像和技能
//所以要遍历出每一个英雄的ename,拼接一个路径重新发请求
JSON.parse(res.body).forEach((item)=>{
//console.log(`https;//pvp.qq.com/web201605/herodetaul/${item.ename}.shtml`)
//详情技能请求
xq.queue(`https://pvp.qq.com/web201605/herodetail/${item.ename}.shtml`)
})
}
done();
}
});
//存取英雄信息
let heros=[]
//创建一个英雄技能详情的信息的抓包
var xq = new Crawler({
maxConnections : 10,
// This will be called for each crawled page
callback : function (error, res, done) {
if(error){
console.log(error);
}else{
var $ = res.$;
// //英雄名字
// console.log($('.cover-name').text())
// // //英雄技能
// console.log($('.skill-name>b').first().text())
// // //英雄头像
// console.log('https:'+$('.ico-play').prev('img').attr('src'))
//增加英雄名字、英雄技能、英雄头像
heros.push({
name:$('.cover-name').text(),
skill:$('.skill-name>b').first().text(),
icon:'https:'+$('.ico-play').prev('img').attr('src'),
status:false
})
}
done();
}
});
//王者荣耀,页面人物信息数据抓取
// Queue just one URL, with default callback
c.queue('https://pvp.qq.com/web201605/js/herolist.json');
//要等待所有异步请求数据全部做完后,才开始入库
xq.on('drain',()=>{
// 调用API,插入数据
heroModel.insert(heros,(err,res)=>{
console.log(err)
console.log(res)
if(!err) console.log("成功添加√")
})
})
//2、连接数据库
//没有自动创建
hm.connect({
host:"localhost",
port:'3306',
user:'root',
password:'xxx',
database:'game'
})
//3、创建Model(表格模型,增删改查)
//如果table表格存在则连接,不存在则自动创建
//最好先创建好数据库和表,因为这个hm模块创建出来的表,再存入中文时会报错
//自己创建的数据库和表可以先设置为utf-8
let heroModel=hm.model('hero',{
name:String,
skill:String,
icon:String,
status:String
})
爬取美女图片
//导入模块
const hm=require('mysql-ithm')
const Crawler = require("crawler");
//存取所有美女图片链接
let links=[]
//1、抓包
var c = new Crawler({
maxConnections : 10,
// This will be called for each crawled page
callback : function (error, res, done) {
if(error){
console.log(error);
}else{
var $ = res.$;
// $ is Cheerio by default
//a lean implementation of core jQuery designed specifically for the server
//遍历当前页面所有指定的美女图片
$('.soxflashtext').each((index,item)=>{
let img=$(item).prev('a').children().attr('src')
let name=$(item).prev('a').children().attr('alt')
links.push({
name,
link:img
})
})
}
done();
}
});
// // Queue just one URL, with default callback
c.queue('https://www.tupianzj.com/meinv/mm/toumingqunzi/');
//要等待所有异步请求数据全部做完后,才开始入库
c.on('drain',()=>{
// 调用API,插入数据
mmModel.insert(links,(err,res)=>{
console.log(err)
console.log(res)
if(!err) console.log("成功添加√")
})
})
//2、连接数据库
//没有自动创建
hm.connect({
host:"localhost",
port:'3306',
user:'root',
password:'xxx',
database:'girl'
})
//3、创建Model(表格模型,增删改查)
//如果table表格存在则连接,不存在则自动创建
//最好先创建好数据库和表,因为这个hm模块创建出来的表,再存入中文时会报错
//自己创建的数据库和表可以先设置为utf-8
let mmModel=hm.model('mm',{
name:String,
link:String
})
cookie
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hjub6QC6-1604457549288)(C:\Users\10796\Pictures\image-20200708233538908.png)]
原生cookie设置
//导入模块
const express= require("express")
//创建服务器
const app=express()
//路由
//登录
app.get('/login',(req,res)=>{
let {username,password}=req.query
//设置cookie
res.writeHead(200,{
'Conten-Type':'text/plain;charset=utf-8',
'Set-Cookie':`user=${password}` //实际开发,是密文
})
res.send()
})
//查询
app.get('/list',(req,res)=>{
console.log(req.headers)
res.send('666')
})
//开启服务器
app.listen(8888,()=>{
console.log("服务器已开启√")
})
第三方 cookie-session 模块
//1、导包
const express=require('express')
const cookieSession=require('cookie-session')
//2、创建服务器
const app=express()
//3、设置cookie中间件
app.use(cookieSession({
name: 'session',
keys: ['jine','niec','good'],
// Cookie Options
maxAge: 24 * 60 * 60 * 1000 // 24 hours(cookie过期时间)
}))
//4、注册路由
//登录接口
app.get('/login',(req,res)=>{
let {name,passwd}=req.query
//登录成功,响应回去一个cookie
//发回去的session,是一个userCookie对象
req.session.userCookie ={name,passwd}
res.send("ok")
})
//5、开启服务器
app.listen(8888,()=>{
console.log("服务器已成功开启√")
})
解决cookie跨域问题
"注意:在前端页面中设置访问对应的服务器接口(设置了cookie),而且浏览器中的URL为本地地址(C://xxxxx/login.html)去访问,浏览器会获取不到cookie,且http://localhost:8888/login.html,也是获取不到cookie"
"只能是访问,http://127.0.0.1:8888/login.html 这样cookie才没有跨域问题"
"下面是解决cookie跨域问题,以下内容来自(https://www.cnblogs.com/lijinwen/p/8012547.html)"
前端的ajax后者axios需要设置withCredentials为true。这样请求会携带上cookie
同时后端使用cors中间件。 即可,如下:
//需要安装并且引入中间件cors
const cors = require('cors');
var corsOptions = {
origin: 'http://localhost:8080',
credentials: true,
maxAge: '1728000'
//这一项是为了跨域专门设置的
}
app.use(cors(corsOptions))
//设置跨域
-----------------------------------------------------------------------------------------
如果不用中间件,也可以这样,显示的设置
Access-Control-Allow-Credentials 为true
注意origin不能是*号。 这样浏览器就不会拦截服务器设置的cookie了
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", req.headers.origin); //需要显示设置来源
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
res.header("Access-Control-Allow-Credentials",true); //带cookies7 res.header("Content-Type", "application/json;charset=utf-8");
next();
});
更多推荐
所有评论(0)