2022前端面试学习笔记

目录

做了一份前端面试复习计划,保熟~ - 掘金

1、MVVM和MVC的区别

2、JS中的基础类型(6种+2种ES6新增)

3、JS中==和===区别

4、JS中的深拷贝浅拷贝区别

5、原型、构造函数和实例的理解

6、什么是闭包   闭包解决了什么问题  闭包导致了什么问题

7、call、apply、bind实现

8、如何理解JS中的this关键词(3点)

9、跨域问题 (5点+1实际经验)

10、宏任务  微任务

11、!important>style>id>class

12、p和div都是块级元素  区别是什么 (3点)

13、盒子模型

14、清除浮动方式  区别 (4种)

14-1、BFC(创建BFC 3种)

14-2、左侧固定+右侧自适应布局方法(5种)

15、重绘  回流   区别

15-1、防抖  节流

16、vue store 存储 dispatch 和 commit的区别

17、水平垂直居中(5点)

18、flex布局  flex:1;(3点)

19、响应式布局(6点)

20、输入url到页面显示  过程 (6步)

20-1、http状态码

20-2 DNS系统

21、web前端优化策略 (6点)

22、package.json文件作用  内容

23、webpack(裂了)

24、es6(10点)

24-1 var let const  区别(3点)

24-2 使用箭头函数注意点(3点)

24-3 模板字符串

24-4 forEach、for in、for of 区别

25、快速排序

25-1、数组扁平化 

26、get post区别 (5点)

27、cookie优缺点

28、new关键字执行过程(原型链)

29、判断数组类型(4种)

29-1、数组方法

29-1、数组去重

30、babel(没看完)

31、vue组件通讯

32、父子组件生命周期执行顺序

33、v-if 和 v-show 的区别

33-1、display:none、visibility:hidden 和 opacity:0 之间的区别?

34、Vue2.0 响应式数据的原理

35、vue-router 路由钩子函数是什么 执行顺序是什么

36、vue-router 动态路由是什么 有什么问题

36-1、vue-router 组件复用导致路由参数失效怎么办?

37、谈一下对 vuex 的个人理解

38、你都做过哪些 Vue 的性能优化

39、keep-alive 使用场景和原理

Vue.set 方法原理

Vue 修饰符有哪些

鼠标按钮修饰符

键盘修饰符

v-bind修饰符

40、Vue 模板编译原理

41、Vue双向绑定理解

42、首屏加载

43、为什么data属性是一个函数而不是一个对象

44、Vue实例挂载的过程

45、vue.nextTick()

46、mixin  混入

47、Observable

48、diff操作

49、TS

Vue+Vue3.0

50、async  defer

51、Promise.all

52、类数组转数组

53、动画属性

54、DOCTYPE有什么作用?标准模式与混杂模式如何区分?它们有何意义?

55、常见的浏览器内核有哪些?

56、HTML5的文件离线储存怎么使用,工作原理是什么

57、websocket

58、JS继承(6种)

59、watch跟computed区别

60、手写axios

61、小程序

62、IE浏览器兼容问题处理

63、gitlab-ci

64、vue原理题

65、回调地狱怎么处理

66、H5新增

67、块状/内联元素

68、css能继承的属性

69、值类型和引用类型区别

70、class的类型实际是函数、本质是null

71、promise

算法

正则了解

shell了解


做了一份前端面试复习计划,保熟~ - 掘金

1、MVVM和MVC的区别

MVVM,Model-View-ViewModel,即模型-视图-视图模型。

【模型】指后端传递的数据, 【视图】指所看到的的页面。

【视图模型】是MVVM的核心,分为两个方向:1、【模型】->【视图】=》数据绑定 2、【视图】->【模型】=》DOM事件监听。两个方向都实现:双向绑定。总结:【视图】和【模型】之间不能直接通信,需要通过viewModel实现。

MVC,Model-View-controller,即模型-视图-控制器。

MVVM框架:VUE,在vue中:Model指的是js中的数据,比如对象、数组等。View指的是页面视图。viewModel指的是vue实例化对象。

从图中可以看出,当执行 new Vue() 时,Vue 就进入了初始化阶段,一方面Vue 会遍历 data 选项中的属性,并用 Object.defineProperty 将它们转为 getter/setter,实现数据变化监听功能;另一方面,Vue 的指令编译器Compile 对元素节点的指令进行解析,初始化视图,并订阅Watcher 来更新视图, 此时Wather 会将自己添加到消息订阅器中(Dep),初始化完毕。当数据发生变化时,Observer 中的 setter 方法被触发,setter 会立即调用Dep.notify(),Dep 开始遍历所有的订阅者,并调用订阅者的 update 方法,订阅者收到通知后对视图进行相应的更新。因为VUE使用Object.defineProperty方法来做数据绑定,而这个方法又无法通过兼容性处理,所以Vue 不支持 IE8 以及更低版本浏览器。另外,查看vue原代码,发现在vue初始化实例时, 有一个proxy代理方法,它的作用就是遍历data中的属性,把它代理到vm的实例上,这也就是我们可以这样调用属性:vm.aaa等于vm.data.aaa。

50行代码的MVVM,感受闭包的艺术 - 掘金

2、JS中的基础类型(6种+2种ES6新增)

ES5中,6种基础类型:Number,String,Boolan,undefined,Null,Symbol ,BigInt。

ES6中,新增一种Symbol 。这种类型的对象永不相等,即始创建的时候传入相同的值,可以解决属性名冲突的问题,做为标记。新增BigInt 可以表示任意大小的整数。

3、JS中==和===区别

==相同,===严格相同。

4、JS中的深拷贝浅拷贝区别

深拷贝实现方法有两种:

1)JSON.stringify/parse

2)利用递归来实现每一层都重新创建对象并赋值

function deepClone(source){
  const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
  for(let keys in source){ // 遍历目标
    if(source.hasOwnProperty(keys)){
      if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象,就递归一下
        targetObj[keys] = source[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepClone(source[keys]);
      }else{ // 如果不是,就直接赋值
        targetObj[keys] = source[keys];
      }
    } 
  }
  return targetObj;
}
const deepCopy = function (src) {

    if (src instanceof Array) {

        return src.map(e => deepCopy(e));

    } else if (src instanceof Object) {

        const target = {};

        Object.keys(src).forEach(key => {

            if (src[key] instanceof Date) {

                target[key] = new Date(src[key].getTime());

            } else if (src[key] instanceof Object) {

                target[key] = deepCopy(src[key]);

            } else {

                target[key] = src[key];

            }

        });

        return target;

    }

    return src;

};

5、原型、构造函数和实例的理解

function Person() {

}
// 虽然写在注释里,但是你要注意:
// prototype是函数才会有的属性
Person.prototype.name = 'Kevin';
var person1 = new Person();
var person2 = new Person();
console.log(person1.name) // Kevin
console.log(person2.name) // Kevin

https://github.com/mqyqingfeng/blog/issues/2

6、什么是闭包   闭包解决了什么问题  闭包导致了什么问题

根据MDN中文的定义,闭包的定义如下:

在JavaScript中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。可以在一个内层函数中访问到其外层函数的作用域。

也可以这样说:

