前端常见面试题

348 阅读32分钟

html,css基础知识

常见面试题

  1. 如何理解html语义化?

    代码结构清晰,可读性高,有利于搜索引擎优化和页面爬取

  2. 默认情况下,哪些html标签是块级元素,哪些是内联元素?

    块级元素:

    div,p,h1-h6,ul,ol,table
    

    内联元素:

    span,img,input,button vm vh
    
  3. 盒模型宽度计算

    offsetWidth = width + padding + border (当前对象的宽度)
      - offsetTop 当前对象到其上级层顶部的距离	
    clientWidth = width + padding
    scrollWidth 滚动条宽度
    
  4. BFC

    bfc: Block format context(块级格式化上下文)
    
    一个独立的渲染区域,内容元素的渲染不会影响边界以外的元素
    
    bfc内部区域的内容不管怎么渲染都不会影响区域外的内容
    
    形成bfc的常见条件:
    	float 不是 none
        display: absolute / fixed
    	overflow: 不是visible
        display: flex,inline-block
    
    常见应用:
      清除浮动  
    
  5. float布局

    • 圣杯布局
    1、三栏布局,中间部分最先加载,最重要
    2、左右两侧宽度固定,中间部分宽度变化
    3、使用float布局
    4、给大盒子添加左右padding给左右两侧留白,左右盒子添加margin负值
    
    • 双飞翼布局
    <div class="center i">
         <div class="c"></div>
    </div>
    <div class="left i"></div>
    <div class="right i"></div>
    
    1、使用margin给两侧留白
    2、使用float布局
    3、给左右元素添加margin负值
    
  6. 手写clear fix

    .clearfix:after {
        content: '';
        clear: both;
        display: table;
    }
    .clearfix {
        *zoom: 1; // 兼容IE低版本
    }
    
  7. flex布局

    flex-direction  justify-content  align-items
    flex-wrap       align-self
    
  8. 盒子水平垂直居中的五大方案

    最初使用的是定位的方式去实现,后来随着css3的兴起,使用了display:flex; 的方式,但是我在看掘金网站的时候看到使用 display: table-cell; 也可以实现,但是这种实现方式只针对行内元素

    * 定位的三种实现方式
    
    方案一:当前元素可以不设置具体的宽高
    position: absolute;
    top: 50%;
    left: 50%;
    margin-top: -50px;
    margin-left: -50px; 
    
    /* 方案二: 当前元素必须要设置具体的宽高 */
    /* position: absolute;
    left: 0px;
    top: 0px;
    bottom: 0px;
    right: 0px;
    margin: auto; */
    
    /* 方案三: */
    /* position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%); */
    
    
    方案四:给当前元素的父盒子添加
    使用 flex 布局
    
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center; 
    
    方案五:给当前元素的父盒子添加(该方式只针对行内元素,且父级元素必须具有固定宽高)
    
    display: table-cell;
    vertical-align: middle;
    text-align: center;
    
    当前元素:display: inline-block;
    
  9. 移动端响应式布局开发的三大方案

    • media (媒体查询)
    • rem
    • flex
    • vh/vw (百分比布局)
  10. 不考虑其它因素,下面哪种渲染性比较高?

.box a { /* 先查找所有a,然后再查找 box 下的 所有 a */ }
a {}

a {} 渲染性比较高,因为css的浏览器渲染机制是选择器从右向左查询
  1. 使用css,让一个div消失在视野中

    /*方案一:  */
    display:none;
    
    /*方案二: */
    opactity:0;
    
    /*方案三:*/
    负margin
    
  2. 对象和数组的区别

  3. Symbol

  4. Object.prototype.toString() / valueOf() 的区别

JS基础知识

原型和原型链

常见面试题

  1. typeof能判断哪些数据类型

    number,string,Symbol,boolean, function

  2. 何时使用 === 何时使用 ==

    除了判断 null 之外,其余判断数据或类型是否相等一律使用 ===

  3. 值类型与引用类型的区别

    值类型,存储在栈中,栈中的数据从上往下存储**(先进先出)**

    引用类型,存储在堆中(堆中的数据从下向上存储)先进后出

  4. 手写深拷贝

  5. 如何判断一个变量是不是数组

    a instanceof Array

  6. 手写建议JQuery

值类型与引用类型

值类型的值直接存储在栈中,各不相关
引用类型的值存储的是一个内存地址,这个内存地址指向于哪个对象(堆中)
这两种存储类型的原因?
- 根据内存空间和cpu耗时考虑规划出的解决方法
- 为了性能,一个json可能会很大,如果像值类型一样直接存储,复制,占内存,耗时
栈:从上往下存
堆:从下往上存

常见值类型

number,string,boolean,underfined,Symbol('xxx')

注:如果一个变量的值为underfined,那么只能使用 let 去定义,不能使用const去定义

注:Symbol 创建出来的值是唯一的

常见引用类型

对象,数组,null(特殊的引用类型,指针指向空地址),function(特殊引用类型,没有拷贝,复制函数这一说法)

typeof运算符

  1. 可以判断所有的值类型,识别函数

    // 值类型
    let a;                           typeof a ==> underfined (重要)
    const str = 'abc';               typeof str ==> string    
    const num = 123;                 typeof num ==> number  
    const b = true;                  typeof b ==> boolean
    const s = Symbol('s');           typeof s ==> Symbol 
    
    // 识别函数 (重要)
    typeof console.log  ==> function
    typeof function (){} ==> function
    
    // 引用数据类型
    typeof null ==> object  (重要)
    typeof {a:1} ==> object
    typeof [1,2] ==> object
    
手写深拷贝
let obj = {
    name: 'zs',
    age: 18,
    address: {
        city: 'beijing'
    },
    arr: [2,'a','b','c']
}

function deepClone(obj = {}) { // 传入一个对象,返回一个深拷贝的对象
    // 值类型,直接返回
    if(typeof obj !== 'object' || typeof obj == null) {
       return obj;
    }
    let res;
    if(res instanceof Array) {
       res = [];
    } else {
        res = {}
    }
    for(let key in obj) {
        res[key] = deepClone(obj[key])
    }
    return res;
}

