HTML5CORE04
目录
前言
一、复习
二、WebSocket
三、服务器搭建
四、聊天室
五、defineProperty
5.1.初识defineProperty
5.2.配置多个属性
5.3.可配置
5.4.赋值监听
5.5.练习
5.6.计算属性
总结
前言
HTML5CORE04学习开始
一、复习
SVG
:
- 利用HTML的 DOM 来绘制图形
- 特色
- 放大缩小都不会失真
- 图片的内容可以通过DOM方式进行操作
Echarts
:
- 第三方的 图表库
- 非常适合时下流行的
数据大屏
- 使用方式:
复制 -> 粘贴 -> 改一改
回调地狱
:
- 当多个 回调函数 层层嵌套时, 会导致 代码的阅读和维护复杂
Promise
:
- 来自ES6提供的构造函数, 其构造出来的对象可以利用
链式语法
来解决 回调地狱 的格式问题- new Promise().then().then().then()...
- 语法糖:
async
与await
用于代替链式写法async function xxx(){ var res = await new Promise(); }
二、WebSocket
- 不同于之前学习的
Http
协议
- Http协议发送的请求, 是一次性连接
- 做法: 客户端主动通过 http 请求向服务器获取数据, 服务器响应请求 把数据回传给用户 -- 到此结束
- websockt: 长链接
- 客户端主动与服务器交互, 交互后要留下联系方式 -- 服务器可以随时随地的与客户端进行交互
三、服务器搭建
- 文件夹:
chat-server
- 初始化:
npm init -y
- 安装模块:
npm i cors express socket.io
客户端需要使用 socket 模块 为其特殊准备的脚本来实现长链接
脚本位置如下: 把其复制到
public
目录里使用
const express = require('express'); // 在 express 上, 加装 socket 模块, 实现长链接 const cors = require('cors'); const http = require('http'); const socket = require('socket.io'); const app = express() // express 本身和 scoket 都属于服务器, 互相不兼容 // 利用 createServer 封装成一个 兼容性更好的服务器 const server = http.createServer(app) // 把 socket 长链接服务器 也放到 server 里 const io = new socket.Server(server, { cors: { origin: '*' } //允许跨域访问 }) // 监听利用 socket 长链接方式 访问的用户 // connection: 连接事件, 当有用户来访时, 会自动触发 io.on('connection', user => { console.log('user:', user) // 监听用户发送的消息: user.on('msg', msg => { console.log('来自用户的消息', msg) // 回复信息: // 假设用户说: 你好, 我们回复 你好,有什么需要帮助的? if (msg.indexOf('你好') != -1) { // 发消息: emit user.emit('reply', '你好,有什么需要帮助的?') } if (msg.indexOf('手机') != -1) { user.emit("reply", '联系方式 18822323333') } if (msg.indexOf('优惠') != -1) { user.emit("reply", '20年店庆大促销, 全场 5折起') } }) // 每隔 2s 给用户主动发消息 // emit: 用于给用户发消息 // 参数1: 消息名 参数2: 消息体 // setInterval(() => { // user.emit('ad', '好消息, 好消息, 20周年庆典 全场5折优惠!!') // }, 2000); }) app.use(express.static('public')) app.use(cors()) // express 服务器 允许跨域 server.listen(3000, () => { console.log('服务器已开启!') })
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>首页</title> <style> #box { width: 400px; display: flex; flex-direction: column; } #box>ul { margin: 0; padding: 0; height: 400px; overflow-y: scroll; background-color: #eee; } #box>ul>li.you { display: flex; flex-direction: column; align-items: flex-end; } </style> </head> <body> <h1>欢迎使用 websocket</h1> <!-- 搭建socket 连接, 需要官方提供的 客户端脚本 --> <div id="box"> <ul> <!-- <li> <h3>人名</h3> <p>文字信息</p> </li> --> </ul> <textarea cols="30" rows="4"></textarea> <button>发送消息</button> </div> <script src="./socket.io.min.js"></script> <script src="./jquery-3.6.1.min.js"></script> <script> // 脚本中提供了与服务器搭建长链接的方法 // io方法: 是脚本中提供的, 专门负责与服务器搭建链接 // 链接到服务器后, 会得到服务器的详细信息 const server = io('http://localhost:3000') // 利用 on 方法, 监听服务器发送的消息 // 参数1: 消息名, 由服务器发消息时设定 // 参数2: 回调函数, 接到的消息体 server.on('ad', msg => { console.log(msg) }) // 点击发送按钮, 获取输入框中的值, 发送给服务器 $('#box>button').on('click', function () { var msg = $('#box>textarea').val() if (msg == '') return $('#box>textarea').val('') //清空值 $('#box>ul').append(`<li class="you"> <h3>你:</h3> <p>${msg}</p> </li>`) // 自动滚动到最底部 // 设置其滚动距离 是可以滚动的最大距离 var maxHeight = $('#box>ul').prop('scrollHeight') $('#box>ul').scrollTop(maxHeight) //设置滚动距离 // 给服务器发消息 // emit: 发送 // 参数1: 消息名 参数2: 值 server.emit('msg', msg) }) // 监听服务器回复的消息 server.on('reply', msg => { console.log('服务器回复:', msg) $('#box>ul').append(`<li> <h3>官方旗舰店:</h3> <p>${msg}</p> </li>`) }) </script> </body> </html>
四、聊天室
- 创建新的服务器
- 新建文件夹 : chat-home
- 初始化 :
npm init -y
- 安装:
npm i express cors socket.io
const cors = require('cors'); const socket = require('socket.io'); const express = require('express'); const http = require('http'); const app = express() const server = http.createServer(app) const io = new socket.Server(server, { cors: { origin: '*' } }) io.on('connection', function (user) { // 上节课: 给连接的用户发消息 // user: 代表当前链接的用户 user.emit('消息名', '消息详情') // 群发: 给所有链接到服务器的用户发消息 io.emit('消息名', '消息详情') }) app.use(express.static('public')) app.use(cors()) server.listen(3000, function () { console.log('服务器已开启'); })
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>聊天室</title> <style> #users { display: flex; margin: 0; padding: 0; list-style: none; flex-direction: column-reverse; height: 300px; overflow-y: scroll; width: 200px; } #box { width: 400px; display: flex; flex-direction: column; } #box>ul { padding: 10px; margin: 0; padding: 0; height: 400px; overflow-y: scroll; background-color: #eee; } #box>ul>li.you { display: flex; flex-direction: column; align-items: flex-end; } #box>ul>li>p { padding: 10px; margin: 0; background-color: green; color: white; border-radius: 4px; } </style> </head> <body> <h1>欢迎来到WEB2208聊天室</h1> <ul id="users" style="display: none;"></ul> <div id="box"> <ul></ul> <textarea cols="30" rows="4"></textarea> <button>发送消息</button> </div> <script src="./jquery-3.6.1.min.js"></script> <script src="./socket.io.min.js"></script> <script> // 非 https 的 const server = io('chathome.xin88.top') // 接收欢迎信息 server.on('welcome', msg => { console.log(msg) }) // 发送个人信息到服务器 server.emit("profile", { uname: '小新', age: 32, phone: '10086' }) // 监听新用户信息 server.on('new_user', data => { console.log('新用户:', data) $('#users').append(`<li>${data.uname}</li>`) }) // 点击发送时 $("#box>button").click(function () { // 消息格式规定: // { uname:用户名, msg:"信息内容"} // 获取输入框的值, 发消息到服务器即可 var msg = $('#box>textarea').val() $('#box>textarea').val('') //清空 server.emit("msg", { uname: "小新", msg }) }) // 接收消息: 显示到 ul 里即可 server.on('msg', data => { console.log(data) $('#box>ul').append(`<li> <h3>${data.uname}</h3> <p>${data.msg}</p> </li>`) $('#box>ul').scrollTop($('#box>ul').prop('scrollHeight')) }) </script> </body> </html>
五、defineProperty
5.1.初识defineProperty
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>defineProperty 15:01</title> </head> <body> <!-- defineProperty 是一个 精确配置 对象属性的 API --> <!-- 主要用于开发 完善的框架时使用 --> <script> // 开启严格模式 // 特点: 取消静默失败, 失败的操作会报错 'use strict' var emp = { ename: "泡泡", age: 18, // 年龄 eid: '000235', //员工编号 salary: 23000, //薪资 -- 希望不要被遍历 } // 精确详细的 为对象中的属性, 添加限定 // define:规定, 定义 property: 属性 Object.defineProperty(emp, 'eid', { // ctrl + i :看提示 // 希望 eid 不要被写入新的值 writable: false, // 是否可以被写入值: false代表不可以 }) Object.defineProperty(emp, 'ename', { writable: false }) // 薪资属性 不要被 for.in 遍历 Object.defineProperty(emp, 'salary', { // 可枚举的, 可遍历的 enumerable: false // 浏览器会把 不可遍历的属性, 用浅色表示 }) // 看代码有何问题 emp.age = 180 // 赋值的逻辑错误 // emp.eid = true // 员工编号不应该被修改 // emp.ename = '哈哈' // 名字不允许改 for (const key in emp) { console.log(key, emp[key]) } console.log(emp); </script> </body> </html>
5.2.配置多个属性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>defineProperty 15:36</title> </head> <body> <script> 'use strict' var phone = { name: "iPhone14", price: 9999, maker: 'Apple', } // 需求1: 让3个属性都不可以被修改值 // Object.defineProperty(phone, 'name', { writable: false }) // Object.defineProperty(phone, 'price', { writable: false }) // Object.defineProperty(phone, 'maker', { // writable: false, // enumerable: false // }) // 合写: 利用 defineProperties 可以一次性给对象配置多个属性 Object.defineProperties(phone, { // 属性名: { 配置项 } name: { writable: false }, price: { writable: false }, maker: { writable: false, enumerable: false } }) // 需求2: 让 maker 属性 不可遍历 // phone.name = '0 0 0' //报错 // phone.price = 3 // 报错 // phone.maker = '444' //报错 for (const key in phone) { console.log(key, phone[key]) } </script> </body> </html>
5.3.可配置
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>defineProperty 16:16</title> </head> <body> <script> 'use strict' var emp = { ename: "泡泡", husband: '大连' // 丈夫 } Object.defineProperty(emp, 'husband', { // 可配置的 configurable: false //不可以重新配置, 例如 删除 }) // defineProperty 也可以为对象新增属性 -- 但是其新增的属性权限都是最低的 // 不可删 不可改 不可遍历 // 当定义的属性不存在, 则自动转为新增操作 Object.defineProperty(emp, 'age', { value: 18, //赋值 }) // delete emp.age // emp.age = 2000 for (const key in emp) { console.log(key, emp[key]) } // delete : 删除对象的属性 // delete emp.husband console.log(emp) </script> </body> </html>
5.4.赋值监听
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>defineProperty 16:29</title> </head> <body> <script> var emp = { ename: "泡泡", age: 18 } // 逻辑有问题, 赋值错误 -- 报错 Object.defineProperty(emp, 'age', { // set: 设置 // 当给属性 赋值时触发: = 赋值符号 set: function (value) { console.log('value:', value) // 判断: 合理的年龄范围 1 - 120 if (value >= 1 && value <= 120) { } else { //抛出报错 throw Error('age的赋值错误, 请检查:' + value) } } }) emp.age = 200 console.log(emp) </script> </body> </html>
5.5.练习
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>练习 16:43</title> </head> <body> <script> var stu = { sname: "凯凯", phone: '19848485444', age: 33, } Object.defineProperties(stu, { age: { // 语法糖: 可以省略 : function // set: function(){} set(value) { if (value >= 1 && value <= 100) { } else { throw Error("age赋值错误" + value) } } }, phone: { set(value) { if (/^1[3-9]\d{9}$/.test(value)) { } else { throw Error("phone赋值错误" + value) } } }, sname: { set(value) { if (typeof value == 'string') { } else { throw Error("sname赋值错误, 必须字符串类型" + value) } } } }) // stu.age = 200 //报错: 合理范围 1 - 100 // stu.phone = '10086' // 报错: 不是手机号格式; 正则 /^1[3-9]\d{9}$/ stu.sname = 666 // 要求必须字符串类型, 用typeof 来判断类型 </script> </body> </html>
5.6.计算属性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>defineProperty 17:21</title> </head> <body> <!-- 计算属性 --> <script> // 矩形对象 var r1 = { width: 100, height: 40, // 计算属性: 当添加 get 关键词在函数前 // 函数在使用时, 必须 省略(); JS会自动触发函数 // 此时函数使用时的格式, 就如同在读取普通的属性 -- 计算属性 // 总结: 书写 get 使用时少写() get area() { return this.width * this.height }, // 这个 get 是语法糖 get zc() { return (this.width + this.height) * 2 } } console.log(r1.zc); console.log(r1.width); console.log(r1.area); console.log(r1.area); console.log(r1.area); // 完整格式 Object.defineProperty(r1, 'area1', { // 当读属性的值时, 自动触发 get: function () { return this.width * this.height } }) console.log('area1:', r1.area1) // 不用() 就能触发 </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>defineProperty 17:40</title> </head> <body> <!-- 赋值监听的完整操作 --> <script> var emp = { ename: '泡泡', age: 18 } Object.defineProperty(emp, 'age', { // 计算属性, 不需要() 就能触发 // emp.age // 类似于 去柜台取钱, 员工到金库取钱出来 给你 get() { return this._age }, set(value) { if (value >= 1 && value <= 120) { // 合法就赋值 console.log('合法的年龄, 进行赋值'); // 问题: 赋值会触发 age 的 set 监听, 触发无限循环 // 由于age属性负责检测赋值, 所以不再具有存值功能 // 需要一个额外的变量来存值, 习惯上用 _age, 带有_ 前缀进行区分 this._age = value } else { throw Error("age赋值错误 " + value) } } }) // emp.age = 200 // 报错: 合理范围 1 - 120 // 站在使用者的角度 // 从表面现象看: 40 是存放在 age 属性的 emp.age = 40 //正常 console.log(emp) // 读取时: 就应该从 age 中读取, 而不是 莫名其妙的 _age // console.log(emp._age) console.log(emp.age) // 可以触发 get 计算属性 </script> </body> </html>
总结
HTML5CORE04学习结束