闭包是指那些能够访问自由变量的函数。自由变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量。闭包=函数+函数能够访问的自由变量

闭包就是能够读取其他函数内部变量的一个函数。

简单的说闭包是解决了函数内变量暴露给函数外访问。
就是为了解决突破函数作用域的限制才有了闭包这种概念。

闭包可能导致内存泄露,即函数执行完成后内部变量引闭包存在原因还可访问

例:

function print(fn) {

        const a = 200;

         fn();

}

const a = 100;

function fn() {

        console.log(a);

}

print(fn); //100

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

7、call、apply、bind实现

call

call()方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法。

var obj = {

        value: "123".

};

function fn() {

        console.log(this.value);

}

fn.call(obj); //123

通过call方法我们做到了以下两点:

1)call改变了this的指向,指向到obj。

2)fn函数执行了。

自己写call方法:

var obj = {

        value: "123",

        fn: function () {

                console.log(this.value);

        },

};

obj.fn(); //123

这时候 this 就指向了 obj ,但是这样做我们手动给 obj 增加了一个 fn 属性,这显然是不行的,不用担心,我们执行完再使用对象属性的删除方法(delete)不就行了

obj.fn = fn;

obj.fn();

delete obj.fn;

根据这个思路,我们可以写出来:

var obj = {

        value: "vortesnail",

      };

function fn(t) {

        console.log(this.value);

        console.log(t)                        

      }

      Function.prototype.myCall = function (context) {

        console.log(111,context);

        var obj2 = {

          value: "test"

        }

        // 判断调用对象

        if (typeof this !== "function") {

          throw new Error("Type error");

        }

        // 首先获取参数

        console.log(333,[...arguments]);

        let args = [...arguments].slice(1);

        console.log(222,args);

        let result = null;

        // 判断 context 是否传入,如果没有传就设置为 window

        context = context || window;

        // 将被调用的方法设置为 context 的属性

        // this 即为我们要调用的方法

        console.log(444, this);

        context.fn = this;

        console.log(555, this);

        // 执行要被调用的方法

        result = context.fn(...args);

        // 删除手动增加的属性方法

        delete context.fn;

        // 将执行结果返回

        return result;

      };

      fn.myCall(obj,"test");   

        //  vortesnail  

        // test

call、apply、bind传参格式不同

 

obj.myFun.call(db,'成都','上海');     // 德玛 年龄 99  来自 成都去往上海
obj.myFun.apply(db,['成都','上海']);      // 德玛 年龄 99  来自 成都去往上海  
obj.myFun.bind(db,'成都','上海')();       // 德玛 年龄 99  来自 成都去往上海
obj.myFun.bind(db,['成都','上海'])();   // 德玛 年龄 99  来自 成都, 上海去往 undefined

8、如何理解JS中的this关键词(3点)

  1. this 就是你 call 一个函数时,传入的 context。(结合7刚开始例子理解)
  2. 如果你的函数调用形式不是 call 形式,请按照「转换代码」将其转换为 call 形式(函数的context就是调用函数的对象本身,如果没有对象则是window,结合第1条)。
  3. 箭头函数里面没有 this 的定义。

9、跨域问题 (5点+1实际经验)

所谓同源是指,域名,协议,端口均相同,只要有一个不同,就是跨域。

1)前端方法就用jsonp  只支持get请求  不支持post请求

为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

原理:动态创建一个script标签。利用script标签的src属性不受同源策略限制,因为所有的src属性和href属性都不受同源策略的限制,可以请求第三方服务器资源内容

2)CORS response 添加 header,可以用工具Charles   或者代码里加

  //*表示支持所有网站访问,也可以额外配置相应网站
  resp.setHeader("Access-Control-Allow-Origin", "*");

        使用抓包工具Charles等,修改header,可进行跨域调试。

3)nginx反向代理转发

4)vue项目中proxy代理

5)跨文档通信 API:window.postMessage(‘message内容’,'域名')。 window.addEventListener('message', function(e))监听

// 父窗口打开一个子窗口
var openWindow = window.open('http://test2.com', 'title');
 
// 父窗口向子窗口发消息(第一个参数代表发送的内容,第二个参数代表接收消息窗口的url)
openWindow.postMessage('Nice to meet you!', 'http://test2.com');

// 监听 message 消息
window.addEventListener('message', function (e) {
  console.log(e.source); // e.source 发送消息的窗口
  console.log(e.origin); // e.origin 消息发向的网址
  console.log(e.data);   // e.data   发送的消息
},false);

6)之前pdf处理过,把插件传到了内网,解决跨域问题

10、宏任务  微任务

  • 宏任务:DOM 渲染后触发,如 setTimeoutsetIntervalDOM 事件script
  • 微任务:DOM 渲染前触发,如 Promise.thenMutationObserver 、Node 环境下的 process.nextTick

微任务在DOM渲染前触发,宏任务在DOM渲染后触发

同步任务->微任务->宏任务

11、!important>style>id>class

!important > 行内 > id选择器 > 属性选择器 = 类选择器 = 伪元素选择器 > 标签选择器 = 伪类选择器(::after)

12、p和div都是块级元素  区别是什么 (3点)

        1、标签不同

        2、p有行间距

        3、页面框架布局使用div,文章内容段落用p

13、盒子模型

CSS3 中的盒模型有以下两种:标准盒模型IE(替代)盒模型

两种盒子模型都是由 content + padding + border + margin 构成,其大小都是由 content + padding + border 决定的,但是盒子内容宽/高度(即 width/height)的计算范围根据盒模型的不同会有所不同:

标准盒模型的宽度:content;

IE(替代)盒模型:content+padding+border;

可以通过 box-sizing 来改变元素的盒模型:

box-sizing: content-box :标准盒模型(默认值)。

box-sizing: border-box :IE(替代)盒模型。

14、清除浮动方式  区别 (4种)

1)添加带clear:both;属性的空元素;

2)容器添加overflow:hidden;通过触发BFC方式,实现清除浮动;

3)容器添加浮动;

4):after伪元素;

.clearfix:after{/*伪元素是行内元素 正常浏览器清除浮动方法*/
        content: "";
        display: block;
        height: 0;
        clear:both;
        visibility: hidden;
    }
    .clearfix{
        *zoom: 1;/*ie6清除浮动的方式 *号只有IE6-IE7执行,其他浏览器不执行*/
    }
 
<body>
    <div class="fahter clearfix">
        <div class="big">big</div>
        <div class="small">small</div>
        <!--<div class="clear">额外标签法</div>-->
    </div>
    <div class="footer"></div>
</body>

14-1、BFC(创建BFC 3种)

块级格式上下文,它是 CSS 视觉渲染的一部分,用于决定块级盒的布局及浮动相互影响范围的一个区域。

创建 BFC 的方式:

  • 绝对定位元素(position 为 absolute 或 fixed )。
  • 行内块元素,即 display 为 inline-block (inline-block,table-cell,table-caption,table,fle)。
  • overflow 的值不为 visible (scroll,hidden,auto,inherit)。

14-2、左侧固定+右侧自适应布局方法(5种)

1)左侧宽度固定+float:left;右侧div  width自动填充;

2)左侧宽度固定+float:left;右侧overflow:hidden;触发BFC,不会重叠;