console.log('深度克隆',deepClone(obj));

何时使用 === 何时使用 ==

除了 == null 之外,其它一律用 ===

instanceof

object是所有class的一个父类

instanceof运算符用来判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上

function Student() {}

let s = new Student(); // s,t 都属于Student的实例对象
let t = new Student();

s instanceof Student  => true
t instanceof Student  => true

[] instanceof Array => true
[] instanceof Object => true
{} instanceof Object => true

s.__proto__ === Student.prototype => true

原型关系

1、每个class(构造函数)都有显示原型 prototype
2、每个实例都有隐式原型 __proto__
3、实例的隐式原型  指向对应class 构造函数的prototype

class的原型本质

原型和原型链 👆

手写JQuery

class JQuery {
    constructor(selector) {
        let dom = document.querySelectorAll(selector);
        for(let i = 0; i< dom.length;i++) {
            this[i] = dom[i]
        }
        this.length = dom.length;
        this.selector = selector;
    }
    get(index) {
        return this[index]
    }
    each(fn) {
        for(let i = 0;i< this.length;i++) {
            return fn(this[i])
        }
    }
    on(type,fn) {
        this.each(elem => {
            return elem.addEventListener(type,fn,false);
        })
    }
}

// JQ插件机制
JQuery.prototype.dialog = function(con) {
    alert(con);
}

// 造轮子/重构/扩展自己的方法
class myJQuery extends JQuery {
    constructor(selector) {
        super(selector); // super指向父类的原型对象
    }
    addClass() {
        // 自己的方法
    }
}

组件化和模块化

组件:

组件最初的目的是代码重用,功能相对单一或者独立,被其它代码所依赖

组件化开发的原则是高度重用,低依赖

模块:

模块最初的目的是将同一类型的代码整合在一起,不同模块之间也会存在依赖关系

作用域闭包

对闭包的理解
闭包就是能够读取其它函数内部的变量
使用闭包的缺点
闭包会让函数中的变量都保存在内存中,内存消耗大,所以不能滥用闭包,可以在不使用该变量的时候将其删除

this的不同应用场景,如何取值?

手写bind函数

闭包在实际开发中的应用场景

自由变量:

一个变量在一个作用域没有定义却使用了,会向其上一层父元素逐层查找,找到找到为止,如果最后找不到会报错

闭包自由变量的查找:

自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方

闭包应用场景:

// 函数作为返回值
function create() {
    const a = 100
    return function () {
        console.log(a)
    }
}

const fn = create()
const a = 200
fn() // 100
// 函数作为参数被传递
function print(fn) {
    const a = 200
    fn()
}
const a = 100
function fn() {
    console.log(a)
}
print(fn) // 100
// 所有的自由变量的查找,是在函数定义的地方,向上级作用域查找
// 不是在执行的地方!!!

this的取值

this 的值是在函数执行时决定

和自由变量的查找规则完全相反

闭包的应用场景

手写bind

// fn.bind({a:1},1,'a',3);
// bind用于改变this的指向,并且返回一个函数

function fn1(a,b,c) {
    console.log('this',this); // {a:1}
    console.log(a,b,c);
    return `this is fn1`
}

const fn2 = fn1.bind({a:1},3,5,10);
// bind 返回的是一个函数,这个函数必须要手动去调用才能执行
const res = fn2();
console.log(res);
// 手写bind功能
Function.prototype.bind1 = function() {
    // 将 arguments 伪数组转化为数组
    let arr = [].slice.call(arguments);
    // 获取并删除数组中的第一项
    let first = arr.shift();
    let that = this; // 
    // bind 函数返回的是一个函数
    return function() {
        return that.apply(first,arr);
    }
}

function fn() {
    console.log(this);
}
let fn2 = fn.bind({a:1},1,2,3,4,5);
fn2();

手写promise加载图片

function loadImg(src) {
    return new Promise((resolve,reject) => {
        let img = document.createElement('img');
        img.onload = () => {
            resolve(src)
        }
        img.onerror = () => {
            let err = new Error(`图片加载错误${src}`)
            reject(err)
        }
        img.src = src;
    })
}

let url1 = `https://img.mukewang.com/5a9fc8070001a82402060220-140-1410.jpg`;
let url2 = `https://img.mukewang.com/5a9fc8070001a82402060220-140-140.jpg`;

loadImg(url1).then(res => {
    console.log('1',res);
    return res; // 普通对象,return的值👇 then中的res
}).then(res => {
    console.log('2',res);
    return loadImg(url2); // return 一个promise实例 下面的then 中的res接收到
}).then(res => {
    console.log('3',res);
}).catch(err => {
    console.log(err);
})

JS异步

对Promise的理解

Promise主要是用于解决回调地狱的问题,并且promise有三种状态 pending,resolved,rejected,
Promise支持异步    

promise的三种状态

pending: (在过程中,还没有结果)pending状态不会触发 then和catch
resolved: (成功了)resolved状态会触发后续的 then 回调
rejected: (失败了)rejected状态会触发后续的 catch 回调

pending -> resolved 
pending -> rejected
这种变化的状态是不可逆的

new Promise({}) 会立刻执行,而 .then 是需要异步的

then和catch改变状态

  • then 正常返回resolved,里面有报错则返回 rejected
  • catch 正常返回resolved,里面有报错返回rejected

async/await 和Promise的关系

1、执行async函数,返回的是Promise对象 2、await相当于Promise的then

async function async1() {
    console.log('async1 start'); // 2
    await async2();
    console.log('async1 end'); // 5 await 后面的都作为回调内容 -- 微任务
}

async function async2() {
    console.log('async2'); // 3
}

3、try ... catch 可捕获异常,代替了Promise的catch

new Promise(function(resolve) {
    console.log('promise1'); // new Promise(初始化promise) 回调函数中的代码会被立即执行
    resolve();
}).then(() => {
    console.log('promise2'); //       
})

