元旦匆匆而过,2020年的春节又接踵而来,大家除了忙的提着裤子加班、年底冲冲冲外,还有着对于明年的迷茫和期待!2019年有多少苦涩心酸,2020年就有更多幸福美好,加油,奥利给!怀着一颗积极向上的心,来面对未来每一天的挑战!
所谓“兵马未动,粮草先行”,我们打响明天的战役也需要精神食粮来做后勤保障才是。在此我整理了多位从业者和我在2019年底至2020年初的一厂面试精选题,希望对磨砺锋芒、奋发向上的小伙伴有所帮助,祝你早日剑指大厂,扬帆起航,奥利给!
盒子模型就是 元素在网页中的实际占位,有两种:标准盒子模型和IE盒子模型
标准(W3C)盒子模型:内容content+填充padding+边框border+边界margin宽高指的是 content 的宽高
低版本IE盒子模型:内容(content+padding+border)+ 边界margin,宽高指的是content+padding+border 部分的宽高
/* 标准模型 */
box-sizing:content-box;
/*IE模型*/
box-sizing:border-box;
父子元素、兄弟元素,当有外边距时,会取其中一个边距的最大值,作为实际的边距。
空元素的有上下边距时,也会取其中更大的一个边距值,作为实际的边距。
这就是边距重叠。
BFC:
概念:块级格式化上下文
原理:
垂直方向的padding: 在css中,padding-top或padding-bottom的百分比值是根据容器的width来计算的。
.wrap{
position: relative;
height: 0; //容器的height设置为0
width: 100%;
padding-top: 75%; //100%*3/4
}
.wrap > *{
position: absolute;//容器的内容的所有元素absolute,然子元素内容都将被padding挤出容器
left: 0;
top: 0;
width: 100%;
height: 100%;
}
+ **padding & calc()**: 跟第一种方法原理相同
```css
padding-top: calc(100%*9/16);
width:100vw;
height:calc(100vw*3/4)
优先级就近原则,同权重情况下样式定义最近者为准
!important>id >class>tag
important比内联优先级高
元素选择符的权值:元素标签(派生选择器):1,class选择符:10,id选择符:100,内联样式权值最大,为1000
一、对于行内元素:
text-align:center;
二、对于确定宽度的块级元素:
(1)margin和width实现水平居中
常用(前提:已设置width值):margin-left:auto; margin-right:auto;
(2)绝对定位和margin-left: -(宽度值/2)实现水平居中
固定宽度块级元素水平居中,通过使用绝对定位,以及设置元素margin-left为其宽度的一半
.content{
width: 200px;
position: absolute;
left: 50%;
margin-left: -100px; // 该元素宽度的一半,即100px
background-color: aqua;
}
(3)position:absolute + (left=0+top=0+right=0+bottom=0) + margin:auto
.content{
position: absolute;
width: 200px;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
三、对于未知宽度的块级元素:
(1)table标签配合margin左右auto实现水平居中
使用table标签(或直接将块级元素设值为display:table),再通过给该标签添加左右margin为auto
(2)inline-block实现水平居中方法
display:inline-block;(或display:inline)和text-align:center;实现水平居中
存在问题:需额外处理inline-block的浏览器兼容性(解决inline-block元素的空白间距)
(3)绝对定位实现水平居中
绝对定位+transform,translateX可以移动本省元素的50%
.content{
position: absolute;
left: 50%;
transform: translateX(-50%); /* 移动元素本身50% */
background: aqua;
}
(4)相对定位实现水平居中
用float或者display把父元素变成行内块状元素
.contentParent{
display: inline-block; /* 把父元素转化为行内块状元素 */
/*float: left; 把父元素转化为行内块状元素 */
position: relative;
left: 50%;
}
/*目标元素*/
.content{
position: relative;
right: 50%;
background-color:aqua;
}
(5)CSS3的flex实现水平居中方法,法一
.contentParent{
display: flex;
flex-direction: column;
}
.content{
align-self:center;
}
(6)CSS3的flex实现水平居中方法,法二
.contentParent{
display: flex;
}
.content{
margin: auto;
}
(7)CSS3的fit-content配合左右margin为auto实现水平居中方法
.content{
width: fit-content;
margin-left: auto;
margin-right: auto;
}
参考链接 https://blog.csdn.net/dengdongxia/article/details/80297116
该布局模型的目的是提供一种更加高效的方式来对容器中的条目进行布局、对齐和分配空间。在传统的布局方式中,block 布局是把块在垂直方向从上到下依次排列的;而 inline 布局则是在水平方向来排列。弹性盒布局并没有这样内在的方向限制,可以由开发人员自由操作。flexbox设置父元素的display
属性为flex
,则子元素都变成flex item
,由此可以控制子元素的排列方式、尺寸、间距等;
试用场景:弹性布局适合于移动前端开发,在Android和ios上也完美支持。
左右边框设置为透明,长度为底部边框的一半。左右边框长度必须设置,不设置则只有底部一条边框,是不能展示的。
{width: 0; height: 0; border-top: 40px solid transparent; border-left: 40px solid transparent; border-right: 40px solid transparent; border-bottom: 40px solid #ff0000;}
//infinite 表示动画无限次播放 linear表示动画从头到尾的速度是相同的
.circle{
animation: myRotation 5s linear infinite;
}
@keyframes myRotation {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
inline默认。此元素会被显示为内联元素,元素前后没有换行符。
block此元素将显示为块级元素,此元素前后会带有换行符。
none此元素不会被显示(隐藏)。
inline-block行内块元素。(CSS2.1 新增的值)
list-item此元素会作为列表显示。
table此元素会作为块级表格来显示(类似table),表格前后带有换行符
absolute
生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位。
元素的位置通过 “left”, “top”, “right” 以及 “bottom” 属性进行规定。
fixed
生成固定定位的元素,相对于浏览器窗口进行定位。(老IE不支持)
元素的位置通过 “left”, “top”, “right” 以及 “bottom” 属性进行规定。
relative
生成相对定位的元素,相对于其正常位置进行定位,不脱离文档流。
因此,“left:20” 会向元素的 LEFT 位置添加 20 像素。
static默认值。
没有定位,元素出现在正常的文档流中(忽略 top, bottom, left, right 或者 z-index 声明)。inherit规定应该从父元素继承 position 属性的值。
因为浏览器的兼容问题,不同浏览器对有些标签的默认值是不同的,如果没对CSS初始化往往会出现浏览器之间的页面显示差异。
在布局方面新增了flex布局;
在选择器方面新增了例如:first-of-type,nth-child等选择器;
在盒模型方面添加了box-sizing来改变盒模型,
在动画方面增加了animation、2d变换、3d变换等。在颜色方面添加透明、rgba等,
在字体方面允许嵌入字体和设置字体阴影,同时当然也有盒子的阴影,
媒体查询。为不同设备基于它们的能力定义不同的样式。
@media screen and (min-width:960px) and (max-width:1200px){
body{
background:yellow;
}
}
元素的显示隐藏方法很多,不同方法的在不同的场景下页面效果不一,对页面的性能也有不同的影响。
元素隐藏方法总结:
如果希望元素不可见、不占据空间、资源会加载、DOM 可访问: display: none
;
如果希望元素不可见、不能点击、但占据空间、资源会加载,可以使用: visibility: hidden
;
如果希望元素不可见、不占据空间、显隐时可以又transition
淡入淡出效果
div{
position: absolute;
visibility: hidden;
opacity: 0;
transition: opacity .5s linear;
background: cyan;
}
div.active{
visibility: visible;
opacity: 1;
}
这里使用visibility: hidden
而不是display: none
,是因为display: none
会影响css3的transition
过渡效果。 但是display: none
并不会影响cssanimation
动画的效果。
如果希望元素不可见、可以点击、占据空间,可以使用: opacity: 0
;
如果希望元素不可见、可以点击、不占据空间,可以使用: opacity: 0; position: absolute;
;
如果希望元素不可见、不能点击、占据空间,可以使用: position: relative; z-index: -1;
;
如果希望元素不可见、不能点击、不占据空间,可以使用: position: absolute ; z-index: -1;
display: none
与visibility: hidden
的区别display: none
的元素不占据任何空间,visibility: hidden
的元素空间保留;display: none
会影响css3的transition
过渡效果,visibility: hidden
不会;display: none
隐藏产生重绘 ( repaint ) 和回流 ( relfow ),visibility: hidden
只会触发重绘;display: none
的节点和子孙节点元素全都不可见,visibility: hidden
的节点的子孙节点元素可以设置 visibility: visible
显示。visibility: hidden
属性值具有继承性,所以子孙元素默认继承了hidden
而隐藏,但是当子孙元素重置为visibility: visible
就不会被隐藏。意外的全局变量引起的内存泄漏
闭包引起的内存泄漏
未清除 dom 元素的引用的内存泄漏
循环引用引起的内存泄漏
被遗忘的计时器或回调引起的内存泄漏
const wm = new WeakMap();
const element = document.getElementById('example');
wm.set(element, 'some information');
wm.get(element) // "some information"
先新建一个 Weakmap 实例。然后,将一个 DOM 节点作为键名存入该实例,并将一些附加信息作为键值,一起存放在 WeakMap 里面。这时,WeakMap 里面对element的引用就是弱引用,不会被计入垃圾回收机制。
也就是说,DOM 节点对象的引用计数是1,而不是2。这时,一旦消除对该节点的引用,它占用的内存就会被垃圾回收机制释放。Weakmap 保存的这个键值对,也会自动消失。
// 该段代码会启动一个针对“example.php”的GET同步请求。
xhr.open("get", "example.php", false)
readyState | 对应常量 | 描述 |
---|---|---|
0(未初始化) | xhr.UNSENT | 请求已建立, 但未初始化(此时未调用open方法) |
1(初始化) | xhr.OPENED | 请求已建立, 但未发送 (已调用open方法, 但未调用send方法) |
2(发送数据) | xhr.HEADERS_RECEIVED | 请求已发送 (send方法已调用, 已收到响应头) |
3(数据发送中) | xhr.LOADING | 请求处理中, 因响应内容不全, 这时通过responseBody和responseText获取可能会出现错误 |
4(完成) | xhr.DONE | 数据接收完毕, 此时可以通过responseBody和responseText获取完整的响应数据 |
//promise 实现ajax
function ajax(method, url, data) {
var request = new XMLHttpRequest();
return new Promise(function (resolve, reject) {
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200) {
resolve(request.responseText);
} else {
reject(request.status);
}
}
};
request.open(method, url);
request.send(data);
});
}
ajax('GET', '/api/categories').then(function (text) { // 如果AJAX成功,获得响应内容
log.innerText = text;
}).catch(function (status) { // 如果AJAX失败,获得响应代码
log.innerText = 'ERROR: ' + status;
});
function instanceOf(left,right) {
let proto = left.__proto__;
let prototype = right.prototype
while(true) {
if(proto === null) return false;
if(proto === prototype) return true;
proto = proto.__proto__;
}
}
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用
const config = {
root: null, // 默认指向浏览器的视口,但可以是任意DOM元素
rootMargin: '0px', // 计算交叉时,root边界盒的偏移量
threshold: 0.5 // 监听对象的交叉区域与边界区域的比率
}
let observer = new IntersectionObserver(fucntion(entries){
// ...
}, config)
new IntersectionObserver(function(entries, self))
<img src="default.jpg" data-src="www.example.com/1.jpg">
const images = document.querySelectorAll('[data-src]')
const config = {
rootMargin: '0px',
threshold: 0
};
let observer = new IntersectionObserver((entries, self)=>{
entries.forEach(entry => {
if(entry.isIntersecting){
// 加载图像
preloadImage(entry.target);
// 解除观察
self.unobserve(entry.target)
}
})
}, config)
images.forEach(image => {
observer.observe(image);
});
function preloadImage(img) {
const src = img.dataset.src
if (!src) { return; }
img.src = src;
}
参考: 实现图片懒加载
var debounce = function(delay, cb) {
var timer;
return function() {
if (timer) clearTimeout(timer);
timer = setTimeout(function() {
cb();
}, delay);
}
}
var throttle = function(delay, cb) {
var startTime = Date.now();
return function() {
var currTime = Date.now();
if (currTime - startTime > delay) {
cb();
startTime = currTime;
}
}
}
//使用cloneNode,但是在元素上绑定的事件不会拷贝
function clone(origin) {
return Object.assign({},origin);
}
//实现了对原始对象的克隆,但是只能克隆原始对象自身的值,不能克隆她继承的值,如果想要保持继承链,可以采用如下方法:
function clone(origin) {
let originProto=Object.getPrototypeOf(origin);
return Object.assign(Object.create(originProto),origin);
}
<script src="path/to/myModule.js" defer></script>
<script src="path/to/myModule.js" async></script>
const foo=Object.freeze({});
foo.prop=123;
console.log(foo.prop);//混杂模式undefined,不起作用
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
}
});
let obj = Object.create(proxy);
obj.time // 35
const curry = (fn, currArgs=[]) => {
return function() {
let args = Array.from(arguments);
[].push.apply(args,currArgs);
if (args.length < fn.length) {
return curry.call(this,fn,...args);
}
return fn.apply(this,args);
}
}
d3通过svg绘制图形,可以自定义事件。svg不依赖分辨率,继续xml绘制图形,可以操作dom。支持事件处理器,复杂度高,会减慢页面的渲染速度。
echarts通过canvas来绘制图形,用户通过配置 options 参数,就可很容易绘制指定图表。canvas依赖分辨率,基于js绘制图形,不支持事件处理,能以png或者jpg的格式保存图片。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
(1)Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
(2)改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
vuex的store有State、 Getter、Mutation 、Action、 Module五种属性;
父组件通过props属性与子组件通信
父组件:
data(){
return {
singleVoiceData:'来自父组件的数据'
}
}
// 父组件调用子组件中的方法,将值传递给子组件
CSSmethods:{`
`this.$refs.singleVoiceRef.openSingleVoice(this.singleVoiceData)`
`}`
子组件通过props来接受数据
props: {parentToChild: {type: String,required: true}},methods:{openSingleVoice(SingleVoice) {console.log(SingleVoice)}}
子组件向父组件传值
vue2.0只允许单向数据传递,我们通过出发事件来改变组件的数据
子组件代码:
methods: {
open() {
this.$emit('showbox','the msg'); //触发showbox方法,'the msg'为向父组件传递的数据
}
}
父组件代码:
//监听子组件触发的showbox事件,然后调用toshow方法
methods: {
toshow(msg) {
this.msg = msg;
}
}
兄弟组件之间的通信
我们可以实例化一个vue实例,相当于一个第三方
eventVue.$emit(‘function1’,value)
eventVue.$on(‘function1’, (message) => { // 箭头函数接收
})
创建一个公共桥梁 eventVue.js
import Vue from 'vue'
export default new Vue()
兄弟组件内引入 eventVue.js
兄弟组件一
import eventVue from './eventVue.js'
export default {
methods: {
// 点击通讯录与员工进行语音聊天
handleChatStaff(staffInfo) {
console.log(staffInfo)
this.staffInfo = staffInfo
eventVue.$emit('updateChatList', this.staffInfo)
},
}
}
兄弟组件二
import eventVue from './eventVue.js'
export default {
created() {
this.updateList()
},
methods: {
updateList() {
eventVue.$on('updateChatList', (message) => { // 与phoneBook组件通信
console.log(message)
this.updateChatListEvent()
})
},
// 更新聊天列表
updateChatListEvent() {},
}
其他参考地址 :
https://www.imooc.com/article/68394?block_id=tuijian_wz
https://www.cnblogs.com/zhangruiqi/p/9386437.html
参考地址:
https://www.cnblogs.com/yalong/p/10388384.html
https://www.jianshu.com/p/2d47396c775c
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
beforeCreate 组件实例被创建之初,组件的属性生效之前
created 组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,$el 还不可用
beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用 mounted el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子
beforeUpdate 组件数据更新之前调用,发生在虚拟 DOM 打补丁之前
update 组件数据更新之后
activited keep-alive 专属,组件被激活时调用
deactivated keep-alive 专属,组件被销毁时调用
beforeDestory 组件销毁前调用
destoryed 组件销毁后调用
比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,可以通过以下写法实现:
// Parent.vue
// Child.vue
mounted() {
this.$emit("mounted");
}
复制代码
以上需要手动通过 $emit 触发父组件的事件,更简单的方式可以在父组件引用子组件时通过 @hook 来监听即可,如下所示:
// Parent.vue
doSomething() {
console.log('父组件监听到 mounted 钩子函数 ...');
},
// Child.vue
mounted(){
console.log('子组件触发 mounted 钩子函数 ...');
},
// 以上输出顺序为:
// 子组件触发 mounted 钩子函数 ...
// 父组件监听到 mounted 钩子函数 ...
复制代码
当然 @hook 方法不仅仅是可以监听 mounted,其它的生命周期事件,例如:created,updated 等都可以监听。
Vue 组件间通信是面试常考的知识点之一,这题有点类似于开放题,你回答出越多方法当然越加分,表明你对 Vue 掌握的越熟练。Vue 组件间通信只要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。
(1)props / $emit
适用 父子组件通信
这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。
(2)ref
与 $parent / $children
适用 父子组件通信
ref
:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例$parent
/ $children
:访问父 / 子实例(3)EventBus ($emit / $on)
适用于 父子、隔代、兄弟组件通信
这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。
(4)$attrs
/$listeners
适用于 隔代组件通信
$attrs
:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind="$attrs"
传入内部组件。通常配合 inheritAttrs 选项一起使用。$listeners
:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners"
传入内部组件(5)provide / inject
适用于 隔代组件通信
祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。 provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
(6)Vuex 适用于 父子、隔代、兄弟组件通信
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
(1)代码层面的优化**
(2)Webpack 层面的优化
(3)基础的 Web 技术的优化
与angularjs的区别:
1**、Angular.js的学习成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比较简单、直观。**
2**、在性能上,Angular.js依赖对数据做脏检查,所以watcher越多越慢。**
3**、Vue.js使用基于依赖追踪的观察并且使用异步队列更新。所有的数据都是独立出发的。**
对于庞大的应用来说,这个优化差异还是比较明显的。
与reactjs的区别:
React 依赖Virtual DOM,而Vue.js使用的是DOM模板。React采用的Virtual DOM会对渲染出来的结果做脏检查。
Vue.js在模板中提供了指令,过滤器等,可以非常方便,快捷地操作Virtual DOM
包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染;
使用:简单页面时
缓存:
不缓存:
使用:复杂项目时
路由字典中定义{path:’/detail’,meta:{keepAlive:false/true}} 是否缓存
根目录中:
css的预编译。
使用步骤:
第一步:用npm 下三个loader(sass-loader、css-loader、node-sass)
第二步:在build目录找到webpack.base.config.js,在那个extends属性中加一个拓展.scss
第三步:还是在同一个文件,配置一个module属性
第四步:然后在组件的style标签加上lang属性 ,例如:lang=”scss”
有哪几大特性:
1、可以用变量,例如($变量名称=值);
2、可以用混合器,例如:定义了字体的混合器
@mixin font-dpr($font-size){
$font:$font-size/2;
font-size: $font;
[data-dpr="2"] & { font-size: $font+2px;}
[data-dpr="3"] & { font-size: $font+4px;}
}
使用方法如下
.div{
@include font-dpr(24px);
}
3、可以嵌套
框架的好处:
组件化: 其中以 React 的组件化最为彻底,甚至可以到函数级别的原子组件,高度的组件化可以是我们的工程易于维护、易于组合拓展。
天然分层: JQuery 时代的代码大部分情况下是面条代码,耦合严重,现代框架不管是 MVC、MVP还是MVVM 模式都能帮助我们进行分层,代码解耦更易于读写。
生态: 现在主流前端框架都自带生态,不管是数据流管理架构还是 UI 库都有成熟的解决方案。
开发效率: 现代前端框架都默认自动更新DOM,而非我们手动操作,解放了开发者的手动DOM成本,提高开发效率,从根本上解决了UI 与状态同步问题.
虚拟DOM的优劣如何?
优点:
保证性能下限: 虚拟DOM可以经过diff找出最小差异,然后批量进行patch,这种操作虽然比不上手动优化,但是比起粗暴的DOM操作性能要好很多,因此虚拟DOM可以保证性能下限
无需手动操作DOM: 虚拟DOM的diff和patch都是在一次更新中自动进行的,我们无需手动操作DOM,极大提高开发效率
跨平台: 虚拟DOM本质上是JavaScript对象,而DOM与平台强相关,相比之下虚拟DOM可以进行更方便地跨平台操作,例如服务器渲染、移动端开发等等
缺点:
无法进行极致优化: 在一些性能要求极高的应用中虚拟DOM无法进行针对性的极致优化,比如VScode采用直接手动操作DOM的方式进行极端的性能优化
虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。虚拟DOM本质上是JavaScript对象,是对真实DOM的抽象,状态变更时,记录新树和旧树的差异,最后把差异更新到真正的dom中
具体实现步骤如下:
1.用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中;
2.当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异;
把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了
组件加载之前,组件加载完成,以及组件更新数据,组件销毁。触发的一系列的方法 ,这就是组件的生命周期函数
1、初始化阶段:
getDefaultProps:获取实例的默认属性
getInitialState:获取每个实例的初始化状态
componentWillMount:组件即将被装载、渲染到页面上
render:组件在这里生成虚拟的DOM节点
componentDidMount:组件真正在被装载之后
2、运行中状态:
componentWillReceiveProps:组件将要接收到属性的时候调用
shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回false,接收数据后不更新,阻止render调用,后面的函数不会被继续执行了)
componentWillUpdate:组件即将更新不能修改属性和状态
render:组件重新描绘
componentDidUpdate:组件已经更新
3、销毁阶段:
componentWillUnmount:组件即将销毁
必须记住的生命周期函数:
*加载的时候:componentWillMount、 render 、componentDidMount(dom操作)
更新的时候:componentWillUpdate、render、componentDidUpdate
*销毁的时候: componentWillUnmount
diff算法作为Virtual DOM的加速器,其算法的改进优化是React整个界面渲染的基础和性能的保障,同时也是React源码中最神秘的,最不可思议的部分
传统diff算法通过循环递归对比差异,算法复杂度为O(n3)。react diff算法制定了三条策略,将算法复杂度从 O(n3)降低到O(n)。
ID
来区分。针对这三个策略,react diff实施的具体策略是:
由于react中性能主要耗费在于update阶段的diff算法,因此性能优化也主要针对diff算法。
1.减少diff算法触发次数
A、 setState机制在正常运行时,由于批更新策略,已经降低了update过程的触发次数。
因此,setState优化主要在于非批更新阶段中(timeout/Promise回调),减少setState的触发次数。
常见的业务场景即处理接口回调时,无论数据处理多么复杂,保证最后只调用一次setState。
B、父组件的render必然会触发子组件进入update阶段(无论props是否更新)。此时最常用的优化方案即为shouldComponentUpdate方法。最常见的方式为进行this.props和this.state的浅比较来判断组件是否需要更新。或者直接使用PureComponent,原理一致。需要注意的是,父组件的render函数如果写的不规范,将会导致上述的策略失效。
C、使用shouldComponentUpdate钩子,根据具体的业务状态,减少不必要的props变化导致的渲染。如一个不用于渲染的props导致的update。
另外, 也要尽量避免在shouldComponentUpdate 中做一些比较复杂的操作, 比如超大数据的pick操作等。
合理设计state,不需要渲染的state,尽量使用实例成员变量。
2、正确使用diff算法
不使用跨层级移动节点的操作。
对于条件渲染多个节点时,尽量采用隐藏等方式切换节点,而不是替换节点。
尽量避免将后面的子节点移动到前面的操作,当节点数量较多时,会产生一定的性能问题。
性能检测工具
React官方提供的:React.addons.Perf
React是facebook搞出来的一个轻量级的组件库,用于解决前端视图层的一些问题,就是MVC中V层的问题,它内部的Instagram网站就是用React搭建的。
解决了三个问题: 1.组件复用问题, 2.性能问题,3.兼容性问题:
React 会创建一个虚拟 DOM(virtual DOM)。当一个组件中的状态改变时,React 首先会通过 “diffing” 算法来标记虚拟 DOM 中的改变,第二步是调节(reconciliation),会用 diff 的结果来更新 DOM。
优点:
1.只需查看 render 函数就会很容易知道一个组件是如何被渲染的
2.JSX 的引入,使得组件的代码更加可读,也更容易看懂组件的布局,或者组件之间是如何互相引用的
3.支持服务端渲染,这可以改进 SEO 和性能
4.易于测试
5.React 只关注 View 层,所以可以和其它任何框架(如Backbone.js, Angular.js)一起使用
Angular是一个成熟的MVC框架,带有很多特定的特性,比如服务、指令、模板、模块、解析器等等。React是一个非常轻量级的库,它只关注MVC的视图部分。
Angular遵循两个方向的数据流,而React遵循从上到下的单向数据流。React在开发特性时给了开发人员很大的自由,例如,调用API的方式、路由等等。我们不需要包括路由器库,除非我们需要它在我们的项目。
AngularJS是为了克服HTML在构建应用上的不足而设计的。 AngularJS有着诸多特性,最为核心的是:
Angular 在 scope 模型上设置了一个监听队列,用来监听数据变化并更新 view 。每次绑定一个东西到 view 上时 AngularJS 就会往 $watch 队列里插入一条 $watch ,用来检测它监视的 model 里是否有变化的东西。当浏览器接收到可以被 angular context 处理的事件时, $digest 循环就会触发,遍历所有的 $watch ,最后更新 dom。
1、每个双向绑定的元素都有一个watcher
2、在某些事件发生的时候,调用digest脏数据检测。
这些事件有:表单元素内容变化、Ajax请求响应、点击按钮执行的函数等。
3、脏数据检测会检测rootscope下所有被watcher的元素。
$digest
函数就是脏数据监测
单页 Web 应用 (single-page application 简称为 SPA) 是一种特殊的 Web 应用。它将所有的活动局限于一个Web页面中,仅在该Web页面初始化时加载相应的HTML、JavaScript 和 CSS。一旦页面加载完成了,SPA不会因为用户的操作而进行页面的重新加载或跳转。取而代之的是利用 JavaScript 动态的变换HTML的内容,从而实现UI与用户的交互。由于避免了页面的重新加载,SPA 可以提供较为流畅的用户体验。
1、优点:
1).良好的交互体验
用户不需要重新刷新页面,获取数据也是通过Ajax异步获取,页面显示流畅。
2).良好的前后端工作分离模式
单页Web应用可以和RESTful规约一起使用,通过REST API提供接口数据,并使用Ajax异步获取,这样有助于分离客户端和服务器端工作。更进一步,可以在客户端也可以分解为静态页面和页面交互两个部分。
3).减轻服务器压力
服务器只用出数据就可以,不用管展示逻辑和页面合成,吞吐能力会提高几倍;
4).共用一套后端程序代码
不用修改后端程序代码就可以同时用于Web界面、手机、平板等多种客户端;
2、缺点:
1).SEO难度较高
由于所有的内容都在一个页面中动态替换显示,所以在SEO上其有着天然的弱势,所以如果你的站点对SEO很看重,且要用单页应用,那么就做些静态页面给搜索引擎用吧。
2).前进、后退管理
由于单页Web应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理,当然此问题也有解决方案,比如利用URI中的散列+iframe实现。
3).初次加载耗时多
为实现单页Web应用功能及显示效果,需要在加载页面的时候将JavaScript、CSS统一加载,部分页面可以在需要的时候加载。所以必须对JavaScript及CSS代码进行合并压缩处理,如果使用第三方库,建议使用一些大公司的CDN,因此带宽的消耗是必然的。
还有2件事拜托大家
一:求赞 求收藏 求分享 求留言,让更多的人看到这篇内容
二:欢迎添加我的个人微信
备注“资料”, 300多篇原创技术文章,海量的视频资料即可获得
备注“加群”,我会拉你进技术交流群,群里大牛学霸具在,哪怕您做个潜水鱼也会学到很多东西
金三银四,磨砺锋芒;剑指大厂,扬帆起航(2020年最全大厂WEB前端面试题精选)下
金三银四,磨砺锋芒;剑指大厂,扬帆起航(2020年最全大厂WEB前端面试题精选)中
金三银四,磨砺锋芒;剑指大厂,扬帆起航(2020年最全大厂WEB前端面试题精选)上
11道浏览器原理面试题
这儿有20道大厂面试题等你查收
2020 前端面试 | “HTML + CSS + JS”专题
图解浏览器的工作原理(史上最全)
BAT前端经典面试问题:史上最最最详细的手写Promise教程
webpack 中文网