3)flex布局:左侧宽度固定,右侧flex:1;

4)绝对定位:父容器position:relative;左侧固定宽度+position:absolute;右侧margin-left:左侧宽度;

5)绝对定位:父容器position:relative;左侧固定宽度;右侧position:absolute;+left:左侧宽度;

15、重绘  回流   区别

回流这一阶段主要是计算节点的位置和几何信息

最终,我们通过构造渲染树和回流阶段,我们知道了哪些节点是可见的,以及可见节点的样式和具体的几何信息(位置、大小),那么我们就可以将渲染树的每个节点都转换为屏幕上的实际像素,这个阶段就叫做重绘节点。

15-1、防抖  节流

1)防抖

JavaScript 专题之跟着 underscore 学防抖

防抖的原理就是:你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要等你触发完事件 n 秒内不再触发事件,我才执行,真是任性呐!

1、immediate 是否立即执行一次

2、clearTimeout  若setTimeout未执行,则阻止。timeout不会变空,是执行次数。

function debounce(func, wait, immediate) {
  let timeout;
  console.log(timeout); //第一次执行这里,后面不执行了
  return function () {
    console.log(2, timeout);  //第二次开始 只执行这里,上面不执行了
    let context = this;
    let args = arguments;

    if (timeout) clearTimeout(timeout);
    if (immediate) {
      let callNow = !timeout;
      //在这里重新开始计时,期间只要触发了就重新开始计时
      timeout = setTimeout(function () {
        //等到wait过后,把timeout变成null,再次触发debounce时会立即执行func
        timeout = null;
      }, wait);
      if (callNow) func.apply(context, args);
    } else {
      timeout = setTimeout(function () {
        func.apply(context, args);
      }, wait);
    }
  };
}

 2)节流

JavaScript 专题之跟着 underscore 学节流

节流的原理很简单:

如果你持续触发事件,每隔一段时间,只执行一次事件。

关于节流的实现,有两种主流的实现方式,一种是使用时间戳,一种是设置定时器。

function throttle(func, wait) {
    var timeout, context, args, result;
    var previous = 0;

    var later = function() {
        previous = +new Date();
        timeout = null;
        func.apply(context, args)
    };

    var throttled = function() {
        var now = +new Date();
        //下次触发 func 剩余的时间
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
         // 如果没有剩余的时间了或者你改了系统时间
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            func.apply(context, args);
        } else if (!timeout) {
            timeout = setTimeout(later, remaining);
        }
    };
    return throttled;
}

16、vue store 存储 dispatch 和 commit的区别

dispatch:含有异步操作。写在action内,action可以直接执行异步。

commit:同步操作。写在mutations内,mutations只能执行同步任务。

17、水平垂直居中(5点)

1)子元素positon:absolute;left:50%;top:50%;transform: translate(-50%, -50%);

2)利用绝对定位,子元素所有方向都为 0 ,将 margin  设置为 auto ,由于宽高固定,对应方向实现平分,该方法必须盒子有宽高

position: absolute; top: 0; left: 0; right: 0; bottom: 0px; margin: auto; height: 100px; width: 100px;

3)利用绝对定位,设置 left: 50% 和 top: 50% 现将子元素左上角移到父元素中心位置,然后再通过 margin-left  和 margin-top  以子元素自己的一半宽高进行负值赋值。该方法必须定宽高

position: absolute; left: 50%; top: 50%; width: 200px; height: 200px; margin-left: -100px; margin-top: -100px;

4)flex:display: flex; justify-content: center; align-items: center;

5)grid布局;

18、flex布局  flex:1;(3点)

  • flex-grow: 1 :该属性默认为 0 ,如果存在剩余空间,元素也不放大。设置为 1  代表会放大。
  • flex-shrink: 1 :该属性默认为 1 ,如果空间不足,元素缩小。
  • flex-basis: 0% :该属性定义在分配多余空间之前,元素占据的主轴空间。浏览器就是根据这个属性来计算是否有多余空间的。默认值为 auto ,即项目本身大小。设置为 0%  之后,因为有 flex-grow  和 flex-shrink  的设置会自动放大或缩小。在做两栏布局时,如果右边的自适应元素 flex-basis  设为 auto  的话,其本身大小将会是 0 。​

19、响应式布局(6点)

1)媒体查询  @media screen and (max-width: 320px)

2)百分比布局

3)rem

4)视口单位  vw,vh,vmin(vw和vh中的较小值),vmax(vw和vh中的较大值)

5)图片响应式:使用max-width(图片自适应)、使用srcset、使用background-image

6)Flex弹性布局、grid网格布局、Columns栅格系统   以及以上结合使用。

20、输入url到页面显示  过程 (6步)

1)域名解析

        输入 URL 后解析出协议、主机、端口、路径等信息,并构造一个 HTTP 请求。

        DNS 域名解析

2)TCP三次握手

        由 TCP 的自身特点可靠传输决定的。客户端和服务端要进行可靠传输,那么就需要确认双方的接收和发送能力。第一次握手可以确认客户端的发送能力,第二次握手,确认了服务端的发送能力和接收能力,所以第三次握手才可以确认客户端的接收能力。不然容易出现丢包的现象。

3)http 请求

4)服务器接收到请求后并返回 HTTP 报文

5)浏览器得到HTTP报文后,开始渲染页面

6)断开TCP链接

20-1、http状态码

1)1XX信息性状态码

2)200成功

3)302重定向  304客户的缓存资源是最新的,要客户使用缓存

4)400发送了错误请求  401没权限  404找不到资源

5)500服务器内部错误 502网关故障 504网关超时

20-2 DNS系统

        DNS 即域名系统,全称是 Domain Name System。当我们在浏览器输入一个 URL 地址时,浏览器要向这个 URL 的主机名对应的服务器发送请求,就得知道服务器的 IP,对于浏览器来说,DNS 的作用就是将主机名转换成 IP 地址

21、web前端优化策略 (6点)

1)减少http请求

2)减少DNS查找

3)CSS放顶部,JS放最底

4)JS、CSS、图片、第三方库 cdn 引入

5)图片压缩

6)webpack:terser-webpack-plugin压缩 JS 代码

                        css-minimizer-webpack-plugin压缩 CSS 代码

                        html-webpack-plugin压缩 HTML 代码

                        compression-webpack-plugin开启gzip压缩

22、package.json文件作用  内容

package.json 文件其实就是对项目或者模块包的描述,里面包含许多元信息。比如项目名称,项目版本,项目执行入口文件,项目贡献者等等。npm install 命令会根据这个文件下载所有依赖模块。

npm init  可自动创建,也可手动创建

23、webpack(裂了)

1、webpack作用

        1)模块打包

        2)变异兼容

        3)能力扩展(按需加载、代码压缩等)

24、es6(10点)

        1)let、const  块级作用域

        2)箭头函数

        3)import  export

        4)Set结构  类似数组  数据唯一  没有重复值

        5)Map结构  类似数组  数据是键值对

        6)...展开运算符

        7)async、await
  使用 async/await, 搭配promise,可以通过编写形似同步的代码来处理异步流程, 提高代码的简洁性和可读性
  async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成

        8)Promise  异步编程

        9)Symbol

        10)解构赋值

        11)for  of结构

        let arr = [11,22,33,44,55];
        let sum = 0;
        for(value of arr){
            sum += value;
        }

