1 jquery
1.1 初始化函数
01.介绍
a.window.onload
执行时间:须等待网页中所有的内容加载完毕后(图片、flash、视频等)才能执行
编写个数:一页面不能同时编写多个
简化写法:无
b.$(document).ready()
执行时间:网页中所有DOM文档结构绘制完毕后即刻执行,可能与DOM元素关联的内容(图片、flash、视频等)并没有加载完
编写个数:同一页面能同时编写多个
简化写法:$(function}{}
02.代码
<html>
<head>
<title>初始化函数:两种方式</title>
<!-- 引入jQuery-->
<script type="text/javascript" src="js/jquery-3.3.1.js" ></script>
<!--使用引入jQuery-->
<script type="text/javascript">
//$(document).ready():初始化函数,当网页中的dom元素(不包含图片、视频、资源)全部加载完毕后,立刻执行
//$(document).ready():同一个页面能同时编写多个
$(document).ready(function(){
alert("hello world");
alert("hello world2");
});
//$(document).ready():简化形式$(function)(){}
$(function(){
alert("hello function...");
});
//$等价于jQuery
jQuery(function(){
alert("hello function2...");
});
//window.onload:初始化函数,当网页中的dom元素(关联图片、视频、资源)全部加载完毕后,立刻执行
//window.onload:同一页面不能同时编写多个
function init(){
alert("hello jquery");
}
</script>
</head>
<body onload="init()">
</body>
</html>
1.2 DOM与jQuery转换
01.DOM模型:将html、xml等文档结构的标签语言,都可以看成DOM模型
02.DOM节点有三种类型:
①元素节点:<html><ul>..<p>
②属性节点:titlesrcalt
③文本节点:文本节点
03.DOM对象:
①概念层面:以上三种节点类型的具体对象就是DOM对象
②使用层面:凡是JavaSCript能够直接操作的对象就是DOM对象
例如,vartitle=document.getElementByld("myTitile");
通过js获取到的title对象就是一个dom对象(就是<p>对象)
04.jQuery对象:
使用层面:凡是jQuery能够直接操作的对象,就是jQuery对象
例如:var$title=$("#myTitile");通过jquery获取到的$title就是一个jquery对象
05.总结
①同样一个元素,即可以成为一个dom对象(javascript对象),也可以成为一个jquery对象
②dom对象只适用于js的各种语法(函数、属性),而jquery对象只用于jquery的各种语法(函数、属性)
③dom对象和jquery对象的各自独立
例如:
title是dom对象,因此可以使用js属性或方法title.innerHTML
Stitle是jquery对象,因此可以使用jquery属性或方法$title.html0
06.DOM对象与jQuery对象转换
DOM对象->jQuery对象:jQuery工厂$(DOM对象)
jQuery对象->DoM对象:基础(jquery对象默认是一个数组或集合:dom对象默认是一个单独的对象)
①数组->单独对象:jquery对象[0]
②集合->单独对象:jquery对象.get(0)
1.3 选择器
01.基本选择器
1.标签选择器:$("标签名") $("p").html()
2.类选择器:$(".class值") $(".myTitile1").html()
3.id选择器:$("#id值") $("#myTitile2").html()
4.并集选择器:逗号,(或,可以同时选中多个) $(".myTitile1,#myTitile2,.myTitile3").length
5.交集选择器:直接拼写,(同时存在,只能选中一个,不能出现歧义) $("p#myTitile2").length
6.全局选择器:选中全部的元素 $("*").length
02.层次选择器
1.相邻同辈选择器(只取后面的元素有效):+ $("选择器1+选择器2") $("#b+li").length
2.通用同辈选择器(只取后面的元素有效):~ $("选择器1~选择器2") $("#b~li").length
3.后代选择器(子代、孙子代...):空格 $("选择器1 选择器2") $("body li").length
4.子代选择器(子代):> $("选择器1>选择器2") $("body>ul>li").length
03.属性选择器
1.$("[属性名]") 全部
2.$("[class=xxx]") 等于,也可以加上'',$("[class='xxx']")
3.$("[class!=a]") 不等于:包括两种情况,一是有class值但不是a
4.$("[class^=a]") class以a开头的元素
5.$("[class$=a]") class以a结尾的元素
6.$("[class*=a]") class有a的全部元素
04.过滤选择器(从0开始,0代表偶数)
过滤选择器的一些方法 和 其他函数类型,
例如,可以$("ul>li:first") 等价于 $("ul>li").first()
有些不同,例如,可以$("ul>li:odd"); 错误$("ul>li").odd();
---------------------------------------------------------------------------------------------------------
1.:first:最开头那一个 $("ul>li:first").html()
2.:last:最后那一个 $("ul>li:last").html()
3.:eq(index):第index个 $("ul>li:eq(2)").html()
4.:even:偶数(4个字母) $("ul>li:even").length
5.:odd:奇数(3个字母) $("ul>li:odd").length
6.:gt(index):>index的全部元素 $("ul>li:gt(2)").length
7.:lt(index):<index的全部元素 $("ul>li:lt(2)").length
8.:not(选择器):除了...以外 $("ul>li:not( ul>li:lt(2) )").length
9.:header:选中所有的标题元素,包括h1、h2...h6 $(":header").css("background-color", "gray")
10.:focus:获取当前焦点的元素 $("input:focus").css("background-color", "red")
---------------------------------------------------------------------------------------------------------
细节:DOM与jQuery对象的互相转换 $($("ul>li:odd")[0]).html()
细节:DOM与jQuery对象的互相转换 $($("ul>li:odd").get(0)).html()
05.可见性选择器
1.:visible:选中所有可见的元素 $(":visible").length
2.:hidden:选中所有隐藏的元素 $(":hidden").length
1.4 事件
01.鼠标事件
鼠标事件click():单击
鼠标事件mouseover():悬浮
鼠标事件mouseout():离开
鼠标事件mouseover + mouseout:链式写法
---------------------------------------------------------------------------------------------------------
$(document).ready(function(){
$("h1").click(function(){
alert("单击事件...");
});
$("div").mouseover(function(){
alert("悬浮事件...");
});
$("div").mouseout(function(){
alert("离开事件...");
});
$("div").mouseover(function(){
alert("悬浮事件...");
}).mouseout(function(){
alert("离开事件...");
});
});
02.键盘事件
键盘事件keydown():键盘按键 从上往下的 过程
键盘事件keypress():按键被压到 最底部
键盘事件keyup():松开按键
---------------------------------------------------------------------------------------------------------
$(document).ready(function(){
$("body").keydown(function(){
if(event.keyCode == 13){
alert("按键 从上往下的 过程...回车");
}
});
$("body").keypress(function(){
alert("按键被压到最底部...");
});
$("body").keyup(function(){
alert("松开按键");
});
});
03.表单事件
表单事件focus:获取光标
表单事件blur:失去光标
---------------------------------------------------------------------------------------------------------
//二、jquery事件:无onXXX,例如click() 写在ready()内
$(document).ready(function(){
$("#uid").focus(function(){
$(this).css("background-color", "red");
});
$("#uid").blur(function(){
$(this).css("background-color", "green");
});
});
04.绑定事件、移除事件
bind 绑定事件
unbind 移除事件
---------------------------------------------------------------------------------------------------------
$(document).ready(function(){
$("#uid").bind("click",function(){
alert("bind形式的单击事件...");
});
$("body").bind( {"keydown":function(){ //绑定多个事件
alert("按键 从上到下的过程...");
},"keypress":function(){
alert("按键 被压到最底部...");
}
} );
});
05.复合事件、显示效果
hover(f1,f2):切换使用mouseover()和mouseout()
toggle(f1,f2,f3,...,fn):轮回使用多个click()事件,toggle()还有其他含义(隐藏与显示)
---------------------------------------------------------------------------------------------------------
$(document).ready(function(){
$("h2").hover(function(){
alert("悬浮...");
}, function(){
alert("离开...")
});
$("body").toggle(function(){
$(this).css("background-color", "red");
},function(){
$(this).css("background-color", "yellow");
},function(){
$(this).css("background-color", "blue");
});
});
1.5 操作DOM
01.样式操作
1.设置CSS:jquery对象.css("属性名","属性值");
jquery对象.css({ "属性名":"属性值", "属性名":"属性值", ... ,"属性名":"属性值" });
2.追加或移除样式class:addClass("x"):增加某一个
addClass("x x x"):增加多个,空格隔开
removeClass("x"):移除某一个
removeClass("x x x"):移除多个,空格隔开
removeClass():移除全部样式
toggleClass("x x x"):切换追加与移除
---------------------------------------------------------------------------------------------------------
$(document).ready(function(){
$("#color").css("color","red");
$("#color").css({"color":"red",
"font-size":"100px",
"background-color":"gray"});
$("#test").addClass("myStyle1");
$("#test").addClass("myStyle1 myStyle2 myStyle3");
$("#test").click(function(){
$("#test").removeClass("myStyle1");
$("#test").removeClass("myStyle1 myStyle2 myStyle3");
$("#test").removeClass();
$("#test").toggleClass("myStyle1 myStyle2 myStyle3");
});
});
02.内容操作
1.html():获取值,获取的是元素的内容 ,包含了 元素内部的 各种标签
2.text():获取值,只获取文本值
3.val():获取value值
---------------------------------------------------------------------------------------------------------
$(document).ready(function(){
alert($("#test").html());
alert($("#test").text());
alert($("#uid").val());
});
03.节点与属性操作
a.节点操作
1.查询节点:jQuery选择器
2.创建节点:$()
$(选择器):获取节点
$(DOM对象):DOM对象转换为jQuery对象
$(html字符串):$("<li onclick="..">xxx</li>")
3.插入节点
a.内部插入
function myTest1(){
var $myElement = $("<li>xxx</li>"); //创建节点
$("ul").append($myElement); //1.$(A).append(B) 将B插入到A之后,如:$("ul").append($node);
$myElement.appendTo($("ul")); // $(A).appendTo(B) 将A插入到B之后,如:$node.appendTo("ul");
$("ul").prepend($myElement); //2.$(A).prepend(B) 将B插入至A之前,如:$("ul").prepend($node);
$myElement.prependTo($("ul")); // $(A).prependTo(B) 将A插入到B之前,如:$node.prependTo("ul");
}
b.外部插入
function myTest2(){
var $myElement = $("<p>yyy</p>"); //创建节点
$("ul").after($myElement); //1.$(A).after(B)) 将B插入到A之后,如:$("ul").after($node);
$myElement.insertAfter($("ul")); // $(A).insertAfter(B) 将A插入到B之后,如:$node.insertAfter("ul");
$("ul").before($myElement); //2.$(A).before(B) 将B插入至A之前,如:$("ul").before($node);
$myElement.insertBefore($("ul")); // $(A).insertBefore(B) 将A插入到B之前,如:$node.insertBefore("ul");
}
4.替换节点
function myTest3(){
$("ul>li:eq(1)").replaceWith("<li>xx</li>"); //1.$X.replaceWith(Y) 用Y替换X
$("<li>xx</li>").replaceAll("ul>li:eq(1)"); //2.$X.replaceWith(Y) 用X替换Y
}
5.删除节点
function myTest4(){
$("ul>li:eq(1)").remove(); //1.remove():彻底删除
$("ul>li:eq(2)").detach(); //2.detach():将结点删除,但关联的事件、数据不会删除(不推荐使用)
$("ul>li:eq(3)").empty(); //3.empty():只删除内容
}
6.克隆节点
function myTest5(){
var $clo = $("ul>li:eq(1)").clone(); //1.clone(true|false):克隆节点,(如果为true,则克隆节点关联的事件; 反之为false,则不会*/
$("ul").append($clo); // 验证克隆是否成功:将克隆后的节点内部插入到ul列表
}
b.属性操作
function myTest6(){
alert( $("#uid").attr("value") ); //1.attr("属性名"):获取属性值value
alert( $("#uid").attr("type") ); // attr("属性名"):获取属性值type
$("#uid").attr("name", "zs"); //2.attr("属性名","属性值"):设置属性值
alert( $("#uid").attr("name") ); // 验证是否修改属性值成功
$("#uid").attr({"name":"ls", "value":"1111"}); //3.attr({ "属性名":"属性值", "属性名":"属性值", "属性名":"属性值" } );
alert( $("#uid").attr("name") + "---" + // 验证是否修改属性值成功
$("#uid").attr("value") );
$("#uid").removeAttr("value"); //removeAttr("属性名"):删除属性值
alert( $("#uid").attr("value") ); // 验证是否删除属性值成功
}
04.获取集合与遍历集合
a.获取集合
function myTest1(){
var $list1 = $("ul"); //1.集合 $(...)
alert($list1.length);
var $list2 = $("ul").children("li"); //2.子节点集合 $(...).children("li")
alert($list2.length);
var $list3 = $("body").find("li"); //3.后代集合(不推荐使用,影响性能) $(...).find( "li")
alert($list3.length);
var $list4 = $("ul>li:eq(1)").next(); //4.同辈集合: next():后一个
alert($list4.html());
var $list5 = $("ul>li:eq(1)").prev(); //4.同辈集合: prev():前一个
alert($list5.html());
var $list6 = $("ul>li:eq(1)").siblings(); //4.同辈集合: siblings():真正的同辈 前、后
alert($list6.length);
var $list7 = $("ul>li:eq(1)").parent(); //5.前辈集合: parent():父代集合
alert($list7.html());
var $list8 = $("ul>li:eq(1)").parents("ul"); //5.前辈集合: parents("ul"):祖先集合 中 只选择"ul"
alert($list8.html());
var $list9 = $("ul>li:eq(1)").siblings(":odd"); //6.过滤集合 i.很多方法的()就是一个过滤选择器,例如parents("ul")、children("li")...
$list9.css("background-color","pink"); // ii.filter("选择器...");
}
b.遍历集合
function myTest2(){
$("ul>li").each(function(){ //1.each()
alert($(this).html());
});
$("ul>li").each(function(index){ //2.each(index):index代表当前是第X个
alert(index + "," + $(this).html());
});
$("ul>li").each(function(index,element){ //3.each(index, element):element代表当前元素,注意一点,element是DOM对象,需要使用$()转化为jQuery对象
alert(index + "," + $(element).html());
});
}
05.CSS-DOM操作
$(document).ready(function(){
$("#test").height(500); //1.height(500):设置高度500
alert( $("#test").height() ); // height( ):获取高度
$("#test").width(500); //2.width(500):设置宽度500
alert( $("#test").width() ); // width( ):获取宽度
$("#test").offset(function(index,oldOffset){ //3.offset(index,oldOffset):设置偏移量,index代表第几个偏移量,oldOffset代表当前坐标
var newOffset = new Object(); // newOffset代表新坐标
newOffset.left = oldOffset.left + 10;
newOffset.top = oldOffset.top + 10;
return newOffset;
});
var $myOffset = $("#test").offset(); // offset():获取偏移量,相对于"左上角的点",有两个坐标(left、top)
alert( $myOffset.left + "," + $myOffset.top)
$("#uid").click(function(){ //4.offsetParent():获取(已定位)的最近的祖先元素
$(this).offsetParent().css("border","10px solid blue"); // 已定位:元素position属性(默认static)被设置为了relative / absolute / fixed
});
$("#test").scrollLeft(30); //5.scrollLeft(30):设置30偏移量,相对滚动条左侧的偏移量
alert( $("#test").scrollLeft() ); // scrollLeft( ):获取偏移量,相对滚动条左侧的偏移量$("#test").scrollLeft(30);
$("#test").scrollTop(30); //6.scrollTop(30):设置30偏移量,相对滚动条顶部的偏移量
alert( $("#test").scrollTop() ); // scrollTop( ):获取偏移量,相对滚动条顶部的偏移量
});
1.6 表单校验
01.字符串处理
<html>
<head>
<title>表单校验</title>
<!-- 引入jQuery-->
<script type="text/javascript" src="js/jquery-3.3.1.js" ></script>
<!--使用引入jQuery-->
<script type="text/javascript">
//jQuery方式
$(document).ready(function(){
/*
blur():失去焦点时触发
*/
$("#uage").blur(function(){
var $age = $("#uage").val();
if(!($age>0 && $age<100)){
alert("年龄输入有误!");
}
});
/*
submit():当单击submit按钮触发时,通过返回值true|false告知程序是否校验成功!
*/
$("myFrom").submit(function(){
var $name = $("#uname").val();
var $pwd = $("#upwd").val();
var $height = $("#uheight").val();
if($name.length<2 || $name.length>6){
alert("名字必须填写2-6位!");
return false;//校验失败
}
if($pwd.length<=6){
alert("密码必须填写6位以上!");
return false;//校验失败
}
if($height<100 || $height>200){
alert("身高应该在100-200!");
return false;//校验失败
}
return true;//校验成功,表单提交
});
});
//JavaScript方式
/*
onsubmit():当单击submit按钮触发时,通过返回值true|false告知程序是否校验成功!
*/
function check(){
var $name = $("#uname").val();
var $pwd = $("#upwd").val();
var $height = $("#uheight").val();
if($name.length<2 || $name.length>6){
alert("名字必须填写2-6位!");
return false;//校验失败
}
if($pwd.length<=6){
alert("密码必须填写6位以上!");
return false;//校验失败
}
if($height<100 || $height>200){
alert("身高应该在100-200!");
return false;//校验失败
}
return true;//校验成功,表单提交
}
</script>
</head>
<body>
<form id="myFrom" action="03050100_表单校验_result.html" onsubmit="return check()">
姓名:<input id="uname"/><br/>
密码:<input id="upwd" type="password"/><br/>
身高:<input id="uheight"/><br/>
<input type="submit" value="注册">
</form>
</body>
</html>
02.正则表达式
<html>
<head>
<title>表单校验</title>
<!-- 引入jQuery-->
<script type="text/javascript" src="js/jquery-3.3.1.js" ></script>
<!--使用引入jQuery-->
<script type="text/javascript">
//jQuery方式
$(document).ready(function(){
$("#utel").blur(function(){
var $tel = $(this).val();
var reg = /^1\d{10}$/ ;
if(!(reg.test($tel))){//如果校验失败!
if($tel="" || $tel.length==0){//失败原因一:空/长度为0,则不显示隐藏域
$("#telTip").css("display","none");
}else{//失败原因二:不符合正则表达式的规则,则显示隐藏域
$("#telTip").css("display","inline");
}
}else{//如果校验成功!
$("#telTip").css("display","none");
}
});
$("#uemail").blur(function(){
var $email = $(this).val();
var reg = /^\w+@[0-9a-zA-Z]{2,4}\.[a-zA-Z]{2,3}(\.[a-zA-Z]{2,3})?$/ ;
if( !reg.test($email)){//如果校验失败!
if($email="" || $email.length==0){//失败原因一:空/长度为0,则不显示隐藏域
$("#emailTip").css("display","none");
}
else{//失败原因二:不符合正则表达式的规则,则显示隐藏域
$("#emailTip").css("display","inline");
}
}else{//如果校验成功!
$("#emailTip").css("display","none");
}
});
});
</script>
</head>
<body>
<form id="myFrom" action="03050100_表单校验_result.html" onsubmit="return check()">
姓名:<input id="uname"/><br/>
密码:<input id="upwd" type="password"/><br/>
身高:<input id="uheight"/><br/>
电话:<input id="utel"/><font id="telTip" color="red" style="display:none">电话号码格式不正确</font><br/> <!--style="display:none设置隐藏域:将文本进行隐藏-->
邮箱:<input id="uemail"/><font id="emailTip"color="red" style="display:none">邮箱格式不正确</font><br/> <!--style="display:none设置隐藏域:将文本进行隐藏-->
<input type="submit" value="注册">
</form>
</body>
</html>
03.表单选择器
<html>
<head>
<title>表单校验</title>
<!-- 引入jQuery-->
<script type="text/javascript" src="js/jquery-3.3.1.js" ></script>
<!--使用引入jQuery-->
<script type="text/javascript">
//jQuery方式
$(document).ready(function(){
//情况一:选择标签input,实际上可以拿到多个标签,包括input、textarea、select、button,可以遍历证明!
$(":input").each(function(){
alert($(this).attr("name"));
});
//情况二:除input的其他标签,仅代表自己一个,直接使用即可
$(":input").attr("myFile");
});
</script>
</head>
<body>
<!--情况一:选择标签input,实际上可以拿到多个标签,包括input、textarea、select、button,可以遍历证明!-->
<input type="text" name="myText" /><br/>
<input type="button" name="myButton"/><br/>
<textarea name="myTextArea"></textarea><br/>
<!--情况二:除input的其他标签,仅代表自己一个,直接使用即可-->
<input type="file" name="myFile"/><br/>
</body>
</html>
2 javascript
2.1 basic
01.bio1
1.JS可以通过不同的方式来输出数据、与用户交互的函数:alert,prompt 和confirm、JS关键字、现代模式,"use strict"、ES6 模块加载
2.JS语法、var、let、const
3.运算符、值的比较、空值合并运算符 '??'、运算符的扩展(ES6)
4.数据类型、类型转换、Symbol
5.条件语句
6.循环语句
7.错误处理
02.bio2
1.Proxy、Reflect
2.Proxy对象
3.Reflect对象
4.Eval 执行代码字符串
5.BigInt
6.ArrayBuffer
03.bio3
es6 ~ es14
01.style1
1.块级作用域
2.字符串
3.解构赋值
4.对象
5.数组
6.函数
7.Map 结构
8.Class
9.模块
10.ESLint 的使用
02.style2
1.概述
2.术语
3.抽象操作的标准流程
4.相等运算符
5.数组的空位
6.数组的 map 方法
2.2 colls
01.bio
1.原始类型的方法(字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined))
2.数字类型
3.字符串
4.数组
5.Iterable object(可迭代对象)
6.Map映射、Set集合
7.WeakMap and WeakSet(弱映射和弱集合)
8.Object.keys,values,entries
9.解构赋值
10.JSON方法
---------------------------------------------------------------------------------------------------------
遍历容器:
1.遍历字符串
2.遍历数组
3.遍历可迭代对象
4.遍历Map映射
5.遍历Set集合
6.遍历Object的keys、values、entries
---------------------------------------------------------------------------------------------------------
Iterator 和 for...of 循环(ES6):
1.Iterator(遍历器)的概念
2.默认 Iterator 接口
3.调用 Iterator 接口的场合
4.字符串的 Iterator 接口
5.Iterator 接口与 Generator 函数
6.遍历器对象的 return(),throw()
7.for...of 循环
02.object
1.对象基础
2.对象方法,"this"
3.symbol类型
4.对象引用和复制
5.构造器和操作符 "new"
6.原始值转换
7.垃圾回收
8.可选链 "?."
9.属性标志和属性描述符
10.属性的 getter 和 setter
11.对象的扩展(ES6)
12.对象的新增方法(ES6)
03.standard
1.Object对象
2.Array对象
3.包装对象
4.Boolean对象
5.Number对象
6.String对象
7.Math对象
8.Date对象
9.RegExp对象
10.JSON对象
2.3 dram
01.class
1.类语法
2.类继承
3.静态属性和静态方法
4.私有的和受保护的属性和方法
5.扩展内建类
6.类检查
7.Mixin模式
8.装饰器(ES6)
9.类的基本语法(ES6)
10.类的继承(ES6)
02.function
1.函数定义
2.函数参数
3.函数调用
4.闭包示例函数
5.this使用场景
6.递归和堆栈
7.全局对象
8.老旧的var、var 与 let/const 有两个主要的区别
9.计划调用:setTimeout、setInterval
10.函数的扩展(ES6)
03.module
1.模块简介
2.导出与导入
3.动态导入
4.Module 的语法
5.Module 的加载实现
04.prototype
1.原型继承
2.原生的原型
3.F.prototype
4.原型方法,没有 __proto__ 的对象
2.4 sync
01.async
async 函数:
1.含义
2.基本用法
3.语法
4.async 函数的实现原理
5.与其他异步处理方法的比较
6.实例:按顺序完成异步操作
7.顶层 await
---------------------------------------------------------------------------------------------------------
异步遍历器:
1.同步遍历器的问题
2.异步遍历的接口
3.for await...of
4.异步 Generator 函数
5.yield* 语句
02.generator
Generator 函数的语法:
1.简介
2.next 方法的参数
3.for...of 循环
4.Generator.prototype.throw()
5.Generator.prototype.return()
6.next()、throw()、return() 的共同点
7.yield* 表达式
8.作为对象属性的 Generator 函数
9.Generator 函数的this
10.含义
11.应用
---------------------------------------------------------------------------------------------------------
Generator 函数的异步应用:
1.传统方法
2.基本概念
3.Generator 函数
4.Thunk 函数
5.co 模块
03.promise,async/await
1.回调简介
2.Promise 对象的构造器(constructor)语法
3.Promise 链
4.使用 promise 进行错误处理
5.Promise 类有 6 种静态方法
6.Promisification
7.微任务(Microtask)
8.async/await
9.Promise对象(ES6)
2.5 zero
01.dom
1.概述
2.Node 接口
3.NodeList 接口,HTMLCollection 接口
4.ParentNode 接口,ChildNode 接口
5.Document 节点
6.Element 节点
7.属性的操作
8.Text 节点和 DocumentFragment 节点
9.CSS 操作
10.Mutation Observer API
02.event
1.EventTarget 接口
2.事件模型
3.Event 对象
4.鼠标事件
5.键盘事件
6.进度事件
7.表单事件
8.触摸事件
9.拖拉事件
10.其他常见事件
11.GlobalEventHandlers 接口
03.form
表单,控件:
1.表单属性和方法
2.聚焦:focus/blur
3.事件:change,input,cut,copy,paste
4.表单:事件和方法提交
04.model
Browser对象:
1.Window对象
2.Navigator对象
3.Screen对象
4.History对象
5.Location对象
6.存储对象:localStorage 和 sessionStorage
---------------------------------------------------------------------------------------------------------
在浏览器中存储数据:
1.Cookie,document.cookie
2.Web 存储对象 localStorage 和 sessionStorage
3.IndexedDB 是一个浏览器内建的数据库,它比 localStorage 强大得多
---------------------------------------------------------------------------------------------------------
CORS 通信:
1.两种请求
2.简单请求:基本流程、withCredentials 属性
3.非简单请求:预检请求、预检请求的回应、浏览器的正常请求和回应
4.与 JSONP 的比较
---------------------------------------------------------------------------------------------------------
File 对象,FileList 对象,FileReader 对象:
1.File 对象
构造函数
实例属性和实例方法
2.FileList 对象
3.FileReader 对象
---------------------------------------------------------------------------------------------------------
表单,FormData 对象:
1.表单概述
2.FormData 对象
概述
实例方法
3.表单的内置验证
自动校验
checkValidity()
willValidate 属性
validationMessage 属性
setCustomValidity()
validity 属性
表单的 novalidate 属性
4.enctype 属性
5.文件上传
---------------------------------------------------------------------------------------------------------
Location 对象,URL 对象,URLSearchParams 对象:
1.Location 对象
2.URL 的编码和解码
3.URL 接口
4.URLSearchParams 对象
---------------------------------------------------------------------------------------------------------
XMLHttpRequest对象:
1.简介
2.XMLHttpRequest 的实例属性
3.XMLHttpRequest 的实例方法
4.XMLHttpRequest 实例的事件
5.Navigator.sendBeacon()
3 typescript
3.1 basic
01.数据类型:
0.变量声明、类型检查
1.原始数据类型:1.布尔值 2.数字 3.字符串 4.空值(Void) 5.Null 和 Undefined;其他数据类型:数组、元组、枚举、Any、Void、Never、Object
2.任意值(Any)用来表示允许赋值为任意类型
3.类型推论、类型断言、类型别名
4.联合类型:联合类型(Union Types)表示取值可以为多种类型中的一种
5.对象的类型——接口:使用接口(Interfaces)来定义对象的类型
6.数组的类型:数组类型有多种定义方式,比较灵活
7.函数的类型:函数是 JavaScript 中的一等公民
8.Symbols:symbol类型的值是通过Symbol构造函数创建的,Symbols是不可改变且唯一的,像字符串一样,symbols也可以被用做对象属性的键
9.高级类型:交叉类型、联合类型、类型保护、可选参数、类型别名、字符串字面量类型、多态的 this 类型和索引类型
10.类型兼容性
11.迭代器和生成器
02.遍历容器:
1.遍历字符串
2.遍历数组
3.遍历可迭代对象
4.遍历元组
5.遍历Map映射
6.遍历Set集合
7.遍历Object的keys、values、entries
3.2 dram
01.示例
1.接口
2.类
3.函数
4.泛型
5.装饰器
6.Mixins
3.3 module
01.示例
1.内置对象
2.声明文件
3.模块
4.命名空间
5.命名空间和模块
6.模块解析
7.声明合并
8.JSX
9.三斜线指令
4 function
4.1 js引入包
00.一个模块(通常是一个 .js 或 .ts 文件)导入导出
a.什么时候不用 {} ?
当模块使用了 export default (默认导出) 时。
import 任意名称 from './module';
特点:一个文件只能有一个 export default。导入时的任意名称是你自己起的,可以随便叫什么。
b.什么时候用 {} ?
当模块使用了 export (命名导出) 时。
import { 导出的名称 } from './module';
特点:一个文件可以有多个 export。导入时 {} 里的名称必须和导出时的名称完全一样。
01.默认导出 (export default) -> 不用 {}
a.说明
这种方式用于导出一个模块最主要、最核心的功能。
b.示例:./tab.js 文件
// 假设这是 tab.js 的内容
const tabFunctions = {
open() { console.log('打开Tab'); },
close() { console.log('关闭Tab'); }
};
// 将 tabFunctions 对象作为这个模块的默认导出
export default tabFunctions;
c.导入方式
// 你可以给它起任何名字,比如 tab, myTab, tabsManager 等
import tab from './tab';
import myTab from './tab'; // myTab 和 tab 指向的是同一个对象
tab.open(); // 正确
myTab.close(); // 正确
02.命名导出 (export) -> 使用 {}
a.说明
这种方式用于从一个模块中导出多个变量、函数或对象。
b.示例:./auth.js 文件
// 假设这是 auth.js 的内容
// 导出一个名为 VueInstanceAuth 的常量
export const VueInstanceAuth = {
login() { /* ... */ },
logout() { /* ... */ }
};
// 还可以导出另一个常量或函数
export const TOKEN_KEY = 'user-token';
export function checkPermission() {
// ...
}
c.导入方式
// 名字必须和导出的名字(VueInstanceAuth, TOKEN_KEY)完全匹配
import { VueInstanceAuth, TOKEN_KEY } from './auth';
// 如果只想导入其中一个
import { checkPermission } from './auth';
// 使用别名 (as)
import { VueInstanceAuth as Auth } from './auth';
VueInstanceAuth.login(); // 正确
console.log(TOKEN_KEY); // 正确
checkPermission(); // 正确
Auth.logout(); // 正确
// import { Auth } from './auth'; // 错误!因为导出时没有一个叫 Auth 的东西
03.代码示例
a.代码1
import tab from './tab';
import { VueInstanceAuth } from './auth';
import cache from './cache';
import { VueInstanceDownload } from './download';
import { App } from 'vue';
import { VueInstanceModal } from './modal';
b.代码2
// 1. './tab' 文件里使用了 `export default`
import tab from './tab';
// 2. './auth' 文件里有 `export const VueInstanceAuth = ...`
import { VueInstanceAuth } from './auth';
// 3. './cache' 文件里使用了 `export default`
import cache from './cache';
// 4. './download' 文件里有 `export const VueInstanceDownload = ...`
import { VueInstanceDownload } from './download';
// 5. 'vue' 这个库里有 `export interface App { ... }` (这是个类型)
import { App } from 'vue';
// 6. './modal' 文件里有 `export const VueInstanceModal = ...`
import { VueInstanceModal } from './modal';
4.2 vue引入js、css
01.方式1:src属性引入
a.vue
<script src="./js/index.js" type="module"></script>
<style scoped src="./css/index.css"></style>
b.优点
简洁明了,一行代码解决
路径相对清晰
Vue 官方支持的语法
可以使用 scoped 属性
c.适用场景
外部文件已经存在
不需要在当前文件中修改样式
希望保持 .vue 文件简洁
02.方式2:@import引入
a.vue
<script>
import componentLogic from './js/index.js'
export default componentLogic
</script>
<style scoped>
@import './css/index.css';
</style>
b.优点
可以混合使用(既引入外部,又写额外样式)
4.3 async函数、箭头函数
01.常用信息1
a.核心原则:async 函数永远返回 Promise
a.无论 return 什么: async 函数的返回值永远是一个 Promise 对象。
return data; -> 返回 Promise.resolve(data)
无 return 或 return; -> 返回 Promise.resolve(undefined)
throw new Error(); -> 返回 Promise.reject(error)
b.箭头函数的隐式返回
a.当箭头函数体只有一行表达式且没有花括号 {} 时,它会自动 return 该表达式的结果。
---
// 以下两种写法在功能上完全等价
// 1. 隐式返回
const myFunc = (file) => handleAsyncUpload(file);
// 2. 显式返回
const myFunc = (file) => {
return handleAsyncUpload(file);
};
---
b.关键点
由于 handleAsyncUpload 是一个 async 函数(返回 Promise),所以 myFunc 无论用哪种写法,最终都会返回这个 Promise。
02.最佳实践:为什么要主动返回 Promise
a.在封装或代理另一个异步函数时,必须确保返回其 Promise。这能为调用方带来三大好处
a.可等待 (await): 调用方可以使用 await 或 .then() 来等待异步操作完成。
---
// 如果不返回 Promise,await 将无效
await myObject.uploadAttachmentBase64(file);
console.log('上传完成!'); // 可以确保在上传结束后才执行
---
b.可捕获错误 (try...catch): 调用方可以捕获异步函数内部抛出的异常。
---
try {
await myObject.uploadAttachmentBase64(file);
} catch (error) {
console.error('上传失败:', error); // 如果不返回 Promise,这里将无法捕获
}
---
c.可获取结果: 如果异步函数 resolve 了一个值(例如文件ID),调用方可以接收它。
---
const fileId = await myObject.uploadAttachmentBase64(file);
---
03.代码建议
a.问题代码 (隐式返回): 语法简洁,但意图不够明确。
---
// provide a set of functions to the component
provide('fileHandlers', {
handleDelayedAttachmentUpload: (file, type) => handleDelayedAttachmentUpload(file, type, fileComposable),
uploadAttachmentBase64: (file, type) => uploadAttachmentBase64(file, type, formComposable, fileComposable),
});
---
b.推荐代码 (显式返回): 代码意图清晰,明确表示“我正在传递一个异步操作的结果”。
---
// provide a set of functions to the component
provide('fileHandlers', {
handleDelayedAttachmentUpload: (file, type) => {
return handleDelayedAttachmentUpload(file, type, fileComposable);
},
uploadAttachmentBase64: (file, type) => {
return uploadAttachmentBase64(file, type, formComposable, fileComposable);
},
});
---
c.结论
在封装 async 函数时,无论是隐式还是显式,务必确保返回原始的 Promise。使用显式 return 可以提升代码的可读性和可维护性。
4.4 async函数、await关键字
00.汇总
a.async用于声明函数
任何使用 `async` 声明的函数都会返回一个 Promise。
即使函数内部没有显式返回 Promise,`async` 函数也会自动将返回值包装在一个已解决的 Promise 中。
b.await用于处理Promise
`await` 关键字会暂停异步函数的执行,直到 Promise 完成。
如果 Promise 被解决,`await` 表达式会返回解决的值。
如果 Promise 被拒绝,`await` 会抛出拒绝的值。
01.一个生动的比喻
a.async 函数:就像你下订单的行为
你告诉店员你要一个汉堡和一杯可乐(执行一个异步函数)。
店员给你一个取餐号(返回一个 Promise)。
这个取餐号承诺你最终会拿到餐品,但不是立刻。你拿到号之后,就可以去旁边玩手机了,不用傻等在柜台。
b.await 关键字:就像你等待取餐的行为
你在座位上玩手机,但时不时会抬头看屏幕上的取餐号。这个“看屏幕等待”的动作就是 await。
await 让你暂停下来,等待你的取餐号被叫到(等待 Promise 完成)。
当屏幕显示你的号码时,你就拿到了汉堡(Promise 的结果)。然后你才能开始吃汉堡(执行后面的代码)。
02.async函数
a.概念
它是一个函数修饰符,用在函数声明或函数表达式前。
b.作用
声明这个函数是一个“异步函数”。
最核心的一点是:async 函数的返回值永远是一个 Promise 对象。
如果函数代码中 return 了一个普通值(如数字、字符串),async 会自动将这个值包装成一个 resolved状态的 Promise。
-----------------------------------------------------------------------------------------------------
async function getNumber() {
return 10; // 实际上返回的是 Promise.resolve(10)
}
-----------------------------------------------------------------------------------------------------
如果函数 return 了一个 Promise,那么该函数的返回值就是那个 Promise。
如果函数内部抛出异常,async 会返回一个 rejected 状态的 Promise。
c.目的
它的主要目的是为了能在函数内部使用 await 关键字。它创建了一个允许 await 暂停执行的环境。
03.await关键字
a.概念
它是一个操作符,用于等待一个 Promise 对象的结果。
b.作用
暂停执行:await 会暂停当前 async 函数的执行,等待它后面的 Promise 对象进入“ settled ”(即 resolved 或 rejected)状态。
获取结果:
如果 Promise 成功 (resolved),await 会“解包”这个 Promise,将其 resolve 的值作为结果返回。
如果 Promise 失败 (rejected),await 会将这个 Promise 的 reject 原因(通常是一个 Error 对象)作为异常抛出。
c.使用限制
await 必须在 async 函数内部使用。 在普通的函数或全局作用域下使用会直接导致语法错误。
(不过,最新的 JS 标准已经支持顶层 await,但这主要用于模块中)。
04.代码示例
a.场景
让我们用代码来看一下没有 async/await 和有 async/await 的区别。
场景:假设我们需要先获取用户信息,再根据用户信息获取他的文章列表。
b.传统 Promise (.then) 写法
function getUser() {
return new Promise(resolve => {
setTimeout(() => resolve({ id: 1, name: 'Alice' }), 1000);
});
}
function getArticles(userId) {
return new Promise(resolve => {
setTimeout(() => resolve(['Article 1', 'Article 2']), 1000);
});
}
// 调用链
getUser()
.then(user => {
console.log('获取到用户:', user);
// 返回新的 Promise 以便链式调用
return getArticles(user.id);
})
.then(articles => {
console.log('获取到文章:', articles);
})
.catch(error => {
console.error('出错了:', error);
});
-----------------------------------------------------------------------------------------------------
缺点:如果调用链很长,就会形成所谓的“回调地狱”或“.then 地狱”,代码横向发展,可读性差。
c.async/await 写法
async function main() {
try {
console.log('开始获取用户...');
const user = await getUser(); // 暂停1秒,直到 getUser 完成
console.log('获取到用户:', user);
console.log('开始获取文章...');
const articles = await getArticles(user.id); // 再暂停1秒,直到 getArticles 完成
console.log('获取到文章:', articles);
} catch (error) {
// 任何一个 await 失败,都会被这里捕获
console.error('出错了:', error);
}
}
main();
-----------------------------------------------------------------------------------------------------
优点:
代码像同步一样直观:从上到下执行,逻辑清晰。
错误处理简单:使用标准的 try...catch 结构,非常统一。
变量传递方便:不需要在 .then 的回调函数之间传递变量。
d.总结
特性 async await
角色 函数修饰符 操作符
功能 1.声明函数为异步 1.暂停 async 函数的执行
2.确保函数总返回一个 Promise 2.等待一个 Promise 的结果
3.创建一个能使用 await 的环境 3.获取 Promise 的成功值或抛出其失败原因
使用位置 函数声明或表达式之前 (async function...) async 函数内部,放在 Promise 对象之前 (await promise)
结果 返回一个 Promise 对象 返回 Promise 的 resolve 值,或者抛出异常
关系 是 await 的“容器”和“环境” 是 async 函数实现同步化写法的“工具”
05.代码示例
a.场景
a.说明
想象一下你的任务清单(JavaScript主线程):
任务1:煮一壶水(一个会阻塞的同步任务)。
任务2:去看两场电影(一个包含多个步骤的 async 函数)。
任务3:打扫房间(另一个普通任务)。
b.没有 async/await 的世界 (纯同步)
你会:
站在厨房死等水烧开(阻塞主线程)。
水烧开后,跑到电影院,买第一场电影的票,看完。
再买第二场电影的票,看完。
最后才回家打扫房间。
问题:在你等待水烧开和看电影的时候,其他所有事都得停下。效率极低。
c.有 async/await 的世界
现在,去看电影() 是一个 async 函数:
async function 看电影() {
console.log('到了电影院,买第一场电影的票...');
await 看完('电影A'); // 暂停`看电影`这个任务,但你(主线程)可以去做别的
console.log('电影A看完了!');
console.log('买第二场电影的票...');
await 看完('电影B'); // 再次暂停`看电影`任务
console.log('电影B也看完了!');
}
d.你的实际行动流程是这样的:
console.log('脚本开始'):你开始执行任务清单。
看电影():你决定去看电影。这个决定本身很快,你拿到了一个“会去看电影”的凭证(Promise),然后立刻就可以去做别的事了。这是 async 的异步性。
console.log('脚本结束,我去打扫房间了'):你发现还有打扫房间的任务,不等电影开始,你先去打扫房间了。主线程没有被阻塞。
与此同时,在电影院(async 函数内部):
你买好了票,开始 await 看完('电影A')。这时,你这个人就“暂停”在电影院里了,等待电影A结束。这是 await 的同步式体验。
电影A结束后,await 完成,你继续执行下一步,打印 电影A看完了!。
然后你开始 await 看完('电影B'),再次“暂停”等待。
电影B结束后,看电影 这个任务才算全部完成。
b.代码证明
a.代码
// 模拟一个耗时的异步任务(比如网络请求)
function longTask(name, duration) {
return new Promise(resolve => {
console.log(`${name} 开始执行...`);
setTimeout(() => {
console.log(`✅ ${name} 执行完毕!`);
resolve();
}, duration);
});
}
// async 函数
async function main() {
console.log("main 函数开始。");
await longTask("任务A", 2000); // 暂停 main 函数的执行,等待 2 秒
await longTask("任务B", 1000); // 再次暂停 main 函数,等待 1 秒
console.log("main 函数结束。");
}
console.log("脚本开始。");
main(); // 调用 async 函数,它会立即返回一个 Promise
console.log("脚本结束。main 函数已调用,但我不会等它。");
b.输出顺序
脚本开始。
main 函数开始。
任务A 开始执行...
脚本结束。main 函数已调用,但我不会等它。 ( 核心证据! 主线程没有被 await 阻塞!)
(等待约2秒后) ✅ 任务A 执行完毕!
任务B 开始执行...
(等待约1秒后) ✅ 任务B 执行完毕!
main 函数结束。
4.5 箭头函数、闭包简化接口
01.核心思想:参数预填充 (Partial Application)
当一个函数需要多个参数,但其中一些参数在当前上下文中是固定的(例如,从 Vue Composables 或 React Hooks 中获取的状态对象),
我们可以使用一个箭头函数来“包装”它,创建一个更简洁的新函数。
这个新函数预先填充(或叫“柯里化”)了那些固定的内部参数,只向外暴露业务真正需要的参数。
02.场景分析:为什么不能直接引用
a.原始函数(需要多个参数)
它既需要业务参数 id,也需要内部依赖 formComposable 等。
// 一个复杂的内部函数,需要4个参数
const loadApplicantDetailWrapper = async (id, formComposable, subTableComposable, fileComposable) => {
// ...
};
b.暴露给外部的接口(期望简化)
我们希望提供一个对象,其方法调用起来尽可能简单。
// ❌ 错误做法:直接引用
const businessLogic = {
// 如果直接引用,调用者必须手动传入所有4个参数
// 这暴露了内部实现,且使用繁琐
loadApplicantDetail: loadApplicantDetailWrapper
};
// 调用方必须这样写:
// businessLogic.loadApplicantDetail(123, formComposable, subTableComposable, fileComposable);
c.正确做法:使用箭头函数包装
// ✅ 正确做法:用箭头函数创建适配器
const businessLogic = {
// 这个箭头函数创建了一个新函数
// 它只接收 id,并自动从当前作用域补全其余参数
loadApplicantDetail: (id) => loadApplicantDetailWrapper(id, formComposable, subTableComposable, fileComposable)
};
// 调用方只需关心业务参数:
// businessLogic.loadApplicantDetail(123);
03.这种模式之所以能工作,依赖于两个关键的 JS 特性
a.闭包 (Closure)
箭头函数 (id) => ... 在被定义时,会捕获并记住其所在作用域中的变量(formComposable, subTableComposable 等)。
当外部调用 businessLogic.loadApplicantDetail(123) 时,即使在不同的地方调用,这个函数依然能访问到当初被“封闭”起来的那些变量。
b.接口封装 (API Encapsulation)
这是优秀软件设计的核心原则。我们通过包装,为外部消费者提供了一个稳定且简洁的 API。
消费者无需关心函数内部需要多少个辅助对象,只需提供核心业务数据 id 即可。这使得重构内部逻辑时,不会影响到外部调用者。
04.结论与最佳实践
当需要将一个具有复杂签名的函数暴露给外部时,优先考虑使用箭头函数进行包装,以简化其调用接口。
这个模式是依赖注入 (Dependency Injection) 的一种轻量级实现,
非常适用于在 Vue Composables、React Hooks 或任何需要组合逻辑的场景中创建干净的、可重用的业务函数。
5 design
5.1 倒计时:3种
00.汇总
setInterval
setTimeout
requestAnimationFrame
01.setInterval
function example1(leftTime) {
let t = leftTime;
setInterval(() => {
t = t - 1000;
console.log(t);
}, 1000);
}
example1(10);
02.setTimeout
function example2(leftTime) {
let t = leftTime;
setTimeout(() => {
t = t - 1000;
if (t > 0) {
console.log(t);
example2(t);
}
console.log(t);
}, 1000);
}
03.requestAnimationFrame
function example4(leftTime) {
let t = leftTime;
function start() {
requestAnimationFrame(() => {
t = t - 1000;
setTimeout(() => {
console.log(t);
start();
}, 1000);
});
}
start();
}
5.2 线程池:webWorker
00.Web Worker线程池概述
a.定义
Web Worker线程池是一种在Web应用中管理多个Web Worker线程的机制。
@它允许开发者在浏览器中并行执行JavaScript代码,从而提高应用的性能和响应速度。
b.用途
提高计算密集型任务的执行效率
避免阻塞主线程,保持UI的流畅性
管理和复用多个Web Worker,减少资源消耗
01.Web Worker基础
a.创建Web Worker
使用JavaScript创建一个新的Web Worker。
// worker.js
self.onmessage = function(event) {
const result = event.data * 2; // 示例计算
self.postMessage(result);
};
// main.js
const worker = new Worker('worker.js');
worker.onmessage = function(event) {
console.log('Result from worker:', event.data);
};
worker.postMessage(10);
b.通信机制
主线程和Web Worker之间通过`postMessage`和`onmessage`进行通信。
02.实现线程池
a.线程池设计
线程池通过预先创建一定数量的Web Worker来管理任务的分配和执行。
b.示例实现
class WorkerPool {
constructor(size) {
this.size = size;
this.pool = [];
this.tasks = [];
this.init();
}
init() {
for (let i = 0; i < this.size; i++) {
const worker = new Worker('worker.js');
worker.onmessage = (event) => {
const { resolve } = this.tasks.shift();
resolve(event.data);
this.runNext();
};
this.pool.push(worker);
}
}
runTask(task) {
return new Promise((resolve) => {
this.tasks.push({ task, resolve });
this.runNext();
});
}
runNext() {
if (this.tasks.length === 0) return;
const availableWorker = this.pool.find(worker => worker.idle);
if (availableWorker) {
const { task } = this.tasks[0];
availableWorker.idle = false;
availableWorker.postMessage(task);
}
}
}
// 使用线程池
const pool = new WorkerPool(4);
pool.runTask(10).then(result => console.log('Task result:', result));
c.任务调度
线程池负责调度任务到空闲的Web Worker,并在任务完成后处理结果。
03.优化和注意事项
a.线程数量
根据设备性能和任务复杂度合理设置线程池的大小,避免过多线程导致资源竞争。
b.任务拆分
将大任务拆分为小任务,以便更好地利用线程池的并行能力。
c.错误处理
在Web Worker中添加错误处理机制,确保任务失败时不会影响整个应用。
-----------------------------------------------------------------------------------------------------
// worker.js
self.onerror = function(error) {
console.error('Worker error:', error);
};
d.资源释放
在不再需要Web Worker时,调用`terminate`方法释放资源。
worker.terminate();
5.3 表单提交:不出现跨域
01.回答
因为Post请求,Content-Type支持multipart/form-data
02.原因
a.同源策略
浏览器的同源策略主要限制的是通过JavaScript进行的跨域请求,比如使用XMLHttpRequest或Fetch API
然而,HTML表单提交(通过<form>元素)并不受同源策略的限制,因为它是浏览器原生支持的行为
b.表单提交机制
当用户提交表单时,浏览器会直接向表单的action属性指定的URL发送请求
这种请求是由浏览器自身发起的,而不是通过JavaScript,因此不受同源策略的限制
c.Content-Type
表单提交的Content-Type可以是application/x-www-form-urlencoded、multipart/form-data或text/plain
其中,multipart/form-data通常用于上传文件
虽然Content-Type不同,但这并不是避免跨域问题的原因
d.CORS(跨域资源共享)
对于通过JavaScript发起的跨域请求,服务器需要设置CORS头来允许跨域访问
然而,表单提交不需要CORS,因为它不受同源策略的限制
5.4 刷新页面数据:webSocket、sse
00.总结
a.WebSocket
双向通信
b.Server-Sent Events (SSE)
单向数据流
01.使用 WebSocket 实现实时数据更新
a.设置 WebSocket 服务器
// 安装 ws 库:npm install ws
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
console.log('Client connected');
// 模拟向客户端发送数据
setInterval(() => {
const data = JSON.stringify({ message: 'Hello, client!', timestamp: new Date() });
ws.send(data);
}, 1000);
ws.on('close', () => {
console.log('Client disconnected');
});
});
b.在前端连接 WebSocket 服务器
// 创建 WebSocket 连接
const socket = new WebSocket('ws://localhost:8080');
// 连接成功时的回调函数
socket.onopen = () => {
console.log('Connected to WebSocket server');
};
// 接收到消息时的回调函数
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received data:', data);
// 更新页面数据
document.getElementById('message').innerText = data.message;
document.getElementById('timestamp').innerText = data.timestamp;
};
// 连接关闭时的回调函数
socket.onclose = () => {
console.log('Disconnected from WebSocket server');
};
// 连接出错时的回调函数
socket.onerror = (error) => {
console.error('WebSocket error:', error);
};
c.更新页面数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocket Example</title>
</head>
<body>
<h1>WebSocket Example</h1>
<p>Message: <span id="message"></span></p>
<p>Timestamp: <span id="timestamp"></span></p>
<script src="app.js"></script>
</body>
</html>
02.使用 Server-Sent Events (SSE) 实现实时数据更新
a.设置 SSE 服务器
// 安装 express 库:npm install express
const express = require('express');
const app = express();
const port = 8080;
app.get('/events', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// 模拟向客户端发送数据
setInterval(() => {
const data = JSON.stringify({ message: 'Hello, client!', timestamp: new Date() });
res.write(`data: ${data}\n\n`);
}, 1000);
});
app.listen(port, () => {
console.log(`SSE server running at http://localhost:${port}`);
});
b.在前端连接 SSE 服务器
// 创建 EventSource 连接
const eventSource = new EventSource('http://localhost:8080/events');
// 接收到消息时的回调函数
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received data:', data);
// 更新页面数据
document.getElementById('message').innerText = data.message;
document.getElementById('timestamp').innerText = data.timestamp;
};
// 连接出错时的回调函数
eventSource.onerror = (error) => {
console.error('SSE error:', error);
};
c.更新页面数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SSE Example</title>
</head>
<body>
<h1>SSE Example</h1>
<p>Message: <span id="message"></span></p>
<p>Timestamp: <span id="timestamp"></span></p>
<script src="app.js"></script>
</body>
</html>