# 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`