24-1 var let const  区别(3点)

        1)var是全局,let、const是块级作用域

        2)var可以在声明的上面访问变量

        3)const声明之后必须赋值,不可改变

24-2 使用箭头函数注意点(3点)

        1)箭头函数里面没有 this 对象,

              此时的 this 是外层的 this 对象,即 Window

        2)没有argument对象

        3)不能用作构造函数,所以代表着不能使用new命令

24-3 模板字符串

        基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定

24-4 forEach、for in、for of 区别

        1)forEach  专门遍历数组   

                数组遍历:arr.forEach((item, index) => { arr[index] == item })

                对象遍历:Object.keys(obj).forEach(key = > { obj[key] })

        2)for in 一般循环对象或json  循环出的key

        3)for of  对象数组都可,循环出value

  • forEach 和 map 的区别;
    • map 不写 return,默认返回 undefined,返回一个数组长度的 undefined 的数组;
    • forEach 中断循环
      • 用数组的方法,return false;
      • 数组的 api,使用 some 或者 every 实现
      • 使用 for 循环或者 for in 代替
      • 使用 throw 抛出异常
  • 遍历对象的方式
    • for in:主要用来遍历对象(for keys in obj)
      • 遍历自身和继承的可枚举属性(延续原型链遍历出对象的原型属性)
      • 有什么问题:要使用 hasOwnProperty 判断,只处理自身的,不处理继承的
    • for of: ES6 新增的,遍历所有数据结构的统一的方法
      • 只要部署了 Symbol.iterator 属性,就被视为具有 iterator 接口,就可以用 for…of 循环遍历它的成员
      • 包括数组、Set 和 Map 结构、某些类似数组的对象(比如 arguments 对象、DOM NodeList 对象)、Generator 对象,以及字符串
    • Object.keys:返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)
    • Object.getOwnPropertyNames(obj):返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)
    • Object.getOwnPropertySymbols()
    • Reflect.ownKeys(obj)遍历:返回一个数组,包含对象自身的所有属性,不管属性名是 Symbol 或字符串,也不管是否可枚举

25、快速排序

function sortArray(nums) {
  quickSort(0, nums.length - 1, nums);
  return nums;
}

function quickSort(start, end, arr) {
  if (start < end) {
    const mid = sort(start, end, arr);
    quickSort(start, mid - 1, arr);
    quickSort(mid + 1, end, arr);
  }
}

function sort(start, end, arr) {
  const base = arr[start];
  let left = start;
  let right = end;
  while (left !== right) {
    while (arr[right] >= base && right > left) {
      right--;
    }
    arr[left] = arr[right];
    while (arr[left] <= base && right > left) {
      left++;
    }
    arr[right] = arr[left];
  }
  arr[left] = base;
  return left;
}

25-1、数组扁平化 

1).flat()方法  arr.flat(Infinity);

Array.prototype.flat = function() {
	return this.toString()		//"1,2,3,4"
			.split(",")   		//["1", "2", "3", "4"]
			.map(item => +item);//[1, 2, 3, 4]
};
console.log([1,[2,[3,4]]].flat())//[1, 2, 3, 4]

2)...扩展运算符+.concat()连接

function flat(arr, depth = 1) {
  if (depth > 0) {
    // 以下代码还可以简化,不过为了可读性,还是....
    return arr.reduce((pre, cur) => {
      return pre.concat(Array.isArray(cur) ? flat(cur, depth - 1) : cur);
    }, []);
  }
  return arr.slice();
}

 arr.reduce(func)        累加器

26、get post区别 (5点)

  • 缓存的角度,GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会。
  • 编码的角度,GET 只能进行 URL 编码,只能接收 ASCII 字符,而 POST 没有限制。
  • 参数的角度,GET 一般放在 URL 中,因此不安全,POST 放在请求体中,更适合传输敏感信息。
  • 幂等性的角度,GET 是幂等的,而 POST 不是。(幂等表示执行相同的操作,结果也是相同的)
  • TCP 的角度,GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。(火狐浏览器除外,它的 POST 请求只发一个 TCP 包)

上面所说的post会比get多一个tcp包其实不太严谨。多发的那个expect 100 continue header报文,是由客户端对httppostget的请求策略决定的,目的是为了避免浪费资源,如带宽,数据传输消耗的时间等等。所以客户端会在发送header的时候添加expect 100去探探路,如果失败了就不用继续发送data,从而减少了资源的浪费。所以是否再发送一个包取决于客户端的实现策略,和get/post并没什么关系。有的客户端比如fireFox就只发送一个包。

27、cookie优缺点

缺点

        1)浏览器cookie个数限制   IE6 20个,IE7、fireFox  50个,chrome、safari无限制

        2)cookie大小4M

        3)安全性不够

        4)每次随http请求发送,造成不必要负担

优点

        1)数据持久

        2)简单易控

28、new关键字执行过程(原型链)

1)首先创建一个空对象。

2)根据原型链,设置空对象的_proto_为构造函数的prototype。

3)构造函数的this指向了这个对象,并执行了构造函数代码。

4)判断函数的返回值类型,如果是引用类型,就返回这个引用类型的对象。

29、判断数组类型(4种)

1)Array.isArray(arr); // true

2)arr.__proto__ === Array.prototype; // true

3)arr instanceof Array; // true

4)Object.prototype.toString.call(arr); // "[object Array]"

29-1、数组方法

会改变原来数组的:
pop()—删除数组的最后一个元素并返回删除的元素。
push()—向数组的末尾添加一个或更多元素,并返回新的长度。
shift()—删除并返回数组的第一个元素。
unshift()—向数组的开头添加一个或更多元素,并返回新的长度。
reverse()—反转数组的元素顺序。
sort()—对数组的元素进行排序。
splice()—用于插入、删除或替换数组的元素。 返回的是含有被删除的数组
································································································
不会改变原来数组的:
concat()—连接两个或更多的数组,并返回结果。
every()—检测数组元素的每个元素是否都符合条件。
some()—检测数组元素中是否有元素符合指定条件。
filter()—检测数组元素,并返回符合条件所有元素的数组。
indexOf()—搜索数组中的元素,并返回它所在的位置。
join()—把数组的所有元素放入一个字符串。
toString()—把数组转换为字符串,并返回结果。
lastIndexOf()—返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。
map()—通过指定函数处理数组的每个元素,并返回处理后的数组。
slice()—选取数组的的一部分,并返回一个新数组。
valueOf()—返回数组对象的原始值。

29-1、数组去重

1)ES6 Set去重

let arr = [1,0,0,2,9,8,3,1];
          function unique(arr) {
                return Array.from(new Set(arr))
          }
          console.log(unique(arr));   // [1,0,2,9,8,3]  
or
      console.log(...new Set(arr)); // [1,0,2,9,8,3]

 2)双重for循环,再利用数组的splice方法去重  ES5常用

