什么是语义化?就是用合理、正确的标签来展现内容,好比h1~h6定义标题。javascript
http://www.daqianduan.com/6549.htmlcss
<link>
标签放在<head></head>
之间?为何最好把 JS 的<script>
标签刚好放在</body>
以前,有例外状况吗?把<link>
放在<head>
中html
把<link>
标签放在<head></head>
之间是规范要求的内容。此外,这种作法可让页面逐步呈现,提升了用户体验。将样式表放在文档底部附近,会使许多浏览器(包括 Internet Explorer)不能逐步呈现页面。一些浏览器会阻止渲染,以免在页面样式发生变化时,从新绘制页面中的元素。这种作法能够防止呈现给用户空白的页面或没有样式的内容。前端
把<script>
标签刚好放在</body>
以前vue
脚本在下载和执行期间会阻止 HTML 解析。把<script>
标签放在底部,保证 HTML 首先完成解析,将页面尽早呈现给用户。html5
例外状况是当你的脚本里包含document.write()
时。可是如今,document.write()
不推荐使用。同时,将<script>
标签放在底部,意味着浏览器不能开始下载脚本,直到整个文档(document)被解析。也许,对此比较好的作法是,<script>
使用defer
属性,放在<head>
中。java
渐进式渲染是用于提升网页性能(尤为是提升用户感知的加载速度),以尽快呈现页面的技术。node
在之前互联网带宽较小的时期,这种技术更为广泛。现在,移动终端的盛行,而移动网络每每不稳定,渐进式渲染在现代前端开发中仍然有用武之地。jquery
一些举例:nginx
DOMContentLoaded
/load
事件加载其余资源和内容。Viewport :字面意思为视图窗口,在移动web开发中使用。表示将设备浏览器宽度虚拟成一个特定的值(或计算得出),这样利于移动web站点跨设备显示效果基本一致。移动版的 Safari 浏览器最新引进了 viewport 这个 meta tag,让网页开发者来控制 viewport 的大小和缩放,其余手机浏览器也基本支持。
在移动端浏览器当中,存在着两种视口,一种是可见视口(也就是咱们说的设备大小),另外一种是视窗视口(网页的宽度是多少)。
举个例子:若是咱们的屏幕是320像素 * 480像素的大小(iPhone4),假设在浏览器中,320像素的屏幕宽度可以展现980像素宽度的内容。那么320像素的宽度就是可见视口的宽度,而可以显示的980像素的宽度就是视窗视口的宽度。
为了显示更多的内容,大多数的浏览器会把本身的视窗视口扩大,简易的理解,就是让本来320像素的屏幕宽度可以容下980像素甚至更宽的内容(将网页等比例缩小)。
当涉及到DOM节点的布局属性发生变化时,就会从新计算该属性,浏览器会从新描绘相应的元素,此过程叫Reflow(回流或重排)。
当影响DOM元素可见性的属性发生变化 (如 color) 时, 浏览器会从新描绘相应的元素, 此过程称为Repaint(重绘)。所以重排必然会引发重绘。
https://harttle.land/2015/08/11/reflow-repaint.html
http://www.blueidea.com/tech/web/2011/8365.asp
https://blog.csdn.net/lhjuejiang/article/details/80795081
https://baijiahao.baidu.com/s?id=1593097105869520145&wfr=spider&for=pc
不一样产品,不一样标准,不一样实现方式
2.作到什么程度
3..如何作
条件注释、CSS Hack、js 能力检测作一些修补
优雅降级 (graceful degradation): 一开始就构建完整的功能,而后再针对低版本浏览器进行兼容。
https://github.com/jirengu/frontend-interview/issues/35
doctype是一种标准通用标记语言的文档类型声明,目的是告诉标准通用标记语言解析器要使用什么样的文档类型定义(DTD)来解析文档。
<!DOCTYPE>声明是用来指示web浏览器关于页面使用哪一个HTML版本进行编写的指令。
<!DOCTYPE>声明必须是HTML文档的第一行,位于html标签以前。
浏览器自己分为两种模式,一种是标准模式,一种是怪异模式,浏览器经过doctype来区分这两种模式,doctype在html中的做用就是触发浏览器的标准模式,若是html中省略了doctype,浏览器就会进入到Quirks模式的怪异状态,在这种模式下,有些样式会和标准模式存在差别,而html标准和dom标准值规定了标准模式下的行为,没有对怪异模式作出规定,所以不一样浏览器在怪异模式下的处理也是不一样的,因此必定要在html开头使用doctype。
一个行内元素只占据它对应标签的边框所包含的空间
通常状况下,行内元素只能包含数据和其余行内元素
b, big, i, small, tt abbr, acronym, cite, code, dfn, em, kbd, strong, samp, var a, bdo, br, img, map, object, q, script, span, sub, sup button, input, label, select, textarea
占据一整行,高度、行高、内边距和外边距均可以改变,能够容纳块级标签和其余行内标签
header,form,ul,ol,table,article,div,hr,aside,figure,canvas,video,audio,footer
若是遇到加载缓慢的第三方内容如图标和广告,这些问题能够由iframe来解决。
iframe页面会增长服务器的http请求
label
标签一般是写在表单内,它关联一个控件,使用 label
能够实现点击文字选取对应的控件。
<input type="checkbox" id="test"> <label for="test" >test</label>
将不想要自动完成的 form
或 input
设置为 autocomplete=off
Document Object Model,文档对象模型
DOM 是为了操做文档出现的 API,document 是其的一个对象
DOM和文档有关,这里的文档指的是网页,也就是html文档。DOM和浏览器无关,他关注的是网页自己的内容。
Browser Object Model,浏览器对象模型
BOM 是为了操做浏览器出现的 API,window 是其的一个对象
window 对象既为 javascript 访问浏览器提供API,同时在 ECMAScript 中充当 Global 对象
浏览器经过优先级规则,判断元素展现哪些样式。优先级经过 4 个维度指标肯定,咱们假定以a、b、c、d
命名,分别表明如下含义:
a
表示是否使用内联样式(inline style)。若是使用,a
为 1,不然为 0。b
表示 ID 选择器的数量。c
表示类选择器、属性选择器和伪类选择器数量之和。d
表示标签(类型)选择器和伪元素选择器之和。优先级的结果并不是经过以上四个值生成一个得分,而是每一个值分开比较。a、b、c、d
权重从左到右,依次减少。判断优先级时,从左到右,一一比较,直到比较出最大值,便可中止。因此,若是b
的值不一样,那么c
和d
无论多大,都不会对结果产生影响。好比0,1,0,0
的优先级高于0,0,10,10
。
当出现优先级相等的状况时,最晚出现的样式规则会被采纳。若是你在样式表里写了相同的规则(不管是在该文件内部仍是其它样式文件中),那么最后出现的(在文件底部的)样式优先级更高,所以会被采纳。
在写样式时,我会使用较低的优先级,这样这些样式能够轻易地覆盖掉。尤为对写 UI 组件的时候更为重要,这样使用者就不须要经过很是复杂的优先级规则或使用!important
的方式,去覆盖组件的样式了。
margin
、padding
、font-size
这些样式所有置成同样。你将必须从新定义各类元素的样式。当须要实现很是个性化的网页设计时,我会选择重置的方式,由于我要写不少自定义的样式以知足设计需求,这时候就再也不须要标准化的默认样式了。
Float
定位的工做原理。浮动(float)是 CSS 定位属性。浮动元素从网页的正常流动中移出,可是保持了部分的流动性,会影响其余元素的定位(好比文字会围绕着浮动元素)。这一点与绝对定位不一样,绝对定位的元素彻底从文档流中脱离。
CSS 的clear
属性经过使用left
、right
、both
,让该元素向下移动(清除浮动)到浮动元素下面。
若是父元素只包含浮动元素,那么该父元素的高度将塌缩为 0。咱们能够经过清除(clear)从浮动元素后到父元素关闭前之间的浮动来修复这个问题。
有一种 hack 的方法,是自定义一个.clearfix
类,利用伪元素选择器::after
清除浮动。另外还有一些方法,好比添加空的<div></div>
和设置浮动元素父元素的overflow
属性。与这些方法不一样的是,clearfix
方法,只须要给父元素添加一个类,定义以下:
.clearfix::after { content: ''; display: block; clear: both; }
值得一提的是,把父元素属性设置为overflow: auto
或overflow: hidden
,会使其内部的子元素造成块格式化上下文(Block Formatting Context),而且父元素会扩张本身,使其可以包围它的子元素。
z-index
属性,并说明如何造成层叠上下文(stacking context)。CSS 中的z-index
属性控制重叠元素的垂直叠加顺序。z-index
只能影响position
值不是static
的元素。
没有定义z-index
的值时,元素按照它们出如今 DOM 中的顺序堆叠(层级越低,出现位置越靠上)。非静态定位的元素(及其子元素)将始终覆盖静态定位(static)的元素,而无论 HTML 层次结构如何。
层叠上下文是包含一组图层的元素。 在一组层叠上下文中,其子元素的z-index
值是相对于该父元素而不是 document root 设置的。每一个层叠上下文彻底独立于它的兄弟元素。若是元素 B 位于元素 A 之上,则即便元素 A 的子元素 C 具备比元素 B 更高的z-index
值,元素 C 也永远不会在元素 B 之上.
每一个层叠上下文是自包含的:当元素的内容发生层叠后,整个该元素将会在父层叠上下文中按顺序进行层叠。少数 CSS 属性会触发一个新的层叠上下文,例如opacity
小于 1,filter
不是none
,transform
不是none
。
块格式上下文(BFC)是 Web 页面的可视化 CSS 渲染的部分,是块级盒布局发生的区域,也是浮动元素与其余元素交互的区域。
一个 HTML 盒(Box)知足如下任意一条,会建立块格式化上下文:
float
的值不是none
.position
的值不是static
或relative
.display
的值是table-cell
、table-caption
、inline-block
、flex
、或inline-flex
。overflow
的值不是visible
。在 BFC 中,每一个盒的左外边缘都与其包含的块的左边缘相接。
两个相邻的块级盒在垂直方向上的边距会发生合并(collapse)。更多内容请参考边距合并(margin collapsing)。
div
方法:<div style="clear:both;"></div>
。.clearfix
类已经提到。overflow: auto
或overflow: hidden
方法:上文已经提到。在大型项目中,我会使用 Clearfix 方法,在须要的地方使用.clearfix
。设置overflow: hidden
的方法可能使其子元素显示不完整,当子元素的高度大于父元素时。
雪碧图是把多张图片整合到一张上的图片。它被运用在众多使用了不少小图标的网站上(Gmail 在使用)。实现方法:
background-image
、background-position
和background-size
属性。好处:
:hover
伪类中的图片,不会出现闪烁。autoprefixer
自动生成 CSS 属性前缀。autoprefixer
自动生成 CSS 属性前缀。这些方法与可访问性(a11y)有关。
visibility: hidden
:元素仍然在页面流中,并占用空间。width: 0; height: 0
:使元素不占用屏幕上的任何空间,致使不显示它。position: absolute; left: -99999px
: 将它置于屏幕以外。text-indent: -9999px
:这只适用于block
元素中的文本。即便 WAI-ARIA 是理想的解决方案,我也会采用绝对定位方法,由于它具备最少的注意事项,适用于大多数元素,并且使用起来很是简单。
screen
,你还能说出一个 @media 属性的例子吗?首先,浏览器从最右边的选择器,即关键选择器(key selector),向左依次匹配。根据关键选择器,浏览器从 DOM 中筛选出元素,而后向上遍历被选元素的父元素,判断是否匹配。选择器匹配语句链越短,浏览器的匹配速度越快。避免使用标签和通用选择器做为关键选择器,由于它们会匹配大量的元素,浏览器必需要进行大量的工做,去判断这些元素的父元素们是否匹配。
BEM (Block Element Modifier) methodology recommends that everything has a single class, and, where you need hierarchy, that gets baked into the name of the class as well, this naturally makes the selector efficient and easy to override.
BEM (Block Element Modifier)原则上建议为独立的 CSS 类命名,而且在须要层级关系时,将关系也体如今命名中,这天然会使选择器高效且易于覆盖。
搞清楚哪些 CSS 属性会触发从新布局(reflow)、重绘(repaint)和合成(compositing)。在写样式时,避免触发从新布局的可能。
优势:
缺点:
喜欢:
Dislikes:
node-sass
使用 Sass,它用 C ++ 编写的 LibSass 绑定。在 Node 版本切换时,我必须常常从新编译。@
做为前缀,容易与 CSS 关键字混淆,如@media
、@import
和@font-face
。使用@font-face
并为不一样的font-weight
定义font-family
。
这部分与上面关于编写高效的 CSS 有关。浏览器从最右边的选择器(关键选择器)根据关键选择器,浏览器从 DOM 中筛选出元素,而后向上遍历被选元素的父元素,判断是否匹配。选择器匹配语句链越短,浏览器的匹配速度越快。
例如,对于形如p span
的选择器,浏览器首先找到全部<span>
元素,并遍历它的父元素直到根元素以找到<p>
元素。对于特定的<span>
,只要找到一个<p>
,就知道'`已经匹配并中止继续匹配。
CSS 伪元素是添加到选择器的关键字,去选择元素的特定部分。它们能够用于装饰(:first-line
,:first-letter
)或将元素添加到标记中(与 content:...组合),而没必要修改标记(:before
,:after
)。
:first-line
和:first-letter
能够用来修饰文字。.clearfix
方法中,使用clear: both
来添加不占空间的元素。:before
和after
展现提示中的三角箭头。鼓励关注点分离,由于三角被视为样式的一部分,而不是真正的 DOM。若是不使用额外的 HTML 元素,只用 CSS 样式绘制三角形是不太可能的。CSS 盒模型描述了以文档树中的元素而生成的矩形框,并根据排版模式进行布局。每一个盒子都有一个内容区域(例如文本,图像等)以及周围可选的padding
、border
和margin
区域。
CSS 盒模型负责计算:
盒模型有如下规则:
width
、height
、padding
、border
和margin
决定。height
,则块级元素的高度等于其包含子元素的内容高度加上padding
(除非有浮动元素,请参阅下文)。width
,则非浮动块级元素的宽度等于其父元素的宽度减去父元素的padding
。height
是由内容的height
来计算的。width
是由内容的width
来计算的。padding
和border
不是元素width
和height
的组成部分。* { box-sizing: border-box; }
会产生怎样的效果?box-sizing: content-box
,元素的宽高只会决定内容(content)的大小。box-sizing: border-box
改变计算元素width
和height
的方式,border
和padding
的大小也将计算在内。height
= 内容(content)的高度 + 垂直方向的padding
+ 垂直方向border
的宽度width
= 内容(content)的宽度 + 水平方向的padding
+ 水平方向border
的宽度display
的属性值都有哪些?none
, block
, inline
, inline-block
, table
, table-row
, table-cell
, list-item
.inline
和inline-block
有什么区别?我把block
也加入其中,为了得到更好的比较。
block |
inline-block |
inline |
|
---|---|---|---|
大小 | 填充其父容器的宽度。 | 取决于内容。 | 取决于内容。 |
定位 | 重新的一行开始,而且不容许旁边有 HTML 元素(除非是float ) |
与其余内容一块儿流动,并容许旁边有其余元素。 | 与其余内容一块儿流动,并容许旁边有其余元素。 |
可否设置width 和height |
能 | 能 | 不能。 设置会被忽略。 |
能够使用vertical-align 对齐 |
不能够 | 能够 | 能够 |
边距(margin)和填充(padding) | 各个方向都存在 | 各个方向都存在 | 只有水平方向存在。垂直方向会被忽略。 尽管border 和padding 在content 周围,但垂直方向上的空间取决于'line-height' |
浮动(float) | - | - | 就像一个block 元素,能够设置垂直边距和填充。 |
relative
、fixed
、absolute
和static
四种定位有什么区别?通过定位的元素,其position
属性值必然是relative
、absolute
、fixed
或sticky
。
static
:默认定位属性值。该关键字指定元素使用正常的布局行为,即元素在文档常规流中当前的布局位置。此时 top, right, bottom, left 和 z-index 属性无效。relative
:该关键字下,元素先放置在未添加定位时的位置,再在不改变页面布局的前提下调整元素位置(所以会在此元素未添加定位时所在位置留下空白)。absolute
:不为元素预留空间,经过指定元素相对于最近的非 static 定位祖先元素的偏移,来肯定元素位置。绝对定位的元素能够设置外边距(margins),且不会与其余边距合并。fixed
:不为元素预留空间,而是经过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。打印时,元素会出如今的每页的固定位置。fixed 属性会建立新的层叠上下文。当元素祖先的 transform 属性非 none 时,容器由视口改成该祖先。sticky
:盒位置根据正常流计算(这称为正常流动中的位置),而后相对于该元素在流中的 flow root(BFC)和 containing block(最近的块级祖先元素)定位。在全部状况下(即使被定位元素为 table
时),该元素定位均不对后续元素形成影响。当元素 B 被粘性定位时,后续元素的位置仍按照 B 未定位时的位置来肯定。position: sticky
对 table
元素的效果与 position: relative
相同。Flex 主要用于一维布局,而 Grid 则用于二维布局。
flex容器中存在两条轴, 横轴和纵轴, 容器中的每一个单元称为flex item。
在容器上能够设置6个属性:
注意:当设置 flex 布局以后,子元素的 float、clear、vertical-align 的属性将会失效。
有六种属性可运用在 item 项目上:
CSS网格布局用于将页面分割成数个主要区域,或者用来定义组件内部元素间大小、位置和图层之间的关系。
像表格同样,网格布局让咱们可以按行或列来对齐元素。 可是,使用CSS网格可能仍是比CSS表格更容易布局。 例如,网格容器的子元素能够本身定位,以便它们像CSS定位的元素同样,真正的有重叠和层次。
响应式设计和自适应设计都以提升不一样设备间的用户体验为目标,根据视窗大小、分辨率、使用环境和控制方式等参数进行优化调整。
响应式设计的适应性原则:网站应该凭借一份代码,在各类设备上都有良好的显示和使用效果。响应式网站经过使用媒体查询,自适应栅格和响应式图片,基于多种因素进行变化,创造出优良的用户体验。就像一个球经过膨胀和收缩,来适应不一样大小的篮圈。
自适应设计更像是渐进式加强的现代解释。与响应式设计单一地去适配不一样,自适应设计经过检测设备和其余特征,从早已定义好的一系列视窗大小和其余特性中,选出最恰当的功能和布局。与使用一个球去穿过各类的篮筐不一样,自适应设计容许使用多个球,而后根据不一样的篮筐大小,去选择最合适的一个。
我倾向于使用更高分辨率的图形(显示尺寸的两倍)来处理视网膜显示。更好的方法是使用媒体查询,像@media only screen and (min-device-pixel-ratio: 2) { ... }
,而后改变background-image
。
对于图标类的图形,我会尽量使用 svg 和图标字体,由于它们在任何分辨率下,都能被渲染得十分清晰。
还有一种方法是,在检查了window.devicePixelRatio
的值后,利用 JavaScript 将<img>
的src
属性修改,用更高分辨率的版本进行替换。
translate()
而不用绝对定位?何时,状况相反。translate()
是transform
的一个值。改变transform
或opacity
不会触发浏览器从新布局(reflow)或重绘(repaint),只会触发复合(compositions)。而改变绝对定位会触发从新布局,进而触发重绘和复合。transform
使浏览器为元素建立一个 GPU 图层,但改变绝对定位会使用到 CPU。 所以translate()
更高效,能够缩短平滑动画的绘制时间。
当使用translate()
时,元素仍然占据其原始空间(有点像position:relative
),这与改变绝对定位不一样。
行内元素:和其余元素都在一行上,高度、行高及外边距和内边距都不可改变,文字图片的宽度不可改变,只能容纳文本或者其余行内元素;其中img是行元素
块级元素:老是在新行上开始,高度、行高及外边距和内边距均可控制,能够容纳内敛元素和其余元素;行元素转换为块级元素方式:display:block;
能够使用flex布局 复制下面的HTML和CSS代码 用浏览器打开能够看到效果
<div class="wrap"> <div class="div1"></div> <div class="div2"></div> </div> .wrap { display: flex; justify-content: space-between; } .div1 { min-width: 200px; } .div2 { width: 100%; background: #e6e6e6; } html, body, div { height: 100%; margin: 0; }
// 父容器 display: flex; justify-content: center; align-items: center;
// 父容器 position: relative; // 子容器 position:absolute; margin:auto; top:0; bottom:0; left:0; right:0;
// 父容器 position: relative; // 子容器 position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
<div class="box"> <div class="content"> <div class="inner"></div> </div> </div> html, body { height: 100%; width: 100%; margin: 0; } .box { display: table; height: 100%; width: 100%; } .content { display: table-cell; vertical-align: middle; text-align: center; } .inner { background-color: #000; display: inline-block; width: 200px; height: 200px; }
是否隐藏 | 是否在文档中占用空间 | 是否会触发事件 | |
---|---|---|---|
display: none | 是 | 否 | 否 |
visibile: hidden | 是 | 是 | 否 |
opacity: 0 | 是 | 是 | 是 |
利用column-count和break-inside这两个CSS3属性便可,复制以下代码便可察看效果
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> body { margin: 0; } .waterfall-container { /*分几列*/ column-count: 2; width: 100%; /* 列间距 */ column-gap: 10px; } .waterfall-item { break-inside: avoid; width: 100%; height: 100px; margin-bottom: 10px; background: #ddd; column-gap: 0; text-align: center; color: #fff; font-size: 40px; } </style> </head> <body> <div class="waterfall-container"> <div class="waterfall-item" style="height: 100px">1</div> <div class="waterfall-item" style="height: 300px">2</div> <div class="waterfall-item" style="height: 400px">3</div> <div class="waterfall-item" style="height: 100px">4</div> <div class="waterfall-item" style="height: 300px">5</div> <div class="waterfall-item" style="height: 600px">6</div> <div class="waterfall-item" style="height: 400px">7</div> <div class="waterfall-item" style="height: 300px">8</div> <div class="waterfall-item" style="height: 700px">9</div> <div class="waterfall-item" style="height: 100px">10</div> </div> </body> </html>
overflow: hidden; text-overflow:ellipsis; white-space: nowrap;
display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; // 最多显示几行 overflow: hidden;
.info-tab { position: relative; } .info-tab::after { content: ''; border: 4px solid transparent; border-top-color: #2c8ac2; position: absolute; top: 0; }
须要用到css
的object-fit
属性
div { width: 200px; height: 200px; } img { object-fit: cover; width: 100%; height: 100%; }
iframe是用来在网页中插入第三方页面,早期的页面使用iframe主要是用于导航栏这种不少页面都相同的部分,这样在切换页面的时候避免重复下载。
优势
缺点
因为不一样的浏览器,好比Internet Explorer 6,Internet Explorer 7,Mozilla Firefox等,对CSS的解析认识不同,所以会致使生成的页面效果不同,得不到咱们所须要的页面效果。
这个时候咱们就须要针对不一样的浏览器去写不一样的CSS,让它可以同时兼容不一样的浏览器,能在不一样的浏览器中也能获得咱们想要的页面效果。
这个针对不一样的浏览器写不一样的CSS code的过程,就叫CSS hack,也叫写CSS hack。
具体请看:
http://www.cnblogs.com/Renyi-Fan/p/9006084.html
外边距合并指的是,当两个垂直外边距相遇时,它们将造成一个外边距。
合并后的外边距的高度等于两个发生合并的外边距的高度中的较大者。
更详细的介绍请看:去除inline-block元素间间距的N种方法
同源策略可防止 JavaScript 发起跨域请求。源被定义为 URI、主机名和端口号的组合。此策略可防止页面上的恶意脚本经过该页面的文档对象模型,访问另外一个网页上的敏感数据。
https://zhuanlan.zhihu.com/p/41479807
跨域资源共享 CORS 阮一峰
这是我认为写得比较通俗易懂的一篇文章 直接转载过来
https://blog.csdn.net/hansexploration/article/details/80314948
PC 时代为了突破浏览器的域名并发限制。有了域名发散。
浏览器有并发限制,是为了防止DDOS攻击。
域名收敛:就是将静态资源放在一个域名下。减小DNS解析的开销。
域名发散:是将静态资源放在多个子域名下,就能够多线程下载,提升并行度,使客户端加载静态资源更加迅速。
域名发散是pc端为了利用浏览器的多线程并行下载能力。而域名收敛多用与移动端,提升性能,由于dns解析是是从后向前迭代解析,若是域名过多性能会降低,增长DNS的解析开销。
<button onclick="func()">按钮</button>
btn.onclick = function(){}
btn.addEventListener('click',function(){})
事件委托利用了事件冒泡,只指定一个事件处理程序,就能够管理某一类型的全部事件。全部用到按钮的事件(多数鼠标事件和键盘事件)都适合采用事件委托技术,
使用事件委托能够节省内存。
<ul> <li>苹果</li> <li>香蕉</li> <li>凤梨</li> </ul> // good document.querySelector('ul').onclick = (event) => { let target = event.target if (target.nodeName === 'LI') { console.log(target.innerHTML) } } // bad document.querySelectorAll('li').forEach((e) => { e.onclick = function() { console.log(this.innerHTML) } })
事件循环是一个单线程循环,用于监视调用堆栈并检查是否有工做即将在任务队列中完成。若是调用堆栈为空而且任务队列中有回调函数,则将回调函数出队并推送到调用堆栈中执行。
<input onclick="sayHi()"/> btn.onclick = function() {} btn.onclick = null
// 绑定 btn.addEventListener('click', sayHi) // 解绑 btn.removeEventListener('click', sayHi)
UI事件,当用户与页面上的元素交互时触发,如:load、scroll 焦点事件,当元素得到或失去焦点时触发,如:blur、focus 鼠标事件,当用户经过鼠标在页面执行操做时触发如:dbclick、mouseup 滚轮事件,当使用鼠标滚轮或相似设备时触发,如:mousewheel 文本事件,当在文档中输入文本时触发,如:textInput 键盘事件,当用户经过键盘在页面上执行操做时触发,如:keydown、keypress 合成事件,当为IME(输入法编辑器)输入字符时触发,如:compositionstart 变更事件,当底层DOM结构发生变化时触发,如:DOMsubtreeModified
https://www.jianshu.com/p/3acdf5f71d5b
let obj = {} obj.__proto__ === Object.prototype // true function Test(){} test.__proto__ == Test.prototype // true
全部的函数都同时拥有__proto__和protytpe属性
函数的__proto__指向本身的函数实现 函数的protytpe是一个对象 因此函数的prototype也有__proto__属性 指向Object.prototype
function func() {} func.prototype.__proto__ === Object.prototype // true
Object.prototype.__proto__指向null
Object.prototype.__proto__ // null
全部的JS对象都有一个prototype属性,指向它的原型对象。当试图访问一个对象的属性时,若是没有在该对象上找到,它还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
JS高程第3版 第6章 继承
寄生组合式继承
function SuperType(name) { this.name = name this.colors = ['red'] } SuperType.prototype.sayName = function() { console.log(this.name) } // 继承实例属性 function SubType(name, age) { SuperType.call(this, name) this.age = age } function inheritPrototype(subType, superType) { let prototype = Object.create(superType.prototype) prototype.constructor = subType subType.prototype = prototype } // 继承原型方法 inheritPrototype(SubType, SuperType) // 定义本身的原型方法 SubType.prototype.sayAge = function() { console.log(this.age) }
闭包是指有权访问另外一个函数做用域中的变量的函数。
function sayHi(name) { return () => { console.log(`Hi! ${name}`) } } const test = sayHi('xiaoming') test() // Hi! xiaoming
虽然sayHi函数已经执行完毕,可是其活动对象也不会被销毁,由于test函数仍然引用着sayHi函数中的变量name,这就是闭包。
但也由于闭包引用着另外一个函数的变量,致使另外一个函数已经不使用了也没法销毁,因此闭包使用过多,会占用较多的内存,这也是一个反作用。
这个题目是考察闭包的使用
function sayHi() { console.log('hi') } function threeTimes(fn) { let times = 0 return () => { if (times++ < 3) { fn() } } } const newFn = threeTimes(sayHi) newFn() newFn() newFn() newFn() newFn() // 后面两次执行都无任何反应
经过闭包变量 times
来控制函数的执行
function add(a, b) { if (b === undefined) { return function(x) { return a + x } } return a + b }
Ajax(asynchronous JavaScript and XML)是使用客户端上的许多 Web 技术,建立异步 Web 应用的一种 Web 开发技术。借助 Ajax,Web 应用能够异步(在后台)向服务器发送数据和从服务器检索数据,而不会干扰现有页面的显示和行为。经过将数据交换层与表示层分离,Ajax 容许网页和扩展 Web 应用程序动态更改内容,而无需从新加载整个页面。实际上,如今一般将 JSON 替换为 XML,由于 JavaScript 对 JSON 有原生支持优点。
XMLHttpRequest API 常常用于异步通讯。此外还有最近流行的fetch API。
let xmlhttp if (window.XMLHttpRequest) { // IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码 xmlhttp = new XMLHttpRequest() } else { // IE6, IE5 浏览器执行代码 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP") } xmlhttp.onreadystatechange = () => { if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { document.getElementById("myDiv").innerHTML = xmlhttp.responseText } } xmlhttp.open("GET", "/ajax/test.txt", true) xmlhttp.send()
优势
缺点
https://github.com/yangshun/front-end-interview-handbook/blob/master/questions/javascript-questions.md
var会使变量提高,这意味着变量能够在声明以前使用。let和const不会使变量提高,提早使用会报错。
变量提高(hoisting)是用于解释代码中变量声明行为的术语。使用var关键字声明或初始化的变量,会将声明语句“提高”到当前做用域的顶部。 可是,只有声明才会触发提高,赋值语句(若是有的话)将保持原样。
用var声明的变量的做用域是它当前的执行上下文,它能够是嵌套的函数,也能够是声明在任何函数外的变量。let和const是块级做用域,意味着它们只能在最近的一组花括号(function、if-else 代码块或 for 循环中)中访问。
function foo() { // 全部变量在函数中均可访问 var bar = 'bar'; let baz = 'baz'; const qux = 'qux'; console.log(bar); // bar console.log(baz); // baz console.log(qux); // qux } console.log(bar); // ReferenceError: bar is not defined console.log(baz); // ReferenceError: baz is not defined console.log(qux); // ReferenceError: qux is not defined
if (true) { var bar = 'bar'; let baz = 'baz'; const qux = 'qux'; } // 用 var 声明的变量在函数做用域上均可访问 console.log(bar); // bar // let 和 const 定义的变量在它们被定义的语句块以外不可访问 console.log(baz); // ReferenceError: baz is not defined console.log(qux); // ReferenceError: qux is not defined
var会使变量提高,这意味着变量能够在声明以前使用。let和const不会使变量提高,提早使用会报错。
console.log(foo); // undefined var foo = 'foo'; console.log(baz); // ReferenceError: can't access lexical declaration 'baz' before initialization let baz = 'baz'; console.log(bar); // ReferenceError: can't access lexical declaration 'bar' before initialization const bar = 'bar';
用var重复声明不会报错,但let和const会。
var foo = 'foo'; var foo = 'bar'; console.log(foo); // "bar" let baz = 'baz'; let baz = 'qux'; // Uncaught SyntaxError: Identifier 'baz' has already been declared
let和const的区别在于:let容许屡次赋值,而const只容许一次。
// 这样不会报错。 let foo = 'foo'; foo = 'bar'; // 这样会报错。 const baz = 'baz'; baz = 'qux';
https://github.com/yangshun/front-end-interview-handbook/blob/master/Translations/Chinese/questions/javascript-questions.md#%E4%BD%BF%E7%94%A8letvar%E5%92%8Cconst%E5%88%9B%E5%BB%BA%E5%8F%98%E9%87%8F%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB
在 JS
中,除了基本数据类型,还存在对象、数组这种引用类型。
基本数据类型,拷贝是直接拷贝变量的值,而引用类型拷贝的实际上是变量的地址。
let o1 = {a: 1} let o2 = o1
在这种状况下,若是改变 o1
或 o2
其中一个值的话,另外一个也会变,由于它们都指向同一个地址。
o2.a = 3 console.log(o1.a) // 3
而浅拷贝和深拷贝就是在这个基础之上作的区分,若是在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有从新建立一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,建立了一个新的对象,而且复制其内的成员变量,则认为是深拷贝。
let o1 = {a:{ b:1 } } let o2 = JSON.parse(JSON.stringify(o1))
另外一种方法
function deepCopy(s) { const d = {} for (let k in s) { if (typeof s[k] == 'object') { d[k] = deepCopy(s[k]) } else { d[k] = s[k] } } return d }
ES5
function unique(arry) { const temp = [] arry.forEach(e => { if (temp.indexOf(e) == -1) { temp.push(e) } }) return temp }
ES6
function unique (arr) { return Array.from(new Set(arr)) }
原始值 "I am a string" 并非一个对象,它只是一个字面量,而且是一个不可变的值。
若是要在这个字面量上执行一些操做,好比获取长度、访问其中某个字符等,那须要将其
转换为 String 对象。
幸亏,在必要时语言会自动把字符串字面量转换成一个 String 对象,也就是说你并不须要
显式建立一个对象。
Array.isArray([]) // true Array.isArray({}) // false typeof [] // "object" typeof {} // "object" Object.prototype == [].__proto__ // false Object.prototype == {}.__proto__ // true Array.prototype == [].__proto__ // true Array.prototype == {}.__proto__ // false
有时 JavaScript 会自动为代码行补上缺失的分号,即自动分号插入(Automatic SemicolonInsertion,ASI)。
由于若是缺失了必要的 ; ,代码将没法运行,语言的容错性也会下降。ASI 能让咱们忽略那些没必要要的 ; 。
请注意,ASI 只在换行符处起做用,而不会在代码行的中间插入分号。
若是 JavaScript 解析器发现代码行可能由于缺失分号而致使错误,那么它就会自动补上分
号。而且,只有在代码行末尾与换行符之间除了空格和注释以外没有别的内容时,它才会
这样作。
https://www.css88.com/archives/7340
特性 | cookie | localStorage | sessionStorage |
---|---|---|---|
由谁初始化 | 客户端或服务器,服务器能够使用Set-Cookie 请求头。 |
客户端 | 客户端 |
数据的生命周期 | 通常由服务器生成,可设置失效时间,若是在浏览器生成,默认是关闭浏览器以后失效 | 永久保存,可清除 | 仅在当前会话有效,关闭页面后清除 |
存放数据大小 | 4KB | 5MB | 5MB |
与服务器通讯 | 每次都会携带在HTTP头中,若是使用cookie保存过多数据会带来性能问题 | 仅在客户端保存 | 仅在客户端保存 |
用途 | 通常由服务器生成,用于标识用户身份 | 用于浏览器缓存数据 | 用于浏览器缓存数据 |
访问权限 | 任意窗口 | 任意窗口 | 当前页面窗口 |
自执行函数:一、声明一个匿名函数二、立刻调用这个匿名函数。
做用:建立一个独立的做用域。
好处:防止变量弥散到全局,以避免各类js库冲突。隔离做用域避免污染,或者截断做用域链,避免闭包形成引用变量没法释放。利用当即执行特性,返回须要的业务函数或对象,避免每次经过条件判断来处理
场景:通常用于框架、插件等场景
有以下几个方式:
https://zhuanlan.zhihu.com/p/41479807
断点续传最核心的内容就是把文件“切片”而后再一片一片的传给服务器,可是这看似简单的上传过程却有着无数的坑。
首先是文件的识别,一个文件被分红了若干份以后如何告诉服务器你切了多少块,以及最终服务器应该如何把你上传上去的文件进行合并,这都是要考虑的。
所以在文件开始上传以前,咱们和服务器要有一个“握手”的过程,告诉服务器文件信息,而后和服务器约定切片的大小,当和服务器达成共识以后就能够开始后续的文件传输了。
前台要把每一块的文件传给后台,成功以后前端和后端都要标识一下,以便后续的断点。
当文件传输中断以后用户再次选择文件就能够经过标识来判断文件是否已经上传了一部分,若是是的话,那么咱们能够接着上次的进度继续传文件,以达到续传的功能。
有了HTML5 的 File api以后切割文件比想一想的要简单的多的多。
只要用slice 方法就能够了
var packet = file.slice(start, end);
参数start是开始切片的位置,end是切片结束的位置 单位都是字节。经过控制start和end 就能够是实现文件的分块
如
file.slice(0,1000); file.slice(1000,2000); file.slice(2000,3000); // ......
在把文件切成片以后,接下来要作的事情就是把这些碎片传到服务器上。
若是中间掉线了,下次再传的时候就得先从服务器获取上一次上传文件的位置,而后以这个位置开始上传接下来的文件内容。
https://www.cnblogs.com/zhwl/p/3580776.html
function Test(){} const test = new Test()
const obj = {}
obj.constructor = Test obj.__proto__ = Test.prototype
Test.call(obj)
call和apply实际上是同样的,区别就在于传参时参数是一个一个传或者是以一个数组的方式来传。
call和apply都是在调用时生效,改变调用者的this指向。
let name = 'Jack' const obj = {name: 'Tom'} function sayHi() {console.log('Hi! ' + this.name)} sayHi() // Hi! Jack sayHi.call(obj) // Hi! Tom
bind也是改变this指向,不过不是在调用时生效,而是返回一个新函数。
const newFunc = sayHi.bind(obj) newFunc() // Hi! Tom
JavaScript
中的this
。JS 中的this
是一个相对复杂的概念,不是简单几句能解释清楚的。粗略地讲,函数的调用方式决定了this
的值。我阅读了网上不少关于this
的文章,Arnav Aggrawal 写的比较清楚。this
取值符合如下规则:
new
关键字,函数内的this
是一个全新的对象。apply
、call
或bind
方法用于调用、建立一个函数,函数内的 this 就是做为参数传入这些方法的对象。this
是调用该函数的对象。好比当obj.method()
被调用时,函数内的 this 将绑定到obj
对象。this
的值指向全局对象(global object)。浏览器环境下this
的值指向window
对象,可是在严格模式下('use strict'
),this
的值为undefined
。this
的值。this
被设置为它被建立时的上下文。想得到更深刻的解释,请查看他在 Medium 上的文章。
https://github.com/yangshun/front-end-interview-handbook/blob/master/Translations/Chinese/questions/javascript-questions.md#%E8%AF%B7%E7%AE%80%E8%BF%B0javascript%E4%B8%AD%E7%9A%84this
若是要判断一个运行中函数的 this 绑定,就须要找到这个函数的直接调用位置。找到以后就能够顺序应用下面这四条规则来判断 this 的绑定对象。
必定要注意,有些调用可能在无心中使用默认绑定规则。若是想“更安全”地忽略 this 绑定,你能够使用一个 DMZ 对象,好比 ø = Object.create(null) ,以保护全局对象。
ES6 中的箭头函数并不会使用四条标准的绑定规则,而是根据当前的词法做用域来决定this ,具体来讲,箭头函数会继承外层函数调用的 this 绑定(不管 this 绑定到什么)。这其实和 ES6 以前代码中的 self = this 机制同样
参考:《你不知道的JavaScript》
==
是抽象相等运算符,而===
是严格相等运算符。==
运算符是在进行必要的类型转换后,再比较。===
运算符不会进行类型转换,因此若是两个值不是相同的类型,会直接返回false
。使用==
时,可能发生一些特别的事情,例如:
1 == '1'; // true 1 == [1]; // true 1 == true; // true 0 == ''; // true 0 == '0'; // true 0 == false; // true
若是你对==
和===
的概念不是特别了解,建议大多数状况下使用===
this
对象,就是定义时所在的对象,而不是使用时所在的对象,用call
apply
bind
也不能改变this
指向new
命令,不然会抛出一个错误。arguments
对象,该对象在函数体内不存在。若是要用,能够用 rest
参数代替。yield
命令,所以箭头函数不能用做 Generator
函数。prototype
Performance 接口能够获取到当前页面中与性能相关的信息。
该类型的对象能够经过调用只读属性 Window.performance 来得到。
白屏时间:
performance.timing.responseStart - performance.timing.navigationStart
首屏时间
window.onload = () => { new Date() - performance.timing.responseStart }
https://developer.mozilla.org/zh-CN/docs/Web/API/Performance
https://github.com/skyline75489/what-happens-when-zh_CN/blob/master/README.rst?utm_medium=social&utm_source=wechat_session&from=timeline&isappinstalled=0
https://www.jianshu.com/p/5d82bba9e1a1
防抖(debounce)
在函数须要频繁触发时,只有当有足够空闲的时间时,才执行一次。就好像在百度搜索时,每次输入以后都有联想词弹出,这个控制联想词的方法就不多是输入框内容一改变就触发的,他必定是当你结束输入一段时间以后才会触发。
节流(thorttle)
预约一个函数只有在大于等于执行周期时才执行,周期内调用不执行。就好像你在淘宝抢购某一件限量热卖商品时,你不断点刷新点购买,但是总有一段时间你点上是没有效果,这里就用到了节流,就是怕点的太快致使系统出现bug。
区别
在发生持续触发事件时,防抖设置事件延迟并在空闲时间去触发事件,而节流则是隔必定的时间触发一次。
具体请看:
https://blog.csdn.net/jacoox/article/details/80719456
HTML5引入了 history.pushState()
和 history.replaceState()
方法,它们分别能够添加和修改历史记录条目。
let stateObj = { foo: "bar", }; history.pushState(stateObj, "page 2", "bar.html");
假设当前页面为 foo.html
,执行上述代码后会变为 bar.html
,点击浏览器后退,会变为 foo.html
,但浏览器并不会刷新。
pushState()
须要三个参数: 一个状态对象, 一个标题 (目前被忽略), 和 (可选的) 一个 URL. 让咱们来解释下这三个参数详细内容:
状态对象 — 状态对象 state
是一个 JavaScript 对象,经过 pushState ()
建立新的历史记录条目。不管何时用户导航到新的状态,popstate
事件就会被触发,且该事件的 state
属性包含该历史记录条目状态对象的副本。
状态对象能够是能被序列化的任何东西。缘由在于 Firefox 将状态对象保存在用户的磁盘上,以便在用户重启浏览器时使用,咱们规定了状态对象在序列化表示后有640k的大小限制。若是你给 pushState()
方法传了一个序列化后大于 640k 的状态对象,该方法会抛出异常。若是你须要更大的空间,建议使用 sessionStorage
以及 localStorage
.
标题 — Firefox 目前忽略这个参数,但将来可能会用到。传递一个空字符串在这里是安全的,而在未来这是不安全的。二选一的话,你能够为跳转的 state
传递一个短标题。
URL — 该参数定义了新的历史URL记录。注意,调用 pushState()
后浏览器并不会当即加载这个 URL,但可能会在稍后某些状况下加载这个 URL,好比在用户从新打开浏览器时。新URL没必要须为绝对路径。若是新URL是相对路径,那么它将被做为相对于当前 URL 处理。新 URL 必须与当前URL同源,不然 pushState()
会抛出一个异常。该参数是可选的,缺省为当前 URL。
function format(str) { let s = '' let count = 0 for (let i = str.length - 1; i >= 0; i--) { s = str[i] + s count++ if (count % 3 == 0 && i != 0) { s = ',' + s } } return s }
function format(str) { return str.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,') }
str.replace(/\s/g, '')
str.replace(/^\s+|\s+$/g, '') // 原生方法 str.trim()
REST 指的是一组架构约束条件和原则。知足这些约束条件和原则的应用程序或设计就是 RESTful。
https://blog.csdn.net/jnshu_it/article/details/80203696
Accept 请求头用来告知客户端能够处理的内容类型,这种内容类型用MIME类型来表示。
服务器使用 Content-Type 应答头通知客户端它的选择。
Accept: text/html Accept: image/* Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
1.Accept属于请求头, Content-Type属于实体头。
Http报头分为通用报头,请求报头,响应报头和实体报头。
请求方的http报头结构:通用报头|请求报头|实体报头
响应方的http报头结构:通用报头|响应报头|实体报头
2.Accept表明发送端(客户端)但愿接受的数据类型。
好比:Accept:text/xml;
表明客户端但愿接受的数据类型是xml类型
Content-Type表明发送端(客户端|服务器)发送的实体数据的数据类型。
好比:Content-Type:text/html;
表明发送端发送的数据格式是html。
两者合起来,
Accept:text/xml;
Content-Type:text/html
即表明但愿接受的数据类型是xml格式,本次请求发送的数据的数据格式是html。
状态码 | 类别 | 描述 |
---|---|---|
1xx | Informational(信息状态码) | 接受请求正在处理 |
2xx | Success(成功状态码) | 请求正常处理完毕 |
3xx | Redirection(重定向状态码) | 须要附加操做已完成请求 |
4xx | Client Error(客户端错误状态码) | 服务器没法处理请求 |
5xx | Server Error(服务器错误状态码) | 服务器处理请求出错 |
https://segmentfault.com/a/1190000010690320
https://zhuanlan.zhihu.com/p/33778904
无状态协议对于事务处理没有记忆能力。缺乏状态意味着若是后续处理须要前面的信息也就是说,
当客户端一次HTTP请求完成之后,客户端再发送一次HTTP请求,HTTP并不知道当前客户端是一个”老用户“。
能够使用Cookie来解决无状态的问题,Cookie就至关于一个通行证,第一次访问的时候给客户端发送一个Cookie,
当客户端再次来的时候,拿着Cookie(通行证),那么服务器就知道这个是”老用户“。
https://zhuanlan.zhihu.com/p/33778904
HTTP通讯机制是在一次完整的HTTP通讯过程当中,Web浏览器与Web服务器之间将完成下列7个步骤:
在HTTP工做开始以前,Web浏览器首先要经过网络与Web服务器创建链接,该链接是经过TCP来完成的,该协议与IP协议共同构建 Internet,即著名的TCP/IP协议族,所以Internet又被称做是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则, 只有低层协议创建以后才能,才能进行更层协议的链接,所以,首先要创建TCP链接,通常TCP链接的端口号是80。
一旦创建了TCP链接,Web浏览器就会向Web服务器发送请求命令。例如:GET /sample/hello.jsp HTTP/1.1。
浏览器发送其请求命令以后,还要以头信息的形式向Web服务器发送一些别的信息,以后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。
客户机向服务器发出请求后,服务器会客户机回送应答, HTTP/1.1 200 OK ,应答的第一部分是协议的版本号和应答状态码。
正如客户端会随同请求发送关于自身的信息同样,服务器也会随同应答向用户发送关于它本身的数据及被请求的文档。
Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。
通常状况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP链接,而后若是浏览器或者服务器在其头信息加入了这行代码:
Connection:keep-alive
TCP链接在发送后将仍然保持打开状态,因而,浏览器能够继续经过相同的链接发送请求。保持链接节省了为每一个请求创建新链接所需的时间,还节约了网络带宽。
创建TCP链接->发送请求行->发送请求头->(到达服务器)发送状态行->发送响应头->发送响应数据->断TCP链接
https://juejin.im/post/5a8102e0f265da4e710f5910
MVVM最先由微软提出来,它借鉴了桌面应用程序的MVC思想,在前端页面中,把Model用纯JavaScript对象表示,View负责显示,二者作到了最大限度的分离
把Model和View关联起来的就是ViewModel。
ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model
View 和 Model 之间的同步工做彻底是自动的,无需人为干涉(由viewModel完成,在这里指VUE)
所以开发者只需关注业务逻辑,不须要手动操做DOM, 不须要关注数据状态的同步问题,复杂的数据状态维护彻底由 MVVM 来统一管理
须要用JavaScript编写一个通用的ViewModel,这样,就能够复用整个MVVM模型了
一个MVVM框架和jQuery操做DOM相比有什么区别?
咱们先看用jQuery实现的修改两个DOM节点的例子:
<!-- HTML --> <p>Hello, <span id="name">Bart</span>!</p> <p>You are <span id="age">12</span>.</p> Hello, Bart! You are 12.
用jQuery修改name和age节点的内容:
var name = 'Homer'; var age = 51; $('#name').text(name); $('#age').text(age);
若是咱们使用MVVM框架来实现一样的功能,咱们首先并不关心DOM的结构,而是关心数据如何存储。最简单的数据存储方式是使用JavaScript对象:
var person = { name: 'Bart', age: 12 }
咱们把变量person看做Model,把HTML某些DOM节点看做View,并假定它们之间被关联起来了。
要把显示的name从Bart改成Homer,把显示的age从12改成51,咱们并不操做DOM,而是直接修改JavaScript对象:
person.name = 'Homer'; person.age = 51;
执行上面的代码,咱们惊讶地发现,改变JavaScript对象的状态,会致使DOM结构做出对应的变化!这让咱们的关注点从如何操做DOM变成了如何更新JavaScript对象的状态,而操做JavaScript对象比DOM简单多了!
这就是MVVM的设计思想:关注Model的变化,让MVVM框架去自动更新DOM的状态,从而把开发者从操做DOM的繁琐步骤中解脱出来!
下图能够很好的解释view viewModel model之间的关系
mvvm的优势便是vue的优势,在这里再总结一下:
数据和视频之间的同步工做彻底是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不须要手动操做DOM, 不须要关注数据状态的同步问题,
复杂的数据状态维护彻底由 MVVM 来统一管理,节省了不少精力。
建立一个Vue实例,是一个漫长的过程,要经历初始化,数据合并,模板解析,数据渲染等等一系列过程。
因此,为了能实如今这个过程里面插入本身想要提早作的事情,就有了生命周期钩子函数。
一辆公交车,从出发点A站到终点站B,中间有不少站点,公交车每到一个站点,就得停下来, 等待客人上车,而后再驶往下一个站点,一直到终点站为止。 A和B之间的站点,就像是这个路程的生命周期。每个站点都是一个不一样的生命周期(站点名不一样), 只要到了站点,就得执行该站点对应的生命周期函数, 只不过每一个站点的生命周期函数都是同样的(等待客人上车)。
Vue中的生命周期也是同样,对应了Vue实例从建立到结束之间的每个过程。
例如,Vue的beforeCreate
周期,指的就是Vue在实例初始化以后,数据观测 (data observer) 和 event/watcher 事件配置以前被调用。
至于Vue具体的生命周期函数有哪些,请看官网API文档
具体例子请看官方文档
Vue的数据双向绑定都是依据Object.defineProperty()这一方法来作的
Object.defineProperty到底有什么做用呢?
MDN
Object.defineProperty(obj, prop, descriptor) obj 要在其上定义属性的对象。 prop 要定义或修改的属性的名称。 descriptor 将被定义或修改的属性描述符。
简单来讲 这个方法能够定义一个对象某个属性的描述符
咱们须要用到的就是描述符当中的getter和setter
const obj = {a:1} obj.a // 1 obj.a = 2
像上面代码中的两个操做 读取和赋值 就是在访问obj.a的getter和setter
当咱们输入obj.a时 就是在访问obj对象a属性的getter 当输入obj.a = 2 时就是在访问obj对象a属性的setter
Object.defineProperty(obj, 'a', { get : function(){ return val }, set : function(newValue){ val = newValue }, enumerable : true, configurable : true })
getter和setter都是一个函数 咱们还能够这样作 例如
get: function() { // 每次访问obj.a时都会执行这段代码 console.log('hello, 你在读取a的值') return val } set: function(newValue) { val = newValue // 每次给obj.a赋值时都会执行这段代码 console.log('你设置了a的值') }
Vue的双向数据绑定就是根据上面的原理来实现的
只要在读取值时收集观察者 在赋值时触发观察者更新函数 就能够实现数据变动 从而实现DOM从新渲染
说到这可能还不是很明白 不要急 慢慢来 先看一下这段代码 复制放到HTML文件里本身运行一下
而后打开网页 在控制台里输入data.user.name看看 会有惊喜
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>动态数据绑定(一)</title> </head> <body> <script> var data = { user: { name: 'xiaoming', age: 18, occupation: 'frontend' }, address: { city: 'shaoguan' } }; function Observer(data) { this.data = data; this.walk(data); } Observer.prototype = { walk: function(obj) { var value, key; for (key in obj) { if (obj.hasOwnProperty(key)) { value = obj[key]; if (typeof value === 'object') { new Observer(value); } this.convert(key, value); } } }, convert: function(key, value) { Object.defineProperty(this.data, key, { get : function(){ console.log("你访问了" + key); return value; }, set : function(newValue){ value = newValue; console.log('你设置了' + key + '=' + value); } }); } } var example = new Observer(data); </script> </body> </html>
说简单点,vue-router的原理就是经过对URL地址变化的监听,继而对不一样的组件进行渲染。
每当URL地址改变时,就对相应的组件进行渲染。原理是很简单,实现方式可能有点复杂,主要有hash模式和history模式。
若是想了解得详细点,建议百度或者阅读源码。
vuex的原理其实很是简单,它为何能实现全部的组件共享同一份数据?
由于vuex生成了一个store实例,而且把这个实例挂在了全部的组件上,全部的组件引用的都是同一个store实例。
store实例上有数据,有方法,方法改变的都是store实例上的数据。因为其余组件引用的是一样的实例,因此一个组件改变了store上的数据,
致使另外一个组件上的数据也会改变,就像是一个对象的引用。
若是对vuex的实现有兴趣,能够看看我本身造的一个vue轮子对应的vuex插件。它实现了除vuex模块外的全部功能。
v-if
是“真正”的条件渲染,由于它会确保在切换过程当中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:若是在初始渲染时条件为假,则什么也不作——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show
就简单得多——无论初始条件是什么,元素老是会被渲染,而且只是简单地基于 CSS 进行切换。
通常来讲,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。所以,若是须要很是频繁地切换,则使用 v-show
较好;若是在运行时条件不多改变,则使用v-if
较好。
https://cn.vuejs.org/v2/guide/conditional.html#v-if-vs-v-show
利用 vue-router
的 beforeEach
事件,能够在跳转页面前判断用户的权限(利用 cookie 或 token),是否可以进入此页面,若是不能则提示错误或重定向到其余页面,在后台管理系统中这种场景常常能遇到。
在 Vue
中,每次切换组件时,都会从新渲染。若是有多个组件切换,又想让它们保持原来的状态,避免从新渲染,这个时候就能够使用 keep-alive
。
keep-alive
能够使被包含的组件保留状态,或避免从新渲染。
先来看一下计算属性的定义:
当其依赖的属性的值发生变化的时,计算属性会从新计算。反之则使用缓存中的属性值。
计算属性和vue中的其它数据同样,都是响应式的,只不过它必须依赖某一个数据实现,而且只有它依赖的数据的值改变了,它才会更新。
$route
是路由信息对象,包括path
,params
,hash
,query
,fullPath
,matched
,name
等路由信息参数。
而 $router
是路由实例对象,包括了路由的跳转方法,钩子函数等
watch
主要做用是监听某个数据值的变化。和计算属性相比除了没有缓存,做用是同样的。
借助 watch
还能够作一些特别的事情,例如监听页面路由,当页面跳转时,咱们能够作相应的权限控制,拒绝没有权限的用户访问页面。