一.v-if与v-show的区别;display:none与visibility:hidden的区别 v-if与v-show的区别:相同点:v-if与v-show都可以动态控制dom元素显藏 不同点:v-if显示隐藏是将dom元素整个添加或删除,而v-show隐藏则是为该元素添加css--display:none,dom元素还在。v-show只是简单的控制元素的display 属性,而 v-if才是条件渲染(条件为真,元素将会被渲染,条件为假,元素会被销毁)。v-show有更高的首次渲染开销,而v-if的首次渲染开销要小的多; v-if 有更高的切换开销,v-show切换开销小;v-if有配套的v-else-if和v-else,而 v-show 没有。v-if 可以搭配 template 使用,而 v-show 不行。 display:none与visibility:hidden的区别:display:none隐藏后的元不占据任何空间,而visibility:hidden隐藏后的元素空间依旧保留。visibility具有继承性,给父元素设置visibility:hidden;子元素也会继承这个属性。但是如果重新给子元素设置visibility:visible,则子元素又会显示出来。这个和display: none有着质的区别。visibility:hidden不会影响计数器的计数,visibility:hidden虽然让一个元素不见了但是其计数器仍在运行。这和display: none完全不一样。 二.vue的生命周期 创建:1、beforeCreate:这个阶段实例已经初始化,只是数据观察与事件机制尚未形成,不能获取DOM节点(没有data,没有el)使用场景:因为此时data和methods都拿不到,所以通常在实例以外使用。2、created:实例已经创建,仍然不能获取DOM节点(有data,没有el)使用场景:模板渲染成html前调用,此时可以获取data和methods,so可以初始化某些属性值,然后再渲染成视图,异步操作可以放在这里。载入:1、beforeMount:是个过渡阶段,此时依然获取不到具体的DOM节点,但是vue挂载的根节点已经创建(有data,有el)2、mounted:数据和DOM都已经被渲染出来了使用场景:模板渲染成html后调用,通常是初始化页面完成后再对数据和DOM做一些操作,需要操作DOM的方法可以放在这里。更新:1、beforeUpdate:检测到数据更新时,但在DOM更新前执行2、updated:更新结束后执行。使用场景:需要对数据更新做统一处理的;如果需要区分不同的数据更新操作可以使用$nextTick。销毁:1、beforeDestroy:当要销毁vue实例时,在销毁前执行。2、destroyed:销毁vue实例时执行。第一次加载页面时会触发哪些钩子函数?beforeCreate、created、beforeMount、mounted 三.mvvm MVVM即Model-View-ViewModel的简写。即模型-视图-视图模型。模型(Model)指的是后端传递的数据。视图(View)指的是所看到的页面。视图模型(ViewModel)是mvvm模式的核心,它是连接view和model的桥梁。它有两个方向:一是将模型(Model)转化成视图(View),即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将视图(View)转化成模型(Model),即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。这两个方向都实现的,我们称之为数据的双向绑定。 模式解析
Model层:数据服务层,跟其他类MVC模式一样,不管最终对接的是数据库还是网络API或是其它,都是在负责数据的存储,并提供访问数据的接口,以支持数据的增删改查的基本操作。 View层:界面层,但大家注意,这里的View层相对于MVP模式中的View来说,指代的范围更狭小,原则上它不包含任何界面逻辑,在基本组件库满足需求的情况下,View层的设计和制作完全不需要程序员的参与,所有工作都由界面设计师完成,这也是MVVM的一个核心的思想。对于MVC系列的其他模式,由于界面设计和逻辑开发可以独立的同时进行,第一个好处是开发周期缩短,程序员再不需要等UI将设计图拿给你才开始写上层的功能代码;第二个好处是界面设计师和程序员都可以在更专注的干好自己份内的事。 ViewModel层:ViewModel翻译过来—视图的模型,很恰当。ViewModel就是完全反应View的状态和行为,是View的内在抽象。John Gossman 在他的博文中说什么?他说ViewModel包含ViewState、ValueConverter、Commands、DataBindings,所以说ViewModel是一个抽象的View一点都没错。我在网上看到很多朋友错误的理解,大家切记ViewModel不是数据模型的封装,不是数据模型的封装,不是数据模型的封装,重说三!从ViewModel的外在属性来看,ViewModel和Model层的数据模型半毛钱关系都没有,它不过是使用了数据模型所携带的数据而已。另外,在MVVM模式的开发设计中,是重View和ViewModel,而轻Model的。当然说轻Model,不是说你Model层就可以随心所欲的设计,而是强调设计师的中心在界面上,程序员的重心在ViewModel上,最后将这精心设计的两层binding起来,就可以保证咱们项目的高大上~。好了,再解释一下上面提到的几个关键词:
ViewState 指数据的数据状态和显示状态。数据状态就是在视图生命周期中展示的数据的值及其变化;显示状态就是视图在生命周期中显示成什么样的。
ValueConverter 用于格式化数据的,比如需求是将时间显示为昨天今天明天,但是模型中是时间戳,我们就需要ValueConverter来对时间进行格式化。
Commands 包含了视图的所有业务行为,比如登陆操作,就对应一个登陆的command,它将于登陆按钮的点击事件绑定起来,当点击事件发生,command内封装的登陆业务就会自动触发。
DataBindings 不用解释,肯定是指View和ViewModel的绑定了。一般的View中数据容器如textview,跟ViewModel的ViewState中的数据字段绑定起来;View的展示属性跟ViewModel的ViewState中的展示状态绑定起来;View的事件跟ViewModel中定义的命令绑定起来。
Binder层:之所以要将Binder单独拿出来说,是因为要实现一个稳定的高效的,应用广泛的绑定机制实际是相当复杂的,牵涉到很多问题。正如微软做的一样,将Binder作为MVVM开发的基础组件内置在了平台中,让开发者解放出来做更有意义的事情。 Controller层:MVVM模式虽然名称里没有“C”的字样,但是并不代表没有Controller,正式Controller将View和ViewModel关联起来,当然用的是Binder层提供的绑定机制。这里提一句:在iOS上,controller也可能负责界面的生命周期、View的组织等工作,但工作量显然轻多了,这就是我们常常说的瘦身的作用。 四.双向绑定及原理 vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。 我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令(如v-model,v-on)对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下3个步骤,实现数据的双向绑定: 1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。 2.实现一个订阅者Watcher,每一个Watcher都绑定一个更新函数,watcher可以收到属性的变化通知并执行相应的函数,从而更新视图。 3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令(v-model,v-on等指令),如果节点存在v-model,v-on等指令,则解析器Compile初始化这类节点的模板数据,使之可以显示在视图上,然后初始化相应的订阅者(Watcher) 1.实现一个Observer Observer是一个数据监听器,其实现核心方法就是Object.defineProperty( )。如果要对所有属性都进行监听的话,那么可以通过递归方法遍历所有属性值,并对其进行Object.defineProperty( )处理 2.实现一个Watcher Watcher就是一个订阅者。用于将Observer发来的update消息处理,执行Watcher绑定的更新函数。 ompile主要的作用是把new SelfVue 绑定的dom节点,(也就是el标签绑定的id)遍历该节点的所有子节点,找出其中所有的v-指令和" {{}} ". 1.如果子节点含有v-指令,即是元素节点,则对这个元素添加监听事件。(如果是v-on,则node.addEventListener('click'),如果是v-model,则node.addEventListener('input'))。接着初始化模板元素,创建一个Watcher绑定这个元素节点。 2.如果子节点是文本节点,即" {{ data }} ",则用正则表达式取出" {{ data }} "中的data,然后var initText = this.vm[exp],用initText去替代其中的data 4.实现一个MVVM 可以说MVVM是Observer,Compile以及Watcher的“boss”了,他需要安排给Observer,Compile以及Watche做的事情如下 a、Observer实现对MVVM自身model数据劫持,监听数据的属性变更,并在变动时进行notify b、Compile实现指令解析,初始化视图,并订阅数据变化,绑定好更新函数 c、Watcher一方面接收Observer通过dep传递过来的数据变化,一方面通知Compile进行view update。 最后,把这个MVVM抽象出来,就是vue中Vue的构造函数了,可以构造出一个vue实例。 五.this的指向 随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指的是,调用函数的那个对象 1、普通函数中的this指向window 2、定时器中的this指向window 3、箭头函数没有this,它的this指向取决于外部环境、 4、事件中的this指向事件的调用者 5、构造函数中this和原型对象中的this,都是指向构造函数new 出来实例对象 6、类 class中的this 指向由constructor构造器new出来的实例对象 7、自调用函数中的this 指向window 8.如果是一般函数,this指向全局对象window; 9.在严格模式下"use strict",为undefined. 10.对象的方法里调用,this指向调用该方法的对象. 11.构造函数里的this,指向创建出来的实例. 如果把new这个关键字放在一个函数调用的前面,JS编译器会做这四件事情: 创建一个新的空的对象把这个对象链接到原型对象上这个对象被绑定为this如果这个函数不返回任何东西,那么就会默认return this 六.性能优化 减少http请求次数、打包压缩上线代码、使用懒加载、使用雪碧图、动态渲染组件 七.深拷贝与浅拷贝 含义:深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用。假设B复制了A,修改A的时候,看B是否发生变化: 如果B跟着也变了,说明是浅拷贝,拿人手短!(修改堆内存中的同一个值)如果B没有改变,说明是深拷贝,自食其力!(修改堆内存中的不同的值
 浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。
 深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。 在计算机中开辟一块新的内存地址用于存放复制的对象。
基本数据类型:number,string,boolean,null,undefined,symbol以及未来ES10新增的BigInt(任意精度整数)七类。
引用数据类型(Object类)有常规名值对的无序对象{a:1},数组[1,2,3],以及函数等。 而这两类数据存储分别是这样的: a.基本类型--名值存储在栈内存中 b.引用数据类型--名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值 八.数组去重 1、Array.filter() + indexOf 方法思路:将两个数组拼接为一个数组,然后使用 ES6 中的 Array.filter() 遍历数组,并结合 indexOf 来排除重复项 function distinct(a, b) { let arr = a.concat(b); return arr.filter((item, index)=> { return arr.indexOf(item) === index //indexOf()方法可返回某个指定的字符串值在字符串中首次出现的位置。 }) } 2、双重 for 循环 方法思路:外层循环遍历元素,内层循环检查是否重复,当有重复值的时候,可以使用 push(),也可以使用 splice() function distinct(a, b) { let arr = a.concat(b); for (let i=0, len=arr.length; i<len; i++) { for (let j=i+1; j<len; j++) { if (arr[i] == arr[j]) { arr.splice(j, 1); // splice 会改变数组长度,所以要将数组长度 len 和下标 j 减一。splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目 注释:该方法会改变原始数组。 len--; j--; } } } return arr } 3、for...of + includes() 方法思路:双重for循环的升级版,外层用 for...of 语句替换 for 循环,把内层循环改为 includes()。先创建一个空数组,当 includes() 返回 false 的时候,就将该元素 push 到空数组中 。类似的,还可以用 indexOf() 来替代 includes() function distinct(a, b) { let arr = a.concat(b) let result = [] for (let i of arr) { !result.includes(i) && result.push(i) } return result }
4、Array.sort() 方法思路:首先使用 sort() 将数组进行排序,然后比较相邻元素是否相等,从而排除重复项 function distinct(a, b) { let arr = a.concat(b) arr = arr.sort() let result = [arr[0]]
for (let i=1, len=arr.length; i<len; i++) {
arr[i] !== arr[i-1] && result.push(arr[i])
}
return result
}
、new Set() ES6 新增了 Set 这一数据结构,类似于数组,但Set 的成员具有唯一性 function distinct(a, b) { return Array.from(new Set([...a, ...b])) } 6、for...of + Object 方法思路:首先创建一个空对象,然后用 for 循环遍历,利用对象的属性不会重复这一特性,校验数组元素是否重复 function distinct(a, b) { let arr = a.concat(b) let result = [] let obj = {}
for (let i of arr) {
if (!obj[i]) {
result.push(i)
obj[i] = 1
}
}
return result
} 几种方法的性能 6>5>4>3>1>2 //利用ES6 Set去重(ES6中最常用) 7.function unique (arr) { return Array.from(new Set(arr)) } var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) //[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}] 九.数组的方法 splice()方法.1.删除任意数量的项,只需要传入两个参数即可。要删除的第一项的位置和要删除的项数.2.添加:可以向指定位置添加任意的项,只需要提供三个参数即可:起始位置,0(要删除的项数)和要添加的项。如果要添加多项可以继续在后面写参数用逗号分隔.3.替换(删除再添加):可以向指定位置添加任意的项,同时删除任意数量的项。需要指定三个参数:起始位置,删除的项数和要添加的项数,添加的项数不用和删除的项数保持一致。4.不接收返回值也是可以的,str.splice(0,3,"red"); console.log(str); // ["red", "lime", "pink", "gary"] 总结:1.splice()方法始终会返回一个数组,该数组中包含从原始数组中删除的项,如果没有删除任何项,那么将会返回一个空数组。 2.注意 该方法和slice()是不一样的,splice()会修改原数组中的项。 3.如果参数写的是负数那么把原本的数组输出出来,不做操作.
slice()方法,⑴可以用来从数组提取指定元素 该方法不会改变元素数组,而是将截取到的元素封装到一个新的数组中返回 ⑵语法:arrayObject.slice(start,end) ⑶参数: 1.截取开始的位置的索引,包含开始索引 2.截取结束的位置的索引,不包含结束索引 3.第二个参数可以省略不写,此时会截取从开始索引往后的所有元素 ⑷索引可以传递一个负值,如果传递一个负值,则从后往前计算 -1 :倒数第一个 -2 :倒数第二个 push 在数组末尾添加一个或多个元素,并返回数组的长度,可以添加任意类型的值作为数组的一个元素。 unshift 在最前面添加一个或多个元素,并返回数组的长度,可以添加任意类型的值作为数组的一个元素。 pop 删除最后一个元素,并返回删除元素的值;如果数组为空则返回undefine。该方法会改变原始数组。
shift 删除数组第一个元素,并返回被删除的元素;如果数组为空则返回undefine。该方法会改变原始数组。
concat 合并两个或多个数组,得到新数组,原始数组不改变,如果要进行concat()操作的参数是数组,那么添加的是数组中的元素,而不是数组
indexOf 数组元素索引,并返回元素索引,不存在返回-1,索引从0开始
语法:arr.indexOf(searchvalue,fromindex)
ps:
1.searchvalue => 必需。规定需检索的字符串值。
2.fromindex => 可选。元素开始检索的位置。如省略,则从数组的首个元素开始
3.indexOf()方法对大小写敏感!
join 数组转字符串,与toString()方法类似
语法:arr.join(separator)
ps:
1.separator => 可选。指定要使用的分隔符。如果省略该参数,则使用逗号作为分隔符
2.返回一个字符串。
3.该字符串是通过把数组arr中的每个元素转换为字符串,然后把这些字符串连接起来,在两个元素之间插入 separator 字符串而生成的。
reverse 颠倒数组中元素的顺序,该方法会改变原来的数组,而不会创建新的数组。
语法:arr.reverse()
split() 方法用于把一个字符串分割成字符串数组。
十.如何合并对象
1.Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。 并且源对象也会被修改。
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }
2..extend(obj1, obj2);
console.log(obj1); // {a: 1, b: 1} obj1已被修改
3.遍历赋值
var obj1={'a':1};
var obj2={'b':2,'c':3};
for(var key in obj2){
if(obj2.hasOwnProperty(key)===true){
//此处hasOwnProperty是判断自有属性,使用 for in 循环遍历对象的属性时,原型链上的所有属性都将被访问会避免原型对象扩展带来的干扰
obj1[key]=obj2[key];
}
}
console.log(obj1);//{'a':1,'b':2,'c':3};
4.对象的深拷贝和浅拷贝
浅拷贝
var obj1={'a':1};
var obj2={'b':{'b1':22,'b2':33}};
.extend(true,obj1, obj2); //第一个参数设为true表示深复制
console.log(obj1) // {'a':1,'b'{'b1':22,'b2':33}}
console.log(obj1.b.b1) // 22
obj2.b.b1=44; //obj2重新赋值
console.log(obj1.b.b1) // 22 obj1拷贝了obj2的所有属性以及值,并不受obj2的影响
十一.cookie、session、localstorage 、 sessionstrorage 之间有什么区别?
与服务器交互:
cookie 是网站为了标示用户身份而储存在用户本地终端上的数据(通常经过加密)
cookie 始终会在同源 http 请求头中携带(即使不需要),在浏览器和服务器间来回传递.sessionStorage 和 localStorage不会自动把数据发给服务器,仅在本地保存仅在客户端(即浏览器)中保存,不参与和服务器的通信.
存储大小:
cookie 数据根据不同浏览器限制,大小一般不能超过 4k
sessionStorage 和 localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
有期时间:
localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除据
sessionStorage 数据在当前浏览器窗口关闭后自动删除
cookie 设置的cookie过期时间之前一直有效,与浏览器是否关闭无关.
session是由cookie进行标记的.如果用session在服务端进行存储,会出现的情况是涉及到服务器之间session的复制.解决办法:将有状态的服务转化为无状态的服务
(1)共享session
将session提取出来,集中存放(有点像开辟了一个session数据库的赶脚。。)
(2)token
服务是不需要进行存储,服务可以通过解析token里面的信息来判断是否登陆。token 里面是可以携带cookie解析出来的信息的。这种情况下,去掉了服务器存储的负担,只需要每次在服务端对token增加一个校验就可以了。
cookie和session的区别:
1、cookie数据存放在客户的浏览器上,session数据放在服务器上
2、cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session
3、session会在一定时间内保存在服务器上,当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie
5、建议将登录信息等重要信息存放为session,其他信息如果需要保留,可以放在cookie中
6、session保存在服务器,客户端不知道其中的信心;cookie保存在客户端,服务器能够知道其中的信息
7、session中保存的是对象,cookie中保存的是字符串
8、session不能区分路径,同一个用户在访问一个网站期间,所有的session在任何一个地方都可以访问到,而cookie中如果设置了路径参数,那么同一个网站中不同路径下的cookie互相是访问不到的
十二.动态路由
动态路由有一个什么适用场景呢?比如在写商品详情页面的时候,页面结构都一样,只是商品id的不同,所以这个时候就可以用动态路由动态。www.jianshu.com/p/f499d9f64…
十三.平时都是用什么实现跨域的?
jsonp: 利用