var arr = [1, 5, 6, 0, 7, 3, 0, 5, 9,5,5];
             function unique(arr) {
                    for (var i = 0, len = arr.length; i < len; i++) {
                        for (var j = i + 1, len = arr.length; j < len; j++) {
                            if (arr[i] === arr[j]) {
                                arr.splice(j, 1);
                                j--;        // 每删除一个数j的值就减1
                            }
                        }                        return arr;
                }
                console.log(unique(arr));       //  1, 5, 6, 0, 7, 3, 9

3)利用数组的sort排序方法去重(相邻元素对比法)

var arr =  [5,7,1,8,1,8,3,4,9,7];
                function unique( arr ){
                    arr = arr.sort();
                    console.log(arr);

                    var arr1 = [arr[0]];
                    for(var i=1,len=arr.length;i<len;i++){
                        if(arr[i] !== arr[i-1]){
                            arr1.push(arr[i]);
                        }
                    }
                    return arr1;
                }
                console.log(unique(arr))l;   //  1, 1, 3, 4, 5, 7, 7, 8, 8, 9

4)利用数组的filter方法去重 

var arr = [1,2,8,9,5,8,4,0,4];
                /*
                模拟: 原始数组:[1,2,8,9,5,8,4,0,4]
                   索引值:0,1,2,3,4,5,6,7,8
                   伪新数组:[1,2,8,9,5,8,4,0,4]
                   使用indexOf方法找到数组中的元素在元素在中第一次出现的索引值
                      索引值:0,1,2,3,4,2,6,7,6
                    返回前后索引值相同的元素:
                    新数组:[1,2,8,9,5,4,0]
                */
          function unique( arr ){
       // 如果新数组的当前元素的索引值 == 该元素在原始数组中的第一个索引,则返回当前元素
                return arr.filter(function(item,index){
                  return arr.indexOf(item,0) === index;
                });
             }
      console.log(unique(arr));    //  1, 2, 8, 9, 5, 4, 0

30、babel(没看完)

不容错过的 Babel7 知识 - 掘金

前端工程师需要了解的 Babel 知识 - 政采云前端团队

31、vue组件通讯

1)props  $emit

2)$refs

3)Vuex

4)$parents   $children

5)provide  inject

6)$attrs 和$listeners A->B->C。Vue 2.4 开始提供了$attrs 和$listeners 来解决这个问题

7)eventBus 兄弟组件数据传递 这种情况下可以使用事件总线的方式

32、父子组件生命周期执行顺序

1)加载渲染过程

父 beforeCreate->父 created->父 beforeMount->子 beforeCreate->子 created->子 beforeMount->子 mounted->父 mounted

2)子组件更新过程

父 beforeUpdate->子 beforeUpdate->子 updated->父 updated

3)父组件更新过程

父 beforeUpdate->父 updated

4)销毁过程

父 beforeDestroy->子 beforeDestroy->子 destroyed->父 destroyed

33、v-if 和 v-show 的区别

v-if 在编译过程中会被转化成三元表达式,条件不满足时不渲染此节点。

v-show 会被编译成指令,条件不满足时控制样式将对应节点隐藏 (display:none)

使用场景

v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景

v-show 适用于需要非常频繁切换条件的场景

33-1、display:none、visibility:hidden 和 opacity:0 之间的区别?

34、Vue2.0 响应式数据的原理

整体思路是数据劫持+观察者模式

对象内部通过 defineReactive 方法,使用 Object.defineProperty 将属性进行劫持(只会劫持已经存在的属性),数组则是通过重写数组方法来实现。当页面使用对应属性时,每个属性都拥有自己的 dep 属性,存放他所依赖的 watcher(依赖收集),当属性变化后会通知自己对应的 watcher 去更新(派发更新)。

相关代码如下

class Observer {
  // 观测值
  constructor(value) {
    this.walk(value);
  }
  walk(data) {
    // 对象上的所有属性依次进行观测
    let keys = Object.keys(data);
    for (let i = 0; i < keys.length; i++) {
      let key = keys[i];
      let value = data[key];
      defineReactive(data, key, value);
    }
  }
}
// Object.defineProperty数据劫持核心 兼容性在ie9以及以上
function defineReactive(data, key, value) {
  observe(value); // 递归关键
  // --如果value还是一个对象会继续走一遍odefineReactive 层层遍历一直到value不是对象才停止
  //   思考?如果Vue数据嵌套层级过深 >>性能会受影响
  Object.defineProperty(data, key, {
    get() {
      console.log("获取值");

      //需要做依赖收集过程 这里代码没写出来
      return value;
    },
    set(newValue) {
      if (newValue === value) return;
      console.log("设置值");
      //需要做派发更新过程 这里代码没写出来
      value = newValue;
    },
  });
}
export function observe(value) {
  // 如果传过来的是对象或者数组 进行属性劫持
  if (
    Object.prototype.toString.call(value) === "[object Object]" ||
    Array.isArray(value)
  ) {
    return new Observer(value);
  }
}

35、vue-router 路由钩子函数是什么 执行顺序是什么

路由钩子的执行流程, 钩子函数种类有:全局守卫、路由守卫、组件守卫

完整的导航解析流程:

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter。
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter。
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

36、vue-router 动态路由是什么 有什么问题

动态路由就是有动态变量的,例:

const User = {
  template: "<div>User</div>",
};

const router = new VueRouter({
  routes: [
    // 动态路径参数 以冒号开头
    { path: "/user/:id", component: User },
  ],
});

36-1、vue-router 组件复用导致路由参数失效怎么办?

1.通过 watch 监听路由参数再发请求

watch: { //通过watch来监听路由变化

 "$route": function(){
 this.getData(this.$route.params.xxx);
 }
}

2.用 :key 来阻止“复用”

<router-view :key="$route.fullPath" />

37、谈一下对 vuex 的个人理解

主要包括以下几个模块:

  • State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
  • Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
  • Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
  • Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
  • Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。

38、你都做过哪些 Vue 的性能优化

  • 图片懒加载
  • 路由懒加载
  • 第三方插件的按需引入
  • 防抖、节流运用
  • 对象层级不要过深,否则性能就会差
  • 不需要响应式的数据不要放到 data 中(可以用 Object.freeze() 冻结数据)
  • v-if 和 v-show 区分使用场景
  • computed 和 watch 区分使用场景
  • v-for 遍历必须加 key,key 最好是 id 值,且避免同时使用 v-if
  • 大数据列表和表格性能优化-虚拟列表/虚拟表格
  • 防止内部泄漏,组件销毁后把全局变量和事件销毁
  • 适当采用 keep-alive 缓存组件
  • 服务端渲染 SSR or 预渲染

39、keep-alive 使用场景和原理

keep-alive 是 Vue 内置的一个组件,可以实现组件缓存,当组件切换时不会对当前组件进行卸载。

  • 常用的两个属性 include/exclude,允许组件有条件的进行缓存。

  • 两个生命周期 activated/deactivated,用来得知当前组件是否处于活跃状态。

  • keep-alive 的中还运用了 LRU(最近最少使用) 算法,选择最近最久未使用的组件予以淘汰。

Vue.set 方法原理

了解 Vue 响应式原理的同学都知道在两种情况下修改数据 Vue 是不会触发视图更新的

1.在实例创建之后添加新的属性到实例上(给响应式对象新增属性)

