目录
16、vue store 存储 dispatch 和 commit的区别
35、vue-router 路由钩子函数是什么 执行顺序是什么
36-1、vue-router 组件复用导致路由参数失效怎么办?
54、DOCTYPE有什么作用?标准模式与混杂模式如何区分?它们有何意义?
做了一份前端面试复习计划,保熟~ - 掘金
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。
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点)
- this 就是你 call 一个函数时,传入的 context。(结合7刚开始例子理解)
- 如果你的函数调用形式不是 call 形式,请按照「转换代码」将其转换为 call 形式(函数的context就是调用函数的对象本身,如果没有对象则是window,结合第1条)。
- 箭头函数里面没有 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 渲染后触发,如
setTimeout
、setInterval
、DOM 事件
、script
。 - 微任务:DOM 渲染前触发,如
Promise.then
、MutationObserver
、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 或字符串,也不管是否可枚举
- for in:主要用来遍历对象(for keys in obj)
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
报文,是由客户端对http
的post
和get
的请求策略决定的,目的是为了避免浪费资源,如带宽,数据传输消耗的时间等等。所以客户端会在发送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(没看完)
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 路由钩子函数是什么 执行顺序是什么
路由钩子的执行流程, 钩子函数种类有:全局守卫、路由守卫、组件守卫
完整的导航解析流程:
- 导航被触发。
- 在失活的组件里调用 beforeRouteLeave 守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫 (2.5+)。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 调用 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 中键点击
-
键盘修饰符
键盘修饰符是用来修饰键盘事件(onkeyup
,onkeydown
)的,有如下:
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
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、常见的浏览器内核有哪些?
-
Trident( MSHTML ):IE MaxThon TT The World 360 搜狗浏览器 -ms-
-
Geckos:Netscape6及以上版本 FireFox Mozilla Suite/SeaMonkey -moz-
-
Presto:Opera7及以上(Opera内核原为:Presto,现为:Blink) -o-
-
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浏览器兼容问题处理
63、gitlab-ci
GitLab CI介绍——入门篇_BuildRun技术团队的博客-CSDN博客_gitlab-ci
64、vue原理题
65、回调地狱怎么处理
66、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