# practice_third
**Repository Path**: KUIYA/practice_third
## Basic Information
- **Project Name**: practice_third
- **Description**: 三阶段项目练习
- **Primary Language**: NodeJS
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-08-03
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## 三阶段各知识点练习
### 20200803_NodeJS回顾
* 依赖内置模块搭建基本的静态资源服务器
+ http
+ fs
+ path
+ url
* 模块化开发
+ 暴露
- module.exports
- exports.module_name
+ 引入
- require(js_file/module)
* requier文件查找策略
+ package.json->main
+ index.js
* 依赖第三方模块Express搭建基本的静态资源服务器
+ 基本配置
+ 中间件概念:在某些操作到达目的前进行拦截处理,在express中本质上是一个函数
- 内置中间件
- 自定义中间件
- 第三方中间件
### 20200804_路由编写及模块化
* 路由参数获取
+ 路径拼接参数:req.query
+ 请求头包含参数:req.body
- 依赖express的中间件`express.urlencoded()`,`express.json()`
- 依赖body-parser的`bodyParser.urlencoded({extended:false})`
+ 动态路由:req.params
* RESTFULApi规范
+ `get`:查
+ `post`:增
+ `put`:覆盖改
+ `patch`:局部改
+ `delete`:删
* 路由模块化编写
+ 中间件:`express.Router()`
### 20200805_nodeJS下的jsonp、服务器代理 || 页面渲染模式 || 数据爬取和数据流
#### 1.跨域
* jsonp的原理和局限
+ `http-server`:自动部署静态资源服务器
* 服务器代理的原理和局限
+ `http-proxy-middleware`:服务器代理中间件
#### 2.页面渲染模式
* 客户端渲染和服务端渲染的过程、优势劣势
+ 过程:页面渲染步骤多寡
+ 优劣势:从前后端的耦合度和SEO分析
#### 3.数据爬取、数据流
* 步骤
+ 分析html结构
+ 工具/模块使用
- `request`:发送请求并获取目标结构
- `cheerio`:过滤并获取数据,类似于JQ
+ 下载图片
+ 写入数据库
```js
// 1.获取文件名
const filename = path.basename(internet_url);
// 2.创建写入流
const fileStream = fs.createWriteStream(Local_path);
// 3.请求网络资源并以流的形式写入本地文件夹
request(internet_url).pipe(fileStream)
```
### 20200806_写入流与读取流、nodeJS下的MySQL数据库操作
#### 1.数据流Stream
* 读取流:`fs.createReadStram()`
+ data事件
+ end事件
* 写入流:`fs.createWriteStream()`
+ finish事件
+ end方法
#### 2.ndoeJS下的MySQL数据库操作
* `mysql`依赖的安装与使用步骤
+ 引入依赖
+ 配置数据库
1. 连接对象方式:`mysql.createConnection()`
2. 连接池方式(推荐写法):`mysql.createPool()`
+ 请求方式 --> sql语句
- 增:post --> insert
- 删:delete --> delete
- 查:get --> select
- 改:put/patch --> update
+ 执行sql语句
1. 连接对象:`connection.query(sql,(err,result,fields){})`
2. 连接池:`pool.query(sql,(err,result,fields){})`
* mysql数据库操作封装
+ 回调
```js
// ./utils/mysql.js
function query(sql,cb){
pool.query(sql,(err,result,fields)=>{
if(err) throw err;
cb(result)
})
}
// ./touter/user.js
router.get('/',(req,res)=>{
query(sql,function(result)=>{
res.send(result)
})
})
```
+ ES8的async/await
```js
// ./utils/mysql.js
function query(sql){
return new Promise((reslove,reject)=>{
if(err){
reject(err)
}
pool.query(sql,(err,result,fields)=>{
resolve(result)
})
})
}
// ./touter/user.js
router.get('/', async(req,res)=>{
let result = '';
try{
result = await query(sql);
}catch(err){
result = err;
}
res.send(result);
})
```
### 20200807_MongoDB数据库、加密、前后端数据交互规范化
#### 1.MongoDB数据库
* 命令行操作MongoDB数据库
+ 数据库db
+ 集合collection
+ 文档document
* nodeJS操作MongoDB数据库
+ 数据库信息配置封装
+ 增删查改封装
#### 2.加密
* 方式
+ 单向加密
+ 对称加密
+ 非对称加密
* 三种加密方式的原理、优缺点以及优化
* 使用:crypto内置模块
```js
/* crypto模块使用:单向加密 */
// 引入
const crypto = require('crypto');
// 路由中
const hashPw = crypto.createHash('md5');
hashPw.update(password);
password = hashPw.digest('hex');
// 优化:加盐(所加的随机字符串被称为盐值)
hashPw.update(password+'jiayan');
```
#### 3.前后端数据交互格式化
```js
// utils/tool.js
// 默认设置
function formatData({code=1,data=[],msg='success'}={}){
// 方便只需要传递一个code参数即可,不需要再设置msg
if(code===0){
msg='fail'
}
return {
code,
data,
msg
}
}
module.exports = formatData;
// reg.js
res.send(formatData()); // 注意需要传递一个空对象作为参数,当然也可在封装那边进行默认值二次设置
```
### 20200810_MongoDB数据库之登录操作、session验证码、token令牌
#### 1、session与验证码
* `验证码`
+ 依赖:`svg-captcha`
+ 过程:后端生成的验证码信息存储在后端的session里,到前端需要验证时,后端提取出验证码跟前端的进行校验
* `session`
+ 依赖:`express-session`
+ 原理
- 第一次请求:服务端生成一个`特殊cookie(connect_sid)`并写入到客户端浏览器里
- 第二次请求:客户端发送请求时携带cookie到后端进行校验
#### 2、token与免登陆
* 依赖:`jsonwebtoken`
* 过程
+ 在后端生成的`有时效性的令牌`返回给前端,并设置存储在`localStorage`
+ 需要验证时,前端`首页`发送请求到后端
+ 若`过期或者被篡改`,则`删除令牌`并`重返登录页`
+ 若仍然有效,刷新页面仍然`停留在后台首页`
+ 在登录页发送请求验证令牌,若有效则`跳转到首页`
### 20200811_后台管理系统之删除用户功能、修改用户信息、基于JQ的Ajax请求二次封装
#### 1.删除用户功能
* 前端传递用户ID,后端根据ID进行删除数据库操作并返回结果给前端,前端根据code码进行重新静态渲染
* 端口提取
#### 2.基于JQ的Ajax请求二次封装
* 将JQ的AJAX请求根据请求类型进行划分
+ `GET、DELETE`:遍历参数拼接路径
+ `POST、PUT、PATCH`:把options参数扩展运算
```js
function request(url,data,options){
// get、delete请求遍历参数拼接到路径
if(['get','delete'].includes(options.method) || options.method === undefined){
}
// post、put、patch把data作为options属性
else if(['post','put','patch'].includes(options.method)){
options.data = data
}
// 同步写法编写异步代码
let result = await $.ajax({
url,
// 扩展运算符扩展后data属性作为ajax方法的属性
...options
}).then(data=>{
return data
});
return result
}
// 封装函数设置类型方法
request.get = function(url,data,options={}){
options.type = 'get';
return request(url,data,options)
}
...
```
#### 3.修改用户信息
* 获取表单数据发送请求后端
* 编写后端修改用户信息接口,并返回前端用户修改信息
* 前端根据返回的用户数据重新静态渲染
+ 非当前用户正常渲染
+ 当前用户重新渲染并更新localStorage
### 20200812_图片上传、webSocket之多人聊天室和socket心跳包
#### 1.图片上传
* 依赖:`npm i multer`
* 中间件参数配置
```js
// router/upload.js
const multer = require('multer')
const path = require('path');
let storage = multer.diskStorage({
destination:path.join(__dirname,'../dist/uploads/');
filename: function (req, file, cb) {
let ext = path.extname(file.originalname);
cb(null, file.fieldname + '-' + Date.now() + ext);
}
})
let uploadMiddleware = multer({storage});
/* 注意单多张图片上传的限制需要在html的input输入框设置multiple属性 */
// 单张
router.post('/avatar',uploadMiddleware.single('avatar'),(req,res)=>{
/* 代码块 */
})
router.post('/goods',uploadMiddleware.array('goods',5),(req,res)=>{
/* 代码块 */
})
```
* 前端图片信息发送
```js
var data = new FormData();
data.set('id',$('#editModal .updateTrue').attr('data-id'));
data.set('avatar',e.target.files[0]);
let result = await request.post('/upload/avatar',data,{
// 不设置content-Type请求头
contentType:false,
// 不处理发送的数据
processData:false,
});
```
#### 2.webSocket协议
* 概念:区别与http协议的`长连接`、`双端都会互相主动发送请求`的一种`tcp协议`
* 依赖:后端第三方模块`ws`和前端H5新特性`WebSocket`
* 多人聊天室本质:基本上都是客户端负责数据操作,服务端只负责数据转发
* 方法
+ 客户端
- 事件
- `socket.onopen`:监听连接服务端事件,成功后触发open事件
- `socket.onmessage`:消息接收监听事件
- 方法
- `socket.send()`:发送信息给服务端
- `socket.close()`:断开连接
+ 服务端
- 事件
- `wss.on('connection',client=>{})`:监听与客户端的连接事件,返回客户端对象
- `client.on(message,msg=>{})`:监听客户端发送消息事件
- 方法
- `send()`:发送消息给客户端
#### 3.socket心跳包
* 应用场景:当与服务器的连接`非人为断开`时,实际上与服务器还保持着连接状态,即`资源占用`,若不及时清除,就会导致服务器资源占用过多,后续客户端无法与服务器建立连接,这时候就需要`不断给服务器发送socket心跳包`
* 本质:客户端与服务端之间在`间隔时间内`来回发送消息来确保是否还保持着连接
* 分类
+ 由客户端发起心跳包(推荐)
> 服务端只需要被动接收,只需要判断在指定时间间隔内是否还能接收到消息,若接收不到,服务端就断开连接,释放资源
+ 由服务端发起心跳包
> 服务端发送需要开启若干个定时器,会消耗服务器性能,所以不推荐
### 20200813_Vue初学习、响应式属性、tab标签切换案例和todolist案例
#### 1.常见软件架构、执行顺序、特点
* `MVC`:V -> C -> M -> V
+ Model:数据层
+ View:视图层
+ Controller:控制层
> 相互依赖、高耦合度、还要考虑网络波动等因素
* `MPV()`:VP、MP双向双通道
+ Precenter:松散控制器
> VM之间的交互只能通过P连接、P|V层的代码编写可能会过于复杂,不利于后期维护
* `MVVM`:V|VM双向单通道,M|VM双向双通道
+ ViewModel:松散控制器,V与M的综合体
> VM层自动化大量操作,只需关注M层即可
#### 2.数据驱动
* 一种`只关注数据修改`来实现视图更新,而`无需关注DOM操作`的思维,与以往大量频繁的DOM节点操作有所区别
#### 3.响应式属性
* 概念:能`被监听到数据改动`并`自动更新视图`的属性被称之为响应式属性
* 原理:把普通属性或者值属性转变为`存储器属性`
* 设置:`Object.defineProperty(targetObj,prop,descriptorObj)`
+ `targetObj`:目标对象
+ `prop`:目标对象的属性
+ `descriptorObj`:属性特性
> 属性的设置有`传统的点语法`和`Object.defineProperty`,传统方式设置的属性的属性特性均默认为true,而Object.defineProperty的则默认均为false
* 查看:`Object.getOwnPropertyDescriptors(targetObj,prop)`
* 分类
+ 值属性
- `configurable`:所有属性特性的总开关
- `writable`:可写性
- `enumerable`:可枚举性(即是否能被遍历)
- `value`:属性的值(严格意义上说不是属性特性)
```js
var data = {
username:'kuiya',
password:123456,
gender:'male',
// age:23,
phone:18319005000,
isSingle:'单身',
sorce:{
math:80,
english:85
}
}
// 1.查看属性特性
console.log('最开始的sorce属性特性:',Object.getOwnPropertyDescriptors(sorce));
// 2.修改属性特性
Object.defineProperty(data,'username',{
writable:false
})
```
+ 存储器属性
- `configurable`
- `enumerable`
- `get`
- `set`
```js
var user = {
us:'kuiya',
// 1 传统设置存储器属性:通过get和set设置的属性方法,值都不是对象本身的,而是代理借来的
get age(){
return 23
},
set age(value){
return value
}
}
// 2 Object.defineProperty设置存储器属性:把other的pw代理给user
var other = {
pw:123456789
}
Object.defineProperty(user,'pw',{
get(){
return other.pw
},
set(newValue){
other.pw = newValue;
return other.pw
}
})
```
* 实现响应式
```js
// 1.模拟响应式
const person = {
username:'kuiya',
password:123456,
sorce:{
math:80,
english:90,
chinese:87
}
}
const newPerson = {};
for(let key in person){
Object.defineProperty(newPerson,key,{
/* 设置成存储器属性后就可以为所欲为 */
get(){
return person[key]
},
set(newValue){
// 监听到值的修改则修改页面数据
box.innerHTML = newValue;
person[key] = newValue;
}
})
}
// 2.Vue实现响应式
const initData = {
age:66,
gender:'female',
score:{
math:79
}
}
var vm = new Vue({
el:'#app',
// 实例化Vue时,内部自动遍历data下所有的属性,把所有属性变成getter&setter,并写入vm实例
data:initData
})
// 3.Vue实例化后的相适应属性设置:目标对象不能是 Vue 实例,或者 Vue 实例的根数据对象
Vue.set(vm.score,'english',87);
```
#### 4.案例涉及到的Vue指令
* 遍历数据:`v-for`(列表循环渲染)
> 数组(item,idx)、对象(value,key,idx)
* 绑定事件:`v-on`(绑定事件,简写`@`)
+ 修饰符
* 条件指令
+ 显示隐藏:`v-show`
+ 创建销毁:`v-if` |`v-else` | `v-else-if`
> 同时设置if、else时,元素必须是同级
* 数据绑定
+ 单向(m->v)
- `v-html`:解析html结构
> 注意内容安全性问题:别人可以直接写入恶意的html代码植入项目导致意外BUG
- `v-text`:纯文本,效果等效于{{}}
- `v-bind`:标签属性绑定,简写`:`
> 同属性会进行合并,比如多个class
+ 双向(m->v||v->m)
- `v-model`
+ `延伸思考`:不用v-model实现双向数据绑定 || v-model原理
#### 5.内置属性
* `$refs`:注册过ref属性的所有DOM元素和组件实例
#### 6.事件修饰符
* `enter`|| `13`:回车键
```html
@key.enter = addItem
```
* `right`:鼠标右键
* `left`:鼠标左键
#### 7.案例
* tab标签切换
* 待办事件清单todoLIst
### 20200814_实例参数配置、组件Component、组件通讯
#### 1.新增参数配置
* `computed`:对data数据进行加工处理,具备缓存特性,降低性能消耗,避免资源浪费
* `compontents`:注册局部组件
#### 2.组件Component
##### 分类
* 全局组件:`Vue.Component(name,options)`
> options包括template、components等属性
* 局部组件:`components`
> 实际开发中局部组件用的比较多
###### 全局组件
* 概念:任意地方都能使用的组件
* 案例:后台管理系统
* 过程描述
1. vue实例化
2. 注册全局组件mainMenu、mainContent
3. mainContent组件注册局部组件user
* 注意点
+ 设置全局组件的自定义标签为驼峰命名法时,在html结构写成标签形式要用横杠形式
+ 组件实例的template属性值一般为字符串模板所在的template标签,即值可以是字符串模板,但因为没有高亮和报错提示,所以直接写字符串模板的写法不推荐
+ 组件市里的data属性值不再是对象形式,而是函数形式,这是为了方便复用
###### 局部组件
* 概念:只能在特定位置使用的组件
* 案例:todoList待办清单
* 过程描述
1. 创建vue实例,注册局部组件todoList
2. 设置todoList组件,并注册局部组件todoForm、todoContent
3. 设置todoForm、todoContent组件,并注册局部组件todoItem
* 注意点:模板组件只能有一个根元素,即模板标签下只能有一个子标签,若存在多组件则需要把这些组件用一个空标签包裹起来
#### 3.组件通讯
* 概念:组件间进行数据传递
* 原则:谁的数据谁修改
* 方式
+ 父传子:父组件进行`属性绑定`,子组件实例设置`接收属性props`
+ 子传父:给予子组件`自定义事件`,并在子组件通过`this.$emit()`触发自定义事件
### 20200817_组件通讯之跨组件通讯、插槽、生命周期函数、VUE-CLI脚手架初使用
#### 1.跨组件通讯
* 事件总线Bus
* 接收方绑定事件:`Bus.$on(event,function(options))`
* 发送方触发事件:`Bus.$emit(event,options)`
* 在自定义组件上绑定事件需要添加`修饰符native`
#### 2.插槽
* 命名插槽:``
* 默认插槽:``
#### 3.生命周期函数
* `创建create`
+ 前:初始化事件、生命周期函数
+ 后:注入响应式属性
* `挂载mount`
+ 前->后:把数据挂载到视图层
* `更新update`
+ 前:挂载后处于监听状态
+ 后:监听到数据更新,虚拟DOM对比,更新视图
* `销毁destroy`
+ 前->后:切断vm与v的联系,销毁监听、子组件和事件,但不对v造成影响,m层还在
* 各阶段的应用场景
#### 4.ESModule
* 特点
1. 一个文件即为一个模块,且每个模块`只加载一次并执行一次`,再次加载同一文件,直接`从内存中读取`
> 与commonJS一致
2. 每一个模块内声明的变量都是`局部变量`, `不会污染全局作用域`
3. 通过`export`导出模块,通过`import`导入模块
> commonJS通过`requier()`引入,通过`module.exports`导出
4. ES6模块`只支持静态导入和导出`,即导入的代码不存在任何变量,只可以在模块的最外层作用域使用import和export
* 语法
+ 导入
+ 导出
* 注意与commonJS的查询规则区别
+ ESModule:module属性
+ commonJS:main属性
### 20200818_vue路由、ElementUI
#### 1.VueRouter
* 安装依赖:`npm i vue-router`
* 使用
* 参数配置
+ `routes`:路由规则(根据不同的url地址实现不同的路由)
- `path`:页面路径
- `component`:指定路由组件
- `redirect`:重定向,匹配指定路由时,重新匹配到其他路由,一般用于重定向`首页和404路由`
+ `mode`:路由模式
- `hash`:哈希路由(默认)
> 有带#号
- `history`:历史路由
> 后期上线需要进行额外的服务器配置,否则后不起作用
* 路由内容显示:`内置组件`
* 导航:`实现路由跳转`
+ `声明式导航`:利用内置组件``配合参数来实现跳转
+ `编程式导航`:利用`实例对象中的$router`的一系列方法来实现跳转
* 路由嵌套
#### 2.ElementUI
* 安装:`npm i element-ui`