同步异步的区别,及应用场景

同步:
	(同步会阻塞代码的执行)
	异步函数的执行结果会影响其下面的代码的执行,如 alert
异步:
	异步是基于JS是单线程语言
	(异步不会阻塞代码的执行)
	异步函数不会影响下面代码的执行,如定时器,ajax请求,promise

异步的本质

js是单线程的

for ... of 的应用场景

for of 是异步执行

forEach,for in是同步执行


function mulit(num) {
    return new Promise((resolve,reject) => {
        setTimeout(() => {
            resolve(num*num)
        },1000)
    })
}

let arr = [1,2,3];
// forEach是同步执行
arr.forEach( async i => {
    let res = await mulit(i);
    console.log('forEach',res); // 过了1s中 一下打印出 1,4,9
});


// forOf 是异步执行
!(async() => {
    for (const value of arr) {
       let res = await mulit(value);
       console.log('forOf',res);  // 第1s 打印出 1, 有过1s 打印出 4s,有过 1s 打印9
    }
})()

宏任务(macroTask)和微任务(microTask)

宏任务:(在DOM渲染后触发)setTimeout, setInterval, Ajax,Dom事件 微任务:(在DOM渲染前触发)Promise , async/await

微任务执行时机比宏任务要早

微任务比宏任务执行时机早的原因:

  • 微任务在DOM渲染前触发,宏任务在DOM渲染后触发
  • 微任务是ES6语法规定的,宏任务是浏览器规定的

Var, Let, Const 的区别

  • vares5 的语法,let,constes6 的语法,var 有变量提升
  • var 和 let 是变量,可修改; const是常量,不可修改
  • let , const 有块级作用域,var 没有

less与sass的区别

  1. 编译环境不同
    • less通过js编译,是在客户端处理(是一门css预处理语言)
    • sass通过ruby,是在服务器端处理(强化css的辅助工具)
  2. 变量符不一样
    • less通过@
    • sass通过$
  3. sass支持条件语句,可以使用if{}else{},for{}循环等等。而less不支持

http与https的区别

  1. 传输信息安全性不同
    • http协议:是超文本传输协议,信息是明文传输。如果攻击者截取web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息
    • https:具有安全性的ssl加密传输协议,为浏览器和服务器之间的通信加密,确保数据传输的安全
  2. 连接方式不同
    • http的连接很简单,是无状态的
    • https协议:是由ssl+http协议构成的可进行加密传输,身份认证的网络协议
  3. 端口不同
    • http协议使用的是80端口
    • https使用的是443端口
  4. 证书申请方式不同
    • http协议免费申请
    • https协议,一般需要缴费

运行环境

网页加载过程

  1. DNS解析:把域名变成IP地址
  2. 浏览器根据IP地址向服务器发起http请求
  3. 浏览器根据IP地址向服务器发起http请求
  4. http Headers
  5. ...

本地存储

Cookie

缺点:
  • 具有存储大小的限制,最大4KB
  • 每次发送http请求,cookie都会被发送到服务端,增加请求的数据量
  • 只能用 document.cookie = ....(有的话就追加,没有的话就覆盖) 来修改cookie的值,太过于简陋

LocalStorage , SessionStorage

  • 最大可存储 5M
  • 不会随着http请求被发送出去
  • localStorage 数据会永久存储,除非代码或手动删除
  • sessionStorage 数据只存在于当前会话,浏览器关闭则清空
  • 一般用 localStorage 会更多一些

http常见面试题

  • 描述一下http的缓存机制(重要)
  • http常见的状态码有哪些?
    • 1XX 服务器收到请求
    • 2XX 请求成功(如:200)
    • 3XX重定向(如:302临时重定向,301永久重定向)
    • 4XX 客户端错误(如:404)
    • 5XX 服务端错误(如:500)
  • http常见的header有哪些?
  • 什么是Restful API
    • 传统API设计:把每个URL当作一个功能
    • Restful API设计,把每个url当成一个唯一的资源

前端常用到的linux命令

  1. 从本地登录到线上机器

    ssh 用户名@线上机器IP地址或者机器的名称

    ssh root@192.168.10.1

  2. 查看文件和文件夹

    ls

    ls -a (可以将隐藏的文件显示出来)

    ll (以列表的形式显示)

  3. clear 清屏

  4. mkdir abc 创建abc文件夹

  5. rm -rf abc 删除abc文件夹,-rf 表示把文件夹中的所有内容全删除掉 -f 表示强制删除

  6. rm a.js 删除a.js文件

  7. rouch a.js 新建文件

  8. vi a.js 新建 a.js 并且打开

  9. vim a.js 打开 a.js 文件

  10. cat a.js 查看文件,并且可以打印出文件中的内容

  11. grep "search" a.js 在 a.js 文件中查找关键字 search

  12. cd abc 进入到 abc 目录

  13. mv index.html index1.html 将index.html 改为 index1.html

  14. mv abc ../ 将 abc文件夹移动到上一级目录

  15. cp a.js a1.js 拷贝 a.js 并未拷贝的文件重命名为 a1.js

http-server教程

全局安装 npm install http-server -g

如果报权限问题的错误,以管理员的身份运行cmd,然后 重新安装

启动 (在项目目录下) http-server -p 8001

