Java Web
CS :客户端-服务器 架构模式
- 优点:充分利用客户端机器的资源,减轻服务器的负荷
(一部分安全要求不高的计算任务存储任务放在客户端执行,不需要把所有的计算和存储都在服务器端执行,从而能櫛够减轻服务器的压力,也能够减轻网络负荷) - 缺点:需要安装;升级维护成本较高;
BS:浏览器-服务器 架构模式
- 优点:客户端不需要安装;维护成本较低;
- 缺点:所有的计算和存储任务都是放在服务器端的,服务器的负荷较重;在服务端计算完成之后把结果再传输给客户端,因此客户端和服务器端会进行非常频繁的数据通信,从而网络负荷较重;
网页主要由三部分组成:
结构:HTML
表现:CSS
行为:JavaScript
HTML
Chrome浏览器调试工具:右键点“检查”,或按F12
常用标签
标题标签 : < h1 > ~ < h6 >
段落标签:< p > < /p > (两段之间会插入一定的间距)
换行标签:< br >
水平分割线:< hr >
文本格式化标签:
- 加粗:< strong >< /strong >或者 < b >< /b >
- 倾斜:< em >< /em > 或者 < i >< /i >
- 删除线:< del >< /del > 或者 < s >< /s >
- 下划线:< ins >< /ins > 或者 < u >< /u >
分区标签:
- < div > < /div > :一行只能放一个 < div > ,是大分区;
- < span > < /span > :一行可以放多个 < span >,是小分区;
其他布局标签:(用于手机网页制作)
标签名 语义 header 网页头部 nav 网页导航 footer 网页底部 aside 网页侧边栏 section 网页区块 article 网页文章
图像标签:
< img src=”图像URL” / >
< img src=”图像URL” alt=”替换的文字” title=”图像提示文字”/ >
图像标签的属性:
属性之间不分先后顺序,但必须用空格分开
属性采用键值对的格式:key=”value”
属性 属性值 说明 src 图片路径 必须属性 alt 文本 替换文本,图像不能显示时出现的文字 title 文本 提示文本,鼠标放到图像上会浮现的文字 width 像素 设置图像的宽度 height 像素 设置图像的高度 border 像素 设置图像的边框粗细
相对路径: / 指下一级路径, ../ 指上一级路径
绝对路径:\
1 | <!-- 注意最后应该有个斜杠/,以表示是目录 --> |
base标签:可以设置当前页面中所有路径工作时,参照哪个路径来进行跳转。
超链接:< a > 具体为:< a href=”跳转目标” target=”目标窗口的弹出方式” > 文本、图像、音频等(点击样式) < /a>
属性 作用 href 用于指定链接目标的url地址(必须属性) target 用于指定链接页面的打开方式,_self为默认值, _blank为在新窗口打开 如:< a href=”http://www.qq.com" target=”_blank”> 腾讯< /a >
内部链接:则href 后可以直接写相对路径。即两者在同一文件夹。
空链接:href 后写个 # ,即 < a href=”#”> ** < /a >
下载链接:href后写压缩文件的路径
锚点链接:在href属性中,设为 #名字 的形式,如 < a href=”#ZZC”> 哈哈 < /a >,然后在目标处,添加一个id属性=设定的名字,如 < h4 id=”ZZC” >
注释: < !– 注释语句 – > ,快捷键 :ctrl + /
特殊字符:(只记空格符就行,所有字符后都要带个分号 ; )
| 特殊字符 | 描述 | 字符的代码 |
|---|---|---|
| 空格符 |   | |
| < | 小于号 | < |
| > | 大于号 | > |
| & | 和号 | & |
| ¥ | 人民币 | ¥ |
| © | 版权 | © |
| ® | 注册商标 | ® |
| ° | 摄氏度 | ° |
| ± | 正负号 | ± |
| × | 乘号 | × |
| ÷ | 除号 | ÷ |
| ² | 平方2 | ² |
| ³ | 立方3 | ³ |
表格标签
< table>< /table>:是用于定于表格的标签
< tr >< /tr >:用于定义表格中的行,必须在< table >中
< td >< /td >:用于单元格,必须嵌套在< tr >中
< th >< /th >:表示表头单元格(文本内容居中加粗)
表格结构标签:< thead >指表格的头部区域,<tbody>指表格 的主体区域。
合并单元格(属性):跨行合并——rowspan=”合并个数“,跨列合并——colspan=”合并个数”(也可说是占了若干个单元格)
合并步骤:找到目标单元格,写上合并方式,如:< td colspan=”2” >< /td >,然后再删除多余的单元格。
表格属性:
| 属性 | 属性值 | 描述 |
|---|---|---|
| align | legt,center,right | 规定表格相对周围元素的对齐方式 |
| border | 1或 ” “ | 是否有边框,默认为” “,即无边框 |
| cellpadding | 像素值 | 规定单元边沿与内容之间的空白,默认1像素 |
| cellspacing | 像素值 | 规定单元格之间的空白,默认1像素 |
| width | 像素值或百分比 | 规定表格的宽度 |
如:
1 | <table align="center" border="1" cellpadding="0"> |
列表
无序列表:< ul > ,列表项:< li >
有序列表:< ol >
自定义列表:< dl > ,表项头< dt >,表项:< dd >
< ul >和< ol >里只能放< li >, < li >里面可以放任意标签,< dl >里只能放< dt >和< dd >。
表单
表单域(区域):< form >
1 <form action="url地址" method="提交方式" name="名称">
表单域属性:
| 属性 | 属性值 | 作用 |
|---|---|---|
| action | url地址 | 指定接收该表的服务器程序的url地址 |
| method | GET/POST | 设置表单数据的提交方式 |
| name | 名称 | 指定表单名称 |
表单元素:
输入元素:< input type=”属性值” / >
input属性:
属性 属性值 描述 name 自定义 定义input元素的名称 value 自定义 规定input元素的值 checked checked 规定此input首次加载时被选中 maxlength 正整数 规定输入字符的最大长度 注:name和value是每个表单元素都有的属性值,主要给后台人员使用;要求单选按钮和复选框要有相同的name值。
type属性值:
属性 描述 button 定义可点击按钮(多数情况下,用于通过 Javascript启动脚本) checkbox 定义复选框。 file 定义输入字段和“浏览”按钮,供文件上传。和 multiple 搭配可以上传多个文件 hidden 定义隐藏的输入字段。 image 定义图像形式的提交按钮 password 定义密码字段。该字段中的字符被掩码。 radio 定义单选按钮 reset 定义重置按钮。重置按钮会清除表单中的所有数据 submit 定义提交按钮。提交按钮会把表单数据发送到服务器。 text 定义单行的输入字段,用户可在其中输入文本。默认宽度为20个字符。 placeholder 占位符,作提示,常用于需要输入的文本框 < label > :用于绑定一个表单元素,当单击此标签内的文本时,会自动选择对应的表单元素,以增加用户体验。
格式:< label >里的for属性 应该与相关元素的 id属性相同。如:
1
2<label for="zzc"> 文本</label>
<input type=" " id="zzc"/>下拉元素:< select > :定义下拉列表
在< option >中添加selected=”selected”时,为默认选中项
1
2
3<select>
<option> 选项 </option>
</select>文本域元素:< textarea >:定义多行文本输入控件
1 | <textarea> |
CSS
CSS:层叠样式表
CSS书写顺序:
- 浮动 / display
- 盒子模型:margin border padding 宽度高度背景色
- 文字样式
导入方式
内联样式:在标签内使用style属性(耦合较高,较少使用)
1
<div style="color: red"> HELLO CSS </div>
内部样式:定义 < style > 标签,在标签内定义css样式;
(一般写在head标签里,title标签下面)
1
2
3
4
5
6
7
8<style>
/*选择器 { 样式 } */
/*给谁改样式 { 改什么样式 } */
p {
color: red; /*键值对用:分开,每行用;结尾*/
fon-size: 12px;
}
</style>外部样式:定义link标签,引入外部的css文件
1
<link rel="stylesheet" href="demo.css">
1
2
3
4/*demo.css*/
div{
color: red;
}
规范:
选择器、属性名、属性值统一小写;
选择器(标签)和大括号中间带空格;
冒号后带空格
选择器
标签选择器:元素名称{color: red;}
1
div{color: red}
id选择器:#id属性值{color: red;}
注意:id是唯一的,一般与js配合制作动态效果
1
2#name{color: red;}
<div id="name">hello css</div>类选择器:.class属性值{color: red;}
1
2.cls{color: red;}
<div class="cls">helle css</div>通配符选择器:*{ color: red;}
作用:为页面的所有标签 设置样式。
1
2
3
4*{
margin: 0;
padding: 0; /*去除默认的外边距*/
}
更多用法:
后代选择器:选择器1 选择器2 { css }
作用:根据嵌套关系,选择父元素的 后代中满足条件的元素。
注:后代包括 儿子、孙子….
1
2
3div p{
....
} /* 选择div下的p标签 */子代选择器: 选择器1 > 选择器2{css}
作用:选择父元素的子代元素。
注:子代只包含儿子
并集选择器: 选择器1,选择器2{css}
作用:用逗号隔开,同时选择多组标签,设置相同的样式。
注:一般写法是 每个选择器写一行,提高代码的可读性。
交集选择器:选择器1选择器2 { css }
作用:选中页面中同时满足多个选择器的标签
注:选择器连着写,且标签选择器优先写在前面。
hover伪类选择器:选择器:hover {css}
作用:在鼠标悬停在元素上时 会出现的状态样式
字体和文本样式
字体样式
字体大小:font-size
取值:数字+px
谷歌浏览器默认文字大小是16px
1
2
3p{
font-size: 30px
}粗细:font-weight
取值:normal,bold;(指正常和加粗)
或取100到900的整百数,正常是400,加粗是700
样式:font-style (是否倾斜)
取值:normal,italic(正常和倾斜)
类型系列:font-family
取值:微软雅黑,黑体………等等,最后应该加sans-serif(无衬线字体)(从设置的第一种字体开始选,没有就选择下一个电脑有的字体)
字体font连写:
取值:font:style weight size family(可以省略前两个值,为默认值)
复合属性:一个属性后面写多个值的写法
1
font: italic 700 66px 宋体
文本样式
文本缩进:text-indent
取值:数字+px 或 数字+em(1em=当前标签的大小,推荐)
1
2
3p{
text-indent: 2em /*缩进两字符*/
}文本水平对齐方式:text-align
属性值:left,center,right;分别是左对齐,居中,右对齐
注:text-align 其实是内容对齐,不仅仅用于文本, 能用于 文本、span标签、a标签、input标签、img标签 。不过需要在以上元素的父元素设置,如
。文本修饰:text-decoration
取值:underline,line-through,overline,none;分别是下划线(常用),删除线,上划线,无装饰线(常用)。
line-height行高
作用:控制一行的上下行间距
取值:数字+px 或 倍数(当前标签font-size的倍数)
注意:
如果同时设置了行高和font连写,注意覆盖问题
font:style weight size/line-height family
1 font:ita1ic 700 66px/2 宋体;
标签水平居中: margin:0 auto
margin:0 auto 一般用于有固定宽度的盒子,如果没有宽度,会默认占满父元素的宽度。
背景
背景色:background-color:
背景图:background-image: url(‘…..’)
背景平铺:background-repeat:
取值 效果 repeat 默认,水平和垂直方向平铺 no-repeat 不平铺 repeat-x 沿水平方向平铺 repeat-y 沿垂直方向平铺
背景位置:background-position:x值 y值;
方向名词
- 水平:left,center,right
- 垂直:top,center,bottom
数字+px(坐标)
背景相关属性连写
顺序无要求,推荐顺序:background:color image repeat position
元素显示模式
< >可以叫标签,标记,元素。
块级元素
独占一行,宽度默认是父元素的宽度,高度由内容撑开,可以设置宽高
如:div,p,h系列,ul,li,dl,dt,dd,form,header
行内元素
一行可以显示多个,宽度和高度默认由内容撑开,不可以设置宽高
如:a,apar,b,u,i,s,strong,ins,em,del
行内块元素
一行可以显示多个,可以设置宽高
如:image,input,textarea,button,select
元素显示模式转换:
作用:改变元素默认的显示特点,让元素符合布局要求
语法:
| 属性 | 效果 |
|---|---|
| display: block | 转成块级元素 |
| display: inline-block | 转成行内块元素 |
| display: inline | 转成行内元素 |
元素的嵌套规范
块级元素一般作为大容器,可以嵌套:文本、块级元素、行内元素、行内块元素等等…
但是:p标签中不要嵌套div、p、h等块级元素
a 标签内部可以嵌套任意元素
但是:a标签不能嵌套a标签
CSS特性
继承性:子元素默认继承父元素的样式特点
控制文字属性的都可以继承,如:
color,font- ,text- ,line-height 。。。。
但有例外:a标签的color有自己的蓝色,h系列的font-size有自己的字体大小,都会继承失效。
层叠性:覆盖,后面的覆盖前面的
优先级:
继承 < 通配符选择器 < 标签选择器 < 类选择器 < id选择器 < 行内样式 < !important
!important可以提升选择器的优先级到最高,写在属性值的后面,分号的前面。
注:继承的优先级不能用 !important提高,它的优先级最低。
盒子模型
页面中的每一个标签,都可看做是一个“盒子”,通过盒子的视角更方便的进行布局。
盒子构成
内容区域 content
内边距区域 padding
取值:可以四个,三个,两个,一个值
padding: 10px 20px 30px 40px (对应上 右 下 左,即逆时针开始数)
padding: 10px 20px 30px (对应上 左右 下,也是逆时针)
padding: 10px 20px(对应上下 左右,也是逆时针)
边框区域 border
如:border:10px solid red ——>10像素,直线,红色(粗细,线段样式,颜色)
如果只需要一边的边框线,则border-方向即可,如:border-left/ right/ top/ bottom
线段:直线solid,dashed虚线,dotted点线(其他的线用不着,需要的用图片)
快捷键:bd
外边距区域 margin
自动内减
box-sizing:border-box
给盒子设置border或paddingl时,盒子会被撑大,需要再次设置参数,手动计算麻烦,所以有了自动内减,浏览器会自动计算,在内容中减去相应大小。
清除默认的内外边距
margin:0;
padding:0;
淘宝网的:
1
2
3
4 blockquote,body,button,dd,dl,dt,fieldset,form,h1,h2,h3,h4,h5,h6,hr,input,legend,li,ol,p,pre,td,textarea,th,ul{
margin:0;
padding:0;
}京东的:
1
2
3
4 *{
margin: 0;
padding: 0;
}
一些问题
塌陷现象:
场景:相互嵌套的块级元素,子元素设置了margin-top,会同样作用在父元素上。致使父元素一起往下移动。
解决:
- 给父元素设置border-top或者padding-top(分隔父子元素的margin-top)
- 给父元素设置 overflow: hidden
- 转换成行内块元素
- 设置浮动
浮动
结构伪类选择器
根据元素在HTML的结构关系查找元素。常用于查找某父级选择器的子元素。
优势:减少对HTML中类的依赖,有利于保持代码的整洁。
| 选择器 | 说明 |
|---|---|
| E:first-child{} | 匹配父元素中第一个子元素,且是E元素 |
| E:last-child{} | 匹配父元素中最后一个子元素,且是E元素 |
| E:nth-child(n){} | 匹配父元素中第n个子元素,且是E元素 |
| E:nth-last-child(n){} | 匹配父元素中倒数第n个子元素,且是E元素 |
n可以写成公式,如4n+1,则会选中第5个,第9个….
伪元素
一般在页面的非主体内容可以使用伪元素。
| 伪元素 | 作用 |
|---|---|
| ::before | 在父元素内容的最前添加一个伪元素 |
| ::after | 在父元素内容的最后添加一个伪元素 |
注:必须设置content属性才能生效,伪元素默认是行内元素
例:
1 | <head> |
浮动特点
浮动元素会脱离标准流(简称:脱标),在标准流中不占位置
标准流:又称文档流 ,指浏览器在渲染网页时默认采用的一套排版规则。
浮动元素比标准流高半个级别,可以覆盖标准流中的元素
浮动找浮动,下一个浮动元素会在上一个浮动元素后面左右浮动
浮动元素有特殊的显示效果
- 一行可以显示多个
- 可以设置宽高
注:浮动的元素不能使用text-align: center或者margin: 0 auto 设置
清除浮动
浮动带来的问题:如果子元素浮动了,此时子元素不占位置,不能撑开标准流的块级父元素。
解决:
直接设置父元素的高度
优点:简单
缺点:有些布局中不能固定父元素高度
额外标签法
在父元素内容的最后添加一个块级元素,并对其设置 clear: both。
缺点:添加了额外标签,会使HTML结构变复杂
单伪元素清除法
用伪元素替代了额外标签。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15/*基本写法*/
.clearfix::after{ /*clearfix常用作需要清除浮动的元素的类名*/
content: '';
display: block;
clear: both;
}
/*补充写法*/
.clearfix::after{
content: '';
display: block;
clear: both;
/*补充的部分是为了在网页中看不见伪元素*/
height: 0;
visibility: hidden;
}双伪元素清除法
1
2
3
4
5
6
7
8.clearfix::before,
.clearfix::after{
content: '';
display: table;
}
.clearfix::after{
clear: both;
}优点:可以在项目中直接使用,直接给标签加类clearfix即可清除浮动。
设置overflow:hidden
直接给父元素设置overflow:hidden即可。
优点:方便
定位
可以让元素自由的摆放在网页的任意位置,一般用于盒子之间的层叠情况
应用:定位之后的元素层级最高,可以层叠在其他盒子上面,也可以让盒子始终固定在屏幕中的某个位置。
定位种类
属性名:position:
属性值:
| 定位方式 | 属性值 |
|---|---|
| 静态定位 | static(加不加效果一样) |
| 相对定位 | relative |
| 绝对定位 | absolute |
| 固定定位 | fixed |
| 粘性定位 | sticky |
偏移值:水平和垂直方向各选一个即可(都写的话以left和top为准)
| 方向 | 属性名 | 属性值 | 含义 |
|---|---|---|---|
| 水平 | left | 数字px | 距离左边的距离 |
| 水平 | right | 数字px | 距离右边的距离 |
| 垂直 | top | 数字px | 距离上边的距离 |
| 垂直 | bottom | 数字px | 距离下边的距离 |
属性值 也可以用 % 来设置,这样就是以参照物来看,如 50%就是左边界在参照物的中间。
相对定位——position: relative
相对于非静态定位的父元素进行移动。
1
2
3
4
5.box{
position: relative;
left: 100px;
top: 200px;
}- 原位置仍在页面 中占位置(没有脱标)
绝对定位——position: absolute
就近找已经定位的父级为参照进行定位,没有定位的父级,则默认相对于浏览器可视区域(即
)进行移动。(子绝父相)原位置在页面中不占位置(已经脱标)
绝对定位的盒子模式具备行内块特点
例题:让元素的位置处于浏览器或参照物正中间。
1
2
3
4
5
6
7
8
9
10
11
12
13.box{
position: absolute;
left: 50%;
margin-left: -150px;/*宽的一半*/
top: 20%;
margin-top: -200px;/*高的一半*/
/*transform:translate(-50%,-50%); 上面的margin位移也可以写成这个,位移自己的一半*/
width: 300px;
heght: 400px
background-color: pink;
}固定定位——position: fixed
特点:
脱标,不占位置;
参照浏览器窗口改变位置;
具备行内块特点
粘性定位——position: sticky
可看作是相对定位和固定定位的混合,允许被定位的元素表现的像相对定位一样,直到滚动至某个阈值点为止,之后变为固定。(如:顶部导航栏最多滚动至顶部起10px,之后固定)
必须指定 top, right, bottom, left 四个阈值其中之⼀,粘性定位才生效。
1
2
3
4
5.xxx {
position: sticky;
top: 30px;
left: 30px;
}
层级关系
不同布局:标准流 < 浮动 < 定位
而相对,绝对,固定三种定位方式层级相同,谁写在下就覆盖其他的。
一些应用
元素居中:
- 先设定width或height;
- 将left,right 或 top,bottom 距离设置为0;
- 将left,right 或 top,bottom 的margin设置为auto
绝对定位和固定定位中,margin为auto时,会自动吸收剩余空间。
元素的重叠:
设置元素的堆叠顺序: z-index
绝对定位、固定定位的补充:
- 如果没有指定宽高,默认大小是内容的大小;
- 一定是块盒;
- 一定不会浮动;
- 不会发生外边距合并;
- 会盖住下面常规流的所有内容(浮动元素只会盖住盒子,不会盖住里面的文字图片,因为浮动元素最初是做文字环绕效果的)
装饰
浏览器解析 行内 和 行内块 时当作文字,会根据基线对齐,所以导致常常图片或按钮之类的与文本框或背景对不齐。
垂直对齐
属性名:vertical-align
属性值:
| 属性值 | 效果 |
|---|---|
| baseline | 默认,基线对齐 |
| top | 顶部对齐 |
| middle | 中间对齐 |
| bottom | 底部对齐 |
光标类型
设置鼠标光标在元素上时显示的样式。
属性名:cursor
属性值:
| 属性值 | 效果 |
|---|---|
| default | 默认值,通常是箭头 |
| pointer | 小手,提示可以点击 |
| text | 工字型,提示可以选择文字 |
| move | 十字光标,提示可以移动 |
边框圆角
场景:让盒子四个角变得圆润,增加页面细节。
属性名:border-radius
常见取值:数字+px、百分比(指圆角半径)
赋值规则:从左上角开始,顺时针赋值,没有赋值的看对角
画一个正圆:
盒子为正方形,设置圆角为盒子宽高的一半,即 border-radius: 50%
按钮胶囊:
盒子为长方形,设置圆角为盒子 高度 的一半,即 border-radius: 50%
也可以使用clip-path属性:
1 | clip-path: circle(45%) |
溢出部分显示
溢出部分:指盒子内容部分所超出盒子范围的区域。
overflow用于控制内容溢出部分的显示效果,如:显示/隐藏滚动条。
属性名:overflow
属性值:
| 属性值 | 效果 |
|---|---|
| visible | 默认值,溢出部分可见 |
| hidden | 溢出部分隐藏 |
| scroll | 无论是否溢出,都显示滚动条 |
| auto | 根据是否溢出,自动显示或隐藏滚动条 |
元素本身隐藏
让某元素本身在屏幕中不可见,如:鼠标hover之后元素隐藏,用于隐藏/显示子菜单之类的场景
属性:
visibility: hidden
占位置的隐藏效果,用的不多
display: none
脱标的隐藏效果(不占位置)
display:black 显示元素
元素整体透明
让某元素(包括内容)一起变透明。
属性名:opacity
属性值:0~1之间的数字——0为完全透明
弹性盒子display:flex
flex用于 按行或按列布局元素(一维布局)。
设置容器为flex后,其子元素的 float 、 clear 、 vertical-align 属性将失效。
flex容器的属性:
flex-direction :主轴反向
1
2
3
4
5
6.box {
flex-direction: row; /* 默认值,横向从左⾄右排列 */
flex-direction: row-reverse; /* 横向从右⾄左排列 */
flex-direction: column; /* 纵向从上到下排列 */
flex-direction: column-reverse; /* 纵向从下到上排列 */
}flex-wrap: 超出父容器的子容器的排列方式
1
2
3
4
5
6
7flex容器 {
flex-wrap: wrap; /* ⾥⾯的flex项溢出时会⾃动换⾏ ,第⼀⾏在上⽅*/
flex-wrap: wrap-reverse; /* ⾥⾯的flex项溢出时会⾃动换⾏ ,第⼀⾏在下⽅*/
}
flex项 {
flex: 200px
}flex-flow:上两者的合并写法
1
2
3
4
5
6
7
8{
flex-flow: row wrap;
}
/* 等价于 */
{
flex-direction: row;
flew-wrap: wrap;
}justify-content:子容器在主轴的排列方向和空间分配
1
2
3
4
5
6
7
8
9flex容器 {
display: flex;
justify-content: stretch; /* ⾃动⼤⼩(auto-sized)的flex项会拉伸 */
justify-content: flex-start; /* 默认值,flex项从flex容器起始线开始排列 */
justify-content: flex-end; /* flex项从flex容器终⽌线开始排列 */
justify-content: center; /* flex项在flex容器中间排列 */
justify-content: space-around; /* 每个flex项左右空间相等 */
justify-content: space-between; /* flex项间的间隔相等,第⼀个和最后⼀个flex项靠在左右两端 */
}align-items:子容器在交叉轴的排列方向
1
2
3
4
5
6
7flex容器 {
display: flex;
align-items: stretch; /* 默认值,flex项会拉伸⾄填满flex容器交叉轴 */
align-items: flex-start; /* flex项依flex容器的顶部对⻬ */
align-items: flex-end; /* flex项依flex容器的底部对⻬ */
align-items: center; /* flex项在交叉轴居中对⻬ */
}align-content:多主轴情况下⼦容器在交叉轴上的对齐和空间分配
1
2
3
4
5
6
7
8flex-item {
align-content: stretch; /* 默认值,flex项拉伸占满整个交叉轴 */
align-content: flex-start; /* flex项依flex容器的顶部对⻬ */
align-content: flex-end; /* flex项依flex容器的底部对⻬ */
align-content: center; /* flex项在交叉轴居中对⻬ */
align-content: space-between; /* 在交叉轴两端对⻬,轴线之间的间隔平均分配 */
align-content: space-around; /* 轴线间的间隔⽐轴线与边框的间隔⼤⼀倍 */
}
flex项的属性:
flex-grow:flex项在可用空间中的拉伸比例(沿主轴方向增长尺寸)
flex-shrink:flex项超出主轴时的压缩比例
flex-basis:flex项在不伸缩情况下的原始尺寸(默认auto,采用元素内容的尺寸)
flex:flex-grow, flex-shrink, flex-basis 的简写属性
1
2
3
4
5
6flex项 {
flex: initial; /* 默认值,相当于 flex: 0 1 auto; 即不可拉伸,可以收缩 */
flex: auto; /* 相当于 flex: 1 1 auto; 即可以拉伸也可以收缩 */
flex: none; /* 相当于 flex: 0 0 auto; 即不可拉伸也不可收缩 */
flex: 正整数; /* 相当于 flex: 1 1 0; 即可以在 flex-basis==0的基础上伸缩
}1
2
3
4
5
6
7
8/* 每个flex项占用空间相等, 最小值是200px */
flex项 {
flex: 1 200px;
}
/* 第二个flex项是其他的2倍 */
flex项:nth-of-type(2) {
flex: 2 200px;
}order:flex项的排列顺序, 默认为0,值越小越靠前
1
2
3
4
5
6
7
8/* 第一个按钮移到主轴的末尾 */
button:first-child {
order: 1;
}
/* 第二个按钮移动到主轴的最前面 */
button:last-child {
order: -1;
}align-self:可使自己覆盖flex容器上设置的 align-items 值
1
2
3
4flex-item {
align-self: auto; /* 默认值,继承flex容器的align-items值 */
align-self: flex-start / flex-end / center / stretch; /* 与align-items值的含义类似 */
}
网格布局display:grid
grid布局是二维布局,能把内容按照行与列的格式排版。
基本概念:
- 网格容器:采用网格布局的区域;
- 网格项:容器内的子元素;
- 网格线:网格的水平和垂直的分界线;
- 网格轨道:相邻两条网格线之间的空间,即行、列。
- 网格单元格:被网格线围起来的最小单元格。
- 网格区域:任意四条网格线组成的空间,包含若干单元格。
使用:
定义:默认创建一列或一行的网格
1
2
3
4.container {
display: grid; /* 设置⽹格布局,一列 */
display: inline-grid; /* 设置⽹格布局,一行 */
}设置行列:
1
2
3
4
5.container {
display: grid;
grid-template-columns: 200px 200px 200px;/*设置3个列,宽度各为200px*/
grid-template-rows: 200px 200px 200px;/*设置3个行,行高各为200px*/
}1
2
3
4.container {
display: grid;
grid-template-columns: 2fr 1fr 1fr /*设置3个列,宽度比为2:1:1*/
}可以混搭:
1
2
3
4container {
display: grid;
grid-template-columns: 100px auto 2fr;
}重复构建行列:repeat()
1
2
3
4.container {
display: grid;
grid-template-columns: repeat(3, 2fr 1fr); /*相当于2fr 1fr 2fr 1fr */
}自动多列填充: auto-fill
1
2
3
4container {
display: grid;
grid-template-columns: repeat(auto-fill, 100px); /*包含多个100px列宽的网格,将容器填满*/
}行列最小尺寸: minmax()
1
2
3
4container {
display: grid;
grid-template-columns: 1fr 1fr minmax(200px, 1fr); /*列宽不小于200px,不大于1fr*/
}网格间隙:gap
1
2
3
4container {
display: grid;
grid-template-columns: 2fr 1fr
gap: 20px 50px; /*行间距20px,列间距50px*/网格区域: grid-template-areas
- 需要填满网格的每个格子
- 对于某个横跨多个格子的元素,重复写上那个元素grid-area属性定义的区域名字
- 所有名字只能出现在⼀个连续的区域,不能在不同的位置出现
- ⼀个连续的区域必须是⼀个矩形
- 使用 . 符号,让⼀个格子留空
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25.container {
display: grid;
grid-template-areas: /* 3⾏2列 */
"header header"
"sidebar content"
"footer footer";
grid-template-columns: 1fr 3fr;
grid-gap:20px;
}
header {
grid-area: header; /* 指定<header>元素的位置 */
}
article {
grid-area: content; /* 指定<article>元素的位置 */
}
aside {
grid-area: sidebar;
}
footer {
grid-area: footer;
}内容对齐:
- 设置整个内容区在容器的对齐方式(在容器中设):
- justify-content 属性是整个内容区域在容器里面的水平位置(左中右),
- align-content 属性是整个内容 区域的垂直位置(上中下)。
- place-content 属性是 align-content 属性和 justify-content 属性的合并简写 形式。
- 设置所有网格项在单元格中的对齐方式(在容器中设):
- justify-items 属性设置单元格内容的水平位置(左中右),
- align-items 属性设置单元格内容的垂直位置 (上中下)。
- place-items 属性是 align-items 属性和 justify-items 属性的合并简写形式。
- 设置某个网格项在单元格中的对齐方式(在网格项中设)
- justify-self 属性设置单元格内容的水平位置(左中右),
- align-self 属性设置单元格内容的垂直位置(上 中下)。
- place-self 属性是 align-self 属性和 justify-self 属性的合并简写形式。
- 设置整个内容区在容器的对齐方式(在容器中设):
居中方法总结
水平居中
行盒:
1
text-align: center;
块盒:
子元素宽度固定:
方法1: margin: 0 auto
1
2
3
4.inner {
width: 200px; /* 宽度固定 */
margin: 0 auto; /* ⽔平居中 */
}用绝对定位:
1
2
3
4
5
6
7
8
9
10
11
12
13.container {
width: 500px;
height: 500px;
position: relative;
}
.inner {
width: 200px; /* 宽度固定 */
height: 100px;
position: absolute;
left: 50%; /*定位到中线*/
margin-left: -100px /*外边距设为宽度的一半*/
}或者:设定元素的宽度 width ,然后设置左右两边的定位坐标为0(即: left: 0; right: 0 ),并将左右 margin 设 置为 auto 。
1
2
3
4
5
6
7
8
9div { /* 假定 div 为 p 的⽗元素 */
position: relative;
}
p {
width: 200px;
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
子元素宽度未知:
将子元素设置为行内块元素,然后⽗元素设置 text-align: center 。
1
2
3
4
5
6
7
8.container {
width: 500px;
height: 500px;
text-align: center; /* 内容居中 */
}
.inner{
display: inline-block; /* 设置为⾏块盒 */
}
多个块盒元素:
方法一: 使⽤ display: inline-block 和 text-align: center
1
2
3
4
5
6
7
8
9
10.container {
width: 500px;
height: 500px;
text-align: center; /* 内容居中 */
}
.inner{
display: inline-block; /* 设置为⾏块盒 */
width: 100px;
height: 150px;
}使用flex布局:
弹性盒⼦中的 Flex items的⽔平居中可使⽤ justify-content 属性设置,使其中的 Flex items沿主轴对⻬。
1
2
3
4
5
6
7
8
9
10.container {
width: 500px;
height: 500px;
display: flex;
justify-content: center /* 在主轴上居中 */
}
.inner{
width: 100px;
height: 150px;
}
垂直居中
单行行内元素:
将子元素的行高( line-height )等于高度( height )就可以了。
1
2
3
4
5
6
7
8
9.container {
width: 500px;
height: 500px;
}
.inner{
display: inline-block;
height: 150px;
line-height: 200px;
}多行元素:
使用绝对定位:
1
2
3
4
5
6
7
8
9
10
11
12.container {
width: 500px;
height: 500px;
position: relative;
}
.inner{
width: 100px;
height: 100px; /*高度固定*/
position: absolute;
top: 50%; /*定到中线*/
margin-top: -100px; /*外边距设为高度的一半*/
}或设定元素的高度 heigh ,然后设置上下两边的定位坐标为0(即: top: 0; bottom: 0 ),并将上下 margin 设置为 auto 。
1
2
3
4
5
6
7
8
9
10
11div { /* 假定 div 为 p 的⽗元素 */
position: relative;
}
p {
height: 50px;
position: absolute;
top: 0;
bottom: 0;
margin-top: auto 0;
}使用flex布局:
1
2
3
4
5
6
7
8
9
10.container {
width: 500px;
height: 500px;
display: flex;
align-items: center;
}
.inner{
width: 100px;
height: 100px;
}
图片文字并排:
使用flex布局:(与上一段代码一致)
给图片设置 vertical-align: middle ,文字盒子设置为 display: inline-block
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18* {
margin: 0;
padding: 0;
}
.container {
width: 500px;
height: 500px;
line-height: 500px;
}
.container img {
display: inline-block;
vertical-align: middle;
}
.container p {
display: inline-block;
}
CSS响应式设计
一些应用
a标签去除下划线:
1
2
3a {
text-decoration: none;
}
JavaScript
跨平台、面向对象的脚本语言,用来控制网页行为,网页交互。
js引擎运行
分两步:预解析,再执行代码
- 预解析:js引擎会把js里面的所有var还有function提升到当前作用域的最前面;
- 变量预解析(变量提升):把所有的变量声明提升到当前作用域最前面,但不提升赋值操作。
- 函数预解析(函数提升):把所有的函数声明提升到当前作用域最前面,不会调用函数。
- 代码执行:按照代码书写顺序从上往下执行。
引入方式
内部脚本:定义在HTML页面中
1 | <script> |
JS代码必须在< script >< /script >之间,可以放置在任何地方,任意数量。一般放在< body >的底部,可改善显示速度,因为脚本执行会拖慢显示。
外部脚本:作为外部JS文件引入
1 | <script src="文件路径"></script> |
外部脚本不能包含< script >标签,< script >不能自闭合
基础语法
与java相似,不过语句末的分号;可有可无。
输出语句
1 | window.alert(" ... ") //写入警告框 |
变量
使用 var 关键字来声明变量。(variable)
- 作用域:全局变量;
- 变量可以重复声明;
1 | var test = 20; |
JS是弱类型语言,变量可以存放不同类型的值
ES6新增 let 关键字来定义变量,其变量只在let所在的代码块内有效,且不允许重复声明。
ES6新增 const 关键字来声明一个只读的常量。
- 全局变量 在浏览器关闭时销毁;
- 局部变量 在程序运行完毕后就销毁;
常量
使用const声量,必须初始化,不能被修改。
1 | const birthday = '11.22.1922' |
数据类型
原始类型:
number:数字(整数,小数,NaN(Not a Number))
.toFixed(x) —— 格式化一个数字,x是要保留的小数点后位数。
string:字符,字符串,单双引号皆可
boolean:布尔类型
null:空
undefined:变量未初始化时的默认值
使用typeof运算符可以获取数据类型
1
typedef 变量名
引用类型
类型转换
- 其他类型转为number:
- string:按字符的字面值转为数字,如果字面值不是数字,则转为NaN。一般使用正负号 或 parseInt 进行转化。
- boolean:true 转为 1, false 转为 0.
- 其他类型转为boolean:
- number:0和NaN转为 false,其他转为 true
- string:空字符串转为 false,其他转为true
- null:转为false
- nudefined:转为false
运算符
关系运算符
- == :先判断类型是否一致,不一致会进行类型转换再比较两个值;
- === :(全等于)如果类型不一致,会直接返回false,类型一致时才会比较两个值。
函数
函数通过function关键字进行定义。
1 | function functionName(参数列表){ |
arguments
是当前函数的内置对象,存储了传递的所有实参。其展示形式是一个伪数组,可以遍历,有lenght属性,有索引,没有push,pop等方法。
内部函数可以访问外部函数的变量:采用链式查找的方式来决定取哪个值,即找最近的——作用域链。
匿名函数:没有名字的函数,通常与事件处理程序一起使用。
1 | const myButton = document.querySelector('button'); |
箭头函数: 尽量使用箭头函数(更简洁)
1 | // 前面的匿名函数也可以写成如下的箭头函数的形式 |
常用对象
Array对象
Array用于定义数组
创建方法:
- 一、var 变量名 = new Array(元素列表);
1
var arr = new Array(1,2,3);
- 二、var 变量名 = [元素列表];
1
var arr = [1,2,3]
属性:
length:数组中元素的个数
push:末尾添加一个元素; unshift:开头添加一个元素
1
2var arrs = [1,2,3];
arrs.push(10);pop:末尾删除一个元素;shift:开头删除一个元素
indexof:查找元素
splice:删除多个元素
1
arrs.splice(0,2); //参数:从0号位开始删,删2个
遍历数组:
1
2
3
4const array = [1,2,3];
for(const e of array){
console.log(e);
}字符串和数组的转化:
1
2
3
4
5
6const data = 'aa,bb,cc,dd';
const array = data.split(',');
const string = array.join(',');
cosnt string2 = array.toString();
String对象
创建方法:
var 变量名 = new String(s);
var 变量名 = s;
1
var str = new String("hello");
属性:
length:字符串的长度
方法:
string[0]:查看指定下标位置的字符
.chaAt():返回在指定位置的字符
.indexOf():检索指定字符串
.trim():去除字符串前后的空格
.concat(str1,str2….):链接多个字符串,等效+,+更常用
.substr(start,lenght):从start开始,取lenght个字符
.splice(a,b):提取下标a到b-1的字符串,只有a下标的话,取下标a之后剩下的字符串。
.replace(a,b):将a替换成b
.includes(string):是否包含某字符串
.startswith(string):是否以某字符串开头
.endswith(string):是否以某字符串结束
Date对象
和Math不同,Date对象需要实例化后才能使用。
Date() ——(没有参数)返回系统的当前时间
使用 .valueOf() 或 getTime() 来获得总的毫秒数(时间戳),从1970年1月1日到现在。
有更常用的写法:将 +new Date() 实例化,也可获得总的毫秒数。
H5新增的获取方法: Date.now()
| 方法名 | 说明 |
|---|---|
| getFullYear() | 获取当年 |
| getMonth() | 获取当月(0~11) |
| getDate() | 获取当天日期 |
| getDay() | 获取星期几(周期0 到 周六6) |
| getHours() | 当前小时 |
| getMinutes() | 当前分钟 |
| getSeconds() | 当前秒 |
自定义对象
格式:
var 对象名 = {属性名 : 属性值,… 函数名 : function(参数){} …. };
或者使用:var 对象名 = new Object(); 来创造一个空的对象,再利用追加属性的方法,往里面添加数据。
使用构造函数来创建对象:
function 构造函数名(参数值){ this.属性 = 值; this.方法 = function(){} }————调用时,用“new 构造方法名” 赋值给变量。
注:构造函数名字首字母要大写
1 | var person = { |
调用属性:对象名.属性名 或者 对象名[‘属性名’]
调用方法:对象名.方法名
遍历对象属性:for…in可以对数组或对象进行遍历。
1 | for(var k in obj){ |
JS执行机制
JS是单线程的,即同一时间只能做一件事,这意味着所有任务需要排队,如果JS执行时间过长,会导致页面渲染不流畅。
于是,HTML5提出Web Worker标准,允许JS脚本创建多个线程。于是就有了同步和异步。
同步
程序之间的执行顺序与排列顺序一致,即同步。
异步
执行某程序的同时可以执行另一个程序,即异步。
同步任务
同步任务都在主线程执行,形成一个执行栈
异步任务
JS的异步是通过回调函数实现的,一般而言,异步任务有三种:
- 普通事件:如click,resize
- 资源加载:如load,error
- 定时器:如setInterval,setTimeout
异步任务相关的回调函数会添加到任务队列(消息队列)中。
执行机制:
先执行执行栈中的同步任务;
遇到异步任务,将其回调函数放入任务队列中;
异步任务会先交给异步进程处理,只有异步任务的事件发生,或如click点击了,setInterval的时间到了,才会把相关的回调函数放入任务队列。
等执行栈中的所有同步任务执行完毕后,系统会按次序读取任务队列中的异步任务,使其进入执行栈,开始执行。
事件循环:主线程不断的重复获取任务,执行任务,再获取任务,反复循环。
BOM对象
Browser Object Model 浏览器对象模型,JS将浏览器的各个组成部分封装成对象:Window,Navigator,Screen,History,Location。
Window对象:浏览器窗口对象
获取:直接使用window,其中window. 可以省略。
1
window.alert();
属性:获取其他四个BOM对象
方法:
- alert() 显示带有一段消息和一个确认的警告框
- confirm() 显示带有一段消息和确认和取消的对话框,点确定会返回ture,点取消会返回false
- setInterval() 按照指定的周期(毫秒计)来调用函数或计算表达式(定时器,循环执行)
- setTimeout() 在指定的毫秒后调用函数或计算表达式(定时器,只执行一次)
Navigator:浏览器对象
常用:userAgent 返回由客服端发送给服务器的user-agent头部的值。
使用下面代码尅判断用户在那个终端打开页面,以实现跳转:
1
2
3
4
5if ((navigator.userAgent.match(/(phone|pad| pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MOOBrowser|JUC|Fennec|wosBrowser|BrowserNG|Webos|Symbian|Windows Phone)/i))){
window.location.href ="" //手机
}else{
window.location.href ="" //电脑
}Screen:屏幕对象
History:历史记录对象
获取:使用window.history获取,其中window. 可以省略。
1
2window.history.方法();
history.方法();方法:
- back() 后退,加载history列表的前一个url
- forward() 前进,加载history列表的下一个url
- go(参数) 参数为n,前进n个页面,参数为-n,后退n个页面
Location:地址栏对象
获取:使用window.location获取,其中window. 可以省略。
1
2window.location.方法();
location.方法();URL:同一资源定位符 Uniform Resource Locator
是互联网上标准资源的地址,互联网的每个文件都有唯一的url,它包含的信息指出文件的位置,以及浏览器应该怎么处理它。
格式:
1
2protocol://host[:port]/path/[?query]#fragment
如:http:/www.baidu.cn/index.html?name=andy&age=18#link组成 说明 protocol 通信协议常用的http, ftp, maito 等 host 主机(域名) port 端口号,可选,省略时使用方案的默认端口如http的默认端口为80 path 路径,由零或多个 ‘/‘ 符号隔开的字符串,一般用来表示主机上的一个目录或文件地址 query 参数,以键值对的形式,通过&符号分隔开来 fragment 片段,#后面内容,常见于链接、锚点 属性:
location.href 设置或返回完整的url
location.host 返回主机/域名
location.port 返回端口号,如果未写则返回空字符串
location.pathname 返回路径
location.search 返回参数
location.hash 返回片段
案例:第一页面,使用提交表单,url里的参数会携带表单的数据到另一个页面。
1
2
3
4
5
6
7
8
9<!-- 第一个页面 -->
<form action="index.html"
用户名: <input type="text" name="uname">
<input type="submit" value="登录">
</form>
<!-- 第二个页面 url为index.html -->
<!-- 假设表单提交的用户名输入为andy,则使用location.search得到 ?uname=andy -->
var arr = location.search.substr(1).split('='); <!-- 去掉问号,再根据等于号做分割,则arr[1]就是名字andy了方法:
方法 返回值 location.assign(url) 跟href一样,可以跳转页面/重定向页面;记录历史 location.replace(url) 替换当前页面,不记录历史,所以不能后退 location.reload() 重新加载页面,相当于刷新(部分有缓存的,从缓存获取数据);如果参数为true,为强制刷新(从服务器重新获取数据)
页面加载事件
onload事件是等页面内容全部加载完毕 才会开始执行。
document.addEventListener(‘load’, function(){ })
window.onload = function(){ } //传统方式只有最后一个onload生效
DOMContentLoaded 是只需DOM加载完毕,不包括flash, css等 就可以执行,速度比load快一些。
- document.addEventListener(‘DOMContentLoaded’, function(){})
窗口大小调整
window.addEventListener(“resize”, function(){})
window.onreasize = function(){ }
常用于响应式布局
定时器
setTimeout(函数,延迟时间)
延迟设定的时候后调用函数。
时间单位:毫秒,省略时默认为0毫秒;
浏览器中经常需要多个定时器,为了区分,一般把定时器赋值给一个变量,以起一个名字。
setTimeout也称为回调函数callback。想click点击事件里的函数也叫回调函数。
- 清除定时器:clearTimeout (定时器)
setInterval(函数,间隔时间)
每隔设定的时间调用以此函数。
- clearInterval(定时器)
DOM对象
Document Object Model,文档对象模型,JS将标记语言的各个组成部分封装为对象,分别有:Document,Element,Attribute,Text,Comment。
JS通过DOM,能对HTML进行操作:改变内容、样式,对事件做出反应,添加和删除元素。
Document:整个文档对象
Element:元素对象
获取:使用document对象的方法获取(document.方法 或者 document对象.方法)
getElementByld
根据id属性值取,返回一个Element对象
getElementsByTagName
根据标签名称获取,返回Element对象数组
getElementsByName
根据name属性值获取,返回Element对象数组
getElementsByClassName
根据class属性值获取,返回Element对象数组
querySelector(选择器)
返回指定选择器的第一个元素对象,选择器需要加符号以区分: .类名 #id 标签名
querySelectorAll (选择器)
返回指定选择器的所有元素对象集合,选择器需要加符号以区分
获取特殊元素:bod y,html
document.body :返回body元素对象
document.documentElement:返回html元素对象
Attribute:属性对象
Text:文本对象
Comment:注释对象
改变元素内容
普通盒子修改,如div标签:
对象.innerHTML
可以识别html标签,保留空格和换行
1
2var div = document.querySelector('div');
div.innerHTML = //修改后的内容或对象对象.innerText
不识别html标签,去除空格和换行
表单内容修改:
对象**.value** = 修改内容
修改样式:
行内样式操作(适合样式较少的情况):
对象.style.样式/属性 = 修改内容
类名样式操作(适合样式复杂的情况):
对象.className = 新类名(修改内容放在新类名里)
1
2
3
4
5
6
7
8
9
10
11
12<head>
<style>
.change{
background-color: red;
font-size: 25px;
}
</style>
</head>
<script>
对象.className = 'change';//覆盖原先的类名
对象.className = '原类名 新类名';//不会覆盖原先的类名,存在多类名
</script>
禁用元素:
对象.disabled = true;
元素操作
获取属性值:
- element.属性 —— 获取内置属性值
- element.getAttribute(‘属性’) —— 主要获取自定义的属性
设置属性值:
- element.属性 = ‘值’ —— 设置内置属性
- element.setAttribute(‘属性’,’值’) —— 主要设置自定义属性
移除属性:
- element.removeAttribute(‘属性’)
H5规定:
设置自定义属性要以 data- 开头,并且赋值。如:
1 <div data-index = "1"></div>
1 element.setAttribute('data-index', 2);H5新增的获取自定义属性的方法(ie11后支持):
使用dataset存放所有以data开头的自定义属性,通过dataset时属性名不用加 data- 前缀。
- element.dataset.属性名
- element.dataset[‘属性名’]
如果自定义属性中由多个 -链接的单词,获取时使用驼峰命名法,去掉 -
阻止链接跳转:添加 javascript:;
1 | <a href='javascript:;'> </a> |
节点操作
节点:网页中所有内容都是节点 (标签,属性,文本,注释),在DOM中,节点用node表示。
基本属性:
nodeType(节点类型)
- 元素节点 nodeType为1
- 属性节点 nodeType为2
- 文本节点 nodeType为3(文本节点包括文字,空格,换行等)
实际主要操作的是元素节点
nodeName(节点名称)
nodeValue(节点值)
节点层级:
父节点: 节点 .parenNode
返回指定节点的最近的父 节点,如果没有父节点则返回null
子节点:节点 .childNodes
返回指定节点的子节点的集合,包括元素节点,文本节点等,需要再处理后才能得到元素节点,较麻烦,一般不使用。
可以使用 节点 .children 来获取子元素节点,虽然不是标准规定,但各浏览器2都支持。
首个字节点和最后一个子节点:
- 节点 .children[0] ——首个
- 节点.children[节点.children.lenght - 1] ——最后一个
下一个兄弟元素节点: 节点. nextElementSibling
上一个兄弟元素节点: 节点. previousElementSibling
创建节点: document.createElement(‘新节点名’)
添加节点:
- 父节点.appendChild(新建的节点) —— 添加到父节点的子节点列表的末尾
- 父节点 .insertBefore(‘新建的节点’,指定节点) ——添加到指定子节点的前面
删除节点: 父节点.removeChild(要删除的子节点)
复制节点: 节点.cloneNode() ——如果括号里参数为空 或者为 false,则为浅拷贝,即只复制节点本身,不复制里面的内容;括号里为true,则为深拷贝,复制标签和里面的子节点。
事件
事件:HTML事件是发生在HTML元素上的”事情”。比如:按钮被点击,鼠标移动到元素之上,按下键盘按键。
1 | 对象.事件 = function(){ |
组成
- 事件源:事件被触发的对象
- 事件类型:如何触发/发生了什么事件
- 事件处理程序:通过函数赋值的方式进行
事件监听:JavaScript可以在事件被侦测到时执行代码。
事件绑定
新方式——事件监听方式:
对象. addEventListener(事件类型,处理函数,flag)
事件监听方式:
事件类型 为字符串形式,如:click,mouseover,注意不再带on
第三个参数flag,如果为false或省略,那么事件流处于冒泡阶段,如果为true,则为捕获阶段。实际开发中,很少使用事件捕获,不过有些事件没有冒泡,如:onblur,onfocus,onmouseenter,onmouseleave
传统方式:
方法一:通过HTML标签中的事件属性进行绑定(耦合)
1
2
3
4<input type = "button" onclick='on()' >
function on(){
alet("我被点了");
}方法二:通过DOM元素属性绑定(推荐)
1
2
3
4
5<input type = "button" id = "btn" >
const btn = document.querySelector("#btn");
btn.addEventListener('click', () => alert("我被点了"));
删除事件
事件监听方式:对象. removeEventListener(事件类型,处理函数)
传统方式:对象. 事件类型 = null
DOM事件流
分为3个阶段:
- 捕获阶段:由最顶层节点开始,然后逐级向下传播到最具体(最小)的元素。
- 当前目标阶段
- 冒泡阶段:事件开始由最具体(最小)的元素接收,然后逐级传播到最顶层阶段。

事件对象
event 是一个事件对象,写在监听函数的小括号里当形参,事件对象只有事件发生了才会存在,是系统自动创建的,不需要传递实参进去。
事件对象:是事件的一系列相关数据的集合,跟事件相关的,如鼠标点击里就包含了鼠标坐标之类的信息。
事件对象可以自己命名,如event,evt,e
属性方法
| 属性/方法 | 说明 |
|---|---|
| e.target | 返回触发事件的对象 |
| e.type | 返回事件的类型 |
| e.preventDefault() | 阻止事件的默认行为,如阻止链接跳转 |
| e.stopPropagation() | 阻止冒泡 |
在监听函数中,e.target指触发事件的对象,this指绑定该事件的对象。
阻止事件冒泡
事件对象.stopPropagation()
低版本浏览器:window.event.cancelBubble = true;
1
2
3
4
5 if(e && e.stopPropagation){//浏览器是否认识新特性
e.stopPropagation();
}else{
window.event.cancelBubble = true;
}
阻止默认行为
阻止默认行为: event.preventDefault()
事件委托
对于子节点要设置相同的事件监听,可以不用在子节点上单独设置,而是设置在其父节点上,然后利用冒泡原理影响设置的每个子节点。
作用:只操作了以此DOM,提供了程序的性能。
如果只想操作触发事件的那个子节点,可以利用e.target 得到触发事件的那个子节点
常见事件
使用传统方式window. 时,事件前要加on,如onclick,onfocus
click:对象被点击
focus:鼠标获得焦点
blur:鼠标失去焦点
mouseover:鼠标经过
mouseout:鼠标离开
mousemove:鼠标移动触发
mouseup:鼠标弹起触发
mousedown:鼠标按下触发
submit:确认按钮被点击(常用于v 表单提交)
change:域的内容被改变
keydown:某个按键被按下
keyup:某个按键被松开
keypress:某个按键被按下,但不识别ctrl shift等功能键
mouseover 和 mouseenter 区别:
mouseover 经过自身盒子会触发,经过子盒子还会触发;
mouseenter 只会经过自身盒子触发
鼠标事件对象
属性:
e.clientX / e.clientY —— 鼠标相对于浏览器窗口的X/Y坐标
e.pageX / e.pageY —— 鼠标相对于文档页面的X/Y坐标
e.screenX / e.screenY —— 鼠标相对于电脑屏幕的X/Y坐标
禁止鼠标右键菜单
1
2
3 document.addEventListener('contextmenu',function(e){
e.preventDefault();
})禁止鼠标选中
1
2
3 document.addEventListener('selectstart',function(e){
e.preventDefault();
})
键盘事件对象
属性:
keyCode:识别按下的是哪个键
事件keyup和keydown不区分字母大小写,keypress区分大小写
例子:按s键,使搜索框获得焦点
1
2
3
4
5
6 var search = document.queryAelector('input');
document.addEventListener('keyup',function(e){
if(e.keyCode === 83){
search.focus();
}
})
网页特效
元素偏移量 offset
获得元素距离带有定位父元素的位置
获得元素自身的大小(宽度高度)
注意:返回的数值都不带单位
常用属性:
| offset系列属性 | 作用 |
|---|---|
| element.offsetParent | 返回该元素带有定位的父元素,如果父级都无定位则返回body |
| element.offsetTop | 返回元素相对带有定位父元素上方的偏移 |
| element.offsetLeft | 返回元素相对带有定位父元素左边框的偏移 |
| element.offsetWidth | 返回自身包括padding、边框、内容区的宽度,返回数值不带单位 |
| element.offsetHeight | 返回自身包括padding、边框、内容区的高度,返回数值不带 单位 |
offset与style的区别:
| offset | style |
|---|---|
| offset可以得到任意样式表中的样式值 | style只能得到行内样式表中的样式值 |
| offset系列获得的数值是没有单位的 | style.width获得的是带有单位的字符串 |
| offsetWidth包含padding+border+width | style.width获得不包含padding和border的值 |
| offsetWidth等属性是只读属性,只能获取不能赋值 | style.width是可读写属性,可以获取也可以赋值 |
所以,想要获取元素大小位置,用offset更合适;想要给元素更改值,则需要用stye改变
元素可视区 client
client翻译:客户端。可以动态获取元素可视区的相关信息
| client系列属性 | 作用 |
|---|---|
| element.clientTop | 返回元素上边框的大小 |
| element.clientLeft | 返回元素左边框的大小 |
| element.clientWidth | 返回自身包括padding、内容区的宽度,不含边框,返回数值不带单位 |
| element.clientHeight | 返回自身包括padding、内容区的高度,不含边框,返回数值不带单位 |
元素滚动 scroll
scroll翻译:滚动。可以动态的得到该元素的大小,滚动距离等
| scroll系列属性 | 作用 |
|---|---|
| element.scrollTop | 返回被卷去的上侧距离,返回数值不带单位 |
| element.scrollLeft | 返回被卷去的左侧距离,返回数值不带单位 |
| element.scrollWidth | 返回自身实际的宽度,不含边框,返回数值不带单位 |
| element.scrollHeight | 返回自身实际的高度,不含边框,返回数值不带单位 |
常用方法:window.scroll(x, y) 滚动窗口至文档的特定位置
offset系列经常用于获得元素位置 offsetLeft offsetTop
client 经常用于获取元素大小 clientWidth clientHeight
scroll 经常用于获取滚动距离 scrallTop scrollLeft
注意:页面滚动的距离通过 window.pagexoffset 获得
动画函数封装
动画实现原理:通过定时器setInterval() 不断移动盒子位置。
步骤:
- 获取盒子当前位置
- 让盒子在当前位置加上一个移动距离
- 利用定时器不断重复操作,并加一个结束的条件
注意 此元素需要添加定位,才能使用element.style.left
如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 //将移动效果封装成函数,调用即可
//obj是传入的对象,target是要移动的距离,callback为传入的其他函数,用于实现额外的效果
function animate(obj, target,callback){
//防止同一对象同时开启多个定时器(只保留当前一个定时器执行)
clearInterval(obj.timer);
//将timer作为传入对象obj的属性,可以避免开辟过多空间
obj.timer = setInterval(function(){
var step = (target - obj.offsetLeft) / 10; //实现缓动效果,速度从快到慢
step = step > 0 ? Math.ceil(step) : Math.floor(step); //去掉小数点
if(obj.offsetLeft >= target){
cleatInterval(obj.timer); //停止定时器
callback && callback; //如果回调函数存在就执行回调函数
}
//步长值变化
obj.style.left = obj.offsetLeft + s + 'px';},15);
}
var div = document.querySelector('div');
animate(div, 300);
因为动画函数经常被使用,所以可以将其单独封装到一个JS文件里面,使用的时候应用这个JS文件即可。
节流阀
作用:当上一个函数动画内容执行完毕,再取执行下一个函数动画,让事件不会连续过快触发。
思想:利用回调函数,添加一个变量来控制,所著函数和解锁函数。
1 | var flag = true; |
本地存储
本地存储特性:
- 数据存储在用户浏览器中
- 设置、读取方便、甚至页面刷新不丢失数据
- 容量较大,sessionStorage约5M、localStorage约20M
- 只能存储字符串,可以将对象 JSON.stringify0编码后存储
sessionStorage
特点:
- 生命周期为关闭浏览器窗口
- 在同一个窗口页面下数据可以共享
- 以键值对的形式存储使用
方法:
存储数据:
sessionStorage.setItem(key, value)
获取数据:
sessionStorage.getItem(key)
删除数据:
sessionStorage.removeItem(key)
删除所有数据
sessionStorage.clear()
localStorage
特点:
- 生命周期永久生效,除非手动删除否则关闭页面也会存在
- 可以多窗口(页面)共享(同一浏览器可以共享)
- 以键值对的形式存储使用
方法:
存储数据:
localStorage.setItem(key, value)
获取数据:
localStorage.getItem(key)
删除数据:
localStorage.removeItem(key)
删除所有数据
localStorage.clear()
jQuery
jQuery是JavaScript库,封装了JS中的DOM操作。
j 指 JavaScript,query 查询
优点:
- 轻量级。核心文件才几十kb,不会影响页面加截速度
- 跨浏览器兼容。基本兼容了现在主流的浏览器
- 链式编程、隐式迭代
- 对事件、样式、动画支持,大大简化了DOM操作
- 支持插件扩展开发。有着丰富的第三方的插件,例如:树形菜单、日期控件、轮播图等
- 免费、开源
入口函数
等DOM接口渲染完毕即可执行内部代码,相当于原生js中的DOMContentLoaded
1 | $(function(){ |
对象
顶级对象 $
$是jQuery的别称,在代码中也可以使用jQuery代替$。
$是jQuery的顶级对象,相当于原生JS中的window。把元素利用$包装成jQuery对象,就可以调用jQuery的方法。
jQuery对象 和 DOM对象
用原生JS获取过来的对象就是DOM对象。
用 jQuery 方式获取过来的对象是jQuery)对象。本质:通过$把DOM元素进行了包装
DOM对象只能使用原生的 JS 属性和方法
jQuery对象只能使用 jQuery 方法
1 | //DOM对象 |
jQuery对象 和 DOM对象 相互转换:
1 | //DOM对象转换为jQuery对象 |
常用API
选择器操作
选择器: $(“选择器”)
**筛选选择器: **
| 语法 | 用法 | 说明 |
|---|---|---|
| :first | $(“li:first”) | 获取第一个li元素 |
| :last | $(“li:last”) | 获取最后一个li元素 |
| :eq(index) | $(“li:eq(2)”) | 获取的li元素中,选择下标为2的元素 |
| :odd | $(“li:odd”) | 获取的li元素中,选择下标为奇数的元素 |
| :even | $(“li:even”) | 获取的li元素中,选择下标为偶数的元素 |
筛选方法:
| 语法 | 用法 | 说明 |
|---|---|---|
| parent() | $(“li”).parent() | 查找父级 |
| children(selector) | $(“ul”).children(“li”) | 相当于$(“u1>li”),最近一级(亲儿子) |
| find(selector) | $(“ul”).find(“li”); | 相当于$(“ul li”), 后代选择器 |
| siblings(selector) | $(“.first”).siblings(“li”); | 查找兄弟节点,不包括自己本身 |
| nextAll ([expr]) | $(“.first”).nextAll () | 查找当前元素之后所有的同辈元素 |
| prevAll (class) | $(“.last”).prevAll () | 查找当前元素之前所有的同辈元素 |
| hasClass (class) | $(‘div’).hasclass(“protected”) | 检查当前的元素是否含有某个特定的类,如果有,则返回true |
| eq (index) | $(“li”).eq(2); | 相当于$(“1i:eq(2)”), index从0开始 |
jQuery使用排他思想:
1 | //例:思想点击 背景颜色跟随 |
链式编程:
1 | //原来写法 |
样式操作
设置样式:
$(“选择器”).css(“属性”) —— 只写属性,则返回属性值
$(“选择器”).css(“属性”,”值”) ——设置一组样式,属性必须加引号,值如果是数字可以不用单位和引号
$(“选择器”).css( {color: “red”, font-size: 20}); ——参数是对象形式,可以设置多组样式,属性和值用冒号隔开,属性可以不加引号
DOM元素设置样式需要显式迭代,而jQuery对象是以伪数组形式存储,它在设置样式时会自动遍历内部DOM元素,即隐式迭代。
类操作:
- 添加类:**$(“div”).addClass(“类名”)**
- 删除类:**$(“div”).removeClass(“类名”)**
- 切换类:**$(“div”).toggleClass(“类名”)**
可以先为某个类设置样式,需要时再通过类操作来修改样式。
效果
显示隐藏
**show **( [speed], [easing], [fn] ) —— 显示
参数都可以省略,则无动画直接显示
speed:预定字符串(“slow”,”normal”,”fast”)或动画时长的毫秒值;
easing:用来指定切换效果,默认”swing“,可选参数“linear”;
fn:回调函数,在动画完成时执行,每个元素执行一次。
(一般都是不加参数直接显示隐藏的)
**hide **( [speed, [easing], [fn]] ) —— 隐藏
toggle ( [speed, [easing], [fn]] ) —— 切换显示和隐藏
滑动
- slideDown ( [speed, [easing], [fn]] ) —— 下拉
- slideUp ( [speed, [easing], [fn]] ) —— 上拉
- slideToggle ( [speed, [easing], [fn]] ) ——切换上下拉动
淡入淡出
fadeIn ( [speed, [easing], [fn]] ) —— 淡入
fadeOut ( [speed, [easing], [fn]] ) —— 淡出
fadeToggle ( [speed, [easing], [fn]] ) —— 切换淡入淡出
fadeTo ( [speed, opacity, [easing], [fn]] ) —— 修改透明度
speed速度为动画时长,必须写,
opacity透明度必须写,
animate ( params, [speed], [easing], [fn] ) —— 自定义动画
params: 样式选择,以对象形式传递,必须写
1
2
3
4
5
6
7$(function(
{$("button").click(function({
$("div").antimate({
legt:200
});
})
})
事件切换
hover 是鼠标经过和离开的复合写法
1 | //两个函数分别是鼠标经过和鼠标离开时分别调用的函数 |
停止排队
动画效果一旦触发就会执行,如果多次触发,就会使多个动画同时执行。
停止排队:stop() —— 用于停止动画效果。可以写到动画的前面,相当于停止上一次的动画。
属性操作
获取元素固有属性的值:prop(“属性名”)
设置固有属性值: prop(“属性”,”属性值”)
元素固有属性就是元素本身自带的属性,如< a >里的href,< input > 里的type。
获取元素自定义属性的值:attr(“属性”)
设置元素自定义属性的值:attr(“属性”,“属性值”)
类似原生的getAttribute()和setAttribute()
数据缓存data():可以在指定的元素上存取数据,并不会修改DOM元素结构,一旦页面刷新,之前存放的数据都将被移除。
获取:data(“属性”) ;设置:data(“属性”,”值”)
内容文本值
**普通元素内容:html()**,相当于原生innerHTML。
获取:html()
设置:html(“内容”)
**普通元素文本内容:text()**,相当于原生innerText。
获取:text()
设置:text(“内容”)
表单的值 val() ,相当于原生value
获取:val()
设置:val(“内容”)
元素操作
jQuery隐式迭代是对同一类元素做同样的操作,如果想要给同一元素做不同操作,仍需要遍历。
1 | $("div".each(function(index,domEle){...})) |
index是每个元素的索引号,domEle是每个DOM元素对象,注意不是jquery对象,要用jquery方法,需要进行对象转化。
1 | $.each(object, function(index, element){}) |
object可以是任何对象,所以$each主要用于数据处理,如数组,对象;
尺寸、位置操作
尺寸:
| 语法 | 说明 |
|---|---|
| width() / height() | 只算width / height |
| innerWidth() / innerHeight() | 包含padding |
| outerWidth() / outerHeight() | 包含padding、border |
| outerWidth(true) / outerHeight(true) | 包含padding、borde、margin |
位置:
offset()
设置或返回距离文档的位置(偏移),跟父级没有关系。offset().left 获取距离文档左侧的距离, offset().top 获取距离文档顶部的距离
- scrollTop()
设置或返回被卷去的头部。
同样有 scrollTop().left 和 scrollTop().top 两个属性。
事件
注册事件
1 | element.事件(function(){}) |
事件处理
on() 绑定事件:匹配元素上绑定一个或多个事件的处理函数。
1 | element.on(events, [selector], fn) |
events:一个或多个用空格分割的事件类型;
selector:元素的子元素选择器;
fn:回调函数;
1 | //绑定多个事件,多个处理程序 |
1 | //可以实现事件委派,把原来加给子元素的事件绑定到父元素上。 |
1 | //on()可以给动态生成的元素绑定事件,而原来的 $("").事件(fn)不能给动态创建的元素绑定事件 |
one() 绑定事件,只触发一次 :
1 | $("div").one("click",function(){ |
off() 解绑事件 :
1 | $("div").off(); //移除div身上的所有事件 |
**trigger() 自动触发事件 ** :
1 | element.trigger("type"); |
1 | //触发事件,但不触发元素的默认行为 |
布局
数据可视化
ECharts是一个使用JavaScript实现的开源可视化库,兼容大部分浏览器,底层依赖矢量图形库ZRender。
ES6
ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。本书中提到 ES6 的地方,一般是指 ES2015 标准,但有时也是泛指“下一代 JavaScript 语言”。
let和const
原先的var声明的变量在全局有效, 而新增的let声明的变量只在局部有效。
1 | // i只在循环体内有效, 在循环体外引用会报错 |
同一作用域不可使用let重复声明同一个变量。
不存在变量提升:let所声明的变量必须先声明再使用,否则会报错。(var的变量可在声明前使用,输出undefined,即变量提升)
暂时性死区:只要块级作用域内存在
let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。 (在let声明的变量之前,且在同一局部域中,不能给同名的变量赋值)1
2
3
4
5
6var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
const 声明一个只读的常量。一旦声明,常量的值就不能改变,同时必须立即初始化,不能留空。
const的作用域与let命令相同:只在声明所在的块级作用域内有效。cosnt实质是保证 变量指向的内存地址不能变,所以对于复合类型的数据,只是其内存地址保存的指向实际数据地址的指针不能变,该类型或对象的真实数据仍可以修改。
ES6中声明变量的6种方法:var,function,let,const,import,class
变量的解构赋值
分为 数组、对象、字符串、数值、布尔值、函数参数 的解构赋值
- 数组的解构赋值
XML
xml 是可扩展的标记性语言。
作用:
- 主要用于存储数据,这些数据具有自我描述性;
- 作为项目或者模块的配置文件;
- 作为网络传输数据的格式(现在改为使用 JSON 为主了)。
语法
文档声明
xml声明:
1
- version 是版本的意思,
- encoding 是编码,
- standalone=”yes/no” 表示这个 xml 文件是否是独立的 xml文件
元素(标签)
元素是指从开始标签到结束标签的内容。(与html一样)
注意大小写敏感,元素都必须闭合,必须有根元素
命名规则:
- 可以包含字母,数字,其他字符(包括中文);
- 不能以数字或标点符号开头;
- 不能包含空格;
元素(标签) 分 单双标签:
- 单标签: < 标签名 属性=”值“ 属性=”值“ …. />
- 双标签:< 标签名 属性=”值“ …> 文本或子标签 </标签名>
xml 属性
与html非常类似,注意属性的值要用 引号” “ 包起来。
文本区域(CDATA 区)
说明此区域是纯文本内容,不需要xml语法解析
1
2
3<![CDATA[
文本内容,不会被xml解析
]]>
XML解析技术
dom4j 解析技术是第三方公司的技术,需要到其官网上下载dom4j的jar包,导入到idea的库中。
1 | // 需要分四步操作: |
JSON
JSON ( JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。
JSON 采用完全独立于语言的文本格式,而且很多语言都提供了对 json 的支持(包括 C, C++, C#, Java, JavaScript, Perl, Python 等)。 使得 JSON 成为理想的数据交换格式。
- 其语法格式简单,层次结构鲜明,现多用于作为 数据载体,在网络中进行数据传输。
轻量级是相较xml而言;
数据交换指的是客户端和服务器之间业务数据的传递格式
定义:
- json由键值对组成,用大括号包围;
- 每个键都用双引号包起来;
- 键与值之间用冒号分割。
1 | var jsonObj = { |
访问:
json本身是一个对象,访问: json对象.key
1 | alert(typeof(jsonObj));// object json 就是一个对象 |
方法:
json有两种存在形式:
- json对象:用于操作json中的数据;
- json字符串:用于客户端和服务器之间数据交换时;
转换方法:
1 | json.stringify() //json对象 转换为 json字符串 |
解析转换:
jackson:
Spring MVC 默认采用Jackson解析Json,尽管还有一些其它同样优秀的json解析工具,例如Fast Json、GSON,但是出于最小依赖的考虑,也许Json解析第一选择就应该是Jackson。
1
Fastjson:
Fastjson是阿里巴巴提供的一个Java语言编写的高性能功能完善的SON库,是目前ava语言中最快的 JSON库,可以实现Java对象和 JSON字符串的相互转换。但拓展性不高。
1
2
3
4 //java对象 转 json字符串
String java对象 = JSON.toJSONString(对象);
//json字符串 转 java对象
User user = JSON.parseObject(jsonStr,User.class); //User为某一个类
gson的一些使用(还是建议使用jackson):
javaBean和json互转:
使用GSON,它是Google提供的用来在Java对象和 JSON数据之间进行映射的Java类库。
- 创建gosn对象
- gosn.toJson(json对象)
- gson.fromJson(json字符串,javaBean类名.class)
1 | public void test(){ |
list和json互转:
- 创建gosn对象
- gosn.toJson(list)
- gson.fromJson(json字符串,list.getclass())
map和json互转:
- 创建gosn对象
- gosn.toJson(map)
- gson.fromJson(json字符串, new TypeToken<HashMap<对应类,对应类>>(){}.getType() );
Vue
Vue是一套前端框架,免除原生JavaScripte中的DOM操作,简化书写。
基于MWM(Model-View-ViewModel)思想,实现数据的双向绑定,将编程的关注点放在数据上。
- M:模型(Model) :data中的数据
- V:视图(View) :模板代码
- VM:视图模型(ViewModel):Vue实例
其中 ViewModel 由Vue提供; 而data中所有属性会通过代理由vm进行管理。
Vue数据代理的原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。为每一个添加到vm上的属性,都指定一个getter/setter。在getter/setter内部去操作(读/写)data中对应的属性。
使用:
- 声明
1 | <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> |
创建Vue实例和容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25<body>
<!--
vue实例和容器是一一对应的;
正式开发中只有一个Vue实例,且配合组件一起使用;
{{xxx}}中的xxx要写js表达式(变量,运算式,函数调用等);
-->
<!-- 准备一个容器 -->
<div id="root">
<h1>hello {{name}}</h1>
</div>
</body>
<script>
//阻止vue在启动时生产提示(本地url地址时没接网络可能报一些错,但不影响运行)
Vue.config.productionTip = false
//创建Vue实例
new Vue({
el: '#root', //el用于指定当前Vue示例为哪个容器服务
data: { //data中用于存储数据,数据供el指定的容器所使用
name: 'valcanoZz'
},
})
</script>
基本使用
模板语法
- 插值语法 —— 用于解析标签体内容。
- 写法:
{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性。
- 写法:
- 指令语法:—— 用于解析标签(包括:标签属性、标签体内容、绑定事件…..)
- 举例:v-bind: href=”xxx” 或 简写为 :href=”xxx”,xxx同样要写js表达式,
且可以直接读取到data中的所有属性。 - 注:Vue中有很多的指令,且形式都是:v-????,此处只是拿v-bind举个例子。
- 举例:v-bind: href=”xxx” 或 简写为 :href=”xxx”,xxx同样要写js表达式,
1 | <body> |
数据绑定
- 单向数据绑定(v-bind)—— 数据只能从data流向页面;
- 双向数据绑定(v-model)—— 数据不仅能从data流向页面,还可以从页面流向data;
v-model 只能用于表单类元素,因为只有表单类元素才有需求更改数据。
1 | <body> |
el与data的写法
一个重要的原则:
由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,就不再是Vue实例了。
1 | <body> |
事件处理
事件绑定 —— v-on
可简写为 @ ;加上小括号也可以传参进去,如果需要接受event对象,要加上$event进行占位;
事件修饰符
- prevent:阻止默认事件(常用)
- stop:阻止事件冒泡(常用)
- once:事件只触发一次(常用)
- capture:使用事件的捕获模式
- self:只有event,target是当前操作的元素时才触发事件
- passive事件的默认行为立即执行,无需等待事件回调执行完毕
1 | <body> |
键盘事件
使用 @keydown.按键别名 和 @keydown.按键别名 来使用键盘事件。
Vue中常用的按键别名:
- 回车 => enter
- 删除 => delete (捕获“删除”和“退格”键)
- 退出 => esc
- 空格 => space
- 换行 => tab (特殊,必须配合keydown去使用)
- 上 => up
- 下 => down
- 左 => left
- 右 => right
Vue 未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
系统修饰键(用法特殊):ctrl、alt、shift、meta
- 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
- 配合keydown使用:正常触发事件。
也可以使用keyCode去指定具体的按键(不推荐)
Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
1 | <body> |
计算属性computed
定义:要用的属性不存在,要通过已有属性计算得来。
原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
get函数什么时候执行?
- 初次读取时会执行一次。
- 当依赖的数据发生改变时会被再次调用。
优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
注:
计算属性最终会出现在 Vue实例上,直接读取使用即可。
如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
1 | <body> |
监视属性watch
监视属性 —— watch
- 当被监视的属性变化时, 回调函数自动调用, 进行相关操作
- 监视的属性必须存在,才能进行监视!!
- 监视的两种写法:
- new Vue时传入watch配置
- 通过vm.$watch监视
深度监视 ——
Vue中的watch默认不监测对象内部值的改变(只管理一层)。
配置deep:true可以监测对象内部值改变(多层)。
注:
Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
使用watch时根据数据的具体结构,决定是否采用深度监视。
1 | <body> |
computed和watch区别
computed能完成的功能,watch都可以完成。
watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
两个重要的小原则:
- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
- 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm或组件实例对象。
如:在vue里使用定时器回调函数,要用箭头函数,否则this会指向管理者window;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<script type="text/javascript">
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三',
fullName:'张-三'
},
watch:{
//修改firstName后,经过1秒再修改fullName
firstName(val){
setTimeout(()=>{
console.log(this)
this.fullName = val + '-' + this.lastName
},1000);
},
lastName(val){
this.fullName = this.firstName + '-' + val
}
}
})
</script>
监视数据
Vue监视数据的原理:
vue会监视data中所有层次的数据。
如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
- 对象中后追加的属性,Vue默认不做响应式处理;
- 如需给后添加的属性做响应式,请使用如下API:
- Vue.set (target,propertyName/index,value) 或 vm.$set (target,propertyName/index,value)
如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
- 调用原生对应的方法对数组进行更新。
- 重新解析模板,进而更新页面。
在Vue修改数组中的某个元素一定要用如下方法:
使用这些API: push()、pop()、shift()、unshift()、splice()、sort()、reverse()
push() 向数组的末尾添加一个或者多个元素,并返回新的长度
pop() 删除并返回数组的最后一个元素
shift() 删除并返回数组的第一个元素
unshift() 向数组的开头添加一个或多个元素,并返回新的长度
splice() 删除元素,并向数组添加新元素
sort() 对数组的元素进行排序
reverse() 颠倒数组中元素的顺序
Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给 vm 或 vm 的根数据对象 添加属性!!!即不能增加data中的第一层数据。
1 | <body> |
绑定样式
class样式
写法:class=”xxx” xxx可以是字符串、对象、数组。
- 字符串写法适用于:类名不确定,要动态获取。
- 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
- 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
style样式(用的少)
- :style=”{fontSize: xxx}” 其中xxx是动态值。
- :style=”[a,b]” 其中a、b是样式对象。
1 |
|
条件渲染
v-if
写法:- v-if=”表达式”
- v-else-if=”表达式”
- v-else=”表达式”
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。v-show
写法:v-show=”表达式”适用于:切换频率较高的场景。
特点:不展示的DOM元素不会被移除,仅仅是使用样式隐藏掉
注:使用v-if的时,元素如果被移除了就无法获取到,而使用v-show一定可以获取到。
1 | <body> |
列表渲染
基本列表
使用v-for指令展示列表数据:v-for="(item, index) in items" :key="index"
注:key的选用,最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key也没有问题。
由于操作DOM较耗性能,所以会向模拟出虚拟DOM,与旧的虚拟DOM比较,然后才更新视图;
key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
旧虚拟DOM中找到了与新虚拟DOM相同的key:
①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
旧虚拟DOM中未找到与新虚拟DOM相同的key,则创建新的真实DOM,随后渲染到到页面。
注:直接用index作为key可能引发一些问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
- 如果结构中还包含输入类的DOM,则会产生错误DOM更新 ==> 界面有问题。
1 | <body> |
列表过滤
使用watch和computed都可以
1 | <body> |
收集表单数据
注意事项:
- 若:<input type=”text”/>,则 v-model收集的是value值,用户输入的就是value值。
- 若:<input type=”radio”/>,则v-model收集的是value值,且要给标签配置value值。
- 若:<input type=”checkbox”/>
- 没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值true,false)
- 配置input的value属性:
- v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
- v-model的初始值是数组,那么收集的的就是value组成的数组
v-model的三个修饰符:
- lazy:失去焦点再收集数据
- number:输入字符串转为有效的数字
- trim:输入首尾空格过滤
1 | <body> |
过滤器
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
语法:
- 注册过滤器:Vue.filter(name,callback) 或
new Vue{filters:{}} - 使用过滤器:
{{ xxx | 过滤器名}}或 v-bind:属性 = “xxx | 过滤器名”
备注:
- 过滤器也可以接收额外参数、多个过滤器也可以串联
- 过滤器并没有改变原本的数据, 而是产生新的对应的数据
1 | <body> |
内置指令/自定义指令
常见指令:
v-bind —— 单向绑定解析表达式, 可简写为 :xxx
v-model —— 双向数据绑定
v-for —— 遍历数组/对象/字符串
v-on —— 绑定事件监听, 可简写为@
v-if —— 条件渲染(动态控制节点是否存存在)
v-else —— 条件渲染(动态控制节点是否存存在)
v-show —— 条件渲染 (动态控制节点是否展示)
v-once —— v-once所在节点在初次动态渲染后,就视为静态内容了。以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
v-text —— 向其所在的节点中渲染文本内容。
(与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。)v-html —— 向指定节点中渲染包含html结构的内容。
(与插值语法的区别:v-html会替换掉节点中所有的内容,{{xx}}则不会。且v-html可以识别html结构。)v-html有安全性问题!!!!
(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!v-cloak指令(没有值)—— 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
使用css的内容不显示 配合v-cloak可以解决网速慢时页面展示出
{{xxx}}的问题。v-pre —— 跳过其所在节点的编译过程。可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
自定义指令:
定义语法:
局部指令:
- ```html
new Vue({
directives:{
//配置对象
指令名:{
bind(element, binding){},
inserted(element, binding){},
update(element, binding){},
}
}
})1
2
3
4
5
6
7
8
* ```vue
new Vue({
directives:{
//回调函数
指令名(){}
}
})
- ```html
全局指令:
1
Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)
配置对象中常用的3个回调:
- bind:指令与元素成功绑定时调用。
- inserted:指令所在元素被插入页面时调用。
- update:指令所在模板结构被重新解析时调用。
注:
指令定义时不加v-,但使用时要加v-;
指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
1 | <body> |
nextTick
- 语法:
this.$nextTick(回调函数) - 作用:在下一次 DOM 更新结束后执行其指定的回调。
- 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
如:
1 | method: { |
Vue实例生命周期
生命周期,又叫生命周期回调函数、生命周期函数、生命周期钩子。
本质:是Vue在关键时刻调用的一些特殊名称的函数。
常用的生命周期钩子:
mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
关于销毁Vue实例:
销毁后借助Vue开发者工具看不到任何信息。
销毁后自定义事件会失效,但原生DOM事件依然有效。
一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。

1 | <body> |
常用的数组方法
.filter()、.map()、.forEach]()、.find()、.findIndex()、.some()、.every()、.fill()
filter():检查数组中符合条件的所有元素,并放入一个新的数组。
- 改变原数组?否
- 对空数组进行检测?否
1
2
3
4const arr= [32, 33, 16, 40];
const arr1 = arr.filter(item => item >= 18)
console.log(arr) // [32, 33, 16, 40]
console.log(arr1) // [32, 33, 40]map():对原数组中的元素调用函数处理,并放入一个新的数组。
- 改变原数组?否
- 对空数组进行检测?否
1
2
3
4const arr= [4, 9, 16, 25];
const arr1 = arr.map(item => item+2)
console.log(arr) // [4, 9, 16, 25]
console.log(arr1) // [6, 11, 18, 27]forEach():用于调用数组的每个元素,并将元素传递给回调函数。
- 改变原数组?否
- 对空数组进行检测?是
- forEach()中不支持使用break(报错)和return(不能结束循环),有需要时可使用常规的for循环。
1
2
3
4
5const arr= [4, 9, 16, 25];
const arr1 = [];
arr.forEach(item => arr1.push(item))
console.log(arr) // [4, 9, 16, 25]
console.log(arr1) // [4, 9, 16, 25]find():返回数组内符合条件的的第一个元素的值,没有符合条件的元素返回 undefined。
改变原数组?否
对空数组进行检测?是
1
2
3
4
5
6const arr= [4, 9, 16, 25];
const b = arr.find(item => item>10)
const c = arr.find(item => item<1)
console.log(arr) // [4, 9, 16, 25]
console.log(b) // 16
console.log(c) // undefinedfindIndex():返回数组内符合条件的的第一个元素的下标,没有符合条件的元素则返回 -1。
改变原数组?否
对空数组进行检测?是
1
2
3
4
5
6const arr= [4, 9, 16, 25];
const b = arr.findIndex(item => item>10)
const c = arr.findIndex(item => item<1)
console.log(arr) // [4, 9, 16, 25]
console.log(b) // 2
console.log(c) // -1some():检测数组中是否有元素满足指定条件, 如果有一个元素满足条件,则返回true , 剩余的元素不会再执行检测, 没有元素满足则返回false。
改变原数组?否
对空数组进行检测?否
1
2
3
4
5
6const arr= [4, 9, 16, 25];
const b = arr.some(item => item>10)
const c = arr.some(item => item<1)
console.log(arr) // [4, 9, 16, 25]
console.log(b) // true
console.log(c) // falseevery():检测数组中的元素是否全部满足条件。
改变原数组?否
对空数组进行检测?否
1
2
3
4
5
6const arr= [4, 9, 16, 25];
const b = arr.every(item => item>10)
const c = arr.every(item => item>1)
console.log(arr) // [4, 9, 16, 25]
console.log(b) // false
console.log(c) // truefill():填充数组
改变原数组?是
对空数组进行检测?是
1
2
3
4
5
6
7
8
9
10
11
12const arr1= [4, 9, 16, 25];
const b = arr1.fill(100);
const arr2= [4, 9, 16, 25];
const c = arr2.fill(100, 2, 4) // 2为开始填充的起始位置,4为结束位置(不包含)
const arr3= [];
const d = arr3.fill(100);
console.log(arr1) // [100, 100, 100, 100]
console.log(b) // [100, 100, 100, 100]
console.log(arr2) // [4, 9, 100, 100]
console.log(c) // [4, 9, 100, 100]
console.log(arr3) // []
console.log(d) // []
常用事件
@blur —— 是当元素失去焦点时所触发的事件
1 | <input type="text" placeholder="请输入内容" @blur="blur"/> |
Vue组件化编程
模块与组件
模块:向外提供特定功能的js程序,作用是便于复用js,简化js编写,提高js运行效率;(因为js文件多而杂)
模块化:应用中的js都以模块来编写;
组件:用来实现局部功能效果的代码合集(html/css/js/图片音频等),作用:便于复用代码,简化项目编码,提高运行效率;(因为界面的功能很复杂)
组件化:应用中的功能以多组件的方式编写;
组件
组件是可复用的 Vue 实例,且带有一个名字。分为:
- 非单文件组件:一个文件中包含有n个组件
- 单文件组件:一个文件中只包含一个组件,文件后缀为 “.vue” (常用)
因为组件是可复用的 Vue 实例,所以它们与
new Vue接收相同的选项,例如data、computed、watch、methods以及生命周期钩子等。仅有的例外是像el这样根实例特有的选项。注:: 组件中的data必须为一个函数,不然,这个的组件的一个实例发生改变,其他实例也会发生相同的改变。
1
2
3
4
5 data() {
return {
name: 'Tom'
}
}
Vue中使用组件的步骤:
- 定义组件(创建组件);
- 使用Vue.extend(options)创建,其中options和new Vue(options) 时传入的options几乎一样,但也有点区别:
- 不能写el,因为最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
- data必须写成函数,这是为了 避免组件被复用时,数据存在引用关系。
- 使用Vue.extend(options)创建,其中options和new Vue(options) 时传入的options几乎一样,但也有点区别:
- 注册组件;
- 局部注册:靠new Vue的时候传入components选项
- 全局注册:靠Vue.component(‘组件名’,组件)
- 使用组件:即编写组件标签
关于组件的命名规范:
- 一个单词组成:
- 首字母小写:school
- 首字母大写:School (推荐)
- 多个单词组成:
- kebab-case命名:my-school
- CamelCase命名:MySchool(需要Vue脚手架支持)(推荐)
注:组件中可以使用name配置项 该组件在开发者工具中呈现的名字。
定义组件的简写方式:
- const school = Vue.extend(options) 可简写为:const school = options
VueComponent
组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
只需要写上组件标签,Vue解析时会帮我们创建该组件的实例对象,即Vue帮我们执行的:new VueComponent(options)。 —— 每次调用Vue.extend,返回的都是一个全新的VueComponent。
关于this的指向:
组件配置中——
data函数、methods中的函数、watch中的函数、computed中的函数 它们的 this 均是【VueComponent实例对象】。
new Vue(options)配置中——
data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。
一个重要的内置关系:VueComponent.prototype._proto_ === Vue.prototype
这个关系让 组件实例对象(vc)可以访问到 Vue原型上的属性、方法。

非单文件组件
1 | <body> |
单文件组件
一般将不同的页面功能 写成多个 .vue文件,由一个App.vue 进行汇总,然后让 main.js 引入 App.vue;
组件结构快捷模板: 输入 <v 按回车即可。
代码格式:
App.vue文件:
1 | <template> |
main.js:
1 | import App from './App.vue' |
使用Vue脚手架
Vue脚手架(Vue CLI,command line interface)是 Vue官方提供的标准化开发工具(开发平台)
初始化脚手架
初始化步骤 ——————
- 仅第一次执行时,全局安装@vue/cli, 在命令行窗口输入 “npm install -g @vue/cli”
- 切换到要创建项目的目录,输入命令 “vue create 项目名”
- 进入到项目中,启动项目 “npm run serve”
如出现下载缓慢可以 配置 npm 淘宝镜像:npm config set registry https://registry.npm.taobao.org
目录结构 ——————
- node_modules
- public
- index.html 主页面
- src
- assets 存放静态资源
- components 存放组件
- App.vue 用于汇总所有组件
- main.js 入口文件
- .gitignore git版本管理忽略配置
- **babel.config.js ** babel的配置文件
- package.json 应用包配置文件
- README.md 应用描述文件
- package-lock.json 包版本控制文件
render函数 ——————
在main.js中用到了render函数,作用是将App组件放入容器,因为在main.js中引入了精简的vue,其中不包含vue的模板解析器,所以render的作用就是代替模板解析器,节省掉这部分代码。
vue.js与vue.runtime.xxx.js的区别:
(1).vue.js是完整版的Vue,包含:核心功能+模板解析器。
(2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。
1 | render: h => h(App), |
vue.config.js 配置文件 ——————
- 使用命令 vue inspect > output.js 可以将Vue脚手架的默认配置输出到output.js文件中查看;
- 使用vue.config.js 配置文件可以对脚手架进行个性化定制,详情见 https://cli.vuejs.org/zh/config/
如:
1 | module.exports = { |
ref与props
ref属性:
- 被用来给元素或子组件注册引用信息(id的替代者)
- 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
- 使用方式:
- 打标识:
<h1 ref="xxx">.....</h1>或<School ref="xxx"></School> - 获取:
this.$refs.xxx
- 打标识:
props配置项:
功能:让组件接收外部传过来的数据,父组件==>子组件
传递数据:
<组件名 name="xxx"/>接收数据:
第一种方式(只接收):
props:['name']第二种方式(限制类型):
props:{name:String}第三种方式(限制类型、限制必要性、指定默认值):
1
2
3
4
5
6
7props:{
name:{
type:String, //类型
required:true, //必要性
default:'老王' //默认值
}
}
备注:props是只读的,Vue底层会监测对props的修改,如果进行了修改,就会发出警告;
若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。(props的内容会比data先准备好)
1
2
3
4
5
6
7//data中的'myName'的值是 props中的'name', 使用'name'不能修改,而'myName'就可以进行修改
data(){
return{
myName: this.name
}
}
props:['name','age']
混入mixin
功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步定义混合:定义 .js文件,写下混入的内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15export const mixin1 = {
methods: {
showName() {
alert(this.name)
}
},
}
export const mixin2 = {
data() {
return {
x: 100,
y: 200
}
}
}第二步使用混入:
全局混入:
Vue.mixin(xxx),在main.js中引入,全局有效
局部混入:mixins:['xxx'],在组件中各自引入1
2
3
4
5
6
7
8
9
10<script>
//局部引入
import {mixin1} from "../mixin"
export default {
name: "MySchool",
//配置项mixins: 配置多个混入
mixins: [mixin1]
};
</script>1
2
3//在main.js中 全局引入及配置
import {mixin1} from "./mixin"
Vue.mixin(mixin1)
插件
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
定义插件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//创建一个plugins.js文件,定义插件
对象.install = function (Vue, options) {
// 1. 添加全局过滤器
Vue.filter(....)
// 2. 添加全局指令
Vue.directive(....)
// 3. 配置全局混入(合)
Vue.mixin(....)
// 4. 添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
}1
2
3
4
5
6
7
8//只有一个对象
export default{
install(Vue){
Vue.directive(....)
Vue.mixin(....)
....
}
}使用插件:
1
2import plugins from './plugins'
Vue.use(plugins)Vue.use()
scoped样式
- 作用:让样式在局部生效,防止冲突。
- 写法:
<style scoped>
Todo-list案例
浏览器本地存储
存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
相关API:
xxxxxStorage.setItem('key', 'value');
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。xxxxxStorage.getItem('person'); 该方法接受一个键名作为参数,返回键名对应的值。
xxxxxStorage.removeItem('key'); 该方法接受一个键名作为参数,并把该键名从存储中删除。
xxxxxStorage.clear() 该方法会清空存储中的所有数据。
备注:
- SessionStorage存储的内容会随着浏览器窗口关闭而消失。
- LocalStorage存储的内容,需要手动清除才会消失。
xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null。JSON.parse(null)的结果依然是null。
组件自定义事件
自定义事件可用于组件间通信:子组件 ===> 父组件
使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
绑定自定义事件:
第一种方式,在父组件中:
<子组件名 @自定义事件名="父组件回调函数"/>或<Demo v-on:自定义事件名="父组件回调函数"/>第二种方式,在父组件中:
1
2
3
4
5<子组件名 ref="xxx"/>
......
mounted(){
this.$refs.xxx.$on('自定义事件名',this.父组件回调函数)
}若想让自定义事件只能触发一次,可以使用
once修饰符,或$once方法。
触发自定义事件:
this.$emit('自定义事件名',数据)解绑自定义事件
this.$off('自定义事件名')解绑多个则参数写成数组形式,解绑全部则不写参数
组件上也可以绑定原生DOM事件,需要使用
native修饰符。注意:通过
this.$refs.xxx.$on('自定义事件',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
全局事件总线
一种组件间通信的方式,适用于任意组件间通信,一般用于没有直接联系的组件之间。
安装全局事件总线:
1
2
3
4
5
6
7new Vue({
......
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
......
})使用事件总线:
接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
1
2
3
4
5
6
7methods(){
方法名(data){......}
}
......
mounted() {
this.$bus.$on('xxxx',this.方法名)
}提供数据:
this.$bus.$emit('xxxx',数据)
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
消息订阅与发布
一种组件间通信的方式,适用于任意组件间通信。
使用步骤:
安装pubsub:
npm i pubsub-js引入:
import pubsub from 'pubsub-js'接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
1
2
3
4
5
6
7methods(){
方法名(data){......}
}
......
mounted() {
this.pid = pubsub.subscribe('xxx',this.方法名) //订阅消息,接受到的数据中第一个参数是消息名,从第二个参数开始才是传过来的数据
}提供数据:
pubsub.publish('xxx',数据)最好在beforeDestroy钩子中,用
PubSub.unsubscribe(pid)去取消订阅。
过度与动画
Vue实现:
。。。。
第三方动画使用,如 Animate.css - 第三方动画 ;
Vue中的ajax/跨域
下载axios:
1
npm i axios
引入:
1
import axios from 'axios'
使用
跨域问题:
在vue.config.js
方式一:
1
2
3
4
5 module.experts={
devServer:{
proxy: 'http://localhost:80' //获取数据的地址
}
}
- 优点:配置简单,请求资源时直接发给前端(8080)即可。
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
前端请求的地址端口要改成 8080 (与自己页面的端口一致)
方式二:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 module.experts={
proxy: {
'/api1': {// 匹配所有以 '/api1'开头的请求路径
target: 'http://localhost:80',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api1': ''}
},
'/api2': {// 匹配所有以 '/api2'开头的请求路径
target: 'http://localhost:5001',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}
}
}
- 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
- 缺点:配置略微繁琐,请求资源时必须加前缀。
vuex
vuex是 在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
使用场景:多个组件需要共享数据时
安装:num i vuex@3(vue2中只能用vuex3版本,vue3用vuex4版本)
搭建环境
创建文件:
src/store/index.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象——响应组件中用户的动作
const actions = {}
//准备mutations对象——修改state中的数据
const mutations = {}
//准备state对象——保存具体的数据
const state = {}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})在
main.js中创建vm时传入store配置项1
2
3
4
5
6
7
8
9
10
11......
//引入store
import store from './store'
......
//创建vm,使用store配置项
new Vue({
el:'#app',
render: h => h(App),
store,
})
使用:
1 | import Vue from 'vue' |
组件中读取vuex中的数据:[this.]$store.state.数据名
组件中修改vuex中的数据:this.$store.dispatch('action中的方法名',数据) 或 this.this.$store.commit('mutations中的方法名',数据)
备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写
dispatch,直接编写commit
getters
概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。
在
store.js中追加getters配置1
2
3
4
5
6
7
8
9
10
11
12
13......
const getters = {
bigSum(state){
return state.sum * 10
}
}
//创建并暴露store
export default new Vuex.Store({
......
getters
})组件中读取数据:
$store.getters.bigSum
四个map方法
先导入map方法:
1 | import {mapState,mapGetters,mapMutations,mapActions} from 'vuex' |
mapState方法:用于帮助我们映射
state中的数据为计算属性… 三个点是es6中的拓展运算符
1
2
3
4
5
6
7computed: {
//借助mapState生成计算属性:sum、school、subject(对象写法)
...mapState({sum:'sum',school:'school',subject:'subject'}),
//借助mapState生成计算属性:sum、school、subject(数组写法)
...mapState(['sum','school','subject']),
},mapGetters方法:用于帮助我们映射
getters中的数据为计算属性1
2
3
4
5
6
7computed: {
//借助mapGetters生成计算属性:bigSum(对象写法)
...mapGetters({bigSum:'bigSum'}),
//借助mapGetters生成计算属性:bigSum(数组写法)
...mapGetters(['bigSum'])
},mapActions方法:用于帮助我们生成与
actions对话的方法,即:包含$store.dispatch(xxx)的函数1
2
3
4
5
6
7methods:{
//靠mapActions生成:incrementOdd、incrementWait(对象形式)
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
//靠mapActions生成:incrementOdd、incrementWait(数组形式)
...mapActions(['jiaOdd','jiaWait'])
}mapMutations方法:用于帮助我们生成与
mutations对话的方法,即:包含$store.commit(xxx)的函数1
2
3
4
5
6
7methods:{
//靠mapActions生成:increment、decrement(对象形式)
...mapMutations({increment:'JIA',decrement:'JIAN'}),
//靠mapMutations生成:JIA、JIAN(对象形式)
...mapMutations(['JIA','JIAN']),
}
备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。
模块化+命名空间
目的:让代码更好维护,让多种数据分类更加明确。
修改
store.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25const countAbout = {
namespaced:true,//开启命名空间
state:{x:1},
mutations: { ... },
actions: { ... },
getters: {
bigSum(state){
return state.sum * 10
}
}
}
const personAbout = {
namespaced:true,//开启命名空间
state:{ ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
countAbout,
personAbout
}
})也可以将将模块分成多个 js 文件,再在 index.js导入使用
开启命名空间后,组件中读取state数据:
1
2
3
4//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),开启命名空间后,组件中读取getters数据:
1
2
3
4//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])开启命名空间后,组件中调用dispatch
1
2
3
4//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})开启命名空间后,组件中调用commit
1
2
3
4//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
vue-router
- 概念: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。
- 前端路由:key是路径,value是组件。
注:
- 路由组件通常存放在
pages文件夹,一般组件通常存放在components文件夹。- 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
- 每个组件都有自己的
$route属性,里面存储着自己的路由信息。- 整个应用只有一个router,可以通过组件的
$router属性获取到。
使用
安装vue-router,命令:
npm i vue-router@3新建router/index.js,配置路由信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27//引入VueRouter
import VueRouter from 'vue-router'
//引入Luyou 组件
import About from '../components/About'
import Home from '../components/Home'
//创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home,
//嵌套/多级路由
children:[{
path: 'news',
component: News
}],
}
]
})
//暴露router
export default router在main.js中引入:
1
2
3
4
5
6
7
8
9
10import VueRouter from 'vue-router'
import router from './router'
Vue.use(VueRouter)
new Vue({
el: '#app',
render: h => h(App),
router: router
})在组件中使用:
实现切换:
1
2<router-link class="xxx" active-class="active" to="/about(即路由配置中的path)">About</router-link>
<router-link class="xxx" active-class="active" to="/home/news">About</router-link> 浏览器的历史记录有两种写入方式:分别为
push和replace,push是追加历史记录,replace是替换当前记录。
路由跳转时候默认为push, 如果要换成replace,则在 <router-link>中加上replace属性,即 <router-link replace>。指定展示位置:
1
<router-view></router-view>
给路由命名:
作用:可以简化路由的跳转。
如何使用
给路由命名,使用 name属性 给路由起名:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello' //给路由命名
path:'welcome',
component:Hello,
}
]
}
]
}简化跳转:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<!--简化前,需要写完整的路径 -->
<router-link to="/demo/test/welcome">跳转</router-link>
<!--简化后,直接通过名字跳转 -->
<router-link :to="{name:'hello'}">跳转</router-link>
<!--简化写法配合传递参数 -->
<router-link
:to="{
name:'hello',
query:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
传参
query 传参:
传递参数
1
2
3
4
5
6
7
8
9
10
11
12
13<!-- 跳转并携带query参数,to的字符串写法 -->
<router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
<!-- 跳转并携带query参数,to的对象写法 -->
<router-link
:to="{
path:'/home/message/detail',
query:{
id:666,
title:'你好'
}
}"
>跳转</router-link>接收参数:
1
2$route.query.id
$route.query.title
params传参:
配置路由,
声明接收params参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
component:Message,
children:[
{
name:'xiangqing',
path:'detail/:id/:title', //使用占位符声明接收params参数
component:Detail
}
]
}
]
}传递参数
1
2
3
4
5
6
7
8
9
10
11
12
13<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="/home/message/detail/666/你好">跳转</router-link>
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link
:to="{
name:'xiangqing',
params:{
id:666,
title:'你好'
}
}"
>跳转</router-link>特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
接收参数:
1
2$route.params.id
$route.params.title
路由的props配置:
作用:让路由组件更方便的收到参数
写在路由配置中
1 | { |
编程式路由导航
作用:不借助
<router-link>实现路由跳转,让路由跳转更加灵活
(<router-link>最终呈现在页面上其实是<a>标签)具体编码:
可以写在方法之类的地方,实现其他标签进行路由跳转功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//$router的API,对象信息的格式和params传参格式一样
this.$router.push({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.replace({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进也可后退
组件生命周期钩子
- 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
- 具体名字:
activated路由组件被激活时触发。deactivated路由组件失活时触发。
如:组件激活时开启定时器,失活时清除定时器
1 | <script> |
路由守卫
作用:对路由进行权限控制
分类:全局守卫、独享守卫、组件内守卫
全局守卫:
路由信息中的 meta 可以任意写上一些键值对,如 meta : { isAuth : true }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{
console.log('beforeEach',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
next() //放行
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next() //放行
}
})
//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
console.log('afterEach',to,from)
if(to.meta.title){
document.title = to.meta.title //修改网页的title
}else{
document.title = 'vue_test'
}
})独享守卫:(直接写在路由信息里,且独显守卫只有前置,没有后置)
1
2
3
4
5
6
7
8
9
10
11
12
13beforeEnter(to,from,next){
console.log('beforeEnter',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){
next()
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next()
}
}组件内守卫:
1
2
3
4
5
6//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}
路由的工作模式
- 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
- hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
- hash模式:
- 地址中永远带着#号,不美观 。
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
- 兼容性较好。
- history模式:
- 地址干净,美观 。
- 兼容性和hash模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
1 | //写在路由文件的index.js中 |
打包
1 | npm run build |
执行命令后会出现dist文件,即打包后的文件内容。
Vue UI组件库
安装:
1 | npm i element-ui |
全局引入:
1 | import Vue from 'vue'; |
按需引入:
安装babel-plugin-component
1
npm install babel-plugin-component -D
修改 babel.config.js 文件,添加内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14module.exports = {
presets: [
["@babel/preset-env", { "modules": false }]
],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}按需引入:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import Vue from 'vue';
import { Button, Select } from 'element-ui';
import App from './App.vue';
Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
/* 或写为
* Vue.use(Button)
* Vue.use(Select)
*/
new Vue({
el: '#app',
render: h => h(App)
});
Vue3
工程结构变更:
1 | // 引入的不再是Vue的构造函数, 而是引入一个叫createApp的工厂函数 |
1 | <template> |
Composition API
即组合式API。
- 在传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。
- 在新的Composition API 中,我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在setup中。
setup
在vue2中,数据写在data中,方法写在methods中,而在vue3中,数据、方法等等均写在setup中。 setup是所有composition API 的基础。
setup的值为一个函数,它的返回值有两种:
若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<template>
<h1>我是App组件, setup</h1>
<h2>姓名: {{ myname }}</h2>
<h2>年龄: {{ age }}</h2>
<button @click="sayHello">打招呼</button>
</template>
<script>
export default {
name: 'App',
setup: function () {
let name = "Tom"
let age = 18
function sayHello() {
alert(`我叫${name}, 今年${age}岁了, 你好`)
}
return {
myname: name,
age,
sayHello
}
}
}
</script>若返回一个渲染函数:则可以自定义渲染内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<template>
<h1>我是App组件, setup</h1>
</template>
<script>
export default {
name: 'App',
setup: function () {
// let name = "Tom"
// let age = 18
// function sayHello() {
// alert(`我叫${name}, 今年${age}岁了, 你好`)
// }
return ()=>{return h('h1', 'zzc')}
}
}
</script>
注意点:
尽量不要与Vue2.x配置混用
- Vue2.x配置(data、methos、computed…)中可以访问到setup中的属性、方法。
- 但在setup中不能访问到Vue2.x配置(data、methos、computed…)。
- 如果有重名, setup优先。
setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性。
后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合,如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<template>
<div class="child">
<h3>我是Child组件</h3>
{{sum}}
</div>
</template>
<script>
import {ref} from 'vue'
export default {
name:'Child',
async setup(){
let sum = ref(0)
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve({sum})
},3000)
})
return await p
}
}
</script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>稍等,加载中...</h3>
</template>
</Suspense>
</div>
</template>
<script>
// import Child from './components/Child'//静态引入
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child')) //异步引入
export default {
name:'App',
components:{Child},
}
</script>
ref函数
作用: 定义一个响应式的数据
语法:
const xxx = ref(initValue)- 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
- JS中操作数据需要”.value”:
xxx.value= yyy - 而模板中读取数据: 不需要.value,直接:
<div>{{xxx}}</div>
备注:
接收的数据可以是:基本类型、也可以是对象类型。
基本类型的数据:响应式依然是靠
Object.defineProperty()的get与set完成的。对象类型的数据:内部 “求助”了Vue3.0中的一个新函数——
reactive函数。
1 | <template> |
reactive函数
- 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用
ref函数) - 语法:
const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象),js操作不同加 “.value” - reactive定义的响应式数据是“深层次的”。
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
1 | <template> |
Vue3.0中的响应式原理
vue2.x的响应式
实现原理:
对象类型:通过
Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
1
2
3
4Object.defineProperty(data, 'count', {
get () {},
set () {}
})
存在问题:
- 新增属性、删除属性,界面不会更新。
- 直接通过下标修改数组, 界面不会自动更新。
- 解决:需要用this.$set,this.$delete,Vue.set,Vue.delete
Vue3.0的响应式
实现原理:
通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
通过Reflect(反射): 对源对象的属性进行操作。
MDN文档中描述的Proxy与Reflect:
Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
Reflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20const p = new Proxy(person, {
// 参数target指的是源对象, propName是拦截到的属性名
// 拦截读取属性值
get(target,propName){
console.log(`有人读取了p身上的${propName}属性`)
return Reflect.get(target,propName)
},
// 拦截设置属性值或添加新属性
set(target,propName,value){
console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`)
Reflect.set(target,propName,value)
},
// 拦截删除属性
deleteProperty(target,propName){
console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
return Reflect.deleteProperty(target,propName)
}
})
p.name = 'tom'
reactive对比ref
- 从定义数据角度对比:
- ref用来定义:基本类型数据。
- reactive用来定义:对象(或数组)类型数据。
- 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过
reactive转为代理对象。
- 从原理角度对比:
- ref通过
Object.defineProperty()的get与set来实现响应式(数据劫持)。 - reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
- ref通过
- 从使用角度对比:
- ref定义的数据:操作数据需要
.value,读取数据时模板中直接读取不需要.value。 - reactive定义的数据:操作数据与读取数据:均不需要
.value。
- ref定义的数据:操作数据需要
setup的两个注意点
- setup执行的时机
- 在beforeCreate之前执行一次,this是undefined,所以setup中是不能用 this 的。
- setup的参数
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
- context:上下文对象
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
this.$attrs。 - slots: 收到的插槽内容, 相当于
this.$slots。 - emit: 分发自定义事件的函数, 相当于
this.$emit。
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
1 | <!-- 组件"Demo" --> |
1 | <template> |
计算属性与监视
computed函数
与Vue2.x中computed配置功能一致
写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import {computed} from 'vue'
setup(){
...
//计算属性——简写
let fullName = computed(()=>{
return person.firstName + '-' + person.lastName
})
//计算属性——完整
let fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
}
watch函数
与Vue2.x中watch配置功能一致
注意点: 监视reactive定义的响应式数据中某个属性时,不能用 “对象名.属性名”,而要用箭头函数返回属性,如 ()=>person.age
两个小“坑”:
- 监视reactive定义的响应式数据时:oldValue无法正确获取,且会强制开启了深度监视(deep配置失效)。
- 监视reactive定义的响应式数据中某个属性时:deep配置有效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32//情况一:监视ref定义的响应式数据
watch(sum, (newValue,oldValue)=>{
console.log('sum变化了',newValue,oldValue)
},{immediate:true})
//情况二:监视多个ref定义的响应式数据
watch([sum,msg], (newValue,oldValue)=>{
console.log('sum或msg变化了',newValue,oldValue)
})
/* 情况三:监视reactive定义的响应式数据
1. 无法正确获得oldValue!!
2. 会强制开启了深度监视
*/
watch(person, (newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>person.job, (newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true})
//情况五:监视reactive定义的响应式数据中的某些属性
watch([()=>person.job,()=>person.name], (newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true})
//特殊情况
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
watchEffect函数
watch的使用:既要指明监视的属性,也要指明监视的回调。
watchEffect的使用:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
1
2
3
4
5
6//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
生命周期
- Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
beforeDestroy改名为beforeUnmountdestroyed改名为unmounted
- Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
beforeCreate===>setup()created===>setup()beforeMount===>onBeforeMountmounted===>onMountedbeforeUpdate===>onBeforeUpdateupdated===>onUpdatedbeforeUnmount==>onBeforeUnmountunmounted===>onUnmounted
自定义hook函数
- 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。
- 类似于vue2.x中的mixin。
- 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。
例子:
将复用的代码封装成hook函数,这里是获取鼠标坐标的功能,放入hooks/usePoint.js(放hook函数的文件一个命名为useXxxx):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26import {reactive,onMounted,onBeforeUnmount} from 'vue'
export default function (){
//实现鼠标“打点”相关的数据
let point = reactive({
x:0,
y:0
})
//实现鼠标“打点”相关的方法
function savePoint(event){
point.x = event.pageX
point.y = event.pageY
console.log(event.pageX,event.pageY)
}
//实现鼠标“打点”相关的生命周期钩子
onMounted(()=>{
window.addEventListener('click',savePoint)
})
onBeforeUnmount(()=>{
window.removeEventListener('click',savePoint)
})
return point
}引用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<template>
<h2>当前点击时鼠标的坐标为:x:{{point.x}},y:{{point.y}}</h2>
</template>
<script>
import usePoint from '../hooks/usePoint'
export default {
name: 'Demo',
setup(){
//数据
let point = usePoint()
return {
sum,
point
}
}
}
</script>
工具
响应式数据的判断
isRef: 检查一个值是否为一个 ref 对象
1
2
3
4
5let foo: unknown
if (isRef(foo)) {
// foo 的类型被收窄为了 Ref<unknown>
foo.value
}isReactive: 检查一个对象是否是由
reactive创建的响应式代理isReadonly: 检查一个对象是否是由
readonly创建的只读代理isProxy: 检查一个对象是否是由
reactive或者readonly方法创建的代理
toRef
作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
语法:
const name = toRef(person,'name')应用: 要将响应式对象中的某个属性单独提供给外部使用时。
扩展:
toRefs与toRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)(返回对象时前面加 “…” 可展开对象的属性)
1 | <template> |
unref
如果参数是 ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val 计算的一个语法糖。
1 | function useFoo(x: number | Ref<number>) { |
其它 Composition API
shallowReactive 与 shallowRef
- shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
- shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。(即传入的是对象,就不做响应式处理)
- 什么时候使用?
- shallowReactive: 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 。
- shallowRef:如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 。
1 | <template> |
readonly 与 shallowReadonly
- readonly: 让一个响应式数据变为只读的(深只读)。
- shallowReadonly:让一个响应式数据的外层数据变为只读的(浅只读)。
- 应用场景: 不希望数据被修改时。
1 | <template> |
toRaw 与 markRaw
- toRaw:
- 作用:将一个由
reactive生成的响应式对象转为普通对象。 - 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
- 作用:将一个由
- markRaw:
- 作用:标记一个对象,使其永远不会再成为响应式对象。
- 应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类库等。
- 当渲染 具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
1 | <template> |
customRef
作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
实现防抖效果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38<template>
<input type="text" v-model="keyword">
<h3>{{keyword}}</h3>
</template>
<script>
import {ref,customRef} from 'vue'
export default {
name:'Demo',
setup(){
// let keyword = ref('hello') //使用Vue准备好的内置ref
//自定义一个myRef
function myRef(value,delay){
let timer
//通过customRef去实现自定义
return customRef((track,trigger)=>{
return{
get(){
track() //告诉Vue追踪该值的变化
return value
},
set(newValue){
clearTimeout(timer) // 清除并只保持一个定时器,可以防抖(避免积压很多定时器去解析模板)
timer = setTimeout(()=>{
value = newValue
trigger() //告诉Vue去重新解析模板, 即更新界面
},delay)
}
}
})
}
let keyword = myRef('hello',500) //使用程序员自定义的ref
return {
keyword
}
}
}
</script>
provide 与 inject
作用:实现祖先与后代组件间通信
套路:父组件有一个
provide选项来提供数据,后代组件有一个inject选项来开始使用这些数据具体写法:
祖组件中:
1
2
3
4
5
6setup(){
......
let car = reactive({name:'奔驰',price:'40万'})
provide('car',car)
......
}后代组件中:
1
2
3
4
5
6setup(props,context){
......
const car = inject('car')
return {car}
......
}
新的组件
Fragment
- 在Vue2中: 组件必须有一个根标签
- 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
- 好处: 减少标签层级, 减小内存占用
Teleport
什么是Teleport?——
Teleport是一种能够将我们的组件html结构移动到指定位置的技术。1
2
3
4
5
6
7
8<teleport to="body"> <!-- 这里的to属性可写html标签或选择器id -->
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<button @click="isShow = false">关闭弹窗</button>
</div>
</div>
</teleport>
Suspense
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
使用步骤:
异步引入组件
1
2
3// import Child from './components/Child' //静态引入
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue')) //异步引入使用
Suspense包裹组件,并配置好default与fallback1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<!-- 正常展示的内容 -->
<template v-slot:default>
<Child/>
</template>
<!-- 加载很慢时先暂时展示的内容 -->
<template v-slot:fallback>
<h3>加载中.....</h3>
</template>
</Suspense>
</div>
</template>
其他
全局API的转移
Vue 2.x 有许多全局 API 和配置。
例如:注册全局组件、注册全局指令等。
1
2
3
4
5
6
7
8
9
10
11
12//注册全局组件
Vue.component('MyButton', {
data: () => ({
count: 0
}),
template: '<button @click="count++">Clicked {{ count }} times.</button>'
})
//注册全局指令
Vue.directive('focus', {
inserted: el => el.focus()
}
Vue3.0中对这些API做出了调整:
将全局的API,即:
Vue.xxx调整到应用实例(app)上2.x 全局 API( Vue)3.x 实例 API ( app)Vue.config.xxxx app.config.xxxx Vue.config.productionTip 移除 Vue.component app.component Vue.directive app.directive Vue.mixin app.mixin Vue.use app.use Vue.prototype app.config.globalProperties
其他改变
data选项应始终被声明为一个函数。
过度类名的更改:
Vue2.x写法
1
2
3
4
5
6
7
8.v-enter,
.v-leave-to {
opacity: 0;
}
.v-leave,
.v-enter-to {
opacity: 1;
}Vue3.x写法
1
2
3
4
5
6
7
8
9.v-enter-from,
.v-leave-to {
opacity: 0;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
移除keyCode作为 v-on 的修饰符,同时也不再支持
config.keyCodes移除
v-on.native修饰符父组件中绑定事件
1
2
3
4<my-component
v-on:close="handleComponentEvent"
v-on:click="handleNativeClickEvent"
/>子组件中声明自定义事件
1
2
3
4
5<script>
export default {
emits: ['close']
}
</script>
移除过滤器(filter)
过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。
Tomcat
Web服务器:是一个应用程序(软件),对HTTP协议的操作进行封装,使得程序员不必直接对协议进行操作,让Web开发更加便捷。主要功能是 “提供网上信息浏览服务”。
Tomcat :是Apache软件基金会一个核心项目,是一个开源免费的轻量级Web服务器,支持Servlet / JSP 和少量JavaEE规范。
启动Tomcat服务器:
到bin目录下,双击startup.bat启动,双击shutdown.bat关闭
修改Tomcat的端口号:
mysql默认的端口号是3306,Tomcat默认的端口号是8080,HTTP协议默认的端口号是80;
修改:到conf目录下,修改server.xml配置文件
1
2
3
4
5<!-- 在69行附近 -->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- 修改其中的port="8080"即可 -->
Tomcat 部署项目:将项目放置到webapps目录下,即部署完成。
- 一般JavaWeb项目会被打成war包,然后将war包放到webapps目录下,Tomcat会自动解压缩war文件
第二种部署方式:
项目不需要放在webapps下,可以放在任何地方;
找到 Tomcat 下的 conf 目录\Catalina\localhost\ 下,创建配置文件,如abc.xml,配置文件内容如下:
1
2
3
4
5
6 <!-- Context 表示一个工程上下文
path 表示工程的访问路径:/abc
docBase 表示你的工程目录在哪里
即url中输入path,也就是/abc,路径就会到docBase那里
-->
<Context path="/abc" docBase=" " /
- 当浏览器地址栏输入访问地址为: http ://ip:port/ 时,也就是没有工程名时,默认访问webapps下的ROOT工程;
- 当浏览器地址栏输入访问地址为: http ://ip:port/ 工程名/ 时,也就是没有资源名时,默认访问工程下的index.html页面;
在IDEA中创建Maven Web项目:
- 编译后的 java字节码文件和resources的资源文件,放到WEB-INF 下的 classes目录下;
- pom.xml中依赖坐标对应的jar包,放入WEB-INF下的Iib目录下;
- WEB-INF是一个受服务器保护的目录,浏览器无法直接访问此目录的内容;
创建方式:


WEB-INF放到webapp目录下
在IDEA中使用Tomcat:
- 方法一:集成本地Tomcat

- 方法二:Tomcat Maven插件

1 | <build> |
Servlet
JavaWeb三大组件:Servlet、.Filter、Listener;
Servlet是 Java提供的一门 动态web资源开发技术。
Servlet是javaEE规范之一,也就是一个接口,将来我们需要定义Servlet类实现Servlet接口,并由web服务器运行Servlet。
Servlet可以接收客户端发过来的请求,并响应数据给客户端;
1 | public interface Servlet |
使用servlet:
- 创建Web项目,导入Servlet依赖坐标
1 | <dependencies> |
创建:定义一个类,实现Servlet接口,并重写接口中所有方法。
1
2
3
4public class ServletDemo1 implements Servlet{
public void service(){}
....
}配置:在类上使用@WebServlet注解,配置该Servlet的访问路径
1
2
public class ServletDemo1 implements Servlet{}访问:启动Tomcat,浏览器输入URL访问该Servlet
1
http://localhost:8080/web-demo/demo1
执行流程

生命周期
Servlet运行在Servlet容器(web服务器)中,其生命周期由容器来管理,分为4个阶段:
加载和实例化:默认情况下,当Servlet第一次被访问时,由容器创建Servlet对象
初始化:在Servlet实例化之后,容器将调用Servlet的 init()方法初始化这个对象,完成一些如加载配置文件、创建连接等初始化的工作。该方法只调用一次
可以在注解中修改init()调用的时机:
1
2
3
//负整数:第一次被访问时创建 Servlet对象
//0或正整数:服务器启动时创建Servlet对象,数字越小优先级越高请求处理/服务:每次请求Servlet时,Servlet容器都会调用Servlet的service()方法对请求进行处理。
服务终止:当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法完成资源的释放。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收
Servlet体系结构

开发B/S架构的web项目,都是针对HTTP协议,所以自定义的Servlet都是继承HttpServlet。
1 |
|
ServletConfig类
ServletConfig类是Servlet程序的配置信息类。
- Servlet程序和ServletConfig对象都是由Tomcat负责创建;
- Servlet程序默认第一次访问时创建,而每个Servlet程序创建时都会创建一个对应的ServletConfig对象。
方法:
- getServletName() —— 获取Servlet程序的别名 servlet-name 的值
- getInitParameter(“ “) —— 获取初始化参数 init-param (括号里写param-name的值,得到param-value值)
- getServletContext() —— 获取ServletContext对象
注: 如果重写了HttpServlet类的init方法,一定要**调用父类的init(ServletConfig)**,因为HttpServlet类的init方法里,保存了ServletConfig对象,不然的话,调用getInitParameter(“ “)方法会报错。
web.xml配置:设置servlet程序的别名,访问路径
1 | <!-- servlet 标签给 Tomcat 配置 Servlet 程序 --> |
ServletContext
- ServletContext是一个接口,它表示Servlet上下文对象;
- 一个 web工程中只有一个 ServletContext 对象实例;
- ServletContext 对象是一个域对象;
- ServletContext 是在 web工程部署启动的时候创建。在 web工程停止的时候销毁;
域对象: 是可以像Map一样存取数据的对象,这里的域指存取数据的操作范围,即整个web工程;
1 | //获取ServletContext对象 |
方法:
getInitParameter(“…”) —— 获取 web.xml 中配置的上下文参数 context-param (括号里写param-name值,得到param-value值)
getContextPath() —— 获取当前的工程路径,格式: /工程路径
getRealPath(“/“) —— 获取工程部署后在服务器硬盘上的绝对路径
getRealPath(“/“) —— 工程部署的路径
getRealPath(“/css”) —— 工程下 css 目录的绝对路径
像 Map 一样存取数
存数据 取数据 删除数据 Map put() get() remove() 域对象 setAttribute() getAttribute() removeAttribute()
1 | <!-- web.xml中相关配置信息 --> |
HTTP协议
HTTP协议是 客户端和服务器之间通信时要发送的数据,需要遵守的规则; HTTP 协议中的数据又叫报文。
- 请求 Request:客户端给服务器发送数据。
- GET请求
- POST请求
- 响应 Response:服务器给客户端回传数据。
GET请求的组成:
请求行: 请求方式 请求的资源路径 请求的协议版本号
如: GET /servlet_1/a.html HTTP/1.1
请求头:以键值对的形式组成
- Accept: 告诉服务器,客户端可以接收的数据类型;
- Accept-Language:告诉服务器,客户端可以接收的语言类型,如:zh_CN指中文中国,en_US指英文美国;
- User-Agent:就是浏览器的信息;
- Accept-Encoding:告诉服务器,客户端可以接收的数据编码(压缩)格式
- Host:表示请求的股务器ip和端口号
- Connection:告诉服务器诗求连接如何处理
- Keep-Alive :告诉服务器回传数据不要马上关闭,保持一小段时间的连接;
- Closed:马上关闭
POST请求的组成:
请求行: 请求方式 请求的资源路径 请求的协议版本号
请求头:
Accept:表示客户端可以接收的数据类型;
Accept-Language:表示客户端可以接收的语言类型;
Referer:表示请求发起时,浏览器地址栏中的地址(从哪来)
User-Agent:表示浏览器的信息
Content-Type:表示发送的数据的类型
如:application/x-www-form-ur lencoded : 表示提交的数据格式是 name=value&name=value,然后对其进行urI编码,使非英文内容转换为:%xx%xx
multipart/form-data :表示以多段的形式提交数据给服务器 (以流的形式提交,用于上传)
Content-Lnegth:表示发送的数据的长度
Cache-Control:表示如何控制缓存, no-cache 指不缓存
空行 (请求头和请求体之间隔一空行)
请求体:发给服务器的数据
区分GET请求和POST请求:
POST请求有:
- form 标签中 method=post
GET请求有:
- form 标签 method=get
- a标签
- link标签引入css
- Script标签引入js文件
- img标签引入图片
- iframe 引入html页面
- 在浏览器地址栏中输入地址后敲回车
响应 的组成:
- 响应行:响应的协议和版本号 状态码 状态描述符
- 响应头:以键值对形式组成
- Server:表示服务器的信息;
- Content-Type:表示响应体的数据类型;
- Content-Length:响应体的长度;
- Date:请求响应的时间(格林时间);
- 空行
- 响应体:(就是回传给客户端的数据)
常用响应码:
- 200 —— 请求成功
- 302 —— 请求重定向
- 404 —— 表示请求服务器已收到,但所要的数据不存在/请求地址错误
- 500 —— 表示服务器已经收到请求,但服务器内部错误(错误代码)
MIME类型
MIME类型 指的是HTTP协议中的数据类型 —— Multipurpose Internet Mail Extensions 多功能Internet邮件扩充服务。
格式:大类型/小类型
常见的MIME类型:
| 文件 | 后缀 | MIME类型 |
|---|---|---|
| 超文本标记语言文本 | .html .htm | text/html |
| 普通文本 | .txt | text/plain |
| RTF文本 | .rtf | application/rtf |
| GIF图形 | .gif | image/gif |
| JPEG图形 | .jpeg .jpg | image/jpeg |
| au声音文件 | .au | audio/basic |
| MIDI音乐文件 | .mid .midi | audio/midi, audio/x-midi |
| RealAudio音乐文件 | .ra .ram | audio/x-pn-realaudio |
| AVI文件 | .avi | vidio/mpeg |
| GZIP文件 | .gz | video/x-msvideo |
| TAR文件 | .tar | application/x-gzip |
| MPEG文件 | v | application/x-tar |
HttpServletRequest类
每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 HTTP 协议信息解析好封装到 Request 对象中。 然后传递到 service 方法(doGet 和 doPost)中给我们使用。
而通过 HttpServletRequest 对象,可以获取到所有 请求 的信息。
方法:
- getRequestURI() 获取请求的资源路径
- getRequestURL() 获取请求的统一资源定位符(绝对路径)
- getRemoteHost() 获取客户端的ip地址
- getHeader() 获取请求头
- getParameter() 获取请求的参数
- getParameterValues() 获取请求的参数(多个值的时候使用)
- getMethod() 获取请求的方式GET或POST
- setAttribute(key,value) 设置域数据
- getAttribute(key) 获取域数据
- getRequestDispatcher() 获取请求转发对象
如果doPost中获取数据出现中文乱码,可以设置请求体的字符集为UTF-8:
1
2
3 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8"); //设置字符集要写在获取请求参数之前,否则不生效
}
HttpServletResponse类
HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,Tomcat 服务器都会创建一个 Response 对象传递给 Servlet 程序去使用。
HttpServletRequest表示请求过来的信息,HttpServletResponse 表示所有响应的信息,;我们如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse对象来进行设置。
两个输出流:
- 字节流 getOutputStream() —— 常用于下载(传递二进制数据)
- 字符流 getWriter() —— 常用于回传字符串(常用)
两个流同时只能使用一个。
1 | public class ResponseIOServlet extends HttpServlet { |
请求重定向
是指客户端给服务器发请求,然后服务器告诉客户端说。我给你一些地址。你去新地址访问,叫请求 重定向。(因为之前的地址可能已经被废弃)
1 | response.sendRedirect("新地址"); |
请求转发/内部转发:
意思是:服务器收到请求后,从一个资源跳转到另一个资源。即服务器内部转发。
1 | request.getRequestDispatcher("demo07").forward(request,response); |

1 | //办事处1 —— Servlet1程序 |
保存作用域
作用域一般有四个:
page(页面级别,现在几乎不用)
request(一次请求响应范围)
重定向中,客户端实际发起了两次请求;所以在第一次请求中在request的作用域中保存数据,数据不会保存到第二次请求。
1
2
3
4
5//在 /Servlet_1 中
req.setAttribute("key","value");
req.sendRedirect("/Servlet_2");
//在 /Servlet_2 中
System.out.println(req.getAttribute("key")); //打印null,不能得到结果session(一次会话范围)
1
2
3
4
5//在 /Servlet_1 中
req.getSession().setAttribute("key","value");
req.sendRedirect("/Servlet_2");
//在 /Servlet_2 中
System.out.println(req.getAttribute("key")); //能得到结果application(整个应用程序范围)
1
2
3
4
5
6
7
8//这里假设/Servlet_1和/Servlet_2是两个不同的用户
//在 /Servlet_1 中
ServletContext application = req.getServletContext();
application.setAttribute("key","value");
req.sendRedirect("/Servlet_2");
//在 /Servlet_2 中
ServletContext application = req.getServletContext();
System.out.println(application.getAttribute("key")); //能得到结果
文件传输
文件上传:
1 | <form action="url地址" enctype="multipart/form-data" method="提交方式" name="名称"> |
multipart/form-data表示提交的数据,以多段(每一个表单项一个数据段)的形式进行拼接,然后以二进制流的形式发送给服务器
查看http协议内容时,context-type部分有属性boundary:表示每段数据的分隔符,分隔符是由浏览器每次都随机生成,它就是每段上传数据的分界符。
文件接收:
1 | <!-- 上传文件的表单 --> |
1 | //fileupload类库的适用: |
文件下载:
1 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { |
Filter
Filter表示过滤器,可以把对资源的请求拦截下来;
通常用过滤器完成一些通用的操作,比如:权限控制、统一编码处理、敏感字符处理等等…
JavaWeb三大组件:Servlet、.Filter、Listener;
使用:实现Filter接口 ,并重写三个方法;注意是javax.servlet. 下的Filter接口。
1 | //设置拦截路径, “/*” 指拦截全部 |
Filter执行流程:
执行放行前逻辑——放行——访问资源——执行放行后逻辑
Filter拦截路径配置
1 | //括号中设置拦截路径 |
- 拦截具体的资源: /index.jsp 只有访问index.jsp时才会被拦截;
- 目录拦截:/user/* 访问/user下的所有资源都会被拦截;
- 后缀名拦截: *.jsp 访问后缀名为jsp的资源都会被拦截;
- 拦截所有: /*
如果要拦截多个页面或分散文件,要在括号例写urlPatterns属性,然后在里面写属性的值,逗号分割;
1
过滤器链
一个Web应用,可以配置多个过滤器,这多个过滤器称为过滤器链。
例如:对同一应用,如果执行了2个过滤器,执行顺序为:Filter1的放行前逻辑,Filter1放行,Filter2放行前逻辑,Filter2放行,Filter2放行后逻辑,Filter1放行后逻辑。(类似递归)
过滤器的执行顺序按 过滤器的类名的字典序排序;
FilterConfig类
Filter过滤器的配置文件类
1 | filterConfig.getFilterName() //获取过滤器的名称 |
Listener
监听器,是在application,session,request三个对象创建、销毁或者往其中添加修改删属性时自动执行代码的功能组件。
Listener分类:JavaWeb中提供了8个监听器
| 监听器分类 | 监听器名称 | 作用 |
|---|---|---|
| ServletContext监听 | ServletContextListener | 用于对ServletContext对象进行监听(仓创建、销毁) |
| ServletContextAttributeListener | 对ServletContext对象中属性的监听(增别改属性) | |
| Session监听 | HttpSessionListener | 对Session对象的整体状态的监听(创建、销毁) |
| HttpSessionAttributeListener | 对Session?对象中的属性监听(增删改属性) | |
| HttpSessionBindingListener | 监听对象于Sessionl的绑定和解除 | |
| HttpSessionActivationListener | 对Session数据的钝化和活化的监听 | |
| Request监听 | ServletRequestListener | 对Requestx对象进行监听(创建、销毁) |
| ServletRequestAttributeListener | 对Reque5t对象中属性的监听(增别改属性) |
使用
实现ServletContextListener接口,并重写两个方法;
1 |
|
会话
会话:用户打开浏览器,访问wb服务器的资源,会话建立,直到有一方断开连接, 会话结束(浏览器关闭)。在一次会话中可以包含多次请求和响应
会话跟踪:服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
由于HTTP协议是无状态的,每次浏览器发出请求,服务器都会将请求视为新的请求,所以需要会话跟踪技术。
- 客户端会话跟踪技术:Cookie
- 服务端会话跟踪技术:Session
cookie
客户端会话技术,将数据保存到客户端,以后客户端每次请求都携带Cookie数据进行访问。
客户端第一次向服务端发送请求,服务器会生成一个cookie对象,和响应一起发给客户端;客户端之后向服务端发送请求时,会携带cookie一起发送,服务端识别cookie就会知道客户端的身份。
使用:
1 | //发送: |
原理:
Cookie的实现是基于HTTP协议的;
响应头:set-cookie
请求头:cookie
注:
Cookie存活时间:
默认情况下,Cookie存储在浏览器内存中,当浏览器关闭,内存释放,则Cookie被销毁。
setMaxAge(int seconds) :设置Cookie存活时间
正数:将Cookie写入浏览器所在电脑的硬盘,持久化存储;到时间自动删除。
负数:默认值,Cookie在当前浏览器内存中,当浏览器关闭,则Cookie被销毁;
零:删除对应Cookie;
Cookie存储中文:
- 默认情况不能存储中文;如果要存中文,则需要进行转码:可用URL编码
session
服务端会话跟踪技术:将数据保存到服务端;
JavaEE提供HttpSession接口,来实现一次会话的多次请求之间的 数据共享功能。
使用:
1 | //获取Session对象 |
原理:
Session技术,底层是基于Cookie技术实现的。
如果客户端发送的请求没有携带cookie,则服务端会新创建一个session和cookie,cookie的key值session的id值相同,然后session保存在服务端,cookie返回给客户端。
如果客户端发送的请求携带了cookie,则服务端会通过cookie的key值找到对应保存的session,然后返回相应的cookie。
注意事项
Session 钝化、活化:
- 钝化:在服务器正常关闭后,Tomcats会自动将Session数据写入硬盘的文件中;
- 活化:再次启动服务器后,从文件中加载数据到Session中;
Session销毁:
默认30分钟无操作,销毁;
1
2
3
4 <!-- 在web.xml中配置所有session的超时时长 -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
1
2
3
4 //也可以使用session的方法设置单个
void setMaxInactiveInterval(int interval) //设置session的超时时间,单位为秒,设置负数表示永不超时
int getMaxInactiveInterval() //获取超时间
void invalidate() //立即超时,销毁
Cookie和Session异同点:
Cookie和Session都是来完成一次会话内多次请求间数据共享的;
区别:
- 存储位置:Cookie是将数据存储在客户端,Session将数据存储在服务端;
- 安全性:Cookie不安全,Session安全;
- 数据大小:Cookie最大KB,Session无大小限制;
- 存储时间:Cookie可以长期存储,Session默认30分钟;
- 服务器性能:Cookie不占服务器资源,Session占用服务器资源;
JSP
概念:Java Server Pages —— Java服务端页面
一种动态的网页技术,其中既可以定义HTML、JS、CSS等静态内容,还可以定义Java代码的动态内容;
主要用于代替Servlet程序回传html页面的数据,因为Servlet程序回传html页面非常繁琐,开发维护成本都较高。
JSP本质上就是一个Servlet程序,JSP在被访问时,由JSP容器(Tomcat)将其转换为Java文件(Servlet),再编译,最终对外提供服务的其实就是字节码文件
JSP = HTML + Java
JSP有很多缺点:
书写麻烦:特别是复杂的页面;
阅读麻烦;
复杂度高:运行需要依赖于各种环境,JRE,JSP容器,JavaEE;
占内存和磁盘:JSP会自动生成java和 .class文件占磁盘,运行的是 .class文件占内存;
调试困难:出错后,需要找到自动生成的,java文件进行调试;
不利于团队协作:前端人员不会java,后端人员不精HTML;….
后续逐渐被ajax+html替代
步骤:
导入JSP坐标——创建jsp文件——编写html标签和java代码
1 | <!-- jsp坐标 --> |
语法
page指令:
1 | <%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %> |
属性:
- language —— 表示 jsp 翻译后是什么语言文件。只支持 java。
- contentType —— 表示 jsp 返回的数据类型是什么。也是源码中 response.setContentType()参数值
- pageEncoding —— 表示当前 jsp 页面文件本身的字符集。
- import —— 跟 java 源代码中一样。用于导包,导类。
- 给out输出流使用的两个属性(不建议改):
- autoFlush —— 设置当 out 输出流缓冲区满了之后,是否自动刷新冲级区。默认值是 true。
- buffer —— 设置 out 缓冲区的大小。默认是 8kb。
- errorPage —— 设置当 jsp 页面运行时出错,自动跳转去的错误页面路径。路径一般以斜杠开头,表示请求地址为 “http ://ip:port/工程路径/ ” 映射到代码的Web目录
- isErrorPage —— 设置当前 jsp 页面是否是错误信息页面。默认是 false。如果是 true 可以 获取异常信息。
- session —— 设置访问当前 jsp 页面,是否会创建 HttpSession 对象。默认是 true。
- extends —— 设置 jsp 翻译出来的 java类继承谁
脚本:
JSP脚本:用于在JSP页面内定义Java代码
分为:
代码脚本 <% … %>
用于编写需要的功能。内容会直接放到 jspService()方法之中。
1
2
3
4
5
6
7
8
9
10
11
12<%--2.代码脚本----for 循环语句--%>
<table border="1" cellspacing="0">
<%
for (int j = 0; j < 10; j++) {
%>
<tr>
<td>第 <%=j + 1%>行</td>
</tr>
<%
}
%>
</table>表达脚本 <%= … %>
用于在jsp页面上输出数据。内容会放到out.print()中,作为out.print0的参数
注:表达脚本中的表达式不能以分号结束。
声明脚本 <%! … %>
可以给 jsp 翻译出来的 java 类定义属性和方法甚至是静态代码块,内部类等。
内容会放到 _jspService()方法之外,被类直接包含。
注:声明脚本很少使用
1
2
3
4
5
6
7
8
9
10
11
12
13<%!
private Integer id;
private String name;
private static Map<String,Object> map;
%>
<%!
static {
map = new HashMap<String,Object>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
}
%>
输出
response.getWriter输出 和 out输出 都用于设置返回给客户端的内容。由于jsp翻译后,底层源码都是用out来输出的,所以一般统一用out来进行输出。
注释:
html注释: < !– – >
java注释:<% // 单行 java 注释 /* 多行 java 注释 */ %>
jsp注释: <%– –%>
其中html注释和java注释会被翻译到java源代码中,jsp注释不会。
对象
jsp的内置对象:是指 Tomcat 在翻译 jsp 页面成为 Servlet 源代码后,内部提供的九大对象,叫内置对象。
request —— 请求对象
response —— 响应对象
pageContext —— jsp的上下文对象
session —— 会话对象
application —— ServletContext.对象
config —— ServletConfig对象
out —— jsp输出流对象
page —— 指向当前jsp的对象
exception —— 异常对象
jsp四大域对象:
pageContext —— request —— session —— application
域对象可以像Map一样存取数据,其范围和Servlet的四个作用域相同。
常用标签
静态包含(常用)
1 | <%@ include file="" %> |
file属性指定要包含的jsp页面的路径,地址的第一个斜杠为 “ http ://ip:port/工程路径/ ” 映射到web目录。
注:静态包含不会翻译被包含的jsp页面,而是直接把页面拷贝到包含的位置执行输出。
动态包含(少用)
1 | <jsp:include page=""> </jsp:include> |
page属性是指定要包含的 jsp页面的路径。
注:动态包含会 直接翻译被包含的jsp页面,同时还可以传递参数。
转发标签(常用)
1 | <jsp:forward page=""> </jsp:forward> |
page属性设置请求转发的路径
相当于:request.getRequestDispatcher(“/xxxx.jsp”).forward(request,reponse);
EL表达式
Expression Language ,即表达式语言,用于简化 JSP页面内的java代码。
主要功能:获取数据;
语法:
1 | ${表达式} |
JavaWeb中的四大域对象:
- page:当前页面有效;
- request:当前请求有效;
- session:当前会话有效;
- application:当前应用有效;
——EL表达式获取数据,会依次从4个域中寻找,直到找到为止。(四个域的范围逐渐增大,即page范围最小,application最大)
empty运算
判断一个数据是否为空,如果为空则输出true。
值为null,空串时,Object类型数组长度为0时,list集合中元素个数为0,map集合元素个数为0,这些情况均视为空。
1
2
3
4<%
request.setAttribute("obj", null);
%>
${empty obj}关系运算: == != < > <= >=
逻辑运算:&& || !
算数运算: + - * / %
三元运算
点运算 中括号运算:
点运算 输出Bean对象中某个属性的值;
中括号运算 输出有序集合中某个元素的值;
1
2
3
4
5
6
7
8
9
10
11
12<body>
<%
Map<String,Object> map = new HashMap<String, Object>();
map.put("a.a.a", "aaaValue");
map.put("b+b+b", "bbbValue");
map.put("c-c-c", "cccValue");
request.setAttribute("map", map);
%>
${ map['a.a.a'] } <br>
${ map["b+b+b"] } <br>
${ map['c-c-c'] } <br>
</body>
11个隐含对象:
| 变量 | 类型 | 作用 |
|---|---|---|
| pageContext | PageContextImpl | 获取jsp九大内置对象 |
| pageScope | Map<String,Object> | 获取pageContext域中的数据 |
| requestScope | Map<String,Object> | 获取Request域的数据 |
| sessionScope | Map<String,Object> | 获取Session域的数据 |
| applicationScope | Map<String,Object> | 获取ServletContext域的数据 |
| param | Map<String,String> | 获取请求参数的值 |
| paramValues | Map<String,String[]> | 获取多个请求参数的值 |
| header | Map<String,String> | 获取请求头的信息 |
| headerValues | Map<String,String[]> | 获取多个请求头的信息 |
| cookie | Map<String,Cookie> | 获取当前请求的Cookie信息 |
获取4个特定域的属性:
| pageScope | pageContext域 |
|---|---|
| requestScope | Request域 |
| sessionScope | Session域 |
| applicationScope | ServletContext域 |
1 | <body> |
pageContext的使用:
1 | <body> |
JSTL标签
JSTL标准标签库(Jsp Standarded Tag Library),使用标签取代JSP页面上的Java代码。
步骤:
1.导入坐标:
1 | <dependency> |
2.在JSP页面上引入JSTL标签库:
1 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> |
JSTL有五个功能不同的标签库:
| 功能范围 | URI | 前缀 |
|---|---|---|
| 核心标签库(常用) | http ://java.sun.com /jsp/jstl/core | c |
| 格式化 | http ://java.sun.com/jsp/jstl/fmt | fmt |
| 函数 | http ://java.sun.com/jsp/jstl/functions | fn |
| 数据库(不适用) | http ://java.sun.com/jsp/jstl/sql | sql |
| XML(不适用) | http ://java.sun.com/jsp/jstl/xml | x |
标签:
if
1 | <c:if test="true或false"> |
forEach
1 | <!-- items是被遍历的容器 var是遍历生产的临时变量 --> |
MVC
MVC是一种分层开发的模式,其中:
- M:Model,业务模型,处理业务
- V:View,视图,界面展示
- C:Controller,控制器,处理请求,调用模型和视图

MVC只能实现模型到视图的单向展示
三层架构
表现层:接收请求,封装数据,调用业务逻辑层,响应数据
业务逻辑层:对业务逻辑进行封装,组合数据访问层的基本功能,形成复杂的业务逻辑功能
数据访问层(持久层):对数据库的CRUD基本操作

AJAX
概念:AJAX(Asynchronous JavaScript And XML):异步的 javaScript 和 XML
AJAX作用
与服务器进行数据交换: 通过AJAX可以给服务器发送请求,并获取服务器响应的数据。
使用了AJAX和服务器进行通信,就可以使用 HTML+AJAX 来替换 JSP 页面了。
异步交互:可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术,如:搜索联想、用户名是否可用校验,等等…
Ajax 请求的局部更新,浏览器地址栏不会发生变化, 局部更新不会舍弃原来页面的内容。
同步:浏览器请求服务器 ,需要等待服务器处理并响应后,才能继续操作。
异步:浏览器请求服务器,服务器在处理时,浏览器可以做其他操作。
使用步骤
服务端:Servlet程序
客户端:
1 |
|
XMLHttpRequest对象
方法:
| 方法 | 说明 |
|---|---|
| abort() | 取消当前请求 |
| getAllResponseHeaders() | 返回头部信息 |
| getResponseHeader() | 返回特定的头部信息 |
| open(method, url, async, user, psw) | 发送请求 —— method:请求类型 GET 或 POST; url:文件位置; async:true(异步)或 false(同步); user:可选的用户名称; psw:可选的密码 |
| send() | 将请求发送到服务器,用于GET请求 |
| send(string) | 将请求发送到服务器,用于POST请求 |
| setRequestHeader() | 向要发送的报头添加标签/值对 |
XMLHttpRequest属性:
| 属性 | 说明 |
|---|---|
| onreadystatechange | 定义当 readyState 属性发生变化时被调用的函数 |
| readyState | 保存XMLHttpRequest的状态 |
| responseXML | 以字符串返回响应数据 |
| status | 返回请求的状态号 |
| statusText | 返回状态文本(如 “OK” 或 “Not Found”) |
readyState中的XMLHttpRequest的状态 :
- 0:请求未初始化
- 1:服务器连接已建立
- 2:请求已收到
- 3:正在处理请求
- 4:请求已完成且响应已就绪
status中常见的 返回请求的状态:
- 200: “OK”
- 403: “Forbidden”
- 404: “Not Found”
示例
需求:在完成用户注册时,当用户名输入框失去焦点时,校验用户名是否在数据库已存在
- 前端完成的逻辑
- 给用户名输入框绑定光标失去焦点事件
onblur - 发送 ajax请求,携带username参数
- 处理响应:是否显示提示信息
- 给用户名输入框绑定光标失去焦点事件
- 后端完成的逻辑
- 接收用户名
- 调用service 查询User。此案例是为了演示前后端异步交互,所以此处我们不做业务逻辑处理
- 返回标记
后端:
1 |
|
前端:
1 | //1. 给用户名输入框绑定 失去焦点事件 |
Axios
Axios 是一个基于 promise 的 HTTP 库,简单的讲就是可以发送get、post请求。
Axios对原生的AJAX进行封装,简化书写。
使用
axios 使用是比较简单的,分为以下两步:
引入 axios 的 js 文件
1
<script src="js/axios-0.18.0.js"></script>
使用axios 发送请求,并获取响应结果
发送 get 请求
1
2
3
4
5
6axios({
method:"get",
url:"http://localhost:8080/ajax-demo1/aJAXDemo1?username=zhangsan"
}).then(function (a){
alert(a.data);
})发送 post 请求
1
2
3
4
5
6
7axios({
method:"post",
url:"http://localhost:8080/ajax-demo1/aJAXDemo1",
data:"username=zhangsan"
}).then(function (a){
alert(a.data);
});
axios() 是用来发送异步请求的,小括号中使用 js 对象传递请求相关的参数:
method属性:用来设置请求方式的。取值为get或者post。url属性:用来书写请求的资源路径。如果是get请求,需要将请求参数拼接到路径的后面,格式为:url?参数名=参数值&参数名2=参数值2。data属性:作为请求体被发送的数据。也就是说如果是post请求的话,数据需要作为data属性的值。
then() 需要传递一个匿名函数。我们将 then() 中传递的匿名函数称为 ==回调函数==,意思是该匿名函数在发送请求时不会被调用,而是在成功响应后调用的函数。而该回调函数中的 resp 参数是对响应的数据进行封装的对象,通过 resp.data 可以获取到响应的数据。
示例
1.后端实现
定义一个用于接收请求的servlet,代码如下:
1 |
|
2.前端实现
引入 js 文件
1
<script src="js/axios-0.18.0.js"></script>
发送 ajax 请求
get 请求
1
2
3
4
5
6axios({
method:"get",
url:"http://localhost:8080/ajax-demo/axiosServlet?username=zhangsan"
}).then(function (resp) {
alert(resp.data);
})post 请求
1
2
3
4
5
6
7axios({
method:"post",
url:"http://localhost:8080/ajax-demo/axiosServlet",
data:"username=zhangsan"
}).then(function (resp) {
alert(resp.data);
})
整体页面代码如下:
1 |
|
方法
为了方便起见, Axios 已经为所有支持的请求方法提供了别名。如下:
get请求 :axios.get(url[,config])delete请求 :axios.delete(url[,config])head请求 :axios.head(url[,config])options请求 :axios.option(url,config])post请求:axios.post(url[,data,config])put请求:axios.put(url[,data,config])patch请求:axios.patch(url[,data,config])
案例中的 get 请求代码可以改为如下:
1 | axios.get("http://localhost:8080/ajax-demo/axiosServlet?username=zhangsan").then(function (resp) { |
入门案例中的 post 请求代码可以改为如下:
1 | axios.post("http://localhost:8080/ajax-demo/axiosServlet","username=zhangsan").then(function (resp) { |
携带JSON数据
1 | var jsObject = {name:"张三"}; |
注:
- 发送异步请求时,如果请求参数是
JSON格式,那请求方式必须是POST。因为JSON串需要放在请求体中。- axios将js对象转为json字符串,用了 js提供的 JSON对象,即 JSON.stringify (jsObject)。(了解即可)
Vue中的axios
下载axios:
1
npm i axios
引入:
1
import axios from 'axios'
使用
跨域问题:
在vue.config.js
方式一:
1
2
3
4
5 module.experts={
devServer:{
proxy: 'http://localhost:80' //获取数据的地址
}
}
- 优点:配置简单,请求资源时直接发给前端(8080)即可。
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
前端请求的地址端口要改成 8080 (与自己页面的端口一致)
方式二:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 module.experts={
proxy: {
'/api1': {// 匹配所有以 '/api1'开头的请求路径
target: 'http://localhost:80',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api1': ''}
},
'/api2': {// 匹配所有以 '/api2'开头的请求路径
target: 'http://localhost:5001',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}
}
}
- 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
- 缺点:配置略微繁琐,请求资源时必须加前缀。
Thymeleaf
Thymeleaf 是(服务器端)视图模板技术,
优势:和springBoot完美契合。不经过服务器运算仍可以直接查看原始值,对前端友好。
物理视图:
在Servlet中,将请求转发到一个HTML页面文件时,使用的完整的转发路径就是 物理视图。如:/pages/user/login_success.html
由于html页面放在统一的目录下,所以转发地址会有相同的前缀 和 后缀,称为视图前缀,视图后缀。
逻辑视图:
物理视图 = 视图前缀 + 逻辑视图 + 视图后缀
也就是说,逻辑视图 就是指转发路径中间不一样的部分(html页面名)。
使用步骤:
添加jar包(maven可直接添加依赖);
配置两个 < context-param >: view-prefix,view-suffix
1
2
3
4
5
6
7
8
9<!-- 在上下文参数中配置 视图前缀和视图后缀 -->
<context-param>
<param-name>view-prefix</param-name>
<param-value>/WEB-INF/view/</param-value>
</context-param>
<context-param>
<param-name>view-suffix</param-name>
<param-value>.html</param-value>
</context-param>新建viewBaseServlet(有两个方法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54public class ViewBaseServlet extends HttpServlet {
private TemplateEngine templateEngine;
public void init() throws ServletException {
// 1.获取ServletContext对象
ServletContext servletContext = this.getServletContext();
// 2.创建Thymeleaf解析器对象
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
// 3.给解析器对象设置参数
// ①HTML是默认模式,明确设置是为了代码更容易理解
templateResolver.setTemplateMode(TemplateMode.HTML);
// ②设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");
templateResolver.setPrefix(viewPrefix);
// ③设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");
templateResolver.setSuffix(viewSuffix);
// ④设置缓存过期时间(毫秒)
templateResolver.setCacheTTLMs(60000L);
// ⑤设置是否缓存
templateResolver.setCacheable(true);
// ⑥设置服务器端编码方式
templateResolver.setCharacterEncoding("utf-8");
// 4.创建模板引擎对象
templateEngine = new TemplateEngine();
// 5.给模板引擎对象设置模板解析器
templateEngine.setTemplateResolver(templateResolver);
}
protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1.设置响应体内容类型和字符集
resp.setContentType("text/html;charset=UTF-8");
// 2.创建WebContext对象
WebContext webContext = new WebContext(req, resp, getServletContext());
// 3.处理模板数据
templateEngine.process(templateName, webContext, resp.getWriter());
}
}
……
WebSocket
WebSocket是一种在单个TCP连接上进行全双工通信的协议。 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据,而不用前端轮询,浪费资源(HTTP是半双工的,服务端也不能主动推送信息)。
- 在WebSocket API中,浏览器和服务器只需完成一次握手,两者之间可以创建持久的连接,并进行双向数据传输。
作用场景: 通告功能,聊天功能,实时更新数据功能,弹幕。。。
建立连接:
- 握手:依旧是TCP三次握手,然后进入进行一次HTTP通信,告知使用WebSocket,然后双方开始使用WebSocket进行通信。
- 数据传输
一个简单使用例子如下
后端springboot使用:
引入依赖:
1
2
3
4<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>配置相关bean
1
2
3
4
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}具体使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class P2PWebSocket {
private static Session[] sessionContainer = new Session[2];
// @OnOpen 当连接进来时调用的方法
public void onOpen(Session session) {
if (sessionContainer[0] == null && sessionContainer[1] == null) {
sessionContainer[0] = session;
log.info("a用户已接入");
} else if (sessionContainer[0] != null && sessionContainer[1] == null) {
sessionContainer[1] = session;
log.info("b用户已接入");
log.info("双方已就绪");
} else {
}
}
// @OnClose 连接关闭时调用的方法
public void onClose(Session session) {
for (int i = 0; i < sessionContainer.length; i++) {
if (session == sessionContainer[i]) {
sessionContainer[i] = null;
log.info((i == 0 ? "a" : "b") + "用户已退出");
}
}
}
// 得到另一个session对象
private Session getOtherSession(Session session) {
for (int i = 0; i < sessionContainer.length; i++) {
if (session == sessionContainer[i]) {
return sessionContainer[(i == 0 ? 1 : 0)];
}
}
return null;
}
// 向另一个会话发送消息
public void sendMessage(String message, Session session) throws IOException {
Session otherSession = this.getOtherSession(session);
otherSession.getBasicRemote().sendText(message);
}
// 异常处理
public void onError(Session session, Throwable error) {
log.error(session + " : " + error);
}
}
前端html使用:
1 | <body> |
然后,开两个前端页面就可以相互通信了