2.直接更改数组下标来修改数组的值

Vue 修饰符有哪些

  • 表单修饰符

        lazy

        在我们填完信息,光标离开标签的时候,才会将值赋予给value,也就是在change事件之后再进行信息同步

        trim

        number

  • 事件修饰符

        stop        阻止了事件冒泡,相当于调用了event.stopPropagation方法

        prevent        阻止了事件的默认行为,相当于调用了event.preventDefault方法

        self        只当在 event.target 是当前元素自身时触发处理函数,阻止事件冒泡

        once        绑定了事件以后只能触发一次,第二次就不会触发

        capture        使事件触发从包含这个元素的顶层开始往下触发

        passive

        在移动端,当我们在监听元素滚动事件的时候,会一直触发onscroll事件会让我们的网页变卡,因此我们使用这个修饰符的时候,相当于给onscroll事件整了一个.lazy修饰符

        native        让组件变成像html内置标签那样监听根元素的原生事件,否则组件上使用 v-on 只会监听自定义事件

  • 鼠标按钮修饰符

        left 左键点击

        right 右键点击

        middle 中键点击

  • 键盘修饰符

键盘修饰符是用来修饰键盘事件(onkeyuponkeydown)的,有如下:

keyCode存在很多,但vue为我们提供了别名,分为以下两种:

  • 普通键(enter、tab、delete、space、esc、up...)

  • 系统修饰键(ctrl、alt、meta、shift...)

<input type="text" @keyup.keyCode="shout()">

  • v-bind修饰符

async        能对props进行一个双向绑定

prop        设置自定义标签属性,避免暴露数据,防止污染HTML结构

camel        将命名变为驼峰命名法,如将view-Box属性名转换为 viewBox

40、Vue 模板编译原理

Vue 的编译过程就是将 template 转化为 render 函数的过程 分为以下三步

第一步是将 模板字符串 转换成 element ASTs(解析器)
第二步是对 AST 进行静态节点标记,主要用来做虚拟DOM的渲染优化(优化器)
第三步是 使用 element ASTs 生成 render 函数代码字符串(代码生成器)

41、Vue双向绑定理解

1)联系MVVM框架,ViewModel的作用:

数据变化后更新视图,视图变化后更新数据。

2)ViewModel由两部分组成

监视器(observer):对所有数据属性进行监听;

解析器(compiler):对每个元素节点的指令进行扫描跟解析,根据指令模板替换数据,以及绑定相应的更新函数

3)Vue为例,Vue双向绑定流程

        1、new Vue()执行初始化,对data进行响应化处理,发生在observer中

        2、同时对模板进行编译(AST),找到其中动态绑定的数据,从data中获取并初始化视图,这个过程发生在compiler中。

        3、同时定义一个更新函数和watcher,将来对应数据变化时,触发watcher调用更新函数。

        4、由于data的某个key在⼀个视图中可能出现多次,所以每个key都需要⼀个管家Dep来管理多个Watcher。

   5、将来data中数据⼀旦发生变化,会首先找到对应的Dep,通知所有Watcher执行更新函数

42、首屏加载

首屏时间(First Contentful Paint),指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容

1)加载慢的原因

  • 网络延时问题

  • 资源文件体积是否过大

  • 资源是否重复发送请求去加载了

  • 加载脚本的时候,渲染内容堵塞了

2)解决方案

  • 减小入口文件体积

  • 静态资源本地缓存

  • UI框架按需加载

  • 图片资源的压缩

  • 组件重复打包

  • 开启GZip压缩

  • 使用SSR

43、为什么data属性是一个函数而不是一个对象

组件必须用data(),因为使用data:{},会导致重复使用组件是,内存地址重复,公用同一个内存地址。函数时不会出现该问题。

44、Vue实例挂载的过程

  • vue构造函数 -> 调用多个init方法(定义 _init,定义$set、$get、$delete、$watch等,定义事件 $on、$once、$off、$emit,定义_update,定义_render返回虚拟dom)
  • beforeCreate阶段,不能访问data,methods,props,computed,watch
  • create阶段,可以访问data,methods,props,computed,watch,访问不到dom
  • mounted阶段,挂载完毕,数据双向绑定,可以访问到dom
  • 调用$mount进行页面的挂载

  • 挂载的时候主要是通过mountComponent方法

  • 定义updateComponent更新函数

  • 执行render生成虚拟DOM

  • _update将虚拟DOM生成真实DOM结构,并且渲染到页面中

45、vue.nextTick()

用于获取数据变化后,最新的dom。

46、mixin  混入

47、Observable

在非父子组件通信时,可以使用通常的bus或者使用vuex,但是实现的功能不是太复杂,而使用上面两个又有点繁琐。这时,observable就是一个很好的选择

创建一个js文件

// 引入vue
import Vue from 'vue
// 创建state对象,使用observable让state对象可响应
export let state = Vue.observable({
  name: '张三',
  'age': 38
})
// 创建对应的方法
export let mutations = {
  changeName(name) {
    state.name = name
  },
  setAge(age) {
    state.age = age
  }
}

 在.vue文件中直接使用即可

<template>
  <div>
    姓名:{{ name }}
    年龄:{{ age }}
    <button @click="changeName('李四')">改变姓名</button>
    <button @click="setAge(18)">改变年龄</button>
  </div>
</template>
import { state, mutations } from '@/store
export default {
  // 在计算属性中拿到值
  computed: {
    name() {
      return state.name
    },
    age() {
      return state.age
    }
  },
  // 调用mutations里面的方法,更新数据
  methods: {
    changeName: mutations.changeName,
    setAge: mutations.setAge
  }
}

48、diff操作

diff 算法是一种通过同层的树节点进行比较的高效算法。

v-for加key,key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。

49、TS

课程指南 | Vue3+TS 快速上手

1)interface 接口  属性?:表示可有可无   readonly  只读

2)implements  类实现接口。extends  接口继承接口  类继承类

3)派生类  super.父方法  public、private(私有)、protected(派生类中可以访问)

        存取器   get/set 属性

4)static修饰的为静态属性/方法。不能用this.xxx使用。类名.静态属性/方法调用和赋值。构造函数不能用static修饰。

5)abstract  抽象类/方法。

6)函数,函数类型固定,返回值固定。函数声明时,可选参数:参数?表示可传可不传;参数也可以给默认值。剩余参数:...参数名。

7)泛型,any 表示不预先指定具体类型。函数泛型:函数名<T, K>。泛型类,泛型接口,泛型约束。

Vue+Vue3.0

1)setup: 组合API的入口函数;ref: 定义一个数据的响应式,ref对象.value,对象是ref类型; reactive: 多个数据的响应式,对象是Proxy类型;  总结:想操作数据影响界面更新,需改动ref对象或reactive对象(代理对象),目标对象数据也会随之变化。

