赞
踩
1.响应式布局如何实现
响应式布局可以让网站同时适配不同分辨率和不同的手机端,让客户有更好的体验。 为什么要使用响应式布局: 在之前使用固定宽高有一定的局限:屏幕越来越宽时,因为定 得宽是固定的,这样会让页面不美观屏幕越来越小时,因为定宽的局限会让用户看不到完整的页面内容 在这种情况下,响应式布局就出现了 响应式布局的实现(有五种方法) 1.百分比布局:百分比布局是相对于父元素进行布局,百分比能够对width,height,padding,margin来进行布局,border,font-size不能; 局限: 因为百分比布局只能对大面积的地方进行布局,所以不够完美 2.css3媒体查询:媒体查询可以根据不同的分辨率来设置不同的css样式,通过这种方式来适配不同屏幕,相比于百分比布局,媒体查询可以对布 局进行更细致的调整 @media screen and(min-width:1200px) @media screen and(max-width:1200px) Screen就是客户端屏幕的信息 局限:媒体查询在设置的时候需要在每个分辨率和操作下设置不同的css样式 3.rem响应式布局:rem是一个单位,跟px,em类似,1rem就等于html的font-size值,有两种方法可以进行设置 1.通过媒体查询来设置,在不同的分辨率下给html的font-size赋值;比较麻烦 2.js进行动态计算给font-size设置值:封装一个函数:通过屏幕的宽度去处理某个数,得出来的值就是html的font-size值,当屏幕宽 度改变的时候就调用这个函数,这样在使用rem的时候就会根据html的font-size来进行等比缩放; 4.弹性盒子布局:通过display:flex;来布局,给父元素设置display:flex;在给子元素设置想要改变的属性; 5.vw响应式布局:vw是基于视口的布局方案,所以在使用的时候需要在meta标签的视口中声明;1vw等于视口宽度的1% 1vh等于视口高度的1%; 现在用的都是vw,因为高度是根据内容的多少而自适应的。
原理:rem是指相对于根元素的字体大小的单位,是基于根元素的font-size值来进行计算,就是1rem就等于html的font-size值;
1rem等于多少px,就是font-size的值*
你设定的rem的值;
--------怎么使用rem来布局
使用Js来动态修改根元素的font-size值,当屏幕宽度发生改变的时候rem就会被动态计算出来,那你设置的rem就会自动转换为px单位;
JS代码:
function rem(){
document.documentElement.style.fontSize=document.documentElement.clientWidth/7.5+'px'
}
rem()
window.onresize=rem;
一共有4中数据类型判断的方法
1.typeof()他可以检测一些普通数据类型,比较直观的数据,比如number,string这些能够具体的检测出来他们的类型,
判断引用数据类型无论引用的对象是什么类型都返回object; [],null,{} :object
2.instanceof 他可以检测引用数据类型,可以判断一个变量是否是某个类型和是否是某个实例,由构造类型判断数据类型,返回的是true或者false,
不可以判断null和undefined fn instanceof Fn
3.constructor 他是通过对象的construnctor直接来判断他是否和他的原型相同,但是一旦构造函数的原型被更改那么这个方法就不好用了
fn.constructor == Array
4.Object.prototype.toString.call() 他目前是一个最完美的解决方案,他只需要在括号里边写你想要判断的数据类型就行了,
他可以直接从原型里面找要判断的类型
函数原型:每个函数都有一个prototype对象,这个对象就是函数的原型,每个函数原型中都有一个constructor属性,就是构造函数本身,
构造函数被实例化后,包含一个__proto__属性,这个属性就是函数的原型,new实例化后的对象没有prototype原型,有__proto__;
函数的原型有一个优点就是在原型中添加属性,所有的实例化对象都可以访问到
JavaScript 的所有对象中都包含了一个 [proto] 内部属性,这个属性所对应的就是自身的原型
JavaScript 的函数对象,除了原型 [proto] 之外,还有 prototype 属性,当函数对象作为构造函数创建实例时,
该 prototype 属性值将被作为实例对象的原型 [proto]
原型链:当一个对象调用自身不存在的属性/方法时,就会去自己 [proto] 关联的前辈 prototype 对象上去找,如果没找到
,就会去该 prototype 原型 [proto] 关联的前辈 prototype 去找。依次类推,直到找到属性/方法或 undefined 为止。从而形成了所谓的“原型链”。
代码实现:先定义一个函数,里面写一些属性;然后在这个函数的原型上设置一些属性;然后在new 实例化这个函数;
最后通过调用这个实例化出来的新函数来查找属性,就可以实现一个原型链;
要了解闭包,先得了解变量的作用域,分为全局作用域和局部作用域,JS中函数内部可以访问函数外部的变量,但函数外部却无法访问函数内部
的变量;
闭包就是能够访问其他函数内部的变量的函数,闭包的本质就是把函数内部和外部连接起来;
闭包有一个封闭性的特点:就是一旦形成闭包,那么外界就无法访问到闭包里面的数据定义:
当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数的内部变量,且返回的那个函数在外部被执行,就产生了闭包.
1:函数套函数
2:内部函数可以直接访问外部函数的内部变量或参数
3:变量或参数不会被垃圾回收机制回收
闭包的优缺点:
1:变量长期驻扎在内存中
2:避免全局变量的污染
3:私有成员的存在
1.虽然说防止了全局变量污染,但是这样容易导致内存泄漏,会让使用过的变量无法回收,一直占着内存;
2、对捕获的变量是引用,不是复制
3、父函数每调用一次,会产生不同的闭包
1.SetTimeout:原生的setTimeout传递的函数不能带参数,通过闭包可以实现传参效果。
2.回调:定义一个行为,然后把它关联到某个用户事件上。代码通常会作为一个回调绑定到事件上;
3.选项卡
原生的setTimeout传递的第一个函数不能带参数 setTimeout(function(param){ alert(param) },1000) 通过闭包可以实现传参效果 function func(param){ return function(){ alert(param) } } var f1 = func(1); setTimeout(f1,1000); ``` - 选项卡案例 ```js for(var i = 0; i< lis.length; i++){ (function(i){ lis[i].click = function(){ for(var m = 0; m< lis.length; m++){ lis[m].className = ""; } lis[i].className = "active"; } })(i) } 作用:改变作用域 ## 内存泄露的解决? 由于IE的js对象和DOM对象使用不同的垃圾收集方法,因此闭包在IE中会导致内存泄露问题,无法销毁驻留在内存中的元素 .在退出 > 函数之前,将不使用的局部变量全部删除。 > 2.通过添加另外一个闭包来避免JS对象和DOM对象之间的循环引用 # 6.js继承 BOM:浏览器对象模型;DOM:文档对象模型; ------1.构造函数继承:构造函数继承的中心思想就是把父函数的this指向改为子函数的this指向,从而实现继承; 优点:可以传递参数,借助call等改变this指向的方法; 改变this指向的方法:call 第二个参数与要改变指向中的方法的参数对应 apply 第二个参数是一个数组,数组中的元素与要改变的方法中的参数对应 bind方法使用后返回的是一个函数,所以第二个参数要在()里写,调用 缺点:他只能继承父函数本身的属性,父类原型的方法却不能使用;无法实现构造函数的复用。每个新实例都有父类构造函数的副本 #代码
function Fu(name) {
this.name = name;
}
function Zi() {
Fu.call(this,”aa”)
}
var obj = new Zi();
console.log(obj.name);
**
## 3. **bind,call,apply的区别**
**
- 都是用来改变this对象的指向
- 第一个参数都是this要指向的对象
- 都可以传递第二个参数
区别
//父函数 function Fu(name,gender,age){ this.name = name; this.gender= gender; this.age = age; this.say = function(){ console.log(this.name + " , " + this.gender + " ,今年" + this.age); } } function Zi(){ Fu.call(this, '小红','男',38); // 小红,男,38 Fu.apply(this,['小刚','女', 19]);// 小刚,女,今年19岁 Fu.bind(this)('小刚','人妖', 19); // 小刚,人妖,19岁 } var zi = new Zi(); zi.say();
总的来说 call后面的参数与要改边指向的方法中的参数是一一对应的,
apply的第二个参数是一个数组,数组中的元素和要绑定的方法一一对应
bind方法使用后返回的是一个函数,因此后面需要()进行调用才可以
//父类
function Fu(name){
this.name=name;
this.sum=function(){
alert(this.name)
}
}
Fu.prototype.age=10
```
------- 2.原型链继承:利用原型让一个引用类型继承另一个引用类型的属性和方法。
优点:子类可以继承父类和父类原型中的属性和方法;
缺点:1.通过原型来实现继承时,原型会变成另一个类型的实例,原先的实例属性变成了现在的原型属性,
该原型的引用类型属性会被所有的实例共享。
2.在创建子类型的实例时,没有办法在不影响所有对象实例的情况下给超类型的构造函数中传递参数。
#代码
function Zi(){
this.name='Key'
}
Zi.prototype=new Fu();
let Zi=new Fu()
console.log(Zi.age)
------- 3.组合式继承:组合式继承就是把构造函数继承和原型链继承结合起来,从而实现继承;
他使用原型链实现对原型属性和方法的继承,又通过借用构造函数来实现对实例属性的继承。这样既通过在原型上定义方法实现了函数复用,又保证每个实例都有他自己的属性。
缺点:1.无论什么情况下,都会调用两次超类型构造函数:一次是在创建
2.子类型原型的时候,另一次是在子类型构造函数内部。
function Zi(name){
Fu.call(this,name)
}
Zi.prototype=new Fu()
var obj=new Zi('mimi')
console.log(obj.age,obj.name)
-----4.原型式继承:
在 object() 函数内部,先传入一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,
最后返回了这个临时类型的一个新实例, 从本质上讲,object() 对传入的对象执行了一次浅拷贝。
特点:类似于复制一个对象,用函数来包装。
缺点:1.所有实例都会继承原型上的属性。
2.无法实现复用。(新实例属性都是后面添加的)
------5.寄生式继承:创建一个仅用于封装继承过程的函数, 该函数在内部已某种方式来增强对象,最后再像真地是它做了所有工作一 样返回对象。
优点:没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成了创建的新对象。
缺点:没用到原型,无法复用。
--------6.寄生组合式继承:
在寄生式继承的基础上,将父类的原型传入包裹函数中,然后定义子类的构造函数,并在子类的构造函数中调用父类的构造函数,
然后生成子类的对象,修改子类的构造函数属性,实现理想的继承
•第一步:创建超类型原型的一个副本
•第二步:为创建的副本添加 constructor 属性
•第三步:将新创建的对象赋值给子类型的原型
优点:只调用了一次超类构造函数,效率高 原型链保持不变
###冒泡排序
function czygetarr(arr){
for(var i=0;i<arr.length-1;i++){
for(var k=i+1;k<arr.length;k++){
if(arr[i]>arr[k]){
var temp =arr[i];
arr[i]=arr[k];
arr[k]=temp;
}
}
}
return arr
}
事件类型:有键盘事件,鼠标事件,表单事件 事件:事件是文档和浏览器窗口中发生的特定的交互瞬间,当我们与浏览器中 web 页面进行某些类型的交互时,事件就发生了。 事件流: 事件流描述的是页面中接收事件的顺序 1.事件冒泡:就是事件开始时由嵌套最深的元素接收,然后逐级向上传播到最外面的那个节点; 阻止冒泡:1.js中的event.stopPropagation() 2.vue修饰符 .stop 加到你想要阻止冒泡的点击事件后面 2.阻止默认行为:1.js中的event.preventDefault() 2.vue中的.prevent 3.事件捕获:由嵌套最外面的节点先触发事件,最后是嵌套最深的节点触发事件 IE 的事件流是事件冒泡流(event bubbling), 而 Netscape 的事件流是事件捕获流(event capturing)。 设置事件捕获:1.js中addEventListener(事件名字,function(){},true/false);true和false如果在未指定的情况下,默认为false,向上冒泡的 事件不会触发;true 为事件捕获,false 为事件冒泡, 2. .captrue 在定义的事件后面写click.captrue 3.事件委托:利用事件冒泡的原理,指定某一个事件程序来管理某一类型的所有事件;比如子元素的事件交由父元素进行处理; 写法: 原生 OUI.onmouseover = function(ev){ var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == "li"){ target.style.background = "red"; } } JQ写法 $("#a").on ("click","li",function(){ alert($(this).html()); })
h5:
1.增加语义化的标签:比如article、footer、header、nav、section
1.新增了webgl 游戏引擎 canvas、svg图表。
2.用于播放视频的 video 和 audio 元素;
3. 本地离线存储 localStorage 长期存度储数据,浏览器关闭知后数据不丢失;sessionStorage 的数据在浏览器关闭后自动删除。
4.表单控件:date、time、email、url、search等;
css3:1.选择器、渐变background-image: linear-gradient线性渐变、radial-gradient径向渐变
-----线性渐变(Linear Gradients)- 向下/向上/向左/向右/对角方向
background: linear-gradient(direction, color-stop1,color-stop2, ...);
-----径向渐变(Radial Gradients)- 由它们的中心定义
2.开启 3d 模式让我们的 2d 空间变成一个 3d 模式
3.变形box-shadow: 2px 2px 2px #000;
4.过渡动画transition: all默认值,所有属性都将获得过渡效果,none:没有属性会获得过渡效果, transition-duration:定义过渡效果花费的时间
5.transform属性: translate(x,y) 根据X轴和Y轴位置给定的参数,从当前元素位置移动,scale(*x*,*y*):改变元素的宽度和高度,rotate(*angle*):在参数中规定角度 旋转角度
6.盒子模型、flex弹性布局、媒体查询@media;
都是h5新增的特性 他们都是保存在浏览器端,而且是同源的
-----LocalStorage:生命周期是永久的,关闭页面或浏览器之后localStorage中的数据也不会消失。就是除非主动删除数据,否则的话数据
永远不会消失;不参与和浏览器的通信,大小为5MB 因为存储在本地,所以能长久保存
:LocalStorage.getItem(),setItem(),removeItem(),clear();
----SessionStorage: 仅在当前会话下有效,关闭页面或浏览器后被清除。存 放数据大小为一般为 5MB,而且它仅在客户端(即浏览器)中保存,
不参与和服务器的通信。源生接口可以接受,亦可再次封装来对 Object 和 Array 有更好的支持。
临时储存在浏览器的内存中,窗口关闭后,内存清空
本地存储有哪些常用的api接口?
方法:SessionStorage.getItem(),setItem(),removeItem(),clear(); 设置俩个参数:key,value 其余都是一个参数:key
键和值都是字符串的形式,如需要存储数组,需要将shuzuzhuanhuaweijson格式
----Cookie:生命周期只在设置的cookie过期时间之前有效,存放数据一般为4K左右,一般数量不能超过20个,cookie可以与服务端进行通
信,但是使用cookie保存过多数据会带来性能问题。而且cookie需要自己封装。总之cookie就是一个存放数据的东西,存放
客户端和应用设备上。
应用场景:用户注册,用户登录,购物车等
cookie的参数:name,value,域名,路径,是否是http,是否是https
cookie的操作
document.cookie = “username=sorber” name名字
document.cookie 获取cookie的内容
**##cookie 的优点**:
具有极高的扩展性和可用性
1.通过良好的编程,控制保存在 cookie 中的 session 对象的大小。
2.通过加密和安全传输技术,减少 cookie 被破解的可能性。
3.只有在 cookie 中存放不敏感的数据,即使被盗取也不会有很大的损失。
4.控制 cookie 的生命期,使之不会永远有效。这样的话偷盗者很可能拿到的就是一个过期的 cookie。</pre>
**.cookie 的缺点:**
1.cookie 的长度和数量的限制。每个 domain 最多只能有 20 条 cookie,每个cookie 长度不能超过 4KB。否则会被截掉。
2.安全性问题。如果 cookie 被人拦掉了,那个人就可以获取到所有 session
信息。加密的话也不起什么作用。
3.有些状态不可能保存在客户端。例如,为了防止重复提交表单,我们需要在服务端保存一个计数器。若吧计数器保存在客户端,则起不到什么作用。</pre> localStorage、sessionStorage、Cookie 共同点:都是保存在浏览器端,且同
源的。
深拷贝与浅拷贝都是针对与引用数据类型来说的;
浅拷贝是会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化。
应用场景就是:对于只有一层结构的Array,Object想要拷贝一个副本时使用;
实现方式:1.使用Object.assign({},obj) 参数1:一个空对象,参数2:要复制的对象;
浅拷贝不能修改基础的数据类型,可以修改引用的数据类型;
2.es6的扩展运算符
深拷贝复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。
深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。
var obj = {name:‘xixi’,age:20,company : { name : ‘腾讯’, address : ‘深圳’} };
var obj_json = JSON.parse(JSON.stringify(obj));
console.log(obj === obj_json);
obj.company.name = “ali”;
obj.name = “hei”;
console.log(obj);
console.log(obj_json);
缺点:只是简单的进行一个深拷贝,当里面的属性也是一个对象型是,就不能了,就又变成了一个浅拷贝
-b. function cloneObject(obj){
var a = {} // 声明一个空对象
for(var x in obj){ // 遍历要复制的对象
if(typeof obj[x] == "object"){// 如果属性值是对象型 递归一下 继续执行cloneObject
a[x]= cloneObject(obj[x])
}else{
a[x] = obj[x]
}
}
return a
}
var c = cloneObject(obj1);
console.log(c)
递归思想
-c. 深拷贝就是递归复制了所有的层级和地址引用。就是拷贝多层,每一级别的数据都会别拷贝出来;应用场景就是复制深层次的object的数据结构;
实现方式:1.利用JSON.parse(JSON.stringify(obj));就是先将这个对象转换为字符串,再将字符串转换为一个对象,这样就实现了一个深拷贝的原理;
2.通过定义一个函数deep来实现,他的参数是一个obj对象,函数里面第一步先判断参数obj是否为一个对象,如果不是就返回return false;
如果是的话就定义一个空的数组作为存储空间;随后在遍历obj,如果内容是一个对象的话就把循环的当前项添加到上面创建的新数组中;
最后在返回这数组;然后要是需要进行深拷贝的话就调用这个函数;
3.可 以 使 用 for in 、 Object.assign 、 扩 展 运 算 符 … 、
Array.prototype.slice()、Array.prototype.concat() 、递归等递归函数实现深拷贝
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring )。
只要等号两边的模式相同,左边的变量就会被赋予对应的值。
要是匹配不成功变量的值就是undefined;他是针对数组或者对象进行模式匹配,然后对
其中的变量进行赋值;他左边表示的是要解构的目标,右边表示解构源;解构赋值还是对赋值运算符的扩展;
应用场景:1.交换变量的值;2.让函数中返回多个值,函数只能返回一个值,但用解构赋值的话可以返回是一个数组或者对象;
3.函数参数的定义; 4.提取json数据
数组的解构赋值 var [a, b, c] = [1, 2, 3];
let [x, y, ...z] = ['a']; 如果解构不成功,变量的值就等于undefined。
x // "a"
y // undefined
z // []
对象的解构赋值 var { foo, bar } = { foo: "aaa", bar: "bbb" };
1.新增了let和const两个块级作用域;和var一样都是定义变量的关键字,var是es5里面的;let和const是es6里面的; 区别:let声明的变量不可以自动提升;var声明的可以自动提升;let不允许在相同作用域里,重复声明一个变量,var可以; let允许块级作用域的任意嵌套,外层无法读取内层作用域的变量,var可以;let声明的作用域只在当前代码块的{}内有效,let可以解决for循环i全局变量污染的问题; const声明的是一个只可以读的常量。只要声明,常量的值就不可以改变。他和let一样const也是一个块级作用域,无法变量提升,重复声明 2.新增了箭头函数 箭头函数和普通函数的不同:他们的写法不同;this指向不同:箭头函数指向的是定义时的对象,普通函数是指向调用它的对象; 箭头函数不可以当作构造函数,就是不可以使用new命令,否则报错;箭头函数没有arguments对象,得到的是外层函数的参数; 总结:箭头函数,最开始就是为了解决this指向问题,箭头函数里面的this是定义的时候确定的,this一旦确定无法改变,箭头函数最好的就是简洁; 同时箭头函数一定是匿名函数,箭头函数适合于没有复杂逻辑和无副作用的纯函数场景下,比如map,filter的回调函数中; 最好不要在最外层定义箭头函数,因为在函数内部操作this会容易污染全局作用域。在箭头函数外部包一层普通函数,可以将this控制在可见范围内; 3.增加了解构赋值 4.模板字符串 传统的输出模板特别繁琐,而且容易拼接错误 ES6中的模板字符串是增强版的字符串,用反引号``,标识,它可以当做普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量; 模板字符串里还可以2放入花括号,在花括号里边可以写入任意JS表达式,还可以引用对象属性。而且模板字符串还可以调用函数; 5.ES6还新增了Set和Map数据结构 Set对象他类似于数组,且成员的值都是唯一的。他常见的方法有5种:add():添加某个值然后返回Set结构本身。size()返回Set实例的成员总数; delete()删除某个值,返回一个布尔值,表示是否删除成功;has()返回一个布尔值,表示该值是否为Set的成员。clear()清除所有成员,没有返回值; Set集合中的数据不允许有重复,可以做数组去重1.(...new set(arr)) 2.arr.forEach(item => set.add(item)); Map对象是键值对集合,和JSON对象类似,但是key不仅可以是字符串还可以是对象; Map常见的方法也有5种:map.set()可以获取对象的属性;get()可以获取对象的name属性;size()获取元素的存储数;has()可以知道指定元素是否 存在;delete()可以删除指定属性 6.把for循环改为for of循环 for in是es5的标准,用来遍历Key值,遍历对象和数组,for of是es6的标准,用来遍历value值,遍历数组,不能遍历普通对象; 因为普通对象没有symbol属性,如果对象拥有symbol属性,就可以使用for of遍历; symbol是es6新增的一种基本数据类型,他是一个独一无二的数据类型,在es5中对象属性名字都是字符串容易造成属性名冲突,为了避免这种情况 Es6就引入了symbol类型,Symbol值能够作为对象属性的标识符; 使用for in会遍历数组所有的可枚举属性,包括原型,所以for in更适合遍历对象; for in遍历的是数组的索引,for of遍历的是数组的元素值 7.Es6新增了模块化 在Es6之前是JavaScript是没有模块系统的,那么他就无法将一个大程序拆分成若干个互相依赖的小文件;如果需要进行模块化操作,就需要从第三方引入; 在ES6中就提供了模块化,才让JavaScript第一次支持了module。ES6的模块化分为导出(export)和导入(import)两个模块; 模块可以理解为和函数代码块一样的功能,是封装对象的属性和方法的JavaScript代码,他可以是某单个文件,变量或者函数 而且引入模块和引入脚本是有区别的,前者更多的是按需引入加载,后者是无论使用还是不使用,全部一次性引入和加载,类似于通过script标签引入Jquery等,这些都是一次性引入的; 导出:Es6模块导出分为默认导出和指定导出,默认导出的话可以用任意名字来接收,比如导出的是export default module1; 接收的时候可以 import x, 如果要一次导出多个,需要用{}包裹,接收时必须与导出时同名 接收:接收用import {name,age} from './module.js' 在导出中除了export关键字,每一个声明必须和脚本中的一样,因为导出的函数和声明需要有一个名称; 导出数据,只需要在里边写上数据名就可以export {name,age} 导出函数只需要你定义好函数,然后把函数名导出就可以export sum; 8. (...)扩展运算符:用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中。 9.新增了Promise关键字
Promise是什么?
Promise是异步编程的一种解决方案,从语法上讲,promise是一个对象,通过他可以获取异步操作的消息;Promise是一个内置的构造函数,他是通过new Promise来操作;
Promise其实就是一个方法,一个JS甚至是一个页面的后续JS代码的执行,因为他们都依赖于异步返回的数据,基于这个数据进行作;
为什么会有Promise?
在ES5中处理异步,基本都是选择回调函数的请求方式,在请求到的成功回调函数里面继续写函数,长期就形成了回调地狱;
在需要的操作少的时候是可以接受的,但是在需要多个操作的时候,会导致多个回调函数嵌套,导致代码不够直观,且难以维护;
所以就出现了Promise最初就为了解决回调地狱的问题;
他采用链式调用的方式并且代码比较简洁明了且容易维护。
Promise的状态有哪些?
1.pending进行中,2.fulfilled/resolved -成功 3.rejected-失败
Promise对象代表一个异步操作,只有异步操作的结果,可以决定当前是哪一种状态,除此之外任何的操作都无法改变这个状态。
Promise原型上还有then(),catch(),all(),race()等方法;
then()是成功之后执行的一个方法;有两个参数,第一个参数是成功之后执行的,第二个是失败之后执行的;
catch()是失败后执行,可以捕获异常;
all()方法是在处理多个异步处理时非常有用,就是调用多个promise回调成功之后的统一处理;
race()方法在处理数据的时候,哪个结果获得的快,就返回哪个,不管结果本身是成功还是失败。可以用来测试多个接口的响应速度;
Promise怎么使用?
Promise是一个构造函数,所以我们在使用的时候是通过new Promise来实例化,它里面有两个默认的参数,resolve和reject是早就封装在构造函数里面的方法。
函数里面可以加一些逻辑,用来判断,返回成功就执行resolve,返回失败就执行reject;resolve方法可以把promise的状态从pedding改为resolved,。Promise有一个特性就是一旦改变
不会再变。所以只要变成resolved或者是rejected.那么他将不会再变。
从本质来说,Promise主要用来解决异步回调的回调嵌套过多,导致的地域回调问题,可以使用then链式方法捕获最后的结果,比如我们经常用的axios就是用的Promise方式处理异步请求;
Promise中封装了原生的XHR,vue中为我们提供了axios来进行数据的请求,在vue项目少的情况下使用,会很方便,简单;
但是如果在vue项目多的情况下
我们就需要在每一个vue项目中写axios请求,这样会比较繁琐并且比较消耗性能。所以我们需要对axios进行进一步的封装。
1.在src里面新建一个utils文件夹,放一个request.js文件,在这个文件里要做3件事,
(1)引入axios; (2)用axios创建一个实例化的空对象取名叫Server,
通过Server设置请求拦截器(interceptors.request)和响应拦截器(interceptors.response)
然后通过.use引入函数,请求拦截器就是当把数据发送出去,还没到达服务端的时候,拦截你的数据
看是否合法;请求拦截器函数里面有两个函数一个叫config,这个函数就是axios,当resolved调用成功之后返回 return
config; ;参数2叫error,这个是rejected失败之后掉用的;失败之后返回return
Promise.reject(error);
(3)设置响应拦截器:响应拦截器就是服务端响应回来,但是数据还没有到达我们客户端的时候;
interceptors.response.use;他也有两个函数,一个就是成功之后的response(),这里边
可以判断一下,如果status状态为200的话就只返回一个data数据,否则的话就返回一个空字符串;
如果失败的话和请求拦截器一样返回return Promise.reject(error)错误;
最后在封装好后,导出模板实例化对象;需要把Server给导出去export default Server;
然后在main.js里面引入,import axios from ‘…/utils/server.js’
Promise封装api可以用来处理一些公共数据,提高了安全性;
15.Ajax是什么?以及如何创建Ajax?
##ajax
Ajax的工作原理相当于在用户和服务器之间加了—个中间层(AJAX引擎),使用户操作与服务器响应异步化。
需要从服务器读取新数据时由Ajax引擎代为向服务器提交请求。
###.post和get的区别?
1.get是显式提交,提交到url地址栏,不安全,post是隐式提交,提交到body,安全高
2.get方式提交的内容长度有限制,post不受长度限制
3.get由于提交到地址栏,所有数据需要转换为unicode编码,post不需要转换
0:表示未初始化
1:与服务建立链接
2:服务器已接收客户端的请求
3:服务正在处理用户的请求
4:请求已经完成,服务器准备就绪
Ajax的核心是通过XMLHttpRequest()对象实现异步更新数据
XMLHttpRequest对象的常用方法和属性
- open() 初始化
- send() 发送请求
- setRequestHeader 设置请求头
- onreadystatechange 用于指定状态改变时所触发的事件处理器
- readyState 获取请求状态
- response 响应的内容
- responseText 获取服务端响应内容
- status http状态码
##优点:
可以在不重新加载整个页面的情况下实现网页的局部更新,
##缺点:
不利于seo技术,由于数据不是写在页面,是通过ajax交互,所以不能被搜索引擎的爬虫,
###用处:
var xmlhttp=new XMLHttpRequest()
xmlhttp.open(url,data,true)
xmlhttp.send()
xmlhttp.onreadystatechange=function(){
if(xmlhttp.readystate==4&&xmlhttp.status==200){
console.log(xmlhttp.responseText)
}
}
Ajax是什么? ajax主要是用来实现客户端和服务端异步通信的效果,实现页面的局部刷新,ajax原生方式主要是通过XMLHttpRequest ,ActiveObject(IE)对象来实现异步通信效果 ajax的优势在哪儿? 1.不需要插件的支持,可以直接被大多数主流的浏览器支持 2.提高web程序性能:传统模式是通过form表单,数据获取是通过页面刷新获取整页内容,ajax可以通过XMLHttpRequest对象向服务器请求数据; 3.提高了用户体验 4.无须刷新页面即可获取数据 Ajax的应用场景 用户登录注册,检测用户数据是否重复 做城市选择,用到二级联动或者三级甚至更高时,可以使用Ajax; jq方式:通过$ajax({- url:'地址', type:'get'请求方式, async: true,是否为异步, dataType:'json'数据的类型, success:(res)=>{成功之后的业务逻辑}, error:(error)=>{失败之后} }) XMLHttpRequest对象的常用方法和属性 方法:open()初始化 send()发送请求 setRequestHeader 设置请求头 属性:onreadystatechange:用于指定状态改变时触发的事件处理器 readyState:获取请求状态 responseText 获取服务端响应内容 status:http状态码 常见的http状态码 200:请求成功 301请求资源移动到新的url地址 403客户端请求无权限,服务器拒绝请求 404找不到文件 500服务内部错误,一般是服务端代码有问题 502网关错误,服务端返回无效响应 503服务器性能问题导致,无法处理客户端请求
通过浏览器发送请求才会产生跨域
1.同源策略
要了解跨域就要先知道同源策略;同源策略是浏览器的一种约定,他是浏览器最核心也是最基本的安全功能,同源指的就是,同协议,同域名,同端口,
同源策略的目的就是限制不同源的document或者脚本之间的相互访问,以免造成干扰和混乱。
如果没有了同源策略,浏览器的一些正常功能可能都会受到影响,因为ajax是很灵活的,他可以发任何的请求,如果没有同源策略的限制,那么只要你 构造好参数和请求路径你的请求想往哪发就往哪发,这样太不安全了,会导致各种数据泄露;有了同源策略,那就不能随意发送请求了。 3.跨域的发生和解决方式 当一个请求url的协议,域名,端口,三者之间任意一个与当前页面的url不同即为跨域; 跨域的解决方式常见的有三种 1.cros服务端操作:把当前请求地址加入可允许访问的header中 Access-Control-Allow-Oragin 2.vue中的代理方式 在vue中使用axios也可以配置跨域,在/config/index.js中将里面的target设置为我们要访问的域名。也就是后台ip地址。 再把changeorigin:true,设置为true开启反向代理,在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样客户端和服务端进行数据的交互就不会有跨域问题 最后pathRiwrite{'^/api':''}设置为空,这里理解成用'/api'代替target里面的地址 3.jsonp是服务器与客户端跨源通信的方法。最大的特点就是简单适用,兼容性好,缺点是只支持get请求,不支持post请求; jsonp实现的原理:主要是利用动态创建script标签请求后端接口地址,然后传递callback参数,后端接收callback,后端再经过数据处理返回 callback函数调用的形式,callback中的参数就是json; jsonp使用场景:1.在jquery的api接口中使用,dataType设置为jsonp,也可以在script标签中通过src调用,需要传递callback回调函数 2.vue-resource插件中`this.$http.jsonp('url',{}).then(res=>{})` ajax的核心是通过XMLHttpRequest获取非本页内容,jsonp的核心是通过script标签来调用服务器提供的js脚本; jsonp只支持get请求,而ajax支持get和post请求
首先就是这三个都是以函数为基础的,但他们却都不相同; computed:计算属性,计算结果会缓存,只有当依赖值改变时才会重新计算 对data里的数据做一些逻辑处理后应用到视图上,依赖于属性值,只有当属性值改变时才会执行 和methods相比有缓存性,在属性值没有发生变化时不会重新计算,效率高 在需要对很多的数据进行处理和计算并且有另一个值需要依赖这个数据进行变化的时候就需要计算属性; 购物车总计,模糊搜索 watch:watch和computed很相似,watch是用来观察和监听页面上的vue实例,在大部分情况下我们还是会使用computed,但如果要在数据变化 的同时进行异步操作的时候,warch是比较合适的选择。watch作为一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是 方法名; 如果在data中没有相应的属性的话,是不能使用watch的。 watch有一个特点,就是当值第一次改变的时候不会执行监听函数的。如果我们需要在最初绑定值的时候也 执行函数就需要用到immediate属性;就需要将immediate设为true;为true的时候就会立马执行handler方法,这个是vue规定的写法,当watch的值发生变化的时候就会触发; deep属性:当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,只有data中的数据才能监听到变化时, 就需要deep属性对对象进行深度监听; methods:是应用于逻辑和方法的处理,事件方法,没有缓存,调用一次,执行一次只要调用就会重新执行一次这个调用是需要有一定的触发条件,比如点击事件; methods和computed最大的区别就是computed依赖于data中的数据,而方法不依赖;这个依赖就是如果data中的数据发生变化,computed 会根据函数里面包含的计算属性重新计算; 总结:他们的形式都是function,而本质的话computed依赖于data中的数据;场景的话:methods是应用于逻辑和方法的处理;其余两者都是监听 data中的数据;写法上:methods可以带参数和返回值,不过不是必须的是可有可无;computed:必须携带返回值,不允许带参数; watch:不允许带参数和返回值;
—用法上:methods:是使用一次调用一次
computed:一次调用,可以看做data;
watch:调用一次
—使用位置:methods:在生明周期函数中使用;
computed:和data一样可以在vue标签{{}}中使用;
watch:一般在生命周期
watch :监听属性的变化,没有缓存 执行异步操作或者开销比较大时
//监听对象
< input type='text' v-model='title'/>
title:'你是谁'
watch:{
title:{
handler(val,oldVal){
console.log(val.oldVal)
}
}
}
//监听数组 复杂数据类型深度监听
< input type='text' v-model='list[0].title'/>
list:[{title:'你是小狗'}]
watch:{
list:{
handler(val){
console.log(val)
}
deep:true
immediate:true 值初始化时就执行
}
}
###19.vue双向数据绑定原理
vue的双向数据绑定原理是采用数据劫持结合发布者和订阅者模式的方式,再利用Object.defineProperty()来劫持各个属性的setter,getter,在数据
变动时发布消息给订阅者,在触发相应的监听回调
其实就是通过Object.defineProperty()这个方法和里面的get()获取属性值和set()设置属性值这两个方法操作的;它里面有两个常用参数,第一个
参数就是定义的对象的名字;比如var obj = {},obj就是第一个参数,第二个参数是要定义或修改的属性的名称,比如要定义obj对象里的name属性;get是获取属性,set是修改属性;
实现过程:1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
2.实现一个订阅者Wather,可以收到属性的变化通知并执行相应的函数,从而更新视图。
3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始话相应的相应的订阅器
props:会接受不同的数据类型和设置默认值 ;有六种数据类型:Number,String,Object,Function,Array,Boolean;
props数据是单项数据传递,父不影响子,子不影响父;而且不能在组件中直接修改prop传递的值,Vue会在浏览器的控制台发出警报;
在vue中父子组件的关系可以总结为props向下传递,事件向上传递。父组件通过prop给子组件下发数据,子组件通过事件给父组件发送消息。
prop是单向绑定的:当父组件的属性变化时,将传导给子组件,但是反过来就不会了。这是为了阻止子组件无意间修改了父组件的状态,避免了
应用的数据流变得难以理解。
解决办法:将想要更改的值,传递给父组件,在父组件中更改,在传递给子组件。
具体步骤:先将值传递给子组件,子组件props接收并使用,然后通过$emit广播一个事件给父组件,并将值传递给父组件,父组件接收子组件广播
过来的事件,并定义一个方法,在该方法中改变传递过来的值,父组件又会将值传递给子组件,这样就形成了一个闭环,问题就解决了。
vue中组件的引入是通过在父组件中使用import {Son} from 后面写入组件路径 ,然后在局部组件components中注册子组件标签名字;
在父组件template标签中引入子组件的标签即可
*在父组件中中给子组件标签绑定一个自定义属性,属性上挂载要传递的值;在子组件中使用props:[‘自定义属性名’】来接收;
prop中可以对传递过来的数据进行数据类型验证,设置默认值等;接收完的数据直接在子组件中使用,不需要在data中定义;
子传父:
在父组件中中给子组件标签绑定一个自定义事件名,事件上挂在需要调用的在子组件中通过this$emit来调用这个方法。两个参数(自定义事件,传递的值)
第一种:先创建一个空的vue实例Bus,通过bus. e m i t ( 定 义 的 事 件 , 值 ) 来 传 到 空 的 v u e 实 例 中 ; 在 需 要 接 收 数 据 的 组 件 中 通 过 b u s . emit(定义的事件,值)来传到空的vue实例中;在需要接收数据的组件中通过bus. emit(定义的事件,值)来传到空的vue实例中;在需要接收数据的组件中通过bus.on(自定义事件,(data)=>{})接收
里面的data就是接收到的值;
第二种就是vuex的方法实现:在需要使用的组件中使用this.$store.state对象就可以使用仓库中state里面的数据;所有组件都可以使用,
22.vue中的生命周期
有八个生命周期
beforeCreate(创建前):这是数据创建完成之前,这个阶段实例的data,methods这些是都访问不到的
Created(创建后):数据加载完成,是最早能够获取数据的函数无法获取DOM节点,这个阶段已经完成了数据观测,属性和方法的运算,但是数据并没有
在DOM元素上进行渲染;可以从服务器获取一些初始化的数据,还有通过ajax向服务器发送一些数据。
beforeMount(挂载前):,编译模板,把 data 里面的数据和模板生成 html,完成了 el 和 data
初始化,注意此时还没有挂在 html 到页面上
Mounted(挂载后):DOM节点挂载完成之后,最早可以操作DOM的函数;可以完成模板中的html渲染到页面上。这里面可以进行ajax交互。可以进行axios
数据请求;而且mounted只会执行一次; beforeUpdate(更新前):在数据更新之前调用;会更新当前组件数据,但不会在页面渲染出来;
Updated(更新后):data数据改变的时候,影响到DOM的结构;在这里可以获取到最新的DOM结构;
beforeDestroy(销毁前):在实例销毁之前调用,这个时候实例还可以使用,在这里可以清除一些组件中的定时器和监听dom事件
Destroyed(销毁后):实例化销毁的时候触发;所有的事件监听器都会被清除,这个函数在服务器端渲染期间不被调用;
第一次加载页面只会触发前四个钩子函数;
created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作
有query和params两种传参 query分为视图导航模式:
格式就是:to="/path"+参数列表,在router-link里边写
编程导航:是通过this. r o u t e r . p u s h ( p a t h : ′ 路 径 + 参 数 ′ ) , 获 取 参 数 的 时 候 是 t h i s . router.push({path:'路径+参数'}),获取参数的时候是this. router.push(path:′路径+参数′),获取参数的时候是this.router.query.参数名
url 地址并且拼接? url 地址并且拼接? 问号传递的参数 8
params传递参数动态路由导航:在路由中找到指定路由的path:在后面加/:id;
视图导航模式:在router-link标签上加:to后面拼接"'/路由地址/'+item.id"来进行路由跳转并传参
编程导航模式:通过this.$router.push({name:'Detail'通过name属性来进行跳转},params:{id:id});获取参数就是:this.$route.params.id;
区别:query要通过path来引入,params要通过name引入,接收参数都是一样的只不过一个是用.query一个是.params;
query是类似于我们ajax中的get传参,params是类型于post传参,就是query在浏览器地址中显示参数,后者不显示;
这个就是vue-router提供的导航守卫主要是用来通过跳转或取消的方式守卫导航。通俗的说就是一个拦截器;
钩子函数有几种参数:to:即将要进入的路由对象;from:当前导航正要离开的路由;next:进行管道中的下一个钩子,必须调用,他里面的参数
为false就终止执行,为true的话就继续执行,里面是一个路由路径的话跳转至指定的路由;
分为全局守卫:有beforeEach、beforeResolve、afterEach(在路由实例对象注册使用);
beforeEach(全局前置守卫)可以进行一个判断,比如让一些用户通过一些用户不通过;如果用户名字是想要通过的那个,就执行next()
如果不是就得注意一个问题,不能直接写next(指定路径),因为那样会造成死循环,直接执行跳转的时候,又会触发beforeEach方法,这样
就成了一个死循环,所以要再加一个判断:就是判断如果要去的路由是指定路径的话执行next(),跳转到指定路径,else的话还是跳转到
该路径,这样都是跳转到该路径,就不会造成死循环了;
beforeResolve(全局解析守卫):和beforeEach类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件解析之后,解析
守卫就会被调用;
afterEach(全局后置钩子):这个是全局后置钩子,他和守卫不同的是,这些钩子不会接受next函数也不会改变导航本身;
路由专属守卫:在配置路由的时候设置,他只有一个路由守卫beforeEnter;
组件内部守卫:有三个方法:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave(在组件属性中定义)
beforeRouteEnter在渲染该组件的对应路由被确认前调用;他不能获取组件实例this因为当前守卫执行前,组件实例还没被创建,可以
通过传一个回调给next来访问组件实例,在导航被确认的时候执行回调,并把组件实例 作为回调方法的参数;
beforeRouteUpdate:在当前路由改变,该组件被复用时调用,这个可以访问组件实例this
beforeRouteLeave:导航离开该组件的对应路由时被调用,他也可使用组件实例this;
25.vuex 中 state,getters,mutations,actions,modules的用途和用法
state 数据源载体,保存着所有的全局变量
getters 用于改变state的值,对state中的数据派生出一些状态,例如对数据进行过滤。会对state中的变量进行过滤再保存,只要state中的变量发生了改变,它也会发生改变,不变化的时候,使用的是缓存。
mutation 唯一可以提交可以改变state的状态,也就是数据的属性值,他是通过直接改变状态的方式改变state,而且mutation必须同步执行,mutations相当于生成了修改全局
actions 他类似于mutation,不同就在于Action提交的是mutation,而不是直接变更状态。Action可以包含任意的异步操作;
Action
提交的是mutation,用commit提交 而不是直接变更状态,可以包含任意异步操作,他接受一个与store实例具有相同方法和属性的context对象,里面有commit方法和state属性等;触发action通过store.dispatch方法触发;
modules 由于使用单一状态树,应用的状态会集中到一个比较大的对象。当应用变得非常复杂的时候,store对象就有可能变得相当臃肿。为解决这个问题,vuex允许
我们将store分割成模块也就是Module,每个模块拥有自己的state,mutation,action,getter。甚至是嵌套子模块;需要注意的是modules模块中的state不能通过this.store.state获取,必须加上module的命名空间;但是getters和mutations能正常使用;
总结:state中的状态数据,只能通过Mutation来修改;getters是来获取state中的数据,只会读取;
actions是处理mutation的;modules是来分模块处理全局状态的;
mapState,mapGetters是来获取全局变量的,作为计算属性处理;
mapMutations,mapActions相当于事件处理函数,放在methods中,等待触发;
mapState,mapGetters,mapMutations,mapActions后面可以是对象,也可以是数组,数组里放原方法或属性名;对象,或者自己定义新的方法或属性名;
//工作流程
1.在vue组件里面,通过dispatch来触发actions提交修改数据的操作。
2.然后再通过actions的commit来触发mutations来修改数据。
3.mutations接收到commit的请求,就会自动通过Mutate来修改state(数据中心里面的数据状态)里面的数据。
4.最后由store触发每一个调用它的组件的更新
**
**
作用:高效的更新虚拟dom 用于管理可复用的元素。因为 Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。
在生成虚拟dom的过程中,相同的组件产生相同的dom结构,不同的组件产生不同的dom结构,同一层级的dom节点,他们可以通过唯一的id
进行区分,这个id就是key。 这么做使 Vue 变得非常快,但是这样也不总是符合实际需求。 2.2.0+ 的版本里,当在组件中使用
v-for 时,key 是必须的。”
**
需要使用key来给每个节点做一个唯一的标识
diff算法可以正确的识别此节点
找到正确的位置插入新的节点
很多时候我们需要直接操作 dom 元素,如果只是个别需要操作 dom 元素,我们可以通过 ref 获取当前 dom 元素,并对其进行操作,但是如果
特别多的时候,我们不可能每次都去写一遍 ref 还有方法,所以这时候自定义指令就可以帮你轻松解决这个问题
分为全局和局部;
钩子函数:
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
钩子函数的参数:
**el**: 指令所绑定的元素,可以**用来直接操作 DOM,就是放置指令的那个元素。
**binding**: 一个对象,里面包含了几个属性:value:指令的绑定值,name:指令名等;
**vnode**:Vue 编译生成的虚拟节点。
**oldVnode**:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
事件修饰符:
.prevent: 提交事件不再重载页面
.stop: 阻止单击事件冒泡
.self: 当事件发生在该元素本身而不是子元素的时候会触发
.capture: 事件侦听,事件发生的时候会调用
.once 只执行一次
面试需要答的:
.trim - 输入首尾空格过滤
.number - 输入字符串转为有效的数字
.lazy - 取代 input 监听 change 事件
.passive:addEventListener中的第三个参数,表示 listener 永远不会调用 preventDefault()
.native:组件绑定当前组件的事件是不会触发的,需要用native才能触发
.sync:对prop进行双向绑定
##js库和框架的区别
#框架 就是提供了前端项目整体解决方案。像angular、backbone、vue就属于框架 库是用来提供一些方法的集合,避免重复定义相同功能的函数并具有一定的模式兼容性,而所谓框架,
更应该是规范开发者按照框架的设计去做一些事,而非简单的工具集的概念,框架可以提供相应的库或者基于库来实现,
但库一般不会具备框架的规范性。
#库 就是自己组合来实现项目。jQuery、React、underscore就是库, jquery就是一些函数的集合,就是把特定效果的代码写好,你只需要在用的时候要用 很少的代码去调用,由你来决定何时使用类库。
##3##Vue的路由实现:hash模式 和 history模式
hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,
即使没有做到对路由的全覆盖,也不会返回 404 错误。history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()
可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
什么是虚拟DOM
可以把Virtual DOM 理解为一个简单的JS对象,并且最少包含标签名( tag)、属性(attrs)和子元素对象( children)三个属性。
将原本需要在真实dom进行的创建节点,删除节点,添加节点等一系列复杂的dom操作全部放到vdom中进行,这样就通过操作vdom来
提高直接操作的dom的效率和性能。
虚拟DOM的最终目标是将虚拟节点渲染到视图上
diff算法分为一下几个步骤
- 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
- 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
- 把所记录的差异应用到所构建的真正的DOM树上,视图就更新了
虚拟DOM就是对于复杂的文档DOM结构提供了一种方便的工具,用于最小化的dom操作本质上是通过JAVAScript来描述DOM之间的元素关系的;
作用是能够更高效的渲染和更新视图实现原理是通过Dff算法来实现,就是比如当前页面的数据发生改变时,dff算法会比较同一层级的节点,如果节点类型不同,
那么会直接干掉前面的节点,重新创建一个新节点,然后插入这个新节点,而不会再比较这个节点以后的子节点了,如果节点类型相同,那么会直接重新计算该节点的属性,对节点进行更新
这就是dff的一个默认执行的操作,通常我们去渲染节点时,需要结合key属性来去使用,作为唯一标识,能够让dff正确识别当前节点,如果数据发生改变,或插入了新的节点时,
这个时候dff算法就可以快速的找到当前需要修改的节点位置,去做视图更新,从而减少了dom重复的操作,大大地优化了网页性能
什么是虚拟dom,和 diff 算法
1、虚拟 DOM 的最终目标是将虚拟节点渲染到视图上。但是如果直接使用虚拟节点覆盖旧节点的话,会有很多不必要的 DOM 操作。例如,一个ul 标签下很多个 li 标签,其中只有一个 li 有变化,这种情况下如果使用新的 ul 去替代旧的 ul,因为这些不必要的 DOM操作而造成了性能上的浪费。 为了避免不必要的DOM 操作,虚拟 DOM 在虚拟节点映射到视图的过程中,将虚拟节点与上一次渲染视图所使用的旧虚拟节点(oldVnode)做对比,找出真正需要更新的节点来进行 DOM
操作,从而避免操作其他无需改动的 DOM。 简而言之主要做了两件事: 提供与真实DOM 节点所对应的虚拟节点 vnode
将虚拟节点vnode 和旧虚拟节点 oldVnode 进行对比,然后更新视图 渲染完成会将数据渲染到js对象上,js比dom节点快
keep-alive是Vue中的内置组件,能在切换过程中把状态保留在内存中,防止重复渲染DOM
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。 prop:
- include: 字符串或正则表达式。只有匹配的组件会被缓存。
- exclude: 字符串或正则表达式。任何匹配的组件都不会被缓存。 为神魔会被缓存? keep-alive生命周期钩子函数:activated、deactivated 场景:Vue中前进刷新,后退缓存用户浏览数据
列表进入详情页,缓存浏览器滚动状态。 列表页面 =>点击进入详情页=> 后退到列表页 要缓存列表原来数据 重新进入列表页面 =>
获取最新的数据 结合Router,缓存部分页面
###图片懒加载 31
当打开一个有很多图片的页面时,先只加载页面上看到的图片,等滚动到页面下面时,再加载所需的图片。这就是图片懒加载。
作用:减少或延迟请求数,缓解浏览器的压力,增强用户体验。
实现方法:1、设置图片src属性为同一张图片,同时自定义一个data-src属性来存储图片的真实地址 2、
页面初始化显示的时候或者浏览器发生滚动的时候判断图片是否在视野中 3、
当图片在视野中时,通过js自动改变该区域的图片的src属性为真实地址
1.document.documentElement.clientHeight获取屏幕可视窗口大小;
2.document.documentElement.scrollTop获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离
3.判断当滚动条滚动到一定高度的时候就进行图片懒加载;
###.瀑布流原理 32 视觉上表现为参差不齐的多栏布局,其实就是等宽不等高排列 瀑布流的实现原理就是通过比较每一列的高度,如果这一列高度低的话就在这一列进行添加;
实现步骤:1.获取存放图片的div和div里面的图片元素;
2.通过window.onload=function(){}在页面加载的时候执行定义的函数;
3.定义一个变量columns计算出页面中有多少列=外层盒子的宽度box.offsetWidth/子元素的宽度items[0].offsetWidth最后计算的时候用parseInt除成整数
4.循环所有的元素,在循环里面判断如果i小于columns的话就把每一项的top值设置为0;把元素的left值设置为元素的宽度乘以下标值;然后在循环外面定义一个空数组
把每一项的高度都添加进去(items[i].offsetHeight);然后就是i>columns的逻辑,在这里面定义一个空变量为minHeight为最小高度,这个最小高度让他默认为0;在定义一个变量为Index为0;然后在写一个循环,循环内容就是每个上面存储每一项高度的数组,然后再通过if判断找出最小的高度值,逻辑就是如果当前循环的高度小于上面定义的minHeight最小高度的话,就让minHeight等于这个高度,然后再让index等于这个元素的下标;
5.最后一步就是把后面的元素插入到高度最小的元素下面,让循环的当前项的top值为最小高度,让他到这个元素的下面;left值为当前项距离外层盒子的Left值;
####.从输入URL到页面加载完成期间经历了什么?33
1.通过DNS服务器:url=>ip地址;
2.到达ip地址对应的服务器;
3.服务器接收用户的请求;
4.把处理后的结果返回给客户端;
5.客户端把结果渲染到浏览器即可,最后页面显示出来;
1.DNS 解析
2.TCP 连接
3.发送 HTTP 请求
4.服务器处理请求并返回需要的数据
5.浏览器解析渲染页面
6.连接结束
####vue2.0和3.0区别 34
1.默认进行懒观察
在 2.x 版本里,不管数据多大,都会在一开始就为其创建观察者。当数据很大时,这可能会在页面载入时造成明显的性能压力。
3.x 版本,只会对「被用于渲染初始可见部分的数据」创建观察者,而且 3.x 的观察者更高效。
2.更精准的变更通知
比例来说:2.x 版本中,使用 Vue.set 来给对象新增一个属性时,这个对象的所有 watcher 都会重新运行;3.x 版本中,只有依赖那个属性的 watcher 才会重新运行。
3. 3.0 新加入了 TypeScript 以及 PWA 的支持
4.部分命令发生了变化:
1.下载安装 npm install -g vue@cli 2.删除了vue list 3.创建项目 vue create 4.启动项目 npm run serve
5.默认项目目录结构也发生了变化:
1.移除了配置文件目录,config 和 build 文件夹
2.移除了 static 文件夹,新增 public 文件夹,并且 index.html 移动到 public 中
3.在 src 文件夹中新增了 views 文件夹,用于分类 视图组件 和 公共组件
1、性能提升 一句话简介:更小巧,更快速;支持摇树优化;支持 Fragments 和跨组件渲染;支持自定义渲染器。 2、API 变动
一句话介绍:除渲染函数 API 和 scoped-slot 语法之外,其余均保持不变或者将通过另外构建一个兼容包 来兼容 2.x。
模板语法的 99% 将保持不变。除了 scoped slot 语法可能会有一些微调之外变动最大的部分将是渲染函数 (render) 中的虚拟
DOM 的格式。 3、重写虚拟 DOM (Virtual DOM Rewrite) 随着虚拟 DOM 重写,减少
运行时(runtime)开销。重写将包括更有效的代码来创建虚拟节点。
React的生命周期从广义上分为三个阶段:挂载,渲染,卸载;因此可以把React的生命周期分为两类:挂载,卸载过程和更新过程。 挂载,卸载过程 1.constructor():constructor()完成了React数据的初始化,他接受两个参数:props和context,当想在函数内部使用这两个参数的时候,需要使用super()传入这两个参数; 需要注意的是只要使用了constructor()就必须写super(),否则会导致this指向错误; 2.componentWillMount():这个一般用的比较少,他更多的是在服务端渲染时使用。他代表的过程是组件已经经历constructor()初始化数据后,但是还未渲染DOM时。 3.componentDidMount():组件第一次渲染完成,此时DOM节点已经生成,可以在这里调用Ajax请求,返回数据setState后组件会重新渲染; 4.componentWillUnmount():在这里完成组件的卸载和数据的销毁。会做2件事:(1):clear清除你在组件中所有的setTimeout,setInterval;(2):移除所有组件中的监听:removeEventListener; 更新过程 5.componentWillReceiveProps(nextProps):在接收父组件改变后的props需要重新渲染组件时用到的比较多;他会接收一个参数nextProps;通过对比nextProps和this.props,将nextProps的state 作为当前组件的state,从而重新渲染组件; 6.shouldComponentUpdate(nextProps,nextState):主要用于性能优化(部分更新);唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后,state发生变化,组件会进入重新渲染的 流程,在这里return false可以组织组件的更新; 7.componentWillUpdate(nextProps,nextState):shouldComponentUpdate返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,这里同样可以拿到nextProps和nextState。 8.componentDidUpdate(prevProps,prevState):组件更新完毕以后,react只会在第一次初始化成功时进入componentDidmount,之后每次重新渲染都会重新进入这个生命周期,这里可以拿到 prevProps和prevState,也就是更新前的props和state; 9.render():render函数会插入jsx生成的dom结构,react会生成一份虚拟dom树,在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的 DOM节点,并重新渲染。
函数式组件又称无状态组件,无状态组件就是他没有自己的state状态管理,他只能接收父组件props传递过来的数据;无状态组件主要用来定义模板;
Class组件又称有状态组件,他有自己的state状态管理,使用this.state.数据的这种表达式把业务数据挂载到组件的实例上,然后传递props到展示组件,展示组件接收到props,把props放到模板里。
类组件里面有一个constructor()这个就是组件的构造方法,只要使用了他就必须使用super(),这个就是指向组件的构造方法;
?
React组件的数据渲染是否被调用是通过传递过来的props完全控制,控制为受控组件,
否则为非受控组件;例如:要实现利用父组件的数据控制子组件中内容的显示与隐藏;
Generator是一个函数,可以在函数内部通过yield返回一个值(此时,Generator函数的执行会暂定,直到下次触发.next());
使用流程:
1.创建一个Generator函数的方法是在function关键字后添加*标识。
2.在调用一个Generator函数后,并不会立即执行其中的代码,函数会返回一个Generator对象,通过调用对象的next函数,可以获得yield/return的返回值。
3.无论是触发了yield还是return,next()函数总会返回一个带有value和done属性的对象。
4.value为返回值,done则是一个Boolean对象,用来标识Generator是否还能继续提供返回值。
需要注意的是:Generator函数的执行时惰性的,yield后的代码只在触发next时才会执行;
next()我们可以在调用next()的时候传递一个参数,可以在上次yield前接收到这个参数:
应用场景:1.因为Generator对象是一个迭代器,所以我们可以直接用于for of循环;
2.模拟实现Promise执行器
async await
async 是 ES7 才有的与异步操作有关的关键字,在定义函数的时候在函数的前面加上async 函数返回的是promise
await 操作符用于等待一个 Promise 对象, 它只能在异步函数 async function 内部使用。也就是async可以没有await,但是await必须要有async
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。有几点优势:1.可以从浏览器中创建 XMLHttpRequests 2.从 node.js 创建 http 请求 3.支持 Promise API
4.拦截请求和响应 5.转换请求数据和响应数据 6.自动转换 JSON 数据;
可以在请求或响应被 then 或 catch 处理前拦截它们。可以为自定义 axios 实例添加拦截器;
请求拦截可以用它做我们的loading 加载和数据的权限验证,包括我们所有的数据预加载也可以实现;
响应拦截:axios 拦截可以配置公用地址,以及对于跨域问题解决
Axios 拦截分为请求拦截和响应拦截,请求拦截就是在你请求的时候会进行触发!只要是你发送一个 axios 请求就会触发!
所以我们主要用它做我们的loading 加载和数据的权限验证,包括我们所有的数据预加载也可以实现,响应拦截主要是我们在 loading 加载,
和做所有数据加载需要整体的结束,这个时候的结束就需要在数据马上发给前端的时候进行隐藏和结束,包括我们的请求头的设置,
后端数据已经发送过来的时候,我们为了确保请求头的传递就必须在看看header 里面是否有你需要的请求,如果有的话,
再次进行设置!当然用 axios 拦截也可以配置公用地址,以及对于跨域问题解决,这个就是用 axios 拦截实现的功能。
1.keywords:用来告诉搜索引擎,你网页的关键字; 2.description:用来告诉搜索引擎,你网站的主要内容; 3.viewport:移动端的窗口:width=device-width宽度等于当前设备的宽度;initial-scale=1.0初始缩放比例,默认为一倍;minimum-scale=1.0/maximum-scale=1.0最小/大缩放比例; user-scalable=no是否允许用户缩放页面; 4.robot:用来告诉爬虫哪些页面需要索引,哪些页面不需要索引。属性值为content,content参数有all,index搜索引擎将索引此网页,follow继续通过此网页的链接索引; none将忽略此网页;noindex 搜索引擎不索引此网页;nofollow搜索引擎不继续通过此网页的链接索引搜索其他的网页; 5.author用于标注网页作者; 6.generator网页制作软件; 7.copyright版权; 8.revist-after重访; 9.renderer他是为双核浏览器准备的,用于指定双核浏览器默认以何种方式渲染页面; 10.expires期限,可以设定网页的到期时间。一旦网页过期,必须到服务器上重新传输; 11.Pragma禁止浏览器从本地计算机的缓存中访问页面内容; 12.Refresh刷新,自动刷新并指向新页面;里面有个content=2是指停留2秒钟后自动刷新到URL网址;后面的分号是在秒数的前面和网址的后面; 13.Set-Cookie用于cookie的设定,如果网页过期,那么存盘的cookie将被删除; 14.Window-target显示窗口的设定,强制页面在当前窗口以独立页面显示; 15.content-Type显示字符集的设定,设定页面使用的字符集; 16.content-language显示语言的设定; 17.cache-control指定请求和响应遵循的缓存机制,
BFC 就是“块级格式化上下文”的意思,创建了 BFC 的元素就是一个独立的盒子,不过只有 Block-level box 可以参与创建 BFC, 它规定了内部的 Block-level Box 如何布局,
并且与这个独立盒子里的布局不受外部影响,当然它也不会影响到外面的元素。
BFC 有一下特性:
内部的 Box 会在垂直方向,从顶部开始一个接一个地放置。
Box 垂直方向的距离由 margin 决定。属于同一个 BFC 的两个相邻 Box 的 margin 会发生叠加,
每个元素的 margin box 的左边, 与包含块 border box 的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
BFC 的区域不会与 float box 叠加。
触发条件:
float的值不为none(默认)
overflow的值不为visible(默认)
display的值为inline-block、table-cell、table-caption
position的值为absolute或fixed
BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然。
计算 BFC 的高度时,浮动元素也参与计算。
语法:Object.defineProperty(obj, prop, descriptor)
定义:Object.defineProperty() 直接在一个对象上定义一个新属性,或者修改现有属性,并返回该对象。
参数含义: obj 需要定义属性的对象。
prop 要定义或修改的属性的名称。
descriptor 需被定义或修改的属性的描述符。
Object.defineProperty()方法有何作用**
用法一:可以给对象上的属性设置set get
var obj={}
//定义一个变量
var username;
//设置对象的属性的值,给每个属性设置一个set和get方法 set当设置属性属性的时候调用 get当获取属性的时候调用
Object.defineProperty(obj,"username",{
get(){
return username; //读取的时候被调用
},
set(val){
console.log('setter',val)
username=val //设置的时候被调用
}
})
obj.username='小wu'
alert(obj.username)
用法二:可以详细地设置属性的值
<script> var obj={ } //创建一个新对象 //设置属性的值username Object.defineProperty(obj,"username",{ //设置默认值 value value:'小三', //writeable 设置属性是否可写 默认的是false writable:true, //enumerable 设置属性是否可枚举,就是遍历 默认的是false enumerable:true, //configurable 设置属性时候可以被删除或者改变 默认的是false configurable:true }) obj.username='小四' for(var i in obj){ console.log(obj[i]) } 可枚举属性对 for/in, Object.keys(), JSON.stringify(), Object.assign() 方法才生效(for/in 是对所有可枚举属性,而其他三种是对自身可枚举属性) ##用途 1)vue 通过 getter-setter 函数来实现双向绑定 2)俗称属性挂载器 3)专门监听对象数组变化的 Object.observe()(es7)也用到了该方法 >描述符: 1.value描述符:设置属性值,默认值为undefined。 2.writable描述符:设置属性的值是否可写,默认值为true。 3.enumerable枚举描述符:设置属性是否可枚举,就是是否使用for/in语句或Object.keys()函数遍历访问,默认为true。 4.configurable描述符:设置是否可配置属性特性,默认为true。如果为false,将无法删除该属性,不能够修改属性值,也不能修改属性的属性描述符。 ####.vue 中数组中的某个对象的属性发生变化,视图不更新如何解决? 43 这种情况分为两种 第一种是数组的值改变,在改变数组的值的时候使用索引值去更改某一项,这样视图不会实时更新,这种情况是因为直接通过索引去改变数组,vue对象监听不到他的变化,所以没有更新; 解决方法:使用vue的变异方法pop(),push(),shift(),unshift(),revese(),sort(),splice()等方法也会触发视图更新 第二种是改变了对象的某一项,但是其他依赖这个数据的视图没有更新,比如父组件和子组件公用一份数据,数据通过props传到子组件,在子组件中修改数据父组件中不会响应; 解决方法:1.利用vue.set(object,key,val):例:vue.set(vm.obj,'k1','v1'); 2.利用Object.assign({},this.obj)创建新对象;如果是数组就把花括号改为中括号; 3.先删除掉那一项,然后再使用set去添加; 扩展: 1.vue鉴权 vue鉴权一般就是指管理后台的权限验证,先从登录说,当用户登录成功后,服务端接口一般会给用户分配一个token用于校验用户是否登录,当用户进入管理后台页面 的时候,我们可以通过token获取当前登录用户的角色,以及当前用户可以访问的路由地址列表,我们可以把用户可以访问的地址列表存储在本地。 后台的鉴权可以从两方面处理,一部分就是用户进入后台后,根据用户可以访问的路由地址信息,渲染出左侧的菜单栏部分,这样的话就只能访问左侧二级菜单相关的页面。 但是用户可能不是通过左侧二级菜单访问,可能是直接在浏览器输入路由地址访问,这个时候我们就得考虑使用路由守卫,路由守卫中有一个to方法,可以获取用户即将要 的路由地址,用这个地址跟用户存储的可访问的路由地址进行比较如果在用户可访问的地址列表中,就执行next(true),否则的话直接跳转到自定义的403页面提示,提示 用户无权限访问当页面。 ## js中如何判断对象中是否有某个属性? 1.obj.hasOwnProperty() 只能获取自定义属性,无法获取原型链属性 2.for in 可以获取object的所有属性,包括自定义属性以及原型链属性。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。