网页主要由三部分组成:
结构:HTML
表现:CSS
行为:JavaScript
Chrome浏览器调试工具:右键点“检查”,或按F12
标题标签 : < h1 > ~ < h6 >
段落标签:< p > < /p > (两段之间会插入一定的间距)
换行标签:< br >
水平分割线:< hr >
文本格式化标签:
分区标签:
其他布局标签:(用于手机网页制作)
标签名 语义 header 网页头部 nav 网页导航 footer 网页底部 aside 网页侧边栏 section 网页区块 article 网页文章
图像标签:
< img src=“图像URL” / >
< img src=“图像URL” alt=“替换的文字” title=“图像提示文字”/ >
图像标签的属性:
属性之间不分先后顺序,但必须用空格分开
属性采用键值对的格式:key=“value”
属性 属性值 说明 src 图片路径 必须属性 alt 文本 替换文本,图像不能显示时出现的文字 title 文本 提示文本,鼠标放到图像上会浮现的文字 width 像素 设置图像的宽度 height 像素 设置图像的高度 border 像素 设置图像的边框粗细
相对路径: / 指下一级路径, …/ 指上一级路径
绝对路径:\
<base href="http://localhost:8080/07_servlet/a/b/">
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 >指表格的头部区域,
指表格 的主体区域。合并单元格(属性):跨行合并——rowspan=”合并个数“,跨列合并——colspan=“合并个数”(也可说是占了若干个单元格)
合并步骤:找到目标单元格,写上合并方式,如:< td colspan=“2” >< /td >,然后再删除多余的单元格。
表格属性:
属性 | 属性值 | 描述 |
---|---|---|
align | legt,center,right | 规定表格相对周围元素的对齐方式 |
border | 1或 ” “ | 是否有边框,默认为” “,即无边框 |
cellpadding | 像素值 | 规定单元边沿与内容之间的空白,默认1像素 |
cellspacing | 像素值 | 规定单元格之间的空白,默认1像素 |
width | 像素值或百分比 | 规定表格的宽度 |
如:
<table align="center" border="1" cellpadding="0">
<tr>
< th>表头单元格的内容th>
< td>单元格中的内容td>
tr>
table>
无序列表:< ul > ,列表项:< li >
有序列表:< ol >
自定义列表:< dl > ,表项头< dt >,表项:< dd >
< ul >和< ol >里只能放< li >, < li >里面可以放任意标签,< dl >里只能放< dt >和< dd >。
表单域(区域):< form >
<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属性相同。如:
<label for="zzc"> 文本label> <input type=" " id="zzc"/>
下拉元素:< select > :定义下拉列表
在< option >中添加selected=”selected”时,为默认选中项
<select>
<option> 选项 option>
select>
文本域元素:< textarea >:定义多行文本输入控件
<textarea>
文本内容
textarea>
CSS:层叠样式表
CSS书写顺序:
- 浮动 / display
- 盒子模型:margin border padding 宽度高度背景色
- 文字样式
内联样式:在标签内使用style属性(耦合较高,较少使用)
<div style="color: red"> HELLO CSS div>
内部样式:定义 < style > 标签,在标签内定义css样式;
(一般写在head标签里,title标签下面)
<style>
/*选择器 { 样式 } */
/*给谁改样式 { 改什么样式 } */
p {
color: red; /*键值对用:分开,每行用;结尾*/
fon-size: 12px;
}
style>
外部样式:定义link标签,引入外部的css文件
<link rel="stylesheet" href="demo.css">
/*demo.css*/
div{
color: red;
}
规范:
选择器、属性名、属性值统一小写;
选择器(标签)和大括号中间带空格;
冒号后带空格
标签选择器:元素名称{color: red;}
div{color: red}
id选择器:#id属性值{color: red;}
注意:id是唯一的,一般与js配合制作动态效果
#name{color: red;}
"name">hello css
类选择器:.class属性值{color: red;}
.cls{color: red;}
"cls">helle css
通配符选择器:*{ color: red;}
作用:为页面的所有标签 设置样式。
*{
margin: 0;
padding: 0; /*去除默认的外边距*/
}
更多用法:
后代选择器:选择器1 选择器2 { css }
作用:根据嵌套关系,选择父元素的 后代中满足条件的元素。
注:后代包括 儿子、孙子…
div p{
....
} /* 选择div下的p标签 */
子代选择器: 选择器1 > 选择器2{css}
作用:选择父元素的子代元素。
注:子代只包含儿子
并集选择器: 选择器1,选择器2{css}
作用:用逗号隔开,同时选择多组标签,设置相同的样式。
注:一般写法是 每个选择器写一行,提高代码的可读性。
交集选择器:选择器1选择器2 { css }
作用:选中页面中同时满足多个选择器的标签
注:选择器连着写,且标签选择器优先写在前面。
hover伪类选择器:选择器:hover {css}
作用:在鼠标悬停在元素上时 会出现的状态样式
字体大小:font-size
取值:数字+px
谷歌浏览器默认文字大小是16px
p{
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(可以省略前两个值,为默认值)
复合属性:一个属性后面写多个值的写法
font: italic 700 66px 宋体
文本缩进:text-indent
取值:数字+px 或 数字+em(1em=当前标签的大小,推荐)
p{
text-indent: 2em /*缩进两字符*/
}
文本水平对齐方式:text-align
属性值:left,center,right;分别是左对齐,居中,右对齐
注:text-align 其实是内容对齐,不仅仅用于文本, 能用于 文本、span标签、a标签、input标签、img标签 。不过需要在以上元素的父元素设置,如。
文本修饰:text-decoration
取值:underline,line-through,overline,none;分别是下划线(常用),删除线,上划线,无装饰线(常用)。
作用:控制一行的上下行间距
取值:数字+px 或 倍数(当前标签font-size的倍数)
注意:
如果同时设置了行高和font连写,注意覆盖问题
font:style weight size/line-height familyfont: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标签
继承性:子元素默认继承父元素的样式特点
控制文字属性的都可以继承,如:
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;
淘宝网的:
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; }
京东的:
*{ margin: 0; padding: 0; }
塌陷现象:
场景:相互嵌套的块级元素,子元素设置了margin-top,会同样作用在父元素上。致使父元素一起往下移动。
解决:
根据元素在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属性才能生效,伪元素默认是行内元素
例:
"father">爱
浮动元素会脱离标准流(简称:脱标),在标准流中不占位置
标准流:又称文档流 ,指浏览器在渲染网页时默认采用的一套排版规则。
浮动元素比标准流高半个级别,可以覆盖标准流中的元素
浮动找浮动,下一个浮动元素会在上一个浮动元素后面左右浮动
浮动元素有特殊的显示效果
注:浮动的元素不能使用text-align: center或者margin: 0 auto 设置
浮动带来的问题:如果子元素浮动了,此时子元素不占位置,不能撑开标准流的块级父元素。
解决:
直接设置父元素的高度
优点:简单
缺点:有些布局中不能固定父元素高度
额外标签法
在父元素内容的最后添加一个块级元素,并对其设置 clear: both。
缺点:添加了额外标签,会使HTML结构变复杂
单伪元素清除法
用伪元素替代了额外标签。
/*基本写法*/
.clearfix::after{ /*clearfix常用作需要清除浮动的元素的类名*/
content: '';
display: block;
clear: both;
}
/*补充写法*/
.clearfix::after{
content: '';
display: block;
clear: both;
/*补充的部分是为了在网页中看不见伪元素*/
height: 0;
visibility: hidden;
}
双伪元素清除法
.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
相对于非静态定位的父元素进行移动。
.box{
position: relative;
left: 100px;
top: 200px;
}
绝对定位——position: absolute
就近找已经定位的父级为参照进行定位,没有定位的父级,则默认相对于浏览器可视区域(即)进行移动。(子绝父相)
原位置在页面中不占位置(已经脱标)
绝对定位的盒子模式具备行内块特点
例题:让元素的位置处于浏览器或参照物正中间。
.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 四个阈值其中之⼀,粘性定位才生效。
.xxx {
position: sticky;
top: 30px;
left: 30px;
}
不同布局:标准流 < 浮动 < 定位
而相对,绝对,固定三种定位方式层级相同,谁写在下就覆盖其他的。
元素居中:
绝对定位和固定定位中,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属性:
clip-path: circle(45%)
溢出部分:指盒子内容部分所超出盒子范围的区域。
overflow用于控制内容溢出部分的显示效果,如:显示/隐藏滚动条。
属性名:overflow
属性值:
属性值 | 效果 |
---|---|
visible | 默认值,溢出部分可见 |
hidden | 溢出部分隐藏 |
scroll | 无论是否溢出,都显示滚动条 |
auto | 根据是否溢出,自动显示或隐藏滚动条 |
让某元素本身在屏幕中不可见,如:鼠标hover之后元素隐藏,用于隐藏/显示子菜单之类的场景
属性:
visibility: hidden
占位置的隐藏效果,用的不多
display: none
脱标的隐藏效果(不占位置)
display:black 显示元素
让某元素(包括内容)一起变透明。
属性名:opacity
属性值:0~1之间的数字——0为完全透明
flex用于 按行或按列布局元素(一维布局)。
设置容器为flex后,其子元素的 float 、 clear 、 vertical-align 属性将失效。
flex容器的属性:
flex-direction :主轴反向
.box {
flex-direction: row; /* 默认值,横向从左⾄右排列 */
flex-direction: row-reverse; /* 横向从右⾄左排列 */
flex-direction: column; /* 纵向从上到下排列 */
flex-direction: column-reverse; /* 纵向从下到上排列 */
}
flex-wrap: 超出父容器的子容器的排列方式
flex容器 {
flex-wrap: wrap; /* ⾥⾯的flex项溢出时会⾃动换⾏ ,第⼀⾏在上⽅*/
flex-wrap: wrap-reverse; /* ⾥⾯的flex项溢出时会⾃动换⾏ ,第⼀⾏在下⽅*/
}
flex项 {
flex: 200px
}
flex-flow:上两者的合并写法
{
flex-flow: row wrap;
}
/* 等价于 */
{
flex-direction: row;
flew-wrap: wrap;
}
justify-content:子容器在主轴的排列方向和空间分配
flex容器 {
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:子容器在交叉轴的排列方向
flex容器 {
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:多主轴情况下⼦容器在交叉轴上的对齐和空间分配
flex-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 的简写属性
flex项 {
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的基础上伸缩
}
/* 每个flex项占用空间相等, 最小值是200px */
flex项 {
flex: 1 200px;
}
/* 第二个flex项是其他的2倍 */
flex项:nth-of-type(2) {
flex: 2 200px;
}
order:flex项的排列顺序, 默认为0,值越小越靠前
/* 第一个按钮移到主轴的末尾 */
button:first-child {
order: 1;
}
/* 第二个按钮移动到主轴的最前面 */
button:last-child {
order: -1;
}
align-self:可使自己覆盖flex容器上设置的 align-items 值
flex-item {
align-self: auto; /* 默认值,继承flex容器的align-items值 */
align-self: flex-start / flex-end / center / stretch; /* 与align-items值的含义类似 */
}
grid布局是二维布局,能把内容按照行与列的格式排版。
基本概念:
使用:
定义:默认创建一列或一行的网格
.container {
display: grid; /* 设置⽹格布局,一列 */
display: inline-grid; /* 设置⽹格布局,一行 */
}
设置行列:
.container {
display: grid;
grid-template-columns: 200px 200px 200px;/*设置3个列,宽度各为200px*/
grid-template-rows: 200px 200px 200px;/*设置3个行,行高各为200px*/
}
.container {
display: grid;
grid-template-columns: 2fr 1fr 1fr /*设置3个列,宽度比为2:1:1*/
}
可以混搭:
container {
display: grid;
grid-template-columns: 100px auto 2fr;
}
重复构建行列:repeat()
.container {
display: grid;
grid-template-columns: repeat(3, 2fr 1fr); /*相当于2fr 1fr 2fr 1fr */
}
自动多列填充: auto-fill
container {
display: grid;
grid-template-columns: repeat(auto-fill, 100px); /*包含多个100px列宽的网格,将容器填满*/
}
行列最小尺寸: minmax()
container {
display: grid;
grid-template-columns: 1fr 1fr minmax(200px, 1fr); /*列宽不小于200px,不大于1fr*/
}
网格间隙:gap
container {
display: grid;
grid-template-columns: 2fr 1fr
gap: 20px 50px; /*行间距20px,列间距50px*/
网格区域: grid-template-areas
.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; /* 指定元素的位置 */
}
article {
grid-area: content; /* 指定元素的位置 */
}
aside {
grid-area: sidebar;
}
footer {
grid-area: footer;
}
内容对齐:
行盒:
text-align: center;
块盒:
子元素宽度固定:
方法1: margin: 0 auto
.inner {
width: 200px; /* 宽度固定 */
margin: 0 auto; /* ⽔平居中 */
}
用绝对定位:
.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 。
div { /* 假定 div 为 p 的⽗元素 */
position: relative;
}
p {
width: 200px;
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
子元素宽度未知:
将子元素设置为行内块元素,然后⽗元素设置 text-align: center 。
.container {
width: 500px;
height: 500px;
text-align: center; /* 内容居中 */
}
.inner{
display: inline-block; /* 设置为⾏块盒 */
}
多个块盒元素:
方法一: 使⽤ display: inline-block 和 text-align: center
.container {
width: 500px;
height: 500px;
text-align: center; /* 内容居中 */
}
.inner{
display: inline-block; /* 设置为⾏块盒 */
width: 100px;
height: 150px;
}
使用flex布局:
弹性盒⼦中的 Flex items的⽔平居中可使⽤ justify-content 属性设置,使其中的 Flex items沿主轴对⻬。
.container {
width: 500px;
height: 500px;
display: flex;
justify-content: center /* 在主轴上居中 */
}
.inner{
width: 100px;
height: 150px;
}
单行行内元素:
将子元素的行高( line-height )等于高度( height )就可以了。
.container {
width: 500px;
height: 500px;
}
.inner{
display: inline-block;
height: 150px;
line-height: 200px;
}
多行元素:
使用绝对定位:
.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 。
div { /* 假定 div 为 p 的⽗元素 */
position: relative;
}
p {
height: 50px;
position: absolute;
top: 0;
bottom: 0;
margin-top: auto 0;
}
使用flex布局:
.container {
width: 500px;
height: 500px;
display: flex;
align-items: center;
}
.inner{
width: 100px;
height: 100px;
}
图片文字并排:
使用flex布局:(与上一段代码一致)
给图片设置 vertical-align: middle ,文字盒子设置为 display: inline-block
* {
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;
}
a标签去除下划线:
a {
text-decoration: none;
}
跨平台、面向对象的脚本语言,用来控制网页行为,网页交互。
分两步:预解析,再执行代码
内部脚本:定义在HTML页面中
<script>
...
</script>
JS代码必须在< script >< /script >之间,可以放置在任何地方,任意数量。一般放在< body >的底部,可改善显示速度,因为脚本执行会拖慢显示。
外部脚本:作为外部JS文件引入
<script src="文件路径"></script>
外部脚本不能包含< script >标签,< script >不能自闭合
与java相似,不过语句末的分号;可有可无。
window.alert(" ... ") //写入警告框
document.write(" ... ") //写入HTML输出
console.log(" ... ") //写入浏览器控制台
console.dir(object) //打印元素对象,查看属性,方法
prompt(info) //弹出输入框,用户可以输入
使用 var 关键字来声明变量。(variable)
- 作用域:全局变量;
- 变量可以重复声明;
var test = 20;
test = "张三";
JS是弱类型语言,变量可以存放不同类型的值
ES6新增 let 关键字来定义变量,其变量只在let所在的代码块内有效,且不允许重复声明。
ES6新增 const 关键字来声明一个只读的常量。
- 全局变量 在浏览器关闭时销毁;
- 局部变量 在程序运行完毕后就销毁;
使用const声量,必须初始化,不能被修改。
const birthday = '11.22.1922'
原始类型:
number:数字(整数,小数,NaN(Not a Number))
.toFixed(x) —— 格式化一个数字,x是要保留的小数点后位数。
string:字符,字符串,单双引号皆可
boolean:布尔类型
null:空
undefined:变量未初始化时的默认值
使用typeof运算符可以获取数据类型
typedef 变量名
引用类型
关系运算符
函数通过function关键字进行定义。
function functionName(参数列表){
......
}
//形式参数不需要类型,返回值也不需要定义类型,因为JavaScreipt是弱类型语言
function add(a,b){
return a + b;
}
//定义方式二
var functionName = function(参数列表){...}
var add = function(a,b){
return a + b;
}
//调用,JS函数调用函数,可以传递任意个参数个数
let result = add(1,2,3);
let result = add(1);
//立即执行函数,作用:立即执行,同时也独立创建了一个作用域,里面所有变量都是局部变量,避免命名冲突;
(function(){})();
(function(){}()); //如:
(function(a,b){
console(a + b);
})(2,3);
(function(a,b){
console.log(a + b);
}(2,3));
arguments
是当前函数的内置对象,存储了传递的所有实参。其展示形式是一个伪数组,可以遍历,有lenght属性,有索引,没有push,pop等方法。
内部函数可以访问外部函数的变量:采用链式查找的方式来决定取哪个值,即找最近的——作用域链。
匿名函数:没有名字的函数,通常与事件处理程序一起使用。
const myButton = document.querySelector('button');
myButton.onclick = function() { // 匿名函数
alert('hello');
}
const textBox = document.querySelector('#text-box');
textBox.addEventListener('keydown', function(event) {
console.log(`You pressed "${event.key}".`);
});
箭头函数: 尽量使用箭头函数(更简洁)
// 前面的匿名函数也可以写成如下的箭头函数的形式
textBox.addEventListener('keydown', (event) => {
console.log(`You pressed "${event.key}".`);
});
// 如果函数体只有一条语句,还可以省略花括号
textBox.addEventListener('keydown', (event) => console.log(`You pressed
"${event.key}".`));
// 如果函数只有一个参数,还可以省略参数的括号
textBox.addEventListener('keydown', event => console.log(`You pressed
"${event.key}".`));
// 如果只有一个返回值,且函数体只有一条语句,可省略掉return语句
const originals = [1, 2, 3];
const doubled = originals.map((item) => item * 2); // 将原始数组的每个元素乘以2得到新的数组
let sum = (a, b) => a + b;
alert(sum(1,3)); // 3
Array用于定义数组
创建方法:
var arr = new Array(1,2,3);
var arr = [1,2,3]
属性:
length:数组中元素的个数
push:末尾添加一个元素; unshift:开头添加一个元素
var arrs = [1,2,3];
arrs.push(10);
pop:末尾删除一个元素;shift:开头删除一个元素
indexof:查找元素
splice:删除多个元素
arrs.splice(0,2); //参数:从0号位开始删,删2个
遍历数组:
const array = [1,2,3];
for(const e of array){
console.log(e);
}
字符串和数组的转化:
const data = 'aa,bb,cc,dd';
const array = data.split(',');
const string = array.join(',');
cosnt string2 = array.toString();
创建方法:
var 变量名 = new String(s);
var 变量名 = s;
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):是否以某字符串结束
和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 构造方法名” 赋值给变量。
注:构造函数名字首字母要大写
var person = {
name:"zhangsan",
age:23,
eat:function(){
alert("干饭");
}
};
var obj = new Object();
obj.name = "zhangsan"
function People(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
this.sing = function(sang){
sonsole.log(sang);
}
}
var zhangsan = new People('刘德华',19,'男');
调用属性:对象名.属性名 或者 对象名[‘属性名’]
调用方法:对象名.方法名
遍历对象属性:for…in可以对数组或对象进行遍历。
for(var k in obj){
console.log(k); //k变量输出得到的是属性名
console.log(obj[k]); //obj[k]得到的是属性值
}
JS是单线程的,即同一时间只能做一件事,这意味着所有任务需要排队,如果JS执行时间过长,会导致页面渲染不流畅。
于是,HTML5提出Web Worker标准,允许JS脚本创建多个线程。于是就有了同步和异步。
同步
程序之间的执行顺序与排列顺序一致,即同步。
异步
执行某程序的同时可以执行另一个程序,即异步。
同步任务
同步任务都在主线程执行,形成一个执行栈
异步任务
JS的异步是通过回调函数实现的,一般而言,异步任务有三种:
异步任务相关的回调函数会添加到任务队列(消息队列)中。
执行机制:
先执行执行栈中的同步任务;
遇到异步任务,将其回调函数放入任务队列中;
异步任务会先交给异步进程处理,只有异步任务的事件发生,或如click点击了,setInterval的时间到了,才会把相关的回调函数放入任务队列。
等执行栈中的所有同步任务执行完毕后,系统会按次序读取任务队列中的异步任务,使其进入执行栈,开始执行。
事件循环:主线程不断的重复获取任务,执行任务,再获取任务,反复循环。
Browser Object Model 浏览器对象模型,JS将浏览器的各个组成部分封装成对象:Window,Navigator,Screen,History,Location。
Window对象:浏览器窗口对象
获取:直接使用window,其中window. 可以省略。
window.alert();
属性:获取其他四个BOM对象
方法:
Navigator:浏览器对象
常用:userAgent 返回由客服端发送给服务器的user-agent头部的值。
使用下面代码尅判断用户在那个终端打开页面,以实现跳转:
if ((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. 可以省略。
window.history.方法();
history.方法();
方法:
Location:地址栏对象
获取:使用window.location获取,其中window. 可以省略。
window.location.方法();
location.方法();
URL:同一资源定位符 Uniform Resource Locator
是互联网上标准资源的地址,互联网的每个文件都有唯一的url,它包含的信息指出文件的位置,以及浏览器应该怎么处理它。
格式:
protocol://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里的参数会携带表单的数据到另一个页面。
<form action="index.html" 用户名: type="text" name="uname"> <input type="submit" value="登录"> form> var arr = location.search.substr(1).split('='); <div id="root"> <h1>hello {{name}}h1> div> body> <script> Vue.config.productionTip = false //阻止vue在启动时生产提示(本地url地址时没接网络可能报一些错,但不影响运行) //创建Vue实例 new Vue({ el: '#root', //el用于指定当前Vue示例为哪个容器服务 data: { //data中用于存储数据,数据供el指定的容器所使用 name: 'valcanoZz' }, }) script>
{{xxx}}
,xxx是js表达式,且可以直接读取到data中的所有属性。<body>
<div id="app">
<h1>插值语法 --> {{name}}h1>
<a v-bind:href="url">指令语法a>
<a :href="url">指令语法 v-bind:的简写a>
div>
body>
<script>
Vue.config.productionTip = false
new Vue({
el: '#app',
data: {
name: 'zzc',
url: 'http://www.baidu.com'
}
})
script>
v-model 只能用于表单类元素,因为只有表单类元素才有需求更改数据。
<body>
<div id="app">
单向数据绑定:<input type="text" :value="name"><br />
双向数据绑定:<input type="text" v-model:value="name"><br>
双向数据绑定简写:<input type="text" v-model="name">
div>
body>
<script>
Vue.config.productionTip = false
new Vue({
el: '#app',
data: {
name: 'zzc',
}
})
script>
一个重要的原则:
由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,就不再是Vue实例了。
<body>
<div id="app">
<h1>你好,{{name}}h1>
div>
body>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el: '#app', //el第一种写法
data: { //data第一种写法:对象式
name: 'zzc',
}
})
//el第二种写法
vm.$mount('#app') //更灵活, 比如可以等1秒后再让Vue挂载容器
//data的第二写法:函数式 —————— 组件必须采用函数式
//注意:data不要写箭头函数,否侧Vue实例调用不到,调用者往外为window
new Vue({
el: '#app',
data() {
console.log('@@@',this) //此处的this是Vue实例对象
return {
name:'zzc'
}
},
})
script>
事件绑定 —— v-on
可简写为 @ ;加上小括号也可以传参进去,如果需要接受event对象,要加上$event进行占位;
事件修饰符
<body>
<div id="app">
<h1>你好,{{name}}h1>
<button v-on:click="showInfo1">事件绑定: v-onbutton>
<button @click="showInfo1">事件绑定的简写: 换成@button>
<button @click="showInfo2(66,$event)">事件绑定: 传参进去,$event作为占位符给event对象button>
<div id="app">
<a href="http://wwww.baidu.com" @click.prevent="showInfo">事件绑定,加".prevent"可阻止默认事件a>
div>
div>
body>
<script>
new Vue({
el: '#app',
data: {
name: 'zzc',
},
methods: { //在methods中配置函数
showInfo1(event) {
console.log(event.target); //event.target是触发事件的对象,即button标签
console.log(this); //这里this是v,即vue实例
},
showInfo2(a, b) {
console.log(a, b);
}
},
})
script>
使用 @keydown.按键别名
和 @keydown.按键别名
来使用键盘事件。
Vue中常用的按键别名:
Vue 未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
系统修饰键(用法特殊):ctrl、alt、shift、meta
- 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
- 配合keydown使用:正常触发事件。
也可以使用keyCode去指定具体的按键(不推荐)
Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
<body>
<div id="root">
<h2>欢迎来到{{name}}学习h2>
<input type="text" placeholder="按下回车提示输入" @keydown.huiche="showInfo">
div>
body>
<script type="text/javascript">
Vue.config.keyCodes.huiche = 13 //定义了一个别名按键
new Vue({
el:'#root',
data:{
name:'zzc'
},
methods: {
showInfo(e){
// console.log(e.key,e.keyCode)
console.log(e.target.value)
}
},
})
script>
定义:要用的属性不存在,要通过已有属性计算得来。
原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
get函数什么时候执行?
- 初次读取时会执行一次。
- 当依赖的数据发生改变时会被再次调用。
优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
注:
计算属性最终会出现在 Vue实例上,直接读取使用即可。
如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
<body>
<div id="root">
姓:<input type="text" v-model="firstName"> <br /><br />
名:<input type="text" v-model="lastName"> <br /><br />
全名:<span>{{firstName}}-{{lastName}}span><br /><br />
全名:<span>{{fullName1()}}span><br /><br />
全名:<span>{{fullName2}}span><br /><br />
全名:<input type="text" v-model="fullName2"> <br /><br />
div>
body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
methods: {
fullName1() { //没有缓存, 每调用一次就执行一次; 而计算属性有缓存,只在初次读和数据变化时才执行
return this.firstName + '-' + this.lastName
}
},
computed: {
fullName2: {
//get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
//get什么时候调用?1.初次读取fullName时。2.所依赖的数据发生变化时。
get() {
// console.log('get被调用了')
// console.log(this) //此处的this是vm
return this.firstName + '-' + this.lastName
},
//set什么时候调用? 当fullName被修改时。
set(value) {
// console.log('set', value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
},
//计算属性时,如果确定只使用get(), 可以简写, 但注意不要和methods混淆了
fullName3(){
return this.firstName + '-' + this.lastName
}
}
})
script>
监视属性 —— watch
深度监视 ——
Vue中的watch默认不监测对象内部值的改变(只管理一层)。
配置deep:true可以监测对象内部值改变(多层)。
注:
Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
使用watch时根据数据的具体结构,决定是否采用深度监视。
<body>
<div id="app">
<h2>今天天气很 {{info}} h2>
<button @click="isHot = !isHot">切换button>
<h3>a的值是:{{numbers.a}}h3>
<button @click="numbers.a++">点我让a+1button>
<h3>b的值是:{{numbers.b}}h3>
<button @click="numbers.b++">点我让b+1button>
div>
body>
<script type="text/javascript">
const vm = new Vue({
el: '#app',
data: {
isHot: true,
numbers: {
a: 1,
b: 1,
}
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
watch: {
isHot: {
immediate: true,// immediate:true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue, oldValue) {
console.log("isHot被修改了", newValue, oldValue)
}
},
// 当只有handler时,可以简写:
isHot(newValue, oldValue){
console.log("isHot被修改了", newValue, oldValue)
},
//监视多级结构中某个属性的变化, 要重新加上引号
'numbers.a': {
handler() {
console.log('a被改变了')
}
},
//监视多级结构中所有属性的变化, 修改属性deep为true
numbers: {
deep: true,
handler() {
console.log('numbers改变了')
}
}
},
})
//监视功能 还可以在实例外定义使用
vm.$watch('isHot', {
immediate: true,
handler(newValue, oldValue) {
console.log("isHot被修改了", newValue, oldValue)
},
})
script>
computed能完成的功能,watch都可以完成。
watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
两个重要的小原则:
如:在vue里使用定时器回调函数,要用箭头函数,否则this会指向管理者window;
<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: 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中的第一层数据。
<body>
<div id="root">
<h2>学生信息h2>
<button @click="student.age++">年龄+1button><br>
<button @click="addSex">添加性别,默认为男button><br>
<button @click="addFriends">在列表首位添加一个朋友button><br>
<button @click="updateFriendName">修改第一个朋友的名字: 张三button><br>
<button @click="addHobby">添加一个爱好button><br>
<button @click="updateHobby">修改第一爱好button><br>
<h3>姓名: {{student.name}}h3>
<h3>年龄:{{student.age}} h3>
<h3>性别:{{student.sex}} h3>
<h3>爱好: h3>
<ul>
<li v-for="(h, index) in student.hobby" :key="index">
{{h}}
li>
ul>
<h3>朋友们: h3>
<ul>
<li v-for="(f, index) in student.friends" :key="index">
{{f.name}} - {{f.age}}
li>
ul>
div>
body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
student: {
name: 'tom',
age: 18,
hobby: ['书法', '弹琴', '看书'],
friends: [
{ name: 'amy', age: 35 },
{ name: 'tony', age: 36 }
]
}
},
methods: {
addSex(){
Vue.set(this.student,'sex','男')
},
addFriends(){
this.student.friends.unshift({name:'aaa',age:40})
},
updateFriendName(){
this.student.friends[0].name = '张三' //fridends[0]没有生成get和set方法,但里面的属性name和age,都有对应的get和set方法; 只要Vue有生成对应的get和set方法,都可以直接修改(会响应至页面)
},
addHobby(){
this.student.hobby.push('新加的爱好') //不能直接改hobby[0],因为hobby[0]没有对应的get和set,修改数据后页面并不会响应
},
updateHobby(){
this.student.hobby.splice(0,1,'修改的爱好') //删掉第0个,添加一个
Vue.set(this.student.hobby, 1, '另一种修改')
}
},
})
script>
class样式
写法:class=“xxx” xxx可以是字符串、对象、数组。
style样式(用的少)
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script type="text/javascript" src="../js/vue.js">script>
<style>
.basic {
width: 400px;
height: 100px;
border: 1px solid black;
}
.happy {
border: 4px solid red;
;
background-color: rgba(255, 255, 0, 0.644);
background: linear-gradient(30deg, yellow, pink, orange, yellow);
}
.sad {
border: 4px dashed rgb(2, 197, 2);
background-color: gray;
}
.normal {
background-color: skyblue;
}
.sty1 {
background-color: yellowgreen;
}
.sty2 {
font-size: 30px;
text-shadow: 2px 2px 10px red;
}
.sty3 {
border-radius: 20px;
}
style>
head>
<body>
<div id="root">
<div class="basic" :class="mood" @click="changeMood">{{name}}div> <br/><br/>
<div class="basic" :class="classArr">{{name}}div> <br/><br/>
<div class="basic" :class="classObj">{{name}}div> <br/><br/>
<div class="basic" :style="styleObj">{{name}}div> <br/><br/>
<div class="basic" :style="styleArr">{{name}}div>
div>
body>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'zzc',
mood:'normal',
classArr:['sty1','sty2','sty3'], //class样式 —— 数组写法
classObj:{ //对象写法
sty1:false,
sty2:false,
},
//style样式 —— 对象写法
styleObj:{
fontSize: '40px',
color:'red',
},
styleObj2:{
backgroundColor:'orange'
},
//数组写法
styleArr:[
{
fontSize: '40px',
color:'blue',
},
{
backgroundColor:'gray'
}
]
},
methods: {
changeMood(){
//随机使用三种样式之一
const arr = ['happy','sad','normal']
const index = Math.floor(Math.random()*3)
this.mood = arr[index]
}
},
})
script>
html>
v-if
写法:
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。
v-show
写法:v-show=“表达式”
适用于:切换频率较高的场景。
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
注:使用v-if的时,元素如果被移除了就无法获取到,而使用v-show一定可以获取到。
<body>
<div id="root">
<h2>当前的n值是:{{n}}h2>
<button @click="n++">点我n+1button>
<h2 v-show="false">欢迎来到{{name}}h2>
<h2 v-show="1 === 1">欢迎来到{{name}}h2>
<h2 v-if="false">欢迎来到{{name}}h2>
<h2 v-if="1 === 1">欢迎来到{{name}}h2>
<div v-if="n === 1">Angulardiv>
<div v-else-if="n === 2">Reactdiv>
<div v-else-if="n === 3">Vuediv>
<div v-else>哈哈div>
<template v-if="n === 1">
<h2>你好h2>
<h2>zzch2>
<h2>广东h2>
template>
div>
body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
name: 'zzc',
n: 0
}
})
script>
使用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更新 ==> 界面有问题。
<body>
<div id="root">
<h2>人员列表 (遍历数组)h2>
<ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-{{p.age}}
li>
ul>
<h2>汽车信息(遍历对象)h2>
<ul>
<li v-for="(value, k) in car" :key="k">
{{value}}-{{k}}
li>
ul>
<h2>遍历字符串h2>
<ul>
<li v-for="(char,index) in str" :key="index">
{{char}}-{{index}}
li>
ul>
<h2>遍历指定次数 h2>
<ul>
<li v-for="(number,index) in 5" :key="index">
{{number}}-{{index}}
li>
ul>
div>
body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
persons: [
{ id: '001', name: '张三', age: 18 },
{ id: '002', name: '李四', age: 19 },
{ id: '003', name: '王五', age: 20 },
],
car: {
name: 'zz',
price: '70万',
color: 'red'
},
str: 'hello'
},
})
script>
使用watch和computed都可以
<body>
<div id="root">
<h2>人员列表h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<ul>
<li v-for="(p,index) of filPerons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
li>
ul>
div>
body>
<script type="text/javascript">
//#region 使用#region和#endregion可以实现代码折叠
new Vue({
el: '#root',
data: {
keyWord: '',
persons: [
{ id: '001', name: '马冬梅', age: 19, sex: '女' },
{ id: '002', name: '周冬雨', age: 20, sex: '女' },
{ id: '003', name: '周杰伦', age: 21, sex: '男' },
{ id: '004', name: '温兆伦', age: 22, sex: '男' }
],
filPerons: [] //筛选后存放数据的数组,如果用原数组的话,数据会丢失
},
watch: {
keyWord: {
immediate: true,
handler(val) {
this.filPerons = this.persons.filter((p) => {
return p.name.indexOf(val) !== -1
})
}
}
}
})
//#endregion
//用computed实现
new Vue({
el: '#root',
data: {
keyWord: '',
persons: [
{ id: '001', name: '马冬梅', age: 19, sex: '女' },
{ id: '002', name: '周冬雨', age: 20, sex: '女' },
{ id: '003', name: '周杰伦', age: 21, sex: '男' },
{ id: '004', name: '温兆伦', age: 22, sex: '男' }
]
},
computed: {
filPerons() {
return this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
}
}
})
script>
注意事项:
v-model的三个修饰符:
<body>
<div id="root">
<form @submit.prevent="demo">
<label for="demo">账号: label>
<input type="text" id="demo" v-model="userInfo.ccount"><br><br>
<label for="pass">密码: label>
<input type="password" id="pass" v-model="userInfo.password"><br><br>
年龄: <input type="number" v-model.number="userInfo.age"><br><br>
性别:
男<input type="radio" name="sex" v-model="userInfo.sex" value="male">
女<input type="radio" name="sex" v-model="userInfo.sex" value="female"><br><br>
爱好:
书法<input type="checkbox" v-model="userInfo.hobby" value="书法">
学习<input type="checkbox" v-model="userInfo.hobby" value="学习">
弹琴<input type="checkbox" v-model="userInfo.hobby" value="弹琴"><br><br>
所属校区:
<select v-model="userInfo.city">
<option value="">请选择校区option>
<option value="beijin">北京option>
<option value="shanghai">上海option>
<option value="shenzhen">深圳4option>
select><br><br>
其他信息:<br>
<textarea cols="30" rows="10" v-model.lazy="userInfo.other">textarea><br><br>
<input type="checkbox" v-model="userInfo.agree"> 阅读并接受<a href="www. baidu.com">《用户协议》a><br><br>
<button>提交button>
form>
div>
body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
userInfo: {
account: '',
password: '',
sex: 'male',
age: '',
hobby: [],
city: 'beijin',
other: '',
agree: ''
}
},
methods: {
demo() {
console.log(JSON.stringify(this._data))
}
},
})
script>
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
语法:
new Vue{filters:{}}
{{ xxx | 过滤器名}}
或 v-bind:属性 = “xxx | 过滤器名”备注:
<body>
<div id="root">
<h2>格式化时间: {{fmTime}} h2>
<h2>格式化时间: {{getFmTime()}} h2>
<h2>格式化时间: {{time | timeFormater}} h2>
<h2>格式化时间: {{time | timeFormater('YYYY-MM-DD') | mySlice}} h2>
div>
body>
<script>
//全局过滤器
Vue.filter('mySlice', function (value) {
return value.slice(0, 4)
})
new Vue({
el: '#root',
data: {
time: 1621561377603
},
computed: {
fmTime() {
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
methods: {
getFmTime() {
return dayjs().format('YYYY-MM-DD HH:mm:ss')
}
},
//局部过滤器
filters: {
timeFormater(value, str = 'YYYY年MM月DD日 HH:mm:ss') {
return dayjs(value).format(str)
},
}
})
script>
常见指令:
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 —— 跳过其所在节点的编译过程。可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
自定义指令:
定义语法:
局部指令:
new Vue({
directives:{
//配置对象
指令名:{
bind(element, binding){},
inserted(element, binding){},
update(element, binding){},
}
}
})
new Vue({
directives:{
//回调函数
指令名(){}
}
})
全局指令:
Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)
配置对象中常用的3个回调:
注:
指令定义时不加v-,但使用时要加v-;
指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
<body>
<div id="root">
<h2>放大后的n的值: <span v-big="n">span> {{n}} h2>
<input type="text" v-f-Bind:value="n"> <br><br>
<button @click="n++">n+1button>
div>
body>
<script>
new Vue({
el: '#root',
data: {
n : 4,
},
directives:{
//big函数何时会被调用?
// 1.指令与元素成功绑定时(一上来)。
// 2.指令所在的模板被重新解析时。
big(element, binding){
console.log('指令被调用')
element.innerText = binding.value * 10
},
'f-bind':{
//指令与元素成功绑定时(一上来)调用
bind(element, binding){
element.value = binding.value
},
//指令所在元素被插入页面时调用
inserted(element, binding){
element.focus()
},
//指令所在的模板被重新解析时调用
update(element, binding){
element.value = binding.value
},
}
}
})
script>
this.$nextTick(回调函数)
如:
method: {
medo(){
...
this.$nextTick(function(){
this.$refs.xxx.focus()
})
}
}
生命周期,又叫生命周期回调函数、生命周期函数、生命周期钩子。
本质:是Vue在关键时刻调用的一些特殊名称的函数。
常用的生命周期钩子:
mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
关于销毁Vue实例:
销毁后借助Vue开发者工具看不到任何信息。
销毁后自定义事件会失效,但原生DOM事件依然有效。
一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。
<body>
<div id="root">
<h2 :style="{opacity}">欢迎学习Vueh2>
<button @click="opacity = 1">透明度设置为1button>
<button @click="stop">点我停止变换button>
div>
body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
opacity: 1
},
methods: {
stop() {
//自己调用销毁方法,一般不会这么做
this.$destroy()
}
},
beforeCreate() {
console.log('beforeCreate')
},
created() {
console.log('created')
},
beforeMount() {
console.log('beforeMount')
},
//Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
mounted() {
console.log('mounted', this)
this.timer = setInterval(() => {
console.log('setInterval')
this.opacity -= 0.01
if (this.opacity <= 0) this.opacity = 1
}, 16)
},
beforeUpdate() {
console.log('beforeUpdate')
},
updated() {
console.log('updated')
},
beforeDestroy() {
clearInterval(this.timer)
console.log('beforeDestroy')
},
destroyed() {
console.log('destroyed')
},
})
script>
@blur —— 是当元素失去焦点时所触发的事件
<input type="text" placeholder="请输入内容" @blur="blur"/>
模块:向外提供特定功能的js程序,作用是便于复用js,简化js编写,提高js运行效率;(因为js文件多而杂)
模块化:应用中的js都以模块来编写;
组件:用来实现局部功能效果的代码合集(html/css/js/图片音频等),作用:便于复用代码,简化项目编码,提高运行效率;(因为界面的功能很复杂)
组件化:应用中的功能以多组件的方式编写;
组件是可复用的 Vue 实例,且带有一个名字。分为:
因为组件是可复用的 Vue 实例,所以它们与
new Vue
接收相同的选项,例如data
、computed
、watch
、methods
以及生命周期钩子等。仅有的例外是像el
这样根实例特有的选项。注:: 组件中的data必须为一个函数,不然,这个的组件的一个实例发生改变,其他实例也会发生相同的改变。
data() { return { name: 'Tom' } }
Vue中使用组件的步骤:
关于组件的命名规范:
- 一个单词组成:
- 首字母小写:school
- 首字母大写:School (推荐)
- 多个单词组成:
- kebab-case命名:my-school
- CamelCase命名:MySchool(需要Vue脚手架支持)(推荐)
注:组件中可以使用name配置项 该组件在开发者工具中呈现的名字。
定义组件的简写方式:
- const school = Vue.extend(options) 可简写为:const school = options
组件本质是一个名为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原型上的属性、方法。
<body>
<div id="root">
<hello>hello>
<hr>
<school>school>
div>
body>
<script>
//第一步:创建student组件
const student = Vue.extend({
template: `
学生姓名:{{studentName}}
学生年龄:{{age}}
`,
data() {
return {
studentName: 'zzc',
age: 18
}
}
})
//第一步:创建school组件
const school = Vue.extend({
template: `
学校名称:{{schoolName}}
学校地址:{{address}}
`,
//组件嵌套
components: {
student,
student_1: student
},
data() {
return {
schoolName: 'zzc',
address: '广东广州',
}
},
methods: {
showName() {
alert(this.schoolName)
}
},
})
//第一步:创建hello组件
const hello = Vue.extend({
template: `
你好啊!{{name}}
`,
data() {
return {
name: 'Tom'
}
}
})
//第二步:全局注册组件
Vue.component('hello', hello)
new Vue({
el: '#root',
//第二步:注册组件(局部注册)
components: {
school,
},
data: {
msg: 'Vue'
}
})
script>
一般将不同的页面功能 写成多个 .vue文件,由一个App.vue 进行汇总,然后让 main.js 引入 App.vue;
组件结构快捷模板: 输入
代码格式:
App.vue文件:
main.js:
import App from './App.vue'
new Vue({
el: '#root',
components:{
App
}
})
Vue脚手架(Vue CLI,command line interface)是 Vue官方提供的标准化开发工具(开发平台)
初始化步骤 ——————
如出现下载缓慢可以 配置 npm 淘宝镜像:npm config set registry https://registry.npm.taobao.org
目录结构 ——————
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函数去指定具体内容。
render: h => h(App),
//上面是简写
render(h){
return h(App),
}
vue.config.js 配置文件 ——————
如:
module.exports = {
pages:{
index: {
//入口文件
entry: 'src/main.js',
},
},
lintOnSave: false //关闭语法检查
}
ref属性:
.....
或
this.$refs.xxx
props配置项:
功能:让组件接收外部传过来的数据,父组件==>子组件
传递数据:<组件名 name="xxx"/>
接收数据:
第一种方式(只接收):props:['name']
第二种方式(限制类型):props:{name:String}
第三种方式(限制类型、限制必要性、指定默认值):
props:{
name:{
type:String, //类型
required:true, //必要性
default:'老王' //默认值
}
}
备注:props是只读的,Vue底层会监测对props的修改,如果进行了修改,就会发出警告;
若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。(props的内容会比data先准备好)
//data中的'myName'的值是 props中的'name', 使用'name'不能修改,而'myName'就可以进行修改 data(){ return{ myName: this.name } } props:['name','age']
功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步定义混合:定义 .js文件,写下混入的内容
export const mixin1 = {
methods: {
showName() {
alert(this.name)
}
},
}
export const mixin2 = {
data() {
return {
x: 100,
y: 200
}
}
}
第二步使用混入:
全局混入:Vue.mixin(xxx)
,在main.js中引入,全局有效
局部混入:mixins:['xxx']
,在组件中各自引入
<script>
//局部引入
import {mixin1} from "../mixin"
export default {
name: "MySchool",
//配置项mixins: 配置多个混入
mixins: [mixin1]
};
script>
//在main.js中 全局引入及配置
import {mixin1} from "./mixin"
Vue.mixin(mixin1)
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
定义插件:
//创建一个plugins.js文件,定义插件
对象.install = function (Vue, options) {
// 1. 添加全局过滤器
Vue.filter(....)
// 2. 添加全局指令
Vue.directive(....)
// 3. 配置全局混入(合)
Vue.mixin(....)
// 4. 添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
}
//只有一个对象
export default{
install(Vue){
Vue.directive(....)
Vue.mixin(....)
....
}
}
使用插件:
import plugins from './plugins'
Vue.use(plugins)
Vue.use()
存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
相关API:
xxxxxStorage.setItem('key', 'value');
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
xxxxxStorage.getItem('person');
该方法接受一个键名作为参数,返回键名对应的值。
xxxxxStorage.removeItem('key');
该方法接受一个键名作为参数,并把该键名从存储中删除。
xxxxxStorage.clear()
该方法会清空存储中的所有数据。
备注:
xxxxxStorage.getItem(xxx)
如果xxx对应的value获取不到,那么getItem的返回值是null。JSON.parse(null)
的结果依然是null。自定义事件可用于组件间通信:子组件 ===> 父组件
使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
绑定自定义事件:
第一种方式,在父组件中:<子组件名 @自定义事件名="父组件回调函数"/>
或
第二种方式,在父组件中:
<子组件名 ref="xxx"/>
......
mounted(){
this.$refs.xxx.$on('自定义事件名',this.父组件回调函数)
}
若想让自定义事件只能触发一次,可以使用once
修饰符,或$once
方法。
触发自定义事件:this.$emit('自定义事件名',数据)
解绑自定义事件this.$off('自定义事件名')
解绑多个则参数写成数组形式,解绑全部则不写参数
组件上也可以绑定原生DOM事件,需要使用native
修饰符。
注意:通过this.$refs.xxx.$on('自定义事件',回调)
绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
一种组件间通信的方式,适用于任意组件间通信,一般用于没有直接联系的组件之间。
安装全局事件总线:
new Vue({
......
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
......
})
使用事件总线:
接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
methods(){
方法名(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组件自身。
methods(){
方法名(data){......}
}
......
mounted() {
this.pid = pubsub.subscribe('xxx',this.方法名) //订阅消息,接受到的数据中第一个参数是消息名,从第二个参数开始才是传过来的数据
}
提供数据:pubsub.publish('xxx',数据)
最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)
去取消订阅。
Vue实现:
。。。。
第三方动画使用,如 Animate.css - 第三方动画 ;
下载axios:
npm i axios
引入:
import axios from 'axios'
使用
跨域问题:
在vue.config.js
方式一:
module.experts={ devServer:{ proxy: 'http://localhost:80' //获取数据的地址 } }
- 优点:配置简单,请求资源时直接发给前端(8080)即可。
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
前端请求的地址端口要改成 8080 (与自己页面的端口一致)
方式二:
module.experts={ proxy: { '/api1': {// 匹配所有以 '/api1'开头的请求路径 target: 'http://localhost:80',// 代理目标的基础路径 changeOrigin: true, pathRewrite: {'^/api1': ''} }, '/api2': {// 匹配所有以 '/api2'开头的请求路径 target: 'http://localhost:5001',// 代理目标的基础路径 changeOrigin: true, pathRewrite: {'^/api2': ''} } } }
- 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
- 缺点:配置略微繁琐,请求资源时必须加前缀。
vuex是 在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
使用场景:多个组件需要共享数据时
安装:num i vuex@3(vue2中只能用vuex3版本,vue3用vuex4版本)
创建文件:src/store/index.js
//引入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
配置项
......
//引入store
import store from './store'
......
//创建vm,使用store配置项
new Vue({
el:'#app',
render: h => h(App),
store,
})
使用:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions = {
//响应组件中加的动作
jia(context,value){
// console.log('actions中的jia被调用了',miniStore,value)
context.commit('JIA',value)
},
}
const mutations = {
//执行加
JIA(state,value){
// console.log('mutations中的JIA被调用了',state,value)
state.sum += value
}
}
//初始化数据
const state = {
sum:0
}
export default new Vuex.Store({
actions,
mutations,
state,
})
组件中读取vuex中的数据:[this.]$store.state.数据名
组件中修改vuex中的数据:this.$store.dispatch('action中的方法名',数据)
或 this.this.$store.commit('mutations中的方法名',数据)
备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写
dispatch
,直接编写commit
概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。
在store.js
中追加getters
配置
......
const getters = {
bigSum(state){
return state.sum * 10
}
}
//创建并暴露store
export default new Vuex.Store({
......
getters
})
组件中读取数据:$store.getters.bigSum
先导入map方法:
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
mapState方法:用于帮助我们映射state
中的数据为计算属性
… 三个点是es6中的拓展运算符
computed: {
//借助mapState生成计算属性:sum、school、subject(对象写法)
...mapState({sum:'sum',school:'school',subject:'subject'}),
//借助mapState生成计算属性:sum、school、subject(数组写法)
...mapState(['sum','school','subject']),
},
mapGetters方法:用于帮助我们映射getters
中的数据为计算属性
computed: {
//借助mapGetters生成计算属性:bigSum(对象写法)
...mapGetters({bigSum:'bigSum'}),
//借助mapGetters生成计算属性:bigSum(数组写法)
...mapGetters(['bigSum'])
},
mapActions方法:用于帮助我们生成与actions
对话的方法,即:包含$store.dispatch(xxx)
的函数
methods:{
//靠mapActions生成:incrementOdd、incrementWait(对象形式)
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
//靠mapActions生成:incrementOdd、incrementWait(数组形式)
...mapActions(['jiaOdd','jiaWait'])
}
mapMutations方法:用于帮助我们生成与mutations
对话的方法,即:包含$store.commit(xxx)
的函数
methods:{
//靠mapActions生成:increment、decrement(对象形式)
...mapMutations({increment:'JIA',decrement:'JIAN'}),
//靠mapMutations生成:JIA、JIAN(对象形式)
...mapMutations(['JIA','JIAN']),
}
备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。
目的:让代码更好维护,让多种数据分类更加明确。
修改store.js
const 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数据:
//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),
开启命名空间后,组件中读取getters数据:
//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])
开启命名空间后,组件中调用dispatch
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
开启命名空间后,组件中调用commit
//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
注:
- 路由组件通常存放在
pages
文件夹,一般组件通常存放在components
文件夹。- 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
- 每个组件都有自己的
$route
属性,里面存储着自己的路由信息。- 整个应用只有一个router,可以通过组件的
$router
属性获取到。
安装vue-router,命令:npm i vue-router@3
新建router/index.js,配置路由信息:
//引入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中引入:
import VueRouter from 'vue-router'
import router from './router'
Vue.use(VueRouter)
new Vue({
el: '#app',
render: h => h(App),
router: router
})
在组件中使用:
实现切换:
<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
,则在中加上replace属性,即 。
指定展示位置:
<router-view></router-view>
给路由命名:
作用:可以简化路由的跳转。
如何使用
给路由命名,使用 name属性 给路由起名:
{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello' //给路由命名
path:'welcome',
component:Hello,
}
]
}
]
}
简化跳转:
跳转
跳转
跳转
query 传参:
传递参数
跳转
跳转
接收参数:
$route.query.id
$route.query.title
params传参:
配置路由,
声明接收params参数
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
component:Message,
children:[
{
name:'xiangqing',
path:'detail/:id/:title', //使用占位符声明接收params参数
component:Detail
}
]
}
]
}
传递参数
跳转
跳转
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
接收参数:
$route.params.id
$route.params.title
路由的props配置:
作用:让路由组件更方便的收到参数
写在路由配置中
{
name:'xiangqing',
path:'detail/:id',
component:Detail,
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{a:900}
//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
// props:true
//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
}
具体编码:
可以写在方法之类的地方,实现其他标签进行路由跳转功能。
//$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
路由组件失活时触发。如:组件激活时开启定时器,失活时清除定时器
<script>
export default {
name:'News',
data() {
return {
opacity:1
}
},
/* beforeDestroy() {
console.log('News组件即将被销毁了')
clearInterval(this.timer)
}, */
/* mounted(){
this.timer = setInterval(() => {
console.log('@')
this.opacity -= 0.01
if(this.opacity <= 0) this.opacity = 1
},16)
}, */
activated() {
console.log('News组件被激活了')
this.timer = setInterval(() => {
console.log('@')
this.opacity -= 0.01
if(this.opacity <= 0) this.opacity = 1
},16)
},
deactivated() {
console.log('News组件失活了')
clearInterval(this.timer)
},
}
</script>
作用:对路由进行权限控制
分类:全局守卫、独享守卫、组件内守卫
全局守卫:
路由信息中的 meta 可以任意写上一些键值对,如 meta : { isAuth : true }
//全局前置守卫:初始化时执行、每次路由切换前执行
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'
}
})
独享守卫:(直接写在路由信息里,且独显守卫只有前置,没有后置)
beforeEnter(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()
}
}
组件内守卫:
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}
//写在路由文件的index.js中
mode: 'history'
npm run build
执行命令后会出现dist文件,即打包后的文件内容。
安装:
npm i element-ui
全局引入:
import Vue from 'vue';
import App from './App.vue';
//引入ElementUI组件库
import ElementUI from 'element-ui';
//引入ElementUI全部样式
import 'element-ui/lib/theme-chalk/index.css';
//应用ElementUI
Vue.use(ElementUI);
new Vue({
el: '#app',
render: h => h(App)
});
按需引入:
安装babel-plugin-component
npm install babel-plugin-component -D
修改 babel.config.js 文件,添加内容:
module.exports = {
presets: [
["@babel/preset-env", { "modules": false }]
],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
按需引入:
import 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)
});
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配置文件
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
Tomcat 部署项目:将项目放置到webapps目录下,即部署完成。
- 一般JavaWeb项目会被打成war包,然后将war包放到webapps目录下,Tomcat会自动解压缩war文件
第二种部署方式:
项目不需要放在webapps下,可以放在任何地方;
找到 Tomcat 下的 conf 目录\Catalina\localhost\ 下,创建配置文件,如abc.xml,配置文件内容如下:
在IDEA中创建Maven Web项目:
创建方式:
WEB-INF放到webapp目录下
在IDEA中使用Tomcat:
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
<configuration>
<port>80port>
<path>/path>
configuration>
plugin>
plugins>
build>
JavaWeb三大组件:Servlet、.Filter、Listener;
Servlet是 Java提供的一门 动态web资源开发技术。
Servlet是javaEE规范之一,也就是一个接口,将来我们需要定义Servlet类实现Servlet接口,并由web服务器运行Servlet。
Servlet可以接收客户端发过来的请求,并响应数据给客户端;
public interface Servlet
<dependencies>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-api artifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.2version>
<scope>providedscope>
dependency>
dependencies>
创建:定义一个类,实现Servlet接口,并重写接口中所有方法。
public class ServletDemo1 implements Servlet{
public void service(){}
....
}
配置:在类上使用@WebServlet注解,配置该Servlet的访问路径
@WebServlet("/demo1")
public class ServletDemo1 implements Servlet{}
访问:启动Tomcat,浏览器输入URL访问该Servlet
http://localhost:8080/web-demo/demo1
执行流程
生命周期
Servlet运行在Servlet容器(web服务器)中,其生命周期由容器来管理,分为4个阶段:
加载和实例化:默认情况下,当Servlet第一次被访问时,由容器创建Servlet对象
初始化:在Servlet实例化之后,容器将调用Servlet的 init()方法初始化这个对象,完成一些如加载配置文件、创建连接等初始化的工作。该方法只调用一次
可以在注解中修改init()调用的时机:
@WebServlet(urlPatterns="/demo",loadOnStartup=1) //负整数:第一次被访问时创建 Servlet对象 //0或正整数:服务器启动时创建Servlet对象,数字越小优先级越高
请求处理/服务:每次请求Servlet时,Servlet容器都会调用Servlet的service()方法对请求进行处理。
服务终止:当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法完成资源的释放。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收
Servlet体系结构
开发B/S架构的web项目,都是针对HTTP协议,所以自定义的Servlet都是继承HttpServlet。
@WebServlet("/demo1")
public class ServletDemo1 extends HttpServlet{
@Override
protected void doGet((HttpServletRequest req,HttpServletResponse resp){
//TOOD Get 请求方式处理逻辑
}
@override
protected void doPost(HttpServletRequest req,HttpservletResponse resp){
//TOOD Post 清求方式处理逻辑
}
}
ServletConfig类是Servlet程序的配置信息类。
方法:
注: 如果重写了HttpServlet类的init方法,一定要调用父类的init(ServletConfig),因为HttpServlet类的init方法里,保存了ServletConfig对象,不然的话,调用getInitParameter(" ")方法会报错。
web.xml配置:设置servlet程序的别名,访问路径
<servlet>
<servlet-name>HelloServletservlet-name>
<servlet-class>com.atguigu.servlet.HelloServletservlet-class>
<init-param>
<param-name>usernameparam-name>
<param-value>rootparam-value>
init-param>
<init-param>
<param-name>urlparam-name>
<param-value>jdbc:mysql://localhost:3306/testparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>HelloServletservlet-name>
<url-pattern>/hellourl-pattern>
servlet-mapping>
域对象: 是可以像Map一样存取数据的对象,这里的域指存取数据的操作范围,即整个web工程;
//获取ServletContext对象
ServletContext context = getServletConfig().getServletContext();
//直接get也可以,本质上也是调用getServletConfig().getServletContext();
ServletContext context = getServletContext();
方法:
getInitParameter(“…”) —— 获取 web.xml 中配置的上下文参数 context-param (括号里写param-name值,得到param-value值)
getContextPath() —— 获取当前的工程路径,格式: /工程路径
getRealPath(“/”) —— 获取工程部署后在服务器硬盘上的绝对路径
getRealPath(“/”) —— 工程部署的路径
getRealPath(“/css”) —— 工程下 css 目录的绝对路径
像 Map 一样存取数
存数据 | 取数据 | 删除数据 | |
---|---|---|---|
Map | put() | get() | remove() |
域对象 | setAttribute() | getAttribute() | removeAttribute() |
<context-param>
<param-name>usernameparam-name>
<param-value>contextparam-value>
context-param>
<context-param>
<param-name>passwordparam-name>
<param-value>rootparam-value>
context-param>
HTTP协议是 客户端和服务器之间通信时要发送的数据,需要遵守的规则; HTTP 协议中的数据又叫报文。
GET请求的组成:
请求行: 请求方式 请求的资源路径 请求的协议版本号
如: GET /servlet_1/a.html HTTP/1.1
请求头:以键值对的形式组成
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页面
- 在浏览器地址栏中输入地址后敲回车
响应 的组成:
常用响应码:
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 |
每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 HTTP 协议信息解析好封装到 Request 对象中。 然后传递到 service 方法(doGet 和 doPost)中给我们使用。
而通过 HttpServletRequest 对象,可以获取到所有 请求 的信息。
方法:
如果doPost中获取数据出现中文乱码,可以设置请求体的字符集为UTF-8:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); //设置字符集要写在获取请求参数之前,否则不生效 }
HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,Tomcat 服务器都会创建一个 Response 对象传递给 Servlet 程序去使用。
HttpServletRequest表示请求过来的信息,HttpServletResponse 表示所有响应的信息,;我们如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse对象来进行设置。
两个输出流:
两个流同时只能使用一个。
public class ResponseIOServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//resp.setContentType("text/html; charset=UTF-8"); 用于解决响应中文乱码:它会同时设置服务器和客户端都使用UTF-8字符集,还设置了响应头;注:要在获取流对象之前使用才有效;
// 要求: 往客户端回传 字符串 数据。
PrintWriter writer = resp.getWriter();
writer.write("response's content!!!");
}
}
是指客户端给服务器发请求,然后服务器告诉客户端说。我给你一些地址。你去新地址访问,叫请求 重定向。(因为之前的地址可能已经被废弃)
response.sendRedirect("新地址");
请求转发/内部转发:
意思是:服务器收到请求后,从一个资源跳转到另一个资源。即服务器内部转发。
request.getRequestDispatcher("demo07").forward(request,response);
//办事处1 —— Servlet1程序
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
// 获取请求的参数(办事的材料)查看
String username = req.getParameter("username");
System.out.println("在 Servlet1(柜台 1)中查看参数(材料):" + username);
// 给材料 盖一个章,并传递到 Servlet2(柜台 2)去查看
req.setAttribute("key1","柜台 1 的章");
// 问路:Servlet2(柜台 2)怎么走
// 请求转发必须要以斜杠打头,/ 斜杠表示地址为:http://ip:port/工程名/ , 映射到 IDEA 代码的 web 目录
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
// RequestDispatcher requestDispatcher = req.getRequestDispatcher("http://www.baidu.com");
// 走向 Sevlet2(柜台 2)
requestDispatcher.forward(req,resp);
}
//办事处2 —— Servlet2程序
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
// 获取请求的参数(办事的材料)查看
String username = req.getParameter("username");
System.out.println("在 Servlet2(柜台 2)中查看参数(材料):" + username);
// 查看 柜台 1 是否有盖章
Object key1 = req.getAttribute("key1");
System.out.println("柜台 1 是否有章:" + key1);
// 处理自己的业务
System.out.println("Servlet2 处理自己的业务 ");
}
作用域一般有四个:
page(页面级别,现在几乎不用)
request(一次请求响应范围)
重定向中,客户端实际发起了两次请求;所以在第一次请求中在request的作用域中保存数据,数据不会保存到第二次请求。
//在 /Servlet_1 中
req.setAttribute("key","value");
req.sendRedirect("/Servlet_2");
//在 /Servlet_2 中
System.out.println(req.getAttribute("key")); //打印null,不能得到结果
session(一次会话范围)
//在 /Servlet_1 中
req.getSession().setAttribute("key","value");
req.sendRedirect("/Servlet_2");
//在 /Servlet_2 中
System.out.println(req.getAttribute("key")); //能得到结果
application(整个应用程序范围)
//这里假设/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")); //能得到结果
文件上传:
<form action="url地址" enctype="multipart/form-data" method="提交方式" name="名称">
multipart/form-data表示提交的数据,以多段(每一个表单项一个数据段)的形式进行拼接,然后以二进制流的形式发送给服务器
查看http协议内容时,context-type部分有属性boundary:表示每段数据的分隔符,分隔符是由浏览器每次都随机生成,它就是每段上传数据的分界符。
文件接收:
<form action="http://192.168.31.74:8080/09_EL_JSTL/uploadServlet" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username" /> <br>
头像:<input type="file" name="photo" > <br>
<input type="submit" value="上传">
form>
//fileupload类库的适用:
//需要导入两个jar包:commons-fileupload.jar 和 commons-io.jar
//解析上传的数据
public class Servlet_1 extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
//1 先判断上传的数据是否多段数据(只有是多段的数据,才是文件上传的)
if (ServletFileUpload.isMultipartContent(req)) {
// 创建 FileItemFactory 工厂实现类
FileItemFactory fileItemFactory = new DiskFileItemFactory();
// 创建用于解析上传数据的工具类 ServletFileUpload 类
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
try {
// 解析上传的数据,得到每一个表单项 FileItem
List<FileItem> list = servletFileUpload.parseRequest(req);
// 循环判断,每一个表单项,是普通类型,还是上传的文件
for (FileItem fileItem : list) {
if (fileItem.isFormField()) {
// 普通表单项
System.out.println("表单项的 name 属性值:" + fileItem.getFieldName());
// 参数 UTF-8.解决乱码问题
System.out.println("表单项的 value 属性值:" + fileItem.getString("UTF-8"));
} else {
// 上传的文件
System.out.println("表单项的 name 属性值:" + fileItem.getFieldName());
System.out.println("上传的文件名:" + fileItem.getName());
fileItem.write(new File("e:\\" + fileItem.getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
文件下载:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 获取要下载的文件名
String downloadFileName = "2.jpg";
//2. 获取要下载的文件内容(通过ServletContext对象可以获取)
ServletContext servletContext = getServletContext();
//获取要下载的文件类型
String mimeType = servletContext.getMimeType("/file/" + downloadFileName);
//4.在回传前,通过响应头告诉客户端返回的数据类型
response.setContentType(mimeType);
//5.告诉客户端收到的数据是用于下载的,还是适用响应头的
response.setHeader("Content-Disposition","attachment;filename=文件名");
//输入流获取文件内容
InputStream resourceAsStream = servletContext.getResourceAsStream("/file/" + downloadFileName);
//获取响应的输出流
OutputStream outputStream = response.getOutputStream();
//读取输入流中全部数据,复制给输出流,输出给客户端
IOUtils.copy(resourceAsStream, outputStream);
}
Filter表示过滤器,可以把对资源的请求拦截下来;
通常用过滤器完成一些通用的操作,比如:权限控制、统一编码处理、敏感字符处理等等…
JavaWeb三大组件:Servlet、.Filter、Listener;
使用:实现Filter接口 ,并重写三个方法;注意是javax.servlet. 下的Filter接口。
@WebFilter("/*") //设置拦截路径, “/*” 指拦截全部
public class Filter_1 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 放行前逻辑(已被拦截)
filterChain.doFilter(servletRequest, servletResponse); //放行
// 放行后逻辑
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化
}
@Override
public void destroy() {
// 销毁
}
}
Filter执行流程:
执行放行前逻辑——放行——访问资源——执行放行后逻辑
Filter拦截路径配置
@WebFilter("...") //括号中设置拦截路径
如果要拦截多个页面或分散文件,要在括号例写urlPatterns属性,然后在里面写属性的值,逗号分割;
@WebFilter(urlPatterns = {"...","...","..."})
过滤器链
一个Web应用,可以配置多个过滤器,这多个过滤器称为过滤器链。
例如:对同一应用,如果执行了2个过滤器,执行顺序为:Filter1的放行前逻辑,Filter1放行,Filter2放行前逻辑,Filter2放行,Filter2放行后逻辑,Filter1放行后逻辑。(类似递归)
过滤器的执行顺序按 过滤器的类名的字典序排序;
FilterConfig类
Filter过滤器的配置文件类
filterConfig.getFilterName() //获取过滤器的名称
filterConfig.getInitParameter("") //获取过滤器中配置的初始化参数
监听器,是在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接口,并重写两个方法;
@WebListener
class a implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent sce) {
//加载资源
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
//释放资源
}
}
会话:用户打开浏览器,访问wb服务器的资源,会话建立,直到有一方断开连接, 会话结束(浏览器关闭)。在一次会话中可以包含多次请求和响应
会话跟踪:服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
由于HTTP协议是无状态的,每次浏览器发出请求,服务器都会将请求视为新的请求,所以需要会话跟踪技术。
客户端会话技术,将数据保存到客户端,以后客户端每次请求都携带Cookie数据进行访问。
客户端第一次向服务端发送请求,服务器会生成一个cookie对象,和响应一起发给客户端;客户端之后向服务端发送请求时,会携带cookie一起发送,服务端识别cookie就会知道客户端的身份。
使用:
//发送:
//创建Cookie对象,设置数据
Cookie cookie = new Cookie("key","value");
//发送Cookie到客户端,使用response对象
response.addCookie(cookie);
//获取:
//获取客户端所有Cookie,使用request对象
Cookie[] cookies = request.getCookies();
//遍历数组,获取每一个Cookie对象
//使用Cookie对象方法获取数据
for (Cookie cookie : cookies){
String name = cookie.getName();
if("key".equals(name)){
String value = cookie.getValue();
break;
}
}
//修改Cookie的值
//方法一:创建一个同名的Cookie对象,直接覆盖
Cookie cookie = new Cookie("key","newValue");
resp.addCookie(cookie);
//方法二:找到对应的Cookie对象,用setValue()修改
Cookie cookie = CookieUtils.findCookie("key2", req.getCookies());
if (cookie != null) {
cookie.setValue("newValue2");
resp.addCookie(cookie); //修改后还要通知客户端保存修改
}
原理:
Cookie的实现是基于HTTP协议的;
响应头:set-cookie
请求头:cookie
注:
Cookie存活时间:
默认情况下,Cookie存储在浏览器内存中,当浏览器关闭,内存释放,则Cookie被销毁。
setMaxAge(int seconds) :设置Cookie存活时间
正数:将Cookie写入浏览器所在电脑的硬盘,持久化存储;到时间自动删除。
负数:默认值,Cookie在当前浏览器内存中,当浏览器关闭,则Cookie被销毁;
零:删除对应Cookie;
Cookie存储中文:
- 默认情况不能存储中文;如果要存中文,则需要进行转码:可用URL编码
服务端会话跟踪技术:将数据保存到服务端;
JavaEE提供HttpSession接口,来实现一次会话的多次请求之间的 数据共享功能。
使用:
//获取Session对象
HttpSession session = request.getSession();
//Session对象功能:
void setAttribute(String name,Object o) //存储数据到session域中
Object getAttribute(String name) //根据key,获取值
void removeAttribute(String name) //根据key,删除该键值对
boolean isNew() //判断是不是刚创建的
原理:
Session技术,底层是基于Cookie技术实现的。
如果客户端发送的请求没有携带cookie,则服务端会新创建一个session和cookie,cookie的key值session的id值相同,然后session保存在服务端,cookie返回给客户端。
如果客户端发送的请求携带了cookie,则服务端会通过cookie的key值找到对应保存的session,然后返回相应的cookie。
注意事项
Session 钝化、活化:
- 钝化:在服务器正常关闭后,Tomcats会自动将Session数据写入硬盘的文件中;
- 活化:再次启动服务器后,从文件中加载数据到Session中;
Session销毁:
默认30分钟无操作,销毁;
<session-config> <session-timeout>30session-timeout> session-config>
//也可以使用session的方法设置单个 void setMaxInactiveInterval(int interval) //设置session的超时时间,单位为秒,设置负数表示永不超时 int getMaxInactiveInterval() //获取超时间 void invalidate() //立即超时,销毁
Cookie和Session异同点:
Cookie和Session都是来完成一次会话内多次请求间数据共享的;
区别:
概念: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代码
<dependencies>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.2version>
<scope>providedscope>
dependency>
dependencies>
page指令:
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
属性:
脚本:
JSP脚本:用于在JSP页面内定义Java代码
分为:
代码脚本 <% … %>
用于编写需要的功能。内容会直接放到 jspService()方法之中。
<%--2.代码脚本----for 循环语句--%>
<%
for (int j = 0; j < 10; j++) {
%>
第 <%=j + 1%>行
<%
}
%>
表达脚本 <%= … %>
用于在jsp页面上输出数据。内容会放到out.print()中,作为out.print0的参数
注:表达脚本中的表达式不能以分号结束。
声明脚本 <%! … %>
可以给 jsp 翻译出来的 java 类定义属性和方法甚至是静态代码块,内部类等。
内容会放到 _jspService()方法之外,被类直接包含。
注:声明脚本很少使用
<%!
private Integer id;
private String name;
private static Map map;
%>
<%!
static {
map = new HashMap();
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的四个作用域相同。
静态包含(常用)
<%@ include file="" %>
<%@ include file="/include/footer.jsp"%>
file属性指定要包含的jsp页面的路径,地址的第一个斜杠为 “ http /ip:port/工程路径/ ” 映射到web目录。
注:静态包含不会翻译被包含的jsp页面,而是直接把页面拷贝到包含的位置执行输出。
动态包含(少用)
page属性是指定要包含的 jsp页面的路径。
注:动态包含会 直接翻译被包含的jsp页面,同时还可以传递参数。
转发标签(常用)
page属性设置请求转发的路径
相当于:request.getRequestDispatcher(“/xxxx.jsp”).forward(request,reponse);
Expression Language ,即表达式语言,用于简化 JSP页面内的java代码。
主要功能:获取数据;
语法:
${表达式}
JavaWeb中的四大域对象:
- page:当前页面有效;
- request:当前请求有效;
- session:当前会话有效;
- application:当前应用有效;
——EL表达式获取数据,会依次从4个域中寻找,直到找到为止。(四个域的范围逐渐增大,即page范围最小,application最大)
empty运算
判断一个数据是否为空,如果为空则输出true。
值为null,空串时,Object类型数组长度为0时,list集合中元素个数为0,map集合元素个数为0,这些情况均视为空。
<%
request.setAttribute("obj", null);
%>
${empty obj}
关系运算: == != < > <= >=
逻辑运算:&& || !
算数运算: + - * / %
三元运算
点运算 中括号运算:
点运算 输出Bean对象中某个属性的值;
中括号运算 输出有序集合中某个元素的值;
<%
Map map = new HashMap();
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'] }
${ map["b+b+b"] }
${ map['c-c-c'] }
11个隐含对象:
变量 | 类型 | 作用 |
---|---|---|
pageContext | PageContextImpl | 获取jsp九大内置对象 |
pageScope | Map |
获取pageContext域中的数据 |
requestScope | Map |
获取Request域的数据 |
sessionScope | Map |
获取Session域的数据 |
applicationScope | Map |
获取ServletContext域的数据 |
param | Map |
获取请求参数的值 |
paramValues | Map |
获取多个请求参数的值 |
header | Map |
获取请求头的信息 |
headerValues | Map |
获取多个请求头的信息 |
cookie | Map |
获取当前请求的Cookie信息 |
获取4个特定域的属性:
pageScope | pageContext域 |
---|---|
requestScope | Request域 |
sessionScope | Session域 |
applicationScope | ServletContext域 |
<%
application.setAttribute("key2", "application");
%>
${ applicationScope.key2 }
pageContext的使用:
<%
pageContext.setAttribute("req", request);
%>
<%= request.getScheme() %>
1.协议: ${ req.scheme }
2.服务器 ip:${ pageContext.request.serverName }
3.服务器端口:${ pageContext.request.serverPort }
4.获取工程路径:${ pageContext.request.contextPath }
5.获取请求方法:${ pageContext.request.method }
6.获取客户端 ip 地址:${ pageContext.request.remoteHost }
7.获取会话的 id 编号:${ pageContext.session.id }
JSTL标准标签库(Jsp Standarded Tag Library),使用标签取代JSP页面上的Java代码。
步骤:
1.导入坐标:
<dependency>
<groupId>jstlgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>taglibsgroupId>
<artifactId>standardartifactId>
<version>1.1.2version>
dependency>
2.在JSP页面上引入JSTL标签库:
<%@ 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
....
hello world!
forEach
$(xx.index)
$(brand.brandName)
S(brand.companyName)
$(brand.description)
for(Brand brand : brands){
Integer id = brand.getld();
String imgUrl = brand.getlmgUrl();
String brandName = brand.getBrandName();
String companyName = brand.getCompanyName();
}
${i}
for(int i = 0; i <= 10; i++){
System.out.println(i);
}
MVC是一种分层开发的模式,其中:
MVC只能实现模型到视图的单向展示
三层架构
表现层:接收请求,封装数据,调用业务逻辑层,响应数据
业务逻辑层:对业务逻辑进行封装,组合数据访问层的基本功能,形成复杂的业务逻辑功能
数据访问层(持久层):对数据库的CRUD基本操作
概念:AJAX(Asynchronous JavaScript And XML):异步的 javaScript 和 XML
AJAX作用
与服务器进行数据交换: 通过AJAX可以给服务器发送请求,并获取服务器响应的数据。
使用了AJAX和服务器进行通信,就可以使用 HTML+AJAX 来替换 JSP 页面了。
异步交互:可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术,如:搜索联想、用户名是否可用校验,等等…
Ajax 请求的局部更新,浏览器地址栏不会发生变化, 局部更新不会舍弃原来页面的内容。
同步:浏览器请求服务器 ,需要等待服务器处理并响应后,才能继续操作。
异步:浏览器请求服务器,服务器在处理时,浏览器可以做其他操作。
服务端:Servlet程序
客户端:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<script>
//1. 创建核心对象
var xhttp;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
//2. 发送请求
xhttp.open("GET", "http://localhost:8080/ajax-demo/ajaxServlet");
xhttp.send();
//3. 获取响应
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
alert(this.responseText);
}
};
script>
body>
html>
方法:
方法 | 说明 |
---|---|
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
后端:
@WebServlet("/selectUserServlet")
public class SelectUserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 接收用户名
String username = request.getParameter("username");
//2. 调用service查询User对象,此处不进行业务逻辑处理,直接给 flag 赋值为 true,表明用户名占用
boolean flag = true;
//3. 响应标记
response.getWriter().write("" + flag);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
前端:
//1. 给用户名输入框绑定 失去焦点事件
document.getElementById("username").onblur = function () {
//2. 发送ajax请求
// 获取用户名的值
var username = this.value;
//2.1. 创建核心对象
var xhttp;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
//2.2. 发送请求
xhttp.open("GET", "http://localhost:8080/ajax-demo/selectUserServlet?username="+username);
xhttp.send();
//2.3. 获取响应
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
//判断
if(this.responseText == "true"){
//用户名存在,显示提示信息
document.getElementById("username_err").style.display = '';
}else {
//用户名不存在 ,q提示信息
document.getElementById("username_err").style.display = 'none';
}
}
};
}
Axios 是一个基于 promise 的 HTTP 库,简单的讲就是可以发送get、post请求。
Axios对原生的AJAX进行封装,简化书写。
axios 使用是比较简单的,分为以下两步:
引入 axios 的 js 文件
<script src="js/axios-0.18.0.js">script>
使用axios 发送请求,并获取响应结果
发送 get 请求
axios({
method:"get",
url:"http://localhost:8080/ajax-demo1/aJAXDemo1?username=zhangsan"
}).then(function (a){
alert(a.data);
})
发送 post 请求
axios({
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,代码如下:
@WebServlet("/axiosServlet")
public class AxiosServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("get...");
//1. 接收请求参数
String username = request.getParameter("username");
System.out.println(username);
//2. 响应数据
response.getWriter().write("hello Axios~");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("post...");
this.doGet(request, response);
}
}
2.前端实现
引入 js 文件
<script src="js/axios-0.18.0.js"></script>
发送 ajax 请求
get 请求
axios({
method:"get",
url:"http://localhost:8080/ajax-demo/axiosServlet?username=zhangsan"
}).then(function (resp) {
alert(resp.data);
})
post 请求
axios({
method:"post",
url:"http://localhost:8080/ajax-demo/axiosServlet",
data:"username=zhangsan"
}).then(function (resp) {
alert(resp.data);
})
整体页面代码如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<script src="js/axios-0.18.0.js">script>
<script>
//1. get
/* axios({
method:"get",
url:"http://localhost:8080/ajax-demo/axiosServlet?username=zhangsan"
}).then(function (resp) {
alert(resp.data);
})*/
//2. post 在js中{} 表示一个js对象,而这个js对象中有三个属性
axios({
method:"post",
url:"http://localhost:8080/ajax-demo/axiosServlet",
data:"username=zhangsan"
}).then(function (resp) {
alert(resp.data);
})
script>
body>
html>
为了方便起见, 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
请求代码可以改为如下:
axios.get("http://localhost:8080/ajax-demo/axiosServlet?username=zhangsan").then(function (resp) {
alert(resp.data);
});
入门案例中的 post
请求代码可以改为如下:
axios.post("http://localhost:8080/ajax-demo/axiosServlet","username=zhangsan").then(function (resp) {
alert(resp.data);
})
var jsObject = {name:"张三"};
axios({
method:"post",
url:"http://localhost:8080/ajax-demo/axiosServlet",
data:jsObject //这里 axios 会将该js对象转换为 json 串的
}).then(function (resp) {
alert(resp.data);
})
注:
- 发送异步请求时,如果请求参数是
JSON
格式,那请求方式必须是POST
。因为JSON
串需要放在请求体中。- axios将js对象转为json字符串,用了 js提供的 JSON对象,即 JSON.stringify (jsObject)。(了解即可)
下载axios:
npm i axios
引入:
import axios from 'axios'
使用
跨域问题:
在vue.config.js
方式一:
module.experts={ devServer:{ proxy: 'http://localhost:80' //获取数据的地址 } }
- 优点:配置简单,请求资源时直接发给前端(8080)即可。
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
前端请求的地址端口要改成 8080 (与自己页面的端口一致)
方式二:
module.experts={ proxy: { '/api1': {// 匹配所有以 '/api1'开头的请求路径 target: 'http://localhost:80',// 代理目标的基础路径 changeOrigin: true, pathRewrite: {'^/api1': ''} }, '/api2': {// 匹配所有以 '/api2'开头的请求路径 target: 'http://localhost:5001',// 代理目标的基础路径 changeOrigin: true, pathRewrite: {'^/api2': ''} } } }
- 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
- 缺点:配置略微繁琐,请求资源时必须加前缀。
Thymeleaf 是(服务器端)视图模板技术,
优势:和springBoot完美契合。不经过服务器运算仍可以直接查看原始值,对前端友好。
物理视图:
在Servlet中,将请求转发到一个HTML页面文件时,使用的完整的转发路径就是 物理视图。如:/pages/user/login_success.html
由于html页面放在统一的目录下,所以转发地址会有相同的前缀 和 后缀,称为视图前缀,视图后缀。
逻辑视图:
物理视图 = 视图前缀 + 逻辑视图 + 视图后缀
也就是说,逻辑视图 就是指转发路径中间不一样的部分(html页面名)。
使用步骤:
添加jar包(maven可直接添加依赖);
配置两个 < context-param >: view-prefix,view-suffix
<context-param>
<param-name>view-prefixparam-name>
<param-value>/WEB-INF/view/param-value>
context-param>
<context-param>
<param-name>view-suffixparam-name>
<param-value>.htmlparam-value>
context-param>
新建viewBaseServlet(有两个方法)
public class ViewBaseServlet extends HttpServlet {
private TemplateEngine templateEngine;
@Override
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());
}
}
…
) {
//判断
if(this.responseText == “true”){
//用户名存在,显示提示信息
document.getElementById(“username_err”).style.display = ‘’;
}else {
//用户名不存在 ,q提示信息
document.getElementById(“username_err”).style.display = ‘none’;
}
}
};
}
## Axios
Axios 是一个基于 promise 的 HTTP 库,简单的讲就是可以发送get、post请求。
Axios对原生的AJAX进行封装,简化书写。
### 使用
axios 使用是比较简单的,分为以下两步:
* 引入 axios 的 js 文件
```html
使用axios 发送请求,并获取响应结果
发送 get 请求
axios({
method:"get",
url:"http://localhost:8080/ajax-demo1/aJAXDemo1?username=zhangsan"
}).then(function (a){
alert(a.data);
})
发送 post 请求
axios({
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,代码如下:
@WebServlet("/axiosServlet")
public class AxiosServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("get...");
//1. 接收请求参数
String username = request.getParameter("username");
System.out.println(username);
//2. 响应数据
response.getWriter().write("hello Axios~");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("post...");
this.doGet(request, response);
}
}
2.前端实现
引入 js 文件
<script src="js/axios-0.18.0.js"></script>
发送 ajax 请求
get 请求
axios({
method:"get",
url:"http://localhost:8080/ajax-demo/axiosServlet?username=zhangsan"
}).then(function (resp) {
alert(resp.data);
})
post 请求
axios({
method:"post",
url:"http://localhost:8080/ajax-demo/axiosServlet",
data:"username=zhangsan"
}).then(function (resp) {
alert(resp.data);
})
整体页面代码如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<script src="js/axios-0.18.0.js">script>
<script>
//1. get
/* axios({
method:"get",
url:"http://localhost:8080/ajax-demo/axiosServlet?username=zhangsan"
}).then(function (resp) {
alert(resp.data);
})*/
//2. post 在js中{} 表示一个js对象,而这个js对象中有三个属性
axios({
method:"post",
url:"http://localhost:8080/ajax-demo/axiosServlet",
data:"username=zhangsan"
}).then(function (resp) {
alert(resp.data);
})
script>
body>
html>
为了方便起见, 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
请求代码可以改为如下:
axios.get("http://localhost:8080/ajax-demo/axiosServlet?username=zhangsan").then(function (resp) {
alert(resp.data);
});
入门案例中的 post
请求代码可以改为如下:
axios.post("http://localhost:8080/ajax-demo/axiosServlet","username=zhangsan").then(function (resp) {
alert(resp.data);
})
var jsObject = {name:"张三"};
axios({
method:"post",
url:"http://localhost:8080/ajax-demo/axiosServlet",
data:jsObject //这里 axios 会将该js对象转换为 json 串的
}).then(function (resp) {
alert(resp.data);
})
注:
- 发送异步请求时,如果请求参数是
JSON
格式,那请求方式必须是POST
。因为JSON
串需要放在请求体中。- axios将js对象转为json字符串,用了 js提供的 JSON对象,即 JSON.stringify (jsObject)。(了解即可)
下载axios:
npm i axios
引入:
import axios from 'axios'
使用
跨域问题:
在vue.config.js
方式一:
module.experts={ devServer:{ proxy: 'http://localhost:80' //获取数据的地址 } }
- 优点:配置简单,请求资源时直接发给前端(8080)即可。
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
前端请求的地址端口要改成 8080 (与自己页面的端口一致)
方式二:
module.experts={ proxy: { '/api1': {// 匹配所有以 '/api1'开头的请求路径 target: 'http://localhost:80',// 代理目标的基础路径 changeOrigin: true, pathRewrite: {'^/api1': ''} }, '/api2': {// 匹配所有以 '/api2'开头的请求路径 target: 'http://localhost:5001',// 代理目标的基础路径 changeOrigin: true, pathRewrite: {'^/api2': ''} } } }
- 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
- 缺点:配置略微繁琐,请求资源时必须加前缀。