vue面试题

  1. 讲一下对MVVM的理解

    MVVM是 model - view - viewModel 的缩写,也就是把MVC中的Controller演变成了ViewModel,
           model 代表数据层
           view 代表UIViewModelViewModel的桥梁,数据会绑定到vm上并自动将数据渲染到页面,视图变化的时候会通知vm层更新数据
    
  2. 简单说一下 vue 2.x 响应式数据的原理(数据双向绑定的原理)

    核心点:Object.defineProperty(),vue双向绑定使用数据劫持和发布订阅
    vue在初始化数据时,会使用Object.defineProperty()重新定义data中的所有属性,当页面使用对应的属性时,首先会进行依赖收集(收集当前组件的watcher) 如果属性发生变化会通知相关依赖进行更新操作(发布订阅)
    
    vue3.0 采用Proxy来实现数据劫持?
    Object.defineProperty() 是由局限性的,他拦截的target就是单纯的对象key的值
    所以,对象属性的删减,数组长度的变化,他就没法执行了
    
    而ES6是新特性,Proxy它可以拦截对象,数组,几乎一切对象包装类型
    但是Proxy没法兼容IE,所以Vue3.0底层还是采用Object.defineProperty
    
  3. vue是如何检测数组变化?

    • 使用函数劫持的方式,重写数组的方法
    • vue 将 data 中的数组,进行原型链重写,指向自己定义的数组原型方法,当调用数组原型上的方法时手动更新通知视图更新,如果数组中包含引用数据类型,会对数组中的引用数据类型进行再次监控
  4. 为何vue采用异步渲染?

    理解:vue是组件级更新,可能频繁更改数据,如果改几次数据更新几次组件,那么性能肯定不高,所以这时候采用异步更新,如果渲染数据对应的都是同一个watcher ,可以把相同的watcher过滤掉,防止一更改数据就更新视图

    防止一更改数据就重新渲染当前组件,为了性能考虑,vue会在本轮数据更新后,再去异步更新视图

  5. nextTick 实现原理?

    原理: nextTick 这个方法主要是使用了 宏任务与微任务 定义了一个异步方法,nextTick多次调用,会维持一个数组(既是将方法存入队列中),之后会异步把数组中的方法依次执行,这样用户就可以在视图更新之后,获取到真实的DOM元素

    作用: 一般来说,在对于MVVM框架结构的技术栈是不推荐操作DOM的,但是很多情况下可能会需要操作DOM,特别是一些charts插件等。

    所以nextTick就出现了,确保我们所操作的DOM是更新之后的

    最常用的操作就是 使用 nextTick 保证当前视图渲染完成

    **场景:**需要在视图更新之后,基于新的视图进行操作

    <template>
        <div @click="btn" ref="btns">{{msg}}</div>
    <template>
    export default {
      name: 'app',
      data() {
        return {
          dialog: false,
          msg: '旧数据'
        }
      },
    
      methods: {
        btn() {
          this.msg = '新洗心心心';
          // 直接打印出现的是旧数据,在nextTick里出现的是新数据
          console.log(this.$refs.btns.innerHTML);
          this.$nextTick(() => {
            console.log(this.$refs.btns.innerHTML);
          })
        }
      },
     } 
    
  6. computed, watch,methods的区别

    • computed 是依赖其它属性的计算值,并且有缓存,只有当依赖的值变化时才会更新
      • (适合在模板渲染中,某个值是依赖于其它的响应式对象甚至是计算属性计算而来的)
    • watch (没有缓存)适合监听某个值的变化去完成一段复杂的业务

    computed 和watch 的区别

    二者内部都是一个watcher

    computed

    • 具有缓存功能,只有依赖的数据发生改变,才会重新进行计算
    • 不支持异步

    watch

    • 不支持缓存,数据变,直接会触发相应的操作
    • 支持异步

    computed和methods

    ​ 计算属性是基于他们的响应式依赖进行缓存的,只有在依赖发生变化时,才会计算求值,而使用 methods,每次都会执行相应的方法。

  7. watch 中的 deep:true 是如何实现的?

    就是依赖递归,性能不高,尽量不要采用

    如果当前监控的值是数组类型。会对对象中的每一项进行求值,此时会将当前watcher存入到对应属性的依赖中,这样数组中对象发生变化时也会通知数据更新。

  8. vue组件生命周期

    • beforeCreated

      实例完全被创建出来之前执行,元素DOM和数据都还没有初始化
      beforeCreated 生命周期函数执行的时候,data和methods中的数据都还没有初始化
      
    • created

      data 和 methods 都已经被初始化好了
      但是DOM还未渲染(在这个生命周期,可以进行异步的请求,但是同步的操作可能会导致页面空白)
      
    • beforeMount

      (只是在内存中渲染好了模板,并没有把模板挂载到真正的页面中去)DOM未完成挂载,数据也初始化完成,页面上显示的还是  {{}} (模板字符串)
      
    • mounted

      将内存中编译好的模板渲染到浏览器的页面中去
      

    修改data中时触发下面的两个函数:

    • beforeUpdate

      页面上的数据是旧的,但是data中的数据是最新的,页面上的数据还没有和最新的数据保持同步

    • updated

      页面和data数据已经保持同步
      

    实例销毁阶段

    • beforeDestory

      实例上所有的data和methods,以及过滤器,指令都处于可销毁状态,但是还没有执行真正的销毁过程
      
    • destoryed

      执行到 destoryed 函数的时候,组件已经完全销毁,此时组件中所有的数据,方法,指令,过滤器都已经不可用了
      
  9. ajax请求放在哪个生命周期中

    • 在created的时候,视图中的dom并没有渲染出来,所以此时如果直接去操作dom节点,无法找到相关的元素
    • 在mounted中,由于此时DOM已经渲染出来,所以可以直接操作DOM节点

    一般情况下都放在 mounted 中,保证逻辑的统一性,因为生命周期是同步执行的,ajax 是异步执行的

    服务端渲染不支持mounted方法,所以在服务端渲染的情况下统一放到 created 中

  10. 何时需要使用 beforeDestory

  • 清除自己定义的定时器
  • 解绑自定义的dom事件,window.scroll,mousemove,
  • 如果当前页面中使用了 $on 方法,那需要在组件销毁前解绑
  1. vue中模板编译的原理

templete -> render

分为两个步骤:先将模板解析成AST(抽象语法树),然后使用AST生成渲染函数
由于静态节点不总是需要渲染,所以生成AST之后,生成渲染函数之前这个阶段,需要做一个优化操作:遍历一遍AST,给所有静态节点做一个标记,这样在虚拟DOM中更新节点时,如果发现这个节点有标记,就不会重新渲染它
  1. 将模板解析成 AST
  2. 遍历AST标记静态节点
  3. 使用AST生成渲染函数

