1.call/apply/bind原理手写
//--call
let obj = {name:'wutongyue'};
Function.prototype.myCall = function(context,...args){
context.fn = this;
let res = context.fn(...args);
delete context.fn;
return res;
}
let fn = function(a,b,c,d){
console.log(a,b,c,d,this.name);
}
let res = fn.myCall(obj,2,3);
//--bind
Function.prototype.myBind = function(context,...args){
context.fn = this;
return function(...argss){
let res = context.fn(...args.concat(argss));
delete context.fn;
return res;
}
}
let bindRes = fn.myBind(obj,2,3);
bindRes(4,5)
2.MVVM原理
简单的说,就是视图请求数据,将请求发给控制层,控制层的两端具有监听机制,直接从模型层获取数据,一端改变全端改变,利用数据劫持,结合订阅模式与发布模式,实现数据的双向绑定
3.MVC原理
简单的说,就是视图请求数据,将请求发给模型层,模型层将请求发给控制层,控制层处理好后将数据发给模型层,模型层将数据发给视图层,视图层再进行更行和页面渲染。M就是模型层,V就是视图层,C就是控制层,控制层是视图层和模型层的桥梁
4.MVVM与MVC的区别
1.简化的业务逻辑层与视图层之间的关系,因为业务逻辑层只需负责处理好数据就行,视图层就自然变化了
2.无需手动操作dom,因为只需操纵数据,视图就自动变化了
3.减少了dom操作,提高了性能,因为是将差异构建到真实的dom树中,实现了局部更新
5.手写promise
const PENDING = 'PENDING';
const SUCCESS = 'SUCCESS';
const FAIL = 'FAIL';
const isFunction = variable => typeof variable === 'function';
class MyPromise{
constructor(handle){
if(!isFunction(handle)) throw new Error('the handle is not a function');
this._status = PENDING;
this._value = undefined;
this.successedQueues = [];//成功执行的函数队列
this.failedQueues = []; //失败执行的函数队列
try{
handle(this._resolve.bind(this),this._reject.bind(this));
}catch(err){
this._reject(err);
}
}
// new MyPromise时handle成功输出的函数
_resolve(val){
if(this._status !== PENDING) return;
const run = () => {
this._status = SUCCESS;
// 成功时执行的函数
const resolve = value => {
let cb;
while(cb = this.successedQueues.shift()){
cb(value);
}
}
// 失败时执行的函数
const reject = err => {
let cb;
while(cb = this.failedQueues.shift()){
cb(err);
}
}
// 对值进行判断
if(val instanceof MyPromise){
val.then(value => {
this._value = value;
resolve(value)
},err => {
this._value = err;
resolve(err)
});
}else{
this._value = val;
resolve(val);
}
}
setTimeout(run,0);
}
// new MyPromise时handle失败输出的函数
_reject(val){
if(this._status !== PENDING) return;
const run = () => {
this._status = SUCCESS;
this._value = val;
let cb;
while(cb = this.failedQueues.shift()){
cb(val);
}
}
setTimeout(run,0)
}
then(onSucess,onFail){
const { _status, _value } = this;
return new MyPromise((onSucessNext,onFailNext) => {
//成功时执行的函数
const success = val => {
try{
if(!isFunction(onSucess)){
onSucessNext(val);
}else{
let res = onSucess(val);
if(res instanceof MyPromise){
res.then(onSucessNext,onFailNext);
}else{
onSucessNext(res);
}
}
}catch(err){
onFail(val);
}
}
// 失败时执行的函数
const fail = val => {
try{
if(!isFunction(onFail)){
onFailNext(val);
}else{
let res = onFail(val);
if(res instanceof MyPromise){
res.then(onSucessNext,onFailNext);
}else{
onSucessNext(res);
}
}
}catch(err){
onFail(val);
}
}
// 对状态进行判断
switch(_status){
case PENDING:
this.successedQueues.push(success);
this.failedQueues.push(fail);
break;
case SUCCESS:
success(_value);
break;
case FAIL:
fail(_value);
break; }
});
}
// 添加静态成功打印方法
static resolve(val){
if(val instanceof MyPromise) return val;
return new MyPromise(resolve => resolve(val));
}
static reject(val){
return new MyPromise((resolve,reject) => reject(val));
}
// 添加静态all方法
static all(list){
return new MyPromise((resolve,reject) => {
let count = 0;
let values = [];
for(let [i,p] of list.entries()){
// 为了保证p是MyRromise对象
this.resolve(p).then(val => {
values[i] = val;
count++;
if(count === values.length) resolve(values);
},err => {
reject(err);
})
}
});
}
// 添加catch方法
static catch(onFail){
return this.then(undefined,onFail);
}
// 添加then后无论如何都执行的方法finally
finally(cb){
return this.then(
value => MyPromise.resolve(cb()).then(() => value),
err => MyPromise.resolve(cb()).then(() => {throw err})
);
}
static race(list){
return new MyPromise((resolve,reject) => {
for(let p of list){
this.resolve(p).then(res => {
resolve(res)
},err => {
reject(err);
})
}
});
}
}
// 测试MyPromise;
let promise = new MyPromise(res => {
res('nihao');
});
promise.then(res => {
return res + 'hello';
}).then(res=> {
console.log(res);
})
// 测试all
let promise1 = new MyPromise(res => {
res('');
});
let promise2 = new MyPromise(res => {
res('');
});
let promise3 = new MyPromise(res => {
res('');
});
MyPromise.all([promise1, promise2, promise3]).then(res => {
const [promise1, promise2, promise3] = res;
console.log(promise1, promise2, promise3);
})
// 测试finally
new MyPromise(res => {res('kkk')}).then(res => {
return res;
}) .then(res => {console.log(res)})
6.订阅模式与发布模式手写
class Emitter {
constructor(){
this.events = {};
}
on(eventName,fn){
let fnList = this.events[eventName] || [];
fn.push(fn)
if(eventName){
this.events[eventName] = fnList;
}
}
emit(eventName,...args){
let funs = this.events[eventName];
if(funs&&funs.length){
for(let fun of funs){
fun(...args);
}
}
}
off(eventName,fn){
let funs = this.events[eventName];
if(!funs) return -1;
if(fn){
funs.splice(fn,1);
}else{
delete this.events[eventName];
}
}
}
7.什么是响应式原理
底层用的是Object.defineProperty,当然vue3改用proxy,拿vue来说,组件的data内部有两个方法,一个getter一个setter,getter主要负责在组件初始化加载时获取相应的依赖,而setter主要负责,当数据变动时,通知watch侦听器对与此数据相依赖的数据或者视图进行更新
8.防抖与节流是什么,请手写
防抖就是在规定时间间隔内若多次调用,那么触发的时间会一直往后延;
节流就是在规定时间内若多次调用,无效,只触发一次
防抖:
function decounce(fn,wait){
let timer = null;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
fn.myApply(this,args);
},wait)
}
}
节流:
function throttle(fn,wait){
let timer = null,
flag = true,
params = null;
return (...args) => {
params = args;
if(!flag) return;
flag = false;
clearTimeout(timer);
timer = setTimeout(() => {
fn.myApply(this,params);
flag = true;
},wait)
}
}
9.手写instanceof原理代码
function instance_of(L,R){
if(L === R) return true;
if(!L) return false;
L = L.__proto__;
R = R.prototype;
while(true){
if(L === null) return false;
if(L === R) return true;
L = L.__proto__;
}
}
console.log(instance_of([],Array));
10.原生ajax原理
第一:创建一个新的XMLHttpRequest对象
第二:设置回调函数
第三:配置请求信息
第四:发送请求
第五:配置回调函数,等待响应
//第一步,创建XMLHttpRequest对象
var xmlHttp = new XMLHttpRequest();
function CommentAll() {
//第二步,注册回调函数
xmlHttp.onreadystatechange = callback1;
//{
// if (xmlHttp.readyState == 4)
// if (xmlHttp.status == 200) {
// var responseText = xmlHttp.responseText;
// }
//}
//第三步,配置请求信息,open(),get
//get请求下参数加在url后,.ashx?methodName = GetAllComment&str1=str1&str2=str2
xmlHttp.open("post", "/ashx/myzhuye/Detail.ashx?methodName=GetAllComment", true);
//post请求下需要配置请求头信息
//xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
//第四步,发送请求,post请求下,要传递的参数放这
xmlHttp.send("methodName = GetAllComment&str1=str1&str2=str2");//"
}
//第五步 根据不同的响应进行处理
function callback1() {
if (xmlHttp.readyState == 4)
if (xmlHttp.status == 200) {
//取得返回的数据
var data = xmlHttp.responseText;
//json字符串转为json格式
data = eval(data);
$.each(data,
function (i, v) {
alert(v);
});
}
}
11.快速排序与二分查找手写
//二分查找
let ind = bsearch([1,2,3,4,5,6,7,8,9],20);
console.log(a)
function bsearch(arr,target){
if(arr&&Array.isArray(arr)){
return bsearchInternally(arr,0,arr.length-1,target)
}
}
function bsearchInternally(arr,left,right,target){
if(left > right) return -1;//查不到
let mid = Math.floor(left + (right - left)/2);
if(arr[mid] === target){
return mid;
}
if(arr[mid] > target){
return bsearchInternally(arr,left,mid-1,target)
}else{
return bsearchInternally(arr,mid+1,right,target)
}
}
// 快速排序
function quickSort(arr){
if(arr&&Array.isArray(arr)){
if(arr.length<2) return arr;//基线条件
// 递归条件
let midIndex = Math.floor(arr.length/2),
basic = arr[midIndex],//基准值
greater = [],
less = [];
for (let i = 0,len = arr.length; i < len; i++) {
if(midIndex === i) continue;
if(basic >= arr[i]){
less.push(arr[i]);
}else{
greater.push(arr[i]);
}
}
return [...quickSort(less),basic,...quickSort(greater)];
}
}
console.log(quickSort([10,1,1,2,3,4,5,6,3,2,4,5,6,2]));
12.手写数据双向绑定原理
let input = document.querySelector("input");
let obj = {name:'wutongyue'};
Object.defineProperty(obj,'name',{
set(value){
console.log(value)
input.value = value;
this.value = value;
}
})
input.oninput = function(e){
obj.name = e.target.value;
}
setTimeout(_ => {
obj.name = 'wutongyue';
},2000)
13.函数的柯里化手写代码
function add(n){
let fn = function(m){
return add(m + n);
}
fn.valueOf = function(){
return n;
}
fn.toString = function(){
return '' + n;
}
return fn;
}
console.log(add(1)(2)(3).valueOf())
14.用es6+类的方式实现拖拽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style>
*{
margin:0;
padding:0;
}
#drag{
width:100px;
height:100px;
background-color: blue;
position:absolute;
}
</style>
<body>
<div id="drag"></div>
<script>
class Drag{
constructor(id){
this.domObj = document.querySelector(id);
this.disX = 0;
this.diY = 0;
this.init();
}
init(){
this.domObj.onmousedown = function(e){
this.disX = e.clientX - this.domObj.offsetLeft;
this.disY = e.clientY - this.domObj.offsetTop;
document.onmousemove = this.mouseMove.bind(this);
document.onmouseup = this.mouseUp.bind(this);
return false;
}.bind(this)
}
mouseMove(ev){
console.log(this.domObj.style.left)
this.domObj.style.left = ev.clientX - this.disX + 'px';
this.domObj.style.top = ev.clientY - this.disY + 'px';
}
mouseUp(){
document.onmousemove = null;
document.onmouseup = null;
}
}
class addDrag extends Drag{
mouseMove(ev){
super.mouseMove(ev);
//以下书写自己额外逻辑代码
}
}
let drag = new Drag('#drag');
</script>
</body>
</html>
15.浏览器输入url以后发生了什么?三次握手、四次挥手
可以分七步来说:
第一:浏览器的主进程介入,开启一个线程
第二:DNS解析,获取ip地址,发起http请求,其中会判断是否从缓存读取,期间有tcp的三次握手,然后等待响应,开始下载报文
第三:将下载的内容转交给renderer进程管理
第四:renderer进程解析css rule tree 和 dom tree
第五:遇到像link、script、img等标签,会对相应的资源进行下载
第六:将生成的css rule tree 和dom tree合成render tree,然后进行渲染
第七:渲染完毕,renderer进程关闭,期间有tcp四次挥手
16.虚拟dom原理、及其优缺点
可以分三点来说:
第一:虚拟dom是真实dom的一个抽象
第二:当状态改变时,记录新树与旧树之间的差异
第三:将差异构建到真实的dom树中
优点有:第一-减少了dom操作,提高了性能,因为是将差异构建到真实dom树中;第二-有利于跨平台,因为本质上虚拟dom就是一个json对象
缺点是在一些对性能要求非常高的场景下无法达到极致化
17.什么是原型链
原型链主要由原型对象构成,每个原型对象都有相应的构造函数,原型对象有一个指针指向这个构造函数,而每一个生成的实例都有相应的内部指针指向这个原型对象,环环相扣形成了一条原型链
18.什么是spa,及其优缺点
SPA即单页,仅在web页面初始化时加载相应的javascript、html、css,一旦加载完成,不会因为用户的操作而使得页面刷新或者跳转。
优点有:第一-提高了用户体验,提高了性能,因为局部的内容改变不会触使整个页面刷新;第二-基于第一点,减少了服务器的压力
缺点有:第一-不利于seo;第二-浏览器的前进后退按钮不能用了,需要自己管理;第三-初次加载缓慢,即首屏加载慢
19.模拟new手写原理代码
//手写new
function myNew(fn,...args){
let obj = {};//创建新的对象
obj.__proto__ = fn.prototype;//将构造函数的原型链付给新的对象
let res = fn.apply(obj,args);//将this上下文指向这个新的对象,并执行构造函数
return typeof res === "object" ? res : obj;
}
function Pro(name){
this.name = name;
}
const newObj = myNew(Pro,'wutongyue');
console.log(newObj.name);
20.计算数组重复元素次数
let arr = [1,1,1,2,2,3,4,5,6,7," "," ",' '];
function countArr(arr){
if(Array.isArray(arr)){
let addObj = {};
arr.forEach(i => {
if(addObj[i])
addObj[i]++;
else
addObj[i] = 1;
})
return addObj;
}
}
console.log(countArr(arr));
21.用递归的方式实现数值区间求和
function add(num1,num2){
let num = num1 + num2;
if(num2 + 1 > 100){
return num;
}else{
return add(num, num2 + 1)
}
}
alert(add(1,2))
22.js的单例模式、工厂模式、适配器模式
// 工厂模式
function factory(name){
let _obj = {}
_obj.name = name;
_obj.fn = () => {
console.log('我是函数');
}
_obj.obj = {
age:35
}
return _obj;
}
let fac = factory('wutongyue');
console.log(fac.fn())
//单例模式
function Single(name){
this.name = name;
}
let singleTon = function(name){
let obj = null;
return (name) => {
if(!obj) obj = new Single(name);
return obj;
}
}()
let res1 = singleTon('hahahah').name;
let res2 = singleTon('wutongyue').name;
console.log(res1,res2);
// 是适配器模式---改用es6类模式
class MyObj{
constructor(name){
this.name = name;
}
}
class Adapter{
constructor(obj){
this._obj = obj;
}
getSpecialName(myName){
return this._obj.name + myName;
}
}
let adapter = new Adapter(new MyObj('hahahah')),
newName = adapter.getSpecialName('wutongyue');
console.log(newName);
23.import与require的区别
区别有:都可以实现异步加载文件
不同的是,1.import只能放在文件的顶部,它是编译式的,而require是运行式的,理论上可以在代码任何一处执行;2.import对应的导出可以使用四种方式‘,分别是exports、module.exports、export、export default,require对应的导出方式为module.exports和exports
24.vue中data为何需是函数而不是对象
这个涉及内存地址指向问题,若是对象,那么我们知道js对象是引用关系,作用域没有被隔离,一旦组件复用就会产生一些问题
25.BFC的特点
例如:1.BFC内部元素与外部元素互不影响;2.BFC元素与浮动元素不会重叠;3.BFC内部元素之间的间距有叠加的效果
26.实现对100000000加“,”,如1000,000,000
console.log('100000000000'.replace(/(?=(\d{3})+$)/g,_$ => _$+',').replace(/^,/,''))
27.实现对数组的全排列
let arr = [1,2,3,4];
function allSort(input){
let containerArr = [],
temp = [];
-function main(input){
let node;
for(let i = 0, len = input.length; i < len; i++){
node = input.splice(i,1)[0];
temp.push(node);
if(input.length == 0) containerArr.push(temp.slice());
main(input);
input.splice(i,0,node);
temp.pop();
}
}(input)
return containerArr;
}
console.log(allSort(arr));
28.vue有几种路由模式,及其区别
有三种,分别是hash路由模式、history路由模式、abstract
因为主要用到hash路由模式和history模式,所以说下这两种模式的区别:1.hash路由模式的地址带有#号;2.history路由模式下的页面可能会出现404的情况;3.hash路由模式兼容低端浏览器和ie浏览器;4.这两种模式下都可以实现改变url地址而使得页面不刷新的这种效果
29.session是什么,应用场景是什么
session是会话储存机制,针对浏览器的,主要用于使得浏览器客户端在访问同一域名下的不同页面时产生关联;应用场景比如验证码功能、统计在线人数、辅助控制用户的登录行为等
30.react最新生命周期
constructor、getDrivedStateFromProps、render、componentDidMount、shouldComponentUpdate、getSnapShotFromProps、componentDidUpdate、componentWillUnmount
31.如何渲染页面,请描述其过程
1.解析dom,生成dom树
2.解析css,生成css rule tree
3.合成dom tree和css rule tree
4.重排和重绘
32.如何seo
1.合理的title、description、keyword
2.将重要内容放在页面的前面
3.服务端渲染ssr(主要因为前后端分离)
4.提高网站速度,因为网站速度是搜索引擎排名的重要指标
5.语义化标签的使用
33.diff算法的复杂度为何可以从O-n3降到O-n
主要基于两个假设:
1.不同的组件产生不同的dom结构,相同的组件产生类似的dom结构
2.同层级的节点中,具有唯一的标识来作为区分
34.css3中animation的6个参数值
animation:name 完成的时间 时间曲线 延迟秒数 循环类型 alternate
35.cmd与amd的区别,请用代码表示
//--amd-依赖前置
define(['./main.js','./test.js'],function(main,test){
let doSome = function(){
main.fn()//做点啥
}
return {
fn:doSome
}
})
//-cmd-依赖就近
define(function(require,exports,module){
let a = require('./a');
a.doSomething()//就近做点啥
})
36.数组中任意两数加和等于目标值手写算法(不重复)
function addTwo(arr,target){
if(!Array.isArray(arr)) return;
if(arr.length < 2) throw new Error('the length of arr must be over 2');
let hash = {},list = [];
for(let [k,v] of arr.entries()){
let val = target - v;
if(hash[val]){
list[0] = v;
list[1] = arr[hash[val]];
break;
}else{
hash[v] = k;
}
}
return list;
}
alert(addTwo([1,2,2,2,2222,88,67,7],74))
37.new实现了什么,请分步骤说明
第一:创建一个新的对象
第二:将构造函数的原型链赋给这个新的对象
第三:将this指向这个新的对象,执行构造函数给这个新的对象赋值
第四:返回this
38.Evenloop是什么
简单的说就是:一个主线程不断从任务队列中调取任务来执行的这么一个循环过程就是Evenloop
39.setState在任何场景都是异步吗,请说明
在组合事件和钩子函数中是异步的,在原生事件和定时器延时器之类的是同步的
40.什么是事件委托
就是利用事件冒泡,仅指定一个事件处理程序就可以管理某类底下所有的事件
41.webpack的loader与plugin
loader是指转换器,可以将像less、sass文件转换为css文件;
plugin是指webpack的一个拓展器,用于监听节点的变化,在合适的节点中做出相应的举动,例如压缩图片等
42.EC是什么
其是指执行环境,可以简单的理解为一个对象,其包含:1.this指向、2.作用域链、3.ao对象;其种类包括:1.全局执行上下文、2.局部执行上下文、3.eval执行上下文
43.vue中的nextTick是什么,其原理
是指在下一次dom更新循环结束之后执行延迟回调,用于获取更新后的dom状态
44.设置cookie和获取cookie手写
// 设置cookie
function setCookie(key,val,days){
let date = new Date();
date.setDate(date.getDate() + days);
document.cookie = `${key}=${escape(val)}; expires=${date.toGMTString()}; path=/`;
console.log(date.toGMTString())
}
// 获取cookie
function getCookie(name){
if(document.cookie.length > 0){
let str = document.cookie;
let start = str.indexOf(`${name}=`);
if(start > -1){
start = start + name.length + 1;
let end = str.indexOf(';',start);
if(end === -1){
end = str.length;
}
return unescape(str.substring(start,end));
}
}
return '';
}
setCookie('hehe','nihao12',5);
let coo = getCookie('hehe');
console.log(coo);
45.vue的设计规则理解
1.是一个渐进式javascript框架(很好的承接过去)
2.具有灵活(按需引入。。。)、高效(响应式原理+diff算法)、易用(只需在业务逻辑层操纵数据,视图就自然变化了)
46.vue与react区别
1.react中setState有坑
2.vue提供了很多简单易用的指令,而react偏原生,提供的较少,v-model…
3.比较虚拟dom的方式有差异,个人看法:vue会更加高效一些
47.封装兼容的requestAnimation,并实现简单例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<style>
#test{
background:red;
width:100px;
height:100px;
position: absolute;
left:0;
}
</style>
<body>
<div id="test"></div>
<script>
window.requestAnimation = function(){
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback,ele){
setTimeout(callback,1000/60);
}
)
}();
let ele = document.querySelector("#test"),
startTime = undefined,
computed = 0;
function render(time){
time = new Date();
if(startTime === undefined) startTime = time;
computed = (time - startTime) / 1000;
//500为运动距离
ele.style.left = computed*500 + 'px';
}
test.onclick = function(){
(function animate(){
render();
if(computed <= 1)
requestAnimation(animate,ele)
})();
}
</script>
</body>
</html>
48.克隆的几种形式,并手写一种递归克隆,克隆对象可能是数组或者对象
// 克隆数组或对象
function clone(object){
if(typeof object !== 'object') return object;
let flag = Array.isArray(object),
parent = flag ? [] : {};
if(flag){
for(let i of object){
parent.push(clone(i));
}
}else{
for(let i in object){
parent[i] = clone(object[i]);
}
}
return parent;
}
let obj = {name:'wutongyue',age:26,hehe:{nihao:' ',arr:[1,2,3,4]}};
let cloneNode = clone(obj);
obj.hehe.arr[2] = 'landfuhasdfjkasdh';
console.log(cloneNode);
49.require.ensure实现懒加载的代码手写
const com = res => require.ensure([],res(require(‘路径’)))
50.vuex的设计思想原理
其实它是借鉴了flux和redux,将数据储存在全局store当中,再将store挂载到vue组件的每一个实例中,利用vue的细粒度数据响应机制来进行高效的状态更新
优点:1.在某些情况可以减少ajax请求、2.很好的解决了非父子组件间通信的问题
使用场景,例如购物车、登录
51.请用代码表示典型的两个内存泄漏例子
//-泄漏例子一,在ie浏览器存在这种情况
let dom = document.createElement('div');
document.body.appendChild(dom);
dom.onclick = function(){};
document.removeChild(dom);
//-内存泄漏例子二-执行的函数的栈不会被销毁
function doSomething(){
let dom = document.getElementById("id")
dom.addEventListener("click",function(){})
//解决方案--dom = null;
}
doSomething()
52.diff算法原理
分两点来说:
1.先同级比较,再比较子节点
2.计较子节点时,采用递归的方式,当然会先判断是否有子节点
53.vue中v-for与v-if为何不能连着用?v-for为何要加key和key为何最好不能用index
为何要加key?–使得diff算法更加高效-1.若不加key,就会复用当前节点,使得下一个节点保留上一个节点的状态,这样会出一些问题、2.若加了key,有利于hash数据结构的利用,因为我们知道哈希以键找值是很快的
为何不能连着用?-因为v-for的优先级高于v-if,节点会先v-for出来,再进行v-if判断,若节点有用还好,没用的话又将它移除,这样会导致浪费性能
为何最好不用index作为key?-因为若数组内的某个元素删除了,会导致此元素后面元素的index发生改变,那么绑定的key就会监测到所绑定的index改变而重新渲染浪费了性能,若绑定的是id就不会有此问题
54.vue项目的优化方案
三个层面:
一、web技术层面:
1.cdn加速
2.利用浏览器本地存储技术,,像localStorage
二、webpack层面:
1.压缩图片
2.提取公共代码
等
三、代码层面:
1.合理的使用v-if和v-show
2.v-if和v-for不要连着用
3.v-for循环时给节点加key
4.watch和computed分场景使用
5.路由懒加载
6.图片懒加载
等
55.vue的渲染过程
这边分四步来说:
1.将template模板转换成render函数
2.调用render函数,获取到虚拟dom节点
3.比较虚拟dom的差异,将差异构建到真实的dom树中
4.当数据变动时,重新调用render函数,获取到虚拟dom节点,返回第三步
56.svg与canvas区别
区别有:比如1.svg不依赖像素,而canvas依赖像素,换句话说就是svg可以无限放大不失真 2.svg支持事件处理器,而canvas不支持 3.canvas适合图像密集型的游戏 而svg适合带有大型渲染区域的应用程序比如谷歌地图
57.H5、公众号、小程序多端情况下如何实现信息共享(联通)
只要绑定同一个微信开放平台的帐号,那么用户的unionid具有唯一性,可通过unionid作为唯一标识来实现信息共享,网页通过授权方式,另一种方式可以采用手机号作为唯一标识,这是通常做法,面试官比较乐意听到答案一