1、最常用之——display: none;
给元素设置display: none;后,元素会从页面中彻底消失,它原本占据的空间会被其他元素占有,会造成浏览器的回流与重绘。
2、最常用之——visibility: hidden;
给元素设置visibility: hidden;后,元素会从页面中消失,它原本占据的空间会被保留,会造成浏览器的重绘,适用于希望元素隐藏又不影响页面布局的场景。
3、隐身大法——opacity: 0;
给元素设置opacity: 0;后,元素变成透明的我们肉眼就看不到了,所以原本占据的空间还在
opacity:设置 div 元素的不透明级别
4、设置盒模型属性为0
将height、width、padding、border、margin等盒模型属性的值全设为0,如果元素內还有子元素或内容,还应overflow: hidden;来隐藏子元素。
.box1 {
width: 0;
height: 0;
padding: 0;
border: 0;
margin: 0;
overflow: hidden;
}
5、设置元素绝对定位与top、right、bottom、left等将元素移出屏幕
.box1 {
position: absolute;
left: 100%;
}
或:
.box1 {
position: absolute;
top: 9999px;
}
v-if: ==display:none
v-show:==visibility:hidden
弹性布局,一个容器中需要有规则得排列对齐得都可以用.
flex布局是一种一维布局模型,一次只能处理一个维度(一行或者一列)上的元素布局,flex布局大部分的属性都是作用于主轴的,在交叉轴上很多时候只能被动地变化。
常用场景
1、在网页中当遇到需要将父元素按照奇数进行等分,比如携程中红色区域是将父元素3等分。如果用流式布局那么就需要设置33。33%。如果用弹性布局只需要给每一个子元素设置一个属性flex:1即可快速实现。
2、当我们需要页面中多个子元素快速实现在父元素中以左右距离适中显示的时候,这个时候我们不需要设置任何的px值,只需要给元素设置justify-content:space-around就可以实现。
第一个参数表示: flex-grow 定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大
第二个参数表示: flex-shrink 定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小
第三个参数表示: flex-basis给上面两个属性分配多余空间之前, 计算项目是否有多余空间, 默认值为 auto, 即项目本身的大小
meta标签是用来描述一个HTML网页文档的属性,比如该网页的作者,日期,网页的关键字,刷新,网页等级设定等等,是文档中的最基本的元信息。
meta标签可以分为两大部分:
http-equiv 类似于http的头部协议,作用是回应给浏览器一些有用的信息来帮助正确精确的显示网页内容。常用的http-equiv 类型有:Content-Type 和 Content-Language(显示字符集的设定)。
指定HTML页面使用的字符编码
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
刷新页面
meta http-equiv="Refresh" content="5; url=http://www.cnblogs.com" />
这段代码可以用于设定网页的到期时间,一旦过期必须到服务器上重新调用(GMT时间格式)。
<meta http-equiv="Expires" content="Mon,1,May 2015 00:00:00 GMT" />
这段代码可以强制页面在当前窗口中以独立页面显示,防止自己的网页被别人当成一个frame调用。Content选项:_blank、_top、_self、_parent
<meta http-equiv="windows-Target" content="_top" />
Page-Enter、Page-Exit是页面被载入和调出时的一些特效。相应的还有:Site-Exit和Site-Enter离开和进入网站。content表示的是网页过渡的效果设置,本例中的RevealTrans是动态滤镜的一种,可以用于进入和退出的效果,Duration表示滤镜特效的持续时间(单位:s), Transition:表示滤镜的类型,取值为0到23。
<Meta http-equiv="Page-Enter" Content="revealTrans(Duration=0.5,tansition=10" />
<Meta http-equiv="Page-Exit" Content="revealTrans(Duration=0.5,transition=12" />
name,常见的有Keyword,Description,Robots等
description中的content=" ", 是对网页概况的介绍,这些信息可能会出现在搜索结果中,因此要尽量的避免和网页内容不相关的描述。
<meta name="description" content="这是我的一篇博客" />
Keywords和description类似,也是用来描述一个网页的属性的,只不过列出的是关键词,而不是网页的摘要。各个关键词之间用逗号(英文逗号)隔开。
<meta name="keywords" content="博客,meta标签" />
很多搜索引擎都是通过放出robot/spider搜索网站,robot/spider自动在www上搜索,当发现新的网站后,这些robot/spider会检索页面中的keywords和descript,然后加入到自己的数据库中。而Robots用来告诉机器人哪些页面需要索引,哪些页面不需要,content的参数有:all, none, index, noindex, follow, nofollow 。 默认情况下是all。
All:文件将被检索,而且页面的链接可以被查询。
none: 页面不被检索,页面的链接不可以被查询。
index: 文件将被检索,让robot/spider登录。
follow: 页面的链接可以被查询。
noindex:阻止页面被列入索引,页面的链接可以被查询(不让robot/spider登录)。
nofollow: 阻止对页面中任何链接进行索引 (注,这不同于超级链接上的nofollow属性,那个属性只是组织索引单独的链接。
<meta name="Robots" content="All" />
< title></title>:简短、描述性、唯一(提升搜索引擎排名)。
<hn></hn>:h1~h6分级标题,用于创建页面信息的层级关系。
<header></header>:页眉通常包括网站标志、主导航、全站链接以及搜索框。
<nav></nav>:标记导航,仅对文档中重要的链接群使用
<main></main>:页面主要内容,一个页面只能使用一次。如果是web应用,则包围其主要功能。
<article></article>:包含像报纸一样的内容= =||是这么理解的,表示文档、页面、应用或一个独立的容器。
<section></section>:具有相似主题的一组内容,比如网站的主页可以分成介绍、新闻条目、联系信息等条块。
<aside></aside>:指定附注栏,包括引述、侧栏、指向文章的一组链接、广告、友情链接、相关产品列表等。
<footer></footer>:页脚,只有当父级是body时,才是整个页面的页脚。
作用:
1 提升可访问性;
2 SEO;
3 结构清晰,利于维护;
先来了解一下input标签的几个基本控制属性。
name属性:元素的名称,也就是name的值代表当前input元素的名字;
value属性:元素的默认值
1)当input type=“text”、“password”、"hidden"时,定义输入字段的初始值;
2)当input type=“button”、“reset”、"submit"时,定义按钮上的显示的文本;
3)当input type=“checkbox”、“radio”、"image"时,定义与输入相关联的值;
注意:input type="checkbox"和input type="radio"中必须设置value属性;value属性无法与input type="file"一通使用。
style属性:为input元素设定CSS样式;
width属性:当input type="image"时,通过width属性控制元素的宽度;
height属性:当input type="image"时,通过height属性控制元素的高度;
maxlength属性:定义input元素中可输入的最长字符数。
type=“text”:创建单行文本输入框
type=“password”:密码输入框
type=“radio”:单选按钮
type=“checkbox”:复选框
type=“button”:普通按钮
type=“submit”:提交按钮
type=“reset”:重置按钮
type=“image”:图像按钮
type=“hidden”:隐藏域
隐藏域在页面上不显示,用来存储与传递表单的值,当用户提交表单时,隐藏域的内容会一起提交给处理程序。
type=“file”:文件域
HTML5新增input type属性
type=“url”:输入URL字段
type=“tel”:用来输入电话号码
type=“search”:搜索字符串
type=“email”:改控件用来输入"email"地址,若用户输入非email格式,那么再支持HTML5的浏览器中提交改表单时,会提示为不是合法格式。
type=“color”:颜色选择器,使用color属性能直接调用系统的颜色调节窗口,默认为黑色
type=“date”:日期控件
type=“month” 年月控件
w3c的盒子模型: padding和border不被包含在定义的width和height之内。对象的实际宽度等于设置的width值和border、padding之和,即 ( Element width = width + border + padding ) 此属性表现为标准模式下的盒模型。
IE的盒子模型: padding和border被包含在定义的width和height之内。对象的实际宽度就等于设置的width值,即使定义有border和padding也不会改变对象的实际宽度,即 ( Element width = width ) 。
ps:一般都使用标准的w3c盒子模型,如果需要使用IE的盒子模型,可以使用box-sizing属性进行修改。
w3c的盒子模型
.test1{
box-sizing:content-box;
width:200px;
padding:10px;
border:15px solid #eee;
}
IE的盒子模型
.test1{
box-sizing:border-box;
width:200px;
padding:10px;
border:15px solid #eee;
}
W3C盒模型是与IE盒模型的区别就是对宽高的定义不同。
W3C认为:宽高是内容区的宽度(只包含节点显示的具体内容)
IE认为:宽高是显示效果的实际效果(包含节点的全部内容)
BFC是一个独立的布局环境,其中的元素布局是不受外界的影响,并且在一个BFC中,块盒与行盒(行盒由一行中所有的内联元素所组成)都会垂直的沿着其父元素的边框排列。
内部的Box会在垂直方向,一个接一个地放置。
Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠。
每个盒子(块盒与行盒)的margin box的左边,与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
BFC的区域不会与float box重叠。
BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
计算BFC的高度时,浮动元素也参与计算。
1、float的值不是none。
2、position的值不是static或者relative。
3、display的值是inline-block、table-cell、flex、table-caption或者inline-flex
4、overflow的值不是visible
1.利用BFC避免margin重叠。
2.自适应两栏布局
3.清除浮动。
总结排序:!important > 行内样式>ID选择器 > 类选择器 > 标签 > 通配符 > 继承 > 浏览器默认属性
同一级别
同一级别中后写的会覆盖先写的样式
标准流:盒子会各占整行位置。子盒子若是标准流,父盒子虽然没有高度,但是会撑开父盒子高度。
浮动:盒子浮了起来,不会占据原来的位置,若父盒子没有定义高度,则不会撑开父盒子,父盒 子高度为0。(浮动可以让多个块级元素在一行显示,且块与块之间没有空隙,但要注意给父盒子清除浮动,否则父盒子不会被撑开)。
为什么要清除浮动呢?清除浮动的本质是什么?
清除浮动主要是为了解决父级元素因为子级浮动引起的内部高度为0的问题。
4. 使用before和after双伪元素清除浮动:(较常用)
文档流
将窗体自上而下分成一行行, 并在每行中按从左至右的顺序排放元素,即为文档流。 只有三种情况会使得元素脱离文档流,分别是:浮动、绝对定位和固定定位。
position 是CSS用来为HTML文档的一些元素提供定位的属性,定位的基本思想很简单,它允许你定义元素框相对于其正常位置应该出现的位置,或者相对于父元素、另一个元素甚至浏览器窗口本身的位置。 常规取值: 1.static(静态) 2.Relative(相对) 3.Absolute(绝对) 4.fixed(固定)
STATIC(静态)
HTML元素的默认值,不受top、bottom、left、right属性影响,元素出现在正常的文档流中RELATIVE(相对) 相对定位,
特点:不脱离文档流的布局,受top、bottom、left、right属性影响,只改变自身的位置,在文档流原先的位置遗留空白区域。定位的起始位置为此元素原先在文档流的位置。
ABSOLUTE(绝对) 绝对定位
特点:脱离文档流的布局,遗留下来的空间由后面的元素填充。定位的起始位置为最近的父元素(position不为static),否则为html文档本身。
FIXED(固定) 固定定位, 特点:类似于absolute,但不随着滚动条的移动而改变位置。元素的位置相对于浏览器窗口是固定位置。
STICKY(定位) sticky 英文字面意思是粘,粘贴,所以可以把它称之为粘性定位.position: sticky;
基于用户的滚动位置来定位。 粘性定位的元素是依赖于用户的滚动,在 position:relative 与 position:fixed
定位之间切换。 它的行为就像 position:relative; 而当页面滚动超出目标区域时,它的表现就像
position:fixed;,它会固定在目标位置。 元素定位表现为在跨越特定阈值前为相对定位,之后为固定定位。 这个特定阈值指的是
top, right, bottom 或 left 之一,换言之,指定 top, right, bottom 或 left
四个阈值其中之一,才可使粘性定位生效。否则其行为与相对定位相同。
absolute与fixed有哪些区别?
fixed与absolute最大的区别在于:absolute的”根元素“是可以被设置的,而fixed则其”根元素“固定为浏览器窗口。即当你滚动网页,其元素与浏览器窗口之间的距离是恒定不变的。
calc()能做什么?
calc()能让你给元素的做计算,你可以给一个div元素,使用百分比、em、px和rem单位值计算出其宽度或者高度,比如说“width:calc(50% + 2em)”,这样一来你就不用考虑元素DIV的宽度值到底是多少,而把这个烦人的任务交由浏览器去计算
calc()的运算规则
使用“+”、“-”、“*” 和 “/”四则运算;
可以使用百分比、px、em、rem等单位;
可以混合使用各种单位进行计算;
表达式中有“+”和“-”时,其前后必须要有空格,如"widht: calc(12%+5em)"这种没有空格的写法是错误的;
表达式中有“*”和“/”时,其前后可以没有空格,但建议留有空格。
浏览器的兼容性
浏览器对calc()的兼容性还算不错,在IE9+、FF4.0+、Chrome19+、Safari6+都得到较好支持,同样需要在其前面加上各浏览器厂商的识别符,不过可惜的是,移动端的浏览器还没仅有“firefox for android 14.0”支持,其他的全军覆没。
大家在实际使用时,同样需要添加浏览器的前缀
1.transform
它其实只是一个静态属性,需要配合transition和animation才能展现出动画效果。你可以把它看成是跟left、top等属性一样,只是一个静态样式而已。
2.transition
它属性是一个简单的动画属性,非常简单非常容易用。可以说它是animation的简化版本,是给普通做简单网页特效用的。
3.Animation
动画属性
transition是过度属性,强调过度,他的实现需要触发一个事件(比如鼠标移动上去,焦点,点击等)才执行动画。它类似于flash的补间动画,设置一个开始关键帧,一个结束关键帧。
animation是动画属性,他的实现不需要触发事件,设定好时间之后可以自己执行,且可以循环一个动画。他也类似于flash的补间动画,但是他可以设置多个关键帧(用@keyframe定义)完成动画。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<title></title>
<style>
.a{
width: 0;
height: 0;
border-left:200px solid transparent ;
border-bottom: 200px solid transparent;
border-right: 200px solid transparent;
border-top: 200px solid red;
animation: move 2s infinite ;
}
@keyframes move {
from{
transform: rotate( 0deg);
}to{
transform: rotate(360deg);
}
}
</style>
<body class="a" >
<div>
</div>
</body>
</html>
sass和less都是对css进行预处理
是一门 CSS 预处理语言,它扩展了 CSS 语言,增加了变量、Mixin、函数等特性,使 CSS 更易维护和扩展。
Less 可以运行在 Node 或浏览器端。
sass 是基于ruby环境
less的后缀是.less
sass的后缀是.sass或者.scss
CSS 本身可能很有趣,但是样式表正变得越来越大、 越来越复杂、越来越难以维护。这就是预处理可以提供帮助的地方。 Sass 为你提供了 CSS 中还不存在的特性,例如变量、 嵌套、混合、继承和其它实用的功能,让编写 CSS 代码变得再次有趣。
流式布局在CSS2时代就有,主要是靠百分比进行排版,可以在不同分辨率下显示相同的版式。
流式布局:网页中主要的划分区域的尺寸使用百分数(搭配min-*、max-*属性使用),例如,设置网页主体的宽度为80%,min-width为960px。图片也作类似处理(width:100%, max-width一般设定为图片本身的尺寸,防止被拉伸而失真)。
这种布局方式在Web前端开发的早期历史上,用来应对不同尺寸的PC屏幕(那是屏幕尺寸的差异不会太大),在当今的移动端开发也是常用布局方式,但缺点明显:宽度使用百分比定义,但是高度和文字大小等大都是用px来固定,所以在大屏幕的手机下显示效果会变成有些页面元素宽度被拉的很长,但是高度、文字大小还是和原来一样(即,这些东西无法变得“流式”),显示非常不协调。
响应式布局的关键技术是CSS3中的媒体查询,监测设备屏幕大小,通过css媒体查询来有针对性的更改页面的布局,可以监测屏幕方向(移动设备),设备类型等等,核心在于感知。在不同屏幕下可以显示不同版式。响应式是用于解决不用设备之间不用分辨率之间的兼容问题(一般是指PC,平板,手机等设备之间较大的分辨率差异)
响应式设计的目标是确保一个页面在所有终端上(各种尺寸的PC、手机、手表、冰箱的Web浏览器等等)都能显示出令人满意的效果,
嗯,因为是css3时代才有的新技术,那坑爹的IE6、7、8铁定不支持了,所以,记得使用媒体查询的时候要用js做一下小小的兼容哦!
响应式页面在头部会加上这一段代码:
<meta name="applicable-device" content="pc,mobile">
<meta http-equiv="Cache-Control" content="no-transform ">
rem,em区别:rem,em都是顺应不同网页字体大小展现而产生的。其中,em是相对其父元素,在实际应用中相对而言会带来很多不便;而rem是始终相对于html大小,即页面根元素。
结论:
1.如果只做pc端,那么静态布局(定宽度)是最好的选择;
2.如果做移动端,且设计对高度和元素间距要求不高,那么弹性布局(rem+js)是最好的选择,一份css+一份js调节font-size搞定;
3.如果pc,移动要兼容,而且要求很高那么响应式布局还是最好的选择,前提是设计根据不同的高宽做不同的设计,响应式根据媒体查询做不同的布局。
新建全局样式表
新建 global.css 文件。
在当前 单页面中添加一个新的style标签
使用 /deep/ 深度修改标签样式
找到需要修改的 ElementUI 标签的类名,然后在类名前加上 /deep/ ,可以强制修改默认样式。这种方式可以直接用到有 scoped 属性的 style 标签中。
// 修改级联选择框的默认宽度
/deep/ .el-cascader {
width: 100%;
}
总结
第一种全局引入css文件的方式,适合于对elementUI整体的修改,比如整体配色的修改;
第二种添加一个style标签的形式,也能够实现修改默认样式的效果,但实际上因为是修改了全局的样式,因此在不同的vue组件中修改同一个样式有可能会有冲突。
第三种方式通过 /deep/ 的方式可以很方便的在vue组件中修改默认样式,也不会于其他页面有冲突。
第四种方式局限性比较大,可以使用,但不推荐使用
文档对象模型(Document Object Model,简称DOM)。我个人认为他就是将,通过浏览器的一些规则解析后,在渲染成我们能够看得见的页面,这整个过程就是DOM。
它的过程分为三个阶段: DOM构造(DOM树构造)、布局、绘制页面
1.DOM构造
浏览器解析html,通过一些规则构建一颗DOM树,这棵树决定了它的关联规则
2.布局
浏览器按照从上到下,从左到右的顺序读取DOM树上的每一个文档节点
3.绘制页面
布局完成后,实际上页面上是看不到任何内容的。布局只是将每一个节点放到相应的位置上 ,但是没有变化。这一步的就是将内容绘制出来,完整渲染到浏览器页面上
概述:
事件代理(Event Delegation),又称之为事件委托。是JavaScript中常用绑定事件的常用技巧。顾名思义,“事件代理”即是把原本需要绑定在子元素的响应事件(click、keydown…)委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。
举个通俗的例子
比如一个宿舍的同学同时快递到了,一种方法就是他们一个个去领取,还有一种方法就是把这件事情委托给宿舍长,让一个人出去拿好所有快递,然后再根据收件人一
一分发给每个宿舍同学;在这里,取快递就是一个事件,每个同学指的是需要响应事件的 DOM
元素,而出去统一领取快递的宿舍长就是代理的元素,所以真正绑定事件的是这个元素,按照收件人分发快递的过程就是在事件执行中,需要判断当前响应的事件应该匹配到被代理元素中的哪一个或者哪几个。
前面提到事件委托的原理是DOM元素的事件冒泡,那么事件冒泡是什么呢?
一个事件触发后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段
如上图所示,事件传播分成三个阶段:
捕获阶段:从window对象传导到目标节点(上层传到底层)称为“捕获阶段”(capture phase),捕获阶段不会响应任何事件;
目标阶段:在目标节点上触发,称为“目标阶段”
冒泡阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)。事件代理即是利用事件冒泡的机制把里层所需要响应的事件绑定到外层;
【1】可以大量节省内存占用,减少事件注册,比如在ul上代理所有li的click事件就非常棒
<ul id="list">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
......
<li>item n</li>
</ul>
// ...... 代表中间还有未知数个 li
如上面代码所示,如果给每个li列表项都绑定一个函数,那对内存的消耗是非常大的,因此较好的解决办法就是将li元素的点击事件绑定到它的父元素ul身上,执行事件的时候再去匹配判断目标元素
【2】可以实现当新增子对象时无需再次对其绑定(动态绑定事件)
假设上述的例子中列表项li就几个,我们给每个列表项都绑定了事件;
在很多时候,我们需要通过 AJAX 或者用户操作动态的增加或者删除列表项li元素,那么在每一次改变的时候都需要重新给新增的元素绑定事件,给即将删去的元素解绑事件;
如果用了事件委托就没有这种麻烦了,因为事件是绑定在父层的,和目标元素的增减是没有关系的,执行到目标元素是在真正响应执行事件函数的过程中去匹配的;所以使用事件在动态绑定事件的情况下是可以减少很多重复工作的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.father{
position: absolute;
top: 100px;
left: 200px;
width: 500px;
}
.title{
width: 200px;
height: 200px;
background-color: red;
cursor: move;
}
.content{
width: 200px;
height: 200px;
background-color: yellow;
}
</style>
</head>
<body>
<div class="father">
<div class="title">
拖动我
</div>
<div class="content">
内容
</div>
</div>
<script>
var dragbox=function (drag, wrap){
// drag 拖拽头 wrap父盒子
// 第一步,获取元素样式
function getCss(ele, prop) {
return parseInt(window.getComputedStyle(ele)[prop]);
}
// 定义坐标记录
var initX,
initY,
dragable = false,
wrapLeft = getCss(wrap, "left"),
wrapRight = getCss(wrap, "top");
// 给拖拽头添加监听事件
drag.addEventListener("mousedown", function (e) {
dragable = true;
initX = e.clientX;
initY = e.clientY;
}, false);
document.addEventListener("mousemove", function (e) {
if (dragable === true ) {
let nowX = e.clientX
nowY = e.clientY
disX = nowX - initX
disY = nowY - initY
wrap.style.left = wrapLeft + disX + "px"
wrap.style.top = wrapRight + disY + "px"
}
});
//
// 如果拖动过 则重置
drag.addEventListener("mouseup", function (e) {
dragable = false;
wrapLeft = getCss(wrap, "left");
wrapRight = getCss(wrap, "top");
}, false);
};
dragbox(document.querySelector(".title"),document.querySelector(".father"))
</script>
</body>
</html>
重绘:当元素的外观或外观可见性(visibility)发生变化时会触发重绘
回流:render树中的部分或全部因为元素的规模尺寸、布局、隐藏等改变,需要重新计算render树。
避免回流和重绘方法
减少回流、重绘其实就是需要减少对render tree的操作,并减少对一些style信息的请求,尽量利用好浏览器的优化策略
(1) 避免操作DOM,创建一个documentFragment或div,在它上面应用所有DOM操作,最后再把它添加到window.document。也可以在一个display:none的元素上进行操作,最终把它显示出来。因为display:none上的DOM操作不会引发回流和重绘。
(2) 让要操作的元素进行"离线处理",处理完后一起更新,这里所谓的"离线处理"即让元素不存在于render tree中。如读取offsetLeft等属性。
(3) 尽可能在DOM树的末端改变class ,尽可能在DOM树的里面改变class,可以限制回流的范围,使其影响尽可能少的节点。
(4) 避免设置多层内联样式,因为每一个都会造成回流,样式合并在一个外部类,这样当该元素的class属性被操作时,只会产生一个reflow。
(5) 将需要多次回流的元素position属性设为absolute或fixed,这样该元素就会脱离文档流,它的变化不会影响其他元素变化。比如动画效果应用到position属性为absolute或fixed的元素上。
(6) 牺牲平滑度换取速度,动画元素每次移动3像素可能在非常快的机器上看起来平滑度低了,但它不会导致CPU在较慢的机器和移动设备中抖动
(7) 避免使用table布局,在布局完全建立之前,table需要很多关口,table是可以影响之前已经进入的DOM元素的显示的元素。即使一些小的变化和会导致table中所有其他节点回流。
回流时,浏览器会使渲染树中受影响的部分失效,然后重新构造这部分的render树。完成回流之后浏览器会重新布局、绘制受影响的部分到屏幕中,该过程就是重绘。所以回流必定会引起重绘,但重绘不一定引起回流。
每个页面至少需要一次回流,就是页面第一次加载时。回流变化涉及到部分页面(或整个页面)的布局。一个元素的回流导致其所有子元素以及DOM中紧随其后的祖先元素和其子元素都发生回流。
执行顺序 先回流后重绘
什么是loader
loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中
什么是plugin
在webpack运行的生命周期中会广播出许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的API改变输出结果。
loader和plugin的区别
对于loader,它是一个转换器,将A文件进行编译形成B文件,这里操作的是文件,比如将A.scss转换为A.css,单纯的文件转换过程
plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务
css-loader:
css-loader 主要用于处理图片路径(包括导入 css 文件的路径),并且会将 css 样式打包进 js
文件中(以模块的形式打包导入)。
style-loader:
style-loader 通过
postcss-loader:
postcss-loader 我们编写 css 时为了兼容不同浏览器会加上前缀比如 -webkit 等。postcss
会帮我们自动加上前缀,不用一个一个的手动添加。
sass-loader:
加载 sass/scss 文件并且变异成 css。
vue-loader:
Vue Loader 是一个 webpack 的 loader,它允许你以一种名为单文件组件 (SFCs)的格式撰写 Vue 组件。
<template>
<div class="example">{
{
msg }}</div>
</template>
<script>
export default {
data () {
return {
msg: 'Hello world!'
}
}
}
</script>
<style>
.example {
color: red;
}
</style>
注意:
Vue Loader 的配置和其它的 loader 不太一样。除了通过一条规则将 vue-loader 应用到所有扩展名为 .vue 的文件上之外,请确保在你的 webpack 配置中添加 Vue Loader 的插件:
// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
module: {
rules: [
// ... 其它规则
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin()
]
}
这个插件是必须的! 它的职责是将你定义过的其它规则复制并应用到 .vue 文件里相应语言的块。例如,如果你有一条匹配 /.js$/ 的规则,那么它会应用到 .vue 文件里的 块。
vue-style-loader:
vue-style-loader 是基于 style-loader 的,与 style-loader 相似,你可以链式的加在
css-loader 后面,通过标签动态的将 css 注入到 document
中。因为它会作为一个内置的依赖被vue-loader 使用,所以通常你不需要自己手动的配置它。 但是如果要支持 Vue SSR
的话,你最好使用 vue-style-loader。当我们以 node 为 target 打包,所有渲染的组件里的 样式会被收集起来并且以
context.styles 暴露给 Vue render context。这样你就可以简单的把样式插入到标签中了。
clean-webpack-plugin:
由于一些历史遗留代码会导致我们的 /dist 目录相当杂乱,webpack打包会生成文件,然后将这些文件放置在 /dist 文件中,但是 webpack 无法追踪到哪些文件实际在项目中用到的。
通常,在每次构建前清理 /dist 文件夹,是比较推荐的做法,因此只会生成用到的文件。clean-webpack-plugin 就很符合这种场景。通过配置,我们可以实现每次构建前清理 /dist 文件夹。当然,它作为一个普及的管理插件,还可以清除些别的,比如日志啥的。
mini-css-extract-plugin:
mini-css-extract-plugin 将 css 提取为独立的文件,对每个包含 css 的js文件都会
创建一个 css 文件。支持按需加载 css 和 sourceMap。暂不支持 HMR。
这个插件应该只用在 production 配置中,并且在 loaders 链中不适用 style-loader,特别是在开发中使用 HMR,因为这个插件暂不支持 HMR。
对webpack的了解
1、webpack是一个打包模块化javascript的工具,在webpack里一切文件皆模块,通过loader转换文件,通过plugin注入钩子,最后输出由多个模块组合成的文件,webpack专注构建模块化
2、WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用
webpack的打包原理
1、 webpack中每个模块有一个唯一的id,是从0开始递增的。整个打包后的bundle.js是一个匿名函数自执行。参数则为一个数组。数组的每一项都为个function。function的内容则为每个模块的内容,并按照require的顺序排列
2、我们看到_webpack_require是模块加载函数,接收模块id(对,webpack中每个模块都会有一个独一无二的id,其实也就是在IIFE传参数组中的索引值(0,1,2…)
3、a依赖b,所以在a中调用webpack加载模块的函数
webpack的构建流程
1、解析webpack配置参数,合并从shell传入和webpack.config.js文件里配置的参数,生产最后的配置结果。
2、注册所有配置的插件,好让插件监听webpack构建生命周期的事件节点,以做出对应的反应。
3、从配置的entry入口文件开始解析文件构建AST语法树,找出每个文件所依赖的文件,递归。
4、在解析文件递归的过程中根据文件类型和loader配置找出合适的loader用来对文件进行转换。
5、递归完后得到每个文件的最终结果,根据entry配置生成代码块chunk。
输出所有chunk到文件系统。
1.不一样的变量声明:const和let
ES6推荐使用let声明局部变量,相比之前的var(无论声明在何处,都会被视为声明在函数的最顶部)
let和var声明的区别:
var x = '全局变量';
{
let x = '局部变量';
console.log(x); // 局部变量
}
console.log(x); // 全局变量
let表示声明变量,而const表示声明常量,两者都为块级作用域;const 声明的变量都会被认为是常量,意思就是它的值被设置完成后就不能再修改了:
const a = 1
a = 0 //报错
如果const的是一个对象,对象所包含的值是可以被修改的。抽象一点儿说,就是对象所指向的地址没有变就行:
const student = {
name: 'cc' }
student.name = 'yy';// 不报错
student = {
name: 'yy' };// 报错
有几个点需要注意:
let 关键词声明的变量不具备变量提升(hoisting)特性 let 和 const 声明只在最靠近的一个块中(花括号内)有效 当使用常量
const 声明时,请使用大写变量,如:CAPITAL_CASING const 在声明时必须被赋值
2.模板字符串
在ES6之前,我们往往这么处理模板字符串:
通过“\”和“+”来构建模板
$("body").html("This demonstrates the output of HTML \
content to the page, including student's\
" + name + ", " + seatNumber + ", " + sex + " and so on.");
而对ES6来说
1、基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定;
2、ES6反引号(``)直接搞定;
$("body").html(`This demonstrates the output of HTML content to the page,
including student's ${
name}, ${
seatNumber}, ${
sex} and so on.`);
3.箭头函数(Arrow Functions)
ES6 中,箭头函数就是函数的一种简写形式,使用括号包裹参数,跟随一个 =>,紧接着是函数体;
箭头函数最直观的三个特点。
1、不需要 function 关键字来创建函数
2、省略 return 关键字
3、继承当前上下文的 this 关键字
// ES5
var add = function (a, b) {
return a + b;
};
// 使用箭头函数
var add = (a, b) => a + b;
// ES5
[1,2,3].map((function(x){
return x + 1;
}).bind(this));
// 使用箭头函数
[1,2,3].map(x => x + 1);
4. 函数的参数默认值
在ES6之前,我们往往这样定义参数的默认值:
// ES6之前,当未传入参数时,text = 'default';
function printText(text) {
text = text || 'default';
console.log(text);
}
// ES6;
function printText(text = 'default') {
console.log(text);
}
printText('hello'); // hello
printText();// default
5.Spread / Rest 操作符
Spread / Rest 操作符指的是 …,具体是 Spread 还是 Rest 需要看上下文语境。
当被用于迭代器中时,它是一个 Spread 操作符:
function foo(x,y,z) {
console.log(x,y,z);
}
let arr = [1,2,3];
foo(...arr); // 1 2 3
当被用于函数传参时,是一个 Rest 操作符:当被用于函数传参时,是一个 Rest 操作符:
function foo(...args) {
console.log(args);
}
foo( 1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
6.对象和数组解构
// 对象
const student = {
name: 'Sam',
age: 22,
sex: '男'
}
// 数组
// const student = ['Sam', 22, '男'];
// ES5;
const name = student.name;
const age = student.age;
const sex = student.sex;
console.log(name + ' --- ' + age + ' --- ' + sex);
// ES6
const {
name, age, sex } = student;
console.log(name + ' --- ' + age + ' --- ' + sex);
7.对象超类
ES6 允许在对象中使用 super 方法:
var parent = {
foo() {
console.log("Hello from the Parent");
}
}
var child = {
foo() {
super.foo();
console.log("Hello from the Child");
}
}
Object.setPrototypeOf(child, parent);
child.foo(); // Hello from the Parent
// Hello from the Child
8.for…of 和 for…in
let letters = ['a', 'b', 'c'];
letters.size = 3;
for (let letter of letters) {
console.log(letter);
}
// 结果: a, b, c
let stus = ["Sam", "22", "男"];
for (let stu in stus) {
console.log(stus[stu]);
}
// 结果: Sam, 22, 男
9.ES6中的类
ES6 中支持 class 语法,不过,ES6的class不是新的对象继承模型,它只是原型链的语法糖表现形式。
函数中使用 static 关键词定义构造函数的的方法和属性:
class Student {
constructor() {
console.log("I'm a student.");
}
study() {
console.log('study!');
}
static read() {
console.log("Reading Now.");
}
}
console.log(typeof Student); // function
let stu = new Student(); // "I'm a student."
stu.study(); // "study!"
stu.read(); // "Reading Now."
类中的继承和超集:
class Phone {
constructor() {
console.log("I'm a phone.");
}
}
class MI extends Phone {
constructor() {
super();
console.log("I'm a phone designed by xiaomi");
}
}
let mi8 = new MI();
10、Promise异步
不适用于解决下一个回调依赖上一次结果的情况
适用一次性执行多个请求,各请求之间无关
用同步的方式书写异步代码
Promise.all 全部成功执行resolve
Promise.race 哪个快就执行resolve
创建一个单一的Promise
let p = new Promise(function(resolve, reject){
//异步代码
//成功:resolve
//失败:reject
if(true){
resolve(‘成功‘);
}else{
reject(‘失败‘)
}
})
p.then(function(data){
console.log(data);
},function(data){
console.log(data);
})
// 成功
创建多个Promise
let p1 = new Promise(function(resolve, reject) {
if (true) {
resolve(‘成功1‘);
} else {
reject(‘失败1‘)
}
})
let p2 = new Promise(function(resolve, reject) {
if (true) {
resolve(‘成功2‘)
} else {
reject(‘失败2‘)
}
})
Promise.all([p1, p2]).then(function(arr) {
//返回值是一个数组,顺序保存多个Promise返回值
console.log(‘p1和p2全部resolve‘);
let [data1, data2] = [arr[0], arr[1]];
console.log(data1 + data2);
}, function(data) {
//有一个失败就立即进入,不会继续执行其他Promise
console.log(‘p1和p2其中有一个reject‘);
console.log(data);
})
封装Promise
function createPromise(num) {
return new Promise(function(resolve, reject) {
if (true) {
resolve(‘成功‘ + num);
} else {
reject(‘失败1‘ + num);
}
})
}
Promise.all([
createPromise(1),
createPromise(2)
]).then(function(arr) {
//返回值是一个数组,顺序保存多个Promise返回值
console.log(‘p1和p2全部resolve‘);
let [data1, data2] = [arr[0], arr[1]];
console.log(data1 + data2);
}, function(data) {
//有一个失败就立即进入,不会继续执行其他Promise
console.log(‘p1和p2其中有一个reject‘);
console.log(data);
})
在有些情况我们也许会碰到如下场景,监听一个input框的输入事件,但是大多数情况下,我们不是为了监听用户输了什么,而是想知道用户输到什么时候结束了,所以如果实时监听键盘事件,在每次用户敲击键盘的时候去触发一个函数,这样就多了非常多无用的操作,比如window.resize事件
//在你的控制台输入以下代码
window.onresize = ()=> {
console.info('onresize');
};
可以看到 如果我们简单的变化浏览器的大小,控制台会一直打印,这并不是我们需要的,我们往往只需要知道,用户在哪里停下就可以了
1、函数防抖
function throttle(method,context){
clearTimeout(method.timer)
method.timer = setTimeout(function(){
method.call(context)
},500)
}
代码意思很明显,这个方法接受你想防抖的函数以及他的上下文,然后在500ms延时之后执行这个函数,若下一次函数在500ms内调用则清除上个定时器然后在延迟500ms执行,函数防抖有效的防止了一个函数被多次调用,比如onresize,onmouseover和上述的键盘监听事件
再来试试
function fun(){
console.log('onresize')
}
function throttle(method,context){
clearTimeout(method.timer);
method.timer=setTimeout(function(){
method.call(context);
},500);
}
window.onresize = ()=>throttle(fun,window)
把这段代码拷进你的控制台然后在试试,是不是好多了
2、函数节流
相比于防抖,节流就是在让函数在特定的时间内只执行一次
// 函数节流
var canRun = true;
document.getElementById("throttle").onscroll = function(){
if(!canRun){
return
}
canRun = false
setTimeout( function () {
console.log("函数节流")
canRun = true
}, 500)
}
用一个flag让该函数在500ms内只执行一次
3、区别
他们都是可以防止一个函数被无意义的高频率调用
区别在于:
函数节流:是确保函数特定的时间内至多执行一次。
函数防抖:是函数在特定的时间内不被再调用后执行。
获取异步请求对象
// 获取异步请求对象
function getXhr(){
var xhr=null;
if(window.XMLHttpRequest){
xhr=new XMLHttprequest();
}else{
// 如果浏览器版本是IE8以下浏览器
xhr=new ActiveXObject('Microsoft.XMLHttp');
}
return xhr;}
创建ajax函数:
// url:"url路径" type:请求方式 data:请求参数类型 dataType:返回的字符串类型
function ajax({
url,type,data,dataType}){
return new Promise(function(resolve,reject){
//1. 创建异步请求对象
var xhr=getXhr();
// 备注:无需通过上面的方式,简单的创建异步请求对象的简化代码如下:
// var xhr = window.XMLHttpRequest ? new XMLHttprequest() : new ActiveXObject('Microsoft.XMLHttp');
//2.绑定监听事件
xhr.onreadystatechange=function(){
// 当异步请求状态变为4时,并且返回的状态码为200,接收响应成功
if(xhr.readyState==4&&xhr.status==200){
// 当返回接收的字符串类型为json串时,自动转换json串
if(dataType!==undefined
&&dataType.toLowerCase()==="json")
var res=JSON.parse(xhr.responseText)
else
// 否则直接获取返回的响应文本中的内容
var res=xhr.responseText
// 通过Promise,将返回的数据向后传递,相当于获取到请求数据将数据return出来
resolve(res);
}
}
// 如果请求方式为get请求,则将请求参数拼接在url后
if(type.toLowerCase()==="get"&&data!==undefined){
url+="?"+data;
}
//3.打开连接
xhr.open(type,url,true);
// 如果请求方式为post请求,则修改请求消息头
if(type.toLowerCase()==="post")
//增加:设置请求消息头
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
//4.发送请求
if(type.toLowerCase()==="post"&&data!==undefined)
xhr.send(data);
else
xhr.send(null);})
let obj = {
//这个上下文环境是window
thist: this,
names :"小张",
foo:function(){
//这个上下文环境是obj
setInterval(()=>{
//箭头函数中的this是来自父块的上下文this
console.log(this)
},1000)
}
}
this 指向es6中箭头函数的this是来自父块上下文环境,如果父块还是箭头函数,依次往上找。 而在es5中this,就是谁调用就是谁,指向不明确。
函数声明、函数表达式、匿名函数
函数声明:function fnName () {…}; 使用function关键字声明一个函数,再指定一个函数名,叫函数声明。
函数表达式 var fnName = function (){…};
使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。
匿名函数:function () {};
使用function关键字声明一个函数,但未给函数命名,所以叫匿名函数,匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等等。
函数声明和函数表达式不同之处在于:
一、Javascript引擎在解析javascript代码时会‘函数声明升’(Function declaration Hoisting)当前执行环境(作用域)上的函数声明
二、函数表达式后面可以加括号立即调用该函数,函数声明不可以,只能以fnName()形式调用 。以下是两者差别的两个例子。
fnName();
function fnName(){
...
}
//正常,因为‘提升'了函数声明,函数调用可在函数声明之前
fnName();
var fnName=function(){
...
}
//报错,变量fnName还未保存对函数的引用,函数调用必须在函数表达式之后
在理解了一些函数基本概念后,回头看看( function(){…} )()和( function (){…} () )这两种立即执行函数的写法,最初我以为是一个括号包裹匿名函数,并后面加个括号立即调用函数,当时不知道为什么要加括号,后来明白,要在函数体后面加括号就能立即调用,则这个函数必须是函数表达式,不能是函数声明。
(function(a){
console.log(a); //firebug输出123,使用()运算符
})(123);
闭包:
闭包,简单来说就是函数嵌套函数,或者说定义在一个函数内部的函数,它是将函数内部和函数外部连接起来的一座桥梁。
闭包可以用在许多地方。它的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
function f1(){
var n=999;
nAdd=function(){
n+=1}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是”nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。