2)vue为解决1、对象新增或删除属性时,界面不更新2.通过数组下边替换元素或更新长度,界面不更新问题,用vue.set解决;vue3  响应式原理核心:proxy(代理)。proxy(target, handler)。target 目标对象。handler  处理器对象  监视数据变化,其中有 get() set()  delete()等13种方法。reflect(反射): 动态对被代理对象的相应属性进行特定的操作。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Proxy 与 Reflect</title>
</head>
<body>
  <script>
    
    const user = {
      name: "John",
      age: 12
    };

    /* 
    proxyUser是代理对象, user是被代理对象
    后面所有的操作都是通过代理对象来操作被代理对象内部属性
    */
    const proxyUser = new Proxy(user, {

      get(target, prop) {
        console.log('劫持get()', prop)
        return Reflect.get(target, prop)
      },

      set(target, prop, val) {
        console.log('劫持set()', prop, val)
        return Reflect.set(target, prop, val); // (2)
      },

      deleteProperty (target, prop) {
        console.log('劫持delete属性', prop)
        return Reflect.deleteProperty(target, prop)
      }
    });
    // 读取属性值
    console.log(proxyUser===user)
    console.log(proxyUser.name, proxyUser.age)
    // 设置属性值
    proxyUser.name = 'bob'
    proxyUser.age = 13
    console.log(user)
    // 添加属性
    proxyUser.sex = '男'
    console.log(user)
    // 删除属性
    delete proxyUser.sex
    console.log(user)
  </script>
</body>
</html>

3)setup细节:

        1、setup在beforeCreate之前执行,组件对象还没被创建,也就意味着组件实例对象this不能用。 

        2、setup(props, context)

                props参数,是一个对象,里面有父向子传的数据。

                context里有三个属性{attrs, emit, slots}

                attrs对象:获取当前子组件标签上的所有属性,但是该属性是没在props声明接受的对象,相当于this.$attrs。

                emit:用来分发自定义事件的函数,相当于this.$emit。

4)reactive和ref细节

        1、ref用来处理基本类型,如果ref一个对象或者数组,内部自动转换为reactive代理对象。

        2、reactive用来处理对象(递归深度响应式)。

        3、ref内部: 通过给value属性添加getter/setter来实现对数据的劫持

        4、reactive内部: 通过使用Proxy来实现对对象内部所有数据的劫持, 并通过Reflect操作对象内部数据

        5、ref的数据操作: 在js中要.value, 在模板中不需要(内部解析模板时会自动添加.value)

5)computed和watch   写法看代码

        1、computed里有get()和set()

        2、watch两个特性,immediate立即执行,deep深度监视。

        3、watch监视非响应式数据,要用回调写法。

        4、watchEffect  监听所有数据,所以不用指定监视对象。默认初始就会执行一次。

<template>
  <h2>App</h2>
  fistName: <input v-model="user.firstName"/><br>
  lastName: <input v-model="user.lastName"/><br>
  fullName1: <input v-model="fullName1"/><br>
  fullName2: <input v-model="fullName2"><br>
  fullName3: <input v-model="fullName3"><br>

</template>

<script lang="ts">
/*
计算属性与监视
1. computed函数: 
  与computed配置功能一致
  只有getter
  有getter和setter
2. watch函数
  与watch配置功能一致
  监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
  默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
  通过配置deep为true, 来指定深度监视
3. watchEffect函数
  不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
  默认初始时就会执行第一次, 从而可以收集需要监视的数据
  监视数据发生变化时回调
*/

import {
  reactive,
  ref,
  computed,
  watch,
  watchEffect
} from 'vue'

export default {

  setup () {
    const user = reactive({
      firstName: 'A',
      lastName: 'B'
    })

    // 只有getter的计算属性
    const fullName1 = computed(() => {
      console.log('fullName1')
      return user.firstName + '-' + user.lastName
    })

    // 有getter与setter的计算属性
    const fullName2 = computed({
      get () {
        console.log('fullName2 get')
        return user.firstName + '-' + user.lastName
      },

      set (value: string) {
        console.log('fullName2 set')
        const names = value.split('-')
        user.firstName = names[0]
        user.lastName = names[1]
      }
    })

    const fullName3 = ref('')

    /* 
    watchEffect: 监视所有回调中使用的数据
    */
    /* 
    watchEffect(() => {
      console.log('watchEffect')
      fullName3.value = user.firstName + '-' + user.lastName
    }) 
    */

    /* 
    使用watch的2个特性:
      深度监视
      初始化立即执行
    */
    watch(user, () => {
      fullName3.value = user.firstName + '-' + user.lastName
    }, {
      immediate: true,  // 是否初始化立即执行一次, 默认是false
      deep: true, // 是否是深度监视, 默认是false
    })

    /* 
    watch一个数据
      默认在数据发生改变时执行回调
    */
    watch(fullName3, (value) => {
      console.log('watch')
      const names = value.split('-')
      user.firstName = names[0]
      user.lastName = names[1]
    })

    /* 
    watch多个数据: 
      使用数组来指定
      如果是ref对象, 直接指定
      如果是reactive对象中的属性,  必须通过函数来指定
    */
    watch([() => user.firstName, () => user.lastName, fullName3], (values) => {
      console.log('监视多个数据', values)
    })

    return {
      user,
      fullName1,
      fullName2,
      fullName3
    }
  }
}
</script>

6)与 2.x 版本生命周期相对应的组合式 API

        1、beforeCreate -> 使用 setup()

        2、created -> 使用 setup()

        3、beforeMount -> onBeforeMount(() => {})

        4、mounted -> onMounted(() => {})

        5、beforeUpdate -> onBeforeUpdate(() => {})

        6、updated -> onUpdated(() => {})

        7、beforeDestroy -> onBeforeUnmount(() => {})

        8、destroyed -> onUnmounted(() => {})

        9、errorCaptured -> onErrorCaptured(() => {})

        6-1)3.0对应生命周期钩子比2.x钩子快。

        6-2)新增两个钩子。

                onRenderTracked  状态跟踪:跟踪页面上所有响应式变量和方法的状态。        

onRenderTracked((event) => {
  console.log("状态跟踪组件----------->");
  console.log(event);
});

                onRenderTriggerd  状态触发。给你变化值的信息,并且新值和旧值都会展示。

onRenderTriggered((event) => {
  console.log("状态触发组件--------------->");
  console.log(event);
});

对 event 对象属性的详细介绍:
- key 那边变量发生了变化
- newValue 更新后变量的值
- oldValue 更新前变量的值
- target 目前页面中的响应变量和函数

 7)自定义hook函数,类似vue2.x的mixin用法:封装ajax请求。

hooks/useRequest.ts

import { ref } from 'vue'
import axios from 'axios'

/* 
使用axios发送异步ajax请求
*/
export default function useUrlLoader<T>(url: string) {

  const result = ref<T | null>(null)
  const loading = ref(true)
  const errorMsg = ref(null)

  axios.get(url)
    .then(response => {
      loading.value = false
      result.value = response.data
    })
    .catch(e => {
      loading.value = false
      errorMsg.value = e.message || '未知错误'
    })

  return {
    loading,
    result,
    errorMsg,
  }
}
<template>
<div class="about">
  <h2 v-if="loading">LOADING...</h2>
  <h2 v-else-if="errorMsg">{{errorMsg}}</h2>
  <!-- <ul v-else>
    <li>id: {{result.id}}</li>
    <li>name: {{result.name}}</li>
    <li>distance: {{result.distance}}</li>
  </ul> -->

  <ul v-for="p in result" :key="p.id">
    <li>id: {{p.id}}</li>
    <li>title: {{p.title}}</li>
    <li>price: {{p.price}}</li>
  </ul>
  <!-- <img v-if="result" :src="result[0].url" alt=""> -->
