资讯详情

前端笔记

一、 ECMAScript 6语法

参考见 https://es6.ruanyifeng.com/

1 ECMAScript和JavaScript的关系

浏览器在1996年之前不支持脚本语言,只支持脚本语言html和css。

NetScape网景公司提出了前端编程语言java以热度命名JavaScript。并且经过与Sun经公司授权,网景公司将JavaScript注册为商标。

1996年,网景公司将脚本提交给标准委员会ECMA。1997年,ECMA将其命名为ECMAScript。因此ECMAScript是国际标准,JavaScript实现标准。

2 ES6与ES5

2011年,ECMAScript5.1.发布。从以后,新标准更新非常频繁,后来统称为ES6。

支持当前主流浏览器ES5,但是对ES6.支持不全面。

Bable是一个ES6转码器,可以ES6代码转为ES在旧浏览器中执行5代码。

Bable配置文件时.babelrc,存储在项目的根目录下。配置文件的基本规则是

{   "presets": [],   "plugins": [] } 

3 let和const

js变量的定义var,var变量增加现象存在,即var声明前可使用变量。

let没有变量增加。建议使用。let和const。const表示常量,let表示变量。

var1 var var1 = 100; //关键词var定义的变量是全局的   // var2            // 因为var2没有定义,所以报错 let var2 = 200; ///在这里使用let定义变量var2   const  name = '张三' // name = '里斯'        //使用const定义的常量不能重写赋值 

4 块级作用域

es五个支持全球作用域和函数作用域,es6.块级作用域增加。

var a = 19; {     var a = 20; } console.log('输出a=', a)    //输出a=20   let  b=10; {     let b=30; } console.log('输出b=', b)    //输出b=10   ///块级作用域的范围仅限于{...}之内 

5 解构赋值

使用符号[]或{}类似于多重赋值。

let a,b = [1,2]     //定义变量a,没有赋值;定义变量b,赋值了 console.log('a=', a, 'b=',b)    //a= undefined b= [ 1, 2 ]  let [c,d] = [1,2]     //解构,使用[] console.log('c=', c, 'd=',d)    //c= 1 d= 2  // 对象的结构,使用{} let {name, age} = {'name':'wuchao', 'age':23} console.log('name=', name, 'age=',age)  let {id, ...other} = {'id':10, 'name':'wuchao', 'age':23, 'address北京市, 'school':'ucas'} console.log('id=', id, 'other=',other)  //id= 10 other= { name: 'wuchao', age: 23, address: 北京市, school: 'ucas' }  // 提取json数据 let jsonData = {id:42, name:'吴超'} let {id, name} = jsonData 

对象的解构赋值可以很容易地将现有对象的方法赋值到某个变量。node中很常用。

// 把Math赋值的三种解构方法 let {log, sin, cos} = Math; // 把console类中的log方法解构赋值; const {log} = console; log('hello')  // 下面是输入模块的指定方法 const {SourceMapConsumer, SourceNode} = require('source-map') 

6 扩展字符串

6.1 es6支持unicode

比如“\u0061”表示”a“。

6.2 遍历字符串

// 遍历字符串 for(let i of 'foo'){   console.log(i) } 

6.2 模板字符串

