目录
做了一份前端面试复习计划,保熟~ - 掘金
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点)
28、new关键字执行过程(原型链)
29、判断数组类型(4种)
29-1、数组方法
29-1、数组去重
30、babel(没看完)
31、vue组件通讯
32、父子组件生命周期执行顺序
33、v-if 和 v-show 的区别
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了解
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,感受闭包的艺术 - 掘金
ES5中,6种基础类型:Number,String,Boolan,undefined,Null,Symbol ,BigInt。
ES6中,新增一种Symbol 。这种类型的对象永不相等,即始创建的时候传入相同的值,可以解决属性名冲突的问题,做为标记。新增BigInt 可以表示任意大小的整数。
==相同,===严格相同。
深拷贝实现方法有两种:
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;
};
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
根据MDN中文的定义,闭包的定义如下:
在JavaScript中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。可以在一个内层函数中访问到其外层函数的作用域。
也可以这样说:
闭包是指那些能够访问自由变量的函数。自由变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量。闭包=函数+函数能够访问的自由变量。
闭包就是能够读取其他函数内部变量的一个函数。
简单的说闭包是解决了函数内变量暴露给函数外访问。
就是为了解决突破函数作用域的限制才有了闭包这种概念。
闭包可能导致内存泄露,即函数执行完成后内部变量引闭包存在原因还可访问
例:
function print(fn) {
const a = 200;
fn();
}
const a = 100;
function fn() {
console.log(a);
}
print(fn); //100
闭包:自由变量的查找,是在函数定义的地方,向上级作用域查找。不是在执行的地方。
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
所谓同源是指,域名,协议,端口均相同,只要有一个不同,就是跨域。
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处理过,把插件传到了内网,解决跨域问题
setTimeout
、setInterval
、DOM 事件
、script
。Promise.then
、MutationObserver
、Node 环境下的 process.nextTick
。微任务在DOM渲染前触发,宏任务在DOM渲染后触发
同步任务->微任务->宏任务
!important > 行内 > id选择器 > 属性选择器 = 类选择器 = 伪元素选择器 > 标签选择器 = 伪类选择器(::after)
1、标签不同
2、p有行间距
3、页面框架布局使用div,文章内容段落用p
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(替代)盒模型。
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执行,其他浏览器不执行*/
}
块级格式上下文,它是 CSS 视觉渲染的一部分,用于决定块级盒的布局及浮动相互影响范围的一个区域。
创建 BFC 的方式:
position
为 absolute
或 fixed
)。display
为 inline-block
(inline-block,table-cell,table-caption,table,fle)。overflow
的值不为 visible
(scroll,hidden,auto,inherit)。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:左侧宽度;
回流这一阶段主要是计算节点的位置和几何信息
最终,我们通过构造渲染树和回流阶段,我们知道了哪些节点是可见的,以及可见节点的样式和具体的几何信息(位置、大小),那么我们就可以将渲染树的每个节点都转换为屏幕上的实际像素,这个阶段就叫做重绘节点。
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;
}
dispatch:含有异步操作。写在action内,action可以直接执行异步。
commit:同步操作。写在mutations内,mutations只能执行同步任务。
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布局;
flex-grow: 1
:该属性默认为 0
,如果存在剩余空间,元素也不放大。设置为 1
代表会放大。flex-shrink: 1
:该属性默认为 1
,如果空间不足,元素缩小。flex-basis: 0%
:该属性定义在分配多余空间之前,元素占据的主轴空间。浏览器就是根据这个属性来计算是否有多余空间的。默认值为 auto
,即项目本身大小。设置为 0%
之后,因为有 flex-grow
和 flex-shrink
的设置会自动放大或缩小。在做两栏布局时,如果右边的自适应元素 flex-basis
设为 auto
的话,其本身大小将会是 0
。
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栅格系统 以及以上结合使用。
1)域名解析
输入 URL 后解析出协议、主机、端口、路径等信息,并构造一个 HTTP 请求。
DNS 域名解析
2)TCP三次握手
由 TCP 的自身特点可靠传输决定的。客户端和服务端要进行可靠传输,那么就需要确认双方的接收和发送能力。第一次握手可以确认客户端的发送能力,第二次握手,确认了服务端的发送能力和接收能力,所以第三次握手才可以确认客户端的接收能力。不然容易出现丢包的现象。
3)http 请求
4)服务器接收到请求后并返回 HTTP 报文
5)浏览器得到HTTP报文后,开始渲染页面
6)断开TCP链接
1)1XX信息性状态码
2)200成功
3)302重定向 304客户的缓存资源是最新的,要客户使用缓存
4)400发送了错误请求 401没权限 404找不到资源
5)500服务器内部错误 502网关故障 504网关超时
DNS 即域名系统,全称是 Domain Name System。当我们在浏览器输入一个 URL 地址时,浏览器要向这个 URL 的主机名对应的服务器发送请求,就得知道服务器的 IP,对于浏览器来说,DNS 的作用就是将主机名转换成 IP 地址。
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压缩
package.json 文件其实就是对项目或者模块包的描述,里面包含许多元信息。比如项目名称,项目版本,项目执行入口文件,项目贡献者等等。npm install 命令会根据这个文件下载所有依赖模块。
npm init 可自动创建,也可手动创建
1、webpack作用
1)模块打包
2)变异兼容
3)能力扩展(按需加载、代码压缩等)
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; }
1)var是全局,let、const是块级作用域
2)var可以在声明的上面访问变量
3)const声明之后必须赋值,不可改变
1)箭头函数里面没有 this 对象,
此时的 this 是外层的 this 对象,即 Window
2)没有argument对象
3)不能用作构造函数,所以代表着不能使用new命令
基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定
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
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;
}
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) 累加器
上面所说的post
会比get
多一个tcp
包其实不太严谨。多发的那个expect 100 continue header
报文,是由客户端对http
的post
和get
的请求策略决定的,目的是为了避免浪费资源,如带宽,数据传输消耗的时间等等。所以客户端会在发送header
的时候添加expect 100
去探探路,如果失败了就不用继续发送data
,从而减少了资源的浪费。所以是否再发送一个包取决于客户端的实现策略,和get/post
并没什么关系。有的客户端比如fireFox
就只发送一个包。
缺点
1)浏览器cookie个数限制 IE6 20个,IE7、fireFox 50个,chrome、safari无限制
2)cookie大小4M
3)安全性不够
4)每次随http请求发送,造成不必要负担
优点
1)数据持久
2)简单易控
1)首先创建一个空对象。
2)根据原型链,设置空对象的_proto_为构造函数的prototype。
3)构造函数的this指向了这个对象,并执行了构造函数代码。
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]"
会改变原来数组的:
pop()—删除数组的最后一个元素并返回删除的元素。
push()—向数组的末尾添加一个或更多元素,并返回新的长度。
shift()—删除并返回数组的第一个元素。
unshift()—向数组的开头添加一个或更多元素,并返回新的长度。
reverse()—反转数组的元素顺序。
sort()—对数组的元素进行排序。
splice()—用于插入、删除或替换数组的元素。 返回的是含有被删除的数组
································································································
不会改变原来数组的:
concat()—连接两个或更多的数组,并返回结果。
every()—检测数组元素的每个元素是否都符合条件。
some()—检测数组元素中是否有元素符合指定条件。
filter()—检测数组元素,并返回符合条件所有元素的数组。
indexOf()—搜索数组中的元素,并返回它所在的位置。
join()—把数组的所有元素放入一个字符串。
toString()—把数组转换为字符串,并返回结果。
lastIndexOf()—返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。
map()—通过指定函数处理数组的每个元素,并返回处理后的数组。
slice()—选取数组的的一部分,并返回一个新数组。
valueOf()—返回数组对象的原始值。
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
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
不容错过的 Babel7 知识 - 掘金
前端工程师需要了解的 Babel 知识 - 政采云前端团队
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 兄弟组件数据传递 这种情况下可以使用事件总线的方式
1)加载渲染过程
父 beforeCreate->父 created->父 beforeMount->子 beforeCreate->子 created->子 beforeMount->子 mounted->父 mounted
2)子组件更新过程
父 beforeUpdate->子 beforeUpdate->子 updated->父 updated
3)父组件更新过程
父 beforeUpdate->父 updated
4)销毁过程
父 beforeDestroy->子 beforeDestroy->子 destroyed->父 destroyed
v-if 在编译过程中会被转化成三元表达式,条件不满足时不渲染此节点。
v-show 会被编译成指令,条件不满足时控制样式将对应节点隐藏 (display:none)
使用场景
v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景
v-show 适用于需要非常频繁切换条件的场景
整体思路是数据劫持+观察者模式
对象内部通过 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);
}
}
路由钩子的执行流程, 钩子函数种类有:全局守卫、路由守卫、组件守卫
完整的导航解析流程:
动态路由就是有动态变量的,例:
const User = {
template: "User",
};
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: "/user/:id", component: User },
],
});
1.通过 watch 监听路由参数再发请求
watch: { //通过watch来监听路由变化
"$route": function(){
this.getData(this.$route.params.xxx);
}
}
2.用 :key 来阻止“复用”
主要包括以下几个模块:
keep-alive 是 Vue 内置的一个组件,可以实现组件缓存,当组件切换时不会对当前组件进行卸载。
常用的两个属性 include/exclude,允许组件有条件的进行缓存。
两个生命周期 activated/deactivated,用来得知当前组件是否处于活跃状态。
keep-alive 的中还运用了 LRU(最近最少使用) 算法,选择最近最久未使用的组件予以淘汰。
了解 Vue 响应式原理的同学都知道在两种情况下修改数据 Vue 是不会触发视图更新的
1.在实例创建之后添加新的属性到实例上(给响应式对象新增属性)
2.直接更改数组下标来修改数组的值
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...)
async 能对props
进行一个双向绑定
prop 设置自定义标签属性,避免暴露数据,防止污染HTML结构
camel 将命名变为驼峰命名法,如将view-Box
属性名转换为 viewBox
Vue 的编译过程就是将 template 转化为 render 函数的过程 分为以下三步
第一步是将 模板字符串 转换成 element ASTs(解析器)
第二步是对 AST 进行静态节点标记,主要用来做虚拟DOM的渲染优化(优化器)
第三步是 使用 element ASTs 生成 render 函数代码字符串(代码生成器)
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
执行更新函数
首屏时间(First Contentful Paint),指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容
1)加载慢的原因
网络延时问题
资源文件体积是否过大
资源是否重复发送请求去加载了
加载脚本的时候,渲染内容堵塞了
2)解决方案
减小入口文件体积
静态资源本地缓存
UI框架按需加载
图片资源的压缩
组件重复打包
开启GZip压缩
使用SSR
组件必须用data(),因为使用data:{},会导致重复使用组件是,内存地址重复,公用同一个内存地址。函数时不会出现该问题。
调用$mount
进行页面的挂载
挂载的时候主要是通过mountComponent
方法
定义updateComponent
更新函数
执行render
生成虚拟DOM
_update
将虚拟DOM
生成真实DOM
结构,并且渲染到页面中
用于获取数据变化后,最新的dom。
在非父子组件通信时,可以使用通常的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
文件中直接使用即可
姓名:{{ name }}
年龄:{{ age }}
import { state, mutations } from '@/store
export default {
// 在计算属性中拿到值
computed: {
name() {
return state.name
},
age() {
return state.age
}
},
// 调用mutations里面的方法,更新数据
methods: {
changeName: mutations.changeName,
setAge: mutations.setAge
}
}
diff
算法是一种通过同层的树节点进行比较的高效算法。
v-for加key,key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。
课程指南 | 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 表示不预先指定具体类型。函数泛型:函数名
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(反射): 动态对被代理对象的相应属性进行特定的操作。
Proxy 与 Reflect
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 监听所有数据,所以不用指定监视对象。默认初始就会执行一次。
App
fistName:
lastName:
fullName1:
fullName2:
fullName3:
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(url: string) {
const result = ref(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,
}
}
LOADING...
{{errorMsg}}
- id: {{p.id}}
- title: {{p.title}}
- price: {{p.price}}
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() 下面例子里有
App
{{keyword}}
async:加载和渲染后续文档元素的过程将和 script.js
的加载与执行并行进行(异步),乱序;
defer:js文件异步读取加载,要在所有元素解析完成之后,DOMContentLoaded
事件触发之前完成;
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);
});
}
}
});
};
1、Array.from();
2、Array.prototype.slice.call():
3、扩展运算符
4、Array.prototype.concat.apply();
告诉浏览器使用哪个版本的HTML规范来渲染文档。DOCTYPE不存在或形式不正确会导致HTML文档以混杂模式呈现。
标准模式(Standards mode)以浏览器支持的最高标准运行;混杂模式(Quirks mode)中页面是一种比较宽松的向后兼容的方式显示。
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-
在线情况下,浏览器发现HTML头部有manifest属性,它会请求manifest文件,如果是第一次访问,那么浏览器就会根据manifest文件的内容下载相应的资源,并进行离线存储。如果已经访问过并且资源已经离线存储了,那么浏览器就会使用离线的资源加载页面。然后浏览器会对比新的manifest文件与旧的manifest文件,如果文件没有发生改变,就不会做任何操作,如果文件改变了,那么就会重新下载文件中的资源,并且进行离线存储。例如,
在页面头部加入manifest属性
1、computed支持缓存;watch不支持缓存
2、computed不支持异步;watch支持异步
3、computed是多对一或一对一关系;watch是一对多关系
4、computed里每个属性都可以设置set跟get;watch监听的数据必须是data或props里声明过的
微信小程序 面试题整理(自用)_热爱°可抵岁月漫长的博客-CSDN博客_小程序面试题
IE浏览器兼容性问题解决方案 - 码上快乐
GitLab CI介绍——入门篇_BuildRun技术团队的博客-CSDN博客_gitlab-ci
12道vue高频原理面试题,你能答出几道? - 知乎
H5新增_·Q·的博客-CSDN博客_h5新增
块状:
、