这三部分内容在模板编译中分别抽象出三个模块实现各自的功能:解析器、优化器和代码生成器。

  1. vue中 v-ifv-show 的区别?

理解:

  • v-if 如果条件不成立不会渲染当前指令所在的dom元素,v-if是有惰性的
  • v-show 只是切换当前dom的显示与隐藏
  1. 为什么 v-forv-if 不能连用?

v-for 会比 v-if 的优先级高一些,如果连用的话,会把 v-if 给每个元素都添加一下,会造成性能的问题

如果实在想连用使用计算属性

  1. 用 vNode描述一个Dom结构
  • 虚拟节点就是用一个对象来描述真实的DOM元素
  • 数据变化是虚拟dom的变化
  1. 简述 vue 中 diff 算法的原理
  • 先同级比较,再比较子节点
  • 先判断一放有人子,一放没儿子的情况
  • 比较都有儿子的情况
  • 递归比较子节点
  1. v-for 中为什么要用 key

key 主要是用于做diff算法的

因为VUE组件高度复用,key可以标识组件的唯一性,可以高效的更新虚拟DOM

key的值尽量使用一个唯一标识,尽量不使用index作为key的值

  1. 描述组件渲染和更新过程
渲染组件时,会通过 **Vue.extend** 方法构建子组件的构造函数,并进行实例化,最终手动调用 `$mount()` 进行挂载。
更新组件时会进行 `patchVNode` 流程,核心就是diff算法
  1. 组件中的data为什么是一个函数?

为了防止 data 被共用,避免组件中的数据互相影响

理解: 同一个组件被复用多次,会创建多个实例。这些实例用的是同一个构造函数,如果data是一个对象的话,那么所有的组件都共享了同一个对象,为了保证组件数据的独立性,要求每个组件必须通过 data 函数返回一个对象,作为组件状态

  1. vue中事件绑定的原理?

  1. v-model的实现原理及如何自定义 v-model

  2. vuev-html 会导致哪些问题?

  • 可能会导致 xss 攻击
  • v-html 会替换掉标签内部的子元素
  1. vue父子组件生命周期调用顺序

理解:

  • 组件的调用顺序都是先父后子,渲染完成的顺序肯定是先子后父
  • 组件的销毁操作是先父后子,销毁完成的顺序是先子后父
  1. vue组件之间如何通信

vue是单向数据流

  1. 为什么要使用异步组件?
  • 如果组件功能比较多,打包出来的结果会大,我们采用异步的方式加载组件,主要依赖 @import() 这个语法,可以实现文件的分割加载
  1. 什么是作用域插槽?

插槽:

作用域插槽:

1597395652861

  1. vue-router有哪些组件?

<router-link><router-view><keep-alive>

  • router-link 粗俗的可以理解为H5中的a标签,用来跳转对应path中的路径,跳转路由用的
  • router-view 与router-link配合,渲染router-link 映射过来的组件
  • keep-alive 控制页面是否需要缓存 $route.meta.keepAlive 即 是否缓存 router-view
  1. routeroute和 router的区别是什么?
  • router为VueRouter的实例,是一个全局路由对象,包含了路由跳转的方法、钩子函数等
  • route 是路由信息对象||跳转的路由对象,每一个路由都会有一个route对象,是一个局部对象,包含path,params,hash,query,fullPath,matched,name等路由信息参数
  1. #####vue中常用的修饰符

  • v-model修饰符

    • .lazy

      输入框改变,这个数据就会改变,lazy这个修饰符会在光标离开input框才会更新数据
      <input type="text" v-model.lazy="value" />
      
    • .trim

      输入框过滤首尾的空格
      <input type="text" v-model.trim='value' />
      
    • .number

      先输入数字就会限制只能输入数字,先输入字符串就相当于没有加number
      <input type="text" v-model.number='value' />
      

其它面试题

浏览器常见的兼容性问题

  • input 组件的placeholder 在苹果收集透明度是0.2

  • 不同浏览器的标签默认margin和padding不同

    • 使用CSS通配符* *{ margin: 0; padding: 0;}
  • li空白间距(在IE下,会增加li和li之间的垂直间距 )

    • 给li里的a显式的添加宽度或者高度 li a{width:20px;} 或者 li {display:inline;} li a{display:block;}
  • new Date()构造函数使用,

    • ‘2019-12-09’是无法被各个浏览器中,使用new Date(str)来正确生成日期对象的。 正确的用法是’2019/12/09’
  • 图片默认有间隙

    • 给 img 标签添加左浮动 flot: left;
    • 给 img 标签添加 display:block
  • 上下margin重叠问题

    • 给上边元素设置了margin-bottom,给下边元素设置了margin-top,浏览器只会识别较大值;
    • 解决: margin-top和margin-bottom中选择一个,只设置其中一个值;
  • 省略号

    描述:这个是在越出长度后会自行的截掉多出部分的文字,并以省略号结尾

    但注意Firefox并不支持。

    .hh {
      -o-text-overflow:ellipsis;
      text-overflow:ellipsis;
      white-space:nowrap;
      overflow:hidden;
    }
    
  • CSS透明

    几款浏览器对透明度的支持方式各不相同,为了保证在IE, Firefox, Chrome, Safari等主流浏览器下都能正常显示透明度的效果,我们可以定义一个透明度的class,因为一写就要写3条,省的每次都复制来复制去了

    transparent{
       filter:alpha(opacity=60);  /*支持 IE 浏览器*/
       -moz-opacity:0.6; /*支持 FireFox 浏览器*/
       opacity:0.6;  /*支持 Chrome, Opera, Safari 等浏览器*/
    }
    
    

