我学会了,用Echarts
Echarts 香不香阿里的DataV就是基于Echarts做的哟,Echarts连续多年获得中国最受欢迎的开源项目之一,这么受欢迎,通用性非常强,是完全免费的哟。Echarts特性可视化类型丰富支持多种数据格式千万级的前端数据渲染动态数据的动画展示支持基于WebGL的3D效果跨平台,小程序中也能用提供了数据深度搜索的系列组件无障碍访问,盲人可以听到 图表内容Echarts支持“canvas”和“svg”两种渲染方式。简单的环境Echarts 官方可以直接下载类库压缩包,可以下载源码,也可以下载编译后直接可用的代码。VS Code 官方可以直接下载开发工具,点击右上角的Download即可完成下载,正常软件的安装方式一路点下一步,即可完成安装。Echarts 官方文档入门教程配置项APIGL配置Echarts 术语查询手册术语查询手册常用组件系列类型坐标系组件小用一把图表示例:生成 A-Z的26种产品维度,生成26份随机数据。注意:由于数据是随机的,则每次渲染图表都不会一样。<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ECharts</title> <!-- 引入 echarts.js --> <script src="./echarts.js"></script> </head> <body> <!-- 为ECharts准备一个具备大小(宽高)的Dom --> <div id="main" style="width: 600px;height:400px;"></div> <script type="text/javascript"> // 基于准备好的dom,初始化echarts实例 var myChart = echarts.init(document.getElementById('main')); // 指定图表的配置项和数据 var option = { title: { text: '我的第一幅ECharts可视化图' tooltip: {}, legend: { data:['各产品销量情况'] xAxis: { // 生成 A-Z 产品维度 data: Array(26).fill(65).map((item, index) => String.fromCharCode(item + index)).map(item => '产品' + item) yAxis: {}, series: [{ name: '销量', type: 'bar', // 生成26份随机数据 data: Array(26).fill(1000).map((item, index) => item * (index + 1) * Math.random()).map(item => ~~item) // 使用刚指定的配置项和数据显示图表。 myChart.setOption(option); </script> </body> </html>Echarts 常用标题、提示框、工具栏、图例、时间轴、数据区域缩放、网格、坐标轴、数据系列、全局字体样式等。标题 titletext:主标题文本,支持\n换行subtext:副标题文本,支持\n换行left:距离左侧容器的距离,单位:像素(10)、百分比(10%)、左中右对齐(left center right)等等show:是否显示,默认true提示框 tooltip在合适的时机向用户提供相关信息trigger:触发类型,图形触发item、坐标轴触发axis、不触发noneformatter:悬浮层的内容格式化器,有 {a} {b} {c} {d} {e} 这五个占位符,代表 系列名、数据名、数据值等。{a} {b}和{c} {d}在不同的图表中表示的含义又不相同。折线图、区域图、柱状图、条形图、K线图:a 系列名、b 类目值、c 数值、d 无数据散点图、气泡图:a 系列名、b 数据名、c 数值数组、d 无数据地图:a 系列名、b 区域名、c 合并数值、d 无数据饼图、仪表盘、漏斗图:a 系列名称、b 数据项名、c 数值、d 百分比axisPointer:坐标指示器,子参数项 type 参数值可为:直线指示器 line、阴影指示器 shadow、十字准星指示器cross、none 无指示器show:是否显示,默认值为true工具栏 toolbox可用于将可视化图表下载到本地、查看可视化底层数据等show:是否显示,默认值为truefeature:常用子参数项有: 保存图片 saveAsImage、还原初始配置 restore、可视化底层数据视图 dataView、切换多种可视化类型(如 折线图、柱状图等) magicType图例 legendshow:是否显示left:距离左侧容器的距离,单位:像素(10)、百分比(10%)、左中右对齐(left center right)等等top:距离顶部的距离,单位:像素(10)、百分比(10%)、顶中底部对齐(top middle bottom)等等orient:图例列表的朝向,默认是水平 horizontal,还可设置为 垂直 verticaldata:图例中的数据数组,通常与数据展示的系列(series)对应起来,data与series一旦对应起来了,它们之间就是强关联的。时间轴 timeline用于切换多套配置项,baseOption、options,每次切换时,baseOption + options配置中某一项的进行合并配置,合并后的配置会用来渲染当前时间的图表。baseOption:默认配置,子参数项 timeline:是否循环 loop、反向播放 rewind、播放速度 playInterval、播放按钮的位置 controlPosition。options:配置集合<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ECharts</title> <!-- 引入 echarts.js --> <script src="./echarts.js"></script> </head> <body> <!-- 为ECharts准备一个具备大小(宽高)的Dom --> <div id="main" style="width: 600px;height:400px;"></div> <script type="text/javascript"> // 基于准备好的dom,初始化echarts实例 var myChart = echarts.init(document.getElementById('main')); // 指定图表的配置项和数据 var option = { baseOption: { timeline: { data: ['2020', '2021', '2022'] title: { subtext: '秀哇' grid: {}, xAxis: [ 'type': 'category', // 生成 A-Z 公司维度 data: Array(26).fill(65).map((item, index) => String.fromCharCode(item + index)).map(item => '公司' + item) yAxis: [ 'type': 'value' series: [ { // 系列一的一些其他配置 type: 'bar' options: [ { // 这是'2020' 对应的 option title: { text: '2020年销量情况' series: [ { // 生成26份随机数据 data: Array(26).fill(1000).map((item, index) => item * (index + 1) * Math.random()).map(item => ~~item) } // 系列一的数据 { // 这是'2021' 对应的 option title: { text: '2021年销量情况' series: [ { // 生成26份随机数据 data: Array(26).fill(1000).map((item, index) => item * (index + 1) * Math.random()).map(item => ~~item) { // 这是'2022' 对应的 option title: { text: '2022年销量情况' series: [ { // 生成26份随机数据 data: Array(26).fill(1000).map((item, index) => item * (index + 1) * Math.random()).map(item => ~~item) // 使用刚指定的配置项和数据显示图表。 myChart.setOption(option); </script> </body> </html>数据区域缩放 dataZoom向用户提供区域缩放的功能滑动条型的数据区域缩放 dataZoomSlider内置型的数据区域缩放 dataZoomInside框选型的数据区域缩放 dataZoomSelect滑动条形的数据区域缩放 和 内置型的数据区域缩放属于dataZoom的子参数项中的配置,常用配置项有xAxisIndex:要控制的X轴的索引,支持取数字和数组,表示控制一个可视化图表中多个图表(比如多条折线图)的那几个图表,0表示第一条折线图。filterMode:过滤模式,dataZoom 数据区域缩放的本质就是数据过滤,也就是过滤掉 缩放区域外 的内容。过滤模式也有多种。filter:过滤掉缩放区域外的数据,当一个可视化图表中包含多个轴时,会影响其它轴的数据范围,对于数据而言,只要有一个维度在缩放区域之外,就会直接被过滤掉。weakFilter:过滤缩放区域外的数据,当一个可视化图表中包含多个轴时,会影响其它轴的数据范围,对于数据而言,当所有的维度在缩放区域之外,才会直接被过滤掉。empty:不影响其它轴的数据范围。none:不过滤掉缩放区域外的数据,只会改变其它轴的数据范围。type:缩放类型slider:带滑块的缩放,可以看到图表下方有一个滑块inside:不带滑块的缩放,将鼠标悬浮到图表区域,然后滑动滚轮,就能缩放了。<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ECharts</title> <!-- 引入 echarts.js --> <script src="./echarts.js"></script> </head> <body> <!-- 为ECharts准备一个具备大小(宽高)的Dom --> <div id="main" style="width: 600px;height:400px;"></div> <script type="text/javascript"> // 基于准备好的dom,初始化echarts实例 var myChart = echarts.init(document.getElementById('main')); // 指定图表的配置项和数据 var option = { dataZoom: [ id: 'dataZoomX', type: 'slider', xAxisIndex: [0], filterMode: 'filter' id: 'dataZoomY', type: 'slider', yAxisIndex: [0], filterMode: 'empty' xAxis: { type: 'value' }, yAxis: { type: 'value' }, series: { type: 'bar', data: Array(26).fill(1000).map((item, index) => item * (index + 1) * Math.random()).map(item => [~~item, ~~item]) // 第一项对应 X 轴,第二项对应 Y 轴 // 使用刚指定的配置项和数据显示图表。 myChart.setOption(option); </script> </body> </html>框选型的数据缩放属于toolbox的一个feature在toolbox的feature中新增一个dataZoom的选项,设置show为true即可。toolbox工具箱中就会多两个按钮,分别是区域缩放和区域缩放撤回。网格 grid用于在坐标系中控制可视化区域的网格空间大小grid中常用的配置项:x、y:x y轴的坐标位置width、height:网格空间的宽高坐标轴 xAxis和yAxis常用的有二维直角坐标系,横轴xAxis和纵轴yAxis。xAxis、yAxis中常用的配置项:position:坐标轴放置的位置,x轴可以使用顶部底部 top、bottom,y轴可以使用左边右边 left、right。type:坐标轴的类型,数值类型的轴 value、类别类型的轴 category、时间类型的轴 time、对数类型的轴 log。value适用于连续的数值、category 适用于离散的类别、time 适用于连续的时间序列、log使用于对数数据。name:坐标轴的名字nameLocation:坐标轴名字的显示位置,start 开始位置、x轴的center和y轴的middle 中间位置、end 结束位置。数据系列 seriesseries 数据系列是用来作为数据的容器,一个图表可能会包含多个系列,每个系列也可能会包含多个数据。 不同的可视化图表,series的形式并不完全相同哟,例如饼图和柱状图,更多不同请看官网series,有详细描述哟。全局字体样式 textStyle用于统一定制化设置字体样式,字体样式和可视化图表的搭配非常重要。设置参数的逻辑参考 CSS 样式textStyle中常用的配置项:color:文字颜色,如 ``#ffffontStyle:文字字体风格,如 normal、italic、oblique。fontWeight:文字字体的粗细,如 normal、bold、bolder、lighter、100、500等。fontFamily:文字字体系列,如 sans-serif、serif、monospace、Arial、Courier New、Microsoft YaHei 等。fontSize:文字字体大小,如 14等。Echarts 可视化图通过以上的常用配置学习,是不是感觉echarts多写多练多查阅官方文档就可以搞定大多数图表了?是的,echarts的通用性非常的强,其实我们还有两大利器,echarts官方示例DEMO 和 民间echarts画廊。利器:不仅可以看到很多官方DEMO和民间的DEMO,你还可以对他们的DEMO进行在线编辑,编辑后的结果还可以直接拿来用哟。非常方便嘞。echarts官方示例DEMO民间echarts画廊折线图折线图是比较常见的展示数据趋势的基础图表,常用于展示时间序列的数据。只需要将series中的type设置为line即可。 如:option = { xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] yAxis: { type: 'value' series: [{ data: [150, 230, 224, 218, 135, 147, 260], type: 'line' };常用配置参数smooth:折线的平滑度,设置为true时折线图就会变得非常平滑,变成一张曲线图。stack:对折线图进行分组,当有多条折线图时,都设置这个属性为同一个值时,同一组的折线图的数据会进行累加求和。一般会配合label属性来使用,可以很直观的看到分组后原来的数据值,也可以不配合label来使用。areaStyle:对折线图区域进行色彩填充,一般配合stack来使用,也可以不配合stack来使用。label: 折线图每一个顶点的数据值,如果要显示的话,就可以设置show为true,也可以设置数据值的显示位置,如top、bottom、left、right、middle、center等。emphasis:折线图的高亮效果,可以设置它的focus为 'series'来实现悬浮时高亮当前选中的这条折线图,其它折线图就会呈灰色,就算是有色彩填充的折线图,也会只高亮当前选择这条折线图。熟悉了这些配置之后,可以去官网瞅瞅折线的DEMO哟,民间画廊中有更多炫酷折线效果的DEMO哟。 基于折线图变形的有聚合折线图(多条折线图)、堆叠折线图(面积图和层叠面积图)等。柱状图柱状图是比较常见的展示数据频数的基础图表,常用于展示离散的数据。只需要将series中的type设置为bar即可。 如:option = { xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] yAxis: { type: 'value' series: [{ data: [120, 200, 150, 80, 70, 110, 130], type: 'bar' };常用配置参数label:柱状图中每一个柱子的数据值,如果要显示的话,就可以设置show为true,也可以设置数据值的显示位置,如top、bottom、left、right、middle、center、side、inside等。stack:对柱状图进行分组,当有多种颜色的柱状图时,都设置这个属性为同一个值时,同一组的柱状图的数据会进行累加求和。一般会配合label属性来使用,可以很直观的看到分组后原来的数据值,也可以不配合label来使用。emphasis:柱状图的高亮效果,可以设置它的focus为 'series'来实现悬浮时高亮当前选中的这个颜色的柱状图,其它柱状图就会呈灰色。基于柱状图变形的有聚合柱状图(垂直)、水平聚合柱状图、堆叠柱状图等。饼图饼图是比较常见的展示数据各项比重的基础图表,它没有坐标轴的概念,也就没有xAxis和xAxis属性,只需要将series中的type设置为pie即可。如:option = { title: { text: '某站点用户访问来源', subtext: '纯属虚构', left: 'center' tooltip: { trigger: 'item', formatter: '{a} <br/>{b} : {c} ({d}%)' legend: { orient: 'vertical', left: 'left', series: [ name: '访问来源', type: 'pie', radius: '50%', data: [ {value: 1048, name: '搜索引擎'}, {value: 735, name: '直接访问'}, {value: 580, name: '邮件营销'}, {value: 484, name: '联盟广告'}, {value: 300, name: '视频广告'} emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' };常用配置参数将legend的orient设置为 vertical 是为了防止水平显示与标题重叠在一起。tooltip中的formatter:{a}表示系列名称、{b}表示数据项名称、{c}表示数值、{d}表示百分比radius:如果值为一个数组,数组中有两个值,内外部半径的比例,那么饼图会变成一个环形图,表示内半径到外半径之间作为饼图的展示区域。如果值为一个值,则表示内部半径为0,外部半径为这个值。roseType(玫瑰类型):南丁格尔类型,false 表示不使用南丁格尔,area 表示使用 面积模式,radius 表示使用 半径模式。面积模式会以值来计算面积来作为某一块区域来呈现玫瑰图,半径模式会以值来计算半径作为某一块区域来呈现玫瑰图。基于饼图变形的有环形图、圆角环形图、南丁格尔玫瑰图、日历饼图、折线联动饼图等。散点图与气泡图散点图是常见的 比较跨类别的聚合数据 的基础图表,散点图可用于展示二个维度的数据信息 比如线性回、指数回归、多项式回归等等。 气泡图是基于散点图的,它可以用于展示三个维度的数据信息,除了x、y轴的坐标,还有大小,也就是可以用于呈现突出效果。 effectScatter 是散点图和气泡图的进阶版,带有动态装饰的效果哟。 普通效果的散点或气泡图只需要将series的type设置为scatter即可,如果要展示突出的动态装饰效果,就将series的type设置为 effectScatter。普通散点图:option = { xAxis: {}, yAxis: {}, series: [{ symbolSize: 20, data: [ // 两个维度的数据 [10.0, 8.04], [8.07, 6.95], [13.0, 7.58], [9.05, 8.81], [11.0, 8.33], [14.0, 7.66], [13.4, 6.81], [10.0, 6.33], [14.0, 8.96], [12.5, 6.82], [9.15, 7.20], [11.5, 7.20], [3.03, 4.23], [12.2, 7.83], [2.02, 4.47], [1.05, 3.33], [4.05, 4.96], [6.03, 7.24], [12.0, 6.26], [12.0, 8.84], [7.08, 5.82], [5.02, 5.68] type: 'scatter' };普通气泡图:option = { xAxis: {}, yAxis: {}, series: [{ data: [ // 三个维度的数据 [4.0, 8.04, 10], [9.0, 6.95, 20], [23.0, 7.58, 30], [18.0, 8.81, 15], [12.0, 8.33, 16], [28.0, 9.96, 45], [10.0, 7.24, 18], [14.0, 5.27, 35], [12.0, 10.14, 20], [10.0, 4.12, 50], [7.0, 5.38, 13], [37.0, 5.0, 13] // 通过这个参数值来控制每一个点的大小 symbolSize: function (data) { return data[2]; type: 'scatter' };涟漪气泡图:雷达图雷达图是用于多个单位在不同项目上表现差异的图表,只需要将series的type设置为radar即可。如: option = { title: { text: '基础雷达图' tooltip: {}, legend: { data: ['预算分配(Allocated Budget)', '实际开销(Actual Spending)'] radar: { // shape: 'circle', name: { textStyle: { color: '#fff', backgroundColor: '#999', borderRadius: 3, padding: [3, 5] indicator: [ { name: '销售(sales)', max: 6500}, { name: '管理(Administration)', max: 16000}, { name: '信息技术(Information Techology)', max: 30000}, { name: '客服(Customer Support)', max: 38000}, { name: '研发(Development)', max: 52000}, { name: '市场(Marketing)', max: 25000} series: [{ name: '预算 vs 开销(Budget vs spending)', type: 'radar', // areaStyle: {normal: {}}, data: [ value: [4300, 10000, 28000, 35000, 50000, 19000], name: '预算分配(Allocated Budget)' value: [5000, 14000, 28000, 31000, 42000, 21000], name: '实际开销(Actual Spending)' 常用配置参数雷达图也没有坐标系的概念,所以它有一个radar属性,主要也是它radar属性的参数配置indicator:指示器,用于存放每一个维度的名称和范围。name:每一个指示器名称的配置项,比如你可以在它里面写textStyle,在里面设置文字颜色color、背景色backgroundColor、边框半径borderRadius等等,设置样式的逻辑和CSS中一致。基于雷达图变形的有:多雷达图、卫星雷达图、面积雷达图。漏斗图漏斗图是 用于进行转化率分析的图表,它也没有坐标轴的概念。只需要将series的type设置为funnel即可。如:option = { title: { text: '漏斗图', subtext: '纯属虚构' tooltip: { trigger: 'item', formatter: "{a} <br/>{b} : {c}%" toolbox: { feature: { dataView: {readOnly: false}, restore: {}, saveAsImage: {} legend: { data: ['展现','点击','访问','咨询','订单'] series: [ name:'漏斗图', type:'funnel', left: '10%', top: 60, //x2: 80, bottom: 60, width: '80%', // height: {totalHeight} - y - y2, min: 0, max: 100, minSize: '0%', maxSize: '100%', sort: 'descending', gap: 2, label: { show: true, position: 'inside' labelLine: { length: 10, lineStyle: { width: 1, type: 'solid' itemStyle: { borderColor: '#fff', borderWidth: 1 emphasis: { label: { fontSize: 20 data: [ {value: 60, name: '访问'}, {value: 40, name: '咨询'}, {value: 20, name: '订单'}, {value: 80, name: '点击'}, {value: 100, name: '展现'} };常用配置参数left:漏斗的左边距,也就是漏斗距离左边的边距top:漏斗的上边距,也就是漏斗距离顶部的边距bottom:漏斗的下边距,也就是漏斗距离底部的边距width:漏斗的显示宽度min:漏斗的最小值max:漏斗的最大值minSize:漏斗的最小宽度maxSize:漏斗的最大宽度sort:漏斗数据的排序方式,降序 descending、升序 ascendinggap:每层漏斗之间的间距label:每层漏斗显示的名称,show为true时就会显示,名称显示的位置,一般都设置显示在每层漏斗的内部 inside,还可以设置 left、right、top、bottom、center、middle。itemStyle:每层漏斗的样式,如每层漏斗边框的颜色、每层漏斗边框的宽度emphasis:高亮时每层漏斗的样式,如 label的fontSize 名称的字体大小。仪表盘仪表盘是 用于表示事件进度的图表,只需要将series的type设置为gauge即可。如:option = { tooltip: { formatter: '{a} <br/>{b} : {c}%' series: [{ name: 'Pressure', type: 'gauge', progress: { show: true detail: { valueAnimation: true, formatter: '{value}' data: [{ value: 50, name: 'SCORE' };常用配置参数progress:进度,将它的show设置为true就可以看到进度条。detail:仪表盘的详情数据,将它的valueAnimation设置为true,就能看到数值跳动的动画效果。通过设置它的formatter可以对要显示的数值进行过滤、格式化的操作。基于仪表盘的变形非常的多,比如 时钟、得分环、多仪表板等等箱线图(盒须图)箱线图是用于表示连续性数据分布情况的图表,比如它的呈现效果的逻辑很像是折线图、柱状图这类带趋势和频数的图表。但是数据逻辑并不是,它的原始数据是二维的,一维数据的每一项会作为x轴上离散的类别,那么一维度数据中每一项中的数据会被组合成上 四位分数、上下限、异常值等。将它的series的type属性设置为boxplot即可。如:var getdata = [ [34,54,67,75,86], [10,25,45,67,73], [24,34,45,55,66], [32,45,50,60,70] var getname=['机电工程学院','自动化学院','计算机学院','轻工化工学院']; option = { tooltip: { trigger: 'item', axisPointer: { type: 'shadow' grid: { left: '50', right: '20', top:'40', bottom:'40', xAxis: { type: 'category', data: getname, axisLabel: { color: '#777777', textStyle: { fontSize: '13' axisTick: { show: false, axisLine: { lineStyle: { color: '#333333', splitLine: { show: false yAxis: { type: 'value', name:'分数(分)', nameTextStyle: { color: '#777777', fontSize: 13, padding:[0,0,0,60] axisLabel: { color: '#777777', textStyle: { fontSize: '13' axisTick: { show: false, axisLine: { lineStyle: { color: '#333333', splitLine: { lineStyle: { color: '#D1D1D1', series: [{ name: 'boxplot', type: 'boxplot', data: getdata, itemStyle: { normal: { borderColor: '#4B96F3', borderWidth: 2, color: '#D9EAFF', tooltip: { formatter: function(param) { return [ 'Upper: ' + param.data[5] + ' 分', 'Q3: ' + param.data[4] + ' 分', 'Median: ' + param.data[3] + ' 分', 'Q1: ' + param.data[2] + ' 分', 'Lower: ' + param.data[1] + ' 分' ].join('<br/>') name: '异常', type: 'scatter', symbolSize: 10, data: [ [0, 17, "异常"], itemStyle: { normal: { color: 'rgba(75,150,243,.7)', label: { show: false, name: '异常', type: 'scatter', symbolSize: 10, data: [ [3, 75, "异常"], itemStyle: { normal: { color: 'rgba(75,150,243,.7)', label: { show: false, };常用配置参数tooltip:工具提示,不是全局的那个tooltip,只是该箱线图的tooltip,用于格式化某一个箱线图的悬浮提示。基于箱线图(盒须图)的变形有 多系列箱线图、垂直箱线图、水平箱线图等。热力图热力图是密度型的图表,散点图、气泡图也属于密度型图表,所以热力图的数据,散点图或气泡图也能使用,所以热力图的数据是三维的。但热力图是使用不同的颜色以及深浅颜色来表示数据的差异,而气泡图是通过点的大小来表示数据的差异的。将它的series的type属性设置为heatmap即可。如:var hours = [0,1,2,3,4,5,6,7,8,9]; var ydays1 = ['0', '1万', '10万', '50万', '100万', '500万', '1000万', '5000万', '1亿', '10亿以上'].reverse(); var ydays2 = ['1万', '10万', '50万', '100万', '500万', '1000万', '5000万', '1亿', '10亿', ' '].reverse(); var data = [] for(let i = 0;i<10;i++) { for (let j = 0;j<10;j++) { data.push([i,j,parseInt(Math.random() * 10)]) option = { title: { text: 'xx规模分布图', left: '8%', align: 'right' tooltip: { position: 'top' animation: false, grid: { width: 650, height: 500, top: '10%' xAxis: { show: false, type: 'category', data: hours, splitArea: { show: true yAxis: [{ name: '单位(元)', type: 'category', data: ydays1, splitArea: { show: false axisTick: { show: false axisLine: { show: false splitLine: { show: false type: 'category', data: ydays2, splitArea: { show: false axisTick: { show: false axisLine: { show: false splitLine: { show: false visualMap: [{ type: 'piecewise', splitNumber: 6, showLabel: false, pieces: [ {gt: 9, color: '#F06326', symbol: 'rect'}, {gt: 8, lt: 9, color: '#F06326', symbol: 'rect'}, {gt: 7, lt: 8, color: '#F06326', symbol: 'rect'}, {gt: 6, lt: 7, color: '#F48A5D', symbol: 'rect'}, {gt: 5, lt: 6, color: '#F48A5D', symbol: 'rect'}, {gt: 4, lt: 5, color: '#FFB366', symbol: 'rect'}, {gt: 3, lt: 4, color: '#FFB366', symbol: 'rect'}, {gt: 2, lt: 3, color: '#FFCF9F', symbol: 'rect'}, {gt: 1, lt: 2, color: '#FFE7CF', symbol: 'rect'}, {lt: 1, color: '#FFF4EB'}, itemWidth: 16, itemHeight: 12, itemGap: 0, text: ['1000','0'], inverse: true, min: 0, max: 10, seriesIndex: [0], orient: 'horizontal', left: '5%', bottom: '5%' legend: { data: [{ name: '当前项目预算所在区间', icon: 'circle', orient: 'horizontal', seriesIndex: [1], right: '30%', bottom: '5%', itemWidth: 15, itemHeight: 15, itemGap: 30, textStyle: { color: '#', fontSize: 16, series: [{ name: '类似项目 Punch Card', type: 'heatmap', data: data, label: { show: false itemStyle: { borderWidth: '2', borderColor: '#fff' zlevel: 2, emphasis: { itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0, 0, 0, 0.5)' name: '当前项目预算所在区间', type: 'scatter', color: '#2CB7CA', zlevel: 3, symbolSize: function (val) { return 15; data: [ [1,6,4] };常配合visualMap一起使用。基于热力图变形的有:热力地图、日历图等。旭日图旭日图是进化版的饼图,它不仅可以展示数据各项比重,还可以体现各项数据的层级关系。将它的series的type属性设置为sunburst即可。如:var data = [{ name: 'Grandpa', children: [{ name: 'Uncle Leo', value: 15, children: [{ name: 'Cousin Jack', value: 2 name: 'Cousin Mary', value: 5, children: [{ name: 'Jackson', value: 2 name: 'Cousin Ben', value: 4 name: 'Father', value: 10, children: [{ name: 'Me', value: 5 name: 'Brother Peter', value: 1 name: 'Nancy', children: [{ name: 'Uncle Nike', children: [{ name: 'Cousin Betty', value: 1 name: 'Cousin Jenny', value: 2 option = { series: { type: 'sunburst', // emphasis: { // focus: 'ancestor' // }, data: data, radius: [0, '90%'], label: { rotate: 'radial' };常用配置参数旭日图的data是一棵树哟,每一层基本都是由name、value、children构成,通过children来指示层级。旭日图配上emphasis的focus非常香。旭日图的变形有:圆角旭日图、单色日爆图等。桑基图桑基图是用于表示能量分流的图表,也叫做桑基能量平衡图,每一个延伸的分支宽度代表了能量的大小,主要的特征是首末端的能量相等。将它的series的type设置为sankey即可。它的数据格式很像是关系图,data就像是节点,links就像边,不过边有权重,权重会变成边的宽度。如:option = { series: { type: 'sankey', layout: 'none', emphasis: { focus: 'adjacency' // 节点 data: [{ name: 'a' name: 'b' name: 'a1' name: 'a2' name: 'b1' name: 'c' // 关系 links: [{ source: 'a', target: 'a1', value: 5 source: 'a', target: 'a2', value: 3 source: 'b', target: 'b1', value: 8 source: 'a', target: 'b1', value: 3 source: 'b1', target: 'a1', value: 1 source: 'b1', target: 'c', value: 2 };桑基图默认是很朴素的,但是在官网和民间画廊中的桑基图都比较炫酷。词云图词云图是将文本的权重(重要程度)或频数数值作为文本的大小来呈现的文字图表,图表中全都是文字。只需要将series的type设置为wordCloud即可。如:let datas = [ {name: "雨伞", value: 30}, {name: "晴天", value: 28}, {name: "电话", value: 24}, {name: "手机", value: 23}, {name: "下雨", value: 22}, {name: "暴雨", value: 21}, {name: "多云", value: 20}, {name: "雨衣", value: 29}, {name: "屋檐", value: 28}, {name: "大风", value: 27}, {name: "台风", value: 26}, {name: "下雪", value: 25}, {name: "打雷", value: 24}, {name: "小雨", value: 30}, {name: "中雨", value: 18}, {name: "大雨", value: 14}, {name: "雷阵雨", value: 13}, {name: "下雪", value: 12}, {name: "小雪", value: 11}, {name: "中雪", value: 10}, {name: "大雪", value: 9}, {name: "暴雪", value: 8}, {name: "东风", value: 7}, {name: "南风", value: 6}, {name: "西北风", value: 5}, {name: "北风", value: 4}, {name: "闪电", value: 3} option = { tooltip: { show: true, position: 'top', textStyle: { fontSize: 30 series: [{ type: "wordCloud", // 网格大小,各项之间间距 gridSize: 20, // 形状 circle 圆,cardioid 心, diamond 菱形, // triangle-forward 、triangle 三角,star五角星 shape: 'circle', // 字体大小范围 sizeRange: [20, 50], // 文字旋转角度范围 rotationRange: [0, 90], // 旋转步值 rotationStep: 90, // 自定义图形 // maskImage: maskImage, left: 'center', top: 'center', right: null, bottom: null, // 画布宽 width: '50%', // 画布高 height: '50%', // 是否渲染超出画布的文字 drawOutOfBound: false, textStyle: { normal: { color: function() { return 'rgb(' + [ Math.round(Math.random() * 200 + 55), Math.round(Math.random() * 200 + 55), Math.round(Math.random() * 200 + 55) ].join(',') + ')'; emphasis: { shadowBlur: 10, shadowColor: '#2ac' data: datas };词云图搭配关键字排名,是比较炫酷的可视化效果。树图树图 是常见的突出层级关系的图表,它可以包含大量的层级关系,父子关系、兄弟姊妹关系。只需要将它的series的type设置为tree即可。树图的数据结构层次很像是旭日图哟,也正如前面所说的,旭日图的data是一棵树嘞。如:const data = { name: 'flare' data.children = [{ "name": "flex", "children": [{ "name": "FlareVis", "value": 4116 "name": "display", "children": [{ "name": "DirtySprite", "value": 8833 "name": "LineSprite", "value": 1732 "name": "RectSprite", "value": 3623 "name": "TextSprite", "value": 10066 data.children.forEach(function(datum, index) { index % 2 === 0 && (datum.collapsed = true); option = { tooltip: { trigger: 'item', triggerOn: 'mousemove' series: [{ type: 'tree', data: [data], top: '1%', left: '7%', bottom: '1%', right: '20%', symbolSize: 7, label: { position: 'left', verticalAlign: 'middle', align: 'right', fontSize: 9 leaves: { label: { position: 'right', verticalAlign: 'middle', align: 'left' emphasis: { focus: 'descendant' expandAndCollapse: true, animationDuration: 550, animationDurationUpdate: 750 }树图的应用很常见,但Echarts的树图很普通,没见过什么好看的树图。但是矩形树图就很哇塞。矩形树图矩形树图是用于表示数据层级关系和数据的权重关系的图表,它是通过嵌套关系以及矩形大小来。它虽然叫矩形树图,但是展示上并不像树哟,只是数据结构的格式像树,作用和旭日图蛮像的。使用它的话只需要将series中的type设置为treemap。如:option = { series: [{ type: 'treemap', data: [{ name: 'nodeA', // First tree value: 10, children: [{ name: 'nodeAa', // First leaf of first tree value: 4 name: 'nodeAb', // Second leaf of first tree value: 6 name: 'nodeB', // Second tree value: 20, children: [{ name: 'nodeBa', // Son of first tree value: 20, children: [{ name: 'nodeBa1', // Granson of first tree value: 20 };矩形树图是一个比较有意思的图,我比较喜欢,比如你可以在官网示例中看到Echarts中看到所有配置项组成的矩形图关系图关系图是用于表示关系网络的图表,通常由两部分组成,节点和边,节点表示某类实体,实体与实体之间会用边来进行关联,关联之后就表示两个实体之间具有某种关系。使用它的话只需要将series的type设置为treemap即可。如:option = { title: { text: 'Graph 简单示例' tooltip: {}, animationDurationUpdate: 1500, animationEasingUpdate: 'quinticInOut', series: [ type: 'graph', layout: 'none', symbolSize: 50, roam: true, label: { show: true edgeSymbol: ['circle', 'arrow'], edgeSymbolSize: [4, 10], edgeLabel: { fontSize: 20 data: [{ name: '节点1', x: 300, y: 300 name: '节点2', x: 800, y: 300 name: '节点3', x: 550, y: 100 name: '节点4', x: 550, y: 500 // links: [], links: [{ source: 0, target: 1, symbolSize: [5, 20], label: { show: true lineStyle: { width: 5, curveness: 0.2 source: '节点2', target: '节点1', label: { show: true lineStyle: { curveness: 0.2 source: '节点1', target: '节点3' source: '节点2', target: '节点3' source: '节点2', target: '节点4' source: '节点1', target: '节点4' lineStyle: { opacity: 0.9, width: 2, curveness: 0 };关系图很有意思哟,它多用于数据分析领域,并且它也支持可编辑功能,常见的流程图、类图、时序图也可以用它来实现哟。Echart中的色彩搭配echarts中的色彩搭配是一件很有意思的事情,有美感的人可以搭配出各种各样的图表皮肤,当然官方也提供了六套普通的皮肤哟,年代感 vintage、暗黑 drak、麦卡龙 macarons、信息图表 infographic、闪耀 shine、罗马 roma 风格,除了这些风格外,同时我们也可以自己去定制搭配。色彩主题的制定比较讲究哟,毕竟每个人的对场景的理解和审美都不太一样,用错场景的色彩主题,也许效果适得其反。可以多看看不同场景下的可视化色彩主题风格,这样你就有不少对可视化色彩主题风格的理解与感悟了。定制搭配在定制搭配界面,官方又给了我们十种默认的搭配方案哟,搭配的界面分为两个部分,分别是功能区和预览区。在功能区中可以调节可视化图表中系列的个数、也能设置echarts中许多配置,比如 图例、工具箱、提示框等等。操作和使用通过在功能区去调节每一个配置项的数据,就能在右侧预览区看到相应的效果了。配置完毕后,有两种方式获取你配置的主题。下载主题JSON:一个配置文件,需要你编写注册这套皮肤的代码。注册皮肤的原理是,echarts默认配置与这套皮肤的配置进行合并,合并后的配置就是你最终的echarts配置。下载主题的js文件:这是一个带有注册这套皮肤代码的umd模块化的js文件,可以直接使用,原理同导出主题JSON一致。导入和导出主题编辑器的操作配置注意哟,导出的配置并不能直接在Echarts中使用,只能在Echarts主题编辑器中使用,使用的方式就是导入配置。这个配置相当于保存了之前在这个主题编辑器中的操作数据,你可以反复导入导出,来多次编辑你自己定义的主题,让主题中的色彩搭配更加漂亮。不同场景下的Dashboard 仪表板可视化图表一般用于制作Dashboard(商业智能仪表盘),它也可以叫做商业智能仪表板。主要用于呈现数据可视化效果,通过可视化的方式为企业和用户度量信息和关键业务指标,都是有一些关键指标和多张可视化图表构成。 比如DevOps(研发流程一体化)的仪表板,包括 折线图、柱状图、雷达图、环形饼图等。我们可以很直观的看到一些工程数的信息,包括接入项目数、接入流水线数、接入工程数、以及构建频率趋势和部署频率随时间变化变化情况等。 Dashboard中的信息可以帮助企业和用户了解他们所关注的内容,辅助他们做出决策。比如电商销售情况的仪表板、车联网情况的仪表板、股票交易情况的仪表板等等。DevOps(研发流程一体化)的仪表板一个仪表板的设计,需要保证可用性、易用性、兼容性等。先考虑用户是否满足了需求,然后考虑用户是否能快速上手,最后考虑用户使用这个仪表板时是否流畅、是否能在多台设备上使用。复杂场景下的可视化图表一个复杂的可视化图表,一般都能拆分为三部分:数据准备部分、固定显示部分、动态数据显示部分。数据准备部分:将数据过滤成该可视化图表支持的那种格式固定显示部分:也就是可视化图表中固定的配置,这些配置带来的效果就是固定显示部分,比如坐标轴、图例、背景色、颜色、宽高、位置、悬浮提示、虚拟地图等等动态数据显示部分:这部分数据是动态获取的,最终也会需要经过过滤,变成可视化图表支持的数据格式。复杂场景下的可视化图表有时会使用到一些Echarts高级用法和快速开发工具,比如富文本、响应式自适应、事件行为、三维可视化、基于Echarts封装的更上一层的应用级框架V-charts等等等,这个我会尽快在下一篇Echarts类的文章中对它进行介绍。
前端服务开发(2) | 学习笔记
开发者学堂课程【基于STM32的端到端物联网全栈开发:前端服务开发(2)】学习笔记,与课程紧密联系,让用户快速学习知识。课程地址:https://developer.aliyun.com/learning/course/574/detail/7950前端服务开发(2)内容介绍:一、初始化前端项目二、使用 Layout 组件三、主页面四、卡片组件的使用五、网络请求六、dva 管理数据七、dva 数据流转项目下载八、项目打包九、项目部署十、总结十一、课程场景概览内容简介:本节将开始初始化前端项目,组件的使用和数据流转,部署。本小节将介绍前端项目的初始化,主要以操作为主,使用 umi 提供脚手架进行。一、初始化前端项目使用 umi 脚手架初始化搭建的项目页面如图所示。1、软件环境安装在正式开始之前,需要系统已经安装以下软件环境:(1)Node.jsNode.js 是一个高性能的 JavaScript 运行环境,基于强大的 Chrome V8引擎打造,安装地址:https://nodejs.org/zh-cn/(2)yarnyarn 是流行的包管理工具,Yarn 会缓存它下载的每个包,所以无需重复下载。它还能并行化操作以最大化资源利用率,安装速度之快前所未有。安装地址:https://yarnpkg.com/zh-Hans/docs/install#windows-stable 或: npm install yarn tyarn -g(3)umiUmi 是一款基于路由的前端开发框架,目前为蚂蚁金服底层前端框架。安装方式:命令行执行:yarn global add umi2、新建项目文件夹(1)Linux&macOS在保存项目的文件夹下采用 terminal 执行:mkdir frontendproj、cd frontendproj。(2)Windows在保存项目的文件夹下右键新建文件夹 frontendproj,进入文件夹,在文件通过 powershell 进入 shell 终端。3、在项目文件夹内命令行执行: yarn create umi脚手架工具将会自动安装必备的软件包依赖,通过按钮选择引用类型为 app->不使用 TypeScript (n) -> 选中 antd 和dva 功能块,选择结束后,umi 将通过脚手架生成项目模版。4、在项目文件夹内命令行执行: yarn 安装依赖yarn 命令会依照 package.json 中定义的依赖包内容自动安装依赖到文件夹下 node_ modules 子文件夹。mock/     // mock 文件所在目录,基于 expressnode_ modules/   //依赖包安装文件夹src/    //源码目录,可选Tayouts   //全局布局   开发中 主要编辑的文件项目初始化目录结构Models   // dva models 文件存放目录,存放 global 的 modelspages/   //页面目录,里面的文件即路由.umi/    // dev 临时目录,需添加到.gitignoreindex.js   // HTML 模板index.css   // 404页面global.css   //约定的全局样式文件,自动引入,也可以用 global. lessapp.js   //可以在这里加入 polyfill. umirc.js   // umi 配置,同 config/config.js,二选一. Env      //环境变量package.json5、在项目文件夹内命令行执行: yarn start 启动项目webpack 会自动编译当前项目文件,并通过本地网络地址8000端口开放访问,访问到以下页面即通过脚手架工具初始化成功。脚手架,可以理解为‘“项目模版”会帮助搭建一个完整的前端应用。访问 localhost:80006、修改组件内容和 Layoutpages/index.js 文件即为首页小丑页面组件,layout/index.js 即为页面布局(layout) 组件,分别修改其中内容观察组件内容变化。​<Layout>​​{this.props.children}​​</Layout>​umi 将 Layout 组件嵌套在 content 上,访问 content 下路由 Layout+contentLayout 组件通过 this.props.数据属性获取子组件的内容。当用户访问 content 的页面下的路由时,umi 会将 Layout组件嵌套在 content 上,并在浏览器中呈现出来。7、添加新的页面Umi 采用约定式路由,即会通过 pages 下新增的文件名或文件夹名称创建组件的路由,在 pages 下新建 test.js 或test/index.js,写入组件内容后保存,修改浏览器路由即可访问新的组件内容。​import React from 'react' ;​​export default class Test extends React. Component {​​render() {​​return (​​<div>​​<h1>Test</h1>​​</div>​​);​​}​​}​浏览器访问 localhost:8000/test接下来的内容将对 layout 组件进行改善,对主页页面进行拆解和组件引入,使脚手架的初始化项目更加贴近项目原型。二、使用 Layout 组件1、Layout 组件Ant Design 提供了丰富的 layout 组件以及示例代码参考: https://ant.design/components/layout-cn/,可以通过 Ant Design 官网轻松获取组件的使用说明以及示例代码。2、Layout 组件引入修改项目 layout/index.js 组件内容,引入 Antd 提供的 layout 组件内容,保存后新的 layout 内容会自动替换。例如引入 Antd 提供的 layout 页面,将页面分为 header 导航栏、content 内容区以及 footer 底栏,通过及其结论和代码即可轻松实现。代码如下:​import React from 'react';​​import " antd/dist/antd.css';​​Import {Layout }from " antd';​​const {Header, Footer, Content }= Layout;​​class BasicLayout extends React. Component {​​render() {​​return (​​<div>​​<Layout>​​<Header >​​header​​</Header>​​<Content>​​{this , props children}​​</Content>​​<Footer style={{textAtign: ​​‘​​center​​’​​, backgroundcolor: #001529 }}>​​<div style={{ color: "white" }}>​​footer​​</div>​​</Footer>​​</Layout>​​</div>​​);​​}​​}​​export default BasicLayout ;​3、完善组件内容根据设计稿,需要在 header 添加 logo 内容以及设备选择下拉框,前端需要通过用户选择的 devicename 参数向后端进行数据请求,下拉框使用案例上提供的 select 组件,修改 BasicLayout 内容为下:​import React from 'react' ;​​import " antd/dist/antd.css' ;​​import { Layout, Select} from ​​‘​​ antd​​’​​;​​const Option =Select .Option;​​const { Header, Footer, Content }= Layout ;​​class BasicLayout extends React .Component {​​render() {​//Select 组件的选择回调函数​const onSelect =(value) => {​​console.log("​​选中​​: " + value);​​}​​Return(​​<div>​​<Layout>​​<Header style={{display:​​’​​faflex', justifyContent: " space-between' }}>​​<div style={{ color: "white", fontSize: '24px', fontWeight: "bold', float: 'left' }}​​>阿里云 Iot | ST 只能家具教程 </div>​<div style={{ display: 'flex', justifyContent: " space- between', alignItems: 'center', color: 'white' }}>​请选择设备​devicename​:​ ​ ​<Select style={{ width: '200px', float: 'right' }}​​Placeholder=*​​ 当前返回无设备 "//当默认没有 Option 或者为选中 option (注释 defaultValue)时显示onChange=(onSelect )//select 组件的选择回调,输出选中 option 的 value 属性,参见 select 组件 API 文档defaultValue={"默认设备[待替换]") //默认选中的选项,应填充设备这部分是使用 Antd Select 下拉框组件​<0ption value={" devicel"}>device1</0ption>​​<option value={"device2" }>device2</Option>​​<0ption value={"device3*}>device3</Option>​​</select>​​</div>​​</Header>​​​<Content>​​{this . props . children}​​</Content>​​layout​​Content​​​<Footer style={{ textAlign: ​​‘​​ center' , backqroundColor: ​​’​​#001529' }}>​​<div style={{ color: "white"}}>​​Powered By abits .cn​​</div>​​</Footer>​​</Layout>​​</div>​​};​​}​​export detault BasicLayout ;​4、完善组件内容保存后,layou 更新的内容有 layout 的 content,使用 Antd Select 下拉框组件。5、待完善的内容至此完成了 header 内容的完善,但是还需要完善的如下:(1)组件联动在 Header 中加入设备选择后,BasicLayout 的作用不仅仅是给页面提供样式嵌套,还需要通过 deviceName 的选择动态更新 Content 中包裹的设备数据的相应变化,实现多个设备数据在页面上有序的切换。(2)数据请求在 Select 组件的 defaultValue={“默认设备[待替换]"}属性中,组件应提前向后端服务请求设备的列表,将列表的第一个设备 deviceName 填充进 defaultValue 属性,当 Select 选择新的 deviceName 时,需要完成新的 deviceName下的设备数据请求,替换已有下拉框内容。组件的数据部分介绍将在下一节 dva 数据流内容进行讲解。三、主页面1、页面拆解根据设计稿,在主页面中需要展示设备当前状态和设备历史状态(历史温湿度曲线、温度报警表格)两个块,为了使得项目代码清晰易懂可维护,使用 React 的组件化实现复杂页面的组件化拆解,拆解出来的组件作为一个独立文件,通过组件组合出目标页面。通过拆解页面可以更大程度上使得各个组件结构将复杂的页面内容以搭积木的方式组合起来,是常用的前端工程实践方法。card 组件在拆解后,在主页面需要确定各子组件的布局及位置,各组件位置可以通过字符进行占位。使用 Ant Design[Card 组件]快速实现主页面的框架。[Card 组件]提供内容块隔离的样式,可通过配置 Card.Grid 快速实现,免去写复杂的 CSS样式。[Card 组件]同时提供可切换的 Tab 页,通过 TabList AP| 即可快速实现 Tab 页的切换,同时也和 Card.Grid 保持风格上的一致。根据拆解划分的组建内容,修改项目的目录如下:mock/  // mock 文件所在目录,基于 expressnode_ modules///依赖包安装文件夹src///源码目录,可选 开发中主要编辑的文件layouts//全局布局models// dva models 文件存放目录,存放 globaL 的 modelspages///页面目录,里面的文件即路由.umi/// dev 临时目录,需添加到.gitignore 主页拆解 UI 组件main///主页组件目录index.js// main 主页组件component///存放子组件的目录 四张卡片组件Card///存放设备实时状态的四个卡片AlarmStatus.js // 报警状态HumiStatus.js//湿度状态TempStatus.js//温度状态OnlineStatus.js //在线状态 曲线图和报警表格组件Chart.js//曲线图组件Table.js // 表格组件index.js// umi默认欢迎页index.css//欢迎页 css 文件global.css//约定的全局样式文件,自动引入,也可以用 global. lessapp.js//可以在这里加入 polyfill.umirc.js// umi配置,同 config/config.js, 二选一.env//环境变量package.json2、框架搭建(1)同时修改 main 文件夹下的/index.js 文件内容为以下:其中1位4张卡片的内容由 card.grid 进行布局,二为曲线图以及报警表格的组件使用 const tablist 的 API 进行布局。​import React from 'react'​​import ' antd/dist/antd.css';​​import { Card, Icon, Tooltip } from 'antd';​​export default class Main extends React . Component{​​constructor(props) {​​super(props);​//记录当前选中的 Tab​this.state = {​​key: 'tab1',​​}​​}​//Tab 之祠切換​onTabChange = (key, type) => {​​this.setState({ ltype]: key });​​}​​render() {​//显示设备历史状态的 TabList曲线图和报警表格组件​const tabList = {​​key: 'tab1',​​tab: (​​<Tooltip title="​设置历史温湿度数据查询">设备历史状态​</Tooltip>​​}​​,​​{​​key: 'tab2',​​tab:​​<Tooltip title="​设置历史温湿度数据查询">设备历史状态​</Tooltip>​​),​​const contentList ={​​tab1: (​​<div>​这里是历史温湿度曲线图组件​</div>),​​tab2: (​​<div>​这里是温湿度历史报警组件​</div>),​​};​//第一 Row 的 Style 内容​const gridStyle = {​​width: '25%'​​,​​height: ' 200px'​​,​​textAlign: ' center',​​padding: ' 10px'​​};​​return (​​<div style={{backgroundRepeat: 'no-repeat'​​,​​backgroundSize:'100% 100%'}}>​​<div style={{ padding: '38px​​°​​ }}>​​<Card​​title={​​<div>​​<Tooltip title=" ​显示设备当前的运行状态">设备当前状态​</Tooltip>​​</div>}​​extra={​​<div>​​<Icon type="reload" style={{ paddingRight: '5px' }} />​​<Tooltip style={{ fontSize: '12px' ​​》​​} title=​​"设备最新一次上报属性的时间">最新一次上报时间:这里待填充设备最新一次上报时间​</Tooltip>​​</div>​​}​​style={{ width: '100%​​’​​ }​​> ​四张卡片组件​<Card.Grid style-(gridStyle)>​<div>这里是在现状态卡片< </div>​</Card. Grid>​​<Card.Grid style={gridStyle}>​<div>这里是报警状态卡片</div>​</Card. Grid>​​<Card.Grid style={gridStyle}>​<div>这里是温度卡片</div>​</Card.Grid>​​<Card.Grid style={gridStyle}>​<div>这里是温度卡片</div>​</Card. Grid>​​</Card>​​</div>​​<div style={{padding: '30px', paddingTop: 0 }}>​​<Card​​style={{ width: '100%' }}​​tablist={tabList}​​activeTabKey={this. state. Key}​​onTabChange={(key) => {this. onTabChange(key, 'key'); }}​​> ​​{contentList [this.state. key]}​​</Card>​​</div>​​</div>​​)​​}​​}​保存修改 main/index.js 后,访问浏览器 localhost:8000/main,页面输出,至此以初步搭建好主页的页面框架。(2)在 main 组件中,使用了 Ant Design 提供的两个组件,[Icon 图标] 和 [Tooltip 文字提示]。Tooltip 组件实现简单的文字提示起泡框,通过<Tooltip>组件包裹目标组件即可,通过 Tooltip 的 titleAPI 进行提示文字设定。Icon 是 Ant Desgin 提供的语义化矢量图形组件,可以通过配置 Icon 的 Type 属性快速集成 AntDesign 提供的图标库中的图标。首页在 Card 组件中,使用卡片组件搭建起了页面的基本骨架,要实现此组件之间的解耦,还需要将子组件引入进来,例如修改 OnlineStatusCard 组件内容为以下,并插入到 main/index.js 主页面的 Card.Grid 位置。同理引入其它组件。项目参考代码:仓库/exercis1, 执行 yarn 安装依赖四、卡片组件的使用1、card 卡片组件(1)Onlinestatuscard组件需要使用请求到的设备在线状态数据,显示设备不同的在线状态。OnlineStatusCard 数据在线状态:state最近一次上线时间: recentOnlineTime由于目前还没有使用 dva,先用组件内的假数据进行 online 展示。OnlineStatusCard 样式通过 Antd 提供 Icon,判断在线状态,显示不同的图标。美观度主要依靠 CsS 功底。在橙色内通过判断在线状态与否,分别渲染组件的内容,将对组件的在离线状态进行判断,并展示不同图标样式,意味着美观度需要具备一定的css功底。(2)AlarmStatusCard报警组件需要显示设备的温度报警状态,以及最近报警时间,在报警后,需要点击图标下发报警解除(向后端进行请求)。OnlineStatusCard 数据,报警状态: state,最近报警时间: recentAlarmTime,采用虚假的数据进行暂时的表示。报警组件的完整内容如下,针对设备报警与否分别展示不同的内容,具体表现在设备发生报警后 icon 为叹号并进行旋转,如设备出现报警,则按 onclick 的方法可以进行调用。用户可以点击图标清除报警,前端通过 clearalarm API 调用后端下发消息给设备。​import React from " react'​​import ​​‘​​antd/dist/antd.css';​​import {Icon, Divider, Tooltip} from 'antd';​​const alarmcard ={​​state:false,​​recentAlarmTime: '2019-01-0100:00:01 ‘​​}​​export default class AlarmStatusCard extends React.Component{​​render() {​//清除警报点击事件回调​const clearAlarm = () => console. log​("点击了清除警报");​return (​​<div style={{display: 'flex', alignItems: 'center', paddingTop: '5%', justifyCont​​<div style={{​​position: " relative',​​border: 'lpx solid #e8e8e8’ ,​​borderRadius: "4px' ,​​height: "100%", width: "100%",​​}}>​​<div style={{​​position:" absolute', top: -20, left: 40,​​height: '100px', width: " 100px' ,​​backgroundColor: " #e25858' ,​​borderRadius: '4px',​​boxShadow: '0 0 10px grey',​​display: 'flex', alignItems: 'center', justifyContent: " center' ,​​}}​​alarmCard.state ===true ? (​//发生报警​<Tooltip title="​点击按钮即可可清除警报​*>​​<Icon​​onCl ick={elearAlarm}​​ //点击后的回调函数​Spin={true}​  //转动​type={"excl amation-circle"}​​Style={{ color: 'white', fontSize: '60px' }}​​/>​​</Tooltip>​​)​​:(​​<Icon​​Type= {"check-cirele"}​​Style={{color: 'white', fontsize: '60px'}}​​/>​​)​​}​​</div>​​<div style={{ textAlign: ' right', paddingRight: " 50px" }}>​​<div style={{ fontSize: '16px', marginTop: "0px' }}>​设备报警状态 </div>​ ​​anbsp;​​<div style={{fontSize: '30px', fontWeight: 'bold' }}>{alarmCard.state===true ?​ *报警" : "正常​*}</div>​​</div>​​ ​​ ​​<Divider​​Style={{​​marginTop: 0​​marginBottom: 0,​​fontsize: "12px",​​padding: '10px Spx 0 Spx'​​}}​>最近报警时间​</Divider>​​<div style={{ fontsize: " 12px" }}>{alarmCard. recentAlarmTime ? alarmCard. recentAlarmTime . "​​暂未报警​​ " )</div>​​</div>​​</div>​​);​​}​​}​最终确认出来的设备正常以及报警状态,组件样式,温度卡片以及深度卡片的内容则较为简单,请参考示例提供的代码。正常:​<icon​​type={ " check-circle"}​​style={{ color:'white', fontSize: ' 60px' }}​​/>​报警:​<Tooltip title=" ​点告按鈕即可清除警扱">​<Icon​​onClick={clearAlarm} ​​//点击后的回调函数​spin={true}​ //转动)​type={"exc' lamation-circle"}​​style={{ color: 'white', fontSize: ' 60px' }}​​/>​​</Tooltip>​2、温湿度卡片温湿度卡片较为简单,请参考课程提供的源码。​README . md​​mock​​package. json​​src​​assets​​yay.jpg ​​axios​​index.js​​tools.js​​global.css​​layouts​​index.css​​index.js​​pages​​index. CSS​​index.js​​main​​components​​Card​​AlarmStatus. js​​Humistatus.jis​​onlinestatus.1 s​​TempStatus.is​​Chart​​index.js​​Table​​index.js​​index.js​​mode' ls​​main.js​​tslint . yml​​webpack. config.js​​yarn-error. log​​yarn. Lock​3、BizCharts 曲线图组件(1)HistoricalChartHistoricalChart 实现历史温湿度数据的展示,通过起止时间请求特定时间段的数据,此外还需要在表格绘制温度报警界限,同时通过输入框提交温度报警阈值。(2)BizChartsBizCharts 是阿里巴巴提供的开源的图表类组件库,是基于 AntV 的「G2」在 React 下的实现,支持丰富的数据图形化表达。温湿度曲线,使用 bizchart 提供的「折线图」绘制,温湿度各绘制一条,采用双坐标轴。由于 umi 默认没有集成 Bizcharts,可以通过yarn 或 npm 命令在项目内安装:yarn add bizcharts 或 npm install --save bizcharts(3)DatePicker「DatePicker 日期选择框」组件是 Antd 中用于选择时间,内部包含 DatePicker、WeekPicker、MonthPicker 等子类,「RangerPicker」是 DatePicker 中选择时间段的组件,通过 API 中 onOk()提供的回调函数,可以获得点击组件弹窗确认(ok)按钮后的起止日期参数:RangerPicker 可以通过 defaultValue 属性设定默认值,通过 ranges 属性可以快速选择时间段,为了方便生成各类时间,我们使用「moment.js」 工具包加快效率,moment.js 提供丰富的各类数据获取接口,方便快速调用,在项目内执行以下命令安装:yarn add moment 或 npm install --save moment(4)Input填写温度报警阈值需要输入框提供输入, Ant Design 提供「InputNumber 数宇输入框」组件进行输入,并对输入变化提供 onChange() API 接口方便实时获取到最新的输入值,提供最大最小值步长限定,提供小数点后6位的输入精度。(5)Button「Button 按钮」组件用于执行用户点击回调后的操作,需要取得时间选择框或者温度阈值输入框中的数值,将数值作为请求参数调用后端服务请求数据,通过提供的 onClick()回调函数 AP| 响应用户的点击。(6)图表数据使用 Bizcharts 图表展示设备的温湿度时序数据,在组件前面加入与 API 文档一致的数据,用 data 标识,此外报警阈值使用 threshold 标识。(7)图标样式Bizcharts 提供了丰富的 demo 源码可供参考学习,缺少双坐标轴的例程,只需要添加一个<Axis>标签用于展示设备湿度属性的坐标轴即可,示例如下(详细参见 exercise2.../component/Chart.js)。时间横轴​<Axis name="gmtCreate" title />​温度竖轴​<Axis​​name=" currentTemperature"​​title​​label={{ formatter: val​​口​​‘${val}°C' }}​湿度竖轴​<Axis​​name="currentHumidity"​​title​​label={{ formatter: val =‘${val}%° }}​提示框​<Tooltip​​// title={"hh"}​​crosshairs={{​​type: "y"​​}}​​/>​温度点​<Geom​​type="line"​​posit ion="gntCreate*currentTemperature"​​size={4}​​// shape={" smooth"}​​color={"#0099ff"}​​/>​湿度点​<Geom​​type="line"​​posit ion="gmtCreate*currentHumidity"​​size={4}​​color={"#33cc33"}​​// shape={"smooth"}​/>温度阀值辅助线​<Line​​top={true} ​​//指定 guide 是否绘制在 canvas 最上层,默认为 false, 即绘制在最下层​start={['min', threshold]}​ //辅助线起始位置,值为原始数据值,支持 callback​end={['max'​,​threshold]}​ //辅助线起始位置,值为原始数据值,支持 callback​lineStyle={{​​stroke:​​‘​​#FF5C68'​​, //线的颜色​lineDash: [0, 2, 2]​, //虚线的设置​lineWidth: 3​ //线的宽度}} //图形样式配置​text={{​​position: 'start',​​ //文本的显示位置​autoRotate: true​, //是否沿线的角度排布,默认为 true​style: { fillk "red" }​,//文本图形样式配置​content​:“温度报警阈值:”​+ threshold + "e​”,//文本的内容​offsetY: -5​ // y 方向的偏移量​}}​​/>​4、Table 表格组件(1)HistoricalAlarmTable设备历史报警数据表格在设计草图中没有完整体现,使用 Ant Design 提供的表格组件即可展现历史报警的列表,通过 RangePicker 组件选择起止时间对报警数据进行查询。(2)Table 组件「Table 表格」组件提供行列的数据展示,并且提供丰富的 AP| 功能,通过 column 属性配置列的样式和内容,通过dataSource 配置数据源,通过 pagination 配置分页大小。(3)「Column」配置Colum 配置项较多,较为简单的配置实现 dataIndex (数据索引)、title (标题显示)属性即可,Table 组件会依据datalndex 中配置的字段解析 dataSource 中的数据,title 为该 datalndex 需要显示的名称,为渲染出的表格的头部信息。在本期例程中,配置的Colum 属性如下:​const columns = [{​​title​​: '设备名',​dataIndex: ' deviceName' ,​​}​​,​​{​​title​​: ' 报警温度值',​dataIndex: ' alarmTemperature' ,​​}​​,​​{​​title​​: '报警时温度阀值' ,​dataIndex: ' tempThreshold',​​}​​,​​{​​title​​: ' 创建时间',​dataIndex: ' gmtCreate '​​}];​(4)「dataSource」配置dateSource 为 Table 的渲染内容,送入 JSONArray 类型的数据即可,JSONArray 的每个单元中的字段与、Column中的 datalndex 字段对应。(5)「pagination」 配置pagination 属性配置与 pagination 组件一致(实际渲染也是 pagination 组件),可通过 pageSize 属性设定每页显示的数据条数。(6)表格组件详细内容详细参考 Table.js 文件。​import React from ' react '​​import ' antd/dist/antd.css';​​import { Table, DatePicker, Button } from 'antd';​​import moment f rom ' moment ' ;​​const { RangePicker } = DatePicker;​​const dateFormat = "MM-DD HH: mm:ss"​​export default class HistoricalAlarmTable extends React. Component {​​constructor(props) {​​super(props);​初始化起止时间​this.state = {​​beginTime: moment().startOf('month'). format('YYYY-MM-DD HH:mm:ss'),​​endTime: moment(). format('YYYY-MM-DD HH:mm:ss'),​​}​​render() {​选择起止时间回调函数​const datePicker0nOk = (dates) => {​​this. setState({​​beginTime: dates [0]. format('YYY-MM-DD H:mm:ss'),​​endTime: dates [1]. format('YYY-M-DD HH:mm:ss'),​​});​​const ranger0nClick = () => console. log("​点击起止时间提交​" );​​return (​​<div>​​<div style=(( paddingBottom: 20 }}>​选择查询时间 起止时间选择与提交​<RangePicker​​defaultValue={ [moment().start0f( 'month'), moment()]}​​ranges={{'​​今天​​': [moment().start0f('day'), moment()], '​​本周​​': [moment().start0f​​on0k={datePicker0n0k}​​format={dateFormat}​​showTime=(true)​​/>​​ ​​<Button type="primary" style={{ display: ' abslute', top: 0 }}onClick={ ranger0nCl​提交​​</Button>​​</div>​​<Table​​columns={columns}​​dataSource={data}​​pagination={{ pageSize: 5}}​​/>​​</div>​​)​​}​​}​到目前为止,完成了 UI 层的开发,阶段参考代码在仓库地址/exercise2文件夹下运行前命令行执行 yarn 安装项目依赖。五、网络请求到目前为止,主要使用物理搭建其项目的UI内容,而数据内容则需要依赖网络请求以及组件数据来进行控制。这节介绍网络请求工具 axios 和数据流框架 dva,1、Axios 请求库「axios」是流行的 HTTP 请求库,可快速集成在项目中,可以通过以下命令快速安装 axios:yarn add axios或npm install --save axiosaxios 请求可以通过 async await 取得最终的请求返回数据。基于 axios 封装 get 和 post 的函数工具,在一级目录下新建 axios 文件夹,新建,tools.js 文件:import axios from ' axios '//通过 async 和 await 进行 promise 对象的处理post 方法​export default {​​async post(url, data) {​​return await axios({​​method: ' post' ,​​url,​​data: data, ​//post 请求参数​timeout: 10000​,​//超时时间})},get 方法a​​sync get(url, data) {​​return await axios({​​method: 'get',​​url,​​params: data, ​​//get 请求时带的参数​timeout: 10000​,​ //超时时间​})​​}​2、Message 弹窗组件Message 是页面全局提示组件,可以用来对于请求异常返回码进行弹窗,提示网络状况。编写函数对请求返回 code码进行判断。判断返回码的函数​const checkStatus = (res) => {​​if (res.status !== 200) {​​message. error​​('请求失败或网络异常,请检查...');​} else {​​if (res.data.code !== 0) {​​message.error(​​'提交失败或服务器异常,请检查...');​}​​}​​}​3、API 封装在 tool.js 中定义了请求的方法,将各请求写入组件将变得难以维护和管理,统一将 API 请求写进 axios/index.js。​import http from ​​‘​​./tools';​​import { message } from 'antd' ; ​​const url = 'ttp:/xxxxx/ap1/v1/device';​​ //请换成你的请求地址,可用请求地址 http://47.110.检查返回码并进行错误弹窗​const checkStatus =(res) =>{​​if (res.status !== 200) {​​Messag.error​​('请求失败或网络异常,请检查..​} else {​​if (res.data.code !== 0) {​​message.error​​('提交失败或服务器异常,请检查...');​}​​}​​}​API 请求封装//查询设备的列表​export const getDeviceList =async ()= > {​//得到原始输出​let res = await http.get(url + '/listDeviceName', null);​​checkStatus(res);​​return res.data;​​}​//查询设备的属性​export const queryDeviceProp = async (params) ”=>{​​let res = await http.get(url + '/queryDeviceProp', params);​​checkStatus(res);​​return res.data;​​}​//查询设备的属性​export const selectChartDataByTime = async (params) = >{​​let res = await http.get(url + '/queryDevicePropHistoryLogs', params);​​checkStatus(res);​​return res .data;​​)​//设置报警阀值​export const setDeviceProperty = async (params) =>{​​let res . await http.get(url + '/setDeviceProperty', params);​​checkStatus(res);​​return res.data​​}​//查询历史报警记录​export const queryAlarmHistoryLogs = async (params) =>{​​let res = await http.get(url + '/queryAlarmHistoryLogs', params);​​checkStatus(res);​​return res.data​​}​//清除报警效果​export const clearAlarm = async (params) =>{​​let res = await http.get(url + '/clearAlarm', params);​​checkStatus(res);​​return res. Data​​}​将本次历程所需要的6套 API 分装成函数以准备进行调用。六、dva 管理数据1、问题:如何在 header 组件内选择不同设备,主页自动请求跳转此设备相关的数据,即如何使一个组件状态可以影响到其他的组件?dva提供了简单易用的解决方案。所有的组件存储在一个状态对象 state 中,各组件通过请求去改变 state。2、在 dva 中有三个核心概念是 state,action 和 view。State 是中心数据仓库,通过组件的 props 连接到组件内部,组件发生操作会 dispatch 一个 action 给 state,从而通知 state 完成改变。随着前端组件增多,功能划分分散,所有的组件维护同一个 state 是非常危险的。Model 是 dva 中 state 的单元,每个前端功能块一个 model 实现解耦。3、Model,state 的单元多个小的 model 的 state 以 namespace 为 key 合成,开发者可以依照业务逻辑,通过namespace 来对 state 进行划分,最终交给 dva 处理model 的常用配置有 namespace. state、reducers、effects :​{​​namespace:' count' ​​,​​state: 0,​​reducers: {​​add(state) { return state + 1 },​​}​​,​​effects: {​​*addAfter1Second(action, { call, put }) {​​yield call(delay, 1000);​​yield put({ type: 'add' });​​},​​}​​,​​}​(1)namespace:当前 Model 的名称。整个应用的 State,由多个小的 Model 的 State 以 namespace 为 key 合成。(2)state:该 Model 当前的状态。数据保存在这里,直接决定了视图层的输出。(3)reducers: Action 处理器,处理同步动作,用来算出最新的 state ( 必须返回一个新的 state)(4)effects:Action 处理器,处理异步动作(进行网络请求的调用,再调用执行 reducers 刷新 state)。4、编辑主页model新建主页 model,新建 main/models/main.js,分为 namespace、state 、reducer 和 effect。(1)引入封装好的 API 函数​import {​​getDeviceList,​​que ryDeviceProp,​​se lectChartDataByTime,​​setDeviceProperty,​​queryAlarmHistoryLogs ,​​clearAlarm​​}from '../../../axios/index' ;​​import ( message ] from ' antd' ​​;(2)将各组件所需数据按照格式初始化在 state//储存 model 的 state状态​state: {​​deviceName: "test_ zhinengjiaju",​​deviceSelectList: ["test_ zhinengjiaju"],​​alarmCard:{​​state: true,​​recentAlarmTime: null​​}​​onlineCard:{​​state: "​​在线​​",​​recent0nlineTime: null​​},​​tempCard: {​​value: 22,​​threshold: 22​​},​​humiCard: {​​value: 43,​​sensor: "DHT11​​温湿度传感器​​"​​},​​updateTime: "​​带初始化​​",​​chart: {​​dataList: [],​​},​​table: {​​data: []​​}​​}​(3)Reducer 为新的 state 合并到旧的函数​reducers: {​//自动合并新的状态到旧的状态​update(state, { payload }) {​​let newState = payload;​​return {​​...state,​​newState​​};​​},​​},​​{​​devicName : "oldDevice",​​deviceData : "oldDeviceData"​​}​​{​​devicName: "newDevice",​​}​​{​​devicName: "newDevice",​​}​​{​​deviceData : "oldDeviceData"​​)​[Example]State fonewStatus 做 merge如 reducers 中有 state 和 devicedata 两个字段,当新的 state 请求到的数据直接去替换掉原有的 state 会造成devicedata 的字段的丢失。通过拓展运算符,3个点 newstate 的会变新 state 内容,两者合成会将新的 state 字段赋上原有的 state 数据,实现 state 内容的差分更新。(4)建立主页 model, state、reducer 和 effectreducer 是可以直接修改 state 的内容的接口,而 effect 则是对网络请求等义务的消息进行请求函数,请求结果需要依靠 reducer 间接修改 state 中的数据内容,通过 put 函数调用 reducer 当中的方法,通过将 API 请求返回的数据分装成为 state 中数据格式即可。(5)Model 与组件的 connect在 model 中定义了组件所需要的数据和网络请求函数,用 connect 将组件所需数据和请求函数绑定。「connect」将model 和组件联系在一起,可以让组件访问 state 中的数据,调用 state 中 reducer方法和effect方法,一个model可以 connect 多个组件,多个组件可以共用一整套数据,共享一整套方法,model 中数据的改动会实时改变在组件的UI显示上(通过 props 传入,组件对应的生命周期函数会被调用( componentWillReceiveProps) )。七、dva 数据流转项目下载Model 和组件 connect 后的项目示例代码打包在项目仓库/exercise3下,启动前通过 yarn 命令安装依赖。八、项目打包前面实现了 web 应用的搭建,只是项目运行在本地的开发电脑上,可以称之为开发环境。如何将前端应用部署在服务器上供所有人访问使用,接下来一起进行项目的部署和调试。前端项目文件多,体系复杂,在开发项目中有更多的文件,都能够就此中有数千个文件高达几百兆。一般使用打包工具对项目进行打包压缩,最终只需要部暑压缩后的文件。umi 集成了打包工具 webpack,在项目主目录下执行 umi build,webpack 会自动将项目的文件进行打包和压缩,一般打包时间在几十秒。打包构建的输出在 dist 文件夹下,众多的js文件被压缩打包进 umi.js,CSS 被打包压缩进 umi.css,图片等前端素材被打包进 static 文件夹,打包后目录结构如下所示:​./dist/​​ index. html​​static​​yay. 44dd3333.jpg ​​umi.css​​umi.js​九、项目部署1、Nginx 服务器部署执行 umi build 打包后的前端内容想要实现应用文件的高性能分发,还需要将项目部暑到高性能服务器上去,「nginx」 是一款流行的免费开源服务器软件,配置简单,性能强大,经常在生产环境中使用。2、Linux 环境下部署(1)在命令行执行以下命令安装 nginx:sudo apt-get install nginx(2)修改/etc/nginx/nginx.config 配置文件内容修改为 build 后文件夹路劲,通过80端口访问(3)重启 nginx 服务:service nginx start访问 ip 地址,即可访问 nginx 部署好的项目内容。3、Windows 环境下部署windows 环境下 nginx 配置内容与 Ubuntu 基本一致,目录地址与Ubuntu 稍有差异,文件系统地址需要改为“/", nginx 1.14版本「下载地址」,下载解压后,修改 conf 文件夹下 nginx.config 内容即可,点击解压文件夹下.exe 文件,即可启动 nginx,windows 环境下 nginx 的重启与关闭可以通过任务管理器执行。通过本次的学习,基于后端服务搭建了前端的 web 应用,对于实时展示设备最新的数据,并且实现了这个设备的交互,实现了 ant design 组建了集成,页面组件的划分,基于 dva 把进行数据管理与展示,所有的组件集成于单个页面当中,也可以基于 lyout 组件组成多个页面,将智能家居的场景各个功能进行细化。十、总结现在已经学完了全部的课程内容,在本课程里一起学习了如何基于 stm32和阿里云 Iot 平台进行互联网应用的开发。首先一起了解了阿里云 Lot 平台特点,stm32产品系列和生态系统以及 mqtt 协议。在设备端开发这一部分,学习了如何使用 stm32cube 图形化工具初始化改 Stm32工程,如何对 paho 协议站进行适配以及阿里云 linkkit c-sdk 的适配和使用。在最后的服务端应用开发部分,学习了如何基于 springboo kand practice mycircle,搭建后端数据库服务器,对于外部前端服务器开发学习的如何使用 umi 框架作为项目骨架,通过可视化组件实现了页面搭建,并采用 dva 框架进行数据管理。课程章节模块内容详细目录(一)课程指南1、课程要解决的痛点课程场景介绍,数据路径端到端2、课程适用于不同资源水平的节点设备低配版节点设备、高配版设备3、课程所需具备的软、硬件 (二)阿里云 lot 平台介绍1、物联网平台简介物联网平台简介2、物联网平台基础概念讲解设备相关概念平台相关概念(三)基于 stm32的节点设备接入阿里云 lot 平台1、基于stm32的节点端及开发环境介绍STM32产品介绍:十四大家族和 IoT 策略  STM32生态系统介绍: STM32CubeSTM32L4R5以及 Nucleo-L4R5介绍ST senso 板和 EMW3080板介绍基于 Paho MQTT 的直连(适用于资源受限设备) Demo 运行起来MQT 协议介绍Demo 介绍基于 Linkkit C-SDK 的 MQTT 直连(适用于资源丰富设备 )Demo运行起来Linkkit C-SDK 介绍Demo 介绍(四)服务器端的应用开发1、综合软件架构介绍软件架构介绍知识结构梳理2、后端服务开发认识后端框架初始化运行第一个后端项目应用系统开发应用调试与部署3、前端服务开发体验认识前端框架初始化并运行第一个前端项目创建和使用组件使用 dva 实现数据流转应用调试与部署十一、课程场景概览最后再来回顾一下本次课程的场景,在本课程的场景中实现了数据从节点端到云端用户应用程序的全链路交互。sdm32端作为设备端完成了前半程数据上传,也就是向云端上传节点的温湿度信息和设备状态,也就是高温报警这一部分硬件应用到了 sdm32L 425mutual 版。Stiks01a2传感器拓展版和信科 EMW3080ipad 拓展版。对于资源授信的设备和资源分配的设备,分别提供了通过 paho 协议站直连和通过阿里云 lot 平台的 Linkkit C-SDK 这两种方式与阿里云 Lot 平台进行连接。数据到阿里云 lot 平台测以后的后半程的处理,在系统中是通过搭建用户服务器和阿里云 Lot 平台通信,来完成数据的读取和控制面源的下发以及人机交互的过程。用户服务器包括前端服务器和后端服务器两个部分,前端负责完成和用户的人机交互这部分功能,后端负责和 Lot 平台通信维护数据库,因为前端服务提供数据。1、每5秒上报温湿度值,闪烁绿灯2、温度超[阈值]亮红灯,并在每10秒向用户服务器报警,直到温度恢复[阈值]以下或者收到警报解除消息3、收到警报解除信息后红灯闪烁4、温度恢复到[阈值]以下天红灯5、湿度值被阿里云 |oT 转发到用户服务器,进行数据库存储,同时在 web 端显示近期温湿度数据曲线6、报警消息被阿里云 |oT 转发到用户服务器,在web端显示7、用户通过 web 端页面解除报警8、用户通过 web 端页面设置[阈值]参数
前端移动端开发技巧之HTML与CSS
### HTML方向##### 调用系统功能使用`<a>`能快速调用移动设备的`电话/短信/邮件`三大通讯功能,使用`<input>`能快速调用移动设备的的`图库/文件`。这些功能方便了页面与系统的交互,关键在于调用格式一定要准确,否则会被`移动端浏览器`忽略。```<!-- 拨打电话 --><a href="tel:10086">拨打电话给10086小姐姐</a><!-- 发送短信 --><a href="sms:10086">发送短信给10086小姐姐</a><!-- 发送邮件 --><a href="mailto:young.joway@aliyun.com">发送邮件给JowayYoung</a><!-- 选择照片或拍摄照片 --><input type="file" accept="image/*"><!-- 选择视频或拍摄视频 --><input type="file" accept="video/*"><!-- 多选文件 --><input type="file" multiple>```##### 忽略自动识别有些`移动端浏览器`会自动将数字字母符号识别为`电话/邮箱`并将其渲染成上述**调用系统功能**里的`<a>`。虽然很方便却有可能违背需求。```<!-- 忽略自动识别电话 --><meta name="format-detection" content="telephone=no"><!-- 忽略自动识别邮箱 --><meta name="format-detection" content="email=no"><!-- 忽略自动识别电话和邮箱 --><meta name="format-detection" content="telephone=no, email=no">```##### 弹出数字键盘使用`<input type="tel">`弹起数字键盘会带上`#`和`*`,适合输入电话。推荐使用`<input pattern="\d*">`弹起数字键盘,适合输入验证码等纯数字格式。```<!-- 纯数字带#和* --><input type="tel"><!-- 纯数字 --><input pattern="\d*">```##### 唤醒原生应用通过`location.href`与原生应用建立通讯渠道,这种页面与客户端的通讯方式称为**URL Scheme**,其基本格式为`scheme://[path][?query]`,笔者曾经发表过[《H5与App的通讯方式》](https://juejin.cn/post/6844904020201455624)讲述`URL Scheme`的使用。*   **scheme**:应用标识,表示应用在系统里的唯一标识*   **path**:应用行为,表示应用某个页面或功能*   **query**:应用参数,表示应用页面或应用功能所需的条件参数`URL Scheme`一般由前端与客户端共同协商。唤醒原生应用的前提是必须在移动设备里安装了该应用,有些`移动端浏览器`即使安装了该应用也无法唤醒原生应用,因为它认为`URL Scheme`是一种潜在的危险行为而禁用它,像`Safari`和`微信浏览器`。还好`微信浏览器`可开启白名单让`URL Scheme`有效。若在页面引用第三方原生应用的`URL Schema`,可通过抓包第三方原生应用获取其`URL`。```<!-- 打开微信 --><a href="weixin://">打开微信</a><!-- 打开支付宝 --><a href="alipays://">打开支付宝</a><!-- 打开支付宝的扫一扫 --><a href="alipays://platformapi/startapp?saId=10000007">打开支付宝的扫一扫</a><!-- 打开支付宝的蚂蚁森林 --><a href="alipays://platformapi/startapp?appId=60000002">打开支付宝的蚂蚁森林</a>```##### 禁止页面缩放在智能手机的普及下,很多网站都具备`桌面端`和`移动端`两种浏览版本,因此无需双击缩放查看页面。禁止页面缩放可保障`移动端浏览器`能无遗漏地展现页面所有布局。```<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, minimum-scale=1, maximum-scale=1">```##### 禁止页面缓存**Cache-Control**指定请求和响应遵循的缓存机制,不想使用浏览器缓存就禁止呗!```<meta http-equiv="Cache-Control" content="no-cache">```##### 禁止字母大写有时在输入框里输入文本会默认开启首字母大写纠正,就是输入首字母小写会被自动纠正成大写,特么的烦。直接声明`autocapitalize=off`关闭首字母大写功能和`autocorrect=off`关闭纠正功能。```<input autocapitalize="off" autocorrect="off">```##### 针对Safari配置贴一些`Safari`较零散且少用的配置。```<!-- 设置Safari全屏,在iOS7+无效 --><meta name="apple-mobile-web-app-capable" content="yes"><!-- 改变Safari状态栏样式,可选default/black/black-translucent,需在上述全屏模式下才有效 --><meta name="apple-mobile-web-app-status-bar-style" content="black"><!-- 添加页面启动占位图 --><link rel="apple-touch-startup-image" href="pig.jpg" media="(device-width: 375px)"><!-- 保存网站到桌面时添加图标 --><link rel="apple-touch-icon" sizes="76x76" href="pig.jpg"><!-- 保存网站到桌面时添加图标且清除默认光泽 --><link rel="apple-touch-icon-precomposed" href="pig.jpg">```##### 针对其他浏览器配置贴一些其他浏览器较零散且少用的配置,主要是常用的`QQ浏览器`、`UC浏览器`和`360浏览器`。```<!-- 强制QQ浏览器竖屏 --><meta name="x5-orientation" content="portrait"><!-- 强制QQ浏览器全屏 --><meta name="x5-fullscreen" content="true"><!-- 开启QQ浏览器应用模式 --><meta name="x5-page-mode" content="app"><!-- 强制UC浏览器竖屏 --><meta name="screen-orientation" content="portrait"><!-- 强制UC浏览器全屏 --><meta name="full-screen" content="yes"><!-- 开启UC浏览器应用模式 --><meta name="browsermode" content="application"><!-- 开启360浏览器极速模式 --><meta name="renderer" content="webkit">```##### 让:active有效,让:hover无效有些元素的`:active`可能会无效,而元素的`:hover`在点击后会一直处于点击状态,需点击其他位置才能解除点击状态。给`<body>`注册一个空的`touchstart事件`可将两种状态反转。```<body ontouchstart></body>```### CSS方向##### 自动适应布局针对移动端,笔者通常会结合JS依据`屏幕宽度`与`设计图宽度`的比例动态声明`<html>`的`font-size`,以`rem`为长度单位声明所有节点的几何属性,这样就能做到大部分移动设备的页面兼容,兼容出入较大的地方再通过`媒体查询`做特别处理。笔者通常将`rem布局比例`设置成`1rem=100px`,即在设计图上`100px`长度在CSS代码上使用`1rem`表示。```function AutoResponse(width = 750) {    const target = document.documentElement;    if (target.clientWidth >= 600) {        target.style.fontSize = "80px";    } else {        target.style.fontSize = target.clientWidth / width * 100 + "px";    }}AutoResponse();window.addEventListener("resize", () => AutoResponse());```当然还可依据`屏幕宽度`与`设计图宽度`的比例使用`calc()`动态声明`<html>`的`font-size`,这样就能节省上述代码。不对,是完全代替上述代码。```html {    font-size: calc(100vw / 7.5);}```若以`iPad Pro`分辨率`1024px`为移动端和桌面端的断点,还可结合`媒体查询`做断点处理。`1024px`以下使用`rem布局`,否则不使用`rem布局`。```@media screen and (max-width: 1024px) {    html {        font-size: calc(100vw / 7.5);    }}```##### 自动适应背景使用`rem布局`声明一个元素背景,多数情况会将`background-size`声明为`cover`。可能在设计图对应分辨率的移动设备下,背景会完美贴合显示,但换到其他分辨率的移动设备下就会出现左右空出`1px`到`npx`的空隙。此时将`background-size`声明为`100% 100%`,跟随`width`和`height`的变化而变化。反正`width`和`height`都是量好的实际尺寸。```.elem {    width: 1rem;    height: 1rem;    background: url("pig.jpg") no-repeat center/100% 100%;}```##### 监听屏幕旋转你还在使用JS判断横屏竖屏调整样式吗?那就真的`Out`了。```/* 竖屏 */@media all and (orientation: portrait) {    /* 自定义样式 */}/* 横屏 */@media all and (orientation: landscape) {   /* 自定义样式 */  }```##### 支持弹性滚动在苹果系统上非`<body>`元素的滚动操作可能会存在卡顿,但安卓系统不会出现该情况。通过声明`overflow-scrolling:touch`调用系统原生滚动事件优化`弹性滚动`,增加页面滚动的流畅度。```body {    -webkit-overflow-scrolling: touch;}.elem {    overflow: auto;}```##### 禁止滚动传播与`桌面端浏览器`不一样,`移动端浏览器`有一个奇怪行为。当页面包含多个滚动区域时,滚完一个区域后若还存在滚动动量则会将这些剩余动量传播到下一个滚动区域,造成该区域也滚动起来。这种行为称为**滚动传播**。若不想产生这种奇怪行为可直接禁止。```.elem {    overscroll-behavior: contain;}```##### 禁止屏幕抖动对于一些突然出现滚动条的页面,可能会产生左右抖动的不良影响。在一个滚动容器里,打开弹窗就隐藏滚动条,关闭弹窗就显示滚动条,来回操作会让屏幕抖动起来。提前声明滚动容器的`padding-right`为滚动条宽度,就能有效消除这个不良影响。每个`移动端浏览器`的滚动条宽度都有可能不一致,甚至不一定占位置,通过以下方式能间接计算出滚动条的宽度。`100vw`为视窗宽度,`100%`为滚动容器内容宽度,相减就是滚动条宽度,妥妥的动态计算。```body {    padding-right: calc(100vw - 100%);}```##### 禁止长按操作有时不想用户长按元素呼出菜单进行`点链接`、`打电话`、`发邮件`、`保存图片`或`扫描二维码`等操作,声明`touch-callout:none`禁止用户长按操作。有时不想用户`复制粘贴`盗文案,声明`user-select:none`禁止用户长按操作和选择复制。```* {    /* pointer-events: none; */ /* 微信浏览器还需附加该属性才有效 */    user-select: none; /* 禁止长按选择文字 */    -webkit-touch-callout: none;}```但声明`user-select:none`会让`<input>`和`<textarea>`无法输入文本,可对其声明`user-select:auto`排除在外。```input,textarea {    user-select: auto;}```##### 禁止字体调整旋转屏幕可能会改变字体大小,声明`text-size-adjust:100%`让字体大小保持不变。```* {    text-size-adjust: 100%;}```##### 禁止高亮显示触摸元素会出现半透明灰色遮罩,不想要!```* {    -webkit-tap-highlight-color: transparent;}```##### 禁止动画闪屏在移动设备上添加动画,多数情况会出现闪屏,给动画元素的父元素构造一个`3D环境`就能让动画稳定运行了。```.elem {    perspective: 1000;    backface-visibility: hidden;    transform-style: preserve-3d;}```##### 美化表单外观表单元素样式太丑希望自定义,`appearance:none`来帮你。```button,input,select,textarea {    appearance: none;    /* 自定义样式 */}```##### 美化滚动占位滚动条样式太丑希望自定义,`::-webkit-scrollbar-*`来帮你。记住以下三个关键词就能随机应变了。*   [x]  **::-webkit-scrollbar**:滚动条整体部分*   [x]  **::-webkit-scrollbar-track**:滚动条轨道部分*   [x]  **::-webkit-scrollbar-thumb**:滚动条滑块部分```::-webkit-scrollbar {    width: 6px;    height: 6px;    background-color: transparent;}::-webkit-scrollbar-track {    background-color: transparent;}::-webkit-scrollbar-thumb {    border-radius: 3px;    background-image: linear-gradient(135deg, #09f, #3c9);}```##### 美化输入占位输入框占位文本太丑,`::-webkit-input-placeholder`来帮你。```input::-webkit-input-placeholder {    color: #66f;}```##### 对齐输入占位有强迫症的同学总会觉得输入框文本位置整体偏上,感觉未居中心里就痒痒的。`桌面端浏览器`里声明`line-height`等于`height`就能解决,但`移动端浏览器`里还是未能解决,需将`line-height`声明为`normal`才行。```input {    line-height: normal;}```##### 对齐下拉选项下拉框选项默认向左对齐,是时候改改向右对齐了。```select option {    direction: rtl;}```##### 修复点击无效在苹果系统上有些情况下非可点击元素监听`click事件`可能会无效,针对该情况只需对不触发`click事件`的元素声明`cursor:pointer`就能解决。```.elem {    cursor: pointer;}```##### 识别文本换行多数情况会使用JS换行文本,那就真的`Out`了。若接口返回字段包含`\n`或`<br>`,千万别替换掉,可声明`white-space:pre-line`交由浏览器做断行处理。```* {    white-space: pre-line;}```##### 开启硬件加速想动画更流畅吗,开启`GPU硬件加速`呗!```.elem {    transform: translate3d(0, 0, 0);    /* transform: translateZ(0); */}```##### 描绘像素边框万年话题,如何描绘`一像素边框`?```.elem {    position: relative;    width: 200px;    height: 80px;    &::after {        position: absolute;        left: 0;        top: 0;        border: 1px solid #f66;        width: 200%;        height: 200%;        content: "";        transform: scale(.5);        transform-origin: left top;    }}```##### 控制溢出文本万年话题,如何控制文本做`单行溢出`和`多行溢出`?```.elem {    width: 400px;    line-height: 30px;    font-size: 20px;    &.sl-ellipsis {        overflow: hidden;        text-overflow: ellipsis;        white-space: nowrap;    }    &.ml-ellipsis {        display: -webkit-box;        overflow: hidden;        text-overflow: ellipsis;        -webkit-line-clamp: 3;        -webkit-box-orient: vertical;    }}```
最新Web前端面试题精选大全及答案
HTML、CSS相关1.网络中使用最多的图片格式有哪些JPEG,GIF,PNG,最流行的是jpeg格式,可以把文件压缩到最小 在ps以jpeg格式存储时,提供11级压缩级别2.请简述css盒子模型一个css盒子从外到内可以分成四个部分:margin(外边距),border(边框),padding(内边距),content(内容)默认情况下,盒子的width和height属性只是设置content(内容)的宽和高盒子真正的宽应该是:内容宽度+左右填充+左右边距+左右边框盒子真正的高应该是:内容高度+上下填充+上下边距+上下边框3.视频/音频标签的使用视频:视频标签属性:src 需要播放的视频地址width/height 设置播放视频的宽高,和img标签的宽高属性一样autoplay 是否自动播放controls 是否显示控制条poster 没有播放之前显示的展位图片loop 是否循环播放perload 预加载视频(缓存)与autoplay相冲突,设置了autoplay属性,perload属性会失效。muted 静音模式音频: 音频属性和视频属性差不多,不过宽高和poster属性不能用4.HTML5新增的内容有哪些新增语义化标签新增表单类型表单元素表单属性表单事件多媒体标签5.Html5 新增的语义化标签有哪些语义化标签优点:1.提升可访问性 2.seo 3.结构清晰,利于维护Header页面头部 main页面主要内容 footer页面底部Nav导航栏 aside侧边栏 article加载页面一块独立内容Section相当于div  figure加载独立内容(上图下字) figcaption  figure的标题Hgroup标题组合标签 mark高亮显示 dialog 加载对话框标签(必须配合open属性)Embed加载插件的标签 video加载视频 audio加载音频(支持格式ogg,mp3,wav)6.Css3新增的特性边框:border-radius添加圆角边框       border-shadow:给框添加阴影 (水平位移,垂直位移,模糊半径,阴影尺寸,阴影颜色,insetr(内/外部阴影))border-image:设置边框图像border-image-source 边框图片的路径border-image-slice 图片边框向内偏移border-image-width 图片边框的宽度border-image-outset 边框图像区域超出边框的量border-image-repeat 图像边框是否平铺(repeat平铺 round铺满 stretch 拉伸)背景:Background-size 背景图片尺寸Background-origin规定background-position属性相对于什么位置定位Background-clip 规定背景的绘制区域(padding-box,border-box,content-box)渐变:Linear-gradient()线性渐变Radial-gradient()径向渐变文本效果:Word-break:定义如何换行Word-wrap:允许长的内容可以自动换行Text-overflow:指定当文本溢出包含它的元素,应该干啥Text-shadow:文字阴影(水平位移,垂直位移,模糊半径,阴影颜色)转换:Transform 应用于2D3D转换,可以将元素旋转,缩放,移动,倾斜Transform-origin 可以更改元素转换的位置,(改变xyz轴)Transform-style 指定嵌套元素怎么样在三位空间中呈现2D转换方法:rotate旋转 translate(x,y)指定元素在二维空间的位移 scale(n)定义缩放转换3D转换方法:Perspective(n)为3D转换 translate rotate scale过渡:Transition-property过渡属性名Transition-duration 完成过渡效果需要花费的时间Transition-timing-function 指定切换效果的速度Transition-delay 指定什么时候开始切换效果动画:animationAnimation-name 为@keyframes动画名称animation-duration 动画需要花费的时间animation-timing-function 动画如何完成一个周期animation-delay 动画启动前的延迟间隔animation-iteration-count 动画播放次数animation-direction 是否轮流反向播放动画7.清除浮动的方式有哪些?请说出各自的优点高度塌陷:当所有的子元素浮动的时候,且父元素没有设置高度,这时候父元素就会产生高度塌陷。清除浮动方式1:给父元素单独定义高度优点:快速简单,代码少 缺点:无法进行响应式布局清除浮动方式2:父级定义overflow:hidden;zoom:1(针对ie6的兼容)优点:简单快速、代码少,兼容性较高 缺点:超出部分被隐藏,布局时要注意清除浮动方式3:在浮动元素后面加一个空标签,clear:both;height:0;overflow:hidden优点:简单快速、代码少,兼容性较高。缺点:增加空标签,不利于页面优化清除浮动方式4:父级定义overflow:auto优点:简单,代码少,兼容性好缺点:内部宽高超过父级div时,会出现滚动条清除浮动方式5:万能清除法:给塌陷的元素添加伪对象.father:after{         Content:“随便写”;         Clear:both;         display:block;         Height:0;         Overflow:hidden;         Visibility:hidden}优点:写法固定,兼容性高        缺点:代码多8.定位的属性值有何区别Position有四个属性值:relative absolute fixed staticRelative相对定位 不脱离文档流,相对于自身定位Absolute 绝对定位,脱离文档流 相对于父级定位Fixed 固定定位,脱离文档流,相对于浏览器窗口定位Static 默认值,元素出现在正常的流中9.子元素如何在父元素中居中水平居中:1.子父元素宽度固定,子元素设置margin:auto,并且子元素不能设置浮动,否则居中失效2.子父元素宽度固定,父元素设置text-align:center,子元素设置display:inline-block,并且子元素不能设置浮动,否则居中失效水平垂直居中:子元素相对于父元素绝对定位,子元素top,left设置50%,子元素margin-top和margin-left减去各自宽高的一半子元素相对于父元素绝对定位,子元素上下左右全为0,然后设置子元素margin:auto父元素设置display:table-cell vertical-align:middle,子元素设置margin:auto子元素相对定位,子元素top,left值为50%,transform:translate(-50%,-50%)子元素相对父元素绝对定位,子元素top,left值为50%,transform:translate(-50%,-50%)父元素设置弹性盒子,display:flex; justfy-content:center ;align-item:center; justfy-content:center10.Border-box与content-box的区别?Content-box 标准盒模型 width不包括padding和borderBorder-box 怪异盒模型width包括padding和border11.如何让元素垂直居中?1).设置子元素和父元素的行高一样2).子元素设置为行内块,再加vertical-align:middle3).已知父元素高度,子元素相对定位,通过transform:translateY(-50%)4).不知道父元素高度,子绝父相,子元素top:50%,transform:translateY(-50%)5).创建一个隐藏节点,让隐藏节点的height为剩余高度的一半6).给父元素display:table,子元素display:table-cell,vertical-align:middle7).给父元素添加伪元素8).弹性盒,父元素display:flex,子元素align-self:center12.如何让chrome浏览器显示小于12px的文字?本来添加谷歌私有属性 -webkit-text-size-adjust:none,现在-webkit-transform:scale()13.Css选择器有哪些,那些属性可以继承,优先级如何计算?Css3新增的伪类有哪些?Css2选择器:元素选择器,id选择器,群组选择器,类选择器,*通配符选择器,后代选择器Css2伪类选择器:a:link/visited/hover/activeCss3选择器:空格 >  +相邻兄弟选择器  ~通用选择器(查找后面所有)结构伪类选择器:查找第几个nth-child(n)查找同一类型第几个nth-of-type查找唯一类型 only-of-type属性选择器:根据标签属性查找 [attr=value]: root 查找根元素html标签: empty 查赵空标签目标伪类选择器:(表单):enabled 查找可以使用的标签:disabled 查找禁止使用的标签:checked 查找被选中的标签伪元素选择器 ::selection设置选中文本内容的高亮显示(只能用于背景色和文本颜色)否定伪类选择器 not()语言伪类选择器 lang(取值)优先级(权重):元素选择器1伪元素选择器1class选择器10伪类选择器10属性选择器10Id选择器100内联样式的权重1000包含选择器权重为权重之和继承样式权重为0那些属性可以继承:Css继承特性主要是文本方面所有元素可继承:visibility和cursor块级元素可继承:text-indent和text-align列表元素可继承:list-style,list-style-type,list-style-position,list-style-image内联元素可继承:letter-spacing,word-spacing,line-height,color,font,font-family,font-sizeFont-style,font-variant,font-weight,text-decoration,text-transform,direction字母间距 段落间距  行高   字体颜色 字体种类 字体大小 字体样式  字体粗细 小型大写字母文本 文本修饰 转换不同元素中的文本 文本方向14.网页中有大量图片加载很慢 你有什么办法进行优化?1.图片懒加载,在图片未可视区域加一个滚动条事件,判断图片位置与浏览器顶端和页面的距离,如果前者小鱼后者,优先加载2.使用图片预加载技术,将当前展示图片的前一张和后一张优先下载3.使用csssprite或者svgsprite15.行内元素/块级元素有哪些?行内元素:相邻的行内元素会排列在同一行,不会独占一行 设置宽高无效 span块级元素:会独占一行 可以设置宽高等属性div可变元素:根据上下文预警决定该元素为块元素还是内联元素块级元素:div h1-h6 hr p ul ol table address blockquote dir from menu行内元素:a br I em img input select span sub sup u textarea可变元素:button del iframe ins16.浏览器的标准模式和怪异模式区别?标准模式:浏览器按照W3C标准解析执行代码怪异模式:浏览器根据自己的方式解析执行代码,因为不同浏览器解析执行方式不一样,所以叫怪异模式区别:在怪异模式下,盒模型为怪异盒模型 而在标准模式下为标准盒子模型图片元素的垂直对齐方式     对于行内元素和table-cell元素,标准模式下vertical-align属性默认值是baseline,而在怪异模式下,table单元格中的图片的vertical-align属性默认值是bottom,因此在图片底部会有几像素的空间元素中的字体         css中font的属性都是可以继承的,怪异模式下,对于table元素,字体的某些元素不能从其他封装元素继承中得到,特别是font-size属性内联元素的尺寸     标准模式下,non-replaced inline元素无法自定义大写,怪异模式下,定义元素的宽高会影响元素的尺寸元素的百分比高度         当一个元素使用百分比高度时,在标准模式下,高度取决于内容变化,在怪异迷失下,百分比被准确应用元素溢出的处理              标准模式下,overflow取值默认值为visible,在怪异模式下,这个溢出会被当做扩展box对待,就是元素的大小由内容决定,溢出不会裁剪,元素框自动调整,包含溢出内容17.Margin和padding在什么场合下使用Margin外边距 自身边框到另一个边框之间的距离Padding 内边距 自身边距到自身内容之间的距离当需要在border外侧添加空白时用margin,当需要在border内侧添加空白时用padding18.弹性盒子布局属性有那些请简述?Flex-direction:弹性容器中子元素排列方式(主轴排列方式)Flex-wrap:设置弹性盒子的子元素超出父容器时是否换行Flex-flow:是flex-direction和flex-wrap简写形式Align-item:设置弹性盒子元素在侧轴上的对齐方式Align-content:设置行对齐Justify-content:设置弹性盒子元素在主轴上的对齐方式19.怎么实现标签的禁用添加disabled属性20.Flex布局原理就是通过给父盒子添加flex属性,来控制子盒子的位置和排列方式21.Px,rem,em的区别Px,绝对长度单位,像素px是相对于显示器屏幕分辨率来说的em 相对长度单位,相对于当前对象内文本的字体尺寸         em的值并不是固定的         em会继承父级元素的字体大小(参考物是父元素的font-size)         em中所有的字体都是相对于父元素的大小决定的rem 相对于html根元素的font-size1em=1rem=16px 在body中加入font-size:62.5% 这样直接就是原来的px数值除以10加上em就可以22.网页的三层结构有哪些结构(html或xhtm标记语言)表现(css样式表)行为(js)请简述媒体查询媒体查询扩展了media属性, 就是根据不同的媒体类型设置不同的css样式,达到自适应的目的。Rem缺点比如:小说网站,屏幕越小的移动设备如果用了rem肯定文字就越小,就会导致看文章的时候特别费眼25.常见的兼容性一阶段内容中记几个26.垂直与水平居中的方式27.三栏布局方式两边固定中间自适应margin负值法:左右两栏均左浮动,左右两栏采用负的margin值。中间栏被宽度为100%的浮动元素包起来自身浮动法:左栏左浮动,右栏右浮动,中间栏放最后绝对定位法:左右两栏采用绝对定位,分别固定于页面的左右两侧,中间的主体栏用左右margin值撑开距离。4.flex  左右固定宽  中间flex:15.网格布局table布局28.Doctype作用声明文档类型Javascript相关1.Js基本数据类型有哪些字符串String 数值Number  布尔boolean   null  undefined  对象 数组2.Ajax如何使用一个完整的AJAX请求包括五个步骤:创建XMLHTTPRequest对象使用open方法创建http请求,并设置请求地址xhr.open(get/post,url,async,true(异步),false(同步))经常使用前三个参数设置发送的数据,用send发送请求注册事件(给ajax设置事件)获取响应并更新页面3.如何判断一个数据是NaNNaN 非数字 但是用typeof检测是number类型利用NaN的定义  用typeof判断是否为number类型并且判断是否满足isnan利用NaN是唯一一个不等于任何自身的特点 n!==n利用ES6中提供的Object.is()方法(判断两个值是否相等)  n==nanJs中null与undefined区别相同点:用if判断时,两者都会被转换成false不同点:number转换的值不同 number(null)为0   number(undefined)为NaNNull表示一个值被定义了,但是这个值是空值Undefined 变量声明但未赋值5.闭包是什么?有什么特性?对页面会有什么影响闭包可以简单理解成:定义在一个函数内部的函数。其中一个内部函数在包含它们的外部函数之外被调用时,就会形成闭包。特点:1.函数嵌套函数。2.函数内部可以引用外部的参数和变量。3.参数和变量不会被垃圾回收机制回收。使用:1.读取函数内部的变量; 2.这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除。优点:1:变量长期驻扎在内存中;2:避免全局变量的污染;3:私有成员的存在 ;缺点:会造成内存泄露6.Js中常见的内存泄漏:1.意外的全局变量2.被遗忘的计时器或回调函数3.脱离DOM的引用4.闭包7.事件委托是什么?如何确定事件源(Event.target  谁调用谁就是事件源)JS高程上讲:事件委托就是利用事件冒泡,只制定一个时间处理程序,就可以管理某一类型的所有事件。事件委托,称事件代理,是js中很常用的绑定事件的技巧,事件委托就是把原本需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务,事件委托的原理是DOM元素的事件冒泡8.什么是事件冒泡?一个事件触发后,会在子元素和父元素之间传播,这种传播分为三个阶段,捕获阶段(从window对象传导到目标节点(从外到里),这个阶段不会响应任何事件),目标阶段,(在目标节点上触发),冒泡阶段(从目标节点传导回window对象(从里到外)),事件委托/事件代理就是利用事件冒泡的机制把里层需要响应的事件绑定到外层9.本地存储与cookie的区别Cookie 是小甜饼的意思。顾名思义,cookie 确实非常小,它的大小限制为4KB左右。它的主要用途有保存登录信息,比如你登录某个网站市场可以看到“记住密码”,这通常就是通过在 Cookie 中存入一段辨别用户身份的数据来实现的。localStoragelocalStorage 是 HTML5 标准中新加入的技术,它并不是什么划时代的新东西。早在 IE 6 时代,就有一个叫 userData 的东西用于本地存储,而当时考虑到浏览器兼容性,更通用的方案是使用 Flash。而如今,localStorage 被大多数浏览器所支持,如果你的网站需要支持 IE6+,那以 userData 作为你方案是种不错的选择。sessionStoragesessionStorage 与 localStorage 的接口类似,但保存数据的生命周期与 localStorage 不同。做过后端开发的同学应该知道 Session 这个词的意思,直译过来是“会话”。而 sessionStorage 是一个前端的概念,它只是可以将一部分数据在当前会话中保存下来,刷新页面数据依旧存在。但当页面关闭后,sessionStorage 中的数据就会被清空。三者的异同特性CookielocalStoragesessionStorage数据的生命期一般由服务器生成,可设置失效时间。如果在浏览器端生成Cookie,默认是关闭浏览器后失效除非被清除,否则永久保存仅在当前会话下有效,关闭页面或浏览器后被清除存放数据大小4K左右一般为5MB与服务器端通信每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题仅在客户端(即浏览器)中保存,不参与和服务器的通信易用性需要程序员自己封装,源生的Cookie接口不友好源生接口可以接受,亦可再次封装来对Object和Array有更好的支持10.ES6新特性const和let、模板字符串、箭头函数、函数的参数默认值、对象和数组解构、for...of 和 for...in、ES6中的类11.Let与var与const的区别Var声明的变量会挂载在window上,而let和const声明的变量不会Var声明的变量存在变量提升,let和const不存在变量提升同一作用域下var可以声明同名变量,let和const、不可以Let和const声明会形成块级作用域Let暂存死区Const一旦声明必须赋值,不能用null占位,声明后不能再修改,如果声明的是复合类型数据,可以修改属性12.数组方法有哪些请简述push() 从后面添加元素,返回值为添加完后的数组的长度arr.pop() 从后面删除元素,只能是一个,返回值是删除的元素arr.shift() 从前面删除元素,只能删除一个 返回值是删除的元素arr.unshift() 从前面添加元素, 返回值是添加完后的数组的长度 arr.splice(i,n) 删除从i(索引值)开始之后的那个元素。返回值是删除的元素arr.concat() 连接两个数组 返回值为连接后的新数组str.split() 将字符串转化为数组 arr.sort() 将数组进行排序,返回值是排好的数组,默认是按照最左边的数字进行排序,不是按照数字大小排序的arr.reverse() 将数组反转,返回值是反转后的数组 arr.slice(start,end) 切去索引值start到索引值end的数组,不包含end索引的值,返回值是切出来的数组 arr.forEach(callback) 遍历数组,无return  即使有return,也不会返回任何值,并且会影响原来的数组 arr.map(callback) 映射数组(遍历数组),有return 返回一个新数组 。 arr.filter(callback) 过滤数组,返回一个满足要求的数组 13.Json如何新增/删除键值对14.什么是面向对象请简述面向对象是一种思想,是基于面向过程而言的,就是说面向对象是将功能等通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节;这种思想是将数据作为第一位,这是对数据一种优化,操作起来更加的方便,简化了过程。Js本身是没有class类型的,但是每个函数都有一个prototype属性,prototype指向一个对象,当函数作为构造函数时,prototype就起到类似于class的作用面向对象有三个特点:封装(隐藏对象的属性和实现细节,对外提供公共访问方式),继承(提高代码复用性,继承是多态的前提),多态(是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象)15.普通函数和构造函数的区别1.构造函数也是一个普通函数,创建方式和普通函数一样,但是构造函数习惯上首字母大写2.调用方式不一样,普通函数直接调用,构造函数要用关键字new来调用3.调用时,构造函数内部会创建一个新对象,就是实例,普通函数不会创建新对象4.构造函数内部的this指向实例,普通函数内部的this指向调用函数的对象(如果没有对象调用,默认为window)5.构造函数默认的返回值是创建的对象(也就是实例),普通函数的返回值由return语句决定6.构造函数的函数名与类名相同16.请简述原型/原型链/(原型)继承什么是原型:    任何对象实例都有一个原型,也叫原型对象,这个原型对象由对象的内置属性_proto_指向它的构造函数的prototype指向的对象,即任何对象都是由一个构造函数创建的,但是不是每一个对象都有prototype,只有方法才有prototype。什么是原型链?原型链基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。我们知道,每个构造函数都有一个原型对象,每个原型对象都有一个指向构造函数的指针,而实例又包涵一个指向原型对象的内部指针。    原型链的核心就是依赖对象的_proto_的指向,当自身不存在的属性时,就一层层的扒出创建对象的构造函数,直至到Object时,就没有_proto_指向了。因为_proto_实质找的是prototype,所以我们只要找这个链条上的构造函数的prototype。其中Object.prototype是没有_proto_属性的,它==null。每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含指向原型对象内部的指针。我们让原型对象(1)等于另一个原型对象的实例(2),此时原型对象(2)将包含一个指向原型对象(1)的指针,再让原型对象(2)的实例等于原型对象(3),如此层层递进就构成了实例和原型的链条,这就是原型链的概念每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数想指针(constructor),而实例对象都包含一个指向原型对象的内部指针(__proto__)。如果让原型对象等于另一个原型对象的实例,此时的原型对象将包含一个指向另一个原型的指针(__proto__),另一个原型也包含着一个指向另一个构造函数的指针(constructor)。假如另一个原型又是另一个类型的实例……这就构成了实例与原型的链条。也叫原型链原型继承是js的一种继承方式,原型链作为实现继承的主要方法,其基本思路是利用原型让一个引用类型继承另一个引用类型的属性和方法, 原型继承:利用原型中的成员可以被和其相关的对象共享这一特性,可以实现继承,这种实现继承的方式,就叫做原型继承.17.Promise的理解 一、什么是Promise?我们用Promise来解决什么问题?我们都知道,Promise是承诺的意思,承诺它过一段时间会给你一个结果。Promise 是一种解决异步编程的方案,相比回调函数和事件更合理和更强大。从语法上讲,promise是一个对象,从它可以获取异步操作的消息;二、promise有三种状态:pending 初始状态也叫等待状态,fulfiled成功状态,rejected失败状态;状态一旦改变,就不会再变。创造promise实例后,它会立即执行。三、Promise的两个特点1、Promise对象的状态不受外界影响2、Promise的状态一旦改变,就不会再变,任何时候都可以得到这个结果,状态不可以逆,四、Promise的三个缺点1)无法取消Promise,一旦新建它就会立即执行,无法中途取消2)如果不设置回调函数,Promise内部抛出的错误,不会反映到外部3)当处于pending(等待)状态时,无法得知目前进展到哪一个阶段,是刚刚开始还是即将完成promise是用来解决两个问题的:1.回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象2.promise可以支持多并发的请求,获取并发请求中的数据这个promise可以解决异步的问题,本身不能说promise是异步的     19.请简述async的用法Async就是generation和promise的语法糖,async就是将generator的*换成async,将yiled换成await函数前必须加一个async,异步操作方法前加一个await关键字,意思就是等一下,执行完了再继续走,注意:await只能在async函数中运行,否则会报错Promise如果返回的是一个错误的结果,如果没有做异常处理,就会报错,所以用try..catch捕获一下异常就可以了20.. 一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?分为4个步骤:当发送一个 URL 请求时,不管这个 URL 是 Web 页面的 URL 还是 Web 页面上每个资源的 URL,浏览器都会开启一个线程来处理这个请求,同时在远程 DNS 服务器上启动一个 DNS 查询。这能使浏览器获得请求对应的 IP 地址。浏览器与远程 Web 服务器通过 TCP 三次握手协商来建立一个 TCP/IP 连接。该握手包括一个同步报文,一个同步-应答报文和一个应答报文,这三个报文在 浏览器和服务器之间传递。该握手首先由客户端尝试建立起通信,然后服务器响应并接受客户端的请求,最后由客户端发出该请求已经被接受的报文。一旦 TCP/IP 连接建立,浏览器会通过该连接向远程服务器发送 HTTP 的 GET 请求。远程服务器找到资源并使用 HTTP 响应返回该资源此时,Web 服务器提供资源服务,客户端开始下载资源。jQuery相关的知识Css预处理sass less是什么?为什么使用他们Sass和less都是css预处理器,是css上的一种抽象层,是一种特殊的语法,最终会编译成css,less是一种动态样式语言,给css赋予了动态语言的特性,比如:变量,继承,嵌套。Less既可以在客户端运行,在可以在服务端运行(需要借助node)Js中.call()与.apply()区别apply:调用一个对象的一个方法,用另一个对象替换当前对象。call:调用一个对象的一个方法,用另一个对象替换当前对象。从定义中可以看出,call和apply都是调用一个对象的一个方法,用另一个对象替换当前对象。而不同之处在于传递的参数,apply最多只能有两个参数——新this对象和一个数组argArray,如果arg不是数组则会报错相同点:两个方法产生的作用是完全一样的。call, apply作用就是借用别人的方法来调用,就像调用自己的一样.不同点:方法传递的参数不同为什么会造成跨域/请简述同源策略出现跨域问题的原因:在前后端分离的模式下,前后端的域名是不一致的,此时就会发生跨域访问问题。在请求的过程中我们要想回去数据一般都是post/get请求,所以..跨域问题出现跨域问题来源于JavaScript的同源策略,即只有 协议+主机名+端口号(如存在)相同,则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。同源策略 是由NetScape提出的一个著名的安全策略。所谓的同源,指的是协议,域名,端口相同。浏览器处于安全方面的考虑,只允许本域名下的接口交互,不同源的客户端脚本,在没有明确授权的情况下,不能读写对方的资源。请输出三种减少页面加载时间的方式优化图片 图像格式的选择(GIF:提供的颜色较少,可用在一些对颜色要求不高的地方) 优化CSS(压缩合并css,如 margin-top, margin-left...) 网址后加斜杠(如www.campr.com/目录,会判断这个目录是什么文件类型,或者是目录。)  cdn托管标明高度和宽度(如果浏览器没有找到这两个参数,它需要一边下载图片一边计算大小,如果图片很多,浏览器需要不断地调整页面。这不但影响速度,也影响浏览体验。 当浏览器知道了高度和宽度参数后,即使图片暂时无法显示,页面上也会腾出图片的空位,然后继续加载后面的内容。从而加载时间快了,浏览体验也更好了) 减少http请求(合并文件,合并图片)This指向在JavaScript中,this通常指向的是我们正在执行的函数本身,或者是,指向该函数所属的对象。全局的this → 指向的是Window对象中的this → 指向其本身事件中this → 指向事件对象什么是jsonp工作原理是什么?他为什么不是真正的ajaxJsonp其实就是一个跨域解决方案。Js跨域请求数据是不可以的,但是js跨域请求js脚本是可以的。所以可以把要请求的数据封装成一个js语句,做一个方法的调用。跨域请求js脚本可以得到此脚本。得到js脚本之后会立即执行。可以把数据做为参数传递到方法中。就可以获得数据。从而解决跨域问题。jsonp原理:(动态创建script标签,回调函数)浏览器在js请求中,是允许通过script标签的src跨域请求,可以在请求的结果中添加回调方法名,在请求页面中定义方法,就可获取到跨域请求的数据。为什么不是真正的 ajax?    1、ajax和jsonp这两种技术在调用方式上"看起来"很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装;2、但ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取本页内容,而jsonp的核心则是动态添加3、所以说,其实ajax与jsonp的区别不在于是否跨域,ajax通过服务端代理一样可以实现跨域,jsonp本身也不排斥同域的数据的获取。4、还有就是,jsonp是一种方式或者说非强制协议,如同ajax一样,它也不一定非要json格式来传递数据,如果你愿意,字符换也行,只不过这样不利于jsonp提供公开服务。请掌握2种以上数组去重的方式使用indexof()方法使用lastindexof()方法  和indexof方法一样  indexof从头部开始匹配  lastindexof从尾部匹配ES6的set结构  set不接受重复数据 使用sort方法先将原数组排序,然后与相邻的比较,如果不同则存入新数组使用filiter和indexof方法使用ES6 的set和扩展运算符使用set和Array.from()方法             array.from可以将set结构转成数组用splice和双层循环使用includes方法深浅拷贝是什么如何实现?深拷贝:指针赋值,并且内容拷贝、                                                                浅拷贝:只是简单的指针赋值                                                                  数组浅拷贝: 如果是数组,可以使用数组的一些方法实现:slice(),concat()返回一个新数组的特性实现拷贝。用扩展运算符spread实现                                                                 数组深拷贝: JSON.parse(JSON.stringify())不仅适用于数组还适用于对象。不能拷贝函数,undefined,symbol。为什么js是弱类型语言弱类型语言实现相对于强类型语言来说的,在强类型语言中,变量类型有多种,比如int char float Boolean  不同类型相互转换有时需要强制转换,而jacascript只有一种类型var,为变量赋值时会自动判断类型并转换,所以是弱类型语言。怎么转换less为css用node将less文件生成为css文件2.用webstorm自动生成echarts使用最多的是什么图表及图表组合For循环与map循环有什么区别For遍历对象自身的和继承可枚举的属性,也就是说会包括哪些原型链上的属性Map方法不会对空数组进行检测,map会返回一个新数组,不会对原数组产生影响请写出一个简单的类与继承创建类有三种:使用function和this关键字原型方法  用prototype和this关键字使用object.create()方法构造继承有六种:原型继承        借用构造函数继承        组合继承        原型式继承    寄生式继承         寄生组合式继承同步与异步的区别/阻塞与非阻塞区别同步(阻塞的)异步(非阻塞)比如:同步,咱两在一起上班,到吃饭时间了,我去喊你一起吃饭,你很忙,我就坐着等你忙完再一起去吃饭         异步,咱两在一起上班,到吃饭时间了,我去喊你一起吃饭,你很忙,我就先自己去吃了,你忙完了再去吃饭同步(阻塞)异步(非阻塞)这两个关注的是程序在等待调用结果时的状态重绘和回流是什么回流:当render tree中的一部分或者全部因为元素的规模尺寸,布局,隐藏等改变而需要重新构建,这就叫回流,每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候一定会发生回流,因为要构建render tree在回流的时候,浏览器会使渲染树中收到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,这就是重绘当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,不会影响布局,就叫重绘http是什么?有什么特点http叫做超文本传输协议,是互联网应用最广泛的一种网络协议特点:基于请求-响应的模式   无状态保存   无连接HTTP协议和HTTPS区别http是超文本传输协议,信息是明文传输,https是具有安全性的ssl解密传输协议http和https连接方式完全不同,端口也不同,http是80,https是443http的连接很简单,是无状态的,https协议是由ssl+http协议构建的可进行加密传输,身份认证的网络协议,比http协议安全原型和继承,prototype,call和apply继承的区别(第一个参数是相同的,第二个的区别在哪)apply()接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。call()方法第一个参数与apply()方法相同,但传递给函数的参数必须列举出来。箭头函数与普通函数的区别箭头函数是匿名函数,不能作为构造函数,不能使用new箭头函数不能绑定arguments,要用rest参数解决箭头函数没有原型属性箭头函数的this永远指向其上下文的this,箭头函数不能绑定this,会捕获其所在的上下文的this值,作为自己的this值什么是js内存泄露?内存泄漏是指一块被分配的内存既不能使用又不能回收,直到浏览器进程结束释放内存的方法:赋值为null你如何对网站的文件和资源进行优化?1、文件合并(目的是减少http请求)2、文件压缩(目的是直接减少文件下载的体积)3、使用cdn托管资源4、使用缓存5、gizp压缩你的js和css文件6、meta标签优化(title,description,keywords)、heading标签的优化、alt优化             7、反向链接,网站外链接优化请简述ajax的执行过程 以及常见的HTTP状态码预加载和懒加载的区别,预加载在什么时间加载合适         预加载是指在页面加载完成之前,提前将所需资源下载,之后使用的时候从缓存中调用;懒加载是延迟加载,按照一定的条件或者需求等到满足条件的时候再加载对应的资源两者主要区别是一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。Jquery选择器有哪些一、基本选择器基本选择器是jQuery中最常用也是最简单的选择器,它通过元素的id、class和标签名等来查找DOM元素。1、ID选择器 #id描述:根据给定的id匹配一个元素, 返回单个元素(注:在网页中,id名称不能重复)示例:$("#test") 选取 id 为 test 的元素2、类选择器 .class描述:根据给定的类名匹配元素,返回元素集合示例:$(".test") 选取所有class为test的元素3、元素选择器 element描述:根据给定的元素名匹配元素,返回元素集合示例:$("p") 选取所有的元素4、*描述:匹配所有元素,返回元素集合示例:$("*") 选取所有的元素5、selector1,selector2,...,selectorN描述:将每个选择器匹配到的元素合并后一起返回,返回合并后的元素集合示例:$("p,span,p.myClass") 选取所有,和class为myClass的标签的元素集合二、层次选择器层次选择器根据层次关系获取特定元素。1、后代选择器示例:$("p span") 选取元素里的所有的元素(注:后代选择器选择父元素所有指定选择的元素,不管是儿子级,还是孙子级)2、子选择器 $("parent>child")示例:$("p>span") 选择元素下的所有元素 (注:子选择器只选择直属于父元素的子元素)3、同辈选择器 $("prev+next")描述:选取紧接在prev元素后的next元素,返回元素集合示例:$(".one+p") 选取class为one的下一个同辈元素集合4、同辈选择器 $("prev~siblings")描述:选取prev元素后的所有siblings元素,返回元素集合示例:$("#two~p")选取id为two的元素后所有同辈元素集合三、过滤选择器1>基本过滤选择器1、 :first描述:选取第一个元素,返回单个元素示例:$("p:first") 选取所有元素中第一个元素2、 :last描述:选取最后一个元素,返回单个元素示例:$("p:last") 选取所有元素中最后一个元素3、 :not(selector)描述:去除所有与给定选择器匹配的元素,返回元素集合示例:$("input:not(.myClass)") 选取class不是myClass的元素4、 :even描述:选取索引是偶数的所有元素,索引从0开始,返回元素集合5、 :odd描述:选取索引是奇数的所有元素,索引从0开始,返回元素集合6、 :eq(index)描述:选取索引等于index的元素,索引从0开始,返回单个元素7、 :gt(index)描述:选取索引大于index的元素,索引从0开始,返回元素集合8、 :lt(index)描述:选取索引小于于index的元素,索引从0开始,返回元素集合9、 :focus描述:选取当前获取焦点的元素2>内容过滤选择器1、:contains(text)描述:选取含有文本内容为text的元素,返回元素集合示例:$("p:contains('我')") 选取含有文本“我”的元素2、:empty描述:选取不包含子元素或者文本元素的空元素,返回元素集合示例:$("p:empty") 选取不包含子元素或者文本元素的空元素()3、:has(selector)描述:选取含有选择器所匹配的元素的元素,返回元素集合示例:$("p:has(p)") 选取含有元素的元素(<p/>)4、:parent描述:选取含有子元素或者文本的元素,返回元素集合示例:$("p:parent") 选取含有子元素或者文本元素的元素(<p/>或者文本)3>可见性过滤选择器1、:hidden描述:选取所有不可见的元素,返回元素集合2、:visible描述:选取所有可见的元素,返回元素集合4>属性过滤选择器(返回元素集合)1、[attribute]示例:$("p[id]") 选取拥有id属性的p元素2、[attribute=value]示例:$("input[name=text]") 选取拥有name属性等于text的input元素3、[attribute!=value]示例:$("input[name!=text]") 选取拥有name属性不等于text的input元素4、[attribute^=value]示例:$("input[name^=text]") 选取拥有name属性以text开始的input元素5、[attribute$=value]示例:$("input[name$=text]") 选取拥有name属性以text结束的input元素6、[attribute*=value]示例:$("input[name*=text]") 选取拥有name属性含有text的input元素7、[attribute~=value]示例:$("input[class~=text]") 选取拥有class属性以空格分割的值中含有text的input元素8、attribute1[attributeN]描述:合并多个属性过滤选择器5>表单对象属性过滤选择器(返回元素集合)1、:enabled描述:选取所有可用元素2、:disabled描述:选取所有不可用元素3、:checked描述:选取所有被选中的元素(单选框,复选框)示例:$("input:checked") 选取所有被选中的元素4、:selected描述:选取所有被选中的选项元素(下拉列表)示例:$("select option:selected") 选取所有被选中的选项元素Jquery插入节点的方法append()  向每个匹配的元素内部追加内容appendTo()  将所有匹配的元素追加到指定元素中,实际上,使用该方法是颠倒了常规的$(A).append(B)的操作 将A追加到B中prepend()  向每个匹配的元素内部前置内容prependTo()  将所有匹配的元素前置到指定的元素中。实际上,使用该方法是颠倒了常规的$(A).prepend(B)的操作,即不是将B前置到A中,而是将A前置到B中after()  在每个匹配的元素之后插入内容insertAfter()  将所有匹配的元素插入到指定元素的后面。实际上,使用该方法是颠倒了常规的$(A).after(B)的操作,即不是讲B插入到A后面,而是将A插入到B后面before()  在每个匹配的元素之前插入内容insertBefore()  将所有匹配的元素插入到指定的元素的前面。实际上,使用该方法是颠倒了常规的$(A).before(B)的操作,即不是将B插入到A前面,而是将A插入到B前面Js的函数节流和函数防抖的区别函数节流是指一定时间内js方法只执行一次。函数防抖是指频繁触发的情况下,只有足够的空闲时间,才执行代码一次函数节流是 声明一个变量当标志位,记录当前代码是否在执行,如果正在执行,取消这次方法执行,直接return,如果空闲,正常触发方法执行函数防抖是需要一个延时器来辅助实现,延迟执行需要执行的代码,如果方法多次触发,把上次记录的延迟执行代码用cleartimeout清除掉,重新开始,如果计时完毕,没有方法来访问触发,则执行代码Get和post不同Get是从服务器上获取数据,post是向服务器传送数据在客户端,get通过url提交数据,数据在url中可以看到,post方式,数据放在html header中提交安全性问题Get提交数据最多只能有1024字节,post没有限制什么是csrf攻击Csrf(跨站点请求伪造) 攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份再攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的Js中手写一个深拷贝什么时候用深拷贝 /浅拷贝无论深浅,都是需要的,当深拷贝发生时通常表明存在着一个聚合关系,当浅拷贝发生时,通常表明存在着相识关系举个简单例子:当实现一个组合模式Composite  Pattern时通常都会实现深拷贝当实现一个观察者模式Observer  Pattern,时,就需要实现浅拷贝Vue相关1.Vue的核心是什么Vue是一套构建用户界面的渐进式自底向上增量开发的MVVM框架,vue的核心只关注视图层,核心思想:  数据驱动(视图的内容随着数据的改变而改变)组件化(可以增加代码的复用性,可维护性,可测试性,提高开发效率,方便重复使用,体现了高内聚低耦合)2.请简述你对vue的理解Vue是一套构建用户界面的渐进式的自底向上增量开发的MVVM框架,核心是关注视图层,vue的核心是为了解决数据的绑定问题,为了开发大型单页面应用和组件化,所以vue的核心思想是数据驱动和组件化,这里也说一下MVVM思想,MVVM思想是 模型  视图  vm是v和m连接的桥梁,当模型层数据修改时,VM层会检测到,并通知视图层进行相应修改3.请简述vue的单向数据流父级prop的更新会向下流动到子组件中,每次父组件发生更新,子组件所有的prop都会刷新为最新的值数据从父组件传递给子组件,只能单向绑定,子组件内部不能直接修改父组件传递过来的数据,(可以使用data和computed解决)Vue常用的修饰符有哪些修饰符:.lazy 改变后触发,光标离开input输入框的时候值才会改变                   .number 将输出字符串转为number类型                   .trim 自动过滤用户输入的首尾空格事件修饰符:.stop 阻止点击事件冒泡,相当于原生js中的event.stopPropagation().prevent 防止执行预设的行为,相当于原生js中event.preventDefault().capture 添加事件侦听器时使用事件捕获模式,就是谁有该事件修饰符,就先触发谁.self  只会触发自己范围内的事件,不包括子元素.once 只执行一次键盘修饰符:.enter 回车键          .tab 制表键             .esc返回键     .space 空格键.up向上键                .down 向下键        .left向左建    .right向右键系统修饰符:.ctrl .alt   .shift  .meta5.v-text与{{}}与v-html区别{{}} 将数据解析为纯文本,不能显示输出htmlv-html 可以渲染输出htmlv-text 将数据解析为纯文本,不能输出真正的html,与花括号的区别是在页面加载时不显示双花括号v-text 指令:作用:操作网页元素中的纯文本内容。{{}}是他的另外一种写法v-text与{{}}区别:v-text与{{}}等价,{{}}叫模板插值,v-text叫指令。有一点区别就是,在渲染的数据比较多的时候,可能会把大括号显示出来,俗称屏幕闪动:6.v-on可以绑定多个方法吗可以  如果绑定多个事件,可以用键值对的形式 事件类型:事件名         如果绑定是多个相同事件,直接用逗号分隔就行7.Vue循环的key作用Key值的存在保证了唯一性,Vue在执行时,会对节点进行检查,如果没有key值,那么vue检查到这里有dom节点,就会对内容清空并赋新值,如果有key值存在,那么会对新老节点进行对比,比较两者key是否相同,进行调换位置或删除操作8.什么是计算属性计算属性是用来声明式的描述一个值依赖了其他的值,当它依赖的这个值发生改变时,就更新DOM当在模板中把数据绑定到一个计算属性上时,vue会在它依赖的任何值导致该计算属性改变时更新DOM每个计算属性都包括一个getter和setter,读取时触发getter,修改时触发setterVue单页面的优缺点单页面spa优点:前后端分离  用户体验好 一个字  快  内容改变不需要重新加载整个页面缺点:不利于seo,  初次加载时耗长(浏览器一开始就要加载html  css js ,所有的页面内容都包含在主页面中) ,页面复杂度提高了,导航不可用Vuex是什么?怎么使用?在那种场景下使用Vuex是一个专为vue.js应用程序开发的状态管理模式,通过创建一个集中的数据存储,方便程序中的所有组件进行访问,简单来说 vuex就是vue的状态管理工具Vuex有五个属性 state  getters  mutations  actions  modulesState就是数据源存放地,对应一般vue对象的data,state里面存放的数据是响应式的,state数据发生改变,对应这个数据的组件也会发生改变  用this.$store.state.xxx调用Getters 相当于store的计算属性,主要是对state中数据的过滤,用this.$store.getters.xxx调用Mutations 处理数据逻辑的方法全部放在mutations中,当触发事件想改变state数据的时候使用mutations,用this.$store.commit调用,给这个方法添加一个参数,就是mutation的载荷(payload)Actions 异步操作数据,但是是通过mutation来操作 用this.$store.dispatch来触发,actions也支持载荷使用场景:组件之间的状态,登录状态,加入购物车,音乐播放Vuex使用流程:下载vuex 在src下创建store以及index.js引入vue和vuex, 使用vuex  ,导出实例对象在main.js中引入,在.vue文件中使用Vue中路由跳转方式(声明式/编程式)Vue中路由跳转有两种,分别是声明式和编程式用js方式进行跳转的叫编程式导航   this.$router.push()用router-link进行跳转的叫声明式   router-view 路由出口,路由模板显示的位置路由中name属性有什么作用?在router-link中使用name导航到对应路由使用name导航的同时,给子路由传递参数12.vue跨域的解决方式1.后台更改header2.使用jq提供jsonp3.用http-proxy-middleware(配置代理服务器的中间件)13.Vue的生命周期请简述vue的生命周期就是vue实例创建到实例销毁的过程。期间会有8个钩子函数的调用。beforeCreate(创建实例)created(创建完成)、beforeMount(开始创建模板)mounted(创建完成)、beforeUpdate(开始更新)updated(更新完成)、beforeDestroy(开始销毁)destroyed(销毁完成)Vue生命周期的作用给了用户在不同阶段添加自己的代码的机会15.DOM渲染在那个生命周期阶段内完成DOM渲染在mounted周期中就已经完成Vue路由的实现前端路由就是更新视图但不请求页面,利用锚点完成切换,页面不会刷新官网推荐用vue-router.js来引入路由模块定义路由组件定义路由,使用component进行路由映射组件,用name导航到对应路由创建router实例,传入routes配置创建和挂载根实例用router-link设置路由跳转17.Vue路由模式hash和history,简单讲一下         Hash模式地址栏中有#,history没有,history模式下刷新,会出现404情况,需要后台配置使用  JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值可以使用 hashchange 事件来监听 hash 值的变化HTML5 提供了 History API 来实现 URL 的变化。其中最主要的 API 有以下两个:history.pushState() 和 history.repalceState()。这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录。18.Vue路由传参的两种方式,params和query方式与区别动态路由也可以叫路由传参,就是根据不同的选择在同一个组件渲染不同的内容用法上:query用path引入,params用name引入,接收参数都是类似的,分别是this.$route.query.name和this.$route.params.nameurl展示上:params类似于post,query类似于get,也就是安全问题,params传值相对更安全点,query通过url传参,刷新页面还在,params刷新页面不在了19.Vue数据绑定的几种方式1.单向绑定  双大括号 {{}}  html内字符串绑定2.v-bind绑定  html属性绑定3.双向绑定 v-model4.一次性绑定  v-once 依赖于v-model20.Vue注册一个全局组件Vue.componnet(“组件的名字”{对象   template 组建的内容})21.Vue的路由钩子函数/路由守卫有哪些全局守卫:beforeEach(to,from,next)和afterEach(to,from)路由独享守卫:beforeEnter组件内的守卫:路由进入/更新/离开之前 beforeRouterEnter/update/leave22.Vue中如何进行动态路由设置?有哪些方式?怎么获取传递过来的数据?动态路由也可以叫路由传参,动态路由有query和prrams两种方式传参query用path引入,params用name引入,query用this.$route.query.name接收参数params用this.$route.params.name接收参数23.Elementui中的常用组件有哪些?请简述你经常使用的 并且他们的属性有哪些?Container布局容器 外层容器顶栏容器侧边栏容器主要内容容器底栏容器Dropdown  下拉菜单 下拉按钮    下拉菜单    下拉项Table  表格Tabs  标签页Form  表单Pagination 分页Message  消息提示24.Vue-cli中如何自定义指令25.Vue中指令有哪些v-for:循环数组,对象,字符串,数字v-on:绑定事件监听v-bind:动态绑定一个或者多个属性v-model:表单控件或者组件上创建双向绑定v-if  v-else  v-else-if  条件渲染v-show  根据表达式真假,切换元素的displayv-html 更新元素的innerhtmlv-text 更新元素的textcontentv-pre 跳过这个元素和子元素的编译过程v-clock 这个指令保持在元素上知道关联实例结束编译v-once  只渲染一次26.Vue如何定义一个过滤器过滤器本质就是一个有参数有返回值的方法new Vue({    filters:{      myCurrency:function(myInput){        return 处理后的数据      }    }})使用方法:{{表达式 | 过滤器}}过滤器高级用法:可以指定参数,告诉过滤器按照参数进行数据的过滤27.对vue 中keep-alive的理解概念:keep-alive是vue的内置组件,当它动态包裹组件时,会缓存不活动的组件实例,它自身不会渲染成一个DOM元素也不会出现在父组件链中作用:在组件切换过程中将状态保留在内存中,防止重复渲染DOM,减少加载时间以及性能消耗,提高用户体验。生命周期函数:Activated在keep-alive组件激活时调用,deactivated在keep-alive组件停用时调用28.如何让组件中的css在当前组件生效在styled中加上scoped29.Vue生命周期一共几个阶段创建  加载   更新   销毁Beforecreate创建前Created   创建后Beforemount   加载前Mounted 加载后Beforeupdate 更新前Updated 更新后Beforedestroy 销毁前Destroyed  销毁后页面第一次加载会触发 beforecreate  created   beforemount   mountedDOM渲染在mounted周期中就已经完成30.Mvvm与mvc的区别Mvc模型视图控制器,视图是可以直接访问模型,所以,视图里面会包含模型信息,mvc关注的是模型不变,所以,在mvc中,模型不依赖视图,但是视图依赖模型Mvvm 模型  视图  和vm  vm是作为模型和视图的桥梁,当模型层数据改变,vm会检测到并通知视图层进行相应的修改31.Vue组件中的data为什么是函数Data是一个函数时,每个组件实例都有自己的作用域,每个实例相互独立,不会相互影响如果是引用类型(对象),当多个组件共用一个数据源时,一处数据改变,所有的组件数据都会改变,所以要利用函数通过return返回对象的拷贝,(返回一个新数据),让每个实例都有自己的作用域,相互不影响。32.Vue双向绑定的原理Vue双向绑定就是:数据变化更新视图,视图变化更新数据Vue数据双向绑定是通过数据劫持和观察者模式来实现的,数据劫持,object.defineproperty它的目的是:当给属性赋值的时候,程序可以感知到,就可以控制改变属性值观察者模式 当属性发生改变的时候,使用该数据的地方也发生改变33.Vue中组件怎么传值正向:父传子  父组件把要传递的数据绑定在属性上,发送,子组件通过props接收逆向:子传父        子组件通过this.$emit(自定义事件名,要发送的数据),父组件设置一个监听事件来接收,然后拿到数据兄弟:eventbus  中央事件总线通过Vuex34.Bootstrap的原理网格系统的实现原理,通过定义容器大小,平分12份,(24份或者32份),再调整内外边距,结合媒体查询,就成了强大的响应式网格系统。比如  row  col-xs-436.如果一个组件在多个项目中使用怎么办37.槽口请简述大概分这几点,首先槽口(插槽)可以放什么内容?放在哪?什么作用?可以放任意内容,在子组件中使用,是为了将父组件中的子组件模板数据正常显示。具名插槽和匿名插槽,作用域插槽,说白了就是在组件上的属性,可以在组件元素内使用,可以在父组件中使用slot-scope从子组件获取数据38.Watch请简述Watch的作用是监控一个值的变化,并调用因为变化需要执行的方法39.Vant Ui请简述下轻量、可靠的移动端 Vue 组件库40.计算属性与watch区别Computed  watch   区别就是computed的缓存功能,当无关数据数据改变时,不会重新计算,直接使用缓存中的值。计算属性是用来声明式的描述一个值依赖了其他的值,当所依赖的值后者变量发生变化时,计算属性也跟着改变,Watch监听的是在data中定义的变量,当该变量变化时,会触发watch中的方法41.mvvm框架是什么?它和其它框架(jquery)的区别是什么?哪些场景适合?Mvvm和其他框架的区别是  vue数据驱动  通过数据来显示视图而不是节点操作适用于数据操作比较多的场景42.Vue首屏加载慢的原因,怎么解决的,白屏时间怎么检测,怎么解决白屏问题首屏加载慢的原因:第一次加载页面有很多组件数据需要渲染解决方法:1.路由懒加载  component:()=>import(“路由地址”)2.ui框架按需加载3.gzip压缩白屏时间检测:????解决白屏问题:①使用v-text渲染数据②使用{{}}语法渲染数据,但是同时使用v-cloak指令(用来保持在元素上直到关联实例结束时候进行编译),v-cloak要放在什么位置呢,v-cloak并不需要添加到每个标签,只要在el挂载的标签上添加就可以43.Vue双数据绑定过程中,这边儿数据改变了怎么通知另一边改变数据劫持和观察者模式Vue数据双向绑定是通过数据劫持和观察者模式来实现的,数据劫持,object.defineproperty它的目的是:当给属性赋值的时候,程序可以感知到,就可以控制属性值的有效范围,可以改变其他属性的值观察者模式它的目的是当属性发生改变的时候,使用该数据的地方也发生改变44.Vuex流程在vue组件里面,通过dispatch来触发actions提交修改数据的操作,然后通过actions的commit触发mutations来修改数据,mutations接收到commit的请求,就会自动通过mutate来修改state,最后由store触发每一个调用它的组件的更新45.Vuex怎么请求异步数据1.首先在state中创建变量2.然后在action中调用封装好的axios请求,异步接收数据,commit提交给mutationsMutations中改变state中的状态,将从action中获取到的值赋值给state46.Vuex中action如何提交给mutation的Action函数接收一个与store实例具有相同方法和属性的context对象,可以调用context.commit提交一个mutation,或者通过context.state和context.getters获取state和getters47.Route与router区别1.   router是VueRouter的一个对象,通过Vue.use(VueRouter)和VueRouter构造函数得到一个router的实例对象,这个对象中是一个全局的对象,他包含了所有的路由包含了许多关键的对象和属性。2.route是一个跳转的路由对象,每一个路由都会有一个route对象,是一个局部的对象,可以获取对应的name,path,params,query等49.vuex的State特性是?State就是数据源的存放地State里面的数据是响应式的,state中的数据改变,对应这个数据的组件也会发生改变State通过mapstate把全局的state和getters映射到当前组件的计算属性中50.vuex的Getter特性是?Getter可以对state进行计算操作,它就是store的计算属性Getter可以在多组件之间复用如果一个状态只在一个组件内使用,可以不用getters51.vuex的Mutation特性是?更改vuex  store中修改状态的唯一办法就是提交mutation,可以在回调函数中修改store中的状态52.vuex的actions特性是?Action类似于mutation,不同的是 action提交的是mutation,不是直接变更状态,可以包含任意异步操作54.vuex的优势优点:解决了非父子组件的通信,减少了ajax请求次数,有些可以直接从state中获取缺点:刷新浏览器,vuex中的state会重新变为初始状态,解决办法是vuex-along,得配合计算属性和sessionstorage来实现55.Vue路由懒加载(按需加载路由)56.v-for与v-if优先级首先不要把v-if与v-for用在同一个元素上,原因:v-for比v-if优先,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候。v-for 比 v-if 具有更高的优先级请写出饿了么5个组件弹窗对话日历表<el-progress:percentage="0">进度条开关React相关fetch VS ajax VS axios传统 Ajax 指的是 XMLHttpRequest(XHR), 最早出现的发送后端请求技术,隶属于原始js中,核心使用XMLHttpRequest对象,多个请求之间如果有先后关系的话,就会出现回调地狱。JQuery ajax 是对原生XHR的封装axios 是一个基于Promise ,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范,fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。React事件处理---修改this指向方式1:通过bind方法进行原地绑定,从而改变this指向方式2:通过创建箭头函数方式3:在constructor中提前对事件进行绑定方式4:将事件调用的写法改为箭头函数的形式请简述你对react的理解React起源于facebook,react是一个用于构建用户界面的js库特点:声明式设计:react采用范式声明,开发者只需要声明显示内容,react就会自动完成高效: react通过对dom的模拟(也就是虚拟dom),最大限度的减少与dom的交互灵活: react可以和已知的库或者框架很好配合组件: 通过react构建组件,让代码更容易复用,能够很好应用在大型项目开发中,把页面功能拆分成小模块  每个小模块就是组件单向数据流:  react是单向数据流,数据通过props从父节点传递到子节点,如果父级的某个props改变了,react会重新渲染所有的子节点react组件之间的数据传递正向传值用props逆向传值用函数传值 通过事件调用函数传递同级传值用pubsub-js用pubsub.publish(事件名,数据)抛出数据用pubsub.subscribe(监听的事件,()=){})接收数据跨组件传递  用context  要使用context进行跨组件传值就需要使用createContext()方法,这个方法有两个对象  provider  生产者   Consumer 消费者Vue与react区别相同点:都支持服务器渲染都有虚拟dom,组件化开发,通过props参数进行父子组件数据的传递,都实现webcomponent规范都是数据驱动视图都有状态管理,react有redux,vue有vuex都有支持native’的方案 react有react native  vue有weex不同点:react严格上只针对mvc的view层,vue是mvvm模式虚拟dom不一样,vue会跟踪每一个组件的依赖关系,不需要重新渲染整个dom组件树,而react不同,当应用的状态被改变时,全部组件都会重新渲染,所以react中用shouldcomponentupdate这个生命周期的钩子函数来控制组件写法不一样 ,react是jsx和inline style ,就是把html和css全写进js中,vue则是html,css ,js在同一个文件数据绑定不一样,vue实现了数据双向绑定,react数据流动是单向的在react中,state对象需要用setstate方法更新状态,在vue中,state对象不是必须的,数据由data属性在vue对象中管理请简述虚拟dom与diff算法虚拟DOM也就是常说的虚拟节点,它是通过js的object对象模拟DOM中的节点,然后再通过特定的渲染方法将其渲染成真实的DOM节点。频繁的操作DOM,或大量造成页面的重绘和回流Diff算法:把树形结构按照层级分解,只比较同级元素,给列表结构的每个单元添加唯一的key值,方便比较你对组件的理解可组合,可复用,可维护,可测试调用 setState 之后发生了什么?React在调用setstate后,react会将传入的参数对象和组件当前的状态合并,触发调和过程,在调和过程中,react会根据新的状态构建react元素树重新渲染整个UI界面,在得到元素树之后,react会自动计算新老节点的差异,根据差异对界面进行最小化重新渲染react 生命周期函数componentWillMount  组件渲染之前调用componentDidMount  在第一次渲染之后调用componentWillReceiveProps  在组件接收到一个新的props时调用shouldComponentUpdate  判断组件是否更新htmlcomponentWillupdate  组件即将更新html时调用componentDidupdate  在组件完成更新后立即调用componentWillUnmount  在组件移除之前调用为什么虚拟 dom 会提高性能?(必考)虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能(组件的)状态(state)和属性(props)之间有何不同State与props区别Props是一个从外部传进组件的参数,主要作用就是父组件向子组件传递数据,但是props对于使用它的组件来说是只读的,一旦赋值不能修改,只能通过外部组件主动传入新的props来重新渲染子组件State 一个组件的显示形态可以由数据状态和外部参数决定,外部参数是props,数据状态就是state,首先,在组件初始化的时候,用this.state给组件设定一个初始的state,在第一次渲染的时候就会用这个数据来渲染组件,state不同于props一点时,state可以修改,通过this.setState()方法来修改stateshouldComponentUpdate 是做什么的这个react生命周期钩子函数是来解决这个问题:在更新数据的时候用setState修改整个数据,数据变了之后,遍历的时候所有内容都要被重新渲染,数据量少还好,数据量大就会严重影响性能解决办法:1.shouldcomponentupdate 在渲染前进行判断组件是否更新,更新了再渲染2.purecomponent(纯组件)省去了虚拟dom生成和对比的过程  在类组件中使用3.react.memo() 类似于纯组件 在无状态组件中使用react diff 原理它是基于三个策略:tree diff  web UI中dom节点跨层级的移动操作特别少,可以忽略不计component diff 拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件会生成不同的树形结构element diff 对于同一层级的一组子节点,他们可以通过唯一的id进行区分何为受控组件React负责渲染表单的组件,值是来自于state控制的,输入表单元素称为受控组件调用 super(props) 的目的是什么Super()调用父类的构造方法,有super,组件才有自己的this,在组件全局中都可以使用this,如果只是constructor而不执行super,之后的this都是错的,super继承父组件的thisReact 中构建组件的方式自定义组件:函数组件或者无状态组件  组件首字母大写类组件:一个类组件必须实现一个render方法,这个方法必须返回一个jsx元素,要用一个外层的元素把所有内容包裹起来小程序相关的小程序的优势无需下载安装,直接使用,运行速度快,项目搭建迅速,短小精悍,每个app源代码不超过2mb小程序的页面构成(4个文件)Index.js   index.json   index.wxml    index.wxss小程序的生命周期Onload   onready    onshow    onhide   onunload  Onpulldownrefresh    onreachbottom    onshareappmessage小程序如何请求数据用request如何提高小程序的首屏加载时间提前请求:异步数据数据请求不需要等待页面渲染完成利用缓存:利用storage API对异步请求数据进行缓存,二次启动时先利用缓存数据渲染页面,再进行后台更新避免白屏:先展示页面骨架和基础内容及时反馈:及时地对需要用户等待的交互操作给出反馈,避免用户以为小程序没有响应性能优化:避免不当使用setdata和onpagescroll请简述你经常使用的小程序的组件View  icon   text  image   swiper   navigator  input   button   mapWxss与css的区别请简述Wxss新增了尺寸单位 rpx提供了全局样式和局部样式Wxss仅支持部分css选择器  id’  class  元素等小程序如何实现响应式Rpx怎么优化小程序提高页面加载速度用户行为预测减少默认data的大小组件化方案自主获知自己的服务器小程序如何显示用户头像与用户名传统接口wx.getuserinfo 目前可以用,需要用户授权,使用时会有官方发提示,这个方法需要升级最新方法:open-data标签,使用这个标签可以不用用户授权直接获取头像和用户名,可以在button中将opendata作为属性写进去,写个点击事件就直接获取到了请谈谈小程序的双向绑定和vue的异同?Vue双向绑定是通过数据拦截和观察者模式,通过this.value获取值,小程序是通过触发表单元素绑定的方法,在方法中用this.setData({key:value})来取值小程序中传参是怎么传的和vue类比介绍说一下微信小程序的适配问题小程序页面间有哪些传递数据的方法?你是怎么封装微信小程序的数据请求的说一下微信小程序的适配问题小程序跳转页面的方式微信小程序如何跳转到其他小程序小程序加载过慢的解决方式其他Typescript是什么 请简述?Typescript 与javascript 的优势?Webpack与gulp区别Gulp是一种能够优化前端开发流程的工具,webpack是一种模块化的解决方案 (grunt)请简述webpack中的loaders与plugin的区别什么是loaders,loaders是文件加载器,能够加载资源文件,并对这些文件进行处理,例如,编译,压缩等,最终一起打包到指定文件中。什么是plugin,在webpack运行的生命周期会有许多事件,plugin可以监听这些事件区别:加载器是用来加载文件的,webpack本身只能加载js文件(内置babel-loader),加载其他文件就需要安装别的loader,比如:css-loader  file-loaderPlugin是扩展webpack功能的,通过plugin  ,webpack可以实现loader不能完成的复杂功能怎么提升页面性能?性能优化有哪些?Node使用来做什么的能够在服务器端运行JavaScriptWebpack:入口,出口,加载器,插件说一下webpack的打包原理Webpack是把项目当做一个整体,通过给定一个主文件,webpack将从这个主文件开始找到项目中所有依赖的文件,使用loaders类处理,最后打包成一个或者多个浏览器可识别的js文件Commonjs ES6模块区别?common模块是拷贝,可以修改值,es6模块是引用,只读状态,不能修改值commonjs模块是运行时加载,es6模块是编译时输出接口Git如何使用/常用指令有哪些你们后台用的是什么技术你的项目比较小为什么还是用vue全家桶请简述你在项目中使用的ui框架前端性能优化的方式越多越好什么是cors说一下对websocked的理解Websocked是一种双向通信协议,在建立连接后,websocked服务器和浏览器都能主动向对方发送或者接收数据,websocked需要类似于tcp的客户端和服务器通过握手连接,连接成功后才能互相通信后台传递过来的数据是那些谈谈Ajax,fetch,axios的区别企业中的项目流程1.WEB前端项目开发流程项目需求分析这个环节是由项目经理完成,项目经理首先和客户进行交流,了解客户的需求,然后分析项目的可行性,如果项目可以被实现,项目经理写出项目需求文档交给设计师完成后续的开发。页面设计/项目选型这个环节主要是UI设计师参与,UI设计师根据产品需求分析文档,对产品的整体美术风格、交互设计、界面结构、操作流程等做出设计。负责项目中各种交互界面、图标、LOGO、按钮等相关元素的设计与制作。并且确定使用技术编码这个部分由程序员来实现。(程序员分为WEB前端开发工程师和后台开发工程师。前端开发人员主要做我们可以在网页上看的见的页面,后台就做一些我们看不见的管理系统以及功能的实现。)程序员根据UI设计师的设计,用编码来完成整个项目的各个功能。测试这部分由程序测试员来完成。程序测试员主要就是测试寻找程序还存在的bug,一般来说刚编码完成的程序都是存在问题的,就需要测试人员反复不断的测试并将存在问题的测试结果交给编码人员进行bug的修复。等到几乎所有bug修复完成,这个项目差不多就可以上线了。维护程序的维护是整个项目的最后一个阶段,但也是耗时最多,成本最高最高的的一个阶段。程序的维护包括程序上线后后续bug的修复和程序版本的更新。更换接口域名就是在开发的时候调用的后台接口是后台测试的接口  项目上线后要把请求的接口替换成上线的域名经常使用的工具代码管理平台:github 码云需求发布平台:钉钉任务,禅道Ui交互平台:蓝湖产品原型工具:axure企业邮箱:阿里 腾讯企业邮箱后台语言:java php python(西安不多)4大公司和小公司开发的区别大型外包公司更加流程化,人员多,沟通少,项目交付后不需要自己维护,采用瀑布开发模式(以文档为主)小型公司:人少 需求经常改变 沟通方便 采用敏捷开发(快速推出v1版本 ,之后迭代)5.前后台分离怎么测试?奇葩问题你们后端开发用的什么?移动端如何刷新页面?项目初始化构建流程项目中自己觉得骄傲的地方?说说自己的缺点热部署是什么?用户有多少怎么调用接口(是怎么跟后台沟通的)单元格测试是怎么做的开发环境,测试环境,上线环境的环境变量你们在开发中是如何处理的​
OpenCV4之C++入门详解 (下)
2.20、图像旋转公式:i′=icosθ−jsinθi′=icosθ−jsin⁡θi′=isinθ+jcosθi′=isin⁡θ+jcosθquickopencv.h#pragma once #include<opencv2/opencv.hpp> using namespace cv; class QuickDemo { public: void colorSpace_Demo(Mat &image); void mat_creation_demo(); void pixel_visit_demo(Mat &image); void operators_demo(Mat &image); void tracking_bar_demo(Mat &image); void key_demo(Mat &image); void color_style_demo(Mat &image); void bitwise_demo(Mat &image); void channels_demo(Mat &image); void inrange_demo(Mat &image); void pixel_statistic_demo(Mat &image); void form_paint_random(); void polyline_drawing_demo(); void mouse_drawing_demo(Mat &image); void norm_demo(Mat &image); void resize_demo(Mat &image); void flip_demo(Mat &image); void rotate_demo(Mat &image); };quickdemo.cppvoid QuickDemo::rotate_demo(Mat &image) { Mat dst, M; int w = image.cols; int h = image.rows; M = getRotationMatrix2D(Point2f(w / 2, h / 2), 45, 1.0); double cos = abs(M.at<double>(0, 0)); double sin = abs(M.at<double>(0, 1)); int nw = cos * w + sin * h; int nh = sin * w + cos * h; M.at<double>(0, 2) += (nw / 2 - w / 2); M.at<double>(1, 2) += (nh / 2 - h / 2); warpAffine(image, dst, M, Size(nw, nh), INTER_LINEAR, 0, Scalar(255, 255, 0)); imshow("旋转展示", dst); }test440.cpp#include<opencv2/opencv.hpp> #include<quickopencv.h> #include<iostream> using namespace cv; using namespace std; int main(int argc, char**argv) { // imread函数的第二个参数有很多,默认为IMREAD_COLOR,还有IMREAD_UNCHANGED,IMREAD_GRAYSCALE,IMREAD_ANYCOLOR等等,实现对不同图片的读取操作 // B,G,R Mat src = imread("D:/images/lena.jpg"); //Mat为matrix,二维图像都是Mat类型,第一个参数为图片绝对路径 if (src.empty()) { printf("could not load image...\n"); return -1; //namedWindow("输入窗口", WINDOW_FREERATIO); //不管图片大小,都能进行调整,图像很小可以不使用这个函数 imshow("输入窗口", src); //第一个参数为窗口名 QuickDemo qd; qd.rotate_demo(src); waitKey(0); //窗口停留时间,0为一直停留,数值为停留的毫秒数 destroyAllWindows(); //关闭所有打开的窗口 return 0; }效果2.21、视频文件摄像头使用quickopencv.h#pragma once #include<opencv2/opencv.hpp> using namespace cv; class QuickDemo { public: void colorSpace_Demo(Mat &image); void mat_creation_demo(); void pixel_visit_demo(Mat &image); void operators_demo(Mat &image); void tracking_bar_demo(Mat &image); void key_demo(Mat &image); void color_style_demo(Mat &image); void bitwise_demo(Mat &image); void channels_demo(Mat &image); void inrange_demo(Mat &image); void pixel_statistic_demo(Mat &image); void form_paint_random(); void polyline_drawing_demo(); void mouse_drawing_demo(Mat &image); void norm_demo(Mat &image); void resize_demo(Mat &image); void flip_demo(Mat &image); void rotate_demo(Mat &image); void video_demo(Mat &image); };quickdemo.cppvoid QuickDemo::video_demo(Mat &image) { //VideoCapture capture(0); //获取当前设备摄像头视频 VideoCapture capture("D:/images/video/example.mp4"); //获取该地址下的视频 Mat frame; while (true) { capture.read(frame); int h = frame.rows; //获取视频每一帧的宽高 int w = frame.cols; //flip(frame, frame, 1); //摄像头需要翻转,视频不需要翻转 if (frame.empty()) { break; resize(frame, frame, Size(w / 4, h / 4), 0, 0, INTER_LINEAR); //缩放视频 imshow("frame", frame); //显示缩放后的视频 colorSpace_Demo(frame); //调用色彩空间转换函数,将视频转换为灰度图像及HSV图像并显示 // TODO: do something.... int c = waitKey(10); if (c == 27) { //退出 break; //release capture.release(); }test440.cpp#include<opencv2/opencv.hpp> #include<quickopencv.h> #include<iostream> using namespace cv; using namespace std; int main(int argc, char**argv) { // imread函数的第二个参数有很多,默认为IMREAD_COLOR,还有IMREAD_UNCHANGED,IMREAD_GRAYSCALE,IMREAD_ANYCOLOR等等,实现对不同图片的读取操作 // B,G,R Mat src = imread("D:/images/lena.jpg"); //Mat为matrix,二维图像都是Mat类型,第一个参数为图片绝对路径 if (src.empty()) { printf("could not load image...\n"); return -1; //namedWindow("输入窗口", WINDOW_FREERATIO); //不管图片大小,都能进行调整,图像很小可以不使用这个函数 //imshow("输入窗口", src); //第一个参数为窗口名 QuickDemo qd; qd.video_demo(src); waitKey(0); //窗口停留时间,0为一直停留,数值为停留的毫秒数 destroyAllWindows(); //关闭所有打开的窗口 return 0; }效果2.22、视频处理与保存分辨率清晰度对照:视频显示格式分辨率尺寸名汉语简称480p、576pSD(Standard Definition)标清720pHD(High Definition)高清1080pFHD(Full High Definition)全高清2kQHD(Quad High Definition)四倍HD4kUHD(Ultra High Definition)超高清 或 4k UHD8kFUHD(Full Ultra High Definition)8k超高清 或 8k UHDquickopencv.h#pragma once #include<opencv2/opencv.hpp> using namespace cv; class QuickDemo { public: void colorSpace_Demo(Mat &image); void mat_creation_demo(); void pixel_visit_demo(Mat &image); void operators_demo(Mat &image); void tracking_bar_demo(Mat &image); void key_demo(Mat &image); void color_style_demo(Mat &image); void bitwise_demo(Mat &image); void channels_demo(Mat &image); void inrange_demo(Mat &image); void pixel_statistic_demo(Mat &image); void form_paint_random(); void polyline_drawing_demo(); void mouse_drawing_demo(Mat &image); void norm_demo(Mat &image); void resize_demo(Mat &image); void flip_demo(Mat &image); void rotate_demo(Mat &image); void video_demo(Mat &image); };quickdemo.cppvoid QuickDemo::video_demo(Mat &image) { //VideoCapture capture(0); //获取当前设备摄像头视频 VideoCapture capture("D:/images/video/example.mp4"); //获取该地址下的视频 int frame_width = capture.get(CAP_PROP_FRAME_WIDTH); //获取视频帧的宽高 int frame_height = capture.get(CAP_PROP_FRAME_HEIGHT); int count = capture.get(CAP_PROP_FRAME_COUNT); //获取视频的全部帧数 double fps = capture.get(CAP_PROP_FPS); //获取视频每秒帧数fps std::cout << "frame width: " << frame_width << std::endl; std::cout << "frame height: " << frame_height << std::endl; std::cout << "FPS: " << fps << std::endl; std::cout << "Number of Frames: " << count << std::endl; int fourcc = VideoWriter::fourcc('a','v','c','1'); //H264编码格式的fourcc code //VideoWriter writer(保存地址,fourcc编码格式code,帧率,保存视频的画面宽高,是否是彩色); VideoWriter writer("D:/test.mp4", fourcc, fps, Size(frame_width, frame_height), true); Mat frame; Mat frame1; while (true) { capture.read(frame); int h = frame.rows; //获取视频每一帧的宽高 int w = frame.cols; //flip(frame, frame, 1); //摄像头需要翻转,视频不需要翻转 if (frame.empty()) { break; resize(frame, frame1, Size(w / 4, h / 4), 0, 0, INTER_LINEAR); //缩放视频 imshow("frame", frame1); //显示缩放后的视频 //colorSpace_Demo(frame); //调用色彩空间转换函数,将视频转换为灰度图像及HSV图像并显示 writer.write(frame); //将每一帧保存到新的文件中 // TODO: do something.... int c = waitKey(10); if (c == 27) { //退出 break; //release capture.release(); writer.release(); }test440.cpp#include<opencv2/opencv.hpp> #include<quickopencv.h> #include<iostream> using namespace cv; using namespace std; int main(int argc, char**argv) { // imread函数的第二个参数有很多,默认为IMREAD_COLOR,还有IMREAD_UNCHANGED,IMREAD_GRAYSCALE,IMREAD_ANYCOLOR等等,实现对不同图片的读取操作 // B,G,R Mat src = imread("D:/images/lena.jpg"); //Mat为matrix,二维图像都是Mat类型,第一个参数为图片绝对路径 if (src.empty()) { printf("could not load image...\n"); return -1; //namedWindow("输入窗口", WINDOW_FREERATIO); //不管图片大小,都能进行调整,图像很小可以不使用这个函数 //imshow("输入窗口", src); //第一个参数为窗口名 QuickDemo qd; qd.video_demo(src); waitKey(0); //窗口停留时间,0为一直停留,数值为停留的毫秒数 destroyAllWindows(); //关闭所有打开的窗口 return 0; }效果运行过程中缺少H264编码器:1、出现错误2、到给出的网站中下载对应的dll文件3、将该文件下载解压后放到opencv的bin目录下4、再次运行正常注意:H264对应的fourcc code为avc1! 具体原因可以自行百度,与OpenCV遵守的开源协议有关2.23、图像直方图图像直方图解释:图像直方图使图像像素值的统计学特征,计算代价较小,具有图像平移、旋转、缩放不变性等众多优点,广泛地应用域图像处理的各个领域,特别是灰度图像的阈值分割、基于颜色的图像检索以及图像分类、反向投影跟踪。常见的分为灰度直方图、颜色直方图。Bins是指直方图的大小范围,对于像素值取在0~255之间的,最少有256个bin,此外还可以有16、32、48、128等,256除以bin的大小应该是整数倍。quickopencv.h#pragma once #include<opencv2/opencv.hpp> using namespace cv; class QuickDemo { public: void colorSpace_Demo(Mat &image); void mat_creation_demo(); void pixel_visit_demo(Mat &image); void operators_demo(Mat &image); void tracking_bar_demo(Mat &image); void key_demo(Mat &image); void color_style_demo(Mat &image); void bitwise_demo(Mat &image); void channels_demo(Mat &image); void inrange_demo(Mat &image); void pixel_statistic_demo(Mat &image); void form_paint_random(); void polyline_drawing_demo(); void mouse_drawing_demo(Mat &image); void norm_demo(Mat &image); void resize_demo(Mat &image); void flip_demo(Mat &image); void rotate_demo(Mat &image); void video_demo(Mat &image); void histogram_demo(Mat &image); };quickdemo.cppvoid QuickDemo::histogram_demo(Mat &image) { //三通道分离 std::vector<Mat> bgr_plane; //定义Mat类型的集合,用来保存三通道数据 split(image, bgr_plane); //将图像三通道分离,保存到定义的集合中 //定义参数变量 const int channels[1] = { 0 }; const int bins[1] = { 256 }; //总共256个灰度级别,bins[中括号内为该变量一共有几个数值] float hranges[2] = { 0,255 }; //每个通道取值范围0-255 const float* ranges[1] = { hranges }; Mat b_hist; Mat g_hist; Mat r_hist; //计算Blue,Green,Red通道的直方图 //calcHist(输入图像指针,输入图像个数,统计直方图的第几通道,mask()只统计不为0的部分,直方图计算的输出值,输出直方图的维度由channels指定,每个维度分成多少个区间,统计像素值的区间) calcHist(&bgr_plane[0], 1, 0, Mat(), b_hist, 1, bins, ranges); calcHist(&bgr_plane[1], 1, 0, Mat(), g_hist, 1, bins, ranges); calcHist(&bgr_plane[2], 1, 0, Mat(), r_hist, 1, bins, ranges); //显示直方图 int hist_w = 512; int hist_h = 400; int bin_w = cvRound((double)hist_w / bins[0]); Mat histImage = Mat::zeros(hist_h, hist_w,CV_8UC3); //归一化直方图数据 normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat()); normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat()); normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat()); //绘制直方图曲线 for (int i = 1; i < bins[0]; i++) { line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))), Point(bin_w*(i), hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, LINE_AA, 0); line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(g_hist.at<float>(i - 1))), Point(bin_w*(i), hist_h - cvRound(b_hist.at<float>(i))), Scalar(0, 255, 0), 2, LINE_AA, 0); line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))), Point(bin_w*(i), hist_h - cvRound(b_hist.at<float>(i))), Scalar(0, 0, 255), 2, LINE_AA, 0); //显示直方图 namedWindow("Histogram Demo", WINDOW_AUTOSIZE); imshow("Histogram Demo", histImage); }test440.cpp#include<opencv2/opencv.hpp> #include<quickopencv.h> #include<iostream> using namespace cv; using namespace std; int main(int argc, char**argv) { // imread函数的第二个参数有很多,默认为IMREAD_COLOR,还有IMREAD_UNCHANGED,IMREAD_GRAYSCALE,IMREAD_ANYCOLOR等等,实现对不同图片的读取操作 // B,G,R Mat src = imread("D:/images/flower.jpg"); //Mat为matrix,二维图像都是Mat类型,第一个参数为图片绝对路径 if (src.empty()) { printf("could not load image...\n"); return -1; //namedWindow("输入窗口", WINDOW_FREERATIO); //不管图片大小,都能进行调整,图像很小可以不使用这个函数 imshow("输入窗口", src); //第一个参数为窗口名 QuickDemo qd; qd.histogram_demo(src); waitKey(0); //窗口停留时间,0为一直停留,数值为停留的毫秒数 destroyAllWindows(); //关闭所有打开的窗口 return 0; }效果2.24、二维直方图quickopencv.h#pragma once #include<opencv2/opencv.hpp> using namespace cv; class QuickDemo { public: void colorSpace_Demo(Mat &image); void mat_creation_demo(); void pixel_visit_demo(Mat &image); void operators_demo(Mat &image); void tracking_bar_demo(Mat &image); void key_demo(Mat &image); void color_style_demo(Mat &image); void bitwise_demo(Mat &image); void channels_demo(Mat &image); void inrange_demo(Mat &image); void pixel_statistic_demo(Mat &image); void form_paint_random(); void polyline_drawing_demo(); void mouse_drawing_demo(Mat &image); void norm_demo(Mat &image); void resize_demo(Mat &image); void flip_demo(Mat &image); void rotate_demo(Mat &image); void video_demo(Mat &image); void histogram_demo(Mat &image); void histogram_2d_demo(Mat &image); };quickdemo.cppvoid QuickDemo::histogram_2d_demo(Mat &image) { //2D直方图 Mat hsv, hs_hist; cvtColor(image, hsv, COLOR_BGR2HSV); //输入图像色彩空间转化 int hbins = 30, sbins = 32; //H通道分为30个bins,S通道分为32个bins int hist_bins[] = { hbins,sbins }; float h_range[] = { 0,180 }; //图像H通道取值范围 float s_range[] = { 0,256 }; //图像S通道取值范围 const float* hs_ranges[] = { h_range,s_range }; int hs_channels[] = { 0,1 }; //calcHist(输入图像,输入图像个数,输入图像的通道,mask处理值不为0的部分,输出结果,输入直方图的维度,每个维度分成多少区间,每个维度取值范围,) calcHist(&hsv, 1, hs_channels, Mat(), hs_hist, 2, hist_bins, hs_ranges, true, false); double maxVal = 0; minMaxLoc(hs_hist, 0, &maxVal, 0, 0); //求图像全局最大最小值 int scale = 10; Mat hist2d_image = Mat::zeros(sbins*scale, hbins*scale, CV_8UC3); Mat hist2d_image_back = Mat::zeros(sbins*scale, hbins*scale, CV_8UC3); for (int h = 0; h < hbins; h++) { for (int s = 0; s < sbins; s++) { float binVal = hs_hist.at<float>(h, s); int intensity = cvRound(binVal * 255 / maxVal); rectangle(hist2d_image, Point(h*scale, s*scale), Point((h + 1)*scale - 1, (s + 1)*scale - 1), Scalar::all(intensity), applyColorMap(hist2d_image, hist2d_image_back, COLORMAP_JET); imshow("H-S Histogram", hist2d_image); imshow("H-S Histogram_back", hist2d_image_back); imwrite("D:/hist_2d.png", hist2d_image); }test440.cpp#include<opencv2/opencv.hpp> #include<quickopencv.h> #include<iostream> using namespace cv; using namespace std; int main(int argc, char**argv) { // imread函数的第二个参数有很多,默认为IMREAD_COLOR,还有IMREAD_UNCHANGED,IMREAD_GRAYSCALE,IMREAD_ANYCOLOR等等,实现对不同图片的读取操作 // B,G,R Mat src = imread("D:/images/flower.jpg"); //Mat为matrix,二维图像都是Mat类型,第一个参数为图片绝对路径 if (src.empty()) { printf("could not load image...\n"); return -1; //namedWindow("输入窗口", WINDOW_FREERATIO); //不管图片大小,都能进行调整,图像很小可以不使用这个函数 imshow("输入窗口", src); //第一个参数为窗口名 QuickDemo qd; qd.histogram_2d_demo(src); waitKey(0); //窗口停留时间,0为一直停留,数值为停留的毫秒数 destroyAllWindows(); //关闭所有打开的窗口 return 0; }效果2.25、直方图均衡化直方图均衡化的概念:直方图均衡化(Histogram Equalization)是一种增强图像对比度(Image Contrast)的方法,其主要思想是将一副图像的直方图分布通过累积分布函数变成近似均匀分布,从而增强图像的对比度。为了将原图像的亮度范围进行扩展, 需要一个映射函数, 将原图像的像素值均衡映射到新直方图中, 这个映射函数有两个条件:①不能打乱原有的像素值大小顺序, 映射后亮、 暗的大小关系不能改变;② 映射后必须在原有的范围内,即像素映射函数的值域应在0和255之间;综合以上两个条件,累积分布函数是个好的选择,因为累积分布函数是单调增函数(控制大小关系),并且值域是0到1(控制越界问题),所以直方图均衡化中使用的是累积分布函数。累积分布函数的数学原理:因为图像由一个个像素点组成,所以图像直方图均衡化是通过离散形式的累积分布函数求解的,直方图均衡化过程中,映射方法是:Sk=∑j=0knjnk=0,1,2,...,L−1Sk=∑j=0knjnk=0,1,2,...,L−1其中,S_k指当前灰度级经过累积分布函数映射后的值,n是图像中像素的总和,n_j是当前灰度级的像素个数,L是图像中的灰度级总数。直方图均衡化的步骤:①依次扫描原始灰度图像的每一个像素, 计算出图像的灰度直方图;②计算灰度直方图的累积分布函数;③根据累积分布函数和直方图均衡化原理得到输入与输出之间的映射关系。④最后根据映射关系得到结果进行图像变换直观理解直方图均衡化原理及过程:quickopencv.h#pragma once #include<opencv2/opencv.hpp> using namespace cv; class QuickDemo { public: void colorSpace_Demo(Mat &image); void mat_creation_demo(); void pixel_visit_demo(Mat &image); void operators_demo(Mat &image); void tracking_bar_demo(Mat &image); void key_demo(Mat &image); void color_style_demo(Mat &image); void bitwise_demo(Mat &image); void channels_demo(Mat &image); void inrange_demo(Mat &image); void pixel_statistic_demo(Mat &image); void form_paint_random(); void polyline_drawing_demo(); void mouse_drawing_demo(Mat &image); void norm_demo(Mat &image); void resize_demo(Mat &image); void flip_demo(Mat &image); void rotate_demo(Mat &image); void video_demo(Mat &image); void histogram_demo(Mat &image); void histogram_2d_demo(Mat &image); void histogram_eq_demo(Mat &image); };quickdemo.cppvoid QuickDemo::histogram_eq_demo(Mat &image) { Mat gray; cvtColor(image, gray, COLOR_BGR2GRAY); imshow("灰度图像", gray); Mat dst; equalizeHist(gray, dst); imshow("直方图均衡化演示", dst); }test440.cpp#include<opencv2/opencv.hpp> #include<quickopencv.h> #include<iostream> using namespace cv; using namespace std; int main(int argc, char**argv) { // imread函数的第二个参数有很多,默认为IMREAD_COLOR,还有IMREAD_UNCHANGED,IMREAD_GRAYSCALE,IMREAD_ANYCOLOR等等,实现对不同图片的读取操作 // B,G,R Mat src = imread("D:/images/flower.jpg"); //Mat为matrix,二维图像都是Mat类型,第一个参数为图片绝对路径 if (src.empty()) { printf("could not load image...\n"); return -1; //namedWindow("输入窗口", WINDOW_FREERATIO); //不管图片大小,都能进行调整,图像很小可以不使用这个函数 imshow("输入窗口", src); //第一个参数为窗口名 QuickDemo qd; qd.histogram_eq_demo(src); waitKey(0); //窗口停留时间,0为一直停留,数值为停留的毫秒数 destroyAllWindows(); //关闭所有打开的窗口 return 0; }效果2.26、图像卷积操作卷积运算原理示意图:quickopencv.h#pragma once #include<opencv2/opencv.hpp> using namespace cv; class QuickDemo { public: void colorSpace_Demo(Mat &image); void mat_creation_demo(); void pixel_visit_demo(Mat &image); void operators_demo(Mat &image); void tracking_bar_demo(Mat &image); void key_demo(Mat &image); void color_style_demo(Mat &image); void bitwise_demo(Mat &image); void channels_demo(Mat &image); void inrange_demo(Mat &image); void pixel_statistic_demo(Mat &image); void form_paint_random(); void polyline_drawing_demo(); void mouse_drawing_demo(Mat &image); void norm_demo(Mat &image); void resize_demo(Mat &image); void flip_demo(Mat &image); void rotate_demo(Mat &image); void video_demo(Mat &image); void histogram_demo(Mat &image); void histogram_2d_demo(Mat &image); void histogram_eq_demo(Mat &image); void blur_demo(Mat &image); };quickdemo.cppvoid QuickDemo::blur_demo(Mat &image) { Mat dst01, dst02, dst03, dst04, dst05; //blur为均值卷积,卷积后求平均值得到对应的值 //blur(输入图像,输出图像,卷积核大小,卷积输出位置默认为中心(-1,-1)),默认均值卷积核,卷积核越大模糊程度越高 blur(image, dst01, Size(3, 3), Point(-1, -1)); blur(image, dst02, Size(13, 13), Point(-1, -1)); blur(image, dst03, Size(23, 23), Point(-1, -1)); blur(image, dst04, Size(13, 1), Point(-1, -1)); blur(image, dst05, Size(1, 13), Point(-1, -1)); imshow("图像模糊01", dst01); imshow("图像模糊02", dst02); imshow("图像模糊03", dst03); imshow("图像模糊04", dst04); imshow("图像模糊05", dst05); }test440.cpp#include<opencv2/opencv.hpp> #include<quickopencv.h> #include<iostream> using namespace cv; using namespace std; int main(int argc, char**argv) { // imread函数的第二个参数有很多,默认为IMREAD_COLOR,还有IMREAD_UNCHANGED,IMREAD_GRAYSCALE,IMREAD_ANYCOLOR等等,实现对不同图片的读取操作 // B,G,R Mat src = imread("D:/images/flower.jpg"); //Mat为matrix,二维图像都是Mat类型,第一个参数为图片绝对路径 if (src.empty()) { printf("could not load image...\n"); return -1; //namedWindow("输入窗口", WINDOW_FREERATIO); //不管图片大小,都能进行调整,图像很小可以不使用这个函数 imshow("输入窗口", src); //第一个参数为窗口名 QuickDemo qd; qd.blur_demo(src); waitKey(0); //窗口停留时间,0为一直停留,数值为停留的毫秒数 destroyAllWindows(); //关闭所有打开的窗口 return 0; }效果2.27、高斯模糊高斯模糊计算公式:高斯核函数符合空间正态分布(正态分布又名高斯分布),越靠近卷积核中心的值权重越大高斯函数示意图:高斯模糊计算公式:G(x,y)=12πσ2e−(x2+y2)/(2σ2)G(x,y)=12πσ2e−(x2+y2)/(2σ2)该计算公式可看作是二维正态分布函数,是根据一维高斯函数推导来的,一维高斯函数就是正态分布函数。使用高斯模糊计算公式根据输入的参数计算高斯权重矩阵,具体计算方法可参考该文章:https://blog.csdn.net/qq_32515081/article/details/115006246。得到权重矩阵后,按卷积核计算规则将每个值乘以对应的矩阵再求和即可得到中心点对应的值。quickopencv.h#pragma once #include<opencv2/opencv.hpp> using namespace cv; class QuickDemo { public: void colorSpace_Demo(Mat &image); void mat_creation_demo(); void pixel_visit_demo(Mat &image); void operators_demo(Mat &image); void tracking_bar_demo(Mat &image); void key_demo(Mat &image); void color_style_demo(Mat &image); void bitwise_demo(Mat &image); void channels_demo(Mat &image); void inrange_demo(Mat &image); void pixel_statistic_demo(Mat &image); void form_paint_random(); void polyline_drawing_demo(); void mouse_drawing_demo(Mat &image); void norm_demo(Mat &image); void resize_demo(Mat &image); void flip_demo(Mat &image); void rotate_demo(Mat &image); void video_demo(Mat &image); void histogram_demo(Mat &image); void histogram_2d_demo(Mat &image); void histogram_eq_demo(Mat &image); void blur_demo(Mat &image); void gaussian_blue_demo(Mat &image); };quickdemo.cppvoid QuickDemo::gaussian_blue_demo(Mat &image) { Mat dst01, dst02, dst03; //GaussianBlur(输入图像,输出图像,卷积核大小,高斯函数σ的值(默认σx=σy),边缘处理方式) GaussianBlur(image, dst01, Size(5, 5), 15); GaussianBlur(image, dst02, Size(3, 3), 15); GaussianBlur(image, dst03, Size(0, 0), 15); imshow("高斯模糊01",dst01); imshow("高斯模糊02",dst02); imshow("高斯模糊03",dst03); }test440.cpp#include<opencv2/opencv.hpp> #include<quickopencv.h> #include<iostream> using namespace cv; using namespace std; int main(int argc, char**argv) { // imread函数的第二个参数有很多,默认为IMREAD_COLOR,还有IMREAD_UNCHANGED,IMREAD_GRAYSCALE,IMREAD_ANYCOLOR等等,实现对不同图片的读取操作 // B,G,R Mat src = imread("D:/images/flower.jpg"); //Mat为matrix,二维图像都是Mat类型,第一个参数为图片绝对路径 if (src.empty()) { printf("could not load image...\n"); return -1; //namedWindow("输入窗口", WINDOW_FREERATIO); //不管图片大小,都能进行调整,图像很小可以不使用这个函数 imshow("输入窗口", src); //第一个参数为窗口名 QuickDemo qd; qd.gaussian_blue_demo(src); waitKey(0); //窗口停留时间,0为一直停留,数值为停留的毫秒数 destroyAllWindows(); //关闭所有打开的窗口 return 0; }效果2.28、高斯双边模糊双边滤波原理:空间距离:当前点距离滤波模板中心点的欧式距离。灰度距离:当前点距离滤波模板中心点的灰度的差值的绝对值。双边滤波的核函数是空间域核与像素范围域核的综合结果:在图像的平坦区域,像素值变化很小,那么像素差值接近于0,对应的像素范围域权重接近于1,此时空间域权重起主要作用,相当于进行高斯模糊;在图像的边缘区域,像素值变化很大,那么像素差值大,对应的像素范围域权重变大,即使距离远空间域权重小,加上像素域权重总的系数也较大,从而保护了边缘的信息。双边滤波在突变的边缘上,使用了像素差权重,很好的保留了边缘。quickopencv.h#pragma once #include<opencv2/opencv.hpp> using namespace cv; class QuickDemo { public: void colorSpace_Demo(Mat &image); void mat_creation_demo(); void pixel_visit_demo(Mat &image); void operators_demo(Mat &image); void tracking_bar_demo(Mat &image); void key_demo(Mat &image); void color_style_demo(Mat &image); void bitwise_demo(Mat &image); void channels_demo(Mat &image); void inrange_demo(Mat &image); void pixel_statistic_demo(Mat &image); void form_paint_random(); void polyline_drawing_demo(); void mouse_drawing_demo(Mat &image); void norm_demo(Mat &image); void resize_demo(Mat &image); void flip_demo(Mat &image); void rotate_demo(Mat &image); void video_demo(Mat &image); void histogram_demo(Mat &image); void histogram_2d_demo(Mat &image); void histogram_eq_demo(Mat &image); void blur_demo(Mat &image); void gaussian_blue_demo(Mat &image); void bifilter_demo(Mat &image); };quickdemo.cppvoid QuickDemo::bifilter_demo(Mat &image) { Mat dst; bilateralFilter(image, dst, 0, 100, 10); imshow("双边模糊", dst); }test440.cpp#include<opencv2/opencv.hpp> #include<quickopencv.h> #include<iostream> using namespace cv; using namespace std; int main(int argc, char**argv) { // imread函数的第二个参数有很多,默认为IMREAD_COLOR,还有IMREAD_UNCHANGED,IMREAD_GRAYSCALE,IMREAD_ANYCOLOR等等,实现对不同图片的读取操作 // B,G,R Mat src = imread("D:/images/lena.jpg"); //Mat为matrix,二维图像都是Mat类型,第一个参数为图片绝对路径 if (src.empty()) { printf("could not load image...\n"); return -1; //namedWindow("输入窗口", WINDOW_FREERATIO); //不管图片大小,都能进行调整,图像很小可以不使用这个函数 imshow("输入窗口", src); //第一个参数为窗口名 QuickDemo qd; qd.bifilter_demo(src); waitKey(0); //窗口停留时间,0为一直停留,数值为停留的毫秒数 destroyAllWindows(); //关闭所有打开的窗口 return 0; }效果3、案例:实时人脸识别先下载三个文件置于opencv的face_detector路径下:D:\environment\opencv\sources\samples\dnn\face_detector下载地址及文件如下:quickopencv.h#pragma once #include<opencv2/opencv.hpp> using namespace cv; class QuickDemo { public: void colorSpace_Demo(Mat &image); void mat_creation_demo(); void pixel_visit_demo(Mat &image); void operators_demo(Mat &image); void tracking_bar_demo(Mat &image); void key_demo(Mat &image); void color_style_demo(Mat &image); void bitwise_demo(Mat &image); void channels_demo(Mat &image); void inrange_demo(Mat &image); void pixel_statistic_demo(Mat &image); void form_paint_random(); void polyline_drawing_demo(); void mouse_drawing_demo(Mat &image); void norm_demo(Mat &image); void resize_demo(Mat &image); void flip_demo(Mat &image); void rotate_demo(Mat &image); void video_demo(Mat &image); void histogram_demo(Mat &image); void histogram_2d_demo(Mat &image); void histogram_eq_demo(Mat &image); void blur_demo(Mat &image); void gaussian_blue_demo(Mat &image); void bifilter_demo(Mat &image); void face_detection_demo(); };quickdemo.cpp#include<quickopencv.h> #include<opencv2/dnn.hpp> using namespace cv; using namespace std; void QuickDemo::face_detection_demo() { std::string root_dir = "D:/environment/opencv/sources/samples/dnn/face_detector/"; //读取深度神经网络 dnn::readNetFromTensorflow(路径+模型文件名,路径+配置文件名) dnn::Net net = dnn::readNetFromTensorflow(root_dir + "opencv_face_detector_uint8.pb", root_dir + "opencv_face_detector.pbtxt"); VideoCapture capture("D:/images/video/example_dsh.mp4"); //加载一段视频 Mat frame; while (true) { capture.read(frame); if (frame.empty()) { break; //张量(tensor)可以看作是一个多维矩阵,这里定义一个张量blob //dnn::blobFromImage(输入图像,scale factor 1.0表示色彩空间还是0-255,模型要求的输入尺寸,模型要求的均值(models.yml文件中的mean值),是否进行通道交换(models.yml文件中),是否要剪切图像) Mat blob = dnn::blobFromImage(frame, 1.0, Size(300, 300), Scalar(104, 177, 123), false, false); //将输入图像张量输入到深度神经网络中 net.setInput(blob); //输入的blob格式 NCHW 多少个图像,输入通道数,高度,宽度 //对输入图片进行推理 Mat probs = net.forward(); //输出结果(多少张图像每张图有对应编号,每张图第几个批次第几张图,多少框,每个框有7列) Mat detectionMat(probs.size[2], probs.size[3], CV_32F, probs.ptr<float>()); //只获取输出结果的后两部分 //解析结果 for (int i = 0; i < detectionMat.rows; i++) { //前两个值代表类型和index,第三个值是人脸得分 float confidence = detectionMat.at<float>(i, 2); //判断是否是人脸的部分 if (confidence > 0.5) { //得到人脸部分矩形的左上角和右下角坐标,用户绘制检测框 int x1 = static_cast<int>(detectionMat.at<float>(i, 3)*frame.cols); int y1 = static_cast<int>(detectionMat.at<float>(i, 4)*frame.rows); int x2 = static_cast<int>(detectionMat.at<float>(i, 5)*frame.cols); int y2 = static_cast<int>(detectionMat.at<float>(i, 6)*frame.rows); Rect box(x1, y1, x2 - x1, y2 - y1); rectangle(frame, box, Scalar(0, 0, 255), 2, 8, 0); imshow("人脸检测演示", frame); //TODO: do something... int c = waitKey(1); if (c == 27) { //退出 break; }深度神经网络模型models.yml配置文件说明:test440.cpp#include<opencv2/opencv.hpp> #include<quickopencv.h> #include<iostream> using namespace cv; using namespace std; int main(int argc, char**argv) { // imread函数的第二个参数有很多,默认为IMREAD_COLOR,还有IMREAD_UNCHANGED,IMREAD_GRAYSCALE,IMREAD_ANYCOLOR等等,实现对不同图片的读取操作 // B,G,R Mat src = imread("D:/images/lena.jpg"); //Mat为matrix,二维图像都是Mat类型,第一个参数为图片绝对路径 if (src.empty()) { printf("could not load image...\n"); return -1; //namedWindow("输入窗口", WINDOW_FREERATIO); //不管图片大小,都能进行调整,图像很小可以不使用这个函数 //imshow("输入窗口", src); //第一个参数为窗口名 QuickDemo qd; qd.face_detection_demo(); waitKey(0); //窗口停留时间,0为一直停留,数值为停留的毫秒数 destroyAllWindows(); //关闭所有打开的窗口 return 0;
个人建站合集(hexo+ next7)
前段时间小牛为了给网站更换评论系统,把网站弄崩了(可太难了~~),准备重新走一遍,顺便把之前写的文章合在一起,并修正。(本文以后随缘更新) [toc]一些必备准备工作拥有github 账户如果没有的话,首先去github官网去注册一个账户(有的话,相信你懂这个网站,这部分你可以跳过)按部就班地填写信息即可,用户名别乱起,以后改起来麻烦,后面还要拿用户名当域名,当然你们开心就好接着创建Repository登陆GitHub,点击右上角的 +号,选择New repository现在新建一个仓库 blog(如果你没有买域名,那Repository的名字格式是username.github.io,比如xiaoniuhululu.github.io )。 其余先不弄,点击Create repository下载git bash这个软件,装到win10电脑中,一路默认安装即可在电脑中,创建一个目录 来保存这个项目期间可能会问你 邮箱地址和用户名,密码 ,按照上面的提示执行命令,把相关信息换成自己的即可。下载nodejs去官网去下,同时配置一下,可以看我之前的一篇文章下载git可以参考小牛以前写过一篇文章关于git的安装和使用,由于本文会很长,为了详略得当,这里就略过了 = =安装使用Hexonode 环境配置好后,直接执行cnpm install -g hexocnpm 是因为我配置了阿里云的镜像接着执行一下命令确认hexo安装完好。hexo -v该命令会显示hexo的版本及依赖的包我们先尝试启动这个网站,没错就这么简单我们在D:\GitBlog这个目录下(这个目录自己选即可),保证这个目录为空。然后初始化我们的网站,输入hexo init初始化文件夹,接着输入cnpm install安装必备的组件。确保git部署,再执行一个命令cnpm install hexo-deployer-git --save输入hexo g生成静态网页,然后输入hexo s打开本地服务器,然后浏览器打开http://localhost:4000/,就可以看到我们的博客啦,效果如下:这里贴一下常用的命令:hexo g //生成 hexo s //启动服务预览 hexo d //部署 hexo server //Hexo会监视文件变动并自动更新,无须重启服务器 hexo server -s //静态模式 hexo server -p 5000 //更改端口 hexo server -i 192.168.20.20 //自定义 IP hexo clean //清除缓存到官网的主题进行更换:hexo themes,找自己喜欢的 这块后面文章再讲,先跳过Netlify和Github持续部署Hexo博客,并实现版本控制2021-4-11 该方法弃用,国内太卡,根本访问不了,已更换成腾讯云服务器简单解释一下,小牛之前是用github page来托管网站,但感觉速度太慢,发现Netlify还不错,测试了一下,还行,就转移过去了Netlify 是一个静态网站自动化部署系统,可快速将静态网站直接构建部署,不需要你提供静态资源服务器。Netlify 基于 JAMstack 架构,即客户端 JavaScript、可重用 API 和预构建 Markup,这是一种现代化、未来化的构建网站的方法,可以提供更好的性能(全球 CDN )、安全性(HTTPS)和更好的开发体验(持续部署及Git集成)。且 Netlify 提供的功能和服务相比 GitHub Pages 功能更为强大——绑定域名、页面重定向、静态资源文件压缩、免费的 HTTPS 等等优点:Simply Push to Deploy支持热部署,只需要将代码 push 到 Git 远程仓库即可自动构建及更新。Free automatic HTTPS支持自定义域名,提供免费 HTTPS ,可上传域名证书。Never have to leave Terminal可在终端中操作 Netlify。由于我们准备使用 使用Netlify和Github持续部署Hexo博客 ,这样我们的博客源码会被其他人看到,里面可能有一些私密密码,所以为了安全起见,决定将该仓库设为私有化。(我这里新建的仓库名为blog,你们可以自定义)(如果是小牛之前仅仅普通的将hexo 部署到github pages ,那可以github仓库可以公开,因为hexo 只会默认推送 /public 目录到github仓库了,源码不会推送上去,所以这个可以放心大胆设为公开化)选择Private,这样别人就无法搜索/查看你的博客分支了,同时我们不要勾选和添加任何README以及gitignore文件,直接选择“Create repository“即可完成仓库的创建。复制你刚刚新建的仓库的地址:`git@github.com:xiaoniuhululu/blog.git`回到项目根目录,我们将本地项目和新建的repository通过git联系起来,在当前本地目录打开git bash执行下面:git init git remote add origin git@github.com:xiaoniuhululu/blog.git //推送git至仓库 git add ./ git commit -m 'init blog' git push --set-upstream origin master编辑配置文件:用vscode工具 打开 本地博客目录下的_config.ymlL:把刚刚拷贝的仓库的地址粘贴到repositorydeploy: type: git #部署方式 repository: git@github.com:xiaoniuhululu/blog.git #github仓库地址 branch: run-page #部署分支 注意这个分支,照做,后面会讲修改完配置文件后,执行hexo clean //清理静态文件 hexo g //生成静态文件 hexo d //部署可以看到github的仓库已经变了,且多了一个分支这样以后:master 通过git命令 提交,管理博客源码use-page 通过hexo命令 提交,更新博客页面接着 托管到Netlify:我们先到Netlify官网注册一下账号,选择GitHub登录然后新建项目: 选择GitHub来源:选择我们在github的项目: 进入一步进行配置:(把后面2个框,清空即可) 等一会,Netlify会自动帮我们生成一个网站更改网址别名,红框是Netlify 为我们提供的域名网站(后面我会用买的域名替换) 访问这个网站,就能跳转到我们的网站配置自定义域名的话,首先你得去买一个域名,然后再Netlify设置中,找到domain,然后去域名提供商 去改dns配置即可 这样以后:master 通过git命令 提交,管理博客源码use-page 通过hexo命令 提交,更新博客页面以后换电脑啥的都不怕了,直接把源码拉下来配置。如果写错了,还可以版本回退是不是很舒服。🌞深度优化网站好的网站,需要持续不断的优化下载Next主题社区版大家去官网下载自己喜欢的主题,官网地址:https://hexo.io/themes/太多了,不知道怎么选?可以去百度一下,我这里就选是Next下载主题next注意:我下的不是原生Next版本,我下的是社区版的Next(为了waline 评论系统),地址:https://github.com/lizheming/hexo-theme-next在GitBlog\themes目录下,执行:git clone https://github.com/lizheming/hexo-theme-next.git修改 站点配置文件_config.yml,注意不是next主题里的_config.yml,找到如下代码:## Themes: https://hexo.io/themes/ ##theme: landscape theme: hexo-theme-next执行以下命令,使配置文件修改生效hexo clean //清理缓存 hexo g //生成 hexo s //启动服务预览修改Next为中文修改 站点配置文件_config.yml#language: en language: zh-CN执行以下命令,使配置文件修改生效:hexo clean //清理缓存 hexo g //生成 hexo s //启动服务预览效果:网站一些基本信息找到Site ,配置网站一些基本信息,下面贴一下官网的基本配置: 修改 站点配置文件_config.yml:执行以下命令,使配置文件修改生效:hexo clean //清理缓存 hexo g //生成 hexo s //启动服务预览效果如下: 新建标签及分类标签修改 主题配置文件_config.yml:把 tags 和 categories ,about前面的 # 删除效果如下:启用关于页,标签页和分类页,我们需要自己手动建页首先打开source目录执行:hexo n page about hexo n page tags hexo n page categoriessource目录下会多处3个文件夹:分别编辑各文件夹的index.md 更换主题的样式next 内置许多样式,可以自己选择在主题配置文件/next/_config.yml中查找:scheme,找到如下代码:# Schemes scheme: Muse #scheme: Mist #scheme: Pisces #scheme: Gemini个人比较喜欢 Gemini选择你喜欢的一种样式,去掉前面的 #,其他主题前加上 # 即可。设置头像在主题配置文件/next/_config.yml中查找:avatar,找到如下代码:小牛这里用的是 图片在自己github图床的地址,如果有小伙伴不了解,可以看我之前的一篇文章,关于github怎么搭建图床的,并通过cdn加速rounded 是表示 头像为原型 rotated 是表示 头像可以旋转效果如下: 增加知乎豆瓣等自定义图标支持Hexo NexT 原始主题是采用了 Font Awesome 图标,并未包含如微信公众号,知乎这些社交网站图标。下载图标 首先,前往 阿里巴巴矢量库 挑选需要的图标,在图标上点击 "购物车" 将其加入购物车。选择「下载代码」,会将所有的图标一起下载下来 修改主题配置文件_config.yml 将下载的文件解压后,在里面,找到 iconfont.css 文件,打开后将其中的所有内容都复制加入到主题 CSS 自定义文件source\_data\styles.styl 中的任意位置。//自定义图标 font-class引用 @font-face {font-family: "iconfont"; src: url('iconfont.eot?t=1616849051933'); /* IE9 */ src: url('iconfont.eot?t=1616849051933#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAQIAAsAAAAACEwAAAO5AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDBgqEIIN+ATYCJAMMCwgABCAFhG0HSRtXBxHVmynJfiTYtnFSVsx/DkRyF+lM8Py/Tr1P+lEEYSgxqAQsly0XGCfAYUk3Q3limnocAFubUotQlRGKJTkPvsv/SH4wwuVD18/fz9dFAW6Wx2ZzqbjvLZuyMW2AA5sOqBurhDeQD+R/GLvyIvZDAJsSaiGduvRyMVEYwwSQmdOmjMNMmVA1hYKJYJTMVcgOEjC1oFYGbPd/L59oICZoJOgYY/tN7jyRdm+S31q6F/cY6RKQjecA2CLQgVqAAhld6hqKHkZroWMrSNVnGigEbaxAPI4vVXFjpf94GoiBgZIgtZpGOymZLYDAW0sCMc7fA0aP5stI+ggfAQ07qUDl5+daVrpty4rIm0Hh12707eDYu2lDzoRtPzokFhsUilQeci5qh8NuOJJoE0vMNDMm+IOjUUsdqBJBZmhQJGL0XNW03PeQl0WhKhRo5hgWysbi8q2PRpq7QxqfdUK5hyQWGxza6+3xzwXOu44kgY1rIu7b/fytqr0b3IuN9/cF9juyzgnb2OV076kqV049NQ3lyjnK+NP7P9oz9rlm5UkTuxrd7f6BdiOoMcienDz3X2+zxVHebkxfbc28GbwmrVwTD+IOjawytLLvVx5aZWSZ5a88zA/q9pFVyqB/WOVgEMpKWEOD/rCLGBPujxpYsX/5Vasr9Kvo3q04sEL/Vav6VxhY8a5bsV+F1avK9684EFzELd9v9ep+5d2KuaOcE2ldA8OGME7Wzdbc1Lp9HuR07l9tScNscZ3JBdJ79pRq7VK+FXwralvQd2/zFNfQtih1t7d3pev80f3Eldmjpat0mT3GlbQq+k+rRq2OqpP1MuOllfw/4/+CkJHyZUEy5QCIe0p077//DYmND10fk9rqj+koAF5s6H7Yl7fzgPlW3AJ/uUQB8bi6cIVh1pUstaV6QmeD/Q7FUMwx/m0xQAav+uPZrfNBMMmOQSORYtAxqURU1FqQgEMjMDBpCzY16brYIZP56IiygBpsACCkcR00UngEOmk8JirqS0gghy9gkCY62AyWzDUdqsg02+4oaBQ92B8kxztZVq2DwhuGczWSVPjsF0pJAeibLpuYcUcZY0J5h0GVgIQ3mMBhuK4Mh3BGp01UPca2pbI7NY63qqajoFH0YH+QHO/kNtY69/0bhnM1UlNVEX6hlNQ69E3XgjijvVXVtRxT3mFQJSDhDSbgwNWMMhzlvTI6bWKP8DG2qB61Rc38+PZ8cR5iDWNNXTTRRaEmFL9fP5i+aQ9sGf7R6RYNh9Psa6oqAAAA') format('woff2'), url('iconfont.woff?t=1616849051933') format('woff'), url('iconfont.ttf?t=1616849051933') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('iconfont.svg?t=1616849051933#iconfont') format('svg'); /* iOS 4.1- */ //以下代码相对下载下来的代码做了部分修改 .iconfont { font-family: "iconfont" !important; font-size: inherit; //修改! font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; .csdn:before { content: "\e50d"; .weixin:before { //修改! content: "\e609"; }主题注入功能修改/themes/next/_config.ymlcustom_file_path: - #style: source/_data/styles.styl + style: source/_data/styles.styl使用说明(侧边栏)social: GitHub: https://github.com/user_id || fab fa-github Zhihu: https://www.csdn.com/people/user_id || iconfont csdn Douban: https://www.douban.com/people/user_id/ || iconfont douban重新部署项目即可看到效果。编写博客Hexo的博客都是用Markdown写的。在D:\username.github.io\source_posts的空白处右键Git Bash Herehexo new '第一篇文章'执行以下命令:hexo clean //清理缓存 hexo g //生成 hexo s //启动服务预览 文章还是空的,我们现在来编辑一下: 分别依次编辑:标签,分类,正文效果:部署至githubhexo clean //清理缓存 hexo g //生成 hexo d //上传部署效果如下: 开启赞赏功能修改 主题配置文件_config.yml,找到Reward,修改:开启订阅公众号修改 主题配置文件_config.yml,找到follow_me,修改:去掉底部-原力驱动设置阅读全文如果你想让你的文章只显示一部分,多余的可以点击阅读全文来查看,那么你需要在你的文章中添加<!--more-->其后面的部分就不会显示了,只能点击阅读全文才能看修改文章底部带#的便签修改前:修改 主题配置文件_config.yml,找到tag_icon,改成true即可效果: 修改网站图标修改 主题配置文件_config.yml,找到favicon,修改:浏览页面显示当前浏览进度修改 主题配置文件_config.yml,找到scrollpercent,改成true即可增加搜索功能安装插件,用于生成博客索引数据(在博客根目录下执行下列命令):cnpm install hexo-generator-search --save在**站点配置文件 _config.yml **中添加如下内容:search: path: ./public/search.xml field: post format: html limit: 10000path:索引文件的路径,相对于站点根目录 field:搜索范围,默认是 post,还可以选择 page、all,设置成 all 表示搜索所有页面 limit:限制搜索的条目数在主题配置文件 _config.yml 中找到local_search ,把enabled改成true即可把他同步到github上时候,发现下面搜索卡死 hexo clean 后再提交就可以了 如果还有问题,可参考https://blog.csdn.net/aoman_hao/article/details/86713171这篇网上的文章增加不蒜子统计功能在主题配置文件 _config.yml 中找到busuanzi_count ,把enabled改成true即可设置license的样式修改 主题配置文件_config.yml,找到creative_commons,修改如下:creative_commons: license: by-nc-sa sidebar: true post: true language:还需修改 站点配置文件_config.yml,找到url,换成自己博客的地址# URL ## If your site is put in a subdirectory, set url as 'http://example.com/child' and root as '/child/' url: https://xiaoniuhululu.github.io/添加聊天功能个人感觉鸡肋,食之无味弃之可惜小牛这里选的是 chatra, 试了试挺好用的去官网:https://app.chatra.io/,注册一个账户,免费的就行获取chatra_id,存在setup里,感觉贼奇葩 修改 主题配置文件_config.yml,找到chat,修改如下:由于我们选择的是chatra,那我们还需配置chatra相关信息把id换一下即可 效果图如下: 文章置顶在博客根目录下安装置顶插件npm uninstall hexo-generator-index --save npm install hexo-generator-index-pin-top --save在需要置顶的文章头加入top: true 例如--- title: test date: 2021 categories: 测试 top: true ---然后在文章标题下面添加一个置顶标签样式打开:/blog/themes/next/layout/_macro 目录下的post.swig文件,定位到div class="post-meta"标签下,插入如下代码:{% if post.top %} <i class="fa fa-thumb-tack"></i> <font color=7D26CD>置顶</font> <span class="post-meta-divider">|</span> {% endif %}添加3d地球太卡了,弃用先去revolvermaps官网 选择一款你喜欢的样式,将代码拷贝出来然后把代码,拷贝到\themes\next\layout_macro\sidebar.swig里即可域名绑定根据之前的教程,目前默认的域名还是username.github.io,感觉怪怪的,但是一般个人网站好像都是www.name.com格式的,为此小牛没法白嫖了,只能掏钱了买域名小牛是去阿里云买的,腾讯云,华为云 啥的都可以买实名认证即可,不需要备案,如果服务器是内地的就需要备案。Hexo网站的服务器是海外的因此可以跳过这步。域名绑定先在阿里云上 控制台->域名服务,找到自己的域名域名解析(本地与服务器映射)点击新手引导这里得填网站的IP地址,但我们现在只知道我们网站默认的github的域名,我们执行ping一下即可(打开终端,输入ping 用户名.github.io)把得到的IP地址填入阿里云中解析记录设置两个:www和@,线路默认就ok(1) @:记录类型选A,A记录的就是ip地址,不用改(2) www:记录类型选CNAME,CNAME记录值填你的github博客网址,如我的是xiaoniuhululu.github.io https://xiaoniuhululu.netlify.app/本地设置修改 这些全部设置完成后,此时你并不能根据申请的域名访问你的博客。接着你需要做的是在hexo根目录的source文件夹里创建CNAME文件,不带任何后缀,里面添加你的域名信息,如:xiaoniuhululu.com。可以用www.xiaoniuhululu.com和xiaoniuhululu.com访问都是可以的。重新hexo g,hexo d,并发布即可用新的域名访问。效果展示:但发现浏览器提示不安全?这个瑕疵可不能忽视,继续折腾一番在网上查了许久,发现github自己就支持自定义域名的https的请求HTTPS 让你的网站和网站访客更安全,并且 Github 提供的这些 IP 地址自动将你的站点加入了 CDN,提高了访问速度。你还可以在 GiHub Pages 仓库的Settings里勾选 'Enforce HTTPS',这样所有访问你站点的请求都会走 HTTPS。不得不感叹,github太良心了自定义hexo创建文章的模板网站好了,应该该写文章了,每次我们会因为格式问题,而导致花费额外的时间,一个良好的模板,会帮助我们节约时间。在博客的scaffolds目录下有三个md文档,只需要改post.md即可。小牛的模板如下:--- title: {{ title }} date: {{ date }} permalink: categories: tags: [] comments: true <!-- 前言 --> <!-- more --> ## 0x01 ## 0x02 ## 0x03 ## 0x04增加鼠标点击特效小牛比较喜欢点击出现的爱心特效,下面简单记录一下过程。将下面JS 文件复制到目录 /themes/next/source/js/cursor/ 下love.js:/*网页鼠标点击特效(爱心)*/ !function (e, t, a) {function r() {for (var e = 0; e < s.length; e++) s[e].alpha <= 0 ? (t.body.removeChild(s[e].el), s.splice(e, 1)) : (s[e].y--, s[e].scale += .004, s[e].alpha -= .013, s[e].el.style.cssText = "left:" + s[e].x + "px;top:" + s[e].y + "px;opacity:" + s[e].alpha + ";transform:scale(" + s[e].scale + "," + s[e].scale + ") rotate(45deg);background:" + s[e].color + ";z-index:99999");requestAnimationFrame(r)}function n() {var t = "function" == typeof e.onclick && e.onclick;e.onclick = function (e) {t && t(), o(e)}}function o(e) {var a = t.createElement("div");a.className = "heart", s.push({el: a,x: e.clientX - 5,y: e.clientY - 5,scale: 1,alpha: 1,color: c()}), t.body.appendChild(a)}function i(e) {var a = t.createElement("style");a.type = "text/css";try {a.appendChild(t.createTextNode(e))} catch (t) {a.styleSheet.cssText = e}t.getElementsByTagName("head")[0].appendChild(a)}function c() {return "rgb(" + ~~(255 * Math.random()) + "," + ~~(255 * Math.random()) + "," + ~~(255 * Math.random()) + ")"}var s = [];e.requestAnimationFrame = e.requestAnimationFrame || e.webkitRequestAnimationFrame || e.mozRequestAnimationFrame || e.oRequestAnimationFrame || e.msRequestAnimationFrame || function (e) {setTimeout(e, 1e3 / 60)}, i(".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}"), n(), r()}(window, document);在配置文件中引入新建的js文件 在…\themes\hexo-theme-next\layout*路径下找到“_layout.swig”*文件,在标签中引入新建的js文件,代码如下:<script type="text/javascript" src="/js/cursor/love.js"></script>上传hexohexo clean hexo g hexo d这样网站就能看到效果了。自定义网页样式全部自定义样式放到 hexo/source/_data/styles.styl 即可。然后在 NexT 的配置文件 next.yml 中取消 styles.styl 的注释:custom_file_path: - #style: source/_data/styles.styl + style: source/_data/styles.styl这部分小牛参考的班班的样式,在此感谢!添加Google广告注册 Google AdSense:google adsense注册账号流程比较简单,跟随页面提示填好各项信息,官网地址值得注意的是:网站域名一定要正确,不要轻易修改,不然后面修改网址(补救)会非常麻烦。添加广告代码注册账号完成之后,google会生成一段代码。需要将谷歌提供给你的一份代码添加到你网站的中,我用的是next主题v7的版本,在 主题配置文件_config.yml中 中释放此段代码:custom_file_path: head: source/_data/head.swig我们接着在 source/_data 中创建 head.swig 文件,并把 AdSense 产生的代码放在 head.swig 里面,就像这样:<script data-ad-client="xxxxxxxxxxxxxxx" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>申请审核重新启动一下本地的 Hexo 环境,检查 标记中有没有出现刚才添加的代码。检查没有问题之后,就可以部署到生产环境使上面的修改生效了。为了保险起见,在生产环境上也要检查一下 标记中有没有出现刚才添加的代码。确认没有问题之后,就可以回到 AdSense 页面中点击验证按钮了。然后 AdSense 会开始审核阶段,这期间我们只需静静等待审核结果的邮件就好了(一般需要3-4天)注意:如果你的网站域名,填写错误。则需要在对错误域名申请时,等待控制台里的“网站”项,可以点击(大概需要2天的时间)。然后点击添加网站。添加之后,申请审核即可。这里值得注意的是:审核可能会一直通过不了(猜测可能默认会去错误网站查找代码),查了许久官方文档,惊喜发现google ad还提供了另一种 验证方式:##在 Search Console 中验证网站所有权:在 Search Console 中验证网站所有权选择网域资源验证方式比较好,只需要一个域名就可以匹配到多种格式的 URL,之后会给你一个 TXT 的记录值,复制它到你域名DNS增加一个 TXT 记录,点击验证即可(小牛域名是在阿里云上买的,所以去阿里云上DNS里添加一下TXT记录即可) 然后回到google站长网站上验证即可。这样就可以验证网站的所有权,google不需要在去网站上寻找广告代码。小牛前后申请了十几次才才成功,大家多点耐心。配置广告模块 进入 AdSense 管理后台之后,进入侧边栏的广告 -> 概览。在这里我们可以配置广告会在什么位置展示了。因为笔者并不希望让我的网站满地都是广告,而影响正常内容的展示,而是只在首页左侧边栏最下面,和在每篇博文结束的位置,各放置一个广告。所以我就需要到按广告单元这个页面手动配置两个广告模块。添加广告模块点击展示广告按钮来新建一个类型为展示的广告。在顶部文本框填入广告模块的名字,比如 side ad,然后在右边栏选择广告的尺寸,是自适应的,还是固定长宽的。如果是自适应尺寸,那么还需要选择广告的大致形状。配置完毕后,点击保存按钮,一个广告模块就配置好了。我这里配置2个:side ad和 bottom ad 两个模块。配置博客页面 广告模块是创建好了,接着需要配置广告展示的位置。修改 主题配置文件_config.yml 中 custom_file_path 的配置:custom_file_path: sidebar: source/_data/sidebar.swig postBodyEnd: source/_data/post-body-end.swig然后我们在 source/_data/ 目录新建两个文件,分别命名为 sidebar.swig 和 post-body-end.swig,分别用来存放侧边栏广告和文章底部广告的代码。回到按广告单元这个页面,在刚才添加的广告单元那里,有一个形似 <> 的按钮,点击它,就可以看到这个广告单元的配置代码了。把 side ad 的代码复制到 sidebar.swig 中,来把这个侧边栏广告真正的放到侧边栏上。比如我的广告代码是这样的:<!-- side ad --> <ins class="adsbygoogle" style="display:block" data-ad-client="████████████████████" data-ad-slot="██████████" data-ad-format="auto" data-full-width-responsive="true"></ins> <script> window.addEventListener('load', function() { (adsbygoogle = window.adsbygoogle || []).push({}); </script>然后把 bottom ad 的代码复制到 post-body-end.swig,来把这个广告放到文章底部。(本部分参考:https://www.boris1993.com/others/hexo-next-enable-google-adsense.html)<!-- bottom ad --> <ins class="adsbygoogle" style="display:block" data-ad-client="████████████████████" data-ad-slot="██████████" data-ad-format="auto" data-full-width-responsive="true"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script>这样,广告就放置好了。接下来,把博客发布上去,就可以开始打广告了。添加 Google Adsense ads.txt申请完 Google Adsense 后,需要将一段代码和一个 ads.txt 文件加入到博客中。前者比较好实现,按照官方教程进行即可。如果不添加后者,你的网站仍然可以展示广告,但是会收到如下警告:什么是 adx.txt 呢?根据 Google 官方的描述:授权数字卖家 (ads.txt) 是一项 IAB Tech Lab 计划,有助于确保只通过您认定的授权卖家(如 AdSense)销售您的数字广告资源。创建自己的 ads.txt 文件后,您可以更好地掌控允许谁在您的网站上销售广告,并可防止向广告客户展示仿冒广告资源。我们强烈建议您使用 ads.txt 文件。它可以帮助买家识别仿冒广告资源,并可以帮助您获得更多广告客户支出,而这些支出原本可能会流向仿冒广告资源。这个文件内容形式如下:google.com, pub-0000000000000000, DIRECT, f08c47fec0942fa0我们不必太担心这个文件的内容,因为 Google 会帮你生成好,你下载即可。此外这个文件必须上传到网站根目录,就是说这个文件必须能够通过 your-website.com/ads.txt 访问到,其中 your-website.com 是你的主域名。那么对于基于 GitHub Page 的 hexo next 主题博客,如何添加呢?官方文档这样说: 将您的 ads.txt 文件上传到您网站的根目录。所谓网站的根目录,就是对应顶级域名的目录或文件夹 (example.com/ads.txt)。如果您要验证是否正确发布了文件,请使用网络浏览器访问 ads.txt 网址 (https://example.com/ads.txt),看看是否可以成功查看文件内容。如果可以,就说明 AdSense 可顺利找到该文件。添加 ads.txt 直接将 ads.txt 放入 /hexo/soruce/ 目录下即可。网上有的说放在根目录的 public 目录下面。。。殊不知 public 目录是不会上传的,这点已经在 hexo 文档上写明了,而且从 .gitignore 中也可以看出来测试最后友情提醒一下:别做一些刻意点击广告的行为,会被封~~添加备案信息在主题配置文件中,搜索 “beian”,即可由于小牛最近每天加班到凌晨,实在时间不够,本文拖了很久,修修改改。next7 评论系统始终得不到很好的解决,next7版本就更新于此,后续会修改错误疏漏,但不会再更新相关内容了,以后会抽空 使用next 8来重构本博客,发布的内容与next8有关。感谢大家的阅读,愿我的文章能为你们点燃思维的火花!如果觉得文章对你有帮助,欢迎关注微信公众号:小牛呼噜噜参考:https://juejin.cn/post/6844903830568583176#heading-1
Objective-C RunLoop原理
前言RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理。之后会介绍一下在 iOS 中,苹果是如何利用 RunLoop 实现自动释放池、延迟回调、触摸事件、屏幕刷新等功能的。目录RunLoop 的概念RunLoop 与线程的关系RunLoop 对外的接口RunLoop 的 ModeRunLoop 的内部逻辑RunLoop 的底层实现苹果用 RunLoop 实现的功能AutoreleasePool事件响应手势识别界面更新定时器PerformSelecter关于GCD关于网络请求RunLoop 的实际应用举例AFNetworkingAsyncDisplayKit正文RunLoop 的概念一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让线程能随时处理事件但并不退出,通常的代码逻辑是这样的:function loop() { initialize(); var message = get_next_message(); process_message(message); } while (message != quit); }这种模型通常被称作 Event Loop。 Event Loop 在很多系统和框架里都有实现,比如 Node.js 的事件处理,比如 Windows 程序的消息循环,再比如 OSX/iOS 里的 RunLoop。实现这种模型的关键点在于:如何管理事件/消息,如何让线程在没有处理消息时休眠以避免资源占用、在有消息到来时立刻被唤醒。所以,RunLoop 实际上就是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行上面 Event Loop 的逻辑。线程执行了这个函数后,就会一直处于这个函数内部 "接受消息->等待->处理" 的循环中,直到这个循环结束(比如传入 quit 的消息),函数返回。OSX/iOS 系统中,提供了两个这样的对象:NSRunLoop 和 CFRunLoopRef。CFRunLoopRef 是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,所有这些 API 都是线程安全的。NSRunLoop 是基于 CFRunLoopRef 的封装,提供了面向对象的 API,但是这些 API 不是线程安全的。CFRunLoopRef 的代码是开源的,你可以在这里下载到整个 CoreFoundation 的源码来查看。(Update: Swift 开源后,苹果又维护了一个跨平台的 CoreFoundation 版本:https://github.com/apple/swift-corelibs-foundation/,这个版本的源码可能和现有 iOS 系统中的实现略不一样,但更容易编译,而且已经适配了 Linux/Windows。)RunLoop 与线程的关系首先,iOS 开发中能遇到两个线程对象: pthread_t 和 NSThread。过去苹果有份文档/tn/tn2028.html)标明了 NSThread 只是 pthread_t 的封装,但那份文档已经失效了,现在它们也有可能都是直接包装自最底层的 mach thread。苹果并没有提供这两个对象相互转换的接口,但不管怎么样,可以肯定的是 pthread_t 和 NSThread 是一一对应的。比如,你可以通过 pthread_main_thread_np() 或 [NSThread mainThread] 来获取主线程;也可以通过 pthread_self() 或 [NSThread currentThread] 来获取当前线程。CFRunLoop 是基于 pthread 来管理的。苹果不允许直接创建 RunLoop,它只提供了两个自动获取的函数:CFRunLoopGetMain() 和 CFRunLoopGetCurrent()。 这两个函数内部的逻辑大概是下面这样:/// 全局的Dictionary,key 是 pthread_t, value 是 CFRunLoopRef static CFMutableDictionaryRef loopsDic; /// 访问 loopsDic 时的锁 static CFSpinLock_t loopsLock; /// 获取一个 pthread 对应的 RunLoop。 CFRunLoopRef _CFRunLoopGet(pthread_t thread) { OSSpinLockLock(&loopsLock); if (!loopsDic) { // 第一次进入时,初始化全局Dic,并先为主线程创建一个 RunLoop。 loopsDic = CFDictionaryCreateMutable(); CFRunLoopRef mainLoop = _CFRunLoopCreate(); CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop); /// 直接从 Dictionary 里获取。 CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread)); if (!loop) { /// 取不到时,创建一个 loop = _CFRunLoopCreate(); CFDictionarySetValue(loopsDic, thread, loop); /// 注册一个回调,当线程销毁时,顺便也销毁其对应的 RunLoop。 _CFSetTSD(..., thread, loop, __CFFinalizeRunLoop); OSSpinLockUnLock(&loopsLock); return loop; CFRunLoopRef CFRunLoopGetMain() { return _CFRunLoopGet(pthread_main_thread_np()); CFRunLoopRef CFRunLoopGetCurrent() { return _CFRunLoopGet(pthread_self()); }从上面的代码可以看出,线程和 RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里。线程刚创建时并没有 RunLoop,如果你不主动获取,那它一直都不会有。RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop(主线程除外)。RunLoop 对外的接口在 CoreFoundation 里面关于 RunLoop 有5个类:CFRunLoopRefCFRunLoopModeRefCFRunLoopSourceRefCFRunLoopTimerRefCFRunLoopObserverRef其中 CFRunLoopModeRef 类并没有对外暴露,只是通过 CFRunLoopRef 的接口进行了封装。他们的关系如下:一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。CFRunLoopSourceRef 是事件产生的地方。Source 有两个版本:Source0 和 Source1。Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程,其原理在下面会讲到。CFRunLoopTimerRef 是基于时间的触发器,它和 NSTimer 是toll-free bridged 的,可以混用。其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。CFRunLoopObserverRef 是观察者,每个 Observer 都包含了一个回调(函数指针),当 RunLoop 的状态发生变化时,观察者就能通过回调接受到这个变化。可以观测的时间点有以下几个:typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) { kCFRunLoopEntry = (1UL << 0), // 即将进入Loop kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timer kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠 kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒 kCFRunLoopExit = (1UL << 7), // 即将退出Loop };上面的 Source/Timer/Observer 被统称为 mode item,一个 item 可以被同时加入多个 mode。但一个 item 被重复加入同一个 mode 时是不会有效果的。如果一个 mode 中一个 item 都没有,则 RunLoop 会直接退出,不进入循环RunLoop 的 ModeCFRunLoopMode 和 CFRunLoop 的结构大致如下:struct __CFRunLoopMode { CFStringRef _name; // Mode Name, 例如 @"kCFRunLoopDefaultMode" CFMutableSetRef _sources0; // Set CFMutableSetRef _sources1; // Set CFMutableArrayRef _observers; // Array CFMutableArrayRef _timers; // Array struct __CFRunLoop { CFMutableSetRef _commonModes; // Set CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer> CFRunLoopModeRef _currentMode; // Current Runloop Mode CFMutableSetRef _modes; // Set };这里有个概念叫 "CommonModes":一个 Mode 可以将自己标记为"Common"属性(通过将其 ModeName 添加到 RunLoop 的 "commonModes" 中)。每当 RunLoop 的内容发生变化时,RunLoop 都会自动将 _commonModeItems 里的 Source/Observer/Timer 同步到具有 "Common" 标记的所有Mode里。CFRunLoop对外暴露的管理 Mode 接口只有下面2个:CFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName); CFRunLoopRunInMode(CFStringRef modeName, ...);Mode 暴露的管理 mode item 的接口有下面几个:CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName); CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName); CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode); CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName); CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName); CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode); 你只能通过 mode name 来操作内部的 mode,当你传入一个新的 mode name 但 RunLoop 内部没有对应 mode 时,RunLoop会自动帮你创建对应的 CFRunLoopModeRef。对于一个 RunLoop 来说,其内部的 mode 只能增加不能删除。苹果公开提供的 Mode 有两个:kCFRunLoopDefaultMode (NSDefaultRunLoopMode) 和 UITrackingRunLoopMode,你可以用这两个 Mode Name 来操作其对应的 Mode。同时苹果还提供了一个操作 Common 标记的字符串:kCFRunLoopCommonModes (NSRunLoopCommonModes),你可以用这个字符串来操作 Common Items,或标记一个 Mode 为 "Common"。使用时注意区分这个字符串和其他 mode name。RunLoop 的内部逻辑根据苹果在文档里的说明,RunLoop 内部的逻辑大致如下:其内部代码整理如下 (太长了不想看可以直接跳过去,后面会有说明):/// 用DefaultMode启动 void CFRunLoopRun(void) { CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false); /// 用指定的Mode启动,允许设置RunLoop超时时间 int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) { return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled); /// RunLoop的实现 int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) { /// 首先根据modeName找到对应mode CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName, false); /// 如果mode里没有source/timer/observer, 直接返回。 if (__CFRunLoopModeIsEmpty(currentMode)) return; /// 1. 通知 Observers: RunLoop 即将进入 loop。 __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry); /// 内部函数,进入loop __CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) { Boolean sourceHandledThisLoop = NO; int retVal = 0; /// 2. 通知 Observers: RunLoop 即将触发 Timer 回调。 __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers); /// 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。 __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources); /// 执行被加入的block __CFRunLoopDoBlocks(runloop, currentMode); /// 4. RunLoop 触发 Source0 (非port) 回调。 sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle); /// 执行被加入的block __CFRunLoopDoBlocks(runloop, currentMode); /// 5. 如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。 if (__Source0DidDispatchPortLastTime) { Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg) if (hasMsg) goto handle_msg; /// 通知 Observers: RunLoop 的线程即将进入休眠(sleep)。 if (!sourceHandledThisLoop) { __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting); /// 7. 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。 /// • 一个基于 port 的Source 的事件。 /// • 一个 Timer 到时间了 /// • RunLoop 自身的超时时间到了 /// • 被其他什么调用者手动唤醒 __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) { mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg /// 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了。 __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting); /// 收到消息,处理消息。 handle_msg: /// 9.1 如果一个 Timer 到时间了,触发这个Timer的回调。 if (msg_is_timer) { __CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time()) /// 9.2 如果有dispatch到main_queue的block,执行block。 else if (msg_is_dispatch) { __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg); /// 9.3 如果一个 Source1 (基于port) 发出事件了,处理这个事件 else { CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort); sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg); if (sourceHandledThisLoop) { mach_msg(reply, MACH_SEND_MSG, reply); /// 执行加入到Loop的block __CFRunLoopDoBlocks(runloop, currentMode); if (sourceHandledThisLoop && stopAfterHandle) { /// 进入loop时参数说处理完事件就返回。 retVal = kCFRunLoopRunHandledSource; } else if (timeout) { /// 超出传入参数标记的超时时间了 retVal = kCFRunLoopRunTimedOut; } else if (__CFRunLoopIsStopped(runloop)) { /// 被外部调用者强制停止了 retVal = kCFRunLoopRunStopped; } else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) { /// source/timer/observer一个都没有了 retVal = kCFRunLoopRunFinished; /// 如果没超时,mode里没空,loop也没被停止,那继续loop。 } while (retVal == 0); /// 10. 通知 Observers: RunLoop 即将退出。 __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); }可以看到,实际上 RunLoop 就是这样一个函数,其内部是一个 do-while 循环。当你调用 CFRunLoopRun() 时,线程就会一直停留在这个循环里;直到超时或被手动停止,该函数才会返回。RunLoop 的底层实现从上面代码可以看到,RunLoop 的核心是基于 mach port 的,其进入休眠时调用的函数是 mach_msg()。为了解释这个逻辑,下面稍微介绍一下 OSX/iOS 的系统架构。苹果官方将整个系统大致划分为上述4个层次:应用层包括用户能接触到的图形应用,例如 Spotlight、Aqua、SpringBoard 等。应用框架层即开发人员接触到的 Cocoa 等框架。核心框架层包括各种核心框架、OpenGL 等内容。Darwin 即操作系统的核心,包括系统内核、驱动、Shell 等内容,这一层是开源的,其所有源码都可以在 opensource.apple.com 里找到。我们在深入看一下 Darwin 这个核心的架构:其中,在硬件层上面的三个组成部分:Mach、BSD、IOKit (还包括一些上面没标注的内容),共同组成了 XNU 内核。XNU 内核的内环被称作 Mach,其作为一个微内核,仅提供了诸如处理器调度、IPC (进程间通信)等非常少量的基础服务。BSD 层可以看作围绕 Mach 层的一个外环,其提供了诸如进程管理、文件系统和网络等功能。IOKit 层是为设备驱动提供了一个面向对象(C++)的一个框架。Mach 本身提供的 API 非常有限,而且苹果也不鼓励使用 Mach 的 API,但是这些API非常基础,如果没有这些API的话,其他任何工作都无法实施。在 Mach 中,所有的东西都是通过自己的对象实现的,进程、线程和虚拟内存都被称为"对象"。和其他架构不同, Mach 的对象间不能直接调用,只能通过消息传递的方式实现对象间的通信。"消息"是 Mach 中最基础的概念,消息在两个端口 (port) 之间传递,这就是 Mach 的 IPC (进程间通信) 的核心。Mach 的消息定义是在 <mach/message.h> 头文件的,很简单:typedef struct { mach_msg_header_t header; mach_msg_body_t body; } mach_msg_base_t; typedef struct { mach_msg_bits_t msgh_bits; mach_msg_size_t msgh_size; mach_port_t msgh_remote_port; mach_port_t msgh_local_port; mach_port_name_t msgh_voucher_port; mach_msg_id_t msgh_id; } mach_msg_header_t;一条 Mach 消息实际上就是一个二进制数据包 (BLOB),其头部定义了当前端口 local_port 和目标端口 remote_port,发送和接受消息是通过同一个 API 进行的,其 option 标记了消息传递的方向:mach_msg_return_t mach_msg( mach_msg_header_t *msg, mach_msg_option_t option, mach_msg_size_t send_size, mach_msg_size_t rcv_size, mach_port_name_t rcv_name, mach_msg_timeout_t timeout, mach_port_name_t notify);为了实现消息的发送和接收,mach_msg() 函数实际上是调用了一个 Mach 陷阱 (trap),即函数 mach_msg_trap(),陷阱这个概念在 Mach 中等同于系统调用。当你在用户态调用 mach_msg_trap() 时会触发陷阱机制,切换到内核态;内核态中内核实现的 mach_msg() 函数会完成实际的工作,如下图:这些概念可以参考维基百科: System_call、Trap_(computing))。RunLoop 的核心就是一个 mach_msg() (见上面代码的第7步),RunLoop 调用这个函数去接收消息,如果没有别人发送 port 消息过来,内核会将线程置于等待状态。例如你在模拟器里跑起一个 iOS 的 App,然后在 App 静止时点击暂停,你会看到主线程调用栈是停留在 mach_msg_trap() 这个地方。关于具体的如何利用 mach port 发送信息,可以看看 NSHipster 这一篇文章,或者这里的中文翻译 。关于Mach的历史可以看看这篇很有趣的文章:Mac OS X 背后的故事(三)Mach 之父 Avie Tevanian。苹果用 RunLoop 实现的功能首先我们可以看一下 App 启动后 RunLoop 的状态:CFRunLoop { current mode = kCFRunLoopDefaultMode common modes = { UITrackingRunLoopMode kCFRunLoopDefaultMode common mode items = { // source0 (manual) CFRunLoopSource {order =-1, { callout = _UIApplicationHandleEventQueue}} CFRunLoopSource {order =-1, { callout = PurpleEventSignalCallback }} CFRunLoopSource {order = 0, { callout = FBSSerialQueueRunLoopSourceHandler}} // source1 (mach port) CFRunLoopSource {order = 0, {port = 17923}} CFRunLoopSource {order = 0, {port = 12039}} CFRunLoopSource {order = 0, {port = 16647}} CFRunLoopSource {order =-1, { callout = PurpleEventCallback}} CFRunLoopSource {order = 0, {port = 2407, callout = _ZL20notify_port_callbackP12__CFMachPortPvlS1_}} CFRunLoopSource {order = 0, {port = 1c03, callout = __IOHIDEventSystemClientAvailabilityCallback}} CFRunLoopSource {order = 0, {port = 1b03, callout = __IOHIDEventSystemClientQueueCallback}} CFRunLoopSource {order = 1, {port = 1903, callout = __IOMIGMachPortPortCallback}} // Ovserver CFRunLoopObserver {order = -2147483647, activities = 0x1, // Entry callout = _wrapRunLoopWithAutoreleasePoolHandler} CFRunLoopObserver {order = 0, activities = 0x20, // BeforeWaiting callout = _UIGestureRecognizerUpdateObserver} CFRunLoopObserver {order = 1999000, activities = 0xa0, // BeforeWaiting | Exit callout = _afterCACommitHandler} CFRunLoopObserver {order = 2000000, activities = 0xa0, // BeforeWaiting | Exit callout = _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv} CFRunLoopObserver {order = 2147483647, activities = 0xa0, // BeforeWaiting | Exit callout = _wrapRunLoopWithAutoreleasePoolHandler} // Timer CFRunLoopTimer {firing = No, interval = 3.1536e+09, tolerance = 0, next fire date = 453098071 (-4421.76019 @ 96223387169499), callout = _ZN2CAL14timer_callbackEP16__CFRunLoopTimerPv (QuartzCore.framework)} modes = { CFRunLoopMode { sources0 = { /* same as 'common mode items' */ }, sources1 = { /* same as 'common mode items' */ }, observers = { /* same as 'common mode items' */ }, timers = { /* same as 'common mode items' */ }, CFRunLoopMode { sources0 = { /* same as 'common mode items' */ }, sources1 = { /* same as 'common mode items' */ }, observers = { /* same as 'common mode items' */ }, timers = { /* same as 'common mode items' */ }, CFRunLoopMode { sources0 = { CFRunLoopSource {order = 0, { callout = FBSSerialQueueRunLoopSourceHandler}} sources1 = (null), observers = { CFRunLoopObserver >{activities = 0xa0, order = 2000000, callout = _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv} timers = (null), CFRunLoopMode { sources0 = { CFRunLoopSource {order = -1, { callout = PurpleEventSignalCallback}} sources1 = { CFRunLoopSource {order = -1, { callout = PurpleEventCallback}} observers = (null), timers = (null), CFRunLoopMode { sources0 = (null), sources1 = (null), observers = (null), timers = (null), }可以看到,系统默认注册了5个Mode:kCFRunLoopDefaultMode: App的默认 Mode,通常主线程是在这个 Mode 下运行的。UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。kCFRunLoopCommonModes: 这是一个占位的 Mode,没有实际作用。你可以在这里看到更多的苹果内部的 Mode,但那些 Mode 在开发中就很难遇到了。当 RunLoop 进行回调时,一般都是通过一个很长的函数调用出去 (call out), 当你在你的代码中下断点调试时,通常能在调用栈上看到这些函数。下面是这几个函数的整理版本,如果你在调用栈中看到这些长函数名,在这里查找一下就能定位到具体的调用地点了:{ /// 1. 通知Observers,即将进入RunLoop /// 此处有Observer会创建AutoreleasePool: _objc_autoreleasePoolPush(); __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry); /// 2. 通知 Observers: 即将触发 Timer 回调。 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeTimers); /// 3. 通知 Observers: 即将触发 Source (非基于port的,Source0) 回调。 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeSources); __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block); /// 4. 触发 Source0 (非基于port的) 回调。 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0); __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block); /// 6. 通知Observers,即将进入休眠 /// 此处有Observer释放并新建AutoreleasePool: _objc_autoreleasePoolPop(); _objc_autoreleasePoolPush(); __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeWaiting); /// 7. sleep to wait msg. mach_msg() -> mach_msg_trap(); /// 8. 通知Observers,线程被唤醒 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopAfterWaiting); /// 9. 如果是被Timer唤醒的,回调Timer __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(timer); /// 9. 如果是被dispatch唤醒的,执行所有调用 dispatch_async 等方法放入main queue 的 block __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(dispatched_block); /// 9. 如果如果Runloop是被 Source1 (基于port的) 的事件唤醒了,处理这个事件 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(source1); } while (...); /// 10. 通知Observers,即将退出RunLoop /// 此处有Observer释放AutoreleasePool: _objc_autoreleasePoolPop(); __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopExit); }AutoreleasePoolApp启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647(2^31-1),优先级最低,保证其释放池子发生在其他所有回调之后。在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被 RunLoop 创建好的 AutoreleasePool 环绕着,所以不会出现内存泄漏,开发者也不必显示创建 Pool 了。事件响应苹果注册了一个 Source1 (基于 mach port 的) 用来接收系统事件,其回调函数为 __IOHIDEventSystemClientQueueCallback()。当一个硬件事件(触摸/锁屏/摇晃等)发生后,首先由 IOKit.framework 生成一个 IOHIDEvent 事件并由 SpringBoard 接收。这个过程的详细情况可以参考这里。SpringBoard 只接收按键(锁屏/静音等),触摸,加速,接近传感器等几种 Event,随后用 mach port 转发给需要的App进程。随后苹果注册的那个 Source1 就会触发回调,并调用 _UIApplicationHandleEventQueue() 进行应用内部的分发。_UIApplicationHandleEventQueue() 会把 IOHIDEvent 处理并包装成 UIEvent 进行处理或分发,其中包括识别 UIGesture/处理屏幕旋转/发送给 UIWindow 等。通常事件比如 UIButton 点击、touchesBegin/Move/End/Cancel 事件都是在这个回调中完成的。手势识别当上面的 _UIApplicationHandleEventQueue() 识别了一个手势时,其首先会调用 Cancel 将当前的 touchesBegin/Move/End 系列回调打断。随后系统将对应的 UIGestureRecognizer 标记为待处理。苹果注册了一个 Observer 监测 BeforeWaiting (Loop即将进入休眠) 事件,这个Observer的回调函数是 _UIGestureRecognizerUpdateObserver(),其内部会获取所有刚被标记为待处理的 GestureRecognizer,并执行GestureRecognizer的回调。当有 UIGestureRecognizer 的变化(创建/销毁/状态改变)时,这个回调都会进行相应处理。界面更新当在操作 UI 时,比如改变了 Frame、更新了 UIView/CALayer 的层次时,或者手动调用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后,这个 UIView/CALayer 就被标记为待处理,并被提交到一个全局的容器去。苹果注册了一个 Observer 监听 BeforeWaiting(即将进入休眠) 和 Exit (即将退出Loop) 事件,回调去执行一个很长的函数:_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()。这个函数里会遍历所有待处理的 UIView/CALayer 以执行实际的绘制和调整,并更新 UI 界面。这个函数内部的调用栈大概是这样的:_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv() QuartzCore:CA::Transaction::observer_callback: CA::Transaction::commit(); CA::Context::commit_transaction(); CA::Layer::layout_and_display_if_needed(); CA::Layer::layout_if_needed(); [CALayer layoutSublayers]; [UIView layoutSubviews]; CA::Layer::display_if_needed(); [CALayer display]; [UIView drawRect];定时器NSTimer 其实就是 CFRunLoopTimerRef,他们之间是 toll-free bridged 的。一个 NSTimer 注册到 RunLoop 后,RunLoop 会为其重复的时间点注册好事件。例如 10:00, 10:10, 10:20 这几个时间点。RunLoop为了节省资源,并不会在非常准确的时间点回调这个Timer。Timer 有个属性叫做 Tolerance (宽容度),标示了当时间点到后,容许有多少最大误差。如果某个时间点被错过了,例如执行了一个很长的任务,则那个时间点的回调也会跳过去,不会延后执行。就比如等公交,如果 10:10 时我忙着玩手机错过了那个点的公交,那我只能等 10:20 这一趟了。CADisplayLink 是一个和屏幕刷新率一致的定时器(但实际实现原理更复杂,和 NSTimer 并不一样,其内部实际是操作了一个 Source)。如果在两次屏幕刷新之间执行了一个长任务,那其中就会有一帧被跳过去(和 NSTimer 相似),造成界面卡顿的感觉。在快速滑动TableView时,即使一帧的卡顿也会让用户有所察觉。Facebook 开源的 AsyncDisplayLink 就是为了解决界面卡顿的问题,其内部也用到了 RunLoop,这个稍后我会再单独写一页博客来分析。PerformSelecter当调用 NSObject 的 performSelecter:afterDelay: 后,实际上其内部会创建一个 Timer 并添加到当前线程的 RunLoop 中。所以如果当前线程没有 RunLoop,则这个方法会失效。当调用 performSelector:onThread: 时,实际上其会创建一个 Timer 加到对应的线程去,同样的,如果对应线程没有 RunLoop 该方法也会失效。关于GCD实际上 RunLoop 底层也会用到 GCD 的东西,比如 RunLoop 是用 dispatch_source_t 实现的 Timer(评论中有人提醒,NSTimer 是用了 XNU 内核的 mk_timer,我也仔细调试了一下,发现 NSTimer 确实是由 mk_timer 驱动,而非 GCD 驱动的)。但同时 GCD 提供的某些接口也用到了 RunLoop, 例如 dispatch_async()。当调用 dispatch_async(dispatch_get_main_queue(), block) 时,libDispatch 会向主线程的 RunLoop 发送消息,RunLoop会被唤醒,并从消息中取得这个 block,并在回调 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__() 里执行这个 block。但这个逻辑仅限于 dispatch 到主线程,dispatch 到其他线程仍然是由 libDispatch 处理的。关于网络请求iOS 中,关于网络请求的接口自下至上有如下几层:CFSocket CFNetwork ->ASIHttpRequest NSURLConnection ->AFNetworking NSURLSession ->AFNetworking2, AlamofireCFSocket 是最底层的接口,只负责 socket 通信。CFNetwork 是基于 CFSocket 等接口的上层封装,ASIHttpRequest 工作于这一层。NSURLConnection 是基于 CFNetwork 的更高层的封装,提供面向对象的接口,AFNetworking 工作于这一层。NSURLSession 是 iOS7 中新增的接口,表面上是和 NSURLConnection 并列的,但底层仍然用到了 NSURLConnection 的部分功能 (比如 com.apple.NSURLConnectionLoader 线程),AFNetworking2 和 Alamofire 工作于这一层。下面主要介绍下 NSURLConnection 的工作过程。通常使用 NSURLConnection 时,你会传入一个 Delegate,当调用了 [connection start] 后,这个 Delegate 就会不停收到事件回调。实际上,start 这个函数的内部会会获取 CurrentRunLoop,然后在其中的 DefaultMode 添加了4个 Source0 (即需要手动触发的Source)。CFMultiplexerSource 是负责各种 Delegate 回调的,CFHTTPCookieStorage 是处理各种 Cookie 的。当开始网络传输时,我们可以看到 NSURLConnection 创建了两个新线程:com.apple.NSURLConnectionLoader 和 com.apple.CFSocket.private。其中 CFSocket 线程是处理底层 socket 连接的。NSURLConnectionLoader 这个线程内部会使用 RunLoop 来接收底层 socket 的事件,并通过之前添加的 Source0 通知到上层的 Delegate。NSURLConnectionLoader 中的 RunLoop 通过一些基于 mach port 的 Source 接收来自底层 CFSocket 的通知。当收到通知后,其会在合适的时机向 CFMultiplexerSource 等 Source0 发送通知,同时唤醒 Delegate 线程的 RunLoop 来让其处理这些通知。CFMultiplexerSource 会在 Delegate 线程的 RunLoop 对 Delegate 执行实际的回调。RunLoop 的实际应用举例AFNetworkingAFURLConnectionOperation 这个类是基于 NSURLConnection 构建的,其希望能在后台线程接收 Delegate 回调。为此 AFNetworking 单独创建了一个线程,并在这个线程中启动了一个 RunLoop:+ (void)networkRequestThreadEntryPoint:(id)__unused object { @autoreleasepool { [[NSThread currentThread] setName:@"AFNetworking"]; NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; [runLoop run]; + (NSThread *)networkRequestThread { static NSThread *_networkRequestThread = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil]; [_networkRequestThread start]; return _networkRequestThread; }RunLoop 启动前内部必须要有至少一个 Timer/Observer/Source,所以 AFNetworking 在 [runLoop run] 之前先创建了一个新的 NSMachPort 添加进去了。通常情况下,调用者需要持有这个 NSMachPort (mach_port) 并在外部线程通过这个 port 发送消息到 loop 内;但此处添加 port 只是为了让 RunLoop 不至于退出,并没有用于实际的发送消息。- (void)start { [self.lock lock]; if ([self isCancelled]) { [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } else if ([self isReady]) { self.state = AFOperationExecutingState; [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; [self.lock unlock]; }当需要这个后台线程执行任务时,AFNetworking 通过调用 [NSObject performSelector:onThread:..] 将这个任务扔到了后台线程的 RunLoop 中。AsyncDisplayKitAsyncDisplayKit 是 Facebook 推出的用于保持界面流畅性的框架,其原理大致如下:UI 线程中一旦出现繁重的任务就会导致界面卡顿,这类任务通常分为3类:排版,绘制,UI对象操作。排版通常包括计算视图大小、计算文本高度、重新计算子式图的排版等操作。绘制一般有文本绘制 (例如 CoreText)、图片绘制 (例如预先解压)、元素绘制 (Quartz)等操作。UI对象操作通常包括 UIView/CALayer 等 UI 对象的创建、设置属性和销毁。其中前两类操作可以通过各种方法扔到后台线程执行,而最后一类操作只能在主线程完成,并且有时后面的操作需要依赖前面操作的结果 (例如TextView创建时可能需要提前计算出文本的大小)。ASDK 所做的,就是尽量将能放入后台的任务放入后台,不能的则尽量推迟 (例如视图的创建、属性的调整)。为此,ASDK 创建了一个名为 ASDisplayNode 的对象,并在内部封装了 UIView/CALayer,它具有和 UIView/CALayer 相似的属性,例如 frame、backgroundColor等。所有这些属性都可以在后台线程更改,开发者可以只通过 Node 来操作其内部的 UIView/CALayer,这样就可以将排版和绘制放入了后台线程。但是无论怎么操作,这些属性总需要在某个时刻同步到主线程的 UIView/CALayer 去。ASDK 仿照 QuartzCore/UIKit 框架的模式,实现了一套类似的界面更新的机制:即在主线程的 RunLoop 中添加一个 Observer,监听了 kCFRunLoopBeforeWaiting 和 kCFRunLoopExit 事件,在收到回调时,遍历所有之前放入队列的待处理的任务,然后一一执行。具体的代码可以看这里_ASAsyncTransactionGroup。
autojs非常见函数1
牙叔教程 简单易懂隐藏导航栏-- 惜缘View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY隐藏标题栏-- 大柒activity.requestWindowFeature(Window.FEATURE_NO_TITLE);img设置自带图标let RID = resources.getIdentifier("ic_accessibility_black_48dp", "drawable", context.getPackageName()); view.setImageResource(RID);全屏-- 大柒"ui"; importClass(android.view.WindowManager); full(true); //是否全屏 function full(enable) { if (enable) { //设置全屏 let lp = activity.getWindow().getAttributes(); lp.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; activity.getWindow().setAttributes(lp); activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); } else { //取消全屏 let attr = activity.getWindow().getAttributes(); attr.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN; activity.getWindow().setAttributes(attr); activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); 全屏--惜缘"ui"; importClass(android.view.WindowManager); importClass(android.view.View); activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); activity .getWindow() .getDecorView() .setSystemUiVisibility( android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | android.view.View.SYSTEM_UI_FLAG_FULLSCREEN ui.layout( <vertical> <img layout_gravity="center" id="img" w="*" h="*" bg="#0000ff" scaleType="centerCrop" src="file://./111.jpg" /> </vertical> );彩图转灰度图images.cvtColor(img, "RGBA2GRAY");手机底部导航栏高度-- 大柒"ui"; var realPoint = new android.graphics.Point(); activity.getWindowManager().getDefaultDisplay().getRealSize(realPoint); var height = activity.getWindowManager().getDefaultDisplay().getHeight(); var navigation_bar_height = realPoint.y - height; console.log("navigation_bar_height", navigation_bar_height);设置Material主题-- ╰つ゛无名大姐‭activity.theme.applyStyle(ui.R.style["Base.V14.Theme.Material3.Dark"], true);"ui"; context.theme.applyStyle(ui.R.style["Base.V14.Theme.Material3.Dark"], true); ui.layout( <vertical> <vertical w="*" h="auto" id="ver"></vertical> </vertical> importClass("com.google.android.material.textfield.TextInputLayout"); aa = new TextInputLayout(context); aa.setHint("请输入用户名"); aa.addView(new android.widget.EditText(context)); ui.ver.addView(aa); aa = new TextInputLayout(context); aa.setHint("请输入密码"); aa.addView(new android.widget.EditText(context)); ui.ver.addView(aa); 修改按钮背景色-- 抠脚-- 大柒view.getBackground().setColorFilter(colors.RED, PorterDuff$Mode.MULTIPLY); view.attr('backgroundTint',colorstr);打开文件选择器intent = new Intent(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("image/*"); activity.startActivityForResult(Intent.createChooser(intent, "选择图片"), 1);指定参数类型// 方法 指定参数类型 p["setShadowLayer(float,float,float,int)"](100, 0, 0, new java.lang.Long(Color.RED)); p["setShadowLayer(float,float,float,int)"](100, 0, 0, $colors.RED); // 构造函数 指定参数类型 let radialGradient = new RadialGradient["(float,float,float,int[],float[],android.graphics.Shader$TileMode)"]( centerX, centerY, radius, radialColors, positionList, Shader.TileMode.REPEAT );判断手机32还是64位importClass(java.lang.System); importClass(android.os.Build); let mCPU = Build.CPU_ABI == "arm64-v8a" ? "64位" : "32位"; let mCPU2 = System.getProperty("os.arch").indexOf("64") != -1 ? "64位" : "32位"; log(mCPU + " " + mCPU2);java类是否有某个字段function hasField(classFullName, fieldName) { classFullName = classFullName || "android.view.WindowManager$LayoutParams"; fieldName = fieldName || "LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES"; let clazz = java.lang.Class.forName(classFullName); let fields = clazz.getDeclaredFields(); let bool = false; for (let i = 0; i < fields.length; i++) { if (fields[i].getName().equals(fieldName)) { bool = true; break; return bool; }内置图标转drawableconst resources = context.getResources(); let imgId = resources.getIdentifier("ic_keyboard_arrow_left_black_48dp", "drawable", context.getPackageName()); let drawable = resources.getDrawable(imgId);获取当前包名-- @Ai:下个版本修复不受限制的问题const _current_pacakge = currentPackage; let currentPackage = function () { let start = new Date().getTime(); try { if (!runtime.getAccessibilityBridge()) { return _current_pacakge(); // 通过windowRoot获取根控件的包名,理论上返回一个 速度较快 let windowRoots = runtime.getAccessibilityBridge().windowRoots(); if (windowRoots && windowRoots.size() > 0) { log(["windowRoots size: {}", windowRoots.size()]); for (let i = 0; i < windowRoots.size(); i++) { let root = windowRoots.get(i); if (root !== null && root.getPackageName()) { return root.getPackageName(); // windowRoot获取失败了通过service.getWindows获取根控件的包名,按倒序从队尾开始获取 速度相对慢一点 let service = runtime.getAccessibilityBridge().getService(); let serviceWindows = service !== null ? service.getWindows() : null; if (serviceWindows && serviceWindows.size() > 0) { log(["windowRoots未能获取包名信息,尝试service window size: {}", serviceWindows.size()]); for (let i = serviceWindows.size() - 1; i >= 0; i--) { let window = serviceWindows.get(i); if (window && window.getRoot() && window.getRoot().getPackageName()) { return window.getRoot().getPackageName(); log(["service.getWindows未能获取包名信息,通过currentPackage()返回数据"]); // 以上方法无法获取的,直接按原方法获取包名 return _current_pacakge(); } finally { log(["获取包名总耗时:{}ms", new Date().getTime() - start]); log(currentPackage()); 主动调用按钮点击事件"ui"; ui.layout( <vertical> <button id="btn">按钮 </button> </vertical> ui.btn.click(function() { toastLog("牙叔教程\n简单易懂") // 注意要在UI线程中,执行点击代码。 ui.post( function() { ui.btn.performClick(); },2000 )设置按钮三秒后才能点击"ui"; ui.layout( <vertical> <button id="btn" text="按钮" /> </vertical> ui.btn.click(function () { toastLog("按钮被点击"); ui.btn.setText("按钮已被点击"); ui.btn.setEnabled(false); setTimeout(function () { ui.btn.setEnabled(true); ui.btn.setText("按钮"); }, 3000); });一开始不要显示输入法activity.getWindow().setSoftInputMode(android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);关闭输入法"ui"; ui.layout( <vertical> <button id="btn">关闭输入法</button> <input w="*" id="input" /> </vertical> ui.btn.on("click", () => { //取消焦点代码 ui.input.clearFocus(); //隐藏键盘代码 let imm = ui.btn.getRootView().getContext().getSystemService(context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(ui.btn.getWindowToken(), android.view.inputmethod.InputMethodManager.HIDE_NOT_ALWAYS); });状态栏高度const resources = context.getResources(); const status_bar_height = resources.getDimensionPixelSize(resources.getIdentifier("status_bar_height", "dimen", "android"));状态栏高度2let rect = new android.graphics.Rect(); activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); statusBarHeights = rect.top;获取图片在ImageView中的坐标function getImageBounds(imageView) { let bounds = new android.graphics.RectF(); drawable = imageView.getDrawable(); if (drawable != null) { imageView.getImageMatrix().mapRect(bounds, new android.graphics.RectF(drawable.getBounds())); return bounds; }java多线程new java.lang.Thread( new java.lang.Runnable(function run() { toastLog("Thread Runnable"); ).start();adb查看内存信息参考: adb shell dumpsys meminfo 详解adb shell dumpsys 命令 查看内存adb shell dumpsys meminfo org.autojs.autojspro查看单个应用最大内存限制adb shell "getprop|grep heapgrowthlimit" // C:\Users\Administrator>adb shell "getprop|grep heapgrowthlimit" // [dalvik.vm.heapgrowthlimit]: [256m]根据进程的名字使用 grep 指令过滤输出 "org.autojs.autojspro" 进程的信息-d: 时间间隔, -d 3, 间隔3秒参考: adb shell top 命令adb shell top -d 3 | grep "org.autojs.autojspro"下拉框spinner --品尚名品 "ui"; ui.layout( <vertical padding="16"> <horizontal> <text textSize="16sp">下拉菜单</text> <spinner id="sp1" entries="aaa|bbb|ccc" /> </horizontal> <horizontal> <text textSize="16sp">对话框菜单</text> <spinner id="sp2" entries="ddd|eee|fff" spinnerMode="dialog" /> </horizontal> <button id="ok">确定</button> <button id="select3">设置指定选项</button> </vertical> ui.ok.on("click", () => { //获取选中项对应的索引值value var i = ui.sp1.getSelectedItemPosition(); var j = ui.sp2.getSelectedItemPosition(); toastLog("您的选择是选项" + i + "和选项" + j); var z = ui.sp1.getSelectedItem().toString(); var x = ui.sp2.getSelectedItem().toString(); toastLog("您的选择内容是" + z + "和" + x); ui.select3.on("click", () => { //根据索引值value设定选项 ui.sp1.setSelection(2); //获取ui.sp1 Adapter对象 ui.sp1.getAdapter(); //获取ui.sp1 下共有几个选项 ui.sp1.getAdapter().getCount(); //获取ui.sp1 对象下指定值的索引位置 ui.sp1.getAdapter().getPosition("ccc"); //根据值直接设置下拉菜单选中项 必须准确填写getPosition("")里的值 否则会出错 ui.sp1.setSelection(ui.sp1.getAdapter().getPosition("ccc")); //先判断值是否存在再设置 不存在的情况下不会出错 let 设定值 = "你好"; if (ui.sp1.getAdapter().getPosition(设定值) != -1) { ui.sp1.setSelection(ui.sp1.getAdapter().getPosition(设定值)); } else { toastLog("该选项不存在: " + 设定值 + " 无法完成设置!"); drawable转Bitmap--稻草人importClass(android.graphics.Bitmap); importClass(android.graphics.Canvas); importClass(android.graphics.PixelFormat); function drawableToBitmap(drawable) { let bitmap = Bitmap.createBitmap( drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); let canvas = new Canvas(bitmap); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); drawable.draw(canvas); return bitmap; }横竖屏判断--抠脚本人var a = context.resources.configuration.orientation; if (a === 1) { toastLog("这是竖屏!!"); } else { toastLog("这是横屏!!"); }获取屏幕旋转的方向var windowManager = context.getSystemService(Context.WINDOW_SERVICE); //by 板蓝根isatis function getRotation() { return windowManager.getDefaultDisplay().getRotation(); //0 1 2 3分别对应 上 左 下 右指定引擎运行代码let src = new com.stardust.autojs.script.StringScriptSource("<code>", "toast(1)"); engines.myEngine().execute(src);获取图片所有像素点的值let img = images.read("/sdcard/1.png"); let r = getPixelsOfBitmap(img.bitmap); log(r); img.recycle(); function getPixelsOfBitmap(bitmap) { let width = bitmap.getWidth(); let height = bitmap.getHeight(); console.log("width=", width, " height=", height); let pixels = util.java.array("int", width * height); let offset = 0; let stride = width; let x = 0; let y = 0; bitmap.getPixels(pixels, offset, stride, x, y, width, height); return pixels; }或者用图片路径, 也可以获取所有像素点images.readPixels(path)path {string} 图片的地址返回 {Object} 包括图片的像素数据和宽高,{data,width,height}读取图片的像素数据和宽高。保存画板canvas上的图片-- @No evil ·火封let bitmap = android.graphics.Bitmap.createBitmap(600, 600, android.graphics.Bitmap.Config.ARGB_8888); let canvas = new Canvas(bitmap); let paint = new Paint(); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(100); //设置画笔宽度 paint.setColor(colors.parseColor("#FF000000")); canvas.drawRect(100, 100, 500, 500, paint); let img = canvas.toImage(); let imgPath = "/sdcard/1.png"; img.saveTo(imgPath); app.viewFile(imgPath); img.recycle();启用悬浮控制条,控制脚本worker.js$engines.startFloatingController("./worker.js");crash-- @筱枫 app.startActivity({ packageName: "org.autojs.autojspro", className: "com.stardust.autojs.core.activity.CrashReportActivity", });类增加属性"nodejs"; require("rhino").install(); const ArrayList = java.util.ArrayList; const list = new ArrayList(); list.add(123); ArrayList.prototype.first = function() { return this.get(0); console.log(list.first()); bitmap转Image对象-- @I'm zz var img = com.stardust.autojs.core.image.ImageWrapper.ofBitmap(bitmap);let autojsMat = new com.stardust.autojs.core.opencv.Mat(mat1, new org.opencv.core.Rect(0, 0, w, h)); var img = com.stardust.autojs.core.image.ImageWrapper.ofMat(autojsMat);选择图片$images.select().on("result", img => {}) on("process") // 可以获取到URI继承java抽象类--李小波 JavaAdapter(需要重写的类,{重写的方法},参数...)//参数决定使用哪个构造方法 autojs实现抽象类的继承// 无障碍异步监听 auto.registerEvent("view_clicked", (e) => { js += 'id("' + e.source.id() + '").text("' + e.source.text() + '").desc("' + e.source.desc() + '").findOne().click();\nsleep(500);\n'; });// 判断稳定模式 toast(serviceFlag = auto.service.serviceInfo.flags == 122 ? "正常" : "稳定模式")// 过滤窗口 function 查找指定app窗口(appName) { log(arguments.callee.name); let windowList = auto.windows; var len = windowList.length; for (var i = 0; i < len; i++) { let item = windowList[i]; if (item.title) { if (item.title === appName) { return item; throw new Error("没有找到指定app窗口: " + appName); function 获取当前页面内容(appName) { log(arguments.callee.name); let window = 查找指定app窗口(appName); let views = com.stardust.automator.UiObject.Companion.createRoot(window.getRoot()).find( idEndsWith("tv_title").visibleToUser(true).boundsInside(0, 0, device.width, device.height) if (views && views.length > 0) { let arr = []; var len = views.length; for (var i = 0; i < len; i++) { let item = views[i]; arr.push(item.text()); return arr.join(", "); } else { return ""; }// 无障碍返回 auto.service.performGlobalAction(1);// 获取自己的app的控件缓存 View.getDrawable().getBitmap()// 清空图片缓存 $ui.imageCache.clearDiskCache(); $ui.imageCache.clearMemory();// 设置光标位置 ui.授权码.setOnFocusChangeListener({ onFocusChange: function(view, hasFocus) { if (hasFocus) { view.setInputType(144); view.setSelection(view.getText().length()); } else { view.setInputType(129); cardStr = view.text() });context.getSystemService(context.USAGE_STATS_SERVICE);// 这个值就是ui绑定变量的上下文 runtime.ui.bindingContextpool.shutdownNow(); pool.shutdown(); let status = pool.awaitTermination(1000, java.util.concurrent.TimeUnit.SECONDS);new RootAutomator({adb: true})runtime.gc()console.error($debug.getStackTrace(e))// 这行代码也许能刷新节点 auto.service.serviceInfo = auto.service.serviceInfo; auto.clearCache();便签 = id("rich_editor").findOne() toast(便签.text()) sleep(2000) 便签.refresh() toast(便签.text())// 当前包名 context.packageNamenew java.util.Timer().schedule(new java.util.TimerTask({ run: () => { launch(context.getPackageName()) }), 2000); java.lang.Runtime.getRuntime().exit(0);// 重启自己 "ui"; let args = $engines.myEngine().execArgv; let id = (args && args["id"]) || 0; ui.layout( <frame> <text text="页面{{id}}"/> </frame> if (id === 0) { toast("2秒后进入页面1"); ui.post(() => { $engines.execScriptFile("./main.js", { arguments: { id: id + 1 }, 2000) } else if (id === 1) { toast("2秒后重启"); ui.post(() => { let Process = android.os.Process; Process.killProcess(Process.myPid()) }, 2000) }requestScreenCaptureAsync(); // 返回promisenew Shell().exec("logcat -f /sdcard/脚本/log.txt");var dcl=new Packages.dalvik.system.DexClassLoader(dexpath, dirpath, jnipath, java.lang.ClassLoader.getSystemClassLoader()); var cls=dcl.loadClass("com.jia.testso.Main"); var api=cls.newInstance(); api.init(context.getClass().getClassLoader()); $shell.isRootAvailable(); $shell.checkAccess("root");$shell.setDefaultOptions({adb: true})// 屏幕方向 context.resources.configuration.orientation
HTML+CSS入门到精通
一、前端简介1、软件的分类1.1、 系统软件WindowsLinuxmacOS1.2、 应用软件OfficeQQ1.3、 游戏软件绝地求生王者荣耀2、 客户端与服务器通常情况下,现在的软件一般由两个部分组成:客户端:用户通过客户端来使用软件。服务器:服务器负责在远程处理业务逻辑。2.1、 服务器服务器开发的语言:JavaPHPC#PythonNode.js……2.2、 客户端客户端的形式文字客户端:占老的方式,通过命令行来使用软件图形化界面:通过点击拖动等来使用软件。Windows 中、macOS 中、Android、iOS 中的大部分应用。(C/S 架构)网页:通过访问网页来使用软件。所有的网站都属于这个范畴。(B/S 架构)3、 网页的特点相较于传统的图形化界面,网页具有如下一些优点:不需要安装无需更新跨平台网页中使用的语言:HTML、CSS、JavaScript4、 网页简史蒂姆·伯纳斯·李爵士,万维网的发明人。1991 年 8 月 6 日,世界上第一个服务器和第一个网站在欧洲核子研究中心上线。第一个网站:http://info.cern.ch/hypertext/WWW/TheProject.html5、 浏览器和网页有了浏览器我们只需要一个网址便可以访问任何的网站。而浏览器中所显示的内容正是我们所说的网页。网页原本的样子:... <!--无障碍占位--> <div id="J_accessibility"></div> <!--顶通占位 --> <div id="J_promotional-top"></div> <div id="shortcut"> <div class="w"> <ul class="fl" clstag="h|keycount|head|topbar_01"> <li class="dropdown" id="ttbar-mycity"></li> </ul> <ul class="fr"> <li class="fore1 dropdown" id="ttbar-login" clstag="h|keycount|head|topbar_02" href="//passport.jd.com/uc/login?ReturnUrl=https%3A%2F%2Fwww.jd.com%2F" class="link-login" >你好,请登录</a > <a href="//reg.jd.com/reg/person?ReturnUrl=https%3A//www.jd.com/" class="link-regist style-red" >免费注册</a </li> <li class="spacer"></li> <li class="fore2" clstag="h|keycount|head|topbar_03"> <div class="dt"> <a target="_blank" href="//order.jd.com/center/list.action" >我的订单</a </div> </li> <li class="spacer"></li> <li class="fore3 dropdown" id="ttbar-myjd" clstag="h|keycount|head|topbar_04" <div class="dt cw-icon"> <a target="_blank" href="//home.jd.com/">我的京东</a ><i class="iconfont"></i><i class="ci-right"><s>◇</s></i> </div> <div class="dd dropdown-layer"></div> </li> <li class="spacer"></li> <li class="fore4" clstag="h|keycount|head|topbar_05"> <div class="dt"> <a target="_blank" href="//vip.jd.com/">京东会员</a> </div> </li> <li class="spacer"></li> <li class="fore5" clstag="h|keycount|head|topbar_06"> <div class="dt"><a target="_blank" href="//b.jd.com/">企业采购</a></div> </li> <li class="spacer"></li> <li class="fore8 dropdown" id="ttbar-serv" clstag="h|keycount|head|topbar_07" <div class="dt cw-icon"> 客户服务<i class="iconfont"></i ><i class="ci-right"><s>◇</s></i> </div> <div class="dd dropdown-layer"></div> </li> <li class="spacer"></li> <li class="fore9 dropdown" id="ttbar-navs" clstag="h|keycount|head|topbar_08" <div class="dt cw-icon"> 网站导航<i class="iconfont"></i ><i class="ci-right"><s>◇</s></i> </div> <div class="dd dropdown-layer"></div> </li> <li class="spacer"></li> <li class="fore10 mobile" id="J_mobile" clstag="h|keycount|head|topbar_09" <div class="dt mobile_txt">手机京东</div> <div class="mobile_static"> <div class="mobile_static_qrcode"></div> </div> <div id="J_mobile_pop" class="mod_loading mobile_pop"></div> </li> </ul> </div> </div> ...浏览器渲染后的样子:前端工程师负责编写网页的源代码。浏览器负责将网页渲染成我们想要的样子。5.1、 浏览器的问题市面上存在有很多不同的浏览器。在万维网的初期,网页编写并没有标准。于是就出现了这种情况:5.2、 W3C 的建立伯纳斯李 1994 年建立万维网联盟(W3C)W3C 的出现为了制订网页开发的标准,以使同一个网页在不同的浏览器中有相同的效果。所以,我们需要制订我们编写的网页都需要遵循 W3C 的规范!5.3、 网页的结构思想根据 W3C 标准,一个网页主要由三部分组成:结构、表现还有行为。结构、表现、行为结构(骨架):HTML 用于描述页面的结构表现(皮肤):CSS 用于控制页面中元素的样式行为(交互):JavaScript 用于响应用户操作6、 网页的基本结构6.1、迭代网页的版本HTML4XHTML2.0HTML56.2、文档声明(doctype)文档声明用来告诉浏览器当前网页的版本<!-- html5的文档声明 --> <!doctype html> <!-- 或者 --> <!DOCTYPE html>6.3、字符编码所有的数据在计算机中存储时都是以二进制形式存储的,文字也不例外。所以一段文字在存储到内存中时,都需要转换为二进制编码当我们读取这段文字时,计算机会将编码转换为字符,供我们阅读编码将字符转换为二进制码的过程称为编码解码将二进制码转换为字符的过程称为解码字符集(charset)编码和解码所采用的规则称为字符集(相当于密码本)乱码如果编码和解码所采用的字符集不同就会出现乱码问题。可以通过 meta 标签来设置网页的字符集,避免乱码问题<meta charset="utf-8" />6.4. 常见的字符集ASCIIASCII(American Standard Code for Information Interchange):美国信息交换标准代码在所有字符集中,最知名的可能要数被称为 ASCII 的8 位字符了。美国信息交换标准代码是由美国国家标准学会(American National Standard Institute , ANSI )制定的,是一种标准的单字节字符编码方案,用于基于文本的数据。它最初是美国国家标准,供不同计算机在相互通信时用作共同遵守的西文字符编码标准,后来它被国际标准化组织(International Organization for Standardization, ISO)定为国际标准,称为 ISO 646 标准。适用于所有拉丁文字字母ASCII 码使用指定的 7 位或 8 位二进制数组合来表示 128 或 256 种可能的字符。标准 ASCII 码也叫基础 ASCII 码,使用 7 位二进制数(剩下的 1 位二进制为 0)来表示所有的大写和小写字母,数字 0 到 9、标点符号,以及在美式英语中使用的特殊控制字符ASCII 码表:Ascii Table - ASCII character codes and html, octal, hex and decimal chart conversionISO-8859-1ISO-8859-1 编码是单字节编码,向下兼容 ASCII,其编码范围是 0x00-0xFF,0x00-0x7F 之间完全和 ASCII 一致,0x80-0x9F 之间是控制字符,0xA0-0xFF 之间是文字符号。ISO 码表:HTML ISO-8859-1 参考手册GB2312GB2312(信息交换用汉字编码字符集)是由中国国家标准总局 1980 年发布。基本集共收入汉字 6763 个和非汉字图形字符 682 个。GB 2312 的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆 99.75%的使用频率。GBKGBK(即“国标”、“扩展”汉语拼音的第一个字母),汉字编码字符集。2000 年已被 GB18030-2000 国家强制标准替代。 2005 年 GB18030-2005 发布,替代了 GB18030-2000。GBK 使用了双字节编码方案,其编码范围从 8140 至 FEFE(剔除 xx7F),共 23940 个码位,共收录了 21003 个汉字,完全兼容 GB2312-80 标准,支持国际标准 ISO/IEC10646-1 和国家标准 GB13000-1 中的全部中日韩汉字,并包含了 BIG5 编码中的所有汉字。Big5Big5,又称为大五码或五大码,是使用繁体中文(正体中文)社区中最常用的电脑汉字字符集标准,共收录 13,060 个汉字。Big5 虽普及于台湾、香港与澳门等繁体中文通行区,但长期以来并非当地的国家/地区标准或官方标准,而只是业界标准。倚天中文系统、Windows 繁体中文版等主要系统的字符集都是以 Big5 为基准,但厂商又各自增加不同的造字与造字区,派生成多种不同版本。UTF-8UTF-8(8 位元,Universal Character Set/Unicode Transformation Format)是针对 Unicode 的一种可变长度字符编码,也叫万国码、统一码。它可以用来表示 Unicode 标准中的任何字符,而且其编码中的第一个字节仍与 ASCII 相容,使得原来处理 ASCII 字符的软件无须或只进行少部分修改后,便可继续使用。UTF-16UTF-16 是 Unicode 的其中一个使用方式。UTF-16 比起 UTF-8,好处在于大部分字符都以固定长度的字节(2 字节)储存,但 UTF-16 却无法兼容于 ASCII 编码。UnicodeUnicode 只是一组字符设定或者说是从数字和字符之间的逻辑映射的概念编码,但是它并没有指定代码点如何在计算机上存储。UCS4、UTF-8、UTF-16(UTF 后的数字代表编码的最小单位,如 UTF-8 表示最小单位 1 字节,所以它可以使用 1、2、3 字节等进行编码,UTF-16 表示最小单位 2 字节,所以它可以使用 2、4 字节进行编码)都是 Unicode 的编码方案。UTF-8 因可以兼容 ASCII 而被广泛使用。如果把各种文字编码形容为各地的方言,那么 Unicode 就是世界各国合作开发的一种语言。6.5. HTML5 的基本结构<!-- 文档声明,声明当前网页的版本 --> <!DOCTYPE html> <!-- html的根标签(元素),网页中的所有内容都要写根元素的里边 --> <html> <!-- head是网页的头部,head中的内容不会在网页中直接出现,主要用来帮助浏览器或搜索引擎来解析网页 --> <head> <!-- meta标签用来设置网页的元数据,这里meta用来设置网页的字符集,避免乱码问题 --> <meta charset="utf-8" /> <!-- title中的内容会显示在浏览器的标题栏,搜索引擎会主要根据title中的内容来判断网页的主要内容 --> <title>网页的标题</title> </head> <!-- body是htm1的子元素,表示网页的主体,网页中所有的可见内容都应该写在body里 --> <body> <!-- h1网页的一级标题 --> <h1>网页的大标题</h1> </body> </html>二、前端开发准备1、离线文档的下载离线文档:Zeal - Offline Documentation Browser这里需要注意:如果你是Mac系统那么你要下载:Dash文档如果安装报错,需安装:Visual C++ Redistributable下载安装完成之后,会在“开始”屏幕或者桌面生成快捷键,双击打开在第一次使用时,并不是直接就有 HTML 文档的,还需要 Download。这里点击工具栏的 Tools-Assets 或者下方的“Install and update docsets”都是 OK 的按照步骤安装即可由于服务器在国外,网络较慢,耐心等待 download 完毕在 Installed 中出现 HTML,同时左侧导航栏有了 HTML,至此安装完毕离线使用,在左侧导航栏可以查询 HTML 标签和属性,右侧显示元素的详细信息2、 文本编辑器的选择Notepad++Notepad++是 Windows 操作系统下的一套文本编辑器,功能比 Windows 中的 Notepad 强大,除了可以用来制作一般的纯文字说明文件,也十分适合编写计算机程序代码。有语法高亮度显示、语法折叠功能,并且支持宏以及扩充基本功能的外挂模组。完全免费,支持众多计算机程序语言:C,C++,Java,C#,XML,SQL,HTML,PHP,ASP 等官方地址:notepad-plus-plus.orgSoftonic 地址:Notepad++ - Download (softonic.com)不过因为国外服务器原因,而且貌似被墙了,所以建议从 Softonic 下载优点:免费开源,轻量流畅,支持插件缺点:界面丑,虽然可以下载皮肤插件(PS:个人感觉皮肤插件也不好用)SublimeSublime Text 是一个文本编辑器(收费软件,可以无限期试用,但是会有激活提示弹窗),同时也是一个先进的代码编辑器。主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。跨平台,同时支持 Windows、Linux、Mac OS X 等操作系统。强大的命令面板功能,可以模糊匹配命令。官方地址:Sublime Text - A sophisticated text editor for code, markup and prose优点:轻量流畅,支持插件,界面简洁,运行速度特别快缺点:不开源,商用收费VS Code √Microsoft 出品,轻量但强大,针对于编写现代 Web 和云应用的跨平台源代码编辑器。可以在 Mac OS X、Windows 和 Linux 等操作平台使用。具有对 JavaScript、TypeScript 和 Node.js 的内置支持,并具有丰富的其他语言(例如 C++,C#,Java,Python,PHP,Go)和运行时(例如.NET 和 Unity)扩展的生态系统。官方地址:Visual Studio Code - Code Editing. Redefined优点:免费开源,轻量流畅,功能丰富,支持插件,界面简洁,智能代码补全,运行速度很快缺点:几乎没有什么太大的缺点(PS:撤销恢复之前的编辑时出现过问题,希望官方能够尽快修复)AtomAtom 是 Github 专门为程序员推出的一个跨平台文本编辑器。完全免费开源的代码编辑器,具有简洁和直观的图形用户界面。支持 CSS,HTML,JavaScript 等网页编程语言。支持宏,自动完成分屏功能,集成了文件管理器。官方地址:AtomGithub 地址:atom/atom: The hackable text editor (github.com)中文地址:Atom 中文网 (baisheng999.com)优点:功能丰富,免费开源,支持插件,界面简洁缺点:相对重量级;打开大文件卡死(PS:产品上经常用它写 amWiki,使用时经常卡死;而且安装过程没有任何选项和提示,默认装到 C 盘)WebStormJetBrains 出品的智能 JavaScript IDE。誉为“Web 前端开发神器”、“最强大的 HTML5 编辑器”、“最智能的 JavaScript IDE”等。与 IntelliJ IDEA 同源,继承了 IntelliJ IDEA 强大的 JS 部分的功能。IntelliJ IDEA 是 java 编程语言开发的集成环境。IntelliJ 在业界被公认为最好的 java 开发工具,尤其在智能代码助手、代码自动提示、重构、JavaEE 支持、各类版本工具(git、svn 等)、JUnit、CVS 整合、代码分析、 创新的 GUI 设计等方面的功能可以说是超常的。它的旗舰版本还支持 HTML,CSS,PHP,MySQL,Python 等。免费版只支持 Java,Kotlin 等少数语言。官方地址:Download WebStorm: The Smartest JavaScript IDE by JetBrains优点:功能强大,支持插件,界面美观,智能代码补全,快速搜索缺点:重量级,占内存;收费除以之外,市面上还有很多功能强大的前端编辑器。HBuilder:DCloud(数字天堂)推出一款支持 HTML5 的 Web 开发 IDE。在语法提示、转到定义、重构、调试等方面都非常高效。缺点是不太稳定,有时会出现卡顿。Dreamweaver:简称“DW,老牌的 IDE ,国人开发,号称为编码极客而生的 IDE。曾经 PS+DW+FW(号称网页三剑客)称霸网页领域。然而之前的版本更新较慢,版本陈旧,已经满足不了广大前端开发者的项目需求,逐渐被市场淘汰。这两款及其他编辑器在这里就不再赘述了(PS:本人没怎么用过,没有太多发言权)这里我选择以 VSCode 作为接下来学习的开发编辑器了。当然每个人有每个人的偏好,你也可以选择自己心仪的编辑器进行开发。3、开发准备为 VSCode 安装以下插件,便于我们进行更好的开发工作Chinese (Simplified) Language Pack for Visual Studio Code:中文(简体)语言包(PS1:不完全显示中文,但是大多数都会译为英文;PS2:喜欢原生态或者英文 OK 的话,可忽略)Ayu:简单的主题与明亮的颜色vscode-icons:好看的图标Live Server:A Quick Development Live Server with live browser reload,即提供一个 live 服务器,并且支持代码与浏览器之间的实时同步刷新(PS:这样我们在写前端代码时就能实时看到效果了)4、使用Live-Server在当前 HTML 中右键单击,选择Open With Live Server踩坑 1Open a folder or workspace...(File -> Open Folder)解决方式:需要打开 HTML 所在的文件夹,通过导航栏 文件-打开文件夹,选择我们编写的 HTML,再去Open With Live Server即可踩坑 2Server is started at 5500 but failed to open in Browser Preview.解决方式:在 liveserver 设置中,找到Live Server>Settings:Use Browser Preview,取消对 Open in Browser Preview inside VS Code,instead of default browser的勾选即可踩坑 3Error: connect ECONNREFUSED 127.0.0.1:80 at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1146:16)解决方式:取消使用代理,修改 enable 为 false(这里我一直以为是 live-server 服务器本身的代理端口)。live-server 默认使用 5500 端口实际上,配置端口要在Live Server › Settings: Port选项进行设置自定义端口号按照上述说明,点击在settings.json中编辑会打开settings.json文件这里如果将liveServer.settings.port配置为 0,会随机选择端口号三、字符实体与语义标签1、字符实体有些时候,在 HTML 中不能直接书写一些特殊符号,如:多个连续的空格(在网页中编写的多个空格默认情况会自动被浏览器解析为一个空格)比如字母两侧的大于小于号(可能会被认为是标签并解析)如果我们需要在网页中书写这些特殊的符号,则需要使用 html 中的实体(转义字符)实体的语法:&实体的名字;,如实体名称显示结果描述 空格>>大于号<<小于号&&与©©版权®®注册商标™™商标××乘号÷÷除号¿¿倒问号更多的字符实体,可参考:HTML 字符实体、HTML ISO-8859-1 参考手册2、meta标签以京东网站为例,右键单击,选择查看网页源代码<meta charset="utf8" version="1" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes" /> <meta name="description" content="京东JD.COM-专业的综合网上购物商城,销售家电、数码通讯、电脑、家居百货、服装服饰、母婴、图书、食品等数万个品牌优质商品.便捷、诚信的服务,为您提供愉悦的网上购物体验!" /> <meta name="Keywords" content="网上购物,网上商城,手机,笔记本,电脑,MP3,CD,VCD,DV,相机,数码,配件,手表,存储卡,京东" />meta 主要用于设置网页中的一些元数据,元数据并不是给用户看的charset :指定网页的字符集name :指定的数据的名称keywords:表示网站的关键字,可以同时指定多个关键字,关键字间使用,隔开description:表示网站的描述信息content :指定的数据的内容,会作为搜索结果的超链接上的文字显示打开 Zeal 手册(前端开发准备中做过介绍)发现除了charset、name、content之外,还有一个叫http-equiv的属性If the http-equiv attribute is set, the <meta> element is a pragma directive, providing information equivalent to what can be given by a similarly-named HTTP header.如果设置了http-equiv属性,<meta>元素就是一个 pragma 指令,提供的信息相当于一个类似名称的 HTTP 头所能提供的信息。点击http-equiv的链接,查看其更详细信息。content-security-policy:允许页面作者为当前页面定义一个内容策略。内容策略主要指定允许的服务器来源和脚本端点,这有助于防范跨站脚本攻击。content-type:声明文档的MIME 类型和字符编码。如果指定,content 属性必须有 "text/html; charset=utf-8 "的值。这相当于一个指定了 charset 属性的<meta>元素,并对文档中的位置有同样的限制。注意:只能在使用text/html的文档中使用,不能在使用 XML MIME 类型的文档中使用。default-style:设置默认的 CSS 样式表集的名称。x-ua-compatible: 如果指定,内容属性必须有 "IE=edge "的值。用户代理被要求忽略这个 pragma。refresh:该指令指定页面重新加载及重定向的方式直到页面应该被重新加载的秒数--只有当 content 属性包含一个正整数时。直到页面重定向到另一个页面的秒数--只有当内容属性包含一个正整数,后面跟着字符串';url=',以及一个有效的 URL。其中我们直接将 Examples 中的示例代码加入 Demo.html 中<meta charset="utf-8" /> <!-- Redirect page after 3 seconds --> <meta http-equiv="refresh" content="3;url=https://www.mozilla.org" />对refresh进行测试,发现过了 3 秒钟之后自动跳转到了指定的网站3、语义标签在网页中 HTML 专门用来负责网页的结构所以在使用 html 标签时,应该关注的是标签的语义,而不是它的样式这里先介绍几个基本的语义标签,还有些常用的标签放在后面具体讲解 标签作用描述块元素 Block Element<h1> <h2> <h3> <h4> <h5> <h6>标题一共有六级标题 从h1 ~ h6重要性递减,h1最重要,h6最不重要 h1 在网页中的重要性仅次于title标签 一般情况下一个页面中只会有一个h1 一般情况下标题标签只会使用到h1 ~ h3,h4 ~ h6很少用 <hgroup>标题组多层次的标题。它将一组<h1> ~ <h6>元素分组 <p>段落页面中的一个段落。由空行或第一行缩进将相邻的文本块分开 <blockquote>长引文用缩进表示所包含文本。 可以用cite属性表示引文来源,用<cite>元素表示来源的文本表述行内元素 Inline Element<q>短引文用一个简短的内联引号包围文本。 大多数浏览器通过在文本周围加上引号来实现。 该元素用于不需要段落分隔的短引文; <br>换行 <em>强调表示强调作用。<em>元素可以嵌套,每一级嵌套表示更高的强调程度 <i>元素效果与它相同,不过<i>不属于语义标签 <strong>重要表示重要性、严肃性或紧迫性。浏览器通常以粗体字呈现内容 <b>元素效果与它相同,不过<b>不属于语义标签举例<h1>Beetles</h1> <h2>External morphology</h2> <h3>Head</h3> <h4>Mouthparts</h4> <h3>Thorax</h3> <h4>Prothorax</h4> <h4>Pterothorax</h4>效果HTML5 提供的新语义元素有标签作用描述<header>页眉介绍性的内容<footer>页脚通常包含有关作者的信息、版权或文件链接<nav>导航链接可以是当前文档内的,也可以是到其他文档的。常见例子是菜单、目录和索引<main>文档主内容中心主题直接相关或扩展的内容<article>文章自成一体,独立分发,可重复使用<section>文档中的节没有一个更具体的语义元素来代表<aside>页面内容以外的内容其内容与文档的主要内容只有间接的关系。经常以边栏或呼出框的形式出现<mark>重要或强调的文本为参考或记事目的而被标记或突出的文本,表明其相关性和重要性<summary><details> 标题为<details>指定一个摘要、标题或图例。点击<summary>可以切换<details>打开和关闭<details>用户能够查看或隐藏的额外细节其中的信息只有被切换到 "打开 "状态时才可见。必须使用<summary>提供一个摘要或标签<figure>自包含内容独立的内容,用<figcaption>元素指定一个可选的标题。比如图示、图表、照片、代码清单等<figcaption><figure> 的标题描述其父元素其余内容的标题或图例<time>定义日期/时间可能包括datetime属性,将日期翻译成机器可读的格式,以便获得更好的搜索引擎结果或自定义功能。如提醒这些新语义标签在视觉效果上基本上没有什么区别4、块元素与行内元素块元素(block element)在网页中一般通过块元素来对页面进行布局行内元素(inline element)行内元素主要用来包裹文字一般情况下会在块元素中放行内元素,而不会在行内元素中放块元素如<p>元素中不能放任何的块元素,如果你放了也不会报错!5、内容修正浏览器在解析网页时,会自动对网页中不符合规范的内容进行修正,比如:标签写在了根元素的外部<p>元素中嵌套了块元素根元素中出现了除head和body以外的子元素这个通过浏览器中的查看网页源代码并不能看到效果,但是使用 F12 进行开发者调试时是能够看到上述几种情况被修正的结果。不过虽然浏览器能够对不规范的页面内容进行修正,还是不建议编写不规范的代码,因为这对后期代码维护或团队代码协作将是非常不好的后果和体验。6、布局标签结构化语义标签header表示网页的头部(页眉)main表示网页的主体部分(一个页面中只会有一个 main)footer表示网页的底部(页脚)nav表示网页中的导航aside和主体相关的其他内容(侧边栏)article表示一个独立的文章section表示一个独立的区块,上边的标签都不能表示时使用 sectiondiv 块元素,没有任何的语义,就用来表示一个区块。目前来讲,div 还是主要的布局元素span 行内元素,没有任何的语义,一般用于在网页中选中文字7、列表在 html 中可以创建列表,html 列表一共有三种:有序列表,使用ol标签来创建有序列表,使用li表示列表项 <ol> <li>Mix flour, baking powder, sugar, and salt.</li> <li>In another bowl, mix eggs, milk, and oil.</li> <li>Stir both mixtures together.</li> <li>Fill muffin tray 3/4 full.</li> <li>Bake for 20 minutes.</li> </ol>无序列表,使用ul标签来创建无序列表,使用li表示列表项 <ul> <li>Milk</li> <li> Cheese <ul> <li> Blue cheese <ul> <li>Sweet blue cheese</li> <li>Sour blue cheese</li> </ul> </li> <li>Feta</li> </ul> </li> </ul>可以看出,列表元素之间是可以互相嵌套的定义列表,使用dl标签来创建定义列表,使用dt表示定义的内容,使用dd来对内容进行解释说明<dl> <dt>Beast of Bodmin</dt> <dd>A large feline inhabiting Bodmin Moor.</dd> <dt>Morgawr</dt> <dd>A sea serpent.</dd> <dt>Owlman</dt> <dd>A giant owl-like creature.</dd> </dl>8、超链接超链接可以让我们从一个页面跳转到其他页面,或者是当前页面的其他的位置使用a标签来定义超链接,href属性指定跳转的目标路径,值可以是一个外部网站的地址,也可以写一个内部页面的地址超链接是也是一个行内元素,在a标签中可以嵌套除它自身外的任何元素外部地址Linking to an absolute URL:链接一个绝对路径Linking to an email address:链接一个 email 地址Linking to telephone numbers:链接电话号码Using the download attribute to save a <canvas> as a PNG:下载图片<ul> <li><a href="https://www.baidu.com">Website</a></li> <li><a href="mailto:example@outlook.com">Email</a></li> <li><a href="tel:+123456789">Phone</a></li> </ul>效果内部地址当我们需要跳转一个服务器内部的页面时,一般我们都会使用相对路径,会以./或../开头./ 表示当前文件所在目录,可以省略不写../表示当前文件所在目录的上一级目录<a href="./test1.html">超链接1</a><br /> <a href="../test2.html">超链接2</a><br /> <a href="./test3/test3.html">超链接3</a><br /> <a href="../test4/test4.html">超链接4</a>效果新建页面target属性,用来指定超链接打开的位置可选值:_self在当前页面中打开超链接,默认值_blank在新建页面中打开超链接<a href="./test1.html">超链接1——默认</a><br /> <a href="./test1.html" target="_self">超链接1——当前页面</a><br /> <a href="./test1.html" target="_blank">超链接1——新建页面</a><br />锚点跳转可以使用javascript:void(0);来作为href的属性,此时点击这个超链接什么也不会发生可以将#作为超链接的路径的占位符使用。可以直接将超链接的href属性设置为#,这样点击超链接以后页面不会发生跳转,而是转到当前页面的顶部的位置可以跳转到页面的指定位置(锚点),只需将href属性设置#目标元素的id属性值(唯一不重复)==这里的数据还可以通过:lorem + tab 这个实现随机的生成英文段落!==<p>汉皇重色思倾国,御宇多年求不得。</p> <p>杨家有女初长成,养在深闺人未识。</p> <p>天生丽质难自弃,一朝选在君王侧。</p> <p><a id="Anchor1" href="#Anchor2"> 回眸一笑百媚生,六宫粉黛无颜色。</a></p> <p>春寒赐浴华清池,温泉水滑洗凝脂。</p> <p>侍儿扶起娇无力,始是新承恩泽时。</p> <p>云鬓花颜金步摇,芙蓉帐暖度春宵。</p> <p>春宵苦短日高起,从此君王不早朝。</p> <p>承欢侍宴无闲暇,春从春游夜专夜。</p> <p><a id="Anchor2" href="#Anchor3"> 后宫佳丽三千人,三千宠爱在一身。</a></p> <p>金屋妆成娇侍夜,玉楼宴罢醉和春。</p> <p>姊妹弟兄皆列土,可怜光彩生门户。</p> <p>遂令天下父母心,不重生男重生女。</p> <p>骊宫高处入青云,仙乐风飘处处闻。</p> <p>缓歌慢舞凝丝竹,尽日君王看不足。</p> <p>渔阳鼙鼓动地来,惊破霓裳羽衣曲。</p> <p>九重城阙烟尘生,千乘万骑西南行。</p> <p>翠华摇摇行复止,西出都门百余里。</p> <p>六军不发无奈何,宛转蛾眉马前死。</p> <p>花钿委地无人收,翠翘金雀玉搔头。</p> <p>君王掩面救不得,回看血泪相和流。</p> <p>黄埃散漫风萧索,云栈萦纡登剑阁。</p> <p>峨嵋山下少人行,旌旗无光日色薄。</p> <p>蜀江水碧蜀山青,圣主朝朝暮暮情。</p> <p>行宫见月伤心色,夜雨闻铃肠断声。</p> <p>天旋地转回龙驭,到此踌躇不能去。</p> <p>马嵬坡下泥土中,不见玉颜空死处。</p> <p>君臣相顾尽沾衣,东望都门信马归。</p> <p>归来池苑皆依旧,太液芙蓉未央柳。</p> <p>芙蓉如面柳如眉,对此如何不泪垂。</p> <p>春风桃李花开夜,秋雨梧桐叶落时。</p> <p>西宫南苑多秋草,落叶满阶红不扫。</p> <p>梨园弟子白发新,椒房阿监青娥老。</p> <p>夕殿萤飞思悄然,孤灯挑尽未成眠。</p> <p><a id="Anchor3" href="#Anchor4"> 迟迟钟鼓初长夜,耿耿星河欲曙天。 </a></p> <p>鸳鸯瓦冷霜华重,翡翠衾寒谁与共。</p> <p>悠悠生死别经年,魂魄不曾来入梦。</p> <p>临邛道士鸿都客,能以精诚致魂魄。</p> <p>为感君王辗转思,遂教方士殷勤觅。</p> <p>排空驭气奔如电,升天入地求之遍。</p> <p>上穷碧落下黄泉,两处茫茫皆不见。</p> <p>忽闻海上有仙山,山在虚无缥渺间。</p> <p>楼阁玲珑五云起,其中绰约多仙子。</p> <p>中有一人字太真,雪肤花貌参差是。</p> <p>金阙西厢叩玉扃,转教小玉报双成。</p> <p>闻道汉家天子使,九华帐里梦魂惊。</p> <p>揽衣推枕起徘徊,珠箔银屏迤逦开。</p> <p>云鬓半偏新睡觉,花冠不整下堂来。</p> <p><a id="Anchor4" href="#Anchor5"> 风吹仙袂飘飖举,犹似霓裳羽衣舞。 </a></p> <p>玉容寂寞泪阑干,梨花一枝春带雨。</p> <p>含情凝睇谢君王,一别音容两渺茫。</p> <p>昭阳殿里恩爱绝,蓬莱宫中日月长。</p> <p>回头下望人寰处,不见长安见尘雾。</p> <p>惟将旧物表深情,钿合金钗寄将去。</p> <p>钗留一股合一扇,钗擘黄金合分钿。</p> <p>但令心似金钿坚,天上人间会相见。</p> <p>临别殷勤重寄词,词中有誓两心知。</p> <p>七月七日长生殿,夜半无人私语时。</p> <p><a id="Anchor5" href="#Anchor6"> 在天愿作比翼鸟,在地愿为连理枝。 </a></p> <p>天长地久有时尽,此恨绵绵无绝期。</p> <!-- Heading to link to --> <a href="#">回到顶部</a>9、图片图片标签用于向当前页面中引入一个外部图片img标签是一个自结束标签,这种元素属于替换元素(块和行内元素之间,具有两种元素的特点)属性src:属性指定的是外部图片的路径(路径规则和超链接是一样的)alt:图片的描述,这个描述默认情况下不会显示,有些浏览器会在图片无法加载时显示,搜索引擎会根据 alt 中的内容来识别图片width:图片的宽度(单位是像素)height :图片的高度(单位是像素)宽度和高度中如果只修改了一个,则另一个会等比例缩放注意一般情况在 pc 端,不建议修改图片的大小,需要多大的图片就裁多大但是在移动端,经常需要对图片进行缩放(大图缩小)举例<img src="https://gitee.com/vectorx/ImageCloud/raw/master/img/20210513002416.png" alt="蒂姆·伯纳斯·李爵士,万维网的发明人" /> <img src="https://gitee.com/vectorx/ImageCloud/raw/master/html5/20210514233853.gif" alt="结构、表现、行为" />图片格式jpeg(jpg)支持的颜色比较丰富不支持透明效果不支持动图一般用来显示照片gif支持的颜色比较单一支持简单透明支持动图png支持的颜色丰富支持复杂透明不支持动图专为网页而生webp这种格式是谷歌新推出的专门用来表示网页中的图片的一种格式具备其他图片格式的所有优点,而且文件还特别的小缺点:兼容性不好base64将图片使用 base64 编码,这样可以将图片转换为字符,通过字符的形式来引入图片图片格式的选择<img width="300" src="data:image/png;base64,AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAxVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zda/P9qhPz/mKr9/7bC/f/Fz/7/ydL+/8HM/v+tu/3/jaH9/156/P8zV/z/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/z9h/P+gsP3/8fP+/////////////////////////////////////////////////+ru/v+Zqv3/PV/8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P9lgPz/6+/+///////////////////////////////////////////////////////////////////////s7/7/Y378/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/aoT8//r6/v///////////////////////v7+/+Po/v/R2f7/y9T+/9rg/v/3+f7////////////////////////////j6P7/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/0Zm/P/w8/7/////////////////5+v+/4ab/f9AYvz/MVX8/zFV/P8xVfz/MVX8/zVY/P9kf/z/tsP9//39/v////////////T2/v8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/sL79/////////////////87W/v8/Yfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/ZYD8//L0/v//////n7D9/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/0Bh/P/6+/7////////////v8v7/QmP8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/TWz8/3GJ/P8yVvz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/e5L8/////////////////5qr/f8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P+mtv3/////////////////XHn8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/7/L/f////////////////87Xfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/ydL+////////////+/v+/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P/Ezv7////////////9/f7/M1b8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/7G//f////////////////9HZ/z/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/kqX9/////////////////22H/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P9kf/z/////////////////pbX9/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zRX/P/v8v7////////////s7/7/Nln8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/6Ky/f////////////////+Inf3/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/RWb8//f4/v////////////H0/v9Kafz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/PV/8/1Jw/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/kKT9/////////////////9vh/v9DZPz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/1Fv/P/m6/7//v7+/3aO/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8zVvz/xM79/////////////////+fr/v9viPz/MVX8/zFV/P8xVfz/MVX8/zRX/P+Emf3/8/X+////////////xc/+/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P87Xfz/ztf+///////////////////////i5/7/sL79/5+w/f+ywP3/6u3+//////////////////////+uvP3/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P83Wvz/sL79//7+/v//////////////////////////////////////////////////////3OL+/0Vl/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/aYP8/9Pb/v//////////////////////////////////////9fb+/5yu/f84W/z/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/1d0/P+Spf3/t8T9/8fR/v/Dzv7/qrn9/3uS/P88Xvz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/MVX8/zFV/P8xVfz/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" />图片效果一样的,选文件小的图片效果不一样的,选图片效果好的尽可能的兼顾和平衡图片效果和文件大小10、内联格式内联框架iframe,用于向当前页面中引入一个其他页面,src指定要引入的网页的路径frameborder指定内联框架的边框举例frameLabelStart--frameLabelEnd 效果11、音视频音频audio标签用来向页面中引入一个外部的音频文件音视频文件引入时,默认情况下不允许用户自己控制播放停止属性:controls是否允许用户控制播放autoplay音频文件是否自动播放如果设置了autoplay,则音乐在打开页面时会自动播放但是目前来讲大部分浏览器都不会自动对音乐进行播放loop音乐是否循环播放<audio src="./source/audio.mp3" controls autoplay loop></audio>source除了通过src属性来指定外部文件的路径以外,还可以通过<source>元素来指定文件的路径<audio controls autoplay loop> 对不起,您的浏览器不支持播放音频!请升级浏览器! <source src="../source/audio.mp3" /> <source src="../source/audio.ogg" /> </audio>IE11 、Chrome、Edge、firefox下,能够正常播放,但是在IE8下就会报错!embedIE8 下不支持audio元素,但是可以使用 <embed> 元素在文档中的指定位置嵌入外部内容。这个内容是由外部应用程序或其他互动内容的来源提供的,如浏览器插件。<embed src="../source/audio.mp3" />在IE8同样会报错!视频使用video标签来向网页中引入一个视频,使用方式和audio基本上是一样的<video controls> <source src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm" type="video/webm" /> <source src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4" type="video/mp4" /> <embed src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4" type="video/mp4" /> </video>效果:(在IE11下可以正常使用,在IE8也可以展示!)其他通过iframe和embed的方式引入视频。以某艺为例,提供了视频链接的 HTML 代码和通用代码frameLabelStart--frameLabelEnd 不过,embed需要 flash 的支持<embed src="//player.video.iqiyi.com/0c53ddd55f262c6d416afa9d1f49dc55/0/0/v_19rrcuh1jw.swf-albumId=1008748400-tvId=1008748400-isPurchase=0-cnId=undefined" allowFullScreen="true" quality="high" width="480" height="350" align="middle" allowScriptAccess="always" type="application/x-shockwave-flash"></embed>四、CSS语法与选择器1、CSS简介层叠样式表网页实际上是一个多层的结构,通过 CSS 可以分别为网页的每一个层来设置样式,而最终我们能看到只是网页的最上边一层总之一句话,CSS 用来设置网页中元素的样式使用 CSS 来修改元素样式的方式大致可以分为 3 种内联样式(行内样式)在标签内部通过style属性来设置元素的样式<p style="color:red;font-size:60px;">内联样式(行内样式)</p>问题:使用内联样式,样式只能对一个标签生效。如果希望影响到多个元素,必须在每一个元素中都复制一遍;并且当样式发生变化时,我们必须要一个一个的修改,非常的不方便。(注意:开发时绝对不要使用内联样式)内部样式表将样式编写到head中的style标签里然后通过 css 的选择器来选中元素并为其设置各种样式可以同时为多个标签设置样式,并且修改时只需要修改一处即可。内部样式表更加方便对样式进行复用<style> color:green; font-size:50px; </style>问题:我们的内部样式表只能对一个网页起作用,它里边的样式不能跨页面进行复用!外部样式表可以将 css 样式编写到一个外部的 CSS 文件中,然后通过link标签来引入外部的 CSS 文件<link rel="stylesheet" href="./style.css" />外部样式表需要通过link标签进行引入,意味着只要想使用这些样式的网页都可以对其进行引用使样式,可以在不同页面之间进行复用将样式编写到外部的 CSS 文件中,可以使用到浏览器的缓存机制,从而加快网页的加载速度,提高用户的体验。2、CSS 基本语法注释css 中的注释只能使用/*和*/包裹。即不管是单行注释,还是多行注释,都是以/*开头,以*/结尾/* css中的单行注释 */ css中的多行注释 css中的多行注释 css中的多行注释 */我们对比下其他几种前端语言的注释html 中的注释只能使用<!--和-->包裹。即不管是单行注释,还是多行注释,都是以<!--开头,以-->结尾<!-- html中的单行注释 --> <!-- html中的多行注释 html中的多行注释 html中的多行注释 -->JS(JavaScript)和 JQuery 中的注释单行注释使用//。多行注释使用/*和*/包裹,以<!--开头,以-->结尾/* JS(JavaScript)和JQuery中的单行注释*/ JS(JavaScript)和JQuery中的多行注释 JS(JavaScript)和JQuery中的多行注释 JS(JavaScript)和JQuery中的多行注释 */基本语法选择器 声明块选择器通过选择器可以选中页面中的指定元素比如p的作用就是选中页面中所有的p元素声明块声明块通过声明块来指定要为元素设置的样式声明块由一个一个的声明组成,声明是一个名值对结构一个样式名对应一个样式值,名和值之间以:连接,以;结尾h1 { color: green; }3、选择器通配选择器(Universal selector)作用:选中页面中的所有元素语法:*例子:*{}* { color: red; }元素选择器(Type selector)也叫类型选择器、标签选择器作用:根据标签名来选中指定的元素语法:elementname{}例子:p{} h1{} div{}p { color: red; color: green; }类选择器(Class selector)作用:根据元素的 class 属性值选中一组元素语法:.classname例子:.blue{}.blue { color: blue; .size { font-size: 20px; class`是一个标签的属性,它和`id`类似,不同的是`class可以重复使用,可以通过class属性来为元素分组,可以同时为一个元素指定多个class属性<p class="blue size">类选择器(Class selector)</p>ID 选择器(ID selector)作用:根据元素的id属性值选中一个元素语法:#idname{}例子:#box{} #red{}#red { color: red; }属性选择器(Attribute selector)作用:根据元素的属性值选中一组元素语法 1:[属性名] 选择含有指定属性的元素语法 2:[属性名=属性值] 选择含有指定属性和属性值的元素语法 3:[属性名^=属性值] 选择属性值以指定值开头的元素语法 4:[属性名$=属性值] 选择属性值以指定值结尾的元素语法 5:[属性名*=属性值] 选择属性值中含有某值的元素例子:p[title]{} p[title=e]{} p[title^=e]{} p[title$=e]{} p[title*=e]{}p[title] { color: orange; p[title="e"] { color: orange; p[title^="e"] { color: orange; p[title$="e"] { color: orange; p[title*="e"] { color: orange; }4、复合选择器交集选择器作用:选中同时复合多个条件的元素语法:选择器1选择器2选择器3选择器n{}注意点:交集选择器中如果有元素选择器,必须使用元素选择器开头div.red { font-size: 30px; .a.b.c { color: blue; }并集选择器(选择器分组)作用:同时选择多个选择器对应的元素语法:选择器1,选择器2,选择器3,选择器n{}例子:#b1,.p1,h1,span,div.red{}h1, span { color: green; }5、关系选择器父元素:直接包含子元素的元素叫做父元素子元素:直接被父元素包含的元素是子元素祖先元素:直接或间接包含后代元素的元素叫做祖先元素;一个元素的父元素也是它的祖先元素后代元素:直接或间接被祖先元素包含的元素叫做后代元素;子元素也是后代元素兄弟元素:拥有相同父元素的元素是兄弟元素子元素选择器(Child combinator)作用:选中指定父元素的指定子元素语法:父元素 > 子元素例子:A > Bdiv.box > p > span { color: orange; }后代元素选择器(Descendant combinator)作用:选中指定元素内的指定后代元素语法:祖先 后代例子:A Bdiv span { color: skyblue; }兄弟元素选择器(Sibling combinator)作用:选择下一个兄弟语法:前一个 + 下一个 前一个 + 下一组例子 1:A1 + A2(Adjacent sibling combinator)例子 2: A1 ~ An(General sibling combinator)p + span { color: red; p ~ span { color: red; }6、伪类选择器伪类(不存在的类,特殊的类)伪类用来描述一个元素的特殊状态,比如:第一个子元素、被点击的元素、鼠标移入的元素.…伪类一般情况下都是使用:开头:first-child 第一个子元素:last-child 最后一个子元素:nth-child()选中第 n 个子元素n:第 n 个,n 的范围 0 到正无穷2n 或 even:选中偶数位的元素2n+1 或 odd:选中奇数位的元素以上这些伪类都是根据所有的子元素进行排序的:first-of-type 同类型中的第一个子元素:last-of-type 同类型中的最后一个子元素:nth-of-type() 选中同类型中的第 n 个子元素这几个伪类的功能和上述的类似,不同点是他们是在同类型元素中进行排序的:not()否定伪类,将符合条件的元素从选择器中去除/* ul下所有li,黑色 */ ul > li { color: black; /* ul下第偶数个li,黄色 */ ul > li:nth-child(2n) { color: yellow; /* ul下第奇数个li,绿色 */ ul > li:nth-child(odd) { color: green; /* ul下第一个li,红色 */ ul > li:first-child { color: red; /* ul下最后一个li,黄色 */ ul > li:last-child { color: orange; }:link 未访问的链接:visited 已访问的链接由于隐私的原因,所以visited这个伪类只能修改链接的颜色:hover 鼠标悬停的链接:active 鼠标点击的链接/* unvisited link */ a:link { color: red; /* visited link */ a:visited { color: yellow; /* mouse over link */ a:hover { color: green; /* selected link */ a:active { color: blue; }7、伪元素选择器伪元素,表示页面中一些特殊的并不真实的存在的元素(特殊的位置)伪元素使用::开头::first-letter 表示第一个字母::first-line 表示第一行::selection 表示选中的内容::before 元素的开始::after 元素的最后::before和::after 必须结合content属性来使用/* 段落首字母设置大小为30px */ p::first-letter { font-size: 30px; /* 段落第一行设置为黄色背景 */ p::first-line { background-color: yellow; /* 段落选中的部分变绿色 */ p::selection { background-color: green;; /* div前加上内容 */ div::before { content: "BEFORE"; color: red; /* div后加上内容 */ div::after { content: "AFTER"; color: blue; }8. CSS Dinner 游戏官方地址:CSS Diner - Where we feast on CSS Selectors!CSS Dinner 是一个帮助初学者快速熟悉 css 各种选择器的网页游戏==纸上得来终觉浅,绝知此事要躬行==五、样式继承与其他概念1、继承样式的继承,我们为一个元素设置的样式,同时也会应用到它的后代元素上继承是发生在祖先后后代之间的,继承的设计是为了方便我们的开发利用继承,我们可以将一些通用的样式,统一设置到共同的祖先元素上。这样只需设置一次即可让所有的元素都具有该样式注意,并不是所有的样式都会被继承:比如背景相关的,布局相关等的这些样式都不会被继承。我们可以再 Zeal 手册中,搜索background-color属性,可以看到一个定义的表格。其中就说明了其不可被继承性2、选择器的权重当我们通过不同的选择器,选中相同的元素,并且为相同的样式设置不同的值时,此时就发生了样式的冲突。发生样式冲突时,应用哪个样式由选择器的权重(优先级)决定选择器的权重选择器权重内联样式1, 0, 0, 0ID 选择器0, 1, 0, 0类和伪类选择器0, 0, 1, 0元素选择器0, 0, 0, 1通配选择器0, 0, 0, 0继承的样式没有优先级比较优先级时,需要将所有的选择器的优先级进行相加计算,最后优先级越高,则越优先显示(分组选择器是单独计算的)选择器的累加不会超过其最大的数量级,类选择器再高也不会超过 ID 选择器如果优先级计算后相同,此时则优先使用靠下的样式可以在某一个样式的后边添加!important,则此时该样式会获取到最高的优先级,甚至超过内联样式,注意:在开发中一定要慎用!<style> #box1 { background-color: orange; div { background-color: yellow; .red { background-color: red; </style> <div id="box1" class="red" style="background-color: skyblue;">选择器的权重</div>3、长度单位像素我们先来看下某度上关于像素(pixel,缩写 px)的介绍像素是指由图像的小方格组成的,这些小方块都有一个明确的位置和被分配的色彩数值,小方格颜色和位置就决定该图像所呈现出来的样子。可以将像素视为整个图像中不可分割的单位或者是元素。不可分割的意思是它不能够再切割成更小单位抑或是元素,它是以一个单一颜色的小格存在 [1] 。每一个点阵图像包含了一定量的像素,这些像素决定图像在屏幕上所呈现的大小。也就是说,显示器屏幕实际上是由一个一个的小点(单位色块,即像素)构成的问题 1:像素和分辨率有什么关系呢?分辨率 = 水平方向像素 * 垂直方向像素屏幕分辨率例如,屏幕分辨率是 1920×1080,则该屏幕水平方向有 1920 个像素,垂直方向有 1080 个像素不同屏幕的像素大小是不同的,也就是说像素大小不像我们现行的长度单位(如米/m)那样有着固定的国际标准所以同样的像素大小在不同的设备上显示效果是不一样的,像素越小的屏幕显示的效果越清晰图像分辨率例如,一张图片分辨率是 300x200,则该图片在屏幕上按 1:1 缩放时,水平方向有 300 个像素,垂直方向有 200 个像素点图片分辨率越高,1:1 缩放时面积越大图片分辨率越低,1:1 缩放时面积越小同一台设备像素大小是不变的,那把图片放大超过 100%时占的像素点就多了,但是图像也会变得模糊问题 2:屏幕实现图片放大或缩小的原理是什么呢?其实是设备通过算法对图像进行了像素补足;同理,把图片按小于 100%缩放时,也是通过算法将图片像素减少百分比也可以将属性值设置为相对于其父元素属性的百分比,可以使子元素跟随父元素(暂且先理解成父元素,后面会详细说)的改变而改变emem 是相对于元素的字体大小来计算的,1em = <self>.font-size,也就说 em 值会根据元素本身的字体大小的改变而改变remrem 是相对于根元素的字体大小来计算,1em = <root>.font-size,也就说 em 值会根据根元素的字体大小的改变而改变<!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>em/rem练习</title> <style> font-size: 30px; .box1 { width: 300px; height: 100px; background-color: orange; .box2 { width: 50%; height: 50%; background-color: purple; .box3 { width: 10em; height: 10em; background-color: lightseagreen; /*当时用rem时,不管怎么改本元素的font-size都是不会变的。需要定义root元素的font-size才可以 */ .box4 { font-size: 30px; width: 10rem; height: 10rem; background-color: red; </style> </head> <body> <div class="box1"> <div class="box2"></div> </div> <div class="box3"></div> <div class="box4"></div> </body> </html>4、颜色单位人眼能够识别多少种颜色?正常人有三种视椎细胞,是三色视觉者(红绿蓝),总共能看到大约 100 万种颜色男的大约 130 万 女的大约 180 万大概有经验的油漆工人辨别 1000 种左右,再高就难以分辨了。比如红色,可以分为 50 个等级,邻近的两个等级能够别出来,说明他的眼睛辨别能力就很不错了。过去的老工人,凭肉眼可辨别 50 种黑色,当然都要有特定的样板色做对比。我引用了网上的一些答案,也是众说纷纭。不过我的理解是人眼能至少接收 100 多万种颜色,因人而异但最多只能够对 1000 多种颜色做出识别,因人而异css 中的颜色名称我们生活中会使用各种颜色名称去描述看到的各种颜色,在 css 中当然也可以直接使用颜色名来设置颜色,比如:red、orange、yellow、blue、green 等等其中有 140 种颜色名称是所有浏览器都支持的,但是有个问题,就是在 css 中直接使用颜色名非常不方便而且世界上有无数种颜色,人眼也不能分辨出所有颜色,更不可能对每一种颜色都进行命名而且就算能够有办法对那么多种颜色进行命名,我们也不可能一个一个的去记或去查这种对应关系。试问下,有多少人看一眼某个颜色,就能够在调色板上快速准确的定位那个颜色或者直接叫出那种颜色的名称?这显然不现实,至少现在如此另外,那么 css 中还可以怎么调和出更多的颜色呢?在介绍 css 的颜色单位之前,我们首先来了解下光的三原色,因为 css 的颜色单位就是按照光的三原色来调和的发现光的色散奥妙之后,牛顿开始推论:既然白光能被分解及合成,那么这七种色光是否也可以被分解或合成。于是,纷繁的实验和不停的计算充斥着他日后的生活。一段时间后,牛顿通过计算,得出了一个结论:七种色光中只有红、绿、蓝三种色光无法被分解,于是也就谈不到合成了。而其他四种色光均可由这三种色光以不同比例相合而成。于是红、绿、蓝则被称为“三原色光”或“光的三原色”(注意,这有别于我们熟知的三原色“红黄蓝”)。牛顿通过计算得出上述结论后,未能完成实验,便与世长辞。这里再科普下光的三原色和颜料的三原色的区别颜料三原色(CMYK):品红、黄、青(天蓝)。色彩三原色可以混合出所有颜料的颜色,同时相加为黑色,黑白灰属于无色系。光学三原色(RGB):红、绿、蓝(靛蓝)。光学三原色混合后,组成显示屏显示颜色,三原色同时相加为白色,白色属于无色系(黑白灰)中的一种。那看到这里有人会问了,css 为什么不按照颜料的三原色来调和呢?因为道理很简单,聪明的小伙伴应该已经知道答案了。上面我们也说过,屏幕是由像素组成的,每个像素就是一个单位色块。而这个单位色块之所以能显示颜色,就是靠发光来实现的既然光是由三种色光组成的,任何一种颜色均可以由这三种颜色调和出来的,那么为什么我们不能用三原色来表示一种颜色呢?RGB 值RGB 通过三原色的不同浓度来调配出不同的颜色语法:RGB(red, green, blue)范围:每一种颜色的范围在 0 ~ 255(0% ~ 100%)之间RGBA就是在 rgb 的基础上增加了一个 a 表示不透明度1表示完全不透明0表示完全透明.5半透明十六进制的 RGB 值就是 RGB 值的十六进制写法语法:#RRGGBB范围:每一种颜色的范围在 00 ~ ff 之间如果颜色两位两位重复可以进行简写,如#aabbcc => #abc在 vscode 中,我们可以看到其会对颜色进行预览展示。并且将鼠标移至 color 处悬浮,会智能的弹出一个 rgb 调色板,方便我们进行调色如果我们看到某种颜色,非常喜欢,那么在哪里才能买得到呢? 怎么知道这个颜色的 rgb 值呢?我们可以直接搜索黄色,哦不是,取色器!有些录制软件也会自带取色功能,如 FastStone Capture下载地址:FastStone Capture - Download这里推荐一个mac的取色器Snipaste:https://www.macwk.com/soft/snipasteHSLHSL值 HSLA值色相(0~360)饱和度,颜色的浓度(0~100%)亮度,颜色的亮度(0~100%)不透明度(0~1)六、盒模型1、文档流(normalflow)网页是一个多层的结构,一层摁着一层通过 CSS 可以分别为每一层来设置样式,作为用户来讲只能看到最顶上一层这些层中,最底下的一层称为文档流文档流是网页的基础我们所创建的元素默认都是在文档流中进行排列对于我们来元素主要有两个状态在文档流中不在文档流中(脱离文档流)那么元素在文档流中有什么特点,我们接着往下看2、块元素块元素会在页面中独占一行默认宽度是父元素的全部(会把父元素撑满)默认高度是被内容撑开(子元素)3、行内元素行内元素不会独占页面的一行,只占自身的大小行内元素在页面中左向右水平排列(书写习惯一致)如果一行之中不能容纳下所有的行内元素,则元素会换到第二行继续自左向右排列行内元素的默认宽度和高度都是被内容撑开4、盒子模型盒模型、盒子模型、框模型(box model)CSS 将页面中的所有元素都设置为了一个矩形的盒子将元素设置为矩形的盒子后,对页面的布局就变成将不同的盒子摆放到不同的位置每一个盒子都由一下几个部分组成:内容区(content)内边距(padding)边框(border)外边距(margin)内容区(content)内容区是盒子模型的中心,它呈现了盒子的主要信息内容,这些内容可以是文本、图片等多种类型元素中的所有的子元素和文本内容都在内容区中width和height 设置排列内容区的大小width 设置内容区的宽度height 设置内容区的高度示例.box1 { width: 200px; height: 200px; border-color: red; solid 实线 dotted 点状虚线 dashed 虚线 double 双线 border-style: solid; background-color: rgb(73, 184, 139); }效果边框(border)边框属于盒子边缘,边框里边属于盒子内部,出了边框都是盒子的外部注意:边框的大小会影响到整个盒子的大小border-width:边框的宽度:默认 3pxborder-top-width 上边框的宽度border-right-width 右边框的宽度border-bottom-width 下边框的宽度border-left-width 左边框的宽度border-color 边框的颜色:默认使用 color 的颜色值border-top-color 上边框的颜色border-right-color 右边框的颜色border-bottom-color 下边框的颜色border-left-color 左边框的颜色border-style:边框的样式:没有默认值,必须指定border-top-style 上边框的样式border-right-style 右边框的样式border-bottom-style 下边框的样式border-left-style 左边框的样式效果(solid)效果(dotted)效果(dashed)效果(double)不论是border-width 、 border-color 、border-style 还是其衍生出来的属性写法,都可以指定每个方向的边框情况设定几个值就决定了对应方向的宽度、颜色或样式四个值:上 右 下 左三个值:上 左右 下两个值:上下 左右一个值:上下左右其实不管设置几个值,只要记住:其顺序是按顺时针方向设置的,剩下的可以由矩形的对称性推导出来border:简写属性,通过该属性可以同时设置边框所有的相关样式,并且没有顺序要求border-top 上边框的宽度、颜色和样式border-right 右边框的宽度、颜色和样式border-bottom 下边框的宽度、颜色和样式border-left 左边框的宽度、颜色和样式.box1 { border: 10px red solid; }内边距(padding)内边距,也叫填充,是内容区和边框之间的空间padding-top 上内边距padding-right 右内边距padding-bottom 下内边距padding-left 左内边距padding 内边距的简写属性,可以同时指定四个方向的内边距,规则和边框中属性值设置一样注意:内边距的设置会影响到盒子的大小,背景颜色会延伸到内边距上示例<style> .outer { width: 200px; height: 200px; border: 10px orange solid; background-color: pink; padding-right: 100px; padding-top: 100px; padding-bottom: 100px; padding-left: 100px; .inner { width: 200px; height: 200px; background-color: greenyellow; </style> <div class="outer"> <div class="inner"></div> </div>效果可以看出,当内外 div 宽度和高度一样时,由于 outer 设置了一个 padding 属性,其盒子大小被“撑大了”盒子可见框的大小,由内容区、内边距和边框共同决定,所以在计算盒子大小时,需要将这三个区域加到一起计算外边距(margin)外边距,也叫空白边,位于盒子的最外围,是添加在边框外周围的空间。空白边使盒子之间不会紧凑地连接在一起,是 CSS 布局的一个重要手段注意:外边距不会影响盒子可见框的大小,但是外边距会影响盒子的位置和占用空间一共有四个方向的外边距:margin-top:上外边距设置正值,元素自身向下移动设置负值,元素自身向上移动margin-right:右外边距设置正值,其右边的元素向右移动设置负值,其右边的元素向左移动上述说法并不准确,对于块元素,设置margin-right不会产生任何效果margin-bottom :下外边距设置正值,其下边的元素向下移动设置负值,其下边的元素向上移动上述说法并不准确,对于块元素,会有垂直方向上的边距重叠问题(后面会细说)margin-left:左外边距设置正值,元素自身向右移动设置负值,元素自身向左移动元素在页面中是按照自左向右的顺序排列的,所以默认情况下如果我们设置的左和上外边距则会移动元素自身而设置下和右外边距会移动其他元素示例 1.box1 { width: 200px; height: 200px; background-color: #bfa; border: 10px orange solid; margin-top: 100px; margin-right: 100px; margin-bottom: 100px; margin-left: 100px; }效果示例 2:.box1 { width: 200px; height: 200px; background-color: #bfa; border: 10px orange solid; margin-bottom: 100px; .box2 { width: 200px; height: 200px; background-color: #bfa; border: 10px red solid; margin-top: 100px; }效果5、水平方向布局元素在其父元素中水平方向的位置由以下几个属性共同决定margin-leftborder-leftpadding-leftwidthpadding-rightborder-rightmargin-right一个元素在其父元素中,水平布局必须要满足以下的等式margin-left + border-left + padding-left + width + padding-right + border-right + margin-right = 其父元素的宽度以上等式必须满足,如果相加结果使等式不成立,则称为==过渡约束==则等式会自动调整调整的情况:如果这七个值中没有auto的情况,则浏览器会自动调整margin-right值以使等式满足100 + 0 + 0 + 200 + 0 + 0 + 0 = 800 ==> 100 + 0 + 0 + 200 + 0 + 0 + 500 = 800如果这七个值中有auto的情况,则会自动调整auto值以使等式成立这七个值中有三个值可以设置为auto :width、margin-left、maring-right如果某个值为 auto,则会自动调整auto的那个值以使等式成立200 + 0 + 0 + auto + 0 + 0 + 200 = 600 ==> 200 + 0 + 0 + 400 + 0 + 0 + 200 = 800auto + 0 + 0 + 200 + 0 + 0 + 200 = 600 ==> 400 + 0 + 0 + 200 + 0 + 0 + 200 = 800200 + 0 + 0 + 200 + 0 + 0 + auto = 600 ==> 200 + 0 + 0 + 200 + 0 + 0 + 400 = 800如果宽度为auto,则宽度会调整到最大,其他auto的外边距会自动设置为 0auto + 0 + 0 + auto + 0 + 0 + 200 = 600 ==> 0 + 0 + 0 + 600 + 0 + 0 + 200 = 800200 + 0 + 0 + auto + 0 + 0 + auto = 600 ==> 200 + 0 + 0 + 600 + 0 + 0 + 0 = 800auto + 0 + 0 + auto + 0 + 0 + auto = 600 ==> 0 + 0 + 0 + 800 + 0 + 0 + 0 = 800如果外边距都为auto,则auto的外边距会自动均分以使等式成立auto + 0 + 0 + 200 + 0 + 0 + auto = 600 ==> 300 + 0 + 0 + 200 + 0 + 0 + 300 = 800示例<style> .box1 { width: 200px; height: 200px; background-color: #bfa; border: 10px orange solid; /* 下列条件等价于 margin: 0 auto */ margin-left: auto; margin-right: auto; </style> <div class="box1"></div>效果6、垂直方向布局元素溢出子元素是在父元素的内容区中排列的,如果子元素的大小超过了父元素,则子元素会从父元素中溢出使用overflow/overflow-x/overflow-y属性来设置父元素如何处理溢出的子元素可选值:visible/hidden/scroll/autovisible 溢出内容会在父元素外部位置显示,默认值示例<style> .box1 { width: 200px; height: 200px; background-color: #bfa; border: 10px orange solid; overflow: visible; /* 默认值 */ </style> <div class="box1"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Asperiores aspernatur illo inventore deleniti laudantium quaerat excepturi sed quidem tempore? Eaque, cumque porro. Fuga quam error cupiditate quasi eveniet in numquam! </div>效果hidden 溢出内容会被裁剪,不会显示示例<style> .box1 { width: 200px; height: 200px; background-color: #bfa; overflow: hidden; /* 隐藏溢出内容 */ </style> <div class="box1"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Asperiores aspernatur illo inventore deleniti laudantium quaerat excepturi sed quidem tempore? Eaque, cumque porro. Fuga quam error cupiditate quasi eveniet in numquam! </div>效果scroll 生成两个滚动条,通过滚动条来查看完整的内容示例<style> .box1 { width: 200px; height: 200px; background-color: #bfa; overflow: scroll; </style> <div class="box1"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Asperiores aspernatur illo inventore deleniti laudantium quaerat excepturi sed quidem tempore? Eaque, cumque porro. Fuga quam error cupiditate quasi eveniet in numquam! </div>效果<style> .box1 { width: 200px; height: 200px; background-color: #bfa; overflow: auto; </style> <div class="box1"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Asperiores aspernatur illo inventore deleniti laudantium quaerat excepturi sed quidem tempore? Eaque, cumque porro. Fuga quam error cupiditate quasi eveniet in numquam! </div>效果边距折叠垂直外边距的重叠(折叠):相邻的垂直方向外边距会发生重叠现象兄弟元素兄弟元素间的相邻,垂直外边距会取两者之间的较大值(两者都是正值)特殊情况:如果相邻的外边距一正一负,则取两者的和如果相邻的外边距都是负值,则取两者中绝对值较大的示例.box1, .box2 { width: 200px; height: 200px; font-size: 100px; .boxl { background-color: #bfa; /*设置一个下外边距*/ margin-bottom: 100px; .box2 { background-color: orange; /*设置一个上外边距*/ margin-top: 100px; }效果疑问当浏览器缩放比例是 100%时,我们使用FastStone Capture工具自带的刻度尺测量,发现“兄弟”之间似乎没有我们想象的那么“亲近”两者的垂直方向间距是 125px,我们明明上下元素设置的都是 100px 啊,这是为什么呢?在网页布局中,通过谷歌浏览器或火狐浏览器预览时,发现我们定义的盒模型 width,height,margin,padding 值都是不准确的谷歌、火狐浏览器 缩放为 80% 时,margin 值才正确[2]总结兄弟元素之间的外边距的重叠,对于开发是有利的,所以我们不需要进行处理父子元素父子元素间相邻外边距,子元素会传递给父元素(上外边距)示例.box3{ width:200px; height:200px; background-color: #bfa; .box4{ width: 100px; height: 100px; background-color: orange; /* margin-top: 100px; */ }效果不加 margin-top加 margin-top父子外边距的折叠会影响到页面的布局,必须要进行处理处理方式 11、我们转换思路,将对子元素的调整转为对父元素的调整.box3 { width: 200px; height: 200px; background-color: #bfa; padding-top: 100px; /* 不调整子元素的margin,而是转而调整父元素的padding */ .box4 { width: 100px; height: 100px; background-color: orange; /* margin-top: 100px; */ }效果可以看到父元素位置虽然正确了,但是高度却被“撑大了”。我们之前说过,padding 属性会影响元素的大小2、这里我们还需要计算并手动调整下父元素的高度.box3 { width: 200px; height: 100px; /* height: 200px; */ background-color: #bfa; padding-top: 100px; .box4 { width: 100px; height: 100px; background-color: orange; }效果处理方式 21、我们仍然保留子元素的margin-top属性,但是给父元素加一个上边框.box3 { width: 200px; height: 200px; background-color: #bfa; border-top: 1px rebeccapurple solid; /* 在父元素上加一个border-top(上边框) */ .box4 { width: 100px; height: 100px; background-color: orange; margin-top: 100px; /* 不删除,保留 */ }效果2、但是因为加了 1px 的边框,所以父元素盒子的高度也增加了 1px。那我们就需要手动调整父元素的高度,同时让边框颜色与父元素盒子颜色保持一致.box3 { width: 200px; height: 199px; /* height: 200px; */ background-color: #bfa; border-top: 1px #bfa solid; .box4 { width: 100px; height: 100px; background-color: orange; margin-top: 100px; }但是我们没有发现一个问题不难发现一个问题,子元素也往下移动了 1px 的距离因为父元素高度少了 1px,而子元素的 margin-top 是从边框下面开始算的所以,凭借大家朴素的情感,哪个应该怎么判? 应该怎么改?改法也很简单,margin-top 减去一个像素即可.box3 { width: 200px; height: 199px; background-color: #bfa; border-top: 1px #bfa solid; .box4 { width: 100px; height: 100px; background-color: orange; margin-top: 99px; /* margin-top: 100px; */ }效果同时,我们用刻度尺测量,父子元素底部是在一条水平线上的脱离文档流上述示例 2 中,使用了 border 属性,就让子元素的外边距不去传递给父元素了,这是为什么呢?margin (子元素远离父元素边框)[3]如果父盒子没有设置 border 框着,那么他的子元素无法利用 margin-top 来远离父元素的上边框如果使用了 margin-top 会使子元素和父元素一起往下移(子想离,父不设置 border 边框 则离得是流 不是父盒子)应该是 border 让元素脱离了文档流(margin 塌陷)好吧好吧,至于什么是 margin 塌陷,我也是问了度娘,有兴趣的可以自行百度,这里就不再赘述了7. 行内元素的盒模型行内元素不支持设置宽度和高度.s1 { /* 行内元素设置了宽高也没用,不会生效 */ width: 100px; height: 100px; background-color: yellow; }行内元素可以设置padding,但是垂直方向padding不会影响页面的布局.s1 { /* 下方的div元素并没有因span设置了padding属性,而受到位置上的影响 */ padding: 100px; background-color: yellow; .box1 { width: 200px; height: 200px; background-color: #bfa; }行内元素可以设置border,垂直方向的border不会影响页面的布局.s1 { border: 10px orange solid; background-color: yellow; .box1 { width: 200px; height: 200px; background-color: #bfa; }行内元素可以设置margin,垂直方向的margin不会影响页面的布局.s1 { margin: 100px; background-color: yellow; .box1 { width: 200px; height: 200px; background-color: #bfa; }如果我就是想要行内元素对页面布局产生影响呢?那就拉出去枪毙了! 那也是有办法的!display用来设置元素显示的类型inline将元素设置为行内元素block 将元素设置为块元素.s1 { margin: 100px; background-color: yellow; /* 将行内元素设置为块元素 */ display: block; }inline-block 将元素设置为行内块元素行内块,既可以设置宽度和高度又不会独占一行.s1 { margin: 100px; background-color: yellow; /* 将行内元素设置为行内块元素,兼顾行内元素和块元素的特点 */ display: inline-block; }table将元素设置为一个表格none元素不在页面中显示.s1 { margin: 100px; background-color: yellow; /* 将行内元素设置为none:不显示 */ display: none; }不显示是不显示了,但是原来属于 s1 的位置也没了visibility用来设置元素的显示状态visible默认值,元素在页面中正常显示hidden元素在页面中隐藏不显示,但是依然占据页面的位置.s1 { margin: 100px; background-color: yellow; display: block; visibility: hidden; }8. 浏览器的默认样式通常情况,浏览器都会为元素设置一些默认样式默认样式的存在会影响到页面的布局,通常情况下编写网页时必须要去除浏览器的默认样式(PC 端的页面)在当今网页设计/开发实践中,使用 CSS 来为语义化的(X)HTML 标记添加样式风格是重要的关键。在设计师们的梦想中都存在着这样的一个完美世界:所有的浏览器都能够理解和适用多有 CSS 规则,并且呈现相同的视觉效果(没有兼容性问题)。但是,我们并没有生活在这个完美的世界,现实中发生的失窃却总是恰恰相反,很多 CSS 样式在不同的浏览器中有着不同的解释和呈现。当今流行的浏览器(如:Firefox、Opera、Internet Explorer、Chrome、Safari 等等)中,有一些都是以自己的方式去理解 CSS 规范,这就会导致有的浏览器对 CSS 的解释与设计师的 CSS 定义初衷相冲突,使得网页的样子在某些浏览器下能正确按照设计师的想法显示而且有些浏览器却并没有按照设计师想要的样子显示出来,这就导致浏览器的兼容性问题。更糟的是,有的浏览器完全无视 CSS 的一些声明和属性。[4]我们可以尝试编写 css 样式,以去除浏览器的默认样式示例html 代码<div class="box1"></div> <p>我是一个段落</p> <p>我是一个段落</p> <p>我是一个段落</p> <ul> <1i>列表项1</1i> <1i>列表项2</1i> <1i>列表项3</1i> </ul>css 代码.box1 { width: 100px; height: 100px; border: 1px solid black; }效果F12 看盒子默认样式段落之间有 16px 的默认行距列表外有 16px 的上下外边距和 40px 的左内边距,而且每项列表前有一个小黑点去除默认样式去除与浏览器的边缘间距body { margin: 0; }去除段落之间行距p { margin: 0; }去除列表的上下外边距和左内边距ul { margin: 0; padding: 0; }我们只是去除了列表的内外边距,但是发现前面的黑点也消失了,真的如此吗?我们先给ul加上一个margin-leftul { margin: 0; padding: 0; margin-left: 16px; }看来黑点并没有自动清除,而只是“缩进”了浏览器的左侧去除列表前的黑点ul { margin: 0; padding: 0; margin-left: 16px; list-style: none; }再将之前加的 16px 的margin-left样式去除ul { margin: 0; padding: 0; /* margin-left: 16px; */ list-style: none; }到这里似乎就大功告成了,但是我们会发现写法似乎 很完美 有点麻烦body { margin: 0; margin: 0; margin: 0; padding: 0; list-style: none; }有没有简化空间了呢?答案是肯定的,我们前面介绍过通配选择器的概念,可以直接简化成一个简化写法* { margin: 0; padding: 0; list-style: none; }效果是一样的去除浏览器的默认样式的需求是非常普遍的,我们难道每次都需要手动去除浏览器的默认样式?这样岂不是很麻烦,难道官方就没有想到解决方案吗?答案也是肯定的,有!正因为上述冲突和问题依然存在于这个”不完美的世界”,所以一些设计师想到了一种避免浏览器兼容性问题的方法,那就是 CSS Reset什么是 CSS Reset?我们可以把它叫做 CSS 重设,也有人叫做 CSS 复位、默认 CSS、CSS 重置等。CSS 重设就是先定义好一些 CSS 样式,来让所有浏览器都按照同样的规则解释 CSS,这样就能避免发生这种问题。[4:1]下方两种 css 样式,我们引入其中一个即可reset 样式官方地址:reset.css<link rel="stylesheet" href="assets/reset.css" />效果我们可以看到 reset.css 的作用就是将各个内外边距置为 0,将一些样式置为 none/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 License: none (public domain) html, body, span, applet, object, iframe, blockquote, abbr, acronym, address, cite, code, samp, small, strike, strong, center, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; /* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, section { display: block; body { line-height: 1; list-style: none; blockquote, quotes: none; blockquote:before, blockquote:after, q:before, q:after { content: ""; content: none; table { border-collapse: collapse; border-spacing: 0; }normalize 样式官方地址:normalize.css<link rel="stylesheet" href="assets/normalize.css">效果这里并没有去除所有样式,因为 normalize 的作用不同于 reset。reset 是将所有默认样式去除,而 normalize 是将所有默认样式统一,这样在不同的浏览器里显示效果也是统一的至于文件内容就不再这里赘述了,感兴趣的可以仔细研究参考资料CSS 盒子模型:https://baike.baidu.com/item/CSS 盒子模型/9814562?fr=aladdin ↩︎谷歌、火狐浏览器 缩放为 80% 时,margin 值才正确:https://www.cnblogs.com/taohuaya/p/7642742.html ↩︎margin(子元素远离父元素边框):https://www.cnblogs.com/FlFtFw/p/9627026.html ↩︎目前比较全的 CSS 重设(reset)方法总结:https://www.cnblogs.com/hnyei/archive/2011/10/04/2198779.html ↩︎ ↩︎七、实战练习1、京东图片列表开发准备本来我们是想直接右键图片另存为的,但是发现并没有该选项,应该是京东对图片做了一定的限制不过,这不妨碍我们获取这些图片,当然你也可以采用截图的方式获取,这里我们采用另外一种方式通过 F12 可以看到,img元素的src属性,我们将三张图片的这些地址 copy 出来,直接在地址栏进行访问//img14.360buyimg.com/babel/s380x300_jfs/t1/168591/2/9328/64891/603ddb1aE93567699/3e4e717eeac051b2.jpg.webp //img12.360buyimg.com/babel/s380x300_jfs/t1/152314/13/19839/57522/603e118dE941f0ce9/fdff58457adbef3e.jpg.webp //img10.360buyimg.com/babel/s380x300_jfs/t1/154848/7/7426/82296/5fc072eeE20809200/34dca267e049d035.jpg.webp这里就可以进行图片另存为了,当然你也可以直接在src上填写这些地址不过,细心的同学会发现,这张图片的格式是jpg.webp .avif 后缀结尾的因为我是在谷歌浏览器中访问的,而谷歌浏览器有自己的特有的一种图片格式 webp(这个我们在第三节-字符实体与语义标签中介绍过)当然这个格式不是谷歌自己进行转换的,而应该是京东做了不同浏览器之间的适配,即不同的浏览器传递不同格式的图片Q:怎么验证这种说法呢?A:我们可以打开非 Chrome 内核的浏览器,使用 F12 查看img的src地址就会发现不一样的地方了这里我用微软自带的 IE 浏览器(温馨提示:微软官宣定于 2022 年 6 月 15 日完全停止对 IE 的支持)扩展:对于AVIF格式,参考:https://zhuanlan.zhihu.com/p/444624167对比就可以发现,无非就是在 Chrome 浏览器里后缀名多了一个.webp而已对比就可以发现,无非就是在 Chrome 浏览器里后缀名多了一个.webp而已<!-- IE中的src地址 --> //img14.360buyimg.com/babel/s380x300_jfs/t1/168591/2/9328/64891/603ddb1aE93567699/3e4e717eeac051b2.jpg <!-- Chrome中的src地址 --> //img14.360buyimg.com/babel/s380x300_jfs/t1/168591/2/9328/64891/603ddb1aE93567699/3e4e717eeac051b2.jpg.webp知道这个原理,我们除了可以直接在图片另存为保存为jpg格式,其实还可以直接修改 url 地址最后,我们将下载的图片放入assets(自定义目录)工程目录下即可布局剖析我们使用 F12 进行调试,可以看到京东图片列表的具体元素及属性整体使用一个li元素包裹,里面又套了一层 div`元素,宽高比:190:470每张图片使用一个img元素,同时分别用a元素包裹,宽高比:190:150三张图片高度和为 1503=450 < 470,注意到图片之间存在 210px 的外边距这样,整个京东的图片列表的整体布局就非常清晰了但是,我们不会那么去实现代码。因为li元素应该包裹在ul元素或者ol元素中,而这里并没有遵循 css 中的语义标签使用规范我们先看一下这么写会有什么问题<link rel="stylesheet" href="../assets/reset.css" /> <!-- ul包裹li --> <ul> <li>ul li</li> <li>ul li</li> <li>ul li</li> </ul> <!-- div包裹li --> <div> <li>div li</li> <li>div li</li> <li>div li</li> </div>效果由于使用了 reset 样式,浏览器的默认样式被我们去除了。但是使用ul包裹的li元素和使用div包裹的li元素存在明显的区别:使用ul包裹的li元素是没有默认样式的使用div包裹的li元素前仍然存在黑点我想是因为京东在这里实现了自己的样式替换,所以为了避免重复 reset 默认样式,我们采用正常的列表标签<ul> <li> <a href="javascript:;"><img src="assets/1.jpg" /></a> </li> <li> <a href="javascript:;"><img src="assets/2.jpg" /></a> </li> <li> <a href="javascript:;"><img src="assets/3.jpg" /></a> </li> </ul>到这里我们基本骨架已经有了,不过因为没有写 css 样式,图片几乎占据了整个浏览器页面样式添加方式 1还记得上面分析对布局结构的分析吗?我们首先调整整体的宽高比和单个图片的宽高比ul { width: 190px; height: 470px; ul > li img { 这里其实只调整高度即可,因为我们下载的图片宽高比跟F12中调试的是一致的 而且一般情况下,不会固定或修改图片在网页中显示的宽高比 因为如果我们随意调整css中的宽高比,会导致图片变形 这里任意只调整高度或宽度,图片可以保持原比例大小 /* width: 190px; */ height: 150px; }当然这只是其中写法,我们还可以换个思路,退一步来思考方式 2我们呢不去限制图片的宽或高,而是对超出ul元素部分(溢出部分)进行隐藏ul { width: 190px; height: 470px; overflow: hidden; }但是因为图片本身的大小还没有做限制,所以图片保持了原来的图片比例和大小小剧场:我们发现下载下来的图片分辨率大小为 380*300,宽和高都刚好是浏览器中图片宽高的 2 倍这只是巧合么?不!这是京东为了高分辨率设备而做的适配,保证在一些高清屏下也能够保持清晰那我们再对图片添加固定的宽或高不就行了?不!我们直接指定宽或高的话,overflow属性不就显得多此一举嘛我们给 img 元素设定一个100%的width属性ul > li img { /* height: 150px; */ width: 100%; }Q:为什么不能用auto呢?A:我们前一节-盒模型中讲过,水平布局必须要满足一个等式,不满足即存在过渡约束,会做自动调整而ul元素是块元素,块元素什么特点?独占一行啊!图片宽度为 380px,浏览器宽度为 1920px(我本机中最大化浏览器的宽度)现在的等式为0 + 0 + 0 + 380px + 0 + 0 + 0 = 1920px这七个值中没有auto的情况,所以浏览器会自动调整margin-right值以使等式满足:0 + 0 + 0 + 380px + 0 + 0 + (1920-380)px = 1920px所以如果使用auto属性值,整个过程中图片的width不会发生变化,展现出来的效果就依然是裁剪的样式Q:为什么100%可以呢?A:我们知道100%是会按照父元素计算的,img的父元素是a,a的父元素是li,li的父元素是ul也就是说,由于我们没有给a和li单独设置样式,因此img最终会根据ul的宽度计算而如果只调整图片的宽或高,图片是会保持原比例进行缩放的所以这个时候就相当于给img设置了一个width=190px的属性值细节完善背景色通过颜色拾取器,识别背景色(我这里使用的是FastStone Capture中自带的Screen Color Picker)ul{ /* 添加背景色 */ background-color: #F4F4F4; }外边距根据布局剖析结果,我们给每个li元素添加一个 10px 的下边距ul > li { margin-bottom: 10px; 但是上述写法有些问题,最后一张图片也有10px的外边距,有可能会影响到页面布局 所以我们可以指定最后一个没有外边距,可以使用之前讲到的伪类选择器 ul > li:not(:last-child) { margin-bottom: 10px; }Q:为什么是给li元素添加呢?A:我们在调整布局结构的时候,特别是设置外边距,一般是设置块元素的,而不建议去调整行内元素或行内块元素最终效果核心代码<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>图片列表</title> <link rel="stylesheet" href="../assets/style/reset.css" /> <style> /* 设置body的背景颜色 */ body { background-color: antiquewhite; /* 设置外部ul的样式 */ .img-list { width: 190px; height: 470px; overflow: hidden; /* 在实际开发中不需要写,ul居中! */ margin: 50px auto; background-color: #f4f4f4; /* 设置li的位置 */ .img-list li:not(:last-child) { margin-bottom: 10px; /* 设置图片的大小 */ .img-list img { width: 100%; </style> </head> <body> <ul class="img-list"> <li> <a href="javascript:;"><img src="../assets/images/1.webp" /></a> </li> <li> <a href="javascript:;"><img src="../assets/images/2.webp" /></a> </li> <li> <a href="javascript:;"><img src="../assets/images/3.webp" /></a> </li> </ul> </body> </html>2、京东左侧导航条开发准备我们需要的就是这些文字,事先复制下来家用电器 手机 / 运营商 / 数码 电脑 / 办公 家居 / 家具 / 家装 / 厨具 男装 / 女装 / 童装 / 内衣 美妆 / 个护清洁 / 宠物 女鞋 / 箱包 / 钟表 / 珠宝 男鞋 / 运动 / 户外 房产 / 汽车 / 汽车用品 母婴 / 玩具乐器 食品 / 酒类 / 生鲜 / 特产 艺术 / 礼品鲜花 / 农资绿植 医药保健 / 计生情趣 图书 / 文娱 / 教育 / 电子书 机票 / 酒店 / 旅游 / 生活 理财 / 众筹 / 白条 / 保险 安装 / 维修 / 清洗 / 二手 工业品布局剖析整体使用ul和li元素,宽高比=190px:470px,其中上下存在 10px 的内边距(影响盒子大小)和 10px 的外边距(影响盒子布局)li中每个元素也比较简单,用a包裹文字,用span包裹斜杠每个li元素的宽高比=190px:25px,其中左边存在 18px 的内边距(注意右边是不存在的)a 元素没有什么大的布局,span元素左右存在 2px 的内边距结构搭建<ul> <li> <a href="javascript:;">家用电器</a> </li> <li> <a href="javascript:;">手机</a> <span>/</span> <a href="javascript:;">运营商</a> <span>/</span> <a href="javascript:;">数码</a> </li> <li> <a href="javascript:;">电脑</a> <span>/</span> <a href="javascript:;">办公</a> </li> <li> <a href="javascript:;">家居</a> <span>/</span> <a href="javascript:;">家具</a> <span>/</span> <a href="javascript:;">家装</a> <span>/</span> <a href="javascript:;">厨具</a> </li> <li> <a href="javascript:;">男装</a> <span>/</span> <a href="javascript:;">女装</a> <span>/</span> <a href="javascript:;">童装</a> <span>/</span> <a href="javascript:;">内衣</a> </li> <li> <a href="javascript:;">美妆</a> <span>/</span> <a href="javascript:;">个护清洁</a> <span>/</span> <a href="javascript:;">宠物</a> </li> <li> <a href="javascript:;">女鞋</a> <span>/</span> <a href="javascript:;">箱包</a> <span>/</span> <a href="javascript:;">钟表</a> <span>/</span> <a href="javascript:;">珠宝</a> </li> <li> <a href="javascript:;">男鞋</a> <span>/</span> <a href="javascript:;">运动</a> <span>/</span> <a href="javascript:;">户外</a> </li> <li> <a href="javascript:;">房产</a> <span>/</span> <a href="javascript:;">汽车</a> <span>/</span> <a href="javascript:;">汽车用品</a> </li> <li> <a href="javascript:;">母婴</a> <span>/</span> <a href="javascript:;">玩具乐器</a> </li> <li> <a href="javascript:;">食品</a> <span>/</span> <a href="javascript:;">酒类</a> <span>/</span> <a href="javascript:;">生鲜</a> <span>/</span> <a href="javascript:;">特产</a> </li> <li> <a href="javascript:;">艺术</a> <span>/</span> <a href="javascript:;">礼品鲜花</a> <span>/</span> <a href="javascript:;">农资绿植</a> </li> <li> <a href="javascript:;">医药保健</a> <span>/</span> <a href="javascript:;">计生情趣</a> </li> <li> <a href="javascript:;">图书</a> <span>/</span> <a href="javascript:;">文娱</a> <span>/</span> <a href="javascript:;">教育</a> <span>/</span> <a href="javascript:;">电子书</a> </li> <li> <a href="javascript:;">机票</a> <span>/</span> <a href="javascript:;">酒店</a> <span>/</span> <a href="javascript:;">旅游</a> <span>/</span> <a href="javascript:;">生活</a> </li> <li> <a href="javascript:;">理财</a> <span>/</span> <a href="javascript:;">众筹</a> <span>/</span> <a href="javascript:;">白条</a> <span>/</span> <a href="javascript:;">保险</a> </li> <li> <a href="javascript:;">安装</a> <span>/</span> <a href="javascript:;">维修</a> <span>/</span> <a href="javascript:;">清洗</a> <span>/</span> <a href="javascript:;">二手</a> </li> <li> <a href="javascript:;">工业品</a> </li> </ul>我们引入reset.css样式来去除浏览器的默认样式<link rel="stylesheet" href="../assets/style/reset.css" />到这里,基本的骨架就有了Q:那有些人会问了,我们不是引入了reset.css重置了浏览器的默认样式吗?为什么超链接还有样式?A:其实,如果仔细看 reset.css 的源代码,会发现a元素只是重置了一些基本的内外边距、边框和字体大小,并没有做完全把a元素的样式去除。这个下面会具体介绍样式添加根据布局剖析,我们可以直接设置整体的样式body { /* 这里顺便添加下背景色,通过颜色拾取器识别 */ background-color: #f4f4f4; /* 宽高 */ width: 190px; height: 450px; /* 内外边距 */ padding: 10px 0; margin: 10px auto; /* 处理溢出部分 */ overflow: hidden; /* 这里顺便添加下背景色,通过颜色拾取器识别 */ background-color: #fefefe; ul > li { /* 宽高,根据继承关系可以不写宽度 */ height: 25px; /* 内外边距,这里只有一个内边距 */ padding-left: 18px; /* 设置背景色:不是必须的,这里只是为了发现一些问题 */ background-color: #bfa; ul > li span { /* 内外边距*/ padding: 0 2px; }到这里整体样式就添加完毕,但我们发现有点问题别急!我们继续进行细节上的样式调整和优化细节完善要求效果目前的效果两个主要问题要求效果文字居中显示,而我们的文字偏左上角,底部有一定间距文字存在换行和重叠现象我们一个一个处理文字调整只需要给li元素添加一行属性line-height: 25px;文字虽然在一行上了,但是依然有重叠问题啊,需要怎么处理呢?要知道文字有几个属性:文字大小文字颜色文字样式我们通过 F12 看下这些属性废话少说,直接写代码font-size: 14px; color: #333;我们这里先不写text-decoration属性,看下效果写上text-decoration属性,再看下效果text-decoration: none;这里可以看到文字下划线消失了,因为我们使用的a标签包裹文字,而超链接具有一定的文字样式(就是蓝色字体带下划线),所以text-decoration属性就是调整文字样式的到这里,我们的重叠问题还是没有解决稳住,我们能赢! 我们再对比下要求的效果和我们现在的效果看出来区别了吗?(当然重点不是我们的背景色,这无关紧要)我们是把/符号用span包裹起来的,但是我们的/符号似乎又大又粗符号调整废话少说,上代码ul > li span { padding: 0 2px; /* 调整符号字体大小 */ font-size: 12px; }不过到这里,还是存在问题,我把span元素的内边距去除才可以 (这里我没搞清楚为什么,知道的小伙伴可以评论或私信我哦;不过对比各个元素的盒子模型没什么区别,而且字体样式我也调整了;而且总感觉/符号之间间隙大了一点,这里存疑先不管了,我们继续往下)悬浮样式我们注意到,当鼠标悬浮在某一行时,其背景颜色会有变化;同时,悬浮在某一个超链接上时,字体颜色变红这里要用到一个伪类选择器:hover,我们还是直接上代码ul > li { height: 25px; padding-left: 18px; /* background-color: #bfa; 同时注释掉之前的一个辅助我们查看问题的背景色 */ line-height: 25px; /* 悬浮在某一行时,其背景颜色会有变化 */ ul > li:hover { background-color: #d9d9d9; /* 悬浮在某一个超链接上时,字体颜色变红 */ ul > li a:hover { color: #c81724; }至此,我们的京东左侧导航栏的前端样式就基本完成了最终效果核心代码<link rel="stylesheet" href="css/reset.css" /> <style> body { /* body背景色,通过颜色拾取器识别 */ background-color: #f4f4f4; /* 整体宽高 */ width: 190px; height: 450px; /* 整体内外边距 */ padding: 10px 0; margin: 10px auto; /* 处理溢出部分 */ overflow: hidden; /* 整体背景色,通过颜色拾取器识别 */ background-color: #fefefe; ul > li { /* 每行宽高 */ height: 25px; padding-left: 18px; /* 每行行高 */ line-height: 25px; ul > li:hover { /* 悬浮背景色 */ background-color: #d9d9d9; ul > li a { /* 字体大小、颜色、样式 */ font-size: 14px; color: #333; text-decoration: none; ul > li a:hover { /* 悬浮字体颜色 */ color: #c81724; ul > li span { /* 内边距,应该是有的,但是有点问题 */ /* padding: 0 2px; */ font-size: 12px; </style>存疑问题通过一番折腾和研究,终于发现问题的关键所在因为在编写 HTML 代码时,每个li元素中的a的span标签都是换行的而 HTML 中会将多个空格合并成一个,所以a的span之间都多了一个空格有几种解决这个问题的方式一是调整 HTML 中每个li元素中的代码,使之在一行上二是给 ul 元素或 li 元素设置一个font-size: 0的属性值三是通过 js 去除多余的换行字符(目前还没有学习到,所以不用这种方式,而且较麻烦)我这里采用第二种方式ul > li { height: 25px; padding-left: 18px; line-height: 25px; /* 设置font-size */ font-size: 0; ul > li span { /* 设置内边距 */ padding: 0 2px; font-size: 12px; }到这里,我们往往会忍不住赞叹一下自己:Nice !3、网易新闻列表有了上面的实战步骤,对于网易新闻列表,我们就不进行那么详细的剖析了,直接上代码结构搭建/* ====================整体==================== */ .news_money { /* 整体布局 */ width: 300px; height: 324px; margin: 35px auto; /* 去除超链接样式 */ text-decoration: none; /* ====================标题==================== */ .news_title { height: 40px; border-top: 1px #ddeedd solid; .news_title .title { /* 标题整体布局 */ width: 32px; height: 24px; line-height: 24px; padding-top: 6px; border-top: 1px #f34540 solid; margin-top: -1px; .news_title a { /* 标题字体样式 */ font-size: 16px; font-weight: bold; color: #404040; .news_title a:hover { /* 标题悬浮样式 */ color: red; /* ====================图片==================== */ .new_img { height: 150px; .news_img:hover { 图片悬浮样式 这部分知识还没有学习到,所以只是做了一个简单的放大效果 但现在的效果其实是不对的,标题文字也被放大了 transform: scale(2); /* 图片标题 */ .news_img .img_title { /* 图片标题整体布局 */ height: 40px; line-height: 40px; margin-top: -40px; padding-left: 30px; .news_img a { /* 图片标题字体样式 */ color: #fff; font-weight: bold; /* ====================新闻列表==================== */ .news_list { height: 120px; margin-top: 12px; .news_list li { /* 新闻列表整体布局 */ width: 285px; height: 30px; line-height: 30px; padding-left: 15px; .news_list a { /* 新闻列表字体样式 */ font-size: 14px; color: #666; .news_list a:hover { /* 新闻列表悬浮样式 */ color: red; }效果:七、盒模型补充1、盒子大小默认情况下,盒子可见框的大小由内容区、内边距和边框共同决定box-sizing用来设置盒子尺寸的计算方式(设置 width 和 height 的作用).box1 { width: 100px; height: 100px; padding: 10px; background-color: orange; border: 10px solid red; /* box-sizing: content-box; */ box-sizing: border-box; }可选值:content-box 默认值,宽度和高度用来设置内容区的大小border-box 宽度和高度用来设置整个盒子可见框的大小width和height指的是内容区、内边距和边框的总大小2、轮廓outline用来设置元素的轮廓线,用法和border一模一样轮廓和边框不同点是,轮廓不会影响到可见框的大小边框.box { width: 200px; height: 200px; background-color: orange; border: 10px red solid; }轮廓.box { width: 200px; height: 200px; background-color: orange; outline: 10px red solid; }可以很明显看到outline与border的区别我们一般不会直接这么设置轮廓,而是下面这种场景.box:hover { outline: 10px red solid; }从上面的动态图也可以很清晰地看出,outline属性并没有改变盒子的布局3、阴影box-shadow属性用于在一个元素的框架周围添加阴影效果你可以设置多个由逗号分隔的效果一个盒状阴影由相对于元素的 X 和 Y 的偏移量、模糊和扩散半径以及颜色来描述box-shadow用来设置元素的阴影效果,阴影不会影响页面布局.box { width: 200px; height: 200px; background-color: yellow; box-shadow: 10px 10px orange; }box-shadow: 10px 10px 5px orange;box-shadow: 10px 10px 5px rgba(0, 0, 0, 0.2);第一个值-水平偏移量:设置阴影的水平位置正值向右移动负值向左移动第二个值-垂直偏移量:设置阴影的垂直位置正值向下移动负值向上移动第三个值-阴影的模糊半径第四个值-阴影的颜色4. 圆角border-radius属性使一个元素的外边框边缘的角变圆你可以设置一个半径来做圆角,或者设置两个半径来做椭圆角border-radius 用来设置圆角,圆角设置的是圆的半径大小border-top-left-radiusborder-top-right-radiusborder-bottom-left-radiusborder-bottom-right-radiusborder-radius: 20px;border-top-right-radius: 50px 100px;border-radius 可以分别指定四个角的圆角四个值:左上 右上 右下 左下三个值:左上 右上/左下 右下两个值:左上/右下 右上/左下一个值:左上/右上/右下/左下这里同样不需要死记硬背,只要记住遵循顺时针方向和矩形中心点对称原则与border不同的是,border是从上开始顺时针设置,而圆角是从左上开始圆原理很简单,就是绘制正方形,并将四个圆角半径设置为正方形的一半.box { width: 200px; height: 200px; background-color: yellow; border-radius: 50%; }椭圆只需要对上述样式对一点点的改动,设置width和height属性不相等即可.box { width: 300px; height: 200px; background-color: yellow; border-radius: 50%; }八、浮动1、浮动的简介通过浮动可以使一个元素向其父元素的左侧或右侧移动使用float属性来设置于元素的浮动none 默认值,元素不浮动left 元素向左浮动right 元素向右浮动注意元素设置浮动以后,水平布局的等式便不需要强制成立元素设置浮动以后,会完全从文档流中脱离,不再占用文档流的位置,所以元素下边的还在文档流中的元素会自动向上移动也可以参考:https://www.cnblogs.com/zhaostudy/p/16558751.html2、浮动的特点浮动元素会完全脱离文档流,不再占据文档流中的位置设置浮动以后,元素会向父元素的左侧或右侧移动浮动元素默认不会从父元素中移出<style> .box1 { width: 100px; height: 100px; background-color: orange; float: left; .box2 { width: 200px; height: 200px; background-color: yellowgreen; </style> <body> <div class="box1"></div> <div class="box2"></div> </body>浮动元素向左或向右移动时,不会超过前边的浮动元素(先来后到的顺序)<style> .box1 { width: 200px; height: 200px; background-color: orange; float: left; .box2 { width: 200px; height: 200px; background-color: red; float: left; .box3 { width: 200px; height: 200px; background-color: yellow; float: left; </style> <div class="box1"></div> <div class="box2"></div> <div class="box3"></div>浮动元素不会超过上边的浮动的兄弟元素,最多就是和它一样高<style> .box1 { width: 300px; height: 300px; background-color: orange; float: left; .box2 { width: 400px; height: 400px; background-color: red; float: left; .box3 { width: 300px; height: 300px; background-color: yellow; float: right; </style> <div class="box1"></div> <div class="box2"></div> <div class="box3"></div>如果浮动元素的上边是一个没有浮动的块元素,则浮动元素无法上移<style> .box1 { width: 200px; height: 200px; background-color: orange; .box2 { width: 200px; height: 200px; background-color: red; float: left; </style> <div class="box1"></div> <div class="box2"></div>浮动元素不会盖住文字,文字会自动环绕在浮动元素的周围,所以我们可以利用浮动来设置文字环绕图片的效果简单总结:浮动目前来讲它的主要作用就是让页面中的元素可以水平排列,通过浮动可以制作一些水平方向的布局元素设置浮动以后,将会从文档流中脱离,从文档流中脱离后,元素的一些特点也会发生变化3、脱离文档流的特点块元素:块元素不再独占页面的一行脱离文档流以后,块元素的宽度和高度默认都被内容撑开<style> .box1 { background-color: orange; /* float: left; */ </style> <div class="box1">hello</div>行内元素:行内元素脱离文档流以后会,特点和块元素一样<style> span { width: 200px; height: 200px; background-color: orange; float: left; </style> <span>I am a Span</span>脱离文档流之后的特点很像行内块元素,不过存在一些差异<style> span { width: 200px; height: 200px; background-color: orange; /* display: inline-block; */ float: left; </style> <span>I am a Span</span> <span>I am a Span</span>4、简单布局整体样式目的熟悉布局(块元素、浮动)公共 css 部分复用复习语义标签代码html 代码<!-- 页眉 --> <header></header> <!-- 主体 --> <main> <!-- 左边栏 --> <nav></nav> <!-- 中心 --> <article> <!-- 内容上 --> <div class="top"></div> <!-- 内容下 --> <div class="bottom"> <!-- 内容左 --> <div class="left"></div> <!-- 内容中 --> <div class="middle"></div> <!-- 内容右 --> <div class="right"></div> </div> </article> <!-- 右边栏 --> <aside></aside> </main> <!-- 页脚 --> <footer></footer>css代码/* 公共部分 */ header, main, footer { width: 1000px; margin: 10px auto; main nav, main article, main aside { float: left; /* 虽然设置浮动了,但整体大小是被内容撑开的,所以设置一个高度 */ height: 100%; .bottom .left, .bottom .middle, .bottom .right { float: left; width: 220px; height: 100%; /* ==========整体布局-上========== */ header { height: 100px; background-color: silver; /* ==========整体布局-中========== */ main { height: 400px; background-color: #bfa; /* ------左边栏------ */ main nav { width: 150px; background-color: red; /* ------中心------ */ main article { width: 680px; background-color: green; margin: 0 10px; /* ---上--- */ article .top { height: 190px; background-color: yellow; margin-bottom: 10px; /* ---下--- */ article .bottom { height: 200px; background-color: orange; /* 左 */ .bottom .left { background-color: lightblue; /* 中 */ .bottom .middle { background-color: gray; margin: 0 10px; /* 右 */ .bottom .right { background-color: wheat; /* ------右边栏------ */ main aside { width: 150px; background-color: blue; /* ==========整体布局-下========== */ footer { height: 100px; background-color: tomato; }效果5、练习:w3school 导航条去除默认样式,引入 reset.css<link rel="stylesheet" href="css/reset.css" />css 样式/* 去除默认样式 */ text-decoration: none; /* ul整体布局 */ .menu { width: 1211px; height: 48px; background-color: #e8e7e3; margin: 100px auto; /* li整体布局 */ .nav { /* 浮动li元素 */ float: left; width: 173px; line-height: 48px; .nav a { /* 注意点:升级为块元素,使之继承父类宽高 否则鼠标悬浮在li元素上时,鼠标“箭头”不会进入a元素变成“小手” */ display: block; /* 内容水平居中 */ text-align: center; /* 字体样式 */ font-size: 14px; color: #777777; font-family: Verdana, Arial, "微软雅黑", "宋体"; /* 超链接悬浮效果 */ .nav a:hover { background-color: #3f3f3f; color: #e8e7e3; }html 代码<ul class="menu"> <li class="nav"><a href="#">HTML/CSS</a></li> <li class="nav"><a href="#">Browser Side</a></li> <li class="nav"><a href="#">Server Side</a></li> <li class="nav"><a href="#">Programming</a></li> <li class="nav"><a href="#">XML</a></li> <li class="nav"><a href="#">Web Building</a></li> <li class="nav"><a href="#">Reference</a></li> </ul>效果73bdf885768ca7b899886f33caef7f4e.gif九、高度塌陷与 BFC1、高度塌陷在浮动布局中,父元素的高度默认是被子元素撑开的当子元素浮动后,其会完全脱离文档流,子元素从文档流中脱离将会无法撑起父元素的高度,导致父元素的高度丢失父元素高度丢失以后,其下的元素会自动上移,导致页面的布局混乱79ace51ec70494c5917ac3d27ff5329b.gif所以高度塌陷是浮动布局中比较常见的一个问题,这个问题我们必须要进行处理!11a161357960e3308e5d889ca6c60eba.jpeg别急,我们接着往下看2、BFCBFC(Block Formatting Context)块级格式化环境BFC 是一个 CSS 中的一个隐含的属性,可以为一个元素开启 BFC开启 BFC 该元素会变成一个独立的布局区域元素开启 BFC 后的特点:不会被浮动元素覆盖父子元素外边距不会重叠可以包含浮动的元素428820832a6f375b1cda617fde8453b8.jpeg可以通过一些特殊方式来开启元素的 BFC:设置为浮动(不推荐):很明显下方元素被覆盖了,总不能让所有元素都浮动吧a38cbf9ed8239821e3a9467190cbf1fa.gif设置为行内块元素(不推荐):不再独占一行,宽度变了,同时与下方元素产生了一点空隙设置overflow为非visible值:既没有覆盖元素,也保持了独占一方的特性(保持了宽度),与下方元素也保持了最初的间隙常用的方式为元素设置overflow:hidden(overflow:auto也是 ok 的) 开启其 BFC, 以使其可以包含浮动元素overflow:scroll 会有滚动条,可能并不需要的,所以不太推荐不过,这种方式也存在一定问题,如下,overflow并没有完全清除 div2 布局上受到的影响3c81ef620cbd90f54bc1023f38967e8b.gif总结可以通过变成浮动元素,来防止自身被浮动元素覆盖(有点“以毒攻毒”那味了)可以设置行内块,来防止自身及其他元素被浮动元素覆盖(如果说浮动是“独善其身”,那行内块就有点“兼济天下”的意思)可以设置overflow属性,包含浮动元素(既“独善其身”,又“兼济天下”,但仍有缺陷)b4e408068ab82a6ecab73a1fbc25f9b5.jpeg6d2288432ad5b7031581962a1d9afbdd.jpeg我们可以打开Zeal/Dash手册(《02-前端开发准备》有介绍),查看关于 BFC 的说明文档image-20220831193146084.png打开Block formatting context模块后,可以看到有很多开启 BFC 的方式image-20220831193241037.png我这里大概翻译了一下,并整理了一份表格,应该看起来更直观一点(有些概念因为还没有学习,翻译和理解有误的地方还望谅解)元素或属性说明<html>文档根元素float: left float: right浮动元素(float不为none)position: absolut position: fixed绝对定位元素display: inline-block行内块元素display: table-cell表格单元,默认值display: table-caption表格标题,默认值display: table display: table-row display: table-row-group display: table-header-group display: table-footer-group display: inline-table匿名的表格单元,分别是 HTML 表格、表行、表体、表头和表脚的默认值overflow: hidden overflow: scroll overflow: autooverflow不为visible和clip的块元素display: flow-root contain: layout contain: content contain: paint display: flex display: inline-flex的直接子元素Flex 项,如果它们本身既不是flex,也不是grid或table容器display: grid display: inline-grid的直接子元素Grid 项,如果它们本身既不是flex,也不是grid或table容器column-count不为auto column-width不为autoMulticol 容器,包含column-count: 1column-span: all应该总是创建一个新的格式化上下文,即使column-span: all元素不在 multicol 容器中但是,注意不管哪种方式,多多少少都会有些隐患、缺陷或者说“副作用”54a3b91bf3738e16dc8d60c506924d20.png3、clear我们这里设计三个兄弟元素,对前两个元素进行float的浮动属性设置,看下效果6285eef0e0e5defcaef2925ec2b85696.gif由于 box1 的浮动,导致 box3 位置上移也就是 box3 受到了 box1 浮动的影响,位置发生了改变(注意,这里文字并没有被覆盖,《09-浮动》一节说过浮动的特点,其中第 7 点就是“文字环绕”的问题)29f0b07925ee64e09181cca348316db7.jpeg如果我们不希望某个元素因为其他元素浮动的影响而改变位置,可以通过clear属性来清除浮动元素对当前元素所产生的影响clear作用:清除浮动元素对当前元素所产生的影响(本质是为元素添加一个margin-top属性,值由浏览器自动计算)可选值:left 清除左侧浮动元素对当前元素的影响right 清除右侧浮动元素对当前元素的影响both 清除两侧中影响较大一侧元素的影响(注意,这里不是同时清除两侧的影响)7b3a3be80af3b924c768f14d1feef532.gif4、after我们学习了上面知识后,了解了高度塌陷问题的解决方式,其中主要有通过overflow: hidden等可以为元素开启 BFC2a64a7f4d7361b89342ad40eabb82710.gif通过clear: both等可以清除浮动对元素产生的影响296eed7a83250499d08f2fbaa1b3291f.gif同时也了解到,这两种方式都有一定的弊端和隐患。那有没有一种更好的方式去解决高度塌陷的问题呢?答案当然是:有!9d32694d81d9cc615113bfd6de3b2a45.png我们直接上效果图ac9d8de28e765aa3612db88e0e3665ab.gifQ1:这里使用了一个伪元素选择器::after,那有人会问了,跟在 box2 下直接定义一个 box3 有什么区别呢?A:我们知道,网页的结构思想是:结构+表现+行为。在 box2 下直接定义一个 box3,属于结构;而使用伪元素选择器,属于表现而高度塌陷问题属于表现问题,定义 box3 的目的是为了撑起 box1 的内容,属于表现,而不是结构,所以在 css 中定义::after更符合网页的编程思想Q2:为什么需要使用display: block呢?A:因为默认情况下,::after伪元素是一个行内元素,如果不转为块元素,将仍然撑不起 box1 的高度38df19f0100616bfd366757a2df68018.png5、clearfix我们在前面《06-盒模型》一节中说过垂直布局中边距重叠的问题:相邻的垂直方向外边距会发生重叠现象ba545f805064146c3744634c3a18d6cc.gif如上图所示,子元素设置了一个margin-top之后,父元素跟随子元素一起进行了移动即我们之前说的父子元素间相邻外边距,子元素会传递给父元素(上外边距)聪明的小伙伴已经想到了,用刚才说的伪元素选择器啊ce282bce62e362e18faf2d65c90b08b3.gif好,我们先来看下效果d03986dc25481fafe66233a5c5470ef6.gif貌似是没有任何变化,到底是什么地方不对呢?09fd881a8713a5541ab547fa10bcea10.jpeg我们再来回顾下使用after伪元素的心路历程:使用无内容的 box3 撑起 box1 ==》表现代替结构(::after代替 box3)clear清除浮动对元素产生的影响(还记得clear的原理么?)9bd752f3fdd37c33a292b5fccd1f2b8f.jpeg其实就是给元素设置了一个margin-top属性,不过这个在开发者工具中是看不到的既然如此,就相当于在 box2 下面添加一个 box3,然后给 box3 设置一个margin-top属性到此为止,∵ 相邻的垂直方向外边距 这个条件仍然满足∴ 会发生重叠现象这个结论也依然成立具体点就是,父子元素间相邻外边距,子元素会传递给父元素(上外边距),表现为 box1 和 box2 同步往下移动那我们应该怎么做才能解决这个问题? 凭你们朴素的情感,应该怎么判? 当然就是让上述条件不满足呗!怎么能够不满足?当然是让两个元素垂直外边距不相邻啊!好,多说无益,我们直接上代码看效果!87eab6edc155de046c6841b0586ba336.gif我们用了before伪元素选择器,目的当然是让 box1 和 box2 的外边距不相邻,但是好像并没有效果我们再换成display: inline-block属性看看b5a137926b68526aff7ad926c032fe98.gif好像是解决了父元素布局的问题,但是子元素怎么还往下跑了一段距离? 是谁给的勇气?因为inline-block兼顾行内元素和块元素的特点,既可以设置宽高也不独占一行在没有设置宽高时,会存在一个默认高度,所以inline-block仍然行不通还有一个属性,display: table7999d34a5a577908d59299abb852fe35.gifBingo!实现了我们最终想要的效果Q1:为什么没有使用 clear 属性?A:不是说了吗?clear是为了清除浮动对布局的影响,我们现在没有浮动的元素啊,我们要讨论的也不是浮动的问题Q2:display 不是还有一个none属性么,为什么不用呢?A:none属性是不占据位置,但是也不能让元素相邻的外边距分离啊Q3:为什么table值就可以呢?A:这个问题问的非常好,算是问到点上了!我们上面在讲开启 BFC 的一些方法的时候,也提到了该属性。而且,应该牢记的是,元素开启 BFC 后的其中一个特点就是 父子元素外边距不会重叠。当然,这里也需要合理选择伪元素选择器,使其外边距不相邻才行另外,总结一下:高度塌陷问题,一般用::after外边距重叠问题,一般用::before不知道到这里,大家能不能想明白这两件事情ca42dae38fd9f042dc0fb3640a1489a9.jpeg那么问题来了,有没有一个两全其美的办法,既可以解决高度塌陷,又可以解决外边距重叠呢?0819d9f0a70423ba3116ab56c728cda3.jpeg当然有!clearfix 这个样式就可以同时解决高度塌陷和外边距重叠的问题当你在遇到这些问题时,直接使用clearfix这个类即可,他就可以帮你轻松搞定 css 中的两大难题.clearfix::before, .clearfix::after { content: ""; display: table; clear: both; }其中.clearfix::before是为了解决外边距重叠问题.clearfix::before { content: ""; display: table; }.clearfix::after是为了解决高度塌陷问题.clearfix::after { content: ""; display: table; clear: both; }两者合在一起,就可以完美地解决高度塌陷和外边距重叠这两大“世纪难题”了f93beec8c02faaf177b44418a7edcb24.png十、定位的简介也可以参考我的另一篇博客:https://www.cnblogs.com/zhaostudy/p/16558751.html需求分析d78c3f1d5c15a2a0e123bbfeac30d9f0.png按照我们之前所学知识,可以怎么实现呢?应该来说不难,很容易实现.box2 { width: 200px; height: 200px; background-color: yellow; /* 左外边距、上外边距 */ margin-left: 200px; margin-top: -200px; .box3 { width: 200px; height: 200px; background-color: orange; /* 上外边距 */ margin-top: 200px; }我们分别给 box2 和 box3 添加了外边距之后,就可以达到需求效果dcb6d48983c9a71520ed18ca7016c458.png当然也可以使用浮动来解决上述问题,但稍微麻烦一点不管怎样,问题也是显而易见。我们实际开发中,页面上的元素可能很多,这样改必然是 牵一发而动全身那么仅仅靠我们之前学习的布局知识,不足以轻松应对这种场景那么就势必需要一个方便我们处理这种场景的办法,它就是定位1775e6b8b98f4993aed3504c42ae9254.jpeg呸!不是!糟老头子定位(position)定位是一种更加高级的布局手段通过定位可以将元素摆放到页面的任意位置使用position属性来设置定位可选值含义static不开启定位,元素是静止的,默认值relative开启元素的相对定位absolute开启元素的绝对定位fixed开启元素的固定定位sticky开启元素的粘滞定位fa89d1acd4846e0baf2ed98a9178f9aa.gif1、相对定位8ef4c181239e6af939dd54a52c05a665.jpeg当元素的 position 属性值设置为relative时,则开启了元素的相对定位偏移量(offset)当元素开启相对定位以后,可以通过偏移量来设置元素的位置offset 属性含义top定位元素和定位位置的上边距离bottom定位元素和定位位置的下边距离left定位元素和定位位置的左侧距离right定位元素和定位位置的右侧距离定位元素垂直方向的位置由top和bottom两个属性控制,通常情况下只会使用其中之一top值越大,定位元素越靠下bottom值越大,定位元素靠上定位元素水平方向的位置由left和right两个属性控制,通常情况下只会使用其中之一left越大,定位元素越靠右right越大,定位元素越靠左ok,介绍完相对布局,我们的需求是不是变得 so easy!9dd1b9b3c2b6e98514e13856d71d264d.gif.box2 { width: 200px; height: 200px; background-color: yellow; /* 开启相对定位 */ position: relative; top: -200px; left: 200px; }我们给 box2 设置相对定位,就得到了我们想要的页面效果029ae4f654a1fa3f55f08982721b354c.png可以看出,使用了相对定位后,只会移动自身的布局位置,而不会对已存在的其他元素产生任何影响现在我们所举的例子不是很明显,但当页面布局比较复杂,特别是页面元素很多的时候,其优越性就可以大大体现出来e75f3c0967512a83df9afb14271c881f.jpeg相对定位的特点当元素开启相对定位以后,如果不设置偏移量元素,则元素不会发生任何变化(这里注意,不仅仅是位置)相对定位是参照于元素在文档流中的位置进行定位的(可以理解为相对于自身原始位置)相对定位会提升元素的层级(表现为可以覆盖其他元素)相对定位不会改变元素的性质:块还是块,行内还是行内99b0e5fa36a4e7f7857a5444399913ec.gifQ1:如果给上述三个 div 都设置相对定位,那么它们的层级关系会是什么样的呢?或者说谁会被谁覆盖呢?A:百闻不如一见,光说不练假把式,我们直接进行测试验证bd6512fde66cbcb7975172b58ee67319.png可以看到覆盖关系是:box3 >> box2 >> box1我们再稍微调整下 box3 和 box2 的前后位置3ee287a695f2d43914314c29df4d5628.png会发现覆盖关系变成了:box2 >> box3 >> box1可以大概猜测:在页面文档流中,越靠下的元素开启相对定位后,其层级越高 (这里也只是我个人的揣测,待后续学习中验证)(在后续学习中已得到验证:没有设置层级或层级z-index设置相同值时,优先显示靠下的元素)Q2:相对定位的第三个特点相对定位会提升元素的层级,是不是就类似于浮动一样脱离了文档流?A:我们可以对比下,浮动和相对定位的区别参考系不同:浮动的参考系是其父元素;相对定位是相对于自身可移动方向不同:浮动只能左右移动;相对定位是上下左右移动影响不同:浮动会影响页面布局(包括下方元素位置影响和高度塌陷问题);相对定位不对影响页面布局性质不同:浮动会改变元素的性质(不再独占一行,其宽高都会被内容撑开);相对定位不会改变元素的性质文字环绕:浮动不会覆盖文字;相对定位可以覆盖文字(这个可以自行验证,不再赘述)当然,浮动和相对定位也有其相似之处浮动和相对定位都是移动位置(貌似是废话)浮动和相对定位不会从父元素中移出可以看出,浮动和相对定位的区别是更多的最后回答一点:浮动脱离了文档流,不再占据页面位置;相对定位仍然占据页面位置(所以怎么能够叫 脱离文档流 呢?)0f32c305a1ce234bb88b60fac5362cb1.jpegQ3:相对定位的第四个特点相对定位不会改变元素的性质:块还是块,行内还是行内,但是上述例子中元素开启相对定位后好像就不再独占一行了,这个怎么理解?A:相比于浮动元素的特点,相对定位不会改变元素的性质其实是一个相对不容易理解的问题。但其实也不难,可以把相对定位认为是元素的灵魂出窍。其位置发生改变以后,布局并没有产生影响,因为它的肉体(结构)仍然占据着原来的那个位置。只是其灵魂(内容)发生了移动。d798c4acf32982334156e7fb6af87f12.gifQ4:相对定位的第四个特点中块还是块,行内还是行内,意味着行内元素也可以使用相对定位是吗?A:眼见为实,耳听为虚,直接看示例效果b2610f9be5b259f4d706570fdc56b392.gif善于思考是好事,但也别忘了自动动手,丰衣足食。自己实操一遍,胜过千言万语916261a9a4ea4c09bd3ce9c7cd0b4796.jpeg2、绝对定位91c24bcde99448a1b749a18190638932.jpeg元素的position属性值设置为absolute时,则开启了元素的绝对定位绝对定位的特点开启绝对定位后,如果不设置偏移量,元素的位置不会发生变化开启绝对定位后,元素会从文档流中脱离绝对定位会改变元素的性质:行内变成块,块的宽高被内容撑开(与相对定位相反)绝对定位会使元素提升一个层级绝对定位元素是相对于其包含块进行定位的(与相对定位不同)e54a3435650519327c62a037e53c567f.gif包含块(containing block)正常情况下:包含块就是离当前元素最近的开启了定位的祖先块元素如果所有的祖先元素都没有开启定位,则html(根元素、初始包含块)就是它的包含块<body> <!-- 如果box1开启定位,则box2的包含块是box1,否则就是body --> <div class="box1"> <div class="box2"></div> </div> <!-- 如果box3开启定位,则em的包含块是box3,否则就是body --> <div class="box3"> <span> <em>hello</em> </span> </div> </body>示例<div class="box2"> <div class="box3"> <div class="box4">4</div> </div> </div>e3c3dbcf463ca8d093ee15cb3dd264ad.gif不给 box2、box3 开起定位,box4 的包含块是html只给 box3 开启定位之后,box4 的包含块是 box3只给 box2 开启定位之后,box4 的包含块是 box2给 box2、box3 都开起定位之后,box4 的包含块是 box3注意:这里上述的条件是开启定位,也就是说只要position不是static(默认值),那么就满足了其成为包含块的必要条件上述示例中,我们给其祖先元素都设置了相对定位。其实改成其他几种定位方式也是可行的,我们可以看下面示例831335182b1a9305ffd8bc15bcf10c59.gif这里就不一一举例了,大家可以对另外几种定位方式进行验证00c84ef1c2a4330a548b488fa0dd3b12.jpeg水平方向的布局我们之前说过,水平方向的布局等式:margin-left + border-left + padding-left + width + padding-right + border-right + margin-right = 其父元素的宽度当使用绝对定位时,需要添加left和right两个值(此时规则和之前一样,只是多添加了两个值)left + margin-left + border-left + padding-left + width + padding-right + border-right + margin-right + right = 其父元素的宽度当发生过度约束时如果 9 个值中没有auto,则自动调整right值以使等式满足(之前 7 个值是margin-right)如果 9 个值中有auto,则自动调整auto的值以使等式满足可设置auto的值:margin-left/margin-right /width /left /right因为left和right的值默认是auto,所以如果没有设置left和right,当等式不满足时,则会自动调整这两个值786d762cd4c84db245181a2cb01965fc.jpeg水平居中<style> .box1 { width: 500px; height: 500px; background-color: #bfa; position: relative; .box2 { width: 100px; height: 100px; background-color: orange; /* 左右外边距设置为auto */ margin-left: auto; margin-right: auto; /* 绝对定位 */ position: absolute; left: 0; right: 0; </style> <div class="box1"> <div class="box2"></div> </div>4c1bf1115b748631f7c6b7960fb00b5f.png垂直方向的布局垂直方向布局的等式的也必须要满足top + margin-top + border-top + padding-top + height + padding-bottom + border-bottom + margin-bottom + top = 其父元素的高度垂直居中.box2 { width: 100px; height: 100px; background-color: orange; /* 左右外边距设置为auto */ margin-top: auto; margin-bottom: auto; /* 绝对定位 */ position: absolute; top: 0; bottom: 0; }7e5028e79e09046fa68be0408d7d88be.png水平垂直居中目前,我们可以根据绝对定位进行元素的水平垂直双方向居中,所以这个方法只是其中之一.box2 { width: 100px; height: 100px; background-color: orange; /* 左右外边距设置为auto */ margin: auto; /* 绝对定位 */ position: absolute; top: 0; bottom: 0; left: 0; right: 0; }5e587fecf532b486ab352520b0a2ae25.png小结水平布局等式:left + margin-left + border-left + padding-left + width + padding-right + border-right + margin-right + right = 其父元素的宽度垂直布局等式:top + margin-top + border-top + padding-top + height + padding-bottom + border-bottom + margin-bottom + top = 其父元素的高度上述等式的过度约束规则与《06-盒模型》中介绍的规则基本一致只是在没有auto时,会自动调整top/bottom/left/rightb5f1d0f16afe417fbf04e27ec6d4ca06.jpeg3、固定定位f37cccc949380a6ff4066aee85bf9679.jpeg将元素的position属性设置为fixed,则开启了元素的固定定位固定定位的特点固定定位也是一种绝对定位,所以固定定位的大部分特点都和绝对定位一样唯一不同的是,固定定位永远参照于浏览器的视口(viewport,可视窗口)进行定位,不会随网页的滚动条滚动示例ecbd7ad7613d232fb6b9f34bb69ca0ff.gif我们再给body设置一个较大的高度,让浏览器滚动起来,看下效果d1b68b515f62e149395e330005ebbf9b.gif会发现,box4 并没有因为滚动而发生未知的变化,这也验证了上述知识,同时也应该明白了视口的概念我们再对比下绝对定位e2cc30763da60bbe06ada221a86c6352.gif相信到这里,大家应该又进一步地理解了固定定位与绝对定位的区别因为固定定位跟绝对定位除了具有上述差别之后,其他的特点跟绝对定位是一样的,所以这里便不再赘述了4、粘滞定位27d32b91c59dacefcf914dc09615fd99.jpeg将元素的position属性设置为sticky,则开启了元素的固定定位这次,我们换个方式,直接来看粘滞定位的效果7ffe6ccad6fb9f528773738c89d00152.gif大家可以看到,右侧边栏部分在一定的情况下是固定的,滚动到上方一定位置开始发生变动我们先带着这个疑问,打开Zeal官方手册,找到position中sticky的相关描述62156a3caf93ee8380075a34be96ed48.pngThe element is positioned according to the normal flow of the document, and then offset relative to its nearest scrolling ancestor and containing block (nearest block-level ancestor), including table-related elements, based on the values of top, right, bottom, and left. The offset does not affect the position of any other elements.This value always creates a new stacking context. Note that a sticky element "sticks" to its nearest ancestor that has a "scrolling mechanism" (created when overflow is hidden, scroll, auto, or overlay), even if that ancestor isn't the nearest actually scrolling ancestor. This effectively inhibits any "sticky" behavior (see the GitHub issue on W3C CSSWG).不要慌,这里大概翻译一下(我这里稍微进行了下省略精简和整理总结)该元素是根据文档流进行定位的,即相对于包含块进行偏移偏移量不会影响任何其他元素的位置粘性元素总是“粘”到其最近的具有“滚动机制”的祖先元素(当overflow为hidden、scroll、auto、overlay时创建),即使该祖先不是最近的实际滚动祖先这里可能最后一点比较难理解,别着急,我们接着往下看示例我们拿之前的w3cschool顶部导航栏进行下魔改/* 设置一个高度 */ body { height: 3000px; .menu { width: 1211px; height: 48px; background-color: #e8e7e3; margin: 100px auto; /* 开启粘滞定位 */ position: sticky; top: 10px; }0a13bb57fb6205400f34f428d6473d01.gif因为在视频中老师并没有对sticky属性做过多的介绍,只是要求我们了解一下,因为在实际开发中,也是结合 js 去实现的,所以我这里同样也就不再深入带大家一起看了粘滞定位的特点粘滞定位和相对定位的特点基本一致(视频中说是和相对定位一致,不过我对比了一下,很多特点是不同的,感觉倒是和固定定位更相似,这里存疑)不同的是粘滞定位可以在元素到达某个位置时将其固定需要注意的是,sticky属性并不兼容 IE(PS:不过微软官方已经宣布将在 2022 年停止对 IE 的维护,IE 将成为历史。虽然我们经常诟病 IE,但作为当年浏览器的一霸,在废弃多年后,不知道还会不会有所怀念,毕竟它代表着我们不断逝去的青春)4421c13d1cdabf20ab3689943c22c777.png5、几种定位的对比我们通过上面的学习,知道position属性有五个可选值但static是默认值,即不开启定位,所以我们只需要对比 4 种定位方式即可定位方式是否不设置偏移量,元素不会发生改变是否脱离文档流是否改变元素性质是否提升元素层级参考系relative(相对定位)√××√参照于元素在文档流中的位置absolute(绝对定位)×√√√参照于其包含块fixed(固定定位)×√√√参照于浏览器的视口sticky(粘滞定位)×√√√参照于浏览器的视口6、补充:元素层级对于开启了定位元素,可以通过z-index属性来指定元素的层级z-index需要一个整数作为参数,值越大元素的层级越高,元素的层级越高越优先显示如果元素的层级一样,则优先显示靠下的元素祖先的元素的层级再高,也不会盖住后代元素9753c51a95247bf081efeef25a08a0e1.jpeg示例<style> div { font-size: 40px; .box1 { width: 200px; height: 200px; background-color: #bfa; position: absolute; top: 0; left: 0; .box2 { width: 200px; height: 200px; background-color: orange; position: absolute; top: 50px; left: 50px; .box3 { width: 200px; height: 200px; background-color: salmon; position: absolute; top: 100px; left: 100px; .box4 { width: 100px; height: 100px; background-color: skyblue; position: absolute; bottom: 0; left: 0; </style> <div class="box1">1</div> <div class="box2">2</div> <div class="box3"> <div class="box4">4</div> </div>f6641b46bc4bc5b5befc129951719c45.png存疑问题Q:浮动也有层级概念吗?如果有,浮动和定位的层级关系是什么样的?A:null / none / undefined 调了一下,出现几种现象给float设置z-index多大都没用,还是会被覆盖默认情况,没有设置z-index或设置z-index大小 ≥0 时,浮动层级没有定位的层级高设置z-index<0 时,浮动层级可以定位的层级高浮动层级(不知道有没有这个概念,本身就是存疑问题,现在这种情况看起来应该是没有这个概念了)0b59d693dde7721d542dfa1bfc55ffa9.png7、总结一般情况下,页面的整体结构大多采用浮动、块进行布局页面某些模块结构一般采用定位进行微调b97ee0b61f5d6d5781972b801d51eba9.jpeg8、练习:京东轮播图css代码:/* 整体居中 */ .box { width: 590px; height: 470px; /* 水平垂直双方向居中 */ margin: auto; position: absolute; top: 0; bottom: 0; left: 0; right: 0; /* ======轮播图Start====== */ .img_list li { /* 每个轮播图绝对定位,让其重叠 */ position: absolute; .img_list li:nth-child(1) { /* 目前还没有学习js,暂时做成静态切换层级 */ z-index: 1; /* 全局图像大小 */ .img_list img { /* 我这里之所以要设置宽高,是因为下载的图片大小不全是一样大的 */ /* 但是一般情况下,这些图片都会裁剪成统一大小,所以可以不用设置 */ width: 590px; height: 470px; /* ======轮播图End====== */ /* ======轮播圆Start====== */ .circle_list { height: 20px; /* 开启绝对定位 */ position: absolute; bottom: 20px; left: 30px; z-index: 2; /* 参考京东原网页,整体字体设置样式,这种设置方式还不太懂 */ /* 其实也可以不设置,不过每个轮播圆之间的间距跟原来就不太一样了 */ font-size: 0; text-align: center; /* 轮播圆细节 */ .circle_list .circle { /* 这里设置display: inline-block; 也是一样的 */ float: left; height: 8px; width: 8px; background-color: rgba(255, 255, 255, 0.4); margin-right: 4px; /* 画圆,这个按照课程中的画法,按照网页源代码调出来的有点问题 */ background-clip: content-box; border: 3px transparent solid; border-radius: 50%; /* 轮播圆悬浮效果 */ .circle_list .circle:hover, .circle_list .circle:nth-child(1) { background-color: #fff; border: 3px rgba(0, 0, 0, 0.1) solid; /* ======轮播圆End====== */html代码:<div class="box"> <ul class="img_list"> <li> <a href="#"><img src="assets/lbt/1.jpg" alt="" /></a> </li> <li> <a href="#"><img src="assets/lbt/2.jpg" alt="" /></a> </li> <li> <a href="#"><img src="assets/lbt/3.jpg" alt="" /></a> </li> <li> <a href="#"><img src="assets/lbt/4.jpg" alt="" /></a> </li> <li> <a href="#"><img src="assets/lbt/5.jpg" alt="" /></a> </li> <li> <a href="#"><img src="assets/lbt/6.jpg" alt="" /></a> </li> <li> <a href="#"><img src="assets/lbt/7.jpg" alt="" /></a> </li> <li> <a href="#"><img src="assets/lbt/8.jpg" alt="" /></a> </li> </ul> <!-- 我这里结构并没有完全按照课程中的结构来,但实现效果是一样的 --> <ul class="circle_list"> <li class="circle"></li> <li class="circle"></li> <li class="circle"></li> <li class="circle"></li> <li class="circle"></li> <li class="circle"></li> <li class="circle"></li> <li class="circle"></li> </ul> </div>别忘了,引入reset样式效果b60d920ab004d9a099d06cc60d10a8c0.gif等到后面学习了 js,就可以实现自动轮播了,到时候再补充完善主要运用水平垂直双方向居中(水平垂直方向等式)absolute开启绝对定位,使其重叠,达到隐藏效果z-index设置层级,实现图片轮播border-radius画圆,transparent边框透明,background-clip:content-box隐藏边框7470bc9dc8195c5f6bb3c43f9f894251.gif十一、字体1、字体相关的样式我们前面讲过字体的两个属性color用来设置字体颜色font-size字体的大小em 相当于当前元素的一个font-sizerem 相对于根元素的一个font-size当然,字体的属性并不止这些2、font-familyfont-family 字体族(字体的格式)serif 衬线字体image-20220831230948779.pngsans-serif 非衬线字体image-20220831231027811.pngmonospace 等宽字体image-20220831231111009.pngcursive 手写体image-20220831231146098.pngfantasy 梦幻字体image-20220831231236965.png上述字体均不表示具体的某种字体,而是字体的分类我们经常使用的一些字体,如微软雅黑、黑体、楷体、宋体、Consolas等,才是具体的某种字体也就是说,font-family 指定字体的类别,浏览器会自动使用该类别下的字体font-family可以同时指定多个字体,多个字体间使用,隔开字体生效时优先使用第一个,第一个无法使用则使用第二个,以此类推font-family: "Courier New", Courier, monospace;image-20220831231528419.png3、几种字体手写体Indie Flower5e61429db239e98963586c869c27aead.pngInk Free01ef23c34bee2c9e4cbe1f043dd3b6da.pngNanum Pen1f564db206d5a9e14aa60b3ee24091d0.pngMV Boli4bb92b873db63ecca98511778c0b17d7.pngSegoe Printb2ee8359722a57602592764f02e8d1cb.pngShadows Intoea4759bf9241a3c272f60185d83a64d7.png艺术体Barrio529e00359270f5ed6c76ab7aefab510d.pngJulius Sans One08bc039ec67f480b350f611b03e73b4d.pngLobsterfe49ec07bc69acc0821ad786a4bde82d.pngMonoton0e23afa75c7a2435a4ef811e666f9ce3.pngPoiret One134a20c01b52b7bf3e956e3797f2cd7b.png乱码字体MT Extra2fc9c72c2fe29ec6b128cf4f658804ad.pngSymbol4f1970f3d6c33a4d3aa954a454b0f4b9.pngWebdingsbaa93c39706e92685e08bc86e5eeae79.pngWingdingsbe8c184600f286f23c67bca3ca173cd4.png中文字体方正粗黑宋简体97d9fc2e7c6ef6e292ef60c1bce24992.png微软雅黑9c2cdbaf3d24848c92930a656497fa2c.png黑体cb88ce51af854d0495ad383713fd42bf.png楷体82bca9d833d14e4d61338e2f3109fd7d.png宋体cf7281c1fe37b4a32d2702e85a1047d1.png仿宋b97ebc4ded090976518db193123a04f3.png4、@font-face我们除了可以使用系统自带的字体样式外,还可以在服务器端自定义字体位置@font-face可以将服务器中的字体直接提供给用户去使用@font-face { /* 指定字体名字 */ font-family: "myFont1"; /* 服务器中字体路径 */ src: url("/font/ZCOOLKuaiLe-Regular.woff"), url("/font/ZCOOLKuaiLe-Regular.otf"), url("/font/ZCOOLKuaiLe-Regular.ttf") format("truetype"); /* 指定字体格式,一般不写 */ font-size: 30px; color: salmon; font-family: myFont1; }ce2b3bdcf85cd81a873aa7b7ee15b2a9-20220831233502269.png问题加载速度:受网络速度影响,可能会出现字体闪烁一下变成最终的字体版权:有些字体是商用收费的,需要注意字体格式:字体格式也有很多种(woff、otf、ttf),未必兼容,可能需要指定多个5、图标字体(iconfont)图标字体简介在网页中经常需要使用一些图标,可以通过图片来引入图标但是图片大小本身比较大,并且非常的不灵活所以在使用图标时,我们还可以将图标直接设置为字体,然后通过@font-face的形式来对字体进行引入这样我们就可以通过使用字体的形式来使用图标fontawesome官方网站:https://fontawesome.com/下载解压完毕之后,直接将 css 和 webfonts 移动到项目中即可使用示例<link rel="stylesheet" href="/font/fontawesome/css/all.css" /> <style> color: green; .fa-venus-mars, .fa-mars-double { color: red; .fa-html5 { color: #e34d22; .fa-css3 { color: blue; .fa-js { color: #d1b514; </style> <!-- 大小 --> <i class="fab fa-weixin fa-lg"></i> <i class="fab fa-weixin fa-2x"></i> <i class="fab fa-weixin fa-3x"></i> <br /> <!-- 边框 --> <i class="fab fa-weixin fa-2x fa-border"></i> <br /> <!-- 旋转 --> <i class="fab fa-weixin fa-2x fa-rotate-90 "></i> <!-- 水平对称 --> <i class="fab fa-weixin fa-2x fa-flip-horizontal "></i> <!-- 垂直对称 --> <i class="fab fa-weixin fa-2x fa-flip-vertical "></i> <br /> <!-- 动画 --> <i class="fa fa-venus-mars fa-3x fa-spin"></i> <i class="fa fa-mars-double fa-3x fa-pulse"></i> <br /> <!-- 列表 --> <ul class="fa-ul"> <li><i class="fa-li fa fa-check-square"></i>can be used</li> <li><i class="fa-li fa fa-spinner fa-spin"></i>as bullets</li> <li><i class="fa-li fa fa-square"></i>in lists</li> </ul> <br /><br /><br /> <!-- 组合 --> <span class="fa-stack fa-lg"> <i class="fab fa-html5 fa-stack-1x fa-10x"></i> <i class="fab fa-css3 fa-stack-1x fa-4x"></i> <i class="fab fa-js fa-stack-1x fa-2x"></i> </span>效果5d28ccfc2ddde16cfc7a5d71b4ab3e52.gif其中fas/fab是免费的,其他是收费的图标字体其他使用方式通过伪元素设置找到要设置图标的元素通过::before或::after选中在content中设置字体的编码设置字体的样式fab:font-family: 'Font Awesome 5 Brands';fas:font-family: 'Font Awesome 5 Free'; font-weight:900;示例<style> .poem { width: 200px; height: 300px; margin: auto; list-style: none; margin-left: -40px; li::before { content: "\f130"; /* font-family: 'Font Awesome 5 Brands'; */ font-family: "Font Awesome 5 Free"; font-weight: 900; margin-right: 10px; color: gray; </style> <div class="poem"> <h1>武陵春·春晚</h1> <p>[宋] 李清照</p> <ul> <li>风住尘香花已尽,</li> <li>日晚倦梳头。</li> <li>物是人非事事休,</li> <li>欲语泪先流。</li> <li>闻说双溪春尚好,</li> <li>也拟泛轻舟。</li> <li>只恐双溪舴艋舟,</li> <li>载不动、许多愁。</li> </ul> </div>效果af91c5ce8fe044b87932183782763606.png通过实体设置通过实体来使用图标字体:&#x图标编码;示例<i class="fas"></i>效果0d46a541e18cad08195ec08765d01614.pngiconfont官方网站:https://www.iconfont.cn/iconfont 是阿里的一个图标字体库,海量图标库,图标字体非常丰富但是版权有点模横两可,如果需要商用,最好联系作者不过一般情况下,公司企业都会有自己的 UI 设计团队,会自己去进行设计这里使用方式大同小异,不过iconfont 需要添加购物车后再添加至项目然后下载,下载包中有 demo.html,详细介绍了使用方式iconfont 也提供了一种在线方式,直接在我的项目中选择在线链接可以复制出一份@font-face的 css 代码e70c3278505584f28c81edfcba5a7056.png后续步骤与前面介绍的一致示例<!-- <link rel="stylesheet" href="/font/iconfont/iconfont.css"> --> <style> i.iconfont { font-size: 100px; p::before { content: "\e811"; font-family: "iconfont"; font-size: 50px; /* 3、通过在线连接:这里link和@font-face择其一即可 */ @font-face { font-family: "iconfont"; /* Project id 2580407 */ src: url("//at.alicdn.com/t/font_2580407_c0kpuhebb7r.woff2?t=1622373966454") format("woff2"), url("//at.alicdn.com/t/font_2580407_c0kpuhebb7r.woff?t=1622373966454") format("woff"), url("//at.alicdn.com/t/font_2580407_c0kpuhebb7r.ttf?t=1622373966454") format("truetype"); </style> <!-- 1、通过字符实体设置 --> <i class="iconfont"></i> <i class="iconfont"></i> <i class="iconfont"></i> <i class="iconfont"></i> <!-- 2、通过伪元素设置 --> <p> Lorem ipsum, dolor sit amet consectetur adipisicing elit. Totam deserunt tempore fugit quos eaque, ipsa rerum suscipit iure cumque aspernatur esse cupiditate nihil quas nulla odit? Sequi accusantium labore maiores. </p>效果a70ece11e3e329e0e8ef4c00757d59c9.png6、行高行高line height文字占有的实际高度,可以通过line-height来设置行高可以直接指定一个大小 px/em也可以直接为行高设置一个小数(字体大小的倍数)行高经常还用来设置文字的行间距:行间距 = 行高 - 字体大小字体框字体框就是字体存在的格子,设置font-size实际上就是在设置字体框的高度行高会在字体框的上下平均分配5d51f7775bdc7bef294a884ee41f2a56.png示例border: 1px black solid; font-size: 100px; /* line-height: 100px; */不指定行高时,content高度131.556px:说明line-height默认值大约是1.31 ~ 1.32(倍数)257b9723fa78a73c3a265e7b1426e034.png指定行高时,content高度99.556px:少了0.444px,并且字母p下面溢出1d940e3ba1bb08a10b2f9ca90177b23e.png存疑问题经测试,line-height大约比100.444px略大一点时,content高度才会大于100px,暂未知原因字体的简写属性font 可以设置字体相关的所有属性:font: font-style font-variant font-weight font-size/line-height font-family其中某些值可以不写,会用默认值默认值属性默认值其他常用值font-stylenormalitalicfont-variantnormalsmall-capsfont-weightnormalboldfont-sizemediumsmall、largeline-heightnormal font-family取决于浏览器 示例 1/* font-size: 50px; font-family: 'Courier New', Courier, monospace; */ font: 50px "Courier New", Courier, monospace;9445661b35e1f4b60f42d2e574986b7c.png示例 2/* small-caps值设置小型大写字母字体,所有小写变大写,同时字体尺寸更小(了解即可) */ font: bold small-caps italic 50px "Courier New", Courier, monospace;306d2133b86ba53701b4d24fd9675094.png注意 Pay Attention:简写属性省略的值会使用默认值,所以会覆盖前面的非简写属性(不仅仅对于字体而言)7、文本对齐方式水平对齐text-align 文本的水平对齐text-align属性值对齐方式说明left左侧对齐right右侧对齐center居中对齐justify两端对齐left 左侧对齐cb27b2eaacb59d1348b203182527c1bb.pngright 右侧对齐145cd2242ce0fc188292586c152192f8.pngcenter 居中对齐0a311344989446b7b51b9d5ebc9aac80.pngjustify 两端对齐0c1230e4dee40476e5e9a75141358962.png垂直对齐vertical-align 设置元素垂直对齐的方式vertical-align 属性值对齐方式说明baseline基线对齐top顶部对齐bottom底部对齐middle居中对齐baseline 基线对齐c2b19aeed53f064e7fe82ca948793bf2.pngtop 顶部对齐4b3a93fc6638b26acc1105a7f02242a7.pngbottom 底部对齐f5c906386a4ea3c9ffdc80cde6b4291f.pngmiddle 居中对齐e5b1f3fc76f55a8600ab3d73e244c529.png这里的居中对齐高度 = 基线高度 + x 的高度 / 2这种居中对齐并非实际上的居中对齐,一般也不会用这种方式对文字进行垂直方向的对齐vertical-align 还可以设置 px 值设置垂直对齐方式vertical-align: 10px;77cbc76d13c88a4ba90c06fd813b3e71.png图片的垂直对齐问题<style> .imgDiv { border: 5px seagreen solid; .imgDiv img { width: 400px; height: 300px; </style> <div class="imgDiv"> <img src="/assets/news.png" alt="" /> </div>1466b529387379a45ce37a70561e2375.png明显默认情况下,图片底部有一定缝隙,我们稍作修改,给 img 元素添加vertical-align属性值/* 只要不是基线对齐,就能消除底部缝隙 */ vertical-align: top; vertical-align: bottom; vertical-align: middle;4c465cbd4b31f9959f3f4245dfd8734f.pngQ:为什么图片会有缝隙?A:图片属于替换元素,特点与文本一致,也有自己的基线,默认也是基线对齐。而基线位置不在最底部,所以会出现缝隙其他样式white-space 设置网页如何处理空白可选值:normal 正常nowrap 不换行pre 保留空白案例:实现网页省略符号white-space: nowrap; overflow: hidden; text-overflow: ellipsis;效果:image-20220906130109796.png十二、背景1、PS的基本设置工欲善其事,必先利其器在介绍背景之前,首先需要做好准备工作:安装 PS 与基本设置这里就不详细介绍 PS 的安装了,因为网上一抓一大把,主要介绍 PS 的基本设置左侧工具栏调成 2 列,更方便使用7d73fd6a596ea0e47e70748496a8bf40.png右侧工具栏不需要的视图统统关掉727d4922a4b6e4dd43795672ded474ea-20220908205014684.png修改单位为像素由于一般默认的单位是厘米,所以这里需要修改在历史记录、颜色或色板附近右键,打开选项卡,选择界面选项bce39fddd0e9beb31e0653283a6a9bd0.png打开单位与标尺,修改单位中的标尺与文字为像素c112282537409ae4e3d29fe0a9c20c33.png2、背景background-color 设置背景颜色background-image设置背景图片如果背景图片大小小于元素,则背景图片会自动在元素中平铺将元素铺满如果背景图片大小大于元素,则背景图片一部分会无法完全显示如果背景图片大小等于元素,则背景图片会直接正常显示background-repeat设置背景图片的重复方式repeat 默认值,背景图片沿着 x 轴和 y 轴双方向重复repeat-x 背景图片沿着 x 轴方向重复repeat-y 背景图片沿着 y 轴方向重复no-repeat 背景图片不重复background-position设置背景图片的位置通过top left right bottom center几个表示方位的词来设置背景图片的位置:使用方位词时必须要同时指定两个值,如果只写一个则第二个默认就是center通过偏移量来指定背景图片的位置:水平方向偏移量、垂直方向变量background-clip设置背景的范围border-box 默认值,背景会出现在边框的下边padding-box 背景不会出现在边框,只出现在内容区和内边距content-box 背景只会出现在内容区background-origin背景图片的偏移量计算的原点border-box 背景图片的变量从边框处开始计算padding-box 默认值,background-position从内边距处开始计算content-box 背景图片的偏移量从内容区处计算background-size设置背景图片的大小第一个值表示宽度,第二个值表示高度;如果只写一个,则第二个值默认是autocover 图片的比例不变,将元素铺满contain 图片比例不变,将图片在元素中完整显示background-attachment背景图片是否跟随元素移动scroll 默认值,背景图片会跟随元素移动fixed 背景会固定在页面中,不会随元素移动可以同时设置背景图片和背景颜色,这样背景颜色将会成为图片的背景色示例 1.box1 { height: 500px; width: 500px; overflow: auto; border: 20px red double; padding: 10px; /* 背景色 */ background-color: darksalmon; /* 背景图 */ background-image: url("/assets/背景.png"); /* 背景图重复方式 */ background-repeat: no-repeat; /* 背景图偏移位置 */ background-position: 0 0; /* 背景图偏移量计算的原点 */ background-origin: content-box; /* 背景范围 */ background-clip: content-box; /* 背景图片大小 */ background-size: contain; .box2 { width: 100px; height: 1000px; background-color: orange; background-image: url("assets/背景2.jpg"); background-repeat: no-repeat; background-position: 50px 50px; /* 背景图片是否跟随移动 */ background-attachment: fixed; }9f806f08854b47c21d02d5c23ed805a7.gifbackgound 背景相关的简写属性,所有背景相关的样式都可以通过该样式来设置并且该样式没有顺序要求,也没有哪个属性是必须写的注意background-size必须写在background-position的后边,并且使用/隔开background-position/background-sizebackground-origin background-clip 两个样式,orgin要在clip的前边示例二:.box1 { height: 500px; width: 500px; border: 10px red double; padding: 10px; background: #bfa url("assets/dlam.png") no-repeat 100px 100px/200px padding-box content-box; }e469a4b629a9d0a42bacbf69b4a88ca2.png练习一:线性渐变效果的背景图12dbf45389452852cdb04ac7b87e5946.png如果我们仔细挂那可能,会发现很多网站导航条的背景色并不是单一的某种颜色,而是有一个渐变的效果不过到目前为止,我们还没有学习线性渐变的内容,不过凭上面所学的知识同样可以实现切图首先,我们需要通过 PS 软件进行切图按住Alt同时滚动鼠标滑轮,可以对图片大小进行缩放;调整至合适大小,再选择矩形块工具,截取一个宽度为 1px 大小的图片9cf933089851d88ab149afbd2a09ffca.png然后选择图像-裁剪,就可以得到一个我们需要的一个背景图片99c888010d9f623899d7c21059dd1f60.pngea1f3a686a6f538d0bd7b221952ec4a7.png最后,选择文件-存储为Web所用格式db062cd60f46f94d2829f9601866ae99.png我这里选择的是 PNG 的格式,你可以对比几种格式,看看最终的图片大小折中选择,最好选择存储位置即可1e51da62eaaa95c55273289aba15decd.png得到我们需要的背景图片之后,就可以引入到css样式中了代码height: 60px; width: 1500px; background: url("assets/背景3.png") repeat-x;效果eb584be8969046de24bbf44e29a30a22.png练习二:按钮点击效果代码<style> a:link { /* 因为本身是行内元素,变成块元素更方便设置宽高 */ display: block; width: 93px; height: 29px; background: url("assets/背景/练习2-背景/link.png"); a:hover { background: url("assets/背景/练习2-背景/hover.png"); a:active { background: url("assets/背景/练习2-背景/active.png"); </style> <a href="javascript:;"></a>效果2f9fc038af13642f79b96a76a02b710f.gif十三、雪碧图与渐变1、雪碧图解决图片闪烁的问题:可以将多个小图片统一保存到一个大图片中,然后通过调整background-position来显示响应的图片这样图片会同时加载到网页中就可以有效的避免出现闪烁的问题这个技术在网页中应用十分广泛,被称为CSS-Sprite,这种图我们称为雪碧图雪碧图的使用步骤:先确定要使用的图标测量图标的大小根据测量结果创建一个元素将雪碧图设置为元素的背景图片设置一个偏移量以显示正确的图片雪碧图的特点:一次性将多个图片加载进页面,降低请求的次数,加快访问速度,提升用户的体验示例 1e8a34378504abff5538d7d362a1d9c3c.pnga:link { display: block; width: 93px; height: 29px; background: url("assets/背景/练习2-背景/btn.png"); /* 默认值,可以不设置 */ background-position: 0 0; a:hover { /* 设置水平方向的一个偏移量;注意是向左移动,所以是负值 */ background-position: -93px 0; a:active { /* 设置水平方向的一个偏移量;注意是向左移动,所以是负值 */ background-position: calc(-93px * 2) 0; }4bef45919d901b3dddf40a364bd89037.gif我们对比以下之前练习中的效果,第一次加载进来的时候会有明显的闪烁48cf54374d1d7827bc18d2e13c3d52b7.gif示例 242c16d9512e844b8d273e8feda0cad1a.png.box1 { width: 109px; height: 33px; background: url("assets/背景/练习3-雪碧图/amazon-sprite_.png"); /* 设置水平和垂直方向的一个偏移量;注意移动方向 */ background-position: -10px -10px; .box2 { width: 42px; height: 30px; background: url("assets/背景/练习3-雪碧图/amazon-sprite_.png"); /* 设置水平和垂直方向的一个偏移量;注意移动方向 */ background-position: -58px -338px; }fbd314d9c0c3da0c4a6deb1b7e3320f1.png2、线性渐变通过渐变可以设置一些复杂的背景颜色,可以实现从一个颜色向其他颜色过渡的效果!!渐变是图片,需要通过background-image来设置线性渐变,颜色沿着一条直线发生变化 linear-gradient()# 红色在开头,黄色在结尾,中间是过渡区域 background-image: linear-gradient(red, yellow);47123ca0139073c1a8a2f61395408167.png线性渐变的开头,我们可以指定一个渐变的方向to leftto rightto bottomto topdeg deg 表示度数turn 表示圈background-image: linear-gradient(to left, red, yellow); background-image: linear-gradient(to right, red, yellow); background-image: linear-gradient(to top, red, yellow); background-image: linear-gradient(to bottom, red, yellow);上面基本的 4 个方向的渐变很好理解,我们就不再做过多的一一解释了我们来看度数的渐变效果background-image: linear-gradient(45deg, red, yellow);64164ca16b1fc085883520eccd349ec1.png会发现它是从左下角往右上角去进行渐变的,为什么呢?我们小时候肯定都用过量角器6ad862f51623cae580cd5d395af6e930.png是不是恍然大悟,我们以原点作为起始点,有角度的那条边去做渐变,再把四象限的概念和矩形内部的四个角对应起来总结:线性渐变的边上的某一点为起点,以一定角度渐变的;渐变方向的颜色是线性变化的,而其垂线方向的颜色是一致的然后看下圈数的表示方法background-image: linear-gradient(0.4turn, red, yellow);因为圈数和角度之间可以相互转换,所以这里就不再进行赘述了另外,渐变可以同时指定多个颜色,多个颜色默认情况下平均分布,也可以手动指定渐变的分布情况repeating-linear-gradient() 可以平铺的线性渐变background-image: repeating-linear-gradient(red, yellow);1ec54cc28b7ec1ffa1dd355206a85eb1.png默认情况下,跟linear-gradient(red, yellow)效果一样,我们稍作改动background-image: repeating-linear-gradient(red 0px, yellow 50px);3e39b9b284335266b3f626529b6aaa79.png由于我们设置的div宽高为200px,所以会有 4 次重复的渐变效果所以默认情况下,下列几种写法是一致的,效果相同background-image: linear-gradient(red, yellow); background-image: repeating-linear-gradient(red, yellow); /* 因为我们设置的div盒子的宽高为200px,所以这里[height]=200px */ background-image: repeating-linear-gradient(red 0, yellow [height]);3、径向渐变radial-gradient() 径向渐变(放射性的效果)background-image: radial-gradient(red, yellow);默认情况下,径向渐变的形状根据元素的形状来计算的正方形 --> 圆形ef3c9f5ed8c21677749b93d2e1d93917.png长方形 --> 椭圆形222148cc7e1047bd50ec3dd165ff6fae.png默认情况下,circle和ellipse是自动适配盒子的,我们也可以手动指定径向渐变的形状形状circle 圆形ellipse椭圆background-image: radial-gradient(circle, red, yellow);f52c0364605c0ad9a20763de0a82db5f.png也可以指定渐变的位置位置toprightleftcenterbottombackground-image: radial-gradient(at left, red, yellow);e67d258061cdf5263312fbbda747964b.png当然,除了上述值,还可以指定像素大小closest-side 近边farthest-side 远边closest-corner 近角farthest-corner 远角background-image: radial-gradient(100px 60px, red, yellow);8bd62e310a802d483b1a4c499db507b1.png同时对其形状/大小和位置进行指定radial-gradient(形状/大小 at 位置, 颜色 位置, 颜色 位置, 颜色 位置) background-image: radial-gradient(circle at 50px 100px, red 50px, yellow 100px);2d952552e1a55ae04a6c358f99f191d1.png总结一下形状circle 圆形ellipse椭圆大小closest-side 近边farthest-side 远边closest-corner 近角farthest-corner 远角位置toprightleftcenterbottom类似于线性渐变,径向渐变也有对应的repeat属性background-image: repeating-radial-gradient( circle at 50px 100px, red 50px, yellow 100px );9c41690bc864e959e50ebc0fd5fdf36c.png总结:径向渐变的渐变方向以圆心为起点,往四周扩散的;同一半径上的颜色是渐变的,同一圆周上的颜色是一致的十四、表格1、表格在现实生活中,我们经常需要使用表格来表示一些格式化数据:课程表、人名单、成绩单...同样在网页中我们也需要使用表格,我们通过table标签来创建一个表格在table中使用tr表示表格中的一行,有几个tr就有几行在tr中使用td表示一个单元格,有几个 td就有几个单元格rowspan 纵向的合并单元格colspan 横向的合并单元格<table border="1" width="50%" align=" center"> <!--在table中使用tr表示表格中的一行,有几个tr就有几行--> <tr> <!--在tr中使用td表示一个单元格,有几个td就有几个单元格--> <td>A1</td> <td>B1</td> <td>C1</td> <td>D1</td> </tr> <tr> <td>A2</td> <td>B2</td> <td>C2</td> <!--rouspan 纵向的合并单元格--> <td rowspan="2">D2</td> </tr> <tr> <td>AB</td> <td>B3</td> <td>C3</td> </tr> <tr> <td>A4</td> <td>B4</td> <!-- colspan横向的合并单元格 --> <td colspan="2">C4</td> </tr> </table>2、长表格可以将一个表格分成三个部分:头部 thead主体 tbody底部 tfootth 表示头部的单元格<table border="1" width="50%" align="center"> <thead> <tr> <td>日期</td> <td>收入</td> <td>支出</td> <td>合计</td> </tr> </thead> <tbody> <tr> <td>2000.1.1</td> <td>500</td> <td>200</td> <td>300</td> </tr> <tr> <td>2000.1.1</td> <td>500</td> <td>200</td> <td>300</td> </tr> <tr> <td>2000.1.1</td> <td>500</td> <td>200</td> <td>300</td> </tr> </tbody> <tfoot> <tr> <td></td> <td></td> <td>合计</td> <td>1200</td> </tr> </tfoot> </table>3、表格的样式HTML 代码<table> <tr> <td>学号</td> <td>姓名</td> <td>性别</td> <td>年龄</td> <td>地址</td> </tr> <tr> <td>1</td> <td>孙悟空</td> <td>男</td> <td>18</td> <td>花果山</td> </tr> <tr> <td>2</td> <td>猪八戒</td> <td>男</td> <td>28</td> <td>高老庄</td> </tr> <tr> <td>3</td> <td>沙和尚</td> <td>男</td> <td>38</td> <td>流沙河</td> </tr> <tr> <td>4</td> <td>唐僧</td> <td>男</td> <td>16</td> <td>女儿国</td> </tr> <tr> <td>1</td> <td>孙悟空</td> <td>男</td> <td>18</td> <td>花果山</td> </tr> <tr> <td>2</td> <td>猪八戒</td> <td>男</td> <td>28</td> <td>高老庄</td> </tr> <tr> <td>3</td> <td>沙和尚</td> <td>男</td> <td>38</td> <td>流沙河</td> </tr> <tr> <td>4</td> <td>唐僧</td> <td>男</td> <td>16</td> <td>女儿国</td> </tr> <tr> <td>4</td> <td>唐僧</td> <td>男</td> <td>16</td> <td>女儿国</td> </tr> <tr> <td>1</td> <td>孙悟空</td> <td>男</td> <td>18</td> <td>花果山</td> </tr> <tr> <td>2</td> <td>猪八戒</td> <td>男</td> <td>28</td> <td>高老庄</td> </tr> <tr> <td>3</td> <td>沙和尚</td> <td>男</td> <td>38</td> <td>流沙河</td> </tr> <tr> <td>4</td> <td>唐僧</td> <td>男</td> <td>16</td> <td>女儿国</td> </tr> </table>CSS 代码table { width: 50%; margin: 0 auto; border: 1px black solid; /* border-spacing:指定边框之间的距离;边框之间虽然没有距离了,但是实际上是两条边框的和,看起来是变粗了 */ /* border-spacing: 0; */ /*border-collapse:collapse;设置边框的合并;真正的将两条边框合并成一条边框 */ border-collapse: collapse; /* 默认情况下元素在td中是垂直居中的,可以通过vectical-align来修改 */ vertical-align: middle; text-align: center; /* 如果表格中没有使用tbody而是直接使用tr,那么浏览器会自动创建一个tbody,并且将tr全都放到tbody中 所以说,tr不是table的子元素 */ tbody tr:nth-child(odd) { background-color: rgb(211, 216, 188); border: 1px black solid; }image-20220910145444570.pngimage-20220910145515402.png其中,border-spacing:指定边框之间的距离border-collapse:设置边框的合并4、表单表单在现实生活中表单用于提交数据在网页中也可以使用表单,网页中的表单用于将本地的数据提交给远程的服务器form 的属性action:表单要提交的服务器的地址文本框注意:数据要提交到服务器中,必须要为元素指定一个name属性值文本框<input type="text" name="username" />密码框密码框<input type="password" name="password" />提交按钮<input type="submit" value="注册" />单选框像这种选择框,必须要措定一个value属性,value属性最终会作为用户填写的值传递给服务器单选框 <input type="radio" name="hello" value="a" /> <input type="radio" name="hello" value="b" checked />多选框多选框 <input type="checkbox" name="test" value="1" /> <input type="checkbox" name="test" value="2" /> <input type="checkbox" name="test" value="3" checked />下拉列表下拉列表 <select name="haha"> <option value="i">选项一</option> <option value="ii" selected>选项二</option> <option value="iii">选项三</option> </select>ea8b0b8806fcebbe73a190b71a09fd3a.png5、表单补充按钮<!-- 提交按钮 --> <input type="submit" /> <!-- 重置按钮 --> <input type="reset" /> <!-- 普通按钮 --> <input type="button" value="按钮" /> <br /><br /> <button type="submit">提交</button> <button type="reset">重置</button> <button type="button">按钮</button>02ca926acc195ce5382d791756dca361.png上面两种写法实际上效果是一致的,区别在于:input是自闭合标签,不需要</input>就能结束;button不是自闭合标签,跟一般标签一样是成对出现的button因为不是自闭合标签,所以使用起来更灵活,可以嵌套其他的标签十五、小米官网-练习1、首先css样式重置html, body, span, applet, object, iframe, blockquote, abbr, acronym, address, cite, code, samp, small, strike, strong, center, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; /* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, section { display: block; body { line-height: 1; list-style: none; blockquote, quotes: none; blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; table { border-collapse: collapse; border-spacing: 0; }2、公共样式的抽取/* 公共样式 */ .clearfix::before, .clearfix::after { content: ''; display: table; clear: both; /* 去除a的下划线 */ text-decoration: none; color: #333; body { font: 14px/1.5 Helvetica Neue, Helvetica, Arial, Microsoft Yahei, Hiragino Sans GB, Heiti SC, WenQuanYi Micro Hei, sans-serif; color: #333; min-height: 1226px; /* 设置一个类,用来表示中间容器的宽度! */ width: 1226px; /* 容器居中 */ margin: 0 auto; }3、 HTML 代码<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>小米商城 - Xiaomi 12、Redmi K50、MIX FOLD,小米电视官方网站</title> <!-- 引入css重置样式 --> <link rel="stylesheet" href="./css/reset.css" /> <!-- 引入公共样式表 --> <link rel="stylesheet" href="./css/base.css" /> <!-- 引入图标字体库 --> <link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.2.0/css/all.css" rel="stylesheet" /> <!-- 引入当前页面的样式表 --> <link rel="stylesheet" href="./css/index.css" /> <!-- 设置网站的图标: 网站的图标:favicon.ico可以直接打开! --> <link rel="icon" href="./img/favicon.ico"> </head> <body> <!-- 顶部导航条 --> <!-- 顶部导航条外部容器 --> <div class="topbar-wrapper"> <!-- 创建内部容器 --> <div class="topbar w clearfix"> <!-- 左侧的导航 --> <ul class="service"> <li><a href="javascript:;">小米商城</a></li> <li class="line">|</li> <li><a href="javascript:;">loT</a></a></li> <li class="line">|</li> <li><a href="javascript:;">MIUI</a></li> <li class="line">|</li> <li><a href="javascript:;">云服务</a></li> <li class="line">|</li> <li><a href="javascript:;">金融</a></li> <li class="line">|</li> <li><a href="javascript:;">有品</a></li> <li class="line">|</li> <li><a href="javascript:;">小爱开放平台</a></li> <li class="line">|</li> <li><a href="javascript:;">企业团购</a></li> <li class="line">|</li> <li><a href="javascript:;">资质证照</a></li> <li class="line">|</li> <li><a href="javascript:;">协议规则</a></li> <li class="line">|</li> <li class="app-wrapper"> <a class="app" href="javascript:;"> 下载app <!-- 添加一个弹出层 --> <div class="qrcode"> <img src="./img/miapp.png" alt=""> <span>小米商城app</span> </div> </a> </li> <li class="line">|</li> <li><a href="javascript:;">Select Location</a></li> </ul> <!-- 购物车 --> <ul class="shop-cart"> <li> <a href="#"> <i class="fas fa-cart-arrow-down"></i> 购物车(0) </a> </li> </ul> <!-- 用户登录注册 --> <ul class="user-info"> <li><a href="javascript:;">登录</a></li> <li class="line">|</li> <li><a href="javascript:;">注册</a></li> <li class="line">|</li> <li><a href="javascript:;">消息通知</a></li> </ul> </div> </div> <!-- 创建一个头部的外部容器 --> <div class="header-wrapper"> <div class="header w clearfix"> <h1 class="logo" title="小米"> <a class="home" href="/"></a> <a class="mi" href="/"></a> </h1> <!-- 创建一个中间导航条的容器 --> <div class="nav-wrapper"> <!-- 创建导航条 --> <ul class="nav clearfix"> <li class="all-goods-wrapper"> <a class="all-goods" href="#">全部商品分类</a> <!-- 创建一个左侧导航菜单 --> <ul class="left-menu"> <li> <a href="#"> 手机 电话卡 <i class="fas fa-angle-right"></i> </a> </li> <li> <a href="#"> 电视 盒子 <i class="fas fa-angle-right"></i> </a> </li> <li> <a href="#"> 笔记本 平板 <i class="fas fa-angle-right"></i> </a> </li> <li> <a href="#"> 家电 插线板 <i class="fas fa-angle-right"></i> </a> </li> <li> <a href="#"> 出行 穿戴 <i class="fas fa-angle-right"></i> </a> </li> <li> <a href="#"> 智能 路由器 <i class="fas fa-angle-right"></i> </a> </li> <li> <a href="#"> 电源 配件 <i class="fas fa-angle-right"></i> </a> </li> <li> <a href="#"> 健康 儿童 <i class="fas fa-angle-right"></i> </a> </li> <li> <a href="#"> 耳机 音箱 <i class="fas fa-angle-right"></i> </a> </li> <li> <a href="#"> 生活 箱包 <i class="fas fa-angle-right"></i> </a> </li> </ul> </li> <li><a href="#">小米手机</a></li> <li><a href="#">Redmi红米</a></li> <li><a href="#">电视</a></li> <li><a href="#">笔记本</a></li> <li><a href="#">家电</a></li> <li><a href="#">路由器</a></li> <li><a href="#">只能硬件</a></li> <li><a href="#">服务</a></li> <li><a href="#">社区</a></li> <!-- 创建一个弹出层 --> <div class="goods-info"> </div> <!-- 创建搜索框的容器 --> <div class="search-wrapper"> <form class="search" action="#"> <input class="search-inp" type="text"> <button class="search-btn"> <i class="fas fa-search"></i> </button> </form> </div> </ul> </div> </div> </div> <!-- 创建banner的容器 --> <div class="banner-wrapper"> <div class="banner w"> <ul class="img-list"> <li> <a href="#"> <img src="./img/banner1.jpg" alt=""> </a> </li> <li> <a href="#"> <img src="./img/banner2.webp" alt=""> </a> </li> <li> <a href="#"> <img src="./img/banner3.webpg" alt=""> </a> </li> <li> <a href="#"> <img src="./img/banner4.webp" alt=""> </a> </li> <li> <a href="#"> <img src="./img/banner5.webp" alt=""> </a> </li> </ul> <div class="pointer"> <a class="active" href="javascript:;"></a> <a href="javascript:;"></a> <a href="javascript:;"></a> <a href="javascript:;"></a> <a href="javascript:;"></a> </div> <div class="prev-next"> <a class="prev" href="javascript:;"></a> <a class="next" href="javascript:;"></a> </div> </div> <!-- 固定定位的工具条 --> <div class="back-top"></div> <!-- 创建广告容器 --> <div class="ad w"> <ul class="shortcut"> <li> <a href="#"> <i class="fas fa-clock"></i> </a> </li> <li> <a href="#"> <i class="fas fa-building"></i> </a> </li> <li> <a href="#"> <i class="fas fa-frog"></i> </a> </li> <li> <a href="#"> <i class="fas fa-address-card"></i> </a> </li> <li> <a href="#"> <i class="fas fa-gift"></i> </a> </li> <li> <a href="#"> <i class="fas fa-mobile-alt"></i> </a> </li> </ul> <ul class="ad-img"> <li> <a href="#"> <img src="./img/01.jpg" alt=""> </a> </li> <li> <a href="#"> <img src="./img/02.jpg" alt=""> </a> </li> <li> <a href="#"> <img src="./img/03.jpg" alt=""> </a> </li> </ul> </div> </div> </body> </html>4、CSS 代码/* 主页index.html的样式表 */ /* 设置顶部导航条的容器 */ .topbar-wrapper { width: 100%; height: 40px; line-height: 40px; background-color: #333; /* 设置超链接的颜色 */ .topbar a { font-size: 12px; color: #b0b0b0; display: block; /* 设置超链接移入的效果 */ .topbar a:hover { color: #fff; /* 设置中间的分割线效果 */ .topbar .line { color: #424242; font-size: 12px; margin: 0 8px; margin-top: -1px; /* 设置左侧导航条 */ .service, .topbar li { float: left; .app { position: relative; /* 设置app下的小三角 */ .app-wrapper:hover .app::after { display: none; content: ''; /* 设置绝对定位 */ position: absolute; display: block; width: 0; height: 0; /* 设置四个方向的边框 */ border: 6px solid transparent; /* 去除上边框 */ border-top: none; /* 单独设置下边框的颜色 */ border-bottom-color: #fff; bottom: 0; left: 0; right: 0; margin: auto; /* 设置下载app的下拉 */ .app .qrcode { /* 如果设置过渡先关闭display */ /* display: none; */ position: absolute; /* left: -38px; */ left: 22px; width: 124px; /* height: 135px; */ /* 这个是通过display设置的方式,不好加动画效果! */ /* 通过height、overflow方便设置动画 */ height: 0; overflow: hidden; margin-left: -62px; line-height: 1; text-align: center; background-color: #fff; box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); z-index: 9999; /* transition设置过度效果 */ transition: height 0.3s; /* 与下方的效果是一样的,另一种写法! */ /* .app:hover .qrcode, .app:hover::after { display: block; .app-wrapper:hover > .app .qrcode { display: block; height: 148px; .app .qrcode img { width: 90px; margin: 17px; margin-bottom: 12px; .app .qrcode span { color: #000; font-size: 13px; /* 设置右侧导航条 */ .shop-cart, .user-info { float: right; .shop-cart { margin-left: 26px; /* 设置购物车的样式 */ .shop-cart a { width: 120px; background-color: #424242; text-align: center; .shop-cart:hover a { background-color: #fff; color: #ff6700; .shop-cart i { margin-right: 2px; /* 设置中间的header */ .header { height: 100px; /* background-color: pink; */ .header-wrapper { /* background-color: red; */ position: relative; /* 设置logo的h1 */ .header .logo { /* 隐藏logo中的文字 */ text-indent: -999px; float: left; margin-top: 22px; width: 55px; height: 55px; position: relative; overflow: hidden; /* 统一设置logo的超链接 */ .header .logo a { /* display: block; */ position: absolute; width: 55px; height: 55px; left: 0; background-color: #ff6700; background-image: url('../img/mi-logo.png'); background-size: cover; background-position: center; transition: left 0.3s; /* 设置home的图标 */ .header .logo .home { left: -55px; background-image: url('../img/mi-home.jpeg'); /* 设置鼠标移入以后两个图标的位置 */ .header .logo:hover .mi { left: 55px; .header .logo:hover .home { left: 0px; .header .nav-wrapper { float: left; margin-left: 7px; /* 设置导航条 */ .header .nav { /* width: 792px; */ height: 100px; line-height: 100px; padding-left: 58px; /* 设置导航条的li */ .nav > li { float: left; .all-goods-wrapper { position: relative; /* 设置左侧导航条的样式 */ .left-menu { width: 234px; height: 420px; background-color: rgba(0, 0, 0, 0.6); position: absolute; z-index: 998; left: -120px; line-height: 1; padding: 20px 0; ul .left-menu li a { display: block; height: 42px; line-height: 42px; color: white; margin-right: 0; padding: 0 30px; font-size: 14px; ul .left-menu li a:hover { color: white; background-color: #ff6700; .left-menu a i { float: right; line-height: 42px; .nav-wrapper li a { font-size: 16px; display: block; margin-right: 20px; .nav-wrapper li a:hover { color: #ff6700; /* 隐藏全部商品 */ .all-goods { visibility: hidden; .nav .goods-info { /* height: 228px; */ height: 0; overflow: hidden; width: 100%; position: absolute; background-color: white; top: 100px; left: 0; transition: height 0.3s; /* background-color: #bfa; */ z-index: 999; .nav li:not(:first-of-type):not(:nth-child(10)):not(:nth-child(9)):hover ~ .goods-info, .goods-info:hover { border-top: 1px solid rgb(224, 224, 224); box-shadow: 0 5px 3px rgba(0, 0, 0, 0.2); height: 228px; /* 设置搜索框的容器 */ .search-wrapper { width: 296px; height: 50px; float: right; margin-top: 25px; .search-wrapper .search-inp { box-sizing: border-box; float: left; padding: 0; height: 50px; border: none; padding: 0 10px; width: 244px; font-size: 16px; border: 1px solid rgb(224, 224, 224); outline: none; /* 设置input获取焦点后的样式 */ .search-wrapper .search-inp:focus, .search-wrapper .search-inp:focus + button { /* outline: 1px solid #ff6700; */ border-color: #ff6700; .search-wrapper .search-btn { float: left; height: 50px; width: 52px; padding: 0; border: none; background-color: #fff; color: #616161; font-size: 16px; border: 1px solid rgb(224, 224, 224); border-left: none; .search-wrapper .search-btn:hover { background-color: #ff6700; color: white; border: none; /* 设置banner */ .banner { position: relative; height: 460px; .banner .img-list li { position: absolute; .banner img { width: 100%; /* 设置5个导航点 */ .pointer { position: absolute; bottom: 22px; right: 35px; .pointer a { float: left; width: 6px; height: 6px; border: 2px rgba(0, 0, 0, 0.4) solid; border-radius: 50%; background-color: rgba(0, 0, 0, 0.4); margin-left: 6px; .pointer a:hover, .pointer a.active { border-color: rgba(0, 0, 0, 0.4); background-color: rgba(225, 225, 225, 0.4); /* 设置两个箭头 */ .prev-next a { width: 41px; height: 69px; position: absolute; background-image: url('../img/icon-slides.png'); top: 0; bottom: 0; margin: auto 0; .prev-next .prev { left: 234px; background-position: -84px 0; .prev-next .prev:hover { background-position: 0 0; .prev-next .next { right: 0; background-position: -125px 0; .prev-next .next:hover { background-position: -42px 0; /* 设置回到顶部的元素 */ .back-top { width: 26px; height: 206px; background-color: #ff6700; position: fixed; bottom: 60px; right: 50%; margin-right: -639px; /* 设置下部的广告区域 */ .ad { /* background-color: orange; */ height: 170px; margin-top: 14px; .ad .shortcut, .ad .ad-img, .ad li { float: left; /* 设置左侧快捷方式 */ .ad .shortcut { width: 228px; height: 168px; background-color: #5f5750; margin-right: 14px; padding-left: 6px; padding-top: 2px; .ad .shortcut li { position: relative; /* 设置上边框 */ .ad .shortcut li::before { content: ''; position: absolute; width: 64px; height: 1px; background-color: #665e57; left: 0; right: 0; top: 0; margin: 0 auto; /* 设置左边框 */ .ad .shortcut li::after { content: ''; position: absolute; height: 70px; width: 1px; background-color: #665e57; top: 0; left: 0; margin: auto 0; /* 设置快捷方式的超链接 */ .ad .shortcut a { color: #cfccca; display: block; height: 84px; width: 76px; text-align: center; font-size: 12px; overflow: hidden; .ad .shortcut a:hover { color: #fff; /* 设置图标字体 */ .ad .shortcut i { display: block; margin-top: 20px; font-size: 20px; margin-bottom: 6px; /* 设置自左侧的图片 */ .ad .ad-img li { width: 316px; margin-right: 15px; .ad .ad-img li:last-child { margin: 0; .ad .ad-img img { width: 100%; vertical-align: top; 5、效果展示image-20220911140001703.png对于小米官网的一些静态资源可以在mi.com的开发者工具进行获取,这里省略!十六、过渡与动画1、过渡过渡(transition)通过过渡可以指定一个属性发生变化时的切换方式通过过渡可以创建一些非常好的效果,提升用户的体验属性值transition-property:指定要执行过渡的属性多个属性间使用,隔开;如果所有属性都需要过渡,则使用all关键字;大部分属性都支持过渡效果;注意过渡时必须是从一个有效数值向另外一个有效数值进行过渡;transition-duration:指定过渡效果的持续时间时间单位:s 和 ms(1s=1000ms)transition-delay:过渡效果的延迟,等待一段时间后在执行过渡transition-timing-function:过渡的时序函数linear匀速运动ease 默认值,慢速开始,先加速后减速ease-in 加速运动ease-out 减速运动ease-in-out 先加速后减速cubic-bezier()来指定时序函数 https://cubic-bezier.comsteps() 分步执行过渡效果,可以设置第二个值:end,在时间结束时执行过渡(默认值)start,在时间开始时执行过渡transition:可以同时设置过渡相关的所有属性只有一个要求,如果要写延迟,则两个时间中第一个是持续时间,第二个是延迟时间/* transition: margin-left 2s 1s; */ transition-property: margin-left; transition-duration: 2s; transition-delay: 1s;7480db3e978d72b04b48e47351b2712a.gif示例/* transition: margin-left 2s 1s; */ transition-property: margin-left; transition-duration: 2s; transition-delay: 1s;7480db3e978d72b04b48e47351b2712a-20220911144020024.gif几种过渡效果对比linear匀速运动transition-timing-function: linear;db1e5c02804cfa5d8cfc03e84f465cc0.gifease 默认值,慢速开始,先加速后减速transition-timing-function: ease;cc12a68d322c7f04647408b28e1a355b.gifease-in 加速运动transition-timing-function: ease-in;e7e99280e1108793f8e0f681587c50fd.gifease-out 减速运动transition-timing-function: ease-out;2c408e4fbec59ccbcf324b308d893ad9.gifease-in-out 先加速后减速transition-timing-function: ease-in-out;a34bb617b70c57b8bdcdbba930315b82.gifcubic-bezier()来指定时序函数transition-timing-function: cubic-bezier(0.17, 1.79, 0.68, -0.69);95be9a5c0834580690f63674754a5a5d.gifsteps()分步执行过渡效果/* transition-timing-function: steps(2, end); */ transition-timing-function: steps(2);fad621042638ade6ffc1ca8ed7291524.giftransition-timing-function: steps(2, start);eafa6e6574b7570eea4cbbd26bafe377.gif2、动画动画和过渡类似,都是可以实现一些动态的效果,不同的是过渡需要在某个属性发生变化时才会触发动画可以自动触发动态效果设置动画效果,必须先要设置一个关键帧,关键帧设置了动画执行每一个步骤@keyframes test { from { margin-left: 0; margin-left: 900px; }animation-name 指定动画的关键帧名称animation-duration:指定动画效果的持续时间animation-delay:动画效果的延迟,等待一段时间后在执行动画animation-timing-function:动画的时序函数animation-iteration-count 动画执行的次数infinite 无限执行animation-direction 指定动画运行的方向normal 从from向to运行,每次都是这样,默认值reverse 从to向from运行,每次都是这样alternate 从from向to运行,重复执行动画时反向执行alternate-reverse 从to向from运行,重复执行动画时反向执行animation-play-state 设置动画的执行状态running 动画执行,默认值paused 动画暂停animation-fill-mode 动画的填充模式none 动画执行完毕,元素回到原来位置,默认值forwards 动画执行完毕,元素会停止在动画结束的位置backwards 动画延时等待时,元素就会处于开始位置both 结合了forwards和backwards示例/* animation-name: test; animation-duration: 2s; animation-delay: 2s; animation-timing-function: linear; animation-iteration-count: infinite; animation-direction: alternate; animation-fill-mode: both; */ animation: test 2s 2s linear infinite alternate both;c81fb42fc5c22a36a108077fb3c388c7.gif3、实战米兔.box { height: 271px; width: 132px; background-image: url("/assets/米兔/bigtap-mitu-queue-big.png"); margin: 100px auto; transition: background-position 1s steps(4); .box:hover { background-position: -528px 0; }cfcff3ec2765beff571c95c8af08b20d.gif奔跑的少年.box { height: 256px; width: calc(1536px / 6); background-image: url("/assets/奔跑的少年/bg2.png"); margin: 100px auto; animation: run 1s steps(6) infinite; /* 关键帧 */ @keyframes run { from { background-position: 0 0; background-position: -1536px 0; }c78e2ab5980c340bc4ee76e3229c8292.gif弹力球.outer { width: 100%; height: 700px; border-bottom: 10px solid #000; /* 外边距重叠,开启BFC */ overflow: hidden; .ball { width: 100px; height: 100px; border-radius: 50%; background-color: gray; animation: bounce 6s ease-in; @keyframes bounce { from { margin-top: 0; margin-top: 600px; animation-timing-function: ease-out; 90% { animation-timing-function: ease-in; 10% { margin-top: 60px; 20% { margin-top: 120px; 30% { margin-top: 180px; 40% { margin-top: 240px; 50% { margin-top: 300px; 60% { margin-top: 360px; 70% { margin-top: 420px; 80% { margin-top: 480px; 90% { margin-top: 540px; 96% { margin-top: 580px; 99% { margin-top: 590px; }52d7d87ae591a78eba76a51b14903865.gif酷炫球div { float: left; width: 100px; height: 100px; border-radius: 50%; animation: bounce 0.5s infinite ease-in alternate; .ball1 { background-color: red; animation-delay: 0.1s; .ball2 { background-color: yellow; animation-delay: 0.2s; .ball3 { background-color: green; animation-delay: 0.3s; .ball4 { background-color: blue; animation-delay: 0.4s; .ball5 { background-color: pink; animation-delay: 0.5s; .ball6 { background-color: orange; animation-delay: 0.6s; .ball7 { background-color: fuchsia; animation-delay: 0.7s; .ball8 { background-color: gray; animation-delay: 0.8s; .ball9 { background-color: darkcyan; animation-delay: 0.9s; .ball10 { background-color: indigo; animation-delay: 1s; .ball11 { background-color: black; animation-delay: 1.1s; .ball12 { background-color: darkcyan; animation-delay: 1.2s; .ball13 { background-color: darkkhaki; animation-delay: 1.3s; .ball14 { background-color: brown; animation-delay: 1.4s; .ball15 { background-color: mediumpurple; animation-delay: 1.5s; @keyframes bounce { from { margin-top: 0; margin-top: 500px; }d50aebb593aefa2d94e62fcbaf49f37c.gif十七、变形:平移、旋转与缩放变形就是指通过 css 来改变元素的形状或位置变形不会影响到页面的布局transform用来设置元素的变形效果1、平移translateX() 沿着由方向平移translateY() 沿着 y 轴方向平移translateZ() 沿着 z 轴方向平移平移元素百分比是相对于自身计算的几种水平垂直双方向居中的方式对比绝对定位的方式/* 这种居中方式,只适用于元素的大小确定 */ position: absolute; top: 0; left: 0; bottom: 0; right: 0; margin: auto;table-cell的方式/* table-cell的方式具有一定局限性 */ display: table-cell; vertical-align: middle; text-align: center;transform的方式/* transform变形平移的方式 */ position: absolute; left: 50%; top: 50%; transform: translateX(-50%) translateY(-50%);浮出效果div { float: left; width: 200px; height: 300px; background-color: silver; margin: 100px 50px auto 50px; transition: all 0.3s; div:hover { box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); transform: translateY(-5px); }241b88c09dcb68952c07cc88a7d68f26.gif2、Z 轴平移z 轴平移,调整元素在 z 轴的位置,正常情况就是调整元素和人眼之间的距离,距离越大,元素离人越近z 轴平移属于立体效果(近大远小),默认情况下网页是不支持透视,如果需要看见效果必须要设置网页的视距透视效果html { background-color: rgb(71, 44, 32); /* 设置当前网页的视距为800px,人眼距离网页的距离 */ perspective: 800px; .box { width: 200px; height: 300px; background-color: silver; margin: 100px auto; transition: all 0.3s; .box:hover { box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); transform: translateZ(200px); }7a257398286736e1e216f16e808ad651.gif3、旋转通过旋转可以使元素沿着 x、y 或 z 旋转指定的角度rotateX()rotateY()rotateZ()/* transform: rotateY(0.5turn); */ transform: rotateY(180deg); /* backface-visibility设置元素的背面:可选值:hidden、visible:默认值! */d68844170ec937f3d811eff8d6e5200e.gif4、缩放对元素进行缩放的函数scalex() 水平方向缩放scaleY() 垂直方向缩放scale() 双方向的缩放.box { height: 200px; width: 200px; background-color: #bfa; margin: 200px auto; transition: 2s; .box:hover { /* transform: scaleX(2); */ /* transform: scaleY(2); */ transform: scale(2); /* 变形的原点 */ transform-origin: 0 0; }b24a8805bd538d5a520c0e68b7e52e5a.gif5、实战鸭子表HTML 代码<div class="clock"> <div class="hour-wrapper"> <div class="hour"></div> </div> <div class="minute-wrapper"> <div class="minute"></div> </div> <div class="second-wrapper"> <div class="second"></div> </div> </div>CSS 代码.clock { width: 500px; height: 500px; background-image: url("assets/鸭子表/clock.png"); background-image: url("assets/鸭子表/clock_duck.jpg"); background-size: cover; margin: 100px auto; position: relative; .clock > div { position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; .clock > div > div { height: 50%; margin: 0 auto; /* 时针 */ .hour-wrapper { height: 60%; width: 60%; animation: clock-run 720s infinite; .hour { width: 8px; background-color: black; /* 分针 */ .minute-wrapper { height: 75%; width: 75%; animation: clock-run 60s steps(60) infinite; .minute { width: 4px; background-color: black; /* 秒针 */ .second-wrapper { height: 90%; width: 90%; animation: clock-run 1s steps(60) infinite; .second { width: 2px; background-color: red; @keyframes clock-run { from { transform: rotateZ(0); transform: rotateZ(360deg); }4fb02c061aeac2234f492f24026a0ea7.gif复仇者联盟HTML 代码<div class="cube"> <div class="surface1"></div> <div class="surface2"></div> <div class="surface3"></div> <div class="surface4"></div> <div class="surface5"></div> <div class="surface6"></div> </div>CSS 代码html { perspective: 800px; .cube { height: 200px; width: 200px; margin: 200px auto; position: relative; /* 设置3d变形效果 */ transform-style: preserve-3d; animation: cube-rotate 12s infinite linear; .cube div { height: 200px; width: 200px; background-size: cover; position: absolute; top: 0; left: 0; /* 为元素设置透明效果 */ opacity: 0.85; .surface1 { background-image: url("/assets/复仇者联盟/1.jpg"); transform: translateX(-100px) rotateY(90deg); .surface2 { background-image: url("/assets/复仇者联盟/2.jpg"); transform: translateX(100px) rotateY(90deg); .surface3 { background-image: url("/assets/复仇者联盟/3.jpg"); transform: translateY(-100px) rotateX(90deg); .surface4 { background-image: url("/assets/复仇者联盟/4.jpg"); transform: translateY(100px) rotateX(90deg); .surface5 { background-image: url("/assets/复仇者联盟/5.jpg"); transform: translateZ(-100px); .surface6 { background-image: url("/assets/复仇者联盟/6.jpg"); transform: translateZ(100px); @keyframes cube-rotate { from { transform: rotateX(0) rotateY(0) rotateZ(0); transform: rotateX(1turn) rotateY(2turn) rotateZ(3turn); }a00ee8c59dd8e4bf7ac14286be0ccd3d.gif十八、Less简介Less官网地址:https://less.bootcss.com/less是一门css的预处理语言less是一个 css 的增强版,通过less可以编写更少的代码实现更强大的样式在less中添加了许多的新特性:像对变量的支持、对mixin的支持...less的语法大体上和css语法一致,但是less中增添了许多对css的扩展,所以浏览器无法直接执行less代码,要执行必须向将less转换为css,然后再由浏览器执行1、安装插件在vscode中搜索less,点击安装image-20220911212840143.png2、编写 LessHTML 代码使用快捷方式创建 HTML 代码f992f8225262f1355df4628586602a87.png回车生成html代码<div class="box1"></div> <div class="box2"></div> <div class="box3"></div>less 代码创建style.less文件,编写less代码body { --height: calc(200px / 2); --width: 100px; div { height: var(--height); width: var(--width); .box1 { background-color: #bfa; .box2 { background-color: red; .box3 { background-color: yellow; }Easy LESS插件会帮助我们在style.less所在目录下面生成一个相同名称的css文件201068ea2c14019210172db470a04934.png查看生成的style.css代码body { --height: calc(200px / 2); --width: 100px; body div { height: var(--height); width: var(--width); body .box1 { background-color: #bfa; body .box2 { background-color: red; body .box3 { background-color: yellow; }我们直接在 HTML 中引入生成的style.css<link rel="stylesheet" href="/css/style.css" />运行代码,查看效果97f40a1f8f76041202ac4ef693ad5b36.png2、Less 语法less 注释less中的单行注释,注释中的内容不会被解析到css中css中的注释,内容会被解析到css文件中// `less`中的单行注释,注释中的内容不会被解析到`css`中 `css`中的注释,内容会被解析到`css`文件中 */父子关系嵌套在less中,父子关系可以直接嵌套// `less`中的单行注释,注释中的内容不会被解析到`css`中 `css`中的注释,内容会被解析到`css`文件中 body { --height: calc(200px / 2); --width: 100px; div { height: var(--height); width: var(--width); .box1 { background-color: #bfa; .box2 { background-color: red; .box3 { background-color: yellow; > .box4 { background-color: green; }对应的css/* `css`中的注释,内容会被解析到`css`文件中 body { --height: calc(200px / 2); --width: 100px; body div { height: var(--height); width: var(--width); body .box1 { background-color: #bfa; body .box1 .box2 { background-color: red; body .box1 .box2 .box3 { background-color: yellow; body .box1 .box2 > .box4 { background-color: green; }变量变量,在变量中可以存储一个任意的值并且我们可以在需要时,任意的修改变量中的值变量的语法:@变量名直接使用使用变量时,则以@变量名的形式使用即可作为类名、属性名或者一部分值使用时,必须以@{变量名}的形式使用可以在变量声明前就使用变量(可以但不建议)@b1:box1; @b2:box2; @b3:box3; @size:200px; @bc:background-color; @bi:background-image; @color:red; @path:image/a/b/c; .@{b1}{ width: @size; height: $width; @{bc}: @color; @{bi}: url("@{path}/1.png"); .@{b2}{ width: @size; height: $width; @{bc}: @color; @{bi}: url("@{path}/2.png"); .@{b3}{ width: @size; height: $width; @{bc}: @color; @{bi}: url("@{path}/3.png"); }生成的css代码.box1 { width: 200px; height: 200px; background-color: red; background-image: url("image / a / b / c/1.png"); .box2 { width: 200px; height: 200px; background-color: red; background-image: url("image / a / b / c/2.png"); .box3 { width: 200px; height: 200px; background-color: red; background-image: url("image / a / b / c/3.png"); }注意:在url中使用less语法需要用引号包裹其他.p1 { width: @size; height: $width; &-wrapper { background-color: peru; // &:hover{ // background-color: blue; :hover { background-color: blue; .p2:extend(.p1) { color: @color; .p3 { .p1(); .p4() { width: @size; height: $width; .p5 { // .p4(); }生成的css代码.p1, .p2 { width: 200px; height: 200px; .p1-wrapper { background-color: peru; .p1 :hover { background-color: blue; .p2 { color: red; .p3 { width: 200px; height: 200px; .p5 { width: 200px; height: 200px; }& 拼接伪元素:extend() 对当前选择器扩展指定选择器的样式(选择器分组).p1() 直接对指定的样式进行引用,这里就相当于将p1的样式在这里进行了复制(mixin 混合)使用类选择器时可以在选择器后边添加一个括号,这时我们实际上就创建了一个mixins混合函数4、混合函数在混合函数中可以直接设置变量,并且可以指定默认值.test(@w:200px, @h:100px, @bc:red) { width: @w; height: @h; background-color: @bc; .p6 { // .test(200px, 100px, red); // 对应参数位传值 // .test(@h:200px,@w:100px,@bc:red); // 写明对应属性,可变换顺序 // .test(); .test(300px); }生成的css代码.p6 { width: 300px; height: 100px; background-color: red; }其他average混合函数.h1 { color: average(red, yellow); }生成的css代码.h1 { color: #ff8000; }darken混合函数body { background-color: darken(#bfa, 50%); }生成的css代码body { background-color: #22aa00; }5、补充创建all.less文件,将我们之前编写的less文件通过@import引入进来可以通过import来将其他的less引入到当前的less中@import "style.less"; @import "syntax.less";查看生成的all.css代码,会发现其他的内容囊括了两个less文件的内容所以,我们可以利用@import来对less文件进行整合,然后引入生成的css文件使用即可这样,每次修改的时候直接对某个模块的less文件进行修改,就会非常简单如果我们观察过之前fontawesome源码文件,会发现其中也有less代码文件71b255aafaccb595fc0341c50f6a3fa0.png不同的less文件里都有其自己的职责,如_animated.less中专门存放动画的混合函数_variables.less中专门存放定义的变量...但是也有个问题,通过F12调试时显示的也是css中对应的行号dcc8dac7e284d8f1f296455dd302d7a6.png如果我们要改,需要找一下,太麻烦了,能不能直接显示less中行号呢?这样我们直接定位到对应less中直接进行修改,维护起来也会比较方便我们需要在Easy LESS插件中修改settings.json文件,在其中添加如下配置"less.compile": { "compress": true, // true => remove surplus whitespace "sourceMap": true, // true => generate source maps (.css.map files) "out": true // false => DON'T output .css files (overridable per-file, see below) }修改完毕后,会发现多生成出来一个all.css.map文件,说明配置生效288677124cd646d938430db12c32efbe.png再刷新下页面,通过F12会发现变成了less文件对应的行号781253b1659517f6dcf08697930b8e6d.png我们来逐一解释下配置的less.compile项中每一个属性的含义compress 生成的css文件代码会被压缩(作用相当于我们之前安装的JS & CSS Minifier (Minify)插件的效果)sourceMap 生成.css.map文件,通过F12可以查看了less文件对应行号out 生成对应css文件(当然是需要了)十九、弹性盒简介可以参考:https://www.cnblogs.com/zhaostudy/p/16558810.html1、基本概念弹性盒flex(弹性盒、伸缩盒)是css中的又一种布局手段,它主要用来代替浮动来完成页面的布局flex可以使元素具有弹性,让元素可以跟随页面的大小的改变而改变弹性容器要使用弹性盒,必须先将一个元素设置为弹性容器我们通过display 来设置弹性容器display:flex 设置为块级弹性容器display:inline-flex 设置为行内的弹性容器/* 设置弹性容器 */ display: flex;5c1d885e5f11e55cf83de4828487003b.png弹性元素弹性容器的子元素是弹性元素(弹性项)弹性元素可以同时是弹性容器2、弹性容器的属性主轴与侧轴主轴:弹性元素的排列方向称为主轴侧轴:与主轴垂直方向的称为侧轴主轴属性排列方式flex-direction 指定容器中弹性元素的排列方式row默认值,弹性元素在容器中水平排列(自左向右)row-reverse 弹性元素在容器中反向水平排列(自右向左)column 弹性元素纵向排列(自上向下)column-reverse 弹性元素反向纵向排列(自下向上)/* 设置弹性元素排列方式 */ flex-direction: column;11729c6c2c818441f5089c3a3984a080.png自动换行flex-wrap 设置弹性元素是否在弹性容器中自动换行nowrap 默认值,元素不会自动换行wrap 元素沿着辅轴方向自动换行/* 设置弹性元素排列方式 */ flex-direction: row; /* 设置自动换行 */ flex-wrap: wrap;c87e43a9db82a4f564f98dbdae47fcc4.png简写属性flex-flow 是wrap和direction的简写属性/* 简写属性 */ flex-flow: row wrap;963e711b9a2217a1c34b1a08538ea795.png空白空间justify-content 如何分配主轴上的空白空间(主轴上的元素如何排列)flex-start 元素沿着主轴起边排列3ee816db158fb6b5d480172f9ac578c0.pngflex-end 元素沿着主轴终边排列bb526f4373ab471791217f20260dbbd1.pngcenter 元素居中排列0c54d451da26478557563813cbbcc77f.pngspace-around 空白分布到元素两侧954f8711b1e786fcc0834a064290c861.pngspace-between 空白均匀分布到元素间03d831ec631b993192edfd186e4c2396.pngspace-evenly 空白分布到元素的单侧110522935bd98f332cb9cf04380f563b.png辅轴属性辅轴对齐align-items元素在辅轴上如何对齐stretch 默认值,将元素的长度设置为相同的值2af92a896e40671ca536a9ed39dc1c1b.pngflex-start 元素不会拉伸,沿着辅轴起边对齐6ab8b213829111066f5cd0ad1fa70bb7.pngflex-end 沿着辅轴的终边对齐9c5f3fff8119986aa076cbe5f3e778d9.pngcenter 居中对齐5448b556d848e25d86ea52c09222f8bf.pngbaseline 基线对齐c33af4a7dc9c4e2ea1a978edcfad4e11.png空白空间align-content 如何分配辅轴上的空白空间(辅轴上的元素如何排列)flex-start 元素沿着辅轴起边排列9508792eb1a11fe0af3d51f1d47bb584.pngflex-end 元素沿着辅轴终边排列13cf3c6ca7af292ddc5fb2279dc19839.pngcenter 元素居中排列d788195de72ec17c817338b220c2ea32.pngspace-around 空白分布到元素两侧3809331c72ca666656b99b1983e0d473.pngspace-between 空白均匀分布到元素间4a28fd1069af8b43b103e48f4db7cecf.pngspace-evenly 空白分布到元素的单侧f07a0ca24286bb5c3c5c3caf5a8e7559.png弹性居中利用弹性盒对元素进行水平垂直双方向居中justify-content: center; align-items: center;0f326ed5006e1aac8e7f9120cb02c8b4.png3、弹性元素的属性伸展系数flex-grow 指定弹性元素的伸展系数,默认值为 0当父元素有多余空间的时,子元素如何伸展父元素的剩余空间,会按照比例进行分配li:nth-child(1) { background-color: #bfa; flex-grow: 1; li:nth-child(2) { background-color: red; flex-grow: 2; li:nth-child(3) { background-color: green; flex-grow: 3; }fe1e85d111f123fbdc490048947a7252.png缩减系数flex-shrink 指定弹性元素的收缩系数,默认值为 1当父元素中的空间不足以容纳所有的子元素时,如何对子元素进行收缩缩减系数的计算方式比较复杂,缩减多少是根据 缩减系数 和 元素大小 来计算li:nth-child(1) { background-color: #bfa; flex-shrink: 1; li:nth-child(2) { background-color: red; flex-shrink: 2; li:nth-child(3) { background-color: green; flex-shrink: 3; }19f06ff1e9a81a0838ab6f1cdfc607a0.png基础长度flex-basis 指定的是元素在主轴上的基础长度如果主轴是横向的,则该值指定的就是元素的宽度如果主轴是纵向的,则该值指定的就是元素的高度默认值是auto,表示参考元素自身的高度或宽度如果传递了一个具体的数值,则以该值为准li:nth-child(1) { background-color: #bfa; flex-basis: 200px; }4ebbe412b98a6bda2cdcf8a95e6687b1.png简写属性flex可以设置弹性元素所有的三个样式 flex: 增长 缩减 基础initial:flex: 0 1 autoauto:flex: 1 1 autonone:flex: 0 0 auto 弹性元素没有弹性排列顺序order 决定弹性元素的排列顺序li:nth-child(1) { background-color: #bfa; order: 2; li:nth-child(2) { background-color: red; order: 3; li:nth-child(3) { background-color: green; order: 1; }9946555125edbfac6ed4bad5db916027.png覆盖辅轴align-self 用来覆盖当前弹性元素上的align-itemsli:nth-child(1) { background-color: #bfa; align-self: flex-end; }088ae6940229c340026c918928f9607e.png
前端复习-----css, html篇(下)
关于fixed和transform结合的小bug我们知道position: fixed;偏移量是以整个浏览器窗口为参照的。对于声明transfrom值非none元素,其子元素中若存在position: fixed将以声明transform的最近祖先作为基准而定位,这是因为transfrom值非none的元素定义了一个局部坐标系统,导致postion: fixed以此坐标系统计算布局。 目前这个bug仍未被解决,官方建议避免在transform元素下做fixed定位。<style> .parent { transform: translate(100px, 0); border: 1px solid black; .son { position: fixed; left: 60px; width: 50px; height: 50px; background: red; </style> <div class="parent"> <div class="son"></div> </div>如何通过css实现滚动进度条body { height: 2000px; background-image: linear-gradient(to right top, #ffcc00 50%, #eee 50%); background-size: 100% calc(100% - 100vh + 5px); background-repeat: no-repeat; //这里是为了覆盖body 5px一下的渐变颜色的。 body::after { content: ""; position: fixed; top: 5px; left: 0; bottom: 0; right: 0; background: #fff; z-index: -1; }分辨率,像素,多倍图同一个分辨率下,显示的像素格越多,图片越清晰。分辨率是度量位图图像内数据量多少的一个参数。像素密度描述的是一英寸斜边代表的范围内含有的物理像素块的数量。设备无关像素:css中的像素。设备无关像素是一个抽象存在的像素。也就是说他可大可小。图片像素就是虚拟像素。是因为他没有固定的尺寸,我们可以根据需要将图片拉伸成任意尺寸,相应的每个像素也会缩放。设备像素比:DPR = 物理像素/设备无关像素图片的分辨率就是指 图片 横向的色块数量 x 纵向的色块数量。图片像素是构成图片的像素块,他们是没有实际尺寸的,你拉伸缩放图片他的像素块数量是不会变的。设备像素是指设备屏幕被划分成的千千万万个小格子,是物理像素,你能够通过设备的长宽和分辨率计算出每个物理像素的尺寸。CSS像素又叫设备无关像素,是一个抽象的概念。不同设备上的1px用肉眼看是不一样长的。是我们编码时用到的单位。一些文字样式white-space:normal(默认值) | nowrap | pre | pre-wrap | pre-linenowrap: 表示永不换行。多个普通的空格被合并为一个。但是不会干扰</br>的实现。pre: preserve的缩写,表示保存。就是现实在页面上的内容和写在html标签中的内容是一模一样的,包括普通的空格也会显示若干个。pre-wrap:preserve-wrap的缩写。表示保留和换行。是前两个属性的综合。pre-line: 多个普通空格被合并,而且会自动换行。(CSS2)使标题竖着排列table-cell:指定对象作为表格单元格。类同于html标签(CSS2)不可设置margin,可以设置paddingtable-row:指定对象作为表格行。类同于html标签是否能发挥作用换行符普通空格自动换行、nbsp;normal××(合并)√√nowrap××(合并)×√pre√√×√pre-wrap√√√√pre-line√×(合并)√√word-break: normal(默认值) | break-all | keep-allbreak-all:非常强烈的折行。如果是一个很长的英文单词,可能将单词从中间截断。keep-all: 只有空格可以触发自动换行,不然就会超出容器宽度。并且不会拆分很长的单词而换行。word-wrap(overflow-wrap):normal | break-wordbreak-word:表示不那么强烈的折行。只有在一行显示不下的时候才会拆分单词。不然就是正常折行。letter-spacing:设置每个字符之间的距离的。如果是英文单词,他表示每个字母之间的距离,而不是每个单词之间的距离。wor-spacing:设置每个英文单词之间的距离。css的层叠上下文定义: MDN中的解释:我们假定用户正面向(浏览器)视窗或网页,而 HTML 元素沿着其相对于用户的一条虚构的 z 轴排开,层叠上下文就是对这些 HTML 元素的一个三维构想。众 HTML 元素基于其元素属性按照优先级顺序占据这个空间。产生层叠上下文的方式:文档根元素(HTML);position 值为 absolute(绝对定位)或 relative(相对定位)且 z-index 值不为 auto 的元素;position 值为 fixed(固定定位)或 sticky(粘滞定位)的元素(沾滞定位适配所有移动设备上的浏览器,但老的桌面浏览器不支持);父元素的display属性值为flex|inline-flex,子元素z-index属性值不为auto的时候,子元素为层叠上下文元素;grid (grid) 容器的子元素,且 z-index 值不为 auto;opacity 属性值小于 1 的元素(参见 the specification for opacity);mix-blend-mode 属性值不为 normal 的元素;以下任意属性值不为none的元素:transformfilterperspectiveclip-pathmask / mask-image / mask-borderisolation 属性值为 isolate 的元素;-webkit-overflow-scrolling 属性值为 touch 的元素;will-change 值设定了任一属性而该属性在 non-initial 值时会创建层叠上下文的元素(参考这篇文章);contain 属性值为 layout、paint 或包含它们其中之一的合成值(比如 contain: strict、contain: content)的元素。层叠等级图处于同一个层叠上下文中,比较的规则。奇葩的测试:当两个父元素,设置相同的z-index时,它们的层叠等级,层叠顺序是相同的, 此时比较的就是正常文档流中的dom顺序。<style> margin: 0; padding: 0; position: relative; z-index: 1; position: relative; z-index: 1; position: absolute; background: red; width: 100px; height: 100px; z-index: 5; position: absolute; top: 100px; background: blue; width: 100px; height: 100px; margin-top: -50px; </style> <div class="A"> <p class="a">a</p> </div> <div class="B"> <p class="b">b</p> </div>层叠上下文元素的background/border的层叠等级小于z-index值小于0的元素的层叠等级。<style> .box { display: flex; .parent { width: 200px; height: 100px; background: #168bf5; /* 虽然设置了z-index,但是没有设置position,z-index无效,.parent还是普通元素,没有产生层叠上下文 */ z-index: 1; .child { width: 100px; height: 200px; background: #32d19c; position: relative; z-index: -1; </style> <div class="box"> <div class="parent"> parent <div class="child">child</div> </div> </div>总结:层叠上下文可以包含在其他层叠上下文中,并且一起创建一个层叠上下文的层级。每个层叠上下文都完全独立于它的兄弟元素:当处理层叠时只考虑子元素。每个层叠上下文都是自包含的:当一个元素的内容发生层叠后,该元素将被作为整体在父级层叠上下文中按顺序进行层叠。比较元素的层叠等级时,都需要在同一个父级层叠上下文中。不同父级层叠上下文中的子元素中的z-index不会进行比较。学习自掘金will-change为web开发者提供了一种告知浏览器该元素会有哪些变化的方法,这样浏览器可以在元素属性真正发生变化之前提前做好对应的优化准备工作。 这种优化可以将一部分复杂的计算工作提前准备好,使页面的反应更为快速灵敏。不要将 will-change 应用到太多元素上给它足够的工作时间:使用时需要尝试去找到一些方法提前一定时间获知元素可能发生的变化,然后为它加上 will-change 属性。建议加在改变元素之前。比如如果想让移入时元素有边框阴影,我们可以将该样式加在移入和元素的时候加给子元素。<style> div { position: relative; padding: 1em; background-color: #fff; div:before { content: ""; position: absolute; top: 0; right: 0; bottom: 0; left: 0; /* z-index: -1; */ box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.3); opacity: 0; will-change: opacity; -webkit-transition: 0.2s; transition: 0.2s; div:hover:before { opacity: 1; </style> <div> <p> dwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwd吧哒哒哒哒哒哒多地对地导弹对哒哒哒哒哒哒多多多多多多多多多 </p> </div>meta标签的作用如果设置了 name 属性,meta 元素提供的是文档级别(document-level)的元数据,应用于整个页面。和content结合使用,以名-值对的方式给文档提供元数据,其中 name 作为元数据的名称,content 作为元数据的值。常用的值: keywords,description, viewportkeywords: 设置关键字,content为要设置的关键字,利于搜索引擎的搜索。description: 该网页的描述文字,content为描述文字,利于搜索引擎的搜索。viewport: 它提供有关视口初始大小的提示,仅供移动设备使用。content表示对于移动端的一些设置。Value可能值描述width一个正整数或者字符串 device-width以pixels(像素)为单位, 定义viewport(视口)的宽度。height一个正整数或者字符串 device-height以pixels(像素)为单位, 定义viewport(视口)的高度。initial-scale一个0.0 到10.0之间的正数定义设备宽度(纵向模式下的设备宽度或横向模式下的设备高度)与视口大小之间的缩放比率。maximum-scale一个0.0 到10.0之间的正数定义缩放的最大值;它必须大于或等于minimum-scale的值,不然会导致不确定的行为发生。minimum-scale一个0.0 到10.0之间的正数定义缩放的最小值;它必须小于或等于maximum-scale的值,不然会导致不确定的行为发生。user-scalable一个布尔值(yes 或者no)如果设置为no,用户将不能放大或缩小网页。默认值为yes。如果设置了 http-equiv 属性,meta 元素则是编译指令,提供的信息与类似命名的HTTP头部相同。常用的值:content-type,refresh(刷新),expires (期限)content-type,设置的content为text/html;charset=utf-8refresh, 让网页多长时间(秒)刷新自己,或在多长时间后让网页自动链接到其它网页。设置的content如果只有一个整数,则表示间隔多少秒刷新一次网页,如果还设置了url,则表示间隔多少秒跳转到指定的url处。<meta http-equiv="refresh" content="2; url=http://39.97.212.45:3000/" >expires, 表示指定缓存文档的过期时间,一旦网页过期后,就必须从服务器中重新请求。content设置的是过期时间。格式必须是GMT<meta http-equiv="expires" content="Wed, 17 Jun 2020 00:00:00 GMT" >如果设置了 charset 属性,meta 元素是一个字符集声明,告诉文档使用哪种字符编码。<meta charset="utf-8" > //表示网页编码为utf-8参考MDN知乎link标签rel: 表示链接方式与包含它的文档之间的关系。icon,表示引入文档图标stylesheet,表示引入css资源preload,表示浏览器应该预加载该资源 ,并且这个属性的使用必须得依赖as,as的值表示加载的内容的类型href:引入的链接。media:表示该链接在什么条件下生效。disabled:表示禁用引入的链接。dns-prefetch: 此属性标识下一个导航可能需要的资源,用户代理应检索该资源。 这允许用户代理在将来请求资源时更快地做出响应。title:属性在元素上有特殊的语义。当用于stylesheet时,它定义了一个首选样式表或备用样式表。不正确地使用它可能会导致样式表被忽略。如果link标签都有title属性那么他只会加载第一个link引入的样式文件。如果有link标签没有title,则表示永久生效的样式文件,而且后引入的覆盖前面引入的。<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!--这个样式生效-> <link rel="stylesheet" href="./css/style3.css" title="base3"> <!-- 这里的样式覆盖了前面的样式。并且始终生效 --> <link rel="stylesheet" href="./css/style1.css"> <!-- 这个样式不生效-> <link rel="stylesheet" href="./css/style2.css" title="base2"> <!-- <link rel="stylesheet" href="./css/style3.css" title="base3"> --> <title>Document</title> </head> <body> <div></div> <span>测试文字</span> </body>参考至 MDNCSS性能优化内联首屏关键CSS(Critical CSS)内联CSS能够使浏览器开始页面渲染的时间提前,因为在HTML下载完成之后就能渲染了。不过内联CSS有一个缺点,内联之后的CSS不会进行缓存,每次都会重新下载。不过如上所说,如果我们将内联后的文件大小控制在了14.6kb以内,这似乎并不是什么大问题。异步加载css通过js动态添加标签。// 创建link标签 const myCSS = document.createElement( "link" ); myCSS.rel = "stylesheet"; myCSS.href = "mystyles.css"; // 插入到header的最后位置 document.head.insertBefore( myCSS, document.head.childNodes[ document.head.childNodes.length - 1 ].nextSibling );去除无用CSS通过Uncss库。有效的使用css选择器保持简单,不要使用嵌套过多过于复杂的选择器。通配符和属性选择器效率最低,需要匹配的元素最多,尽量避免使用。不要使用类选择器和ID选择器修饰元素标签,如p.a,这样多此一举,还会降低效率。不要为了追求速度而放弃可读性与可维护性。不要使用@import引入css文件首先,使用@import引入CSS会影响浏览器的并行下载。使用@import引用的CSS文件只有在引用它的那个css文件被下载、解析之后,浏览器才会知道还有另外一个css需要下载,这时才去下载,然后下载后开始解析、构建render tree等一系列操作。这就导致浏览器无法并行下载所需的样式文件。分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用场display: none (不占空间,不能点击)(场景,显示出原来这里不存在的结构)visibility: hidden(占据空间,不能点击)(场景:显示不会导致页面结构发生变动,不会撑开)opacity: 0(占据空间,可以点击)(场景:可以跟transition搭配)结构: : 会让元素完全从渲染树中消失,渲染的时候不占据任何空间, 不能点击, visibility: hidden:不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,不能点击 opacity: 0: 不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,可以点击继承: display: none和opacity: 0:是非继承属性,子孙节点消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示。 visibility: hidden:是继承属性,子孙节点消失由于继承了hidden,通过设置visibility: visible;可以让子孙节点显式。性能: displaynone : 修改元素会造成文档回流,读屏器不会读取display: none元素内容,性能消耗较大 : 修改元素只会造成本元素的重绘,性能消耗较少读屏器读取visibility: hidden元素内容 opacity: 0 : 修改元素会造成重绘,性能消耗较少联系:它们都能让元素不可见边框背景border-image/* border-image: image-source image-height(如果是数值的话是边框宽度的倍数) image-width image-repeat */<style> .box1 { border: 30px solid transparent; padding: 20px; border-image: url("https://mdn.mozillademos.org/files/4127/border.png") 30; .box2 { border: 30px solid transparent; padding: 20px; border-image: linear-gradient(red 49%, green 50%) 30; </style> </head> <body> <div class="box1"></div> <div class="box2"></div> </body>选择器的权重CSS基本选择器包含ID选择器、类选择器、标签选择器、通配符选择器。 正常情况下,一般都能答出!important > 行内样式 > ID选择器 > 类选择器 > 标签选择器 > 通配符选择器。下三角1.利用border2.clip-path.triangle { width: 30px; height: 30px; background: red; clip-path: polygon(0px 0px, 0px 30px, 30px 0px); // 将坐标(0,0),(0,30),(30,0)连成一个三角形 transform: rotate(225deg); // 旋转225,变成下三角 }clip-path/* 前面的四个值,指的是距离兄弟元素的距离。为上右下左 。后面的值表示插入的长方形四个角的弧度。*/ clip-path: inset(35% 35% 35% 35% round 0 70% 0 70%); /* 半径以一个以百分比表示的值将以公式 sqrt(width^2+height^2)/sqrt(2)计算,其中width与height为相关盒模型的宽与高。 圆心位置是以相关盒模型的宽高决定的。x:宽度换算。y:高度换算。*/ clip-path: circle(25% at 50% 50%); /* 半径和圆心位置以百分比表示的长度将把盒模型的宽与高作为参照,宽作为 rx 的参照值,高作为 ry 的参照值。 */ clip-path: ellipse(25% 25% at 50% 50%); /* 百分比表示的值x坐标是以模型和的宽度为参考的。y坐标是以模型和的高度做参考的。 */ clip-path: polygon(50% 0, 25% 50%, 75% 50%);1rem、1em、1vh、1px各自代表的含义?remrem是全部的长度都相对于根元素元素。通常做法是给html元素设置一个字体大小,然后其他元素的长度单位就为rem。em子元素字体大小的em是相对于父元素字体大小元素的width/height/padding/margin用em的话是相对于该元素的font-sizevw/vh全称是 Viewport Width 和 Viewport Height,视窗的宽度和高度,相当于 屏幕宽度和高度的 1%,不过,处理宽度的时候%单位更合适,处理高度的 话 vh 单位更好。pxpx像素(Pixel)。相对长度单位。像素px是相对于显示器屏幕分辨率而言的。一般电脑的分辨率有{1920*1024}等不同的分辨率1920*1024 前者是屏幕宽度总共有1920个像素,后者则是高度为1024个像素伪类和伪元素的区别伪类:就是选择普通选择器不能选择的元素状态。由于状态的变化是⾮静态的,所以元素达到⼀个特定状态时,它可能得到⼀个伪类的样式;当状态改变时,它⼜会失去这个样式。使用单引号表示伪元素:设置文档流不存在的内容,是虚拟容器。核⼼就是需要创建通常不存在于⽂档中的元素。以双引号来表示相同之处:伪类和伪元素都不出现在源⽂件和DOM树中。也就是说在html源⽂件中是看不到伪类和伪元素的。不同之处:伪类其实就是基于普通DOM元素⽽产⽣的不同状态,他是DOM元素的某⼀特征。伪元素能够创建在DOM树中不存在的抽象对象,⽽且这些抽象对象是能够访问到的。CSS命名规范BEM命名其中,block表示的是独立的分块或组件;独立就意味着,把这一部分放到其他部分也可以正常展示与使用,它不会依赖其父元素或兄弟元素。element表示每个block中更细粒度的元素;modifier则通常会用来表示该block或者element不同的类型和状态。.block {} .block__element {} .block--modifier {} .block__element--modifier {}vertical-alignvertical-align 用来指定行内元素(inline)或表格单元格(table-cell)元素的垂直对齐方。只能是inline和table-cell元素。不能是line-block。所以如果使图片居中,我们只能设置他的类型为table-cell。div { display: table-cell; height: 500px; width: 900px; border: 1px solid black; text-align: center; /* CSS 的属性 vertical-align 用来指定行内元素(inline)或表格单元格(table-cell)元素的垂直对齐方 */ vertical-align: middle; }text-aligntext-align CSS属性定义行内内容(例如文字)如何相对它的块父元素对齐。text-align 并不控制块元素自己的对齐,只控制它的行内内容的对齐。link和@import的区别@import中的样式优先级最低。内部样式也会覆盖他。Link 属于 html 标签,而@import 是 CSS 中提供的在页面加载的时候,link 会同时被加载,而@import 引用的 CSS 会在页面加载完成后才会加 载引用的 CSS@import 只有在 ie5 以上才可以被识别,而 link 是 html 标签,不存在浏览器兼容性问题Link 引入样式的权重大于@import 的引用(@import 是将引用的样式导入到当前的页面中)Doctype 的作用?严格模式与混杂模式的区别?用于告知浏览器该以何种模式来渲染文档严格模式下:页面排版及 JS 解析是以该浏览器支持的最高标准来执行。混杂模式:不严格按照标准执行,主要用来兼容旧的浏览器,向后兼容。轻松切换图片img:hover{ content: url(path); }content 改变的仅仅是视觉呈现,当我们鼠标右键或其他形式保存这张图片时,所保存的还是原来 src 对应的图片。这种方法还可以用在官网标志上。由于使用 conetnt 生成图片无法设置图片的尺寸,要想在移动端使用该技术,建议使用SVG图片。