</div>
</template>

<script lang="ts">
import {
  watch
} from "vue"
import useRequest from './hooks/useRequest'

// 地址数据接口
interface AddressResult {
  id: number;
  name: string;
  distance: string;
}

// 产品数据接口
interface ProductResult {
  id: string;
  title: string;
  price: number;
}

export default {
  setup() {

    // const {loading, result, errorMsg} = useRequest<AddressResult>('/data/address.json')
    const {loading, result, errorMsg} = useRequest<ProductResult[]>('/data/products.json')

    watch(result, () => {
      if (result.value) {
        console.log(result.value.length) // 有提示
      }
    })

    return {
      loading,
      result, 
      errorMsg
    }
  }
}
</script>

8)toRefs可以把reactive包裹的数据变成普通的对象的ref对象。(注意:ref对象js里调用要加.value

9)其他API  

        1、shallowReactive  shallowRef  浅响应式(区别于reactive  ref  深度响应式)。

        shallowReactive : 只处理了对象内最外层属性的响应式(也就是浅响应式)

        shallowRef: 只处理了value的响应式, 不进行对象的reactive处理

        2、readOnly(深只读)  shallowReadOnly (浅只读,深层对象可以修改)

        3、toRaw  markRaw

                toRaw

                        由响应式代理对象变为非响应式的普通对象,并返回

                markRaw

                        标记一个对象,使其永远不会转换为代理。返回非响应式的普通对象本身

        4、toRef(据说很有用)

                1)为源响应式对象上的某个属性创建一个 ref对象,二者内部操作的是同一个数据值, 更新时二者是同步的

                2)区别ref: 拷贝了一份新的数据值单独操作, 更新时相互不影响

        5、customRef 自定义ref

        有track跟trigger回调  对应 get()根set()   下面例子里有

<template>
  <h2>App</h2>
  <input v-model="keyword" placeholder="搜索关键字"/>
  <p>{{keyword}}</p>
</template>

<script lang="ts">
/*
customRef:
  创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制

需求: 
  使用 customRef 实现 debounce 的示例
*/

import {
  ref,
  customRef
} from 'vue'

export default {

  setup () {
    const keyword = useDebouncedRef('', 500)
    console.log(keyword)
    return {
      keyword
    }
  },
}

/* 
实现函数防抖的自定义ref
*/
function useDebouncedRef<T>(value: T, delay = 200) {
  let timeout: number
  return customRef((track, trigger) => {
    return {
      get() {
        // 告诉Vue追踪数据
        track()
        return value
      },
      set(newValue: T) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          // 告诉Vue去触发界面更新
          trigger()
        }, delay)
      }
    }
  })
}

</script>

50、async  defer

async:加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步),乱序;

defer:js文件异步读取加载,要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成;

51、Promise.all

Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    // 参数可以不是数组,但必须具有 Iterator 接口
    if (typeof promises[Symbol.iterator] !== "function") {
      reject("Type error");
    }
    if (promises.length === 0) {
      resolve([]);
    } else {
      const res = [];
      let count = 0;
      const len = promises.length;
      for (let i = 0; i < len; i++) {
        //考虑到 promises[i] 可能是 thenable 对象也可能是普通值
        Promise.resolve(promises[i])
          .then((data) => {
            res[i] = data;
            if (++count === len) {
              resolve(res);
            }
          })
          .catch((err) => {
            reject(err);
          });
      }
    }
  });
};

52、类数组转数组

1、Array.from();

2、Array.prototype.slice.call():

3、扩展运算符

4、Array.prototype.concat.apply();

53、动画属性

  • animation-name   名字
  • animation-duration  时间
  • animation-timing-function  速度曲线
  • animation-delay  动画开始之前的延迟
  • animation-iteration-count  动画执行次数
  • animation-direction   是否轮流反向播放动画

54、DOCTYPE有什么作用?标准模式与混杂模式如何区分?它们有何意义?

告诉浏览器使用哪个版本的HTML规范来渲染文档。DOCTYPE不存在或形式不正确会导致HTML文档以混杂模式呈现。
标准模式(Standards mode)以浏览器支持的最高标准运行;混杂模式(Quirks mode)中页面是一种比较宽松的向后兼容的方式显示。

55、常见的浏览器内核有哪些?

  1. Trident( MSHTML ):IE MaxThon TT The World 360 搜狗浏览器  -ms-

  2. Geckos:Netscape6及以上版本 FireFox Mozilla Suite/SeaMonkey  -moz-

  3. Presto:Opera7及以上(Opera内核原为:Presto,现为:Blink)  -o-

  4. Webkit:Safari Chrome  -webkit-

56、HTML5的文件离线储存怎么使用,工作原理是什么

在线情况下,浏览器发现HTML头部有manifest属性,它会请求manifest文件,如果是第一次访问,那么浏览器就会根据manifest文件的内容下载相应的资源,并进行离线存储。如果已经访问过并且资源已经离线存储了,那么浏览器就会使用离线的资源加载页面。然后浏览器会对比新的manifest文件与旧的manifest文件,如果文件没有发生改变,就不会做任何操作,如果文件改变了,那么就会重新下载文件中的资源,并且进行离线存储。例如,

在页面头部加入manifest属性

<html manifest='cache.manifest'>

57、websocket

58、JS继承(6种)

59、watch跟computed区别

1、computed支持缓存;watch不支持缓存

2、computed不支持异步;watch支持异步

3、computed是多对一或一对一关系;watch是一对多关系

4、computed里每个属性都可以设置set跟get;watch监听的数据必须是data或props里声明过的

60、手写axios

61、小程序

微信小程序 面试题整理(自用)_热爱°可抵岁月漫长的博客-CSDN博客_小程序面试题

62、IE浏览器兼容问题处理

IE浏览器兼容性问题解决方案 - 码上快乐

63、gitlab-ci

GitLab CI介绍——入门篇_BuildRun技术团队的博客-CSDN博客_gitlab-ci

64、vue原理题

12道vue高频原理面试题,你能答出几道? - 知乎

65、回调地狱怎么处理

66、H5新增

H5新增_·Q·的博客-CSDN博客_h5新增

67、块状/内联元素

块状:<div>、<p>、<h1>-<h6>、 <ol>、<ul>、<dl>、<table>、 <address>、<blockquote> 、<form>

内联:<a>、<span>、<br>、<i>、<em>、<strong> <label>、<q>、<var>、<cite>、<code>

68、css能继承的属性

visibility、cursor、line-height、font-size

69、值类型和引用类型区别

引用类型有:Object、Array、Function

两者直接的区别在于值类型保存具体的值,引用类型保存值的地址

70、class的类型实际是函数、本质是null

71、promise

1)三种状态:pending(待定状态)、resolve、reject

2)变化过程:pending->resolve/reject

3)resolve不能到reject状态

4).catch里再接promise会到.then

算法

正则了解

shell了解

最全的 Vue 面试题+详解答案 - 掘金

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值