常见跨域的解决办法

  • JSONP

    JSONP的实现原理很简单,就是利用 `<script>` 没有跨域限制的漏洞。通过 `<script>` 标签指向一个需要访问的地址,并提供一个回调函数来接收数据当需要通讯时
    
    
    <script src="http://domain/api?param1=a&param2=b&callback=jsonp"></script>
    <script>
        function jsonp(data) {
        	console.log(data)
    	}
    </script>
    
    JSONP 使用简单且兼容性不错,但是只限于 get 请求。
    只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题
    
    在开发中可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就需要自己封装一个 JSONP,以下是简单实现
    
    function jsonp(url, jsonpCallback, success) {
      let script = document.createElement('script')
      script.src = url
      script.async = true
      script.type = 'text/javascript'
      window[jsonpCallback] = function(data) {
        success && success(data)
      }
      document.body.appendChild(script)
    }
    jsonp('http://xxx', 'callback', function(value) {
      console.log(value)
    })
    
  • CORS Cross-Origin Resource Sharing 跨域资源共享

    • 定义了必须在访问跨域资源时,浏览器和服务器应该如何沟通

      跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。
      
      使用方法:php后端设置 Access-Control-Allow-Origin
  • nginx代理跨域

    nginx 反向代理接口跨域
    

TS常见面试题

  1. TS中interface与class的区别?

    • interface 只声明成员方法并不做实现

      • 也就是说:interface只是定义了这个接口会有什么,但是没有告诉你具体是什么

      • interface Point {
            lng:number;
            lat:number;
            sayPosition():void;
        }
        只是定义了一个数据类型和函数,但具体内容没有定义,需要你自己的子类去实现
        
    • class类 声明并实现

      • class Point {
            constructor(lng,lat){
                this.lng = lng;
                this.lat = lat;
            }
            sayPosition(){
                console.log('point:',this.lng,this.lat);
            }
        }
        
  2. x

React常见面试题

  1. React中keys的作用

    在开发过程中,我们需要保证某个元素的key在同级元素中具有唯一性。
    在React Diff 算法中React会借助key判断当前元素是新创建的还是被移动过来的元素,从而减少不必要的元素重渲染。
    
  2. 调用setState之后发生了什么

    在代码中调用setState函数之后,React会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程,
    经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面。在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
    
  3. React生命周期函数

    • 初始化阶段

      • defaultProps 设置默认的props,也可以用 defaultProps设置组件的默认属性
    • componentWillMount()

      • 组件初始化时调用,以后组件更新不调用,整个生命周期只调用一次,此时可以修改state
      • 组件将要被挂载到页面上去state中的数据和methods都可以获取到并且能调用一般不用
    • render

      • 将组件上的html标签都渲染到页面上去
      • 创建虚拟dom,进行diff算法,更新dom树都在此进行。此时就不能更改state了
    • componentDidMount

      • 组件已经挂载到页面 ,可以进行ajax请求
    • componentWillReceiveProps

      • 当前子组件的属性被父组件修改时会触发
    • shouldComponentUpdate(nextProps,nextState) react接收新的state和props时调用

      ​ 返回值是 true/false, false不更新组件

      • react性能优化非常重要的一个环节。
        组件接收新的 state 或 props时调用,我们可以设置在此对比前后两个props和state是否相同,如果相同则返回 false组织更新,
        (因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,节省大量性能,尤其是在dom结构复杂的时候)
        
      • shouldComponentUpdate这个方法用来判断是否需要调用render方法重新描绘dom.因为 dom 的描绘非常消耗性能,如果我们能在 shouldComponentUpdate 方法中能够写出更优化的 dom diff 算法,可以极大的提高性能。
        
    • componentWillUpdate(nextProps,nextState)

      组件将要更新时调用,此时可以修改state(更新之前调用)
      
    • render

      • 组件渲染
    • componentDidUpdate

      • 组件修改完成
    • componentWillUnmount()

      • 组件被卸载的时候触发

    1597637644210

  4. 为什么虚拟DOM会提高性能

    虚拟dom相当于在JS和真实DOM中间加了一个缓存,利用 dom diff算法避免了没有必要的dom操作,从而提高性能。
    用JS对象结构表示DOM树的结构,然后用这个树构建一个真正的DOM树,插到文档当中,当状态变更的时候,重新构造一颗新的对象树。然后用新旧树进行比较,记录两棵树的差异,把所记录的差异更新到所构建的真正的DOM树上,视图就更新了。
    
  5. React diff 原理

    • 把树形结构按照层级分解,只比较同级元素
    • 给列表结构的每个单元添加唯一的key属性,方便比较
    • React 只会匹配相同 class 的 component(这里面的 class 指的是组件的名字)
    • 合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.
    • 选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能
  6. React中refs的作用

    RefsReact 提供给我们的,安全访问dom元素或者某个组件实例的句柄
    我们可以为元素添加 ref 属性然后在回调函数中接受该元素在 DOM 树中的句柄,该值会作为回调函数的第一个参数返回
    class CustomForm extends Component {
      handleSubmit = () => {
        console.log("Input Value: ", this.input.value)
      }
      render () {
        return (
          <form onSubmit={this.handleSubmit}>
            <input
              type='text'
              ref={(input) => this.input = input} />
            <button type='submit'>Submit</button>
          </form>
        )
      }
    }
    
  7. 类组件和函数式组件之间的区别

    • 构造函数创建出来的组件,叫做 无状态组件
    • 用 class关键字 创建出来的组件,叫做 有状态组件
    • 组件中的 props 和 state/data 之间的区别
      • props中的数据是只读的,不能重新赋值
      • state/data中的数据,都是可读可写的
      • props中的数据,都是外界传递过来的
      • state/data中的数据,都是组件私有的(通过Ajax获取回来的数据,一般都是私有数据)
    • 什么情况下使用有状态组件,什么情况下使用无状态组件?
      • React官方说:无状态组件,由于没有自己的state和生命周期函数,所以运行效率会比有状态组件稍微高一些

    有状态组件和无状态组件的本质区别

    有无state属性 有无生命周期函数

  8. JSX语法

    就是符合xml规范的JS语法(xml语法格式相对来说要比html严谨很多)

  9. extendssuper 介绍

    如果一个子类通过extends关键字继承父类,那么在子类的constructor构造函数中,必须优先调用一下super()
    
    super() 是一个函数,而且他是父类的构造器,子类的super其实就是父类中 constructor 构造器的一个引用
    
  10. react-redux将组件分为两大类 =》UI组件和容器组件

