1、React 中 keys 的作用是什么?
Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
render () {
return (
<ul>
{this.state.todoItems.map(
({item, key}) => {
return <li key={key}>{item}</li>
})
}
</ul>
)}
在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性。
2、正则表达式验证相关面试题。
// 手机号码
function checkPhone(phone){
if(!(/^1[3456789]\d{9}$/.test(phone))){
alert("手机号码有误,请重填");
return false;
}
}
// 判断字符串以字母开头,后面可以是数字,下划线,字母,长度为6-30
function checout(str){
var reg=/^[a-zA-Z]\w{5,29}$/;
if(str && reg.test(str)){
alert('正确')
}
}
// 写一个function,清除字符串前后的空格。
function trim(str) {
if (str & typeof str === "string") {
return str.replace(/(^s*)|(s*)$/g,"");
}
}
3、javascript的typeof返回哪些数据类型.
string,boolean,number,undefined,function,object
4、例举3种强制类型转换和2种隐式类型转换?
答案:强制(parseInt,parseFloat,number) 隐式(== ===)
4、split() join() 的区别
答案:前者是将字符串切割成数组的形式,后者是将数组转换成字符串
5、 ajax请求的时候get 和post方式的区别
- GET请求会将参数跟在URL后进行传递,而POST请求则是作为HTTP消息的实体内容发送给WEB服务器。当然在Ajax请求中,这种区别对用户是不可见的
- GEt传输数据容量小,不安全,post传输数据内容大,更加安全;
6、call和apply的区别
相同点:改变函数的this指向
var obj = {name: 'lisi'}
function fn() {
console.log(this)
}
fn.call(null) //this指向window
fn.apply(null) //this指向window
fn.call(obj) //this指向obj
fn.apply(obj) //this指向obj
不同点: call传参形式是,从第二个开始一个一个传,apply的第二个参数为数组,数组的每一项为函数的参数
Object.call(this,obj1,obj2,obj3)
Object.apply(this,arguments)
7、事件委托是什么
利用事件冒泡的原理,让自己的所触发的事件,让他的父元素代替执行!
8、闭包是什么,有什么特性,对页面有什么影响,有什么好处,闭包使用场景:
***闭包***就是能够读取其他函数内部变量的函数,使得函数不被GC回收,如果过多使用闭包,容易导致内存泄露(不再用到的内存,没有及时释放,就叫做内存泄漏) 闭包的好处 希望一个变量长期驻扎在内存当中(不被垃圾回收机制回收), 避免全局变量的污染,私有成员的存在,安全性提高 闭包使用场景: 函数作为返回值;函数作为参数传递;闭包实际应用中主要用于封装变量,收敛权限;
9、如何阻止事件冒泡,如何阻止默认事件
阻止事件冒泡:ev.stopPropagation();何阻止默认事件: (1)return false;(2) ev.preventDefault();
10、 解释jsonp的原理,以及为什么不是真正的ajax
动态创建script标签,回调函数,Ajax是页面无刷新请求数据操作
11、”==”和“===”的不同
前者会自动转换类型,再判断是否相等,后者不会自动类型转换,直接去比较
12、函数声明与函数表达式的区别?
在Javscript中,解析器在向执行环境中加载数据时,对函数声明和函数表达式并非是一视同仁的,解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问),至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解析执行。
// 函数声明
function add(){}
// 函数表达式
var del = function del(){} // 命名函数函数表达式
var update= function(){} // 命名函数函数表达式
function(){} // 匿名函数表达式
函数表达式可以直接在函数定义后面加小括号执行,而函数声明则不行
var mult = function(x,y){ return(x*y); }();//正常
function div(x,y){ return(x/y); }()//报错
13、对作用域上下文和this的理解,看下列代码:
var User = {
count: 1,
getCount: function() {
return this.count;
}
};
console.log(User.getCount()); // what?
var func = User.getCount;
console.log(func()); // what?
问两处console输出什么?为什么? 答案:是1和undefined。 func是在window的上下文中被执行的,所以不会访问到count属性。
14、看下面代码,给出输出结果
for(var i = 1; i <= 3; i++){ //建议使用let 可正常输出i的值
setTimeout(function(){
console.log(i);
},0);
};
答案:4 4 4。 原因:Javascript事件处理器在线程空闲之前不会运行。
15、Javascript的事件流模型都有什么?以及事件流的典型应用事件代理
- “事件冒泡”:事件开始由最具体的元素接受,然后逐级向上传播
- “事件捕捉”:事件由最不具体的节点先接收,然后逐级向下,一直到最具体的
- “DOM事件流”:三个阶段:事件捕捉,目标阶段,事件冒泡
事件代理
- 事件代理的原理用到的就是事件冒泡和目标元素,把事件处理器添加到父元素,等待子元素事件冒泡,并且父元素能够通过target(IE为srcElement)判断是哪个子元素或者e.stopPropagation()阻止冒泡,从而做相应处理。
- 1)将多个事件处理器减少到一个,因为事件处理器要驻留内存,这样就提高了性能。想象如果有一个100行的表格,对比传统的为每个单元格绑定事件处理器的方式和事件代理(即table上添加一个事件处理器),不难得出结论,事件代理确实避免了一些潜在的风险,提高了性能。 2)DOM更新无需重新绑定事件处理器,因为事件代理对不同子元素可采用不同处理方法。如果新增其他子元素(a,span,div等),直接修改事件代理的事件处理函数即可,不需要重新绑定处理器,不需要再次循环遍历。
16、下满代码输出什么
var a = null;
alert(typeof a);
答案:object
解释:null是一个只有一个值的数据类型,这个值就是null。表示一个空指针对象,所以用typeof检测会返回”object”。
17、回答以下代码,alert的值分别是多少?
<script>
var a = 100;
function test(){
alert(a);
a = 10; //去掉了var 就变成定义了全局变量了
alert(a);
}
test();
alert(a);
</script>
正确答案是: 100, 10, 10
18、javaScript的2种变量范围有什么不同?
- 全局变量:当前页面内有效
- 局部变量:函数方法内有效
19、js 实现一个函数对javascript中json 对象进行克隆
var oldObject ="sdf";
var newObject = JSON.parse(JSON.stringify(oldObject));
console.log(newObject);
20、js 实现 ajax 请求或者submit请求时 锁屏功能以及开锁功能(请求时界面Loading以及元素不能点击,请求完成即消除Loading)
function(url, fn) {
// XMLHttpRequest对象用于在后台与服务器交换数据
var obj = new XMLHttpRequest();
obj.open('GET', url, true);
obj.onreadystatechange = function() {
if(obj.readyState == 4 && obj.status == 200||obj.status == 304){
loading.style.display = "none"
} else {
alert("不能点击,哈哈哈!");
}
};
obj.send(null);
}
21、请用js计算1-10000中出现的0 的次数
// 创建一个1-10000的数组集合
var arr = new Array(10000).fill('').map((item, index) => {
return index + 1
}
)
// 筛选出了所有带0的数字
var nerArrrr = arr.filter(item => /0/.test(item))
var length = nerArrrr.reduce((count, item) => {
return count + (String(item).match(/0/g)).length
}, 0)
console.log(length)
22、数组降维
var arr = [[0,1,2,3], [4,5,6,7], [8,9,0,1], [2,3,4,5]];
// 方法一
var result = [];
for(var i=0;i<arr.length;i++){
for(var j=0;j<arr[i].length;j++){
result.push(arr[i][j]);
}
}
// 方法二
var result = [];
for(var i=0;i<arr.length;i++){
result = result.concat(arr[i]);
}
// 方法三
function Jw(obj){
return Array.prototype.concat.apply([],obj);
}
Jw(arr);
23、将url的查询参数解析成字典对象
function getUrlDic(){
var queryDec = window.location.search.substring(1).split("&")
var decObect = {}
for(var i = 0; i < queryDec.length; i++){
var searchHash = queryDec[i].split("=")
var key = decodeURIComponent(searchHash[0])
var value = decodeURIComponent(searchHash[1])
decObect[key] = value
}
console.log(decObect)
}
getUrlDic()
24、判断一个字符串中出现次数最多的字符,统计这个次数
let str = 'asfjasiofoivnoi';
function count(str){
let obj = {},
arr = str.split('');
//遍历数组
arr.forEach(function(val,index){
//将数组的元素存入对象,初始值为1,如果后面遍历的元素之前已存在就+1
if(obj[val]){
obj[val]+=1;
}else{
obj[val]=1
}
})
//遍历对象中的属性(字符),值(出现的次数)
let num=0
for(let key in obj){
if(num < obj[key]){
//将最多的次数赋给num
num = obj[key];
}
}
for(let key in obj){
if(num == obj[key]){
console.log('最多的字符串是'+key+', 出现次数是'+num);
}
}
}
count(str);
25、null和undefined的区别?
1、首先看一个判断题:null和undefined 是否相等
console.log(null==undefined)//true
console.log(null===undefined)//false
typeof null; // "object"
typeof undefined; // "undefined"
- 原因:null: Null类型,代表“空值”
- 当一个声明了一个变量未初始化时,得到的就是undefined。
2、那到底什么时候是null,什么时候是undefined呢?null表示"没有对象",即该处不应该有值。典型用法是
-
作为函数的参数,表示该函数的参数不是对象。
-
作为对象原型链的终点。
undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。典型用法是:
- 变量被声明了,但没有赋值时,就等于undefined。
- 调用函数时,应该提供的参数没有提供,该参数等于undefined。
- 对象没有赋值的属性,该属性的值为undefined。
- 函数没有返回值时,默认返回undefined。
26、new操作符具体干了什么呢?
我们知道 new 运算符是用来实例化一个类,从而在内存中分配一个实例对象。
new共经过了4几个阶段 1、创建一个空对象
var obj=new Object();
2、设置原型链
obj.__proto__= Base.prototype;
3、让Base中的this指向obj,并执行Base的函数体。
var result =Base.call(obj);
4、判断Base的返回值类型:如果是值类型,返回obj。如果是引用类型,就返回这个引用类型的对象。
if (typeof(result) == "object"){
Base=result;
}
else{
Base=obj;;
}
27、defer和async的区别
defer和async都是可选的,且只对外部脚本文件有效
<script src="main.js"></script>
浏览器会立即加载并执行指定的脚本,“立即”指在渲染该script标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。
<script async src="main.js"></script>
浏览器会立即下载脚本,但不妨碍页面中的其他操作,比如下载其他资源或等待加载其他脚本。加载和渲染后续文档元素的过程和main.js的加载与执行并行进行(异步)。
async不保证按照脚本出现的先后顺序执行,因此,确保两者之前互不依赖非常重要,指定async属性的目的是不让页面等待两个脚本的下载和执行,从而异步加载页面其他内容,建议异步脚本不要在加载期间修改DOM。
异步脚本一定会在页面的load事件前执行,但可能会在DOMContentLoaded事件触发之前或之后执行。
<script defer="defer" src="main1.js"></script>
<script defer="defer" src="main2.js"></script>
表示脚本会被延迟到文档完全被解析和显示之后再执行,加载后续文档元素的过程将和main.js的加载并行进行(异步)。HTML5规范要求脚本按照它们出现的先后顺序执行,因此第一个延迟脚本会先于第二个延迟脚本执行,而这两个脚本会先于DOMContentLoaded事件。在现实当中,延迟脚本并不一定会按照顺序执行,也不一定会在DOMContentLoaded事件触发前执行,因此***最好只包含一个延迟脚本。***
- defer 和 async 在网络读取(下载)这块儿是一样的,都是异步的(相较于 HTML 解析)
- 它俩的差别在于脚本下载完之后何时执行,显然defer是最接近我们对于应用脚本加载和执行的要求的
- 关于 defer,此图未尽之处在于它是按照加载顺序执行脚本的,这一点要善加利用
- async 则是一个乱序执行的主,反正对它来说脚本的加载和执行是紧紧挨着的,所以不管你声明的顺序如何,只要它加载完了就会立刻执行 仔细想想,async 对于应用脚本的用处不大,因为它完全不考虑依赖(哪怕是最低级的顺序执行),不过它对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的,最典型的例子:Google Analytics
27、js延迟加载的方式有哪些?
defer和async、动态创建DOM方式(创建script,插入到DOM中,加载完毕后callBack)、按需异步载入js
28、javaScript的2种变量范围有什么不同?
- 全局变量:当前页面内有效
- 局部变量:函数方法内有效
29vue双向数据绑定的原理是什么
首先传输对象的双向数据绑定 Object.defineProperty(target, key, decription),在decription中设置get和set属性(此时应注意description中get和set不能与描述属性共存) 数组的实现与对象不同。
let obj = {}
let input = document.getElementById('input')
let span = document.getElementById('span')
Object.defineProperty(obj, 'text', {
configurable: true,
enumerable: true,
get() {
console.log('获取数据了')
},
set(newVal) {
console.log('数据更新了')
input.value = newVal
span.innerHTML = newVal
}
})
input.addEventListener('keyup', function(e) {
obj.text = e.target.value
})
同时运用观察者模式实现wather, 完成用户数据和view视图的更新
28、以下JS函数用于获取url参数:
function getQueryVariable(variable)
{
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
}
29、react setState后会发生什么?
setState() 将总是触发一次重绘,除非在 shouldComponentUpdate() 中实现了条件渲染逻辑。如果可变对象被使用了,但又不能在 shouldComponentUpdate() 中实现这种逻辑,仅在新 state 和之前的 state 存在差异的时候调用 setState() 可以避免不必要的重新渲染。
react setState后:
- React会将当前传入的参数对象与组件当前的状态合并,然后触发调和过程,在调和的过程中,React会以相对高效的方式根据新的状态构建React元素树并且重新渲染整个UI界面.
- React得到的元素树之后,React会自动计算出新的树与老的树的节点的差异,然后根据差异对界面进行最小化的渲染,在React的差异算法中,React能够精确的知道在哪些位置发生看改变以及应该如何去改变,这样就保证了UI是按需更新的而不是重新渲染整个界面.
setState—常规情况:在同一个方法中多次setState是会被合并的,并且对相同属性的设置只保留最后一次的设置 参考详情
31、react 生命周期函数
初始化阶段,这是组件即将开始其生命之旅并进入 DOM 的阶段。
-
getDefaultProps:获取实例的默认属性
-
getInitialState:获取每个实例的初始化状态
-
componentWillMount:组件即将被装载、渲染到页面上,在渲染之前执行,在客户端和服务器端都会执行
-
render:组件在这里生成虚拟的 DOM 节点
-
componentDidMount:仅在第一次渲染后在客户端执行,组件真正在被装载之后(请求数据)
-
运行中状态;一旦组件被添加到 DOM,它只有在 prop 或状态发生变化时才可能更新和重新渲染。这些只发生在这个阶段。
-
componentWillReceiveProps:通过父组件修props改时候调用,当从父类接收到 props 并且在调用另一个渲染器之前调用。
-
shouldComponentUpdate:(nextProps及nextState)组件接受到新属性或者新状态的时候(可以返回 false,接收数据后不更新,阻止 render 调用,后面的函数不会被继续执行了)
-
componentWillUpdate:在 DOM 中进行渲染之前调用,组件即将更新不能修改属性和状态
-
render:组件重新描绘
-
componentDidUpdate:在渲染发生后立即调用,组件已经更新
销毁阶段,这是组件生命周期的最后阶段,组件被销毁并从 DOM 中删除
componentWillUnmount:组件被卸载的时候调用。在此方法中执行任何必要的清理,一般在componentDidMount里面注册的事件需要在这里删除,例如使计时器无效、取消网络请求或清除在组件,清理内存空间
32、React绑定this的三种方式
- bind()
- 构造函数内部绑定,在构造函数constructor内绑定this,好处是仅需要绑定一次,避免每次渲染时都要重新绑定,函数在别处复用时也无需再次绑定
- 箭头函数 ##33、 javascript对象和数组的深拷贝与浅拷贝 juejin.cn/post/684490…
34、Cookie在客户机上是如何存储的
Cookies就是服务器暂存放在你的电脑里的文本文件,好让服务器用来辨认你的计算机。当你在浏览网站的时候,Web服务器会先送一小小资料放在你的计算机上,Cookies 会帮你在网站上所打的文字或是一些选择都记录下来。当下次你再访问同一个网站,Web服务器会先看看有没有它上次留下的Cookies资料,有的话,就会依据Cookie里的内容来判断使用者,送出特定的网页内容给你。
35、编写一个方法 去掉一个数组的重复元素
方法一:Array.from()方法可以将Set结构转化为数组结构
function unique(array) {
return Array.from(new Set(array));
}
unique([1,2,3,3]);
方法二:扩展运算符(…)内部使用for…of循环
let arr = [1,2,3,3];
let unique = [...new Set(arr)];
方法三
function unique(arr){
var isRepeated, result = [];
for(var i=0; i<arr.length; i++){
isRepeated = false;
for(var j=0; j<result.length; j++){
if(arr[i] === result[j]){
isRepeated = true;
break;
}
}
if(!isRepeated){
result.push(arr[i]);
}
}
return result;
}
var arr = [1,2,3,4,3,2,1,2,3];
console.log(unique(arr));
36、73、你如何组织自己的代码?是使用模块模式,还是使用经典继承的方法?
对内:模块模式
对外:继承
37、你如何优化自己的代码?
-
代码重用
-
避免全局变量(命名空间,封闭空间,模块化mvc..)
-
拆分函数避免函数过于臃肿
-
注释
你能解释一下JavaScript中的继承是如何工作的吗?
子构造函数中执行父构造函数,并用call\apply改变this 克隆父构造函数原型上的方法
38、dom选择器优先级是什么,以及权重值计算(一道老问题了)
- 1.行内样式 1000
- 2.id 100
- 3.类选择器、伪类选择器、属性选择器[type="text"] 10
- 4.标签选择器、伪元素选择器(::first-line) 1
- 5.通配符*、子选择器、相邻选择器 0
39、react和vue比较来说有什么区别
react整体是函数式的思想,把组件设计成纯组件,状态和逻辑通过参数传入,所以在react中,是单向数据流,推崇结合immutable来实现数据不可变。react在setState之后会重新走渲染的流程,如果shouldComponentUpdate返回的是true,就继续渲染,如果返回了false,就不会重新渲染,PureComponent就是重写了shouldComponentUpdate,然后在里面作了props和state的浅层对比。
而vue的思想是响应式的,也就是基于是数据可变的,通过对每一个属性建立Watcher来监听,当属性变化的时候,响应式的更新对应的虚拟dom。
总之,react的性能优化需要手动去做,而vue的性能优化是自动的,但是vue的响应式机制也有问题,就是当state特别多的时候,Watcher也会很多,会导致卡顿,所以大型应用(状态特别多的)一般用react,更加可控。
40、水平垂直居中
// 第一种
#container{
position:relative;
}
#center{
width:100px;
height:100px;
position:absolute;
top:50%;
left:50%;
transform: translate(-50%,-50%);
}
// 第二种
#container{
position:relative;
}
#center{
width:100px;
height:100px;
position:absolute;
top:50%;
left:50%;
margin:-50px 0 0 -50px;
}
// 第三种
#container{
position:relative;
}
#center{
position:absolute;
margin:auto;
top:0;
bottom:0;
left:0;
right:0;
}
// 第四种 flex
#container{
display:flex;
justify-content:center;
align-items: center;
}
41、怎么判断两个对象相等
JSON.stringify(obj)==JSON.stringify(obj);//true
42、Vue router怎么实现跳转?
router-link
router.go(1)
router.push('/')
43、Vue router 跳转和 location.href 有什么区别?
router 是 hash 改变
location.href 是页面跳转,刷新页面
44、重排和重绘,什么情况会触发重排和重绘
部分渲染树(或者整个渲染树)需要重新分析并且节点尺寸需要重新计算。这被称为重排。注意这里至少会有一次重排-初始化页面布局。 由于节点的几何属性发生改变或者由于样式发生改变,例如改变元素背景色时,屏幕上的部分内容需要更新。这样的更新被称为重绘。
- 添加、删除、更新 DOM 节点
- 通过 display: none 隐藏一个 DOM 节点-触发重排和重绘
- 通过 visibility: hidden 隐藏一个 DOM
- 节点-只触发重绘,因为没有几何变化
- 移动或者给页面中的 DOM 节点添加动画
- 添加一个样式表,调整样式属性
- 用户行为,例如调整窗口大小,改变字号,或者滚动。
45、git使用过程中,如果你在开发着业务,突然另一个分支有一个bug要改,你怎么办
git stash //将本次修改存到暂存区(紧急切换分支时)
git stash list
git stash pop //将所有暂存区的内容取出来
46、Git 常用命令
git add # 将工作区的修改提交到暂存区
git commit # 将暂存区的修改提交到当前分支
git reset # 回退到某一个版本
git stash # 保存某次修改
git pull # 从远程更新代码
git push # 将本地代码更新到远程分支上
git reflog # 查看历史命令
git status # 查看当前仓库的状态
git diff # 查看修改
git log # 查看提交历史
git revert # 回退某个修改
47、js加载位置区别优缺点
html文件是自上而下的执行方式,但引入的css和javascript的顺序有所不同,css引入执行加载时,程序仍然往下执行,而执行到script脚本是则中断线程,待该script脚本执行结束之后程序才继续往下执行。 所以,大部分网上讨论是将script脚本放在body之后,那样dom的生成就不会因为长时间执行script脚本而延迟阻塞,加快了页面的加载速度。 但又不能将所有的script放在body之后,因为有一些页面的效果的实现,是需要预先动态的加载一些js脚本。所以这些脚本应该放在body之前。 其次,不能将需要访问dom元素的js放在body之前,因为此时还没有开始生成dom,所以在body之前的访问dom元素的js会出错,或者无效 script放置位置的原则“页面效果实现类的js应该放在body之前,动作,交互,事件驱动,需要访问dom属性的js都可以放在body之后
47、vue-cli 工程常用的 npm 命令有哪些?
npm install
npm run dev
npm run build
安装模块;
-save-dev 是指将包信息添加到 package.json 里的 devDependencies节点,表示开发时依赖的包。
npm install --save-dev
-save 是指将包信息添加到 package.json 里的dependencies节点,表示发布时依赖的包。
npm install --save
48、vue中 keep-alive 组件的作用?
keep-alive是Vue.js的一个内置组件。它能够把不活动的组件实例保存在内存中,而不是直接将其销毁,它是一个抽象组件,不会被渲染到真实DOM中,也不会出现在父组件链中。 它提供了include与exclude两个属性,允许组件有条件地进行缓存。
49、从浏览器地址栏输入url到显示页面的步骤
(1)域名解析 在浏览器地址栏输入URL,浏览器查看缓存,判断请求资源是否新鲜,浏览器解析URL获取协议,主机,端口,path。浏览器组装一个HTTP(GET)请求报文,浏览器获取主机ip地址(从缓存、hosts、路由、DNS解析等)
(2)TCP连接HTTP协议是使用TCP协议作为其传输层协议的,在拿到服务器的IP地址后,浏览器客户端会与服务器建立TCP连接。该过程包括三次握手
第一次握手:建立连接时,客户端向服务端发送请求报文 第二次握手:服务器收到请求报文后,如同意连接,则向客户端发送确认报文 第三次握手,客户端收到服务器的确认后,再次向服务器给出确认报文,完成连接。
三次握手主要是为了防止已经失效的请求报文字段发送给服务器,浪费资源。 **(3)浏览器发送HTTP请求 浏览器构建http请求报文,并通过TCP协议传送到服务器的指定端口。http请求报文一共包括三个部分: 请求行:指定http请求的方法、url、http协议版本等 请求头:描述浏览器的相关信息,语言、编码等,是否包含缓存验证信息如果验证缓存新鲜,返回304 请求正文:当发送POST,PUT等请求时,通常需要向服务器传递数据。这些数据就储存在请求正文中。
(4)浏览器接受相应
然后根据情况选择关闭TCP连接或者保留重用; 如果资源可缓存,进行缓存; 对响应进行解码(例如gzip压缩); 根据资源类型决定如何处理(假设资源为HTML文档);
(6)浏览器页面渲染 解析HTML文档,构件DOM树,下载资源,构造CSSOM树,执行js脚本,这些操作没有严格的先后顺序,以下分别解释 构建DOM树: 根据HTML规范将字符流解析为标记 词法分析将标记转换为对象并定义属性和规则 根据HTML标记关系将对象组成DOM树 解析过程中遇到图片、样式表、js文件,启动下载 构建CSSOM树: 字符流转换为标记流 根据标记创建节点 节点创建CSSOM树 根据DOM树和CSSOM树构建渲染树: 从DOM树的根节点遍历所有可见节点,不可见节点包括:
- script,meta这样本身不可见的标签
- 被css隐藏的节点,如display: none
对每一个可见节点,找到恰当的CSSOM规则并应用 发布可视节点的内容和计算样式
js解析如下:
浏览器创建Document对象并解析HTML,将解析到的元素和文本节点添加到文档中,此时document.readystate为loading HTML解析器遇到没有async和defer的script时,将他们添加到文档中,然后执行行内或外部脚本。这些脚本会同步执行,并且在脚本下载和执行时解析器会暂停。这样就可以用document.write()把文本插入到输入流中。同步脚本经常简单定义函数和注册事件处理程序,他们可以遍历和操作script和他们之前的文档内容 当解析器遇到设置了async属性的script时,开始下载脚本并继续解析文档。脚本会在它下载完成后尽快执行,但是解析器不会停下来等它下载。异步脚本禁止使用document.write(),它们可以访问自己script和之前的文档元素 当文档完成解析,document.readState变成interactive 所有defer脚本会按照在文档出现的顺序执行,延迟脚本能访问完整文档树,禁止使用document.write() 浏览器在Document对象上触发DOMContentLoaded事件 此时文档完全解析完成,浏览器可能还在等待如图片等内容加载,等这些内容完成载入并且所有异步脚本完成载入和执行,document.readState变为complete,window触发load事件 显示页面(HTML解析过程中会逐步显示页面)
59、H5与iOS/Android的通信方式(原生内嵌H5页面通信模式浅析
引入JsBridge(安卓)或WebViewJavascriptBridge(iOS)库的方案,首先安卓、iOS、web前端三方要在一起定义好需要使用的接口的方法名及传递的参数,三方统一,定义好各个方法及传递的方式
60、redux中间件
redux中间件中间件提供第三方插件的模式,自定义拦截 action -> reducer 的过程。变为 action -> middlewares -> reducer 。这种机制可以让我们改变数据流,实现如异步 action ,action 过滤,日志输出,异常报告等功能。常见的中间件:
- redux-logger:提供日志输出
- redux-thunk:处理异步操作
- redux-promise:处理异步操作,actionCreator的返回值是promise
61.redux有什么缺点
- 一个组件所需要的数据,必须由父组件传过来,而不能像flux中直接从store取。
- 当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新render,可能会有效率影响,或者需要写复杂的shouldComponentUpdate进行判断。
62、react组件的划分业务组件技术组件?
根据组件的职责通常把组件分为UI组件和容器组件。UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。两者通过React-Redux 提供connect方法联系起来。、
63、为什么虚拟dom会提高性能?
虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。
具体实现步骤如下:用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。
64、react diff算法?
把树形结构按照层级分解,只比较同级元素。给列表结构的每个单元添加唯一的key属性,方便比较。React 只会匹配相同class的component (这里面的class指的是组件的名字)合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个事件循环结束, React 检查所有标记 dirty 的component重新绘制.选择性子树渲染。开发人员可以重写shouldComponentUpdate提高diff的性能。
65、react性能优化方案
- 重写shouldComponentUpdate来避免不必要的dom操作。
- 使用 production 版本的react.js。
- 使用key来帮助React识别列表中所有子组件的最小变化。