let name ='张三' , age =23  let s1 = 我的名字叫''' name 年龄是‘,年龄是’ age   // 字符串拼接 console.log('s1=', s1)  let s2 = 我的名字叫${name},年龄是${age}'  // 这里使用单引号,变量无法分析 console.log('s2=', s2)  let s3 = `我的名字叫${name},年龄是${age}`  // 使用浮号可以分析变量 console.log('s3=', s3) 

6.3 标签模板

标签模板相当于函数调用。

alert`hello`  等同于  alert(['hello']) 

6.4 新增方法

let s = 'Hello world!'  s.includes('o') //true s.startsWith('Hello') //true s.endsWith('!') //true  'x'.repeat(3) //'xxx' 'x'.padStart(5, 'ab') //'ababx' 'x'.padEnd(5, 'ab') //'xabab'  '   abc   '.trim() //'abc' 

7 函数的扩展

7.1 参数的默认值

es默认值不能用于之前的函数参数。

function log(x, y='world'){...} 

7.2 结合解构赋值默认值

// 定义 function foo({x, y=5}){...} // 调用 foo({}) //undefined 5 foo({x:1}) //1 5 foo({x:1, y:2}) // 1 2 foo() // TypeError // 定义 function fetch(url, {body='', method='GET', headers={}}){...} // 调用 fetch('http://www.baidu.com', {}) //GET fetch('http://www.baidu.com') //报错 

7.3 rest参数

es6引入rest参数(形式为 …用于获取函数的多余参数的变量名)。rest参数匹配的变量是数组。

function add(...values) add(1,2,3) 

7.4 箭头函数

// 有名函数 function f1(a, b){return a b;}  // 箭头函数 (a, b)=>{     console.log(进入箭头函数, )     return a b } // 简洁写法 (a, b)=>a b  // 常用作匿名函数 [1,2,3].map(x=>x*x)  // 箭头函数体内this对象是定义对象,而不是使用对象 function foo(){   setTimeout(()=>{     console.log(this.id)   }, 100) }  var id=21 foo.call({id:42}) // 42 

8 数组扩展

9 对象的扩展

ES作为对象的属性和方法,允许在大括号中直接写入变量和函数。

9.1 属性简写

const name = "吴超" const baz = {name}  // {name:"吴超"} 

在上述代码中,变量foo直接写在大括号中。这时,属性名就是变量名,属性值就是变量值。

function f1(name, age){     return {name, age} } f1("张三", 23)  // Object {nae:'张三', age:23}

9.2 方法简写

以前的写法是

{
  f1:function(){return "f1"}
}

新的写法是

{
  f1(){return "f1"}
}

9.3 对象的定义

现在,使用属性、方法的简写,就可以非常方便的定义一个对象

var id = 1
const person = {
  id,
  name:'张三',
  say(msg){
     return 'hello '+msg
  }
}

9.4 链判断

如果对象嵌套对象,读取最内层对象的属性msg.body.user.name,如果一个对象不存在,就报错。

使用链判断运算符?.,不存在,则返回undefined,最好指定默认值。

// 对象判断链
msg?.body?.user?.name|| 'default'
// 数组判断链
obj?.[1]
// 函数判断链
a?.b()

10、Symbol

11 Set和Map数据结构

12 Proxy

13 Promise对象

14 迭代器和for…of循环

二、 Node语法

1 引言

1.1 nodejs是js的运行环境

nodejs是一个基于chrome v8引擎的js运行环境。

nodejs是让js运行在服务端。nodejs是服务端的开发框架。 bs/cs

1.2 特点

事件驱动、异步处理、非阻塞、高并发、单进程单线程

“拉”模式

“推”模式

1.3 组成

v8、libuv(event loop)、js库

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dDfSr8Qz-1608279639345)(vuejs学习笔记.assets/u=2398826131,3965435416&fm=26&gp=0.jpg)]

1.4 安装

不建议装到有中文有空格的路径下。

vs code

1.5 基本命令

.exit 退出命令行的方法

2 全局对象

global对象,等价于浏览器中的windows对象,是全局的。

2.1 全局变量

console.log('文件名', __filename)// 文件名 F:\学佳澳\高校实训20-24日\代码\day0106_global全局变量.js
console.log('目录位置', __dirname)  //目录位置 F:\学佳澳\高校实训20-24日\代码

2.2 全局函数

setTimeout(cb,ms)只调用一次函数

t = setInterval(cb, ms) 间隔指定时间,周期性的执行函数。

clearTimeout(t) 停止前面的周期调用

// 只执行一次函数调用,5000表示5秒钟后执行这个函数
setTimeout(()=>console.log('只输出一次', ), 5000)

// 周期性执行某个函数,时间单位是毫秒,1000表示1秒钟
// let t = setInterval(()=>console.log('输出我', ), 1000)

// clearInterval(t)    // 停止前面的周期性调用

2.3 console对象

是用的最频繁的一个node对象

// 日志输出级别, fatal
// console.log(, )
// console.error(, )
// console.info(, )

// 分组显示日志
console.group('aaaa')
console.log('Runoob,这个在分组里面。')
console.groupEnd()

function f2(a,b){return a+b;}
// 显示函数的调用环境
console.trace('函数调用', f2(1,2)) 


// 显示一个对象的所有信息,可以与console.log(...)的输出信息,做比较
console.dir({'name':'zhangsan', 'age':23})

function f1(a,b){return a+b;}
//统计函数的执行时间
console.time('flag1')// 记录一个开始时间
f1(1,2) //函数整个执行过程
console.timeEnd('flag1')// 记录一个结束时间

2.4 process对象

获取操作系统的进程信息,与操作系统交互

console.log('进程号', process.pid)
console.log('进程名', process.title)
console.log('CPU架构', process.arch)
console.log('操作系统', process.platform)
console.log('当前的工作目录', process.cwd())
// process.chdir()    切换工作目录
// process.kill()       杀死进程
// process.abort()      终止进程

3 异步回调

nodejs是异步执行,异步是通过什么体现的哪?通过回调函数。

但是,通常理解,回调不一定是异步

//前面讲的定时器就是异步
setTimeout(()=>console.log('异步执行', ), 1000)

console.log('程序结束', )

讲一下nodejs中的同步和异步操作

let fs = require('fs')
// 同步操作
// const data = fs.readFileSync('day0101_let和const.js')
// console.log('输出结果', data.toString())



// 异步操作
const data = fs.readFile('day0101_let和const.js', (err, data)=>{
    if(err){console.log('出错了', err);return;}
    console.log('输出结果', data.toString())
})
console.log('--------------------------------------', )

4 事件循环

设计上应该是借鉴了操作系统的思路。

事件机制中,由三部分组成:事件、事件发出、事件接收处理

nodejs负责接收事件、处理事件

我们可以发出事件、自定义处理事件的方法、注册事件处理方法

// 引入events事件模块
const events = require('events')
// 创建类EventEmitter的对象
var eventEmitter = new events.EventEmitter()

// 事件机制中,由三部分组成:事件、事件发出、事件接收处理
// nodejs负责接收事件、处理事件
// 我们可以发出事件、自定义处理事件的方法、注册事件处理方法
eventEmitter.on('event1', ()=>{
    console.log('处理事件', )
});

// 触发事件
eventEmitter.emit('event1');

事件机制,可以让行为和操作相分离,也属于解耦的一种方式。

5 模块系统

编程语言自身提供一些开发包/api,更复杂的更多的功能需要模块提供。

一般一个js文件,就可以作为一个模块。模块间可以互相调用。

关键词:exports、module.exports、require

  • require:是node和es6都支持的;
  • export/import:只有es6支持,node不支持
  • exports/module.exports:只有node支持,es6不支持

简单理解:exports = module.exports = {}

下面是模块文件modue01.js

// module.exports = name = '吴超'   // 导出变量

// module.exports = add = (x,y)=>x+y    // 导出函数

class Student{
    constructor(id, name){
        this.id = id;
        this.name = name;
    }
}

// module.exports = stu = new Student(12, '张三')   // 导出对象

下面是调用部分

const aaa = require('./module01')
console.log('输出', aaa)

// console.log('姓名', name)
// console.log('执行函数', add(1,2))
// console.log('学生', stu)

三、 npm

npm(node package manager)是nodejs的 包管理工具,用于node插件的管理(包括安装、卸载、管理依赖等)。

npm是随同nodejs一起安装的管理工具。

java领域有类似的是maven,linux领域有类似的yum、rpm

因为nodejs做的很小巧、高内聚。为了丰富功能,通过package的形式扩展。

使用npm下载模块的时候,自动下载依赖的模块和对应的版本。

1 初始化项目

通过初始化项目,可以创建一个package.json文件。

npm init 项目名称

在package.json里面记录了项目依赖的模块。当把项目给别人时,别人执行npm install即可把依赖项安装,非常方便。

2 安装模块

需要知道模块名称即可。

npm   install   xxx

在国外有一个nodejs的模块仓库,免费的,谁都可以下载。当我们执行install的时候,就去这个仓库寻找特定的模块和指定的版本。下载到本地。

下载到本地项目的node_modules文件夹中,同时注册到package-lock.json文件中。

使用参数-g可以安装到全局。

npm  root  -g	//可以查看全局模块库的位置

npm  install  xxx -g

npm  uninstall   xxx    //卸载模块

3 package.json和package-lock.json

npm5之前,不会生成pacakge-lock.json文件。

package.json文件定的是大版本,不关心小版本。

package-lock.json文件锁定小版本。

4 运行任务

使用npm可以运行package.json中的任务

{
    "scripts":{
        "task1":"node -v"
      }
}

在命令行运行

npm    run    task1

npm start和是npm run start的简写形式。

在一个npm管理项目中,一般默认有start的定义,且会经常使用,所以就在npm执行中简化输入目的设置了npm run start的简写,类似的还有npm stop、npm test等等。而其他的一些不太通用的命令项则只能通过npm run <命令项>的形式执行。

5 使用国内的npm源

使用淘宝镜像

npm  i  cnpm  -g  --registry=https://registry.npm.taobao.org

6 使用npm源的切换工具nrm

npm   i  nrm  -g		// 安装nrm
nrm   ls	//查看有哪些源仓库
nrm   use   taobao	//指定使用哪个源仓库

四、 Node常用模块

1、文件系统fs

api中由同步和异步两个版本,建议使用异步版本。

const fs = require('fs')

1.1 获取文件信息

比如可以判断是文件还是文件夹?还可以知道文件大小、创建者、创建时间、权限等等。

// 访问文件信息,在进行文件夹遍历的时候,需要查看类型和基本信息
fs.stat('module01.js', (err, data)=>{
    console.log('类型', data.isFile()?'文件':'文件夹')
    console.log('文件信息', data)
})

1.2 读文件

读文件的时候,回调函数中,首先要进行错误判断,如果有错误,输出错误原因后返回;

fs.readFile('module01.jsasfd', (err, data)=>{
    if(err){
        console.error('出错了', '文件不存在')
        return
    }
    console.log('文件内容', data.toString())
})

1.3 写文件

// 每次新创建一个文件。如果已经存在,则先删除再创建
fs.writeFile('a.txt', '我的名字叫吴超,年龄23',()=>{})

// 追加
// fs.writeFile('a.txt', '我的名字叫吴超,年龄23',{flag:'a'}, ()=>{})

2、Buffer类

在js中,只有字符串类型,没有二进制类型。

如果要读写图片、音视频文件的时候,就需要使用Buffer类。

学习api的时候,把Buffer想象成String类。

//方法1:创建指定大小的字节空间,指定填充的值,默认是0
let bf1 = Buffer.alloc(10, 1)
console.log('bf1= ', bf1)
// 方法2:根据字节数组创建
let bf2 = Buffer.from([1,2,3,4,5])
console.log('bf2=', bf2)
// 方法3:把字符串转为Buffer
let bf3 = Buffer.from('hello world我的祖国')
console.log('bf3 = ', bf3)

// 输出,指定输出格式
console.log('bf1 = ', bf1.toString('hex'))
// 输出字符串的时候,可以指定编码格式
console.log('bf3 = ', bf3.toString('utf-8'))

3、Stream类

Stream类是个抽象接口,有四种类型:Readable、Writable、Duplex、Transform类型。

所有的Stream对象都是EventEmitter的实例。

3.1 读流

const  fs = require('fs')

const readStream = fs.createReadStream('module01.js')
// 注册事件
readStream.on('data', (chunk)=>{
    console.log('有数据了', chunk.toString())
})
// 注册事件
readStream.on('end', ()=>{
    console.log('读取结束')
})
// 注册事件
readStream.on('error', (error)=>{
    console.error('出错了', error)
})

3.2 写流

const fs = require('fs')

const writeStream = fs.createWriteStream('b.txt')

writeStream.on('finish', ()=>console.log('写完了'))

writeStream.write('hello world我的祖国')
// 调用end方法时,触发finish事件
writeStream.end('完成写入数据');

3.3 压缩解压缩

使用第三方库compressing,支持windows和linux。

先安装库 npm install compressing

const compressing = require('compressing')

// 压缩文件夹
// compressing.zip.compressDir('F:/DRMsoft', "DRMsoft.zip")
// compressing.zip.compressFile()

// 解压缩
compressing.zip.uncompress("DRMsoft.zip", __dirname)

4、案例:复制文件夹

知识点:路径的遍历、判断文件类型、判断文件是否存在、创建文件夹、数据复制

const fs = require('fs')

// 复制工具
var copyTool = function(src, dst, callback){
    if(! fs.existsSync(src)){//src不存在
        console.error('错误:', '文件不存在')
        return;
    }
    fs.exists(dst, (isExist)=>{
        // if(isExist){    //存在目标文件夹
        //     callback(src, dst)
        // }else{  //不存在目标文件夹
        //     fs.mkdir(dst, ()=>callback(src, dst))
        // }
        if(! isExist){
            fs.mkdirSync(dst)
        }
        callback(src, dst)
    })
};

var copy = function(src, dst){
    fs.readdir(src, (err, names)=>{ // 读取文件夹
        // console.log('读取的内容', names)
        names.forEach( name=>{
            // console.log('读取的名称', name)
            let _src = src+'/'+name
            let _dst = dst+'/'+name
            fs.stat(_src, (err, st)=>{
                // console.log('读取的名称', _src, st.isFile()?'文件':'文件夹')
                if(st.isFile()){    //如果碰到是文件,下面完成的时是复制功能
                    // fs.createReadStream(_src).pipe(fs.createWriteStream(_dst))

                    let _readStream = fs.createReadStream(_src)
                    let _writeStream = fs.createWriteStream(_dst)
                    //管道
                    _readStream.pipe(_writeStream)
                }else if(st.isDirectory()){
                    copyTool(_src, _dst, copy)
                }
            })
        } )
    });
}

copyTool('F:/DRMsoft', __dirname+'/aaa', copy)

5、GET请求

网络请求,知道请求路径是什么,请求参数是什么,可以响应结果。

http://localhost:3000/hello/?name=wuchao&age=23

5.1 引入的模块

const http = require('http')
const url = require('url')
const util = require('util')

5.2 解析请求路径和请求参数

http.createServer((req, res)=>{
    let method = req.method //值常见的有GET\POST
    console.log('请求方法', method)

    // 这里的第二个参数必须是true
    let _url = url.parse(req.url, parseQueryString=true)
    console.log('请求路径', _url.pathname)
    console.log('请求参数是', _url.query.name, _url.query.age)
    // res.end(util.inspect(_url))
}).listen(3000, console.log('服务器启动了'))

5.3 返回html页面

const http = require('http')

htmlcode1 = `
<form action="http://localhost:3000/asadfadsf"  method="GET">
<input type="text" name="name" value="zhagnsan">
<input type="submit" value="提交">
</form>
`

http.createServer((req, res)=>{
    // 必须指定响应头,浏览器才能解析html
    res.writeHead(500, {'Content-Type':'text/html; charset=utf8'})
    // 调用write或者end写应答
    res.end(htmlcode1)
}).listen(3000)

6、POST请求

POST请求是Stream,基于事件的。

解析POST请求的数据,使用querystring模块中的parse方法。

const http = require('http')
const querystring = require('querystring')

let form_html = `
<form action="http://localhost:3000/save" method="post">
姓名:<input type="text" name="name" id=""><br>
年龄:<input type="text" name="age" id=""><br>
<button type="submit">
    保存
</button>
</form>
`

http.createServer((req, res)=>{
    let data = '';  //data保存post中的数据
    req.on('data', chunk=>{
        data+=chunk;
        // console.log('到来的数据', chunk.toString())
    })
    req.on('end',()=>{
        // 解析post数据
        let _data = querystring.parse(data)
        console.log('解析post数据', _data)
    })

    res.writeHead(200, {'Content-Type':'text/html; charset=utf8'})
    res.end(form_html)
}).listen(3000, console.log('服务器启动了', ))

7、重定向

为了解决重复提交,使用重定向。

在应答中使用302状态码,指定新的访问地址

res.writeHead(302, {'Location':'http://localhost:3000/'})

下面是完整的代码

const http = require('http')
const querystring = require('querystring')

let form_html = `
<form action="http://localhost:3000/save" method="post">
姓名:<input type="text" name="name" id=""><br>
年龄:<input type="text" name="age" id=""><br>
<button type="submit">
    保存
</button>
</form>
`

http.createServer((req, res)=>{
    if('GET'==req.method){
        res.writeHead(200, {'Content-Type':'text/html; charset=utf8'})
        res.end(form_html)
    }else if('POST'==req.method){
        let data = '';  //data保存post中的数据
        req.on('data', chunk=>{
            data+=chunk;
            // console.log('到来的数据', chunk.toString())
        })
        req.on('end',()=>{
            // 解析post数据
            let _data = querystring.parse(data)
            console.log('解析post数据', _data)
            res.writeHead(302, {'Location':'http://localhost:3000/'})
            res.end()
            return
        })
    }

}).listen(3000, console.log('服务器启动了', ))

8、读写json文件

// 把string转为json形式,把json形式转为string

//把json形式转为string
// console.log('输出字符串', typeof JSON.stringify({'name':'wuchao', 'age':23}))

// 把string转为json形式
// console.log('输出json格式', typeof JSON.parse('{"name":"wuchao","age":23}'))

const fs = require('fs')
// 读json文件
fs.readFile('data.json', (err, data)=>{
    let jsondata = JSON.parse(data.toString())
    console.log('输出文件中的内容', jsondata,  jsondata.name, jsondata.age)
})

9、案例:学生信息管理系统

用http、fs、url、querystring等模块,做一个学生信息管理系统,

【1】用户通过get访问首页,服务器从json文件中加载数据,返回学生列表;

【2】用户在页面中点击“添加“, 发送get请求,服务器返回需要填写信息的表单;

【3】用户填写表单,点击提交,发送post请求到服务器,服务器解析数据保存到磁盘文件;重定向到学生列表;

const fs = require('fs')
const http = require('http')
const url = require('url')
const querystring = require('querystring')



table_html = `
<table border="1px solid red" style="width:500px">
<thead>
    <tr><th>姓名</th><th>年龄</th></tr>
</thead>
<tbody>
    <tr><td>aaaa</td><td>aaaa</td></tr>
    <tr><td>aaaa</td><td>aaaa</td></tr>
</tbody>
</table>
<a href="http://localhost:3000/toadd">添加</a>
`

form_html = `
<form action="http://localhost:3000/save" method="post">
姓名:<input type="text" name="name" id=""><br>
年龄:<input type="text" name="age" id=""><br>
<button type="submit">
    保存
</button>
</form>
`

function readJSON(){
    let data = fs.readFileSync(__dirname+'/data.json').toString()
    data = JSON.parse(data)
    console.log('读取到的JSON数据', data)
    return data
}

function fillTable(jsondata){
    head = `<table border="1px solid red" style="width:500px">
    <thead>
        <tr><th>姓名</th><th>年龄</th></tr>
    </thead>
    <tbody>`
    
    jsondata.forEach((stu, index)=>{
        //let row = '<tr><td>'+stu.name+'</td><td>'+stu.age+'</td></tr>'
        let row = `<tr><td>${stu.name}</td><td>${stu.age}</td></tr>`
        //console.log('第'+index+'行', row)
        head += row
    })
    
    tail = `</tbody>
    </table>
    <a href="http://localhost:3000/toadd">添加</a>`
    return head+tail
}

function writeJSON(stu){
    let data = readJSON()
    data.push(stu)
    fs.writeFileSync(__dirname+'/data.json', JSON.stringify(data))
}

function parsePOST(req, writeJSON){
    let data = ''
    req.on('data', (chunk)=>{data+=chunk})
    req.on('end', ()=>{
        data = querystring.parse(data)
        // console.log('post参数', data.name, data.age)
        writeJSON(data)
    })
}


function route(req, res){
    let method = req.method
    let pathname = url.parse(req.url).pathname
    
    switch(pathname){
        case '/':
            table = fillTable(readJSON())
            res.end(table)
            break;
        case '/toadd':
            res.end(form_html)
            break;
        case '/save':
            parsePOST(req, writeJSON)
            res.writeHead(302, {'Location':'http://localhost:3000/'})
            res.end()
            break;
        default:
            res.end('错误的请求路径 '+pathname)
    }
}

function init_app(){
    if(! fs.existsSync(__dirname+'/data.json')){
        fs.writeFileSync(__dirname+'/data.json', '[]')
    }
}
init_app()

http.createServer((req, res)=>{
    res.writeHead(200, {'Content-Type':'text/html; charset=utf8'})
    route(req, res)
}).listen(3000, console.log('启动服务器,监听3000端口.....'))

五、 express框架

express是基于nodejs的web框架。

1 极简入门

可以在修改代码的时候,重启应用nodemon xxx.js

//加载模块
const express = require('express')
// 创建web服务器
const app = express()
// 启动服务器
app.listen(3000, console.log('服务器启动了,监听3000端口' ))

2 返回各种类型的应答

// 响应get请求
app.get('/', (req, res)=>{
    // 输出文本
    // res.send('hello express')

    // 输出json
    // res.send({'name':'wuchao', 'age':23})

    // 输出html文件,使用绝对路径
    // res.sendFile(__dirname+'/index.html')  

    // 渲染模板
    // res.render(...)
})

3 解析GET请求

对于普通的get请求,如http://localhost:3000/?name=wuchao&age=23,使用req.query 获得get请求信息。

对于url路径中含有数值的,即有名路径,使用req.params获得

// 普通路径
app.get('/', (req, res)=>{
   res.send(res.query.name)
})
// 有名路径
app.get('/user/:id', (req, res)=>{
    res.send(req.params.id)
})

4 解析POST请求参数

引入第三方模块body-parser,然后使用req.body解析参数。

// 引入模块
const bodyparser = require('body-parser')
// 使用中间件,处理application/x-www-form-urlencoded
app.use(bodyparser.urlencoded({extended:false}))

app.get('/', (req, res)=>{
    // 获取GET请求参数
    console.log('GET请求参数', req.query)
    res.sendFile(__dirname+"/pages/index.html")
})

app.post('/save', (req, res)=>{
    // 获取POST请求参数
    console.log('POST请求参数', req.body)
})

5 解析json参数

app.use(express.json())

6 路由

当请求路径特别多的时候,我们需要对每一个请求单独做出响应,这时候就会产生大量的代码,堆积在一起。使用路由,可以让请求的路径结构更加清晰,同时也可以分模块处理。

思路:对url进行分级,分为多个大的模块,每个模块下面分为好多请求

/stu/list /stu/add /stu/save /stu/delete

定义一个路由模块stu.js

const express = require('express')

// 定义了一个一级路由
const stu = express.Router()
// 定义二级路由
stu.get('/list', (req, res)=>{
    console.log('/stu/list', )
    res.send('/stu/list')
})
stu.get('/add', (req, res)=>{res.send('/stu/add')})

module.exports=stu

在服务器模块中,使用路由模块

const express = require('express')
const app = express()
const stu = require('./stu')

// 告诉服务器,使用一级路径/stu
app.use('/stu', stu)

app.listen(3000, console.log('the server is running....', ))

7 模板引擎art-template

常用的操作:输出、判断、循环

art-template模板既可以用在前端js,也可以用在后端js。

7.1 使用模板

安装模板

npm install  art-template   express-art-template

加载中间件

// 使用中间件,使用express-art-template模板
// 模板文件后缀是html
app.engine('html', require('express-art-template'))

// 模板文件默认存放在views目录下,也可以修改目录
app.set('views', 目录路径)

// 如果输出页面时使用res.render(__dirname+'...') 这种绝对路径,那么关于views的设置无效

输出模板文件的时候,必须使用res.render方法,否则不会渲染模板文件中的语法。

res.render(__dirname + "/pages/index.html", {'stulist': stulist});

7.2 模板语法

if是用于判断的。

{
    
       {if msg}}
	{
    
       {msg}}
{
    
       {/if}}

each是循环数组stu,stu里面的每一个元素是一个json,在循环体内使用$value表示每一个循环遍历,使用 ¥ index表示循环序号。

{
    
       {each stu}}
  <tr><td>{
    
       {$value.name}}</td><td>{
    
       {$value.age}}</td></tr>
{
    
       {/each}}

7.3 模板继承

模板继承允许构建 一个模板的骨架,然后向里面填充组成部分。

基本语法如下,在骨架模板中适宜{ {block}}作为占位符,在继承模板中使用{ {block}}定义具体的内容。

{
    
       {extend './layout.art'}}
{
    
       {block 'head'}} ... {
    
       {/block}}

下面是骨架模板

<!--layout.art-->
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>{
    
       {block 'title'}}My Site{
    
       {/block}}</title>

    {
    
       {block 'head'}}
    <link rel="stylesheet" href="main.css">
    {
    
       {/block}}
</head>
<body>
    {
    
       {block 'content'}}{
    
       {/block}}
</body>
</html>

下面是继承后的模板

<!--index.art-->
{
    
       {extend './layout.art'}}

{
    
       {block 'title'}}{
    
       {title}}{
    
       {/block}}

{
    
       {block 'head'}}
    <link rel="stylesheet" href="custom.css">
{
    
       {/block}}

{
    
       {block 'content'}}
<p>This is just an awesome page.</p>
{
    
       {/block}}

7.4 包含模板

把某一个公共部分,加入到当前页面中来。

{
    
       {include './header.art'}}
{
    
       {include './header.art' data}}

7.5 过滤器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UR6Mfeq5-1608279639349)(vuejs学习笔记.assets/1281517-20180205155942998-197158086.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G5SpHacp-1608279639352)(…/…/…/_学习笔记/前端笔记/nodejs/1281517-20180205160104326-1983829336.png)]

8 会话管理

使用第三方模块express-session来管理会话。

session是另一种记录客户状态的机制,与cookie保存在客户端浏览器不同,session保存在服务器当中;

当客户端访问服务器时,服务器会生成一个session对象,对象中保存的是key:value值,同时服务器会将key传回给客户端的cookie当中;

当用户第二次访问服务器时,就会把cookie当中的key传回到服务器中,最后服务器会吧value值返回给客户端。

因此上面的key则是全局唯一的标识,客户端和服务端依靠这个全局唯一的标识来访问会话信息数据。

接下来,安装模块,并使用中间件

// npm install express-session		// 安装模块

// 导入模块
const session = require('express-session')
// 配置中间件
app.use(session({
    secret: "keyboard cat",
    resave: false,
    saveUninitialized: true,
    cookie: ('name', 'value',{maxAge:  5*60*1000,
    secure: false})
}))

接下来就可以使用req.session了

// 赋值
req.session.user = '吴超'
// 取值
req.session.user
// 销毁会话
req.session.destroy((err)=>{res.send('销毁')})

会话的配置项有

  • name - cookie的名字(原属性名为 key)。(默认:’connect.sid’)
  1. store - session 的存储方式,默认存放在内存中,也可以使用 redis,mongodb 等。express 生态中都有相应模块的支持
  2. secret - 通过设置的 secret 字符串,来计算 hash 值并放在 cookie 中,使产生的 signedCookie 防篡改
  3. cookie - session cookie设置 (默认:{ path: ‘/‘, httpOnly: true,secure: false, maxAge: null })
  4. genid - 生成新session ID的函数 (默认使用uid2库)
  5. rolling - 在每次请求时强行设置cookie,这将重置cookie过期时间(默认:false)
  6. resave - 强制保存session即使它并没有变化 (默认: true, 建议设为:false)
  7. proxy - 当设置了secure cookies(通过”x-forwarded-proto” header )时信任反向代理。当设定为true时, ”x-forwarded-proto” header 将被使用。当设定为false时,所有headers将被忽略。当该属性没有被设定时,将使用Express的trust proxy。
  8. saveUninitialized - 强制将未初始化的session存储。当新建了一个session且未设定属性或值时,它就处于未初始化状态。在设定一个cookie前,这对于登陆验证,减轻服务端存储压力,权限控制是有帮助的。(默认:true)
  9. unset - 控制req.session是否取消(例如通过 delete,或者将它的值设置为null)。这可以使session保持存储状态但忽略修改或删除的请求(默认:keep)

9 中间件完成权限控制

使用中间件完成权限控制。

app.use((req, res, next)=>{
    console.log(req.url, )
    if(没有权限){
        res.redirect('/')	//重定向
        return;
    }
    next()	// 放行
})

10 数据范围

数据的范围可以是res、session或者app。指定数据范围的目的是为了向页面传值,以及在不同的express方法之间传值。

应用级别使用app.locals,会保存在应用的整个生命周期;

响应级别使用res.locals,会保存在本次请求应答的生命周期中;

app.locals
res.locals.session = req.session

11 上传

使用第三方模块multer

首先看一下页面的结构

<form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="content" />
    <input type="submit" value="上传文件" />
</form>

接下来看一下js部分的代码

// 安装模块
// npm install multer
// 导入模块
var multer  = require('multer')
// 创建multer实例,指定存放文件的位置
var upload = multer({dest: 'upload_tmp/'});
// 以下是接收上传信息,upload.any()是个能够接收任何上传的中间件,
app.post('/upload', upload.any(), (req, res)=>{
	//req.files接收所有的文件,这是个数组。里面的文件有个属性path表示上传后的路径,可以复制到其他位置
    console.log('上传文件信息', req.files)
})

接下来是完整的代码

var fs = require('fs');
var express = require('express');
var multer  = require('multer');

var router = express.Router();
// 指定存放文件的位置
var upload = multer({dest: 'upload_tmp/'});

router.post('/', upload.any(), function(req, res, next) {
    console.log(req.files[0]);  // 上传的文件信息

    var des_file = "./upload/" + req.files[0].originalname;
    fs.readFile( req.files[0].path, function (err, data) {
        fs.writeFile(des_file, data, function (err) {
            if( err ){
                console.log( err );
            }else{
                response = {
                    message:'File uploaded successfully',
                    filename:req.files[0].originalname
                };
                console.log( response );
                res.end( JSON.stringify( response ) );
            }
        });
    });
});

module.exports = router;

multer的配置有:

  • dest或storage 存储文件的位置
  • fileFilter 文件过滤器
  • limits 限制上传的数据
  • preservePath 保存包含文件名的完整文件路径

12 文件下载

下载文件只需要使用==res.download(文件路径,下载文件名,回调函数)==即可

app.get('/download',function (req,res) {
    res.download(__dirname+'/route.js','route.js',
    function (err) {
        if(err){
          console.log(err)
       }
    })
})

14 验证码

使用第三方模块svg-captcha

// npm install svg-captcha   // 安装模块
// 导入模块
const svgCaptcha = require('svg-captcha')

router.get('/static/captcha', (req, res)=>{
    let captch = svgCaptcha.create({
        color: true,    // 彩色
        width:100,      // 宽度
        height:60,      // 高度
        fontSize:48,    // 字体大小
        size:4,         // 验证码个数,这里的captch.text是4个字母
        noise:3,        // 干扰线条
        ignoreChars:'0o1ilI'    // 不包括的字符
    });
    // 保存到session中,登录时取出校验
    req.session.captcha = captch.text.toLocaleLowerCase();
    // 防止使用模板:指定输出类型
    res.type('html')
    // 这里的captch.data 是一个svg的html内容。指定输出内容
    res.end(captch.data)
});

如果要在页面上输出

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
<script>
	var captcha = function(){
		$.get('http://localhost:3000/captcha', data=>{
			$('#captchaImg').html(data)
		})
	}
	$(function(){
		captcha()
	   $('#captchaImg').click(()=>{
		captcha()
	   })
	});
</script>

// 假设页面有下面这个span
    <form action="/login" method="post">
        用户名:<input type="text" name="username" id="username" value="张三">
        <br/>
        密码:<input type="password" name="password" id="password" value="admin">
        <br/>
        验证码:<input type="text" name="captchaCode" id="captchaCode"><span id="captchaImg"></span>
        <br>
        <input type="submit" value="登录">
    </form>

15 响应静态文件

所谓的静态文件,指的是css、js、各种图片。

静态文件通常放在一个统一的文件夹中,比如有如下的目录结构

/static/js/index.js

那么,我们使用 app.use(express.static(‘static’)) 表示static目录。相当于隐藏了指定的目录。

这样,在地址栏就可以使用http://localhost:3000/js/index.js 就可以访问到该文件。在页面中引用该js的话,也应该使用 “/js/index.js” 的形式。

还可以使用app.use(‘/public’, express.static(‘static’)) 表示static目录。注意前面的public必须使用**/**开始。

在地址栏就需要使用http://localhost:3000/public/js/index.js 就可以访问到该文件。

16 跨域请求

安装模块cors,增加下面一行即可。

app.use(require('cors')())

17 mysql操作

使用第三方模块mysql

var mysql = require('mysql');
 
var pool = mysql.createPool({
    connectionLimit: 10,
    host: 'localhost',
    port: '3306',
    user: 'root',
    password: '',
    database: 'xkdb'
});

pool.getConnection(function (err, connection) {
	if (err) throw err;

	var sqlStr='SELECT * FROM category';
	connection.query(sqlStr, function (err, rows,fields) {
		if (err) throw err;
		console.log('json', {results: JSON.stringify(rows),fields:JSON.stringify(fields)});

		connection.release();
	});
});

18 模块

分模块操作,可以让逻辑更加清晰。

const express = require(`express`)
const users = express.Router()

users.use((req, res, next) => {
  console.log(`路由执行成功啦~~~`, Date.now());
  next()
})

users.get(`/`, (req, res, next) => {
  res.json({
    status: 200,
    data: `请求成功`
  })
})

module.exports = users

在主文件中

//  使用路由 /user 是路由指向名称
import users from '...'
app.use(`/users`,users)

六、 案例:学生管理系统

1、 功能列表

  • 数据保存在mysql中
  • 首页是学生列表;
  • 没有权限时,只能看到学生列表,看不到修改、删除、添加
  • 没有权限时,都会跳转到首页的学生列表
  • 登录页面,用户名、密码、验证码;如果登录成功,跳转到首页
  • 有权限时,首页显示添加、修改、删除
  • 可以退出

2、 搭架子

  1. 创建一个文件夹stu-mis

  2. 进入这个文件夹,在命令行执行npx express-generator,生成模板项目

  3. 可以删除views文件夹中的所有模板文件。在命令行执行npm install art-template express-art-template,安装art模板引擎。

  4. 在命令行执行npm install,安装模块

  5. 修改app.js的内容的第14行

    app.set('html', require('express-art-template'));
    

    修改app.js的内容的第20行

    app.use('/static', express.static(path.join(__dirname, 'public')));
    

    这样,就可以使用/static访问静态内容了。

  6. 修改index.js文件,

      res.render('index.html');
    

    在views文件夹中创建index.html文件

  7. 在命令行执行nodemon,运行项目。在浏览器访问http://localhost:3000 就能看到首页面。

  8. 在vscode中安装 EJS language support 插件

    至此,架子搭建完毕。

3、显示表格

修改index.js内容

router.get("/", function (req, res, next) {
  stuList = [
    { id: 1, name: "张三", age: 23 },
    { id: 2, name: "李四", age: 24 },
  ];
  res.render("index.html", { title: "Express", stuList: stuList });
});

修改views文件夹中index.html内容

    <table>
        {
    
       {each stuList}}
        <tr>
            <td>{
    
       {$value.id}}</td>
            <td>{
    
       {$value.name}}</td>
            <td>{
    
       {$value.age}}</td>
        </tr>
        {
    
       {/each}}
    </table>

4、访问数据库

创建数据库访问模块db.js

let mysql = require('mysql')

let pool = mysql.createPool({
    host:'localhost',
    port:3306,
    user:'root',
    password:'admin',
    database:'test',
});

module.exports = function(sql, callback){
    pool.getConnection(function(err, conn){
        if(err){
            console.error('数据库连接错误', err)
            throw err;
        }
        conn.query(sql, function(err, result){
            if(err){
                console.error('执行语句错误', err)
                throw err;
            }
            console.log('执行结果',  result)
            callback(result);
            conn.release();
        });
    });
}

修改index.js中的内容

router.get("/", function (req, res, next) {
  function callback(rows){
    res.render("index", { title: "Express", stuList: rows });
  }
  db('select * from stu', callback);
});

这样,以后就可以非常方便的使用数据库了。

5、使用会话权限管理

在app.js中,使用会话中间件,这里的会话中间件,一定要位于前其他app.use(…)的前面。

// npm install express-session		// 安装模块

// 导入模块
const session = require('express-session')
// 配置中间件
app.use(session({
    secret: "keyboard cat",
    resave: false,
    saveUninitialized: true,
    cookie: ('name', 'value',{maxAge:  5*60*1000,
    secure: false})
}));
// 配置session中间件
app.use((req, res, next)=>{
  res.locals.session = req.session
  next()
});

权限管理

// 使用中间件完成权限控制
app.use((req, res, next)=>{
  if(req.url.startsWith('/static')){
    next();
    return;
  }
  if(req.url=='/login'){
      console.log('请求/login', )
      next()
      return;
  }
  if(! req.session.user){
      console.log('重定向/login', )
      res.redirect('/login')
      return;
  }
  console.log('放行', req.url)
  next()  //调用next()表示放行
});

以上的代码,要放在其他app.use()之前。

创建一个login.html文件

    <form action="/login" method="post">
        用户名:<input type="text" name="username" id="username">
        <br/>
        密码:<input type="password" name="password" id="password">
        <br/>
        <input type="submit" value="登录">
    </form>

6、使用post登录

修改app.js文件,在权限控制代码后面增加以下内容:

// 引入模块
const bodyparser = require('body-parser')
// 使用中间件,处理application/x-www-form-urlencoded
app.use(bodyparser.urlencoded({extended:false}))

接下来就可以使用req.body访问post请求的参数了。

如果登录成功,就可以使用

req.session.user = ....

设置会话。

销毁会话,使用

req.session.destroy((err)=>{console.print('销毁')})

如果已经配置了

app.use((req, res, next)=>{
  res.locals.session = req.session
  next()
});

那么,在页面访问会话中的内容,使用

{
    
       {session.user}}

7、退出操作

router.get('/logout', function(req, res){
  req.session.destroy((err)=>{});
  res.redirect('/');
});

8、编辑操作

在页面使用

<a href="/edit?id={
    
       {$value.id}}">修改</a>

在index.js中,增加函数

router.get('/edit', function(req, res){
  let id = req.query.id;
  db(`select * from stu where id=${id}`, rows=>{
    res.render('edit.html', {stu:rows[0]});
  });
});

保存修改的函数

router.post('/edit', function(req, res){
  let id = req.body.id, name = req.body.name, age=req.body.age;
  db(`update stu set name='${name}', age=${age} where id=${id}`, rows=>{
    res.redirect('/')
  });
});

9、添加、删除操作

参考编辑操作,略

10、上传头像

上传文件的表单,使用post提交,要有属性enctype="multipart/form-data"

    <form action="/add" method="post" enctype="multipart/form-data">
        用户名:<input type="text" name="name" id="">
        <br>
        密码:<input type="text" name="pwd" id="">
        <br>
        年龄:<input type="text" name="age" id="">
        <br>
        照片:<input type="file" name="picture" id="">
        <br>
        <input type="submit" value="添加">
    </form>

nodejs中使用multer模块,先安装npm install multer。然后在index.js中增加以下内容

// 导入模块
var multer  = require('multer')
// 创建multer实例,指定存放文件的位置
var upload = multer({dest: 'upload_tmp/'});
// 上传目录
var UPLOAD_DIR = "./upload/";
// 文件夹必须存在,否则报错
if(!fs.existsSync(UPLOAD_DIR)){
  fs.mkdirSync(UPLOAD_DIR);
}

修改一下以前的添加方法

router.post('/add', upload.any(), function(req, res){
  let name = req.body.name, pwd = req.body.pwd, age = req.body.age;

  // 上传的文件都保存在req.files,每个文件都是一个对象。
  // 对象的path是上传后的路径,名称是随机生成的。
  // 同步读取文件
  let data = fs.readFileSync(req.files[0].path);
  // 对象的originalname是原始文件名
  let dst_file =  UPLOAD_DIR + req.files[0].originalname;
  // 同步写入文件
  fs.writeFileSync(dst_file, data);  
  
  let sql = `insert into stu(name,pwd,age,image)values('${name}', '${pwd}', ${age}, '${dst_file}')`
  db(sql, rows=>{
    res.redirect('/')
  });
});

11、下载头像

只需要使用res.download()即可。

router.get('/download', function(req, res){
  let id = req.query.id;
  db(`select * from stu where id=${id}`, (rows)=>{
    res.download(rows[0].image);
  });
});

12、验证码

使用svg-captcha模块,安装npm install svg-captcha

下面是生成验证码的代码。要注意url使用/static开头,防止权限过滤掉。

// 导入模块
const svgCaptcha = require('svg-captcha')
// 页面:获取验证码
router.get('/static/captcha', (req, res)=>{
    let captch = svgCaptcha.create({
        color: true,    // 彩色
        width:100,      // 宽度
        height:60,      // 高度
        fontSize:48,    // 字体大小
        size:4,         // 验证码个数,这里的captch.text是4个字母
        noise:3,        // 干扰线条
        ignoreChars:'0o1ilI'    // 不包括的字符
    });
    // 保存到session中,登录时取出校验
    req.session.captcha = captch.text.toLocaleLowerCase();
    // 防止使用模板:指定输出类型
    res.type('html')
    // 这里的captch.data 是一个svg的html内容。指定输出内容
    res.end(captch.data)
});

下面是页面中使用验证码的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
<script>
	var captcha = function(){
		$.get('/static/captcha', data=>{
			$('#captchaImg').html(data)
		})
	}
	$(function(){
		captcha()
	   $('#captchaImg').click(()=>{
		captcha()
	   })
	});
</script>
</head>
<body>
   
    <form action="/login" method="post">
        用户名:<input type="text" name="username" id="username" value="张三">
        <br/>
        密码:<input type="password" name="password" id="password" value="admin">
        <br/>
        验证码:<input type="text" name="captchaCode" id="captchaCode"><span id="captchaImg"></span>
        <br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

登录时,进行验证

router.post('/login', (req, res)=>{
  let name = req.body.username, pwd = req.body.password, captchaCode = req.body.captchaCode;
  // 判断验证码
  if (req.session.captcha != captchaCode.toLocaleLowerCase()){
    res.render('login.html', {error:'验证码错误'});
    return;
  }
  let sql = `select * from stu where name='${name}' and pwd='${pwd}'`
  
  db(sql, rows=>{
    if(rows){
      req.session.user = rows[0]
      res.redirect('/')
    }else{
      res.render('login.html')
    }
  });
});

七、Mongoose操作

使用npm install mongoose -S,按照mongoose

7.1 创建数据库连接

const mongoose = require('mongoose')

mongoose.connect('mongodb://localhost:27017/test44', {
    useNewUrlParser:true,
    useCreateIndex:true,
    useFindAndModify:true    
}).then(()=>console.log('数据库链接正常'))
.catch(err=>console.error(`数据库链接出错 `, err))

该文件可以在main.js中引入,与模型无关。这样,一次性加载数据库连接。

7.2 创建模型

下面的模型中,使用了ObjectId类型,默认是null。

const mongoose = require("mongoose");

const Funcpoint = mongoose.model(
  "Funcpoint",
  new mongoose.Schema({
    pid: {
      type: mongoose.ObjectId,
      ref: "Funcpoint",
      default: null,
    },
    label: {
      type: String,
      required: true,
    }
   })
 )

模型有很多参数,如下

const User = mongoose.model(
  "User",
  new mongoose.Schema({
    username: { 
      type: String, 
      required: true, 
      unique: true  // 唯一性
    },
    pas

标签: 7b0bs41变送器hy104压力变送器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台