juejin.cn/post/684516…

  1. 受控组件和非受控组件

    受控组件:

    在HTML中标签 input textarea select 标签值改变,通常是根据用户输入进行更新

    在React中,可变状态通常保存在组件的状态属性中,并且只能使用 setState() 更新,而呈现表单的React组件也控制着在后续用户输入时该表单中发生的情况

    由React控制的输入表单元素而改变其值的方式,称为:“受控组件”。

    非受控组件:

    表单数据由DOM本身处理。即不受setState()的控制,与传统的HTML表单输入相似,input输入值即显示最新值(使用 ref从DOM获取表单值)

  2. 高阶组件(higher order component)

    高阶组件是一个以组件为参数并返回一个新组件的函数。HOC 运行你重用代码、逻辑和引导抽象。最常见的可能是 Redux 的 connect 函数。除了简单分享工具库和简单的组合,HOC 最好的方式是共享 React 组件之间的行为。如果你发现你在不同的地方写了大量代码来做同一件事时,就应该考虑将代码重构为可重用的 HOC。

  3. redux

    • store 仓库,存储数据**(图书管理员,存储当前藏书的状态)**
    • reducer 修改老的state和action,返回一个新的 state (借书或者还书的行为,通过reducer计算改变图书馆藏书的状态)
    • action 更新store中的数据

    获取store中的数据:

    this.props.store.getState();

    调用action中的方法:

    store.dispatch(action);

  4. react-redux

    react-redux 将所有的组件分为两大类,UI组件,容器组件

    react-redux 向外暴露两个APIProvider组件类 和 connect 函数
    
    Provider组件接收store属性,让所有组件都可以看到store,从而通过 store读取/更新状态
    connect函数:
    	接收两个参数mapStateToProps 和 mapDispatchToProps
        connect 执行的返回值为一个高阶组件,包装UI组件,返回一个新的容器组件,容器组件会向UI组件传入前面指定的一般/函数类型属性
    https://juejin.cn/post/6845166890776543239
    
  5. redux-thunk

    用来实现redux异步的中间件插件(默认redux是无法进行异步操作的)

    // store.js
    import {createStore,applyMiddleware} from 'redux';
    import reducer from './reducer';
    
    // redux-thunk只有一个函数 thunk
    // 用来实现redux异步的中间件插件
    import thunk from 'redux-thunk';
    
    export default createStore(reducer,applyMiddleware(thunk));
    

其它面试题

  1. 重置elementUi默认样式

    • 在样式外新增一个样式不添加scoped
    <style>
    	.my .el-input__inner{
    		border-radius: 15px;/* 这个样式起效果 */
    	}
    </style>
    
    • 使用deep样式穿透

      <style scoped>
      	.my .el-input__inner{
      		border-radius: 30px;/* 这个不起作用 */
      	}
      	.my /deep/ .el-input__inner{
      		border-radius: 30px;/* 这个起作用 */
      	}
      </style> 
      
    • 使用>>>穿透

      <style scoped>
      	.my .el-input__inner{
      		border-radius: 30px;/* 这个不起作用 */
      	}
      	.my >>> .el-input__inner{
      		border-radius: 30px;/* 这些起作用 */
      		border: 1px solid #eceef2;
      		outline: 0;
      	}
      </style>
      
  2. 后台管理系统怎么做到用户权限不同

    • 定义一个静态文件,存储侧边栏的 id,title,url
    • 用户登录之后会返回当前用户菜单数据
  3. 前端通信方式?

    • Ajax
    • WebSocket(不受同源策略限制)
    • CORS
  4. web前端性能优化常见面试题

    • 减少资源体积:压缩代码

    • 减少访问次数:合并代码,SSR服务器渲染(将网页和数据一起加载一起渲染),缓存

    • 懒加载(图片懒加载)

    • 使用cdn(主要是针对静态文件)

    • 减少http请求,合理浏览器缓存

    • 合并 CSS图片,使用精灵图片

    • **CSS放在页面最上部,javascript放在页面最下面:**让浏览器尽快下载CSS渲染页面

    • 用innerHTML代替DOM操作,减少DOM操作次数

  5. 性能优化的原则

    性能优化的目的是提高网页的加载速度

    • 多使用内存,缓存或其它方法
    • 减少CPU计算量,减少网络加载耗时
    • (适用于所有编程的性能优化,------ 空间换时间)
  6. 盒模型常见的面试题(box-sizing)

    标准盒模型和IE盒模型

    标准盒模型:box-sizing: content-box; 指的是内容的宽高,并不是盒子的宽高

    **盒子的宽高 = content + border + padding **

    IE盒模型(指的是盒子的宽高):box-sizing:border-box;

    IE盒模型 = content + padding + border

    • border-box 为元素指定的任何内边距和边框都将在已设定的宽度和高度内进行绘制
    • content-box 在宽度和高度之外绘制元素的内边距和边框
    • inherit 规定应从父元素继承 box-sizing 属性的值
  7. css 如何画一条直线

  8. Hash模式和History模式的区别?

    juejin.cn/post/684490…

    vue默认使用的hash

    • hash
      • hash 发生变化的url都会被浏览器记录下来,浏览器的前进后退都可以用了
      • hash 虽然出现在url中,但不会被包括在http请求中,改变hash不会重新加载页面
      • hash模式依靠的是 onhashchange() 事件,去监听 localtion.hash 的改变
    • history
      • 利用了 html5 History Interface 中新增的 pushState()和replaceState() 方法
      • history.pushState() 来完成URL跳转而无需重新加载页面
  9. 常见的web前端攻击方式有哪些?

    • XSS 跨站请求攻击

      • 在网页中嵌入一段js代码,获取访问者的cookie信息,并发送到指定的服务器上

      • XSS预防
        • 替换特殊字符,如 < 变成 &lt; > 变成 &gt;
        • <script> 变成 &lt;script&gt; 直接显示,而不会作为脚本执行
        • 提示:前端要替换后端也要替换,都做总不会有错
    • XSRF 跨站请求伪造

      • 案例

        1、你正在购物,看中一个商品,商品id 100
        2、付款接口 xxx.com/pay?id=100,但是没有任何验证
        3、我是攻击者看中了一个商品,商品 id=200 
        4、我想让你去帮我付款
        5、我向你发送一封邮件,然后这个邮件正文隐藏着 <img src="xxx.com/pay?id=100" />
        6、你一查看邮件,就帮我购买了 id 是 200 的商品    
        
      • XSRF预防

        • 使用post接口,(post接口跨域需要server端支持)
        • 增加验证,例如密码,短信验证码,指纹等
  10. 数组的 pop,push,unshift,shift

  • pop 把数组的最后一个元素删除掉,返回当前删掉的元素,会对原数组造成影响
  • shift 把数组第一个元素删掉,返回当前删掉的元素,会对原数组造成影响
  • push 向数组追加元素,返回数组的长度,会对原数组造成影响
  • unshift 向数组的开头插入元素,返回数组的长度,会对原数组造成影响

typeof 能判断哪些类型

值类型和函数
string,number,Symbol,boolean,function, underfined
判断引用类型 typeof {} ==> object, typeof [] ==> object, typeof null ==> object

何时使用 === 何时使用 ==

判断 null 使用 == ,其余情况都是用 ===

值类型引用类型的区别

值类型:存储的值,存储在栈中
引用类型: 存储的是地址,存储在堆中(引用数据类型数据量比较大,如果像值类型一样存储在栈中,会比较消耗性能)

手写深拷贝(深度克隆)

let obj = {
    name: 'zs',
    age: 20,
    address: {
        city: 'shanghai'
    }
}

function deepClone(obj) {
    if(typeof obj == null || typeof !== object) { // 值类型
       return obj;
    } 
    // 引用数据类型
    let result;
    if(instanceof obj === Array) {
       result = []
    } else if(instanceof obj === Object) {
       result = {}       
    }
    for(let key in obj) {
        result[key] = deepClone(obj[key]);
    }
    return result;
}
let res = deepClone(obj);

如何判断一个变量是不是数组

instanceof

手写一个简单的jquery,考虑插件和可扩展性

class myJQuery {
    constructor() {
        
    }
}

插件是直接在原型上添加功能函数
可扩展性:extendes继承,super指向父类的原型对象

class的原型本质,怎么理解?

其实就是原型与原型链之间的关系
1、每个class都有一个显式原型 prototype
2、每个class类的实例都有一个隐式原型 __proto__
3、实例的隐士原型 __proto__ === class的显式原型

this的不同应用场景,如何取值?

1、普通函数,this指向window
2、构造函数中,this指向当前的实例对象
3this的指向是在函数调用的时候决定的,不是在定义的时候决定的

手写bind函数实际开发中闭包的应用场景

function.prototype.myBind = function(){
    let arr = [].slice.call(arguments); // 将伪数组转换为真正的数据
    let first = arr.shift(); // 截取数组中的第一个,即this的指向
    let that = this;
    return function {
        return that.apply(first,arr);
    }
}

function test() {
    console.log(this); // {a:1}
}
let fn = test.myBind({a:1},1,2,4,7);
fn();

异步同步的区别

手写promise加载图片

VUE与React的区别

juejin.cn/post/684490…

  • 监听数据变化的实现原理不同

    • Vue 通过 getter/setter 以及一些函数的劫持,能精确知道数据变化,不需要特别的优化就能达到很好的性能
    • React 默认是通过比较引用的方式进行的,如果不优化(PureComponent/shouldComponentUpdate)可能导致大量不必要的VDOM的重新渲染
    为什么 React 不精确监听数据变化呢?
    这是因为 VueReact 设计理念上的区别,Vue 使用的是可变数据,而React更强调数据的不可变。所以应该说没有好坏之分,Vue更加简单,而React构建大型应用的时候更加鲁棒。
    
  • 数据流不同

    • vue默认是支持双向绑定的,而react从诞生之初就不支持双向绑定,React一直提倡的是单向数据流它称之为onChange/setState()模式
  • 模板渲染方式的不同

  • 在表层上, 模板的语法不同**(但其实这只是表面现象,毕竟React并不必须依赖JSX)**
    • React 是通过JSX渲染模板
    • 而Vue是通过一种拓展的HTML语法进行渲染

Uni-app开发遇到的bug

  • scroll-view 无法横向排列的问题

    • scroll-view 的子元素的 class 设置为 display:inline-block;
  • 自定义tabbar

    • uni-app的tabbar最多支持5个页面,即使是自定义的,需要动态改变的tabbar,加起来也不能超过5个,需要在page.json文件进行设置
    • 自定义tabbar里最好使用 cover-view 和 cover-image 来创建视图
  • border为1rpx时在各个机型上的适配问题

    • 给要添加border的元素添加一个伪元素,并设置width,height,(200%)border-width:2rpx,再使用 transform:scale(0.5)
  • 页面滚动问题

    "disableScroll": true, //true禁止滚动 false可以滚动

  • 自定义tabbar相关问题

    按官方介绍 可以用cover-view来自定义tabbar,但是cover-view设置
    
    position: fixed;
    bottom: 0;
    
    在iPhone上加载分页数据时会出现cover-view上移的bug, 目前方案为改cover-view为view,层级设置高一点, 如:
    z-index: 999
    

webpack

Loader与Plugin的区别?

Loader
Loader本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。
因为webpack只认识JS,所以Loader就成了翻译官,对其它类型的资源进行转译的预处理工作

微信小程序登录功能

less和sass的区别