最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 3月面试实录(未整理完)

    正文概述 掘金(Zyong)   2021-03-30   525
    • 以下是我三月里所有面试过的公司问的真实题目
    • 后面还有很多没有写答案总结,写了答案的,如果答得不好,请多多指教。
    • 我已上岸,希望大家加油!

    公司个人问题

    • 自我介绍
    • 公司团队规模
    • 公司的业务
    • 你的主要工作
    • 前端技术后端技术
    • 如何说服上司进行项目重构
    • 讲一个你觉得做的不错的项目
    • 项目开发流程
    • 如何进行项目部署
    • 性能监控,数据分析有没有做过
      • 主要是通过navigator Timing api进行监控
      • 引入的是岳鹰监控平台进行监控
    • 团队之间的代码管理和审查

    Css

    卡片翻转

    css盒子模型

    盒子模型包括元素的内容,边框(border)、内边距(padding)、外边距(margin)组成 标准盒子模型大小= width(content) + border + padding + margin 怪异盒子大小= width(content+border+padding) + margin

    css左右布局的方式

    BFC

    • 什么是BFC?如何应用?
      • Block format context , 块级格式化.上下文
      • 一块独立渲染区域 ,内部元素的渲染不会影响边界以外的元素
    • 形成BFC的常见条件
      • float 不是 none
      • position 是 absolute 或 fixed
      • overflow 不是 visible
      • display是flex inline-block
    • BFC的常见应用
      • 清除浮动

    Js基础

    判断数据类型

    1. typeof

    • 判断所有的值类型(undefined,string,number,boolean,symbol)
    • 判断是否是函数 // function
    • 判断是否是引用类型 object(null,[1,2,3],{ a : 2 })

    3月面试实录(未整理完) 3月面试实录(未整理完)

    2. instanceof

    • 用来判断A是否为B的实例,比如A instanceof B
    • instance只能用来判断两个对象是否属于实例关系,而不能判断一个对象实例具体属于哪种类型
    • 所以一般用来判断是否是一个数组,[] instanceof Array

    3. Object.prototype.toString.call()

    developer.mozilla.org/zh-CN/docs/…

    • toString()是Object的原型方法,调用该方法,默认返回当前对象的[Class],这是一个内部属性,其格式为[object Xxx],其中Xxx就是对象的类型
    • 对于Object对象,直接调用toString()就能返回[object Object],而对于其他对象,则需要通过call/apply来调用才能返回正确的类型信息
    let obj = new Object()
    obj.toString()  // [object Object]
    
    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 的引用
    

    4. constructor

    constructor是原型prototype的一个属性,当函数被定义时,js引擎会为函数添加原型prototype,并且这个prototype中的constructor属性指向函数引用,因此重写prototype会丢失原来的constructor. 注意: 1:null 和 undefined 无constructor,这种方法判断不了。 2:还有,如果自定义对象,开发者重写prototype之后,原有的constructor会丢失,因此,为了规范开发,在重写对象原型时一般都需要重新给 constructor 赋值,以保证对象实例的类型不被篡改。 3月面试实录(未整理完)

    什么是闭包

    • 在内层函数中可以访问到外层函数的作用域就是闭包

    箭头函数和普通函数的区别

    www.jianshu.com/p/231a6f58e…

    1. 箭头函数是匿名函数,不能作为构造函数,不能使用new
    2. 箭头函数不能绑定arguments,取而代之用rest参数...解决
    3. 箭头函数的this永远指向其上下文的this,没有办改变其指向,普通函数的this指向调用它的对象
    4. 箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值

    简单说一下this指向问题

    1、普通函数调用 this指向windows; 2、对象函数调用 this指向这个对象; 3、构造函数调用 this指向当前实例本身(新创建的对象); 4、call和apply,bind调用 传入什么指向什么,传入的参数;(call可以传多个参数,apply可以传二个参数,第二个数组,不然报错TypeError) 5、箭头函数调用 this指向上级作用域的值(当前函数上下文);

    Js数组

    数组中查找指定元素

    juejin.cn/post/687784…

    1. includes

    developer.mozilla.org/en-US/docs/… includes() 方法用来判断一个数组是否包含一个指定的值,如果包含则返回 true,否则返回 false。

    var a = [1,2,3,4,5,6]
    
    a.includes(2)  // true
    a.includes(2,3)  // false
    a.includes(5,-2)  // true
    a.includes(5,-1)  // false
    

    2. indexOf

    developer.mozilla.org/en-US/docs/… indexOf() 方法返回指定元素在数组中的第一个索引,如果不存在,则返回-1。

    var array = [2, 5, 9];
    
    array.indexOf(2);     // 0
    array.indexOf(7);     // -1
    array.indexOf(9, 2);  // 2
    array.indexOf(2, -1); // -1
    array.indexOf(2, -3); // 0
    

    3. lastIndexOf

    developer.mozilla.org/en-US/docs/… lastIndexOf() 方法返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找,从 fromIndex 处开始。

    var array = [2, 5, 9, 2];
    
    array.lastIndexOf(2);      // 3
    array.lastIndexOf(7);      // -1
    array.lastIndexOf(2, 3);   // 3
    array.lastIndexOf(2, 2);   // 0
    array.lastIndexOf(2, -2);  // 0
    array.lastIndexOf(2, -1);  // 3
    
    

    4. some

    developer.mozilla.org/zh-CN/docs/… some() 方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个 Boolean 类型的值。

    function isBiggerThan10(element, index, array) {
      return element > 10;
    }
    
    [2, 5, 8, 1, 4].some(isBiggerThan10);  // false
    [12, 5, 8, 1, 4].some(isBiggerThan10); // true
    

    5. every

    developer.mozilla.org/zh-CN/docs/… every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。

    function isBigEnough(element, index, array) {
      return element >= 10;
    }
    [12, 5, 8, 130, 44].every(isBigEnough);   // false
    [12, 54, 18, 130, 44].every(isBigEnough); // true
    

    6. filter

    developer.mozilla.org/zh-CN/docs/… filter() 方法创建一个新数组, 包含通过所提供函数实现的测试的所有元素。

    function isBigEnough(element) {
      return element >= 10;
    }
    
    var filtered = [12, 5, 8, 130, 35].filter(isBigEnough);
    // filtered is [12, 130, 35] 
    

    7.find

    developer.mozilla.org/zh-CN/docs/… find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。

    var inventory = [
        {name: 'apples', quantity: 2},
        {name: 'bananas', quantity: 0},
        {name: 'orange', quantity: 5}
    ];
    
    function findOranges(fruit) { 
        return fruit.name === 'orange';
    }
    
    console.log(inventory.find(findOrange));
    // { name: 'orange', quantity: 5 }
    
    

    8. findIndex

    developer.mozilla.org/zh-CN/docs/… findIndex() 方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1。

    var inventory = [
        {name: 'apple', quantity: 2},
        {name: 'banana', quantity: 0},
        {name: 'orange', quantity: 5}
    ];
    
    function findOrange(fruit) { 
        return fruit.name === 'orange';
    }
    
    console.log(inventory.findIndex(findOrange));
    
    

    3月面试实录(未整理完)

    数组对象排序

    对以下数据进行排序

    var person = [{name:"Rom",age:12},{name:"Bob",age:22},{name:"Ma",age:5},{name:"Tony",age:25}]
     
    
    

    sort

    developer.mozilla.org/zh-CN/docs/…

    • 升序排列是把数据从小到大进行排列(1、2、3、4、5)
    • 降序排列是把数据从大到小进行排列(5、4、3、2、1)
    person.sort((a,b)=>{ return a.age-b.age})//升序
     
    person.sort((a,b)=>{ return b.age-a.age})//降序
    

    1.如果没有指明 compareFunction ,那么元素会按照转换为的字符串的诸个字符的Unicode位点进行排序。 2.如果指明了 compareFunction ,那么数组会按照调用该函数的返回值排序。即 a 和 b 是两个将要被比较的元素:

    a.如果 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 之前; b.如果 compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变。(ECMAScript 标准并不保证这一行为,而且也不是所有浏览器都会遵守,例如 Mozilla 在 2003 年之前的版本); c.如果 compareFunction(a, b) 大于 0 , b 会被排列到 a 之前。 d. compareFunction(a, b)必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的。(利用这一特性,可实现随机排序)

    for-of 和 for-in 的区别

    for-in是ES5标准,遍历的是key(可遍历对象、数组或字符串的key);for-of是ES6标准,遍历的是value(可遍历对象、数组或字符串的value)

    for-in

    var arr = [1, 2, 4, 5, 7];
    for (var index in arr) {
      console.log(myArray[index]);
    }
    

    for-in弊端

    1.index索引为字符串型数字(注意,非数字),不能直接进行几何运算。 2.遍历顺序有可能不是按照实际数组的内部顺序(可能按照随机顺序)。 3.使用for-in会遍历数组所有的可枚举属性,包括原型。例如上例的原型方法method和name属性都会被遍历出来,通常需要配合hasOwnProperty()方法判断某个属性是否该对象的实例属性,来将原型对象从循环中剔除。

    • 所以for-in更适合遍历对象,通常是建议不要使用for-in遍历数组。
    for (var key in myObject) {
      if(myObject.hasOwnProperty(key)){
        console.log(key);
      }
    }
    

    for-of

    for-of可以简单、正确地遍历数组(不遍历原型method和name)。 因此建议是使用for-of遍历数组,因为for-of遍历的只是数组内的元素,而不包括数组的原型属性method和索引name。

    var myArray = [1, 2, 4, 5, 6, 7];
    myArray.name = "数组";
    myArray.getName = function() { return this.name; }
    for (var value of myArray) {
        console.log(value);
    }
    

    区别总结

    • for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值。
    • for-in总是得到对象的key或数组、字符串的下标。
    • for-of总是得到对象的value或数组、字符串的值,另外还可以用于遍历Map和Set。

    数组去重

    segmentfault.com/a/119000001…

    1. ES6 Set去重

    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", {}, {}]
    
    // 简化
    [...new Set(arr)]
    

    2. for嵌套for,然后splice去重

    {}和NaN无法去重

    function unique(arr) {
    	for (let i = 0; i < arr.length; i++) {
    		for (let j = i + 1; j < arr.length; j++) {
    			if (arr[i] === arr[j]) {
    				arr.splice(j, 1)
    				j-- // 删除一个数据,索引关系变了,需要减1找到没有判断过的那个数据
    			}
    		}
    	}
    	return 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))  //NaN和{}没有去重
    

    3. indexOf去重

    function unique(arr) {
    	if (!Array.isArray(arr)) {
    		console.log('is not array')
    		return
    	}
    	let array = []
    	for (let i = 0; i < arr.length; i++) {
    		if (array.indexOf(arr[i]) === -1) {
    			array.push(arr[i])
    		}
    	}
    	return array
    }
    
    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, "NaN", 0, "a", {…}, {…}]  //NaN、{}没有去重
    

    4. includes去重

    function unique(arr) {
        if (!Array.isArray(arr)) {
            console.log('type error!')
            return
        }
        var array =[];
        for(var i = 0; i < arr.length; i++) {
                if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
                        array.push(arr[i]);
                  }
        }
        return array
    }
    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", {…}, {…}]     //{}没有去重
    

    5. filter + hasOwnProperty去重(可以去重所有)

    function unique(arr) {
    	let obj = {}
    	return arr.filter((item, index, arr) => {
    		console.log(typeof item + item)
    		return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
    	})
    }
    
    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", {…}]   //所有的都去重了
    
    // if (obj.hasOwnProperty(typeof item + item)) {
    // 	return false
    // } else {
    // 	obj[typeof item + item] = true
    // 	return obj[typeof item + item]
    // }
    
    // if (!obj.hasOwnProperty(typeof item + item)) {
    // 	obj[typeof item + item] = true
    // 	return obj[typeof item + item]
    // }
    

    6. filter + indexOf

    function unique(arr) {
    	return arr.filter((item, index, arr) => {
        //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
    		return arr.indexOf(item,0) === index
    	})
    }
    
    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", 0, "a", {…}, {…}]
    

    Js进阶

    深拷贝和浅拷贝的区别

    拷贝对象

    const obj1 = {
    	age: 20,
    	name: 'xxx',
    	address: {
    		city: 'beijing'
    	},
    	arr: ['a', 'b', 'c']
    }
    

    浅拷贝:

    • 只拷贝第一层的对象的属性
    • 是对对象地址的复制,并没有开辟新的栈
    • 复制的结果是两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象的属性也会改变

    for...in实现

    function simpleCopy(obj) {
    	// 判断结果是对象还是数组
    	let result = Array.isArray(obj) ? [] : {}
    	for (let i in obj) {
    		// for...in遍历的是key
    		// console.log(i)
    		result[i] = obj[i]
    	}
    	return result
    }
    let obj2 = simpleCopy(obj1)
    console.log(obj2)
    

    Object.assign实现

    let obj2 = Object.assign(obj1)
    console.log(obj2)
    
    obj2.address.city = 'shanghai'
    obj2.arr[0] = 'a1'
    console.log(obj1.address.city)
    console.log(obj1.arr[0])
    

    直接赋值

    深拷贝:

    • 递归拷贝所有层级的属性,
    • 是开辟新的栈
    • 两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性

    递归实现

    function deepClone(obj) {
    	// 判断是否是对象,不是对象,返回结果(返回值类型)
      // obj == null判断了null和undefined两种情况  null==undefined
    	if (typeof obj !== 'object' || obj == null) {
    		return obj
    	}
    	// 初始化结果数据,如果是数组赋值为[],否则为{}
    	let result = Array.isArray(obj) ? [] : {}
    
    	for (let key in obj) {
    		// 判断是否是自己的属性方法,而不是原型属性方法
    		// 如果是递归复制
    		if (obj.hasOwnProperty(key)) {
    			result[key] = deepClone(obj[key])
    		}
    	}
    	return result
    }
    
    let obj2 = deepClone(obj1)
    console.log(obj2)
    obj2.address.city = 'shanghai'
    obj2.arr[0] = 'a1'
    console.log(obj1.address.city)
    console.log(obj1.arr[0])
    
    function deepClone(obj){
        let objClone = Array.isArray(obj)?[]:{};
        if(obj && typeof obj==="object"){
            for(key in obj){
                if(obj.hasOwnProperty(key)){
                    //判断ojb子元素是否为对象,如果是,递归复制
                    if(obj[key]&&typeof obj[key] ==="object"){
                        objClone[key] = deepClone(obj[key]);
                    }else{
                        //如果不是,简单复制
                        objClone[key] = obj[key];
                    }
                }
            }
        }
        return objClone;
    }    
    let a=[1,2,3,4],
        b=deepClone(a);
    a[0]=2;
    console.log(a,b);
    

    Reflect法

    function isObject(obj) {
    	if (typeof obj !== 'object' || obj == null) {
    		return false
    	}
    	return true
    }
    
    function deepClone(obj) {
    	// 如果Reflect.ownKeys()方法的第一个参数不是对象,会报错。
    	if (!isObject(obj)) {
    		throw new Error('obj 不是一个对象')
    	}
    	let result = Array.isArray(obj) ? [...obj] : { ...obj }
    	Reflect.ownKeys(result).forEach(key => {
    		console.log(key)
    		// 是对象进行递归遍历,不是则直接赋值
    		result[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
    	})
    	return result
    }
    
    let obj2 = deepClone(obj1)
    console.log(obj2)
    

    JSON实现

    缺点: 无法实现对对象中方法的深拷贝,会显示为undefined

    function deepClone2(obj) {
      var _obj = JSON.stringify(obj),
        objClone = JSON.parse(_obj);
      return objClone;
    }
    

    lodash函数库实现深拷贝

    let result = _.cloneDeep(test)
    

    封装的通用 js 函数有哪些

    判空、格式化日期、防抖节流、本地存储、axios二次封装、获取url参数、生成随机数、await处理promise返回参数、判断浏览器环境手机系统信息、校验函数

    简单介绍下js垃圾回收机制

    www.cnblogs.com/fundebug/p/…

    介绍

    一般来说没有被引用的对象就是垃圾,就是要被清除, 有个例外如果几个对象引用形成一个环,互相引用,但根访问不到它们,这几个对象也是垃圾,也要被清除。 基本的垃圾回收算法称为**“标记-清除”**,定期执行以下“垃圾回收”步骤:

    • 垃圾回收器获取根并**“标记”**(记住)它们。
    • 然后它访问并“标记”所有来自它们的引用。
    • 然后它访问标记的对象并标记它们的引用。所有被访问的对象都被记住,以便以后不再访问同一个对象两次。
    • 以此类推,直到有未访问的引用(可以从根访问)为止。
    • 除标记的对象外,所有对象都被删除。

    js垃圾回收器的性能

    因为js垃圾回收器是每隔一个周期就执行一次垃圾回收。 如果为变量分配的内存数量不大的话,那么垃圾回收器的回收工作量就不大。但是,当垃圾回收器的工作量过大的时候,就很可能会出现卡顿的情况。

    js内存机制

    event loop(事件循环/事件轮询)

    什么是event loop

    • js是单线程运行的
    • 异步就要基于回调来实现
    • event loop就是异步实现的原理

    event loop过程

    整个过程包含

    • Call stack(调用栈)、
    • WebApis(浏览器api)、
    • Callback Queue(回调函数队列)、
    • event loop(事件轮询)

    过程1

    • 同步代码一步一步放在call stack
    • 遇到异步,会先记录,等待时机(可能是同步代码执行完)
    • 时机到了,就移动到callback Queue

    过程2

    • 如果同步代码执行完了,Event loop开始工作
    • 轮询查找callback queue,如有则移动到call stack执行
    • 然后继续轮询查找

    Vue基础

    1. vue 组件通信

    2. vue 按需加载组件

    • 异步组件
    • import函数
    • 按需加载,异步加载大组件
    <!-- 异步组件 -->
    <FormDemo v-if="showFormDemo"/>
    <button @click="showFormDemo = true">show form demo</button>
    
    components: {
        FormDemo: () => import('../BaseUse/FormDemo'),
    },
    data() {
      return {
          showFormDemo: false,
      }
    }
    
    • 按需加载路由组件
    {
      path: '/home',
      name: 'Home',
      component: () => import(/* webpackChunkName: "tabbar" */ '@/views/tabBar/home/index.vue'),
      meta: { title: '首页', keepAlive: false, showTab: true } as IRouterMeta
    },
    

    3. 封装的通用组件有哪些

    4. 组件如何实现 v-model

    • 自定义v-model
    • 主要是组件上的model属性(prop,event)
    • prop是要绑定的数据
    • event要绑定的事件
    自定义 v-model -->
    <!-- <p>{{name}}</p>
    <CustomVModel v-model="name"/>
      
      
    <template>
        <!-- 例如:vue 颜色选择 -->
        <input type="text"
            :value="text1"
            @input="$emit('change1', $event.target.value)"
        >
        <!--
            1. 上面的 input 使用了 :value 而不是 v-model
            2. 上面的 change1 和 model.event1 要对应起来
            3. text1 属性对应起来
        -->
    </template>
    
    <script>
    export default {
        model: {
            prop: 'text1', // 对应 props text1
            event: 'change1'
        },
        props: {
            text1: String,
            default() {
                return ''
            }
        }
    }
    </script>
    

    5. computed和watch的区别

    计算属性computed

    1. 支持缓存,只有依赖数据发生改变,才会重新进行计算
    2. 不支持异步,当computed内有异步操作时无效,无法监听数据的变化

    3.computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值 4. 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed 5.如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。

    侦听属性watch

    1. 不支持缓存,数据变,直接会触发相应的操作;

    2.watch支持异步; 3.监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值; 4. 当一个属性发生变化时,需要执行对应的操作;一对多; 5. 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,   immediate:组件加载立即触发回调函数执行,   deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到。 6.当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的

    使用场景

    computed          当一个属性受多个属性影响的时候就需要用到computed     最典型的例子: 购物车商品结算的时候 watch     当一条数据影响多条数据的时候就需要用watch     搜索数据

    生命周期有什么,哪些在页面第一次加载执行

    第一次渲染

    • beforeCreate
    • created
    • beforeMount
    • mounted

    更新

    • beforeUpdate
    • updated

    销毁

    • beforeDestroy
    • destroyed

    keep-live

    • activated 组件激活
    • deactivated 组件停用

    什么时候进行异步请求

    • mounted
    • dispatch一个action

    vue修饰符有哪些

    • 表单修饰符
      • .lazy 懒加载
      • .trim 去除首尾空格
      • .number 转换成number
    • 事件修饰符
      • .stop 阻止冒泡
      • .prevent 阻止默认行为
      • .self 触发自身元素
      • .once 只触发一次
      • .capture 事件捕获阶段
      • .passive 懒触发事件
      • **.native **把一个vue组件转化为一个普通的HTML标签,触发事件
    • 鼠标按键修饰符
      • .left 左键点击
      • .right 右键点击
      • .middle 中键点击
    • 键值修饰符
      • **.keyCode **对应ASCII码
    • v-bind修饰符(实在不知道叫啥名字)
      • **.sync **对props进行双向绑定

    data为什么是一个函数

    • .vue组件编译完之后实际上是一个Class
    • 每次使用这个组件相当于是对组件的实例化
    • 实例化的时候去执行data
    • 如果data不是一个函数,那么每一个组件实例的数据都一样的,也就数据共享了
    • 一个组件的数据进行修改时,其他组件数据也会进行修改

    v-show和v-if的区别

    • v-show通过CSS display控制显示和隐藏
    • v-if组件真正的渲染和销毁,而不是显示和隐藏
      • 模板编译成render,with语法,转换成了三元运算
    • 频繁切换显示状态用v-show , 否则用v-if

    v-for为什么用key

    www.jianshu.com/p/4bd5e745c…

    • 必须用key ,且不能是index和random(随机数)
    • diff算法中通过tag和key来判断,是否是sameNode
    • 减少渲染次数,提升渲染性能

    key重复了会怎么样

    • 如果key重复了,在进行增加删除操作时,索引和数组数据关系会错乱
    • diff算法会认为当前节点后的所有节点都进行了更新,会造成多次重复渲染

    defineProperty重写了数组方法会不会影响正常使用数组方法

    • 不会
    • 通过Object.create()创建一个新的对象,原型指向数组原型
    • 扩展的数组方法执行时调用的是数组原型上的方法,以及视图更新

    keep-live的属性和生命周期

    • include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
    • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
    • max - 数字。最多可以缓存多少组件实例。

    1.activated:页面第一次进入的时候,钩子触发的顺序是created->mounted->activated 2.deactivated :页面退出的时候会触发deactivated,当再次前进或者后退的时候只触发activated

    components的name有什么用,场景

    blog.csdn.net/weixin_3901…

    1. 当使用组件递归调用时,被递归调用的组件必须定义name属性,因为在组件里面调用自己时,不是使用的在components里注册的组件,而是使用根据name属性查找组件
    2. keep-alive包裹动态组件时,会缓存不活动的组件实例,会出现include和exclude属性,包含或者排除指定name组件
    3. 封装通用组件时,可以通过获取组件实例的name属性,定义为组件名字,方便管理
    4. vue-tools插件调试时没有name属性会报错或警告

    如何进行用户鉴权,设计动态路由

    juejin.cn/post/684490… addrouter的坑 blog.csdn.net/weixin_3417…

    Vue原理

    $nexttick内部实现

    computed和watch内部实现原理

    router的路由方式以及实现原理

    • hash - window.onhashchange
    • H5 history - history.pushState 和 window.onpopstate
    • H5 history 需要后端支持

    event-bus实现原理,自己设计一个on,on,on,emit

    性能优化

    1. 性能优化有哪些

    通用的性能优化:

    • 让加载更快:
      • 减少资源体积:代码压缩
      • 减少访问次数:合并代码,SSR服务器渲染,缓存(http缓存,本地缓存)
      • 使用更快的网络:CDN
    • 让渲染更快
      • CSS放在head,JS放在body最下面
      • 尽早执行JS,用DOMContentLoaded触发
      • 懒加载(图片懒加载,上滑加载更多,分页)
      • 对DOM查询进行缓存
      • 频繁DOM操作,合并到一起插入DOM结构
      • 防抖debounce和节流throttle

    Vue性能优化

    • 合理使用v-show和v-if
    • 合理使用computed和watch
    • v-for时加唯一key,避免和v-if同时使用
    • 自定义事件、DOM事件及时销毁
    • 合理使用异步组件
    • 合理使用keep-live
    • data层级不要太深
    • 使用vue-loader在开发环境做模板编译
    • 使用SSR

    WebPack性能优化

    www.yuque.com/docs/share/… 《Webpack和babel面试题》

    2. 防抖和节流的区别

    www.jianshu.com/p/c8b86b09d… zhuanlan.zhihu.com/p/72923073 segmentfault.com/a/119000001…

    防抖debounce

    对于短时间内连续触发的事件(如滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行一次。 所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间

    • 持续触发不执行
    • 不触发一段时间后再执行
    <input type="text" id="input1">
    
    const input1 = document.getElementById('input1')
    // 不能用箭头函数
    input1.addEventListener('keyup', debounce(function (e) {
        console.log(e.target)
        console.log(input1.value)
    }, 600))
    
    
    // 防抖
    function debounce(fn, delay = 500) {
        // timer 是闭包中的
        let timer = null
    
        return function () {
            if (timer) {
                clearTimeout(timer)
            }
            timer = setTimeout(() => {
                fn.apply(this, arguments)
                timer = null
            }, delay)
        }
    }
    

    节流throttle

    如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。 节流的意思是让函数有节制地执行,而不是毫无节制的触发一次就执行一次。什么叫有节制呢?就是在一段时间内,只执行一次。

    • 持续触发并不会执行多少次
    • 到一定时间再去执行
    <div id="div1" draggable="true">可拖拽<div>
      
    const div1 = document.getElementById('div1')
    
    div1.addEventListener('drag', throttle(function (e) {
        console.log(e.offsetX, e.offsetY)
    }))
    
    // 节流
    function throttle(fn, delay = 100) {
        let timer = null
    
        return function () {
            if (timer) {
                return
            }
            timer = setTimeout(() => {
                fn.apply(this, arguments)
                timer = null
            }, delay)
        }
    }
    

    应用实例

    resize、scroll、mousemove、输入框持续输入,将输入内容远程校验、多次触发点击事件

    工程化

    git rebase 和 git merge 有啥区别

    www.jianshu.com/p/4079284dd…

    手写题

    大写转小写小写转大写

    Array.prototype.map.call(str,a=>a.toUpperCase(a)==a?a.toLowerCase():a.toUpperCase()).join('');
    
    function upLower(str) {
    	return str.split('').map(item => {
    		console.log(item)
    		return item.toUpperCase() === item ? item.toLowerCase() : item.toUpperCase()
    	})
    }
    
    console.log(upLower('ABc'))
    

    算法题

    1. 找出数组中出现次数最多的数

    // 找出数组中出现次数最多的数
    const arr = [1, 2, 3, 4, 5, 2, 1]
    function fn(arr) {
    	let map = new Map()
    	let maxIndex = 0
    	for (let i = 0; i < arr.length; i++) {
    		if (!map.get(arr[i])) {
    			map.set(arr[i], 1)
    		} else {
    			map.set(arr[i], map.get(arr[i]) + 1)
    		}
    		maxIndex = Math.max(maxIndex, map.get(arr[i]))
    	}
    	let result = []
    	for (let [key, value] of map) {
    		if (value === maxIndex) {
    			result.push(key)
    		}
    	}
    	return result
    }
    
    console.log(fn(arr))
    
    • 前端跨域的方式
    • cors
    • 数据劫持
    • diff算法,虚拟DOM真实DOM、
    • html5、css3新特性
    • es6新特性
    • webpack常见loader
    • 原型链
    • es5实现继承、es6实现、继承、
    • 2个手写代码:
      • 深拷贝
      • URL字符串转换成对象
    • 原理:Vue框架的东西问的比较多
    • 赋值组件通信
    • 组件定义钩子鉴定,怎么执行
    • promise原理
    • vuex是什么
    • 小程序的页面通信
    • 小程序的性能优化(页面加载太慢怎么办)
    • 小程序的数据存储方式
    • 小程序直播
    • 小程序的双向绑定和vue的双向绑定区别
    • 小程序用户鉴权方式(如何判断用户是否登陆)
    • token,token解析,token的加密方式
    • 你会用uniapp吗
    • cookie和storage的区别
    • set是用来做什么的
    • Generator 函数
    • promise和await的区别,解决了什么问题
    • ajax是通过什么api实现的
    • Reflect
    • 数组的方法有哪些,some和every的区别
    • vuex刷新页面之后没有数据了怎么办
    • 跨域如果不看控制台,如何判断
    • 公司团队规模
    • 公司的业务
    • 前端技术后端技术
    • 如何说服上司进行项目重构
    • 讲一个你觉得做的不错的项目
    • 项目开发流程
    • 你的主要工作
    • 如何进行项目部署
    • 性能监控,数据分析有没有做过
    • 团队之间的代码管理和审查
    • git用的merge还是base
    • 20000s不用api换算成时分秒 求模 求余
    • 技术选型你有做过哪些工作
    • Eslint使用的规范
    • v-for如果没有加key的话会报错吗
    • 你有做过哪些组件的开发
    • 使用第三方库封装的组件,你是如何保证它的灵活性的,比如富文本的封装
    • 如果一个搜索组件,我需要两个或多个输入框,如果保证灵活性
    • 传入options,遍历options然后渲染,这里面会不会有一些坑
    • Vue3和Vue2有什么区别
    • 什么是重排和重绘
    • flex有哪些属性,子元素flex的三个属性都是什么意思
    • flex处理列表换行
    • css中有哪些属性可以继承
    • Es6常用的属性方法
    • let、const和var的区别
    • 箭头函数和普通函数的区别
    • this指向问题
    • 3月面试实录(未整理完)
    • 使用asyn/await如何进行错误处理
    • 宏任务和微任务的区别
    • 3月面试实录(未整理完)
    • 常用的生命周期分别做什么
    • addEventListenter和传统的事件监听的区别
    • v-for的key有什么作用,用index行不行
    • 虚拟dom是什么

    起源地下载网 » 3月面试实录(未整理完)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元