对目前项目做的贡献

项目做过哪写优化

  1. table渲染
  2. 子组件回退刷新(diff记录子组件是否有操作,父组件是否需要刷新, 列:详情页操作后回退父组件刷新)
  3. https://juejin.im/post/5e649e3e5188252c06113021
  4. https://juejin.im/post/5d690c726fb9a06b155dd40d
  5. https://juejin.im/post/5e8b261ae51d4546c0382ab4
  6. 路由懒加载原理 https://juejin.im/post/5ed7b687518825432632981e
  • 尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
  • v-if和v-for不能连用
  • 如果需要使用v-for给每项元素绑定事件时使用事件代理
  • SPA 页面采用keep-alive缓存组件
  • 在更多的情况下,使用v-if替代v-show
  • key保证唯一
  • 使用路由懒加载、异步组件
  • 防抖、节流
  • 第三方模块按需导入
  • 长列表滚动到可视区域动态加载
  • 图片懒加载

    为什么React有componentShoudUpdate而Vue没有

    1.应为React的diff是依据dom的变化,通过setData设置数据, Vue底层实现了监听数据的diff,初始化时对数据做了数据劫持.

    在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树。当然,这可以通过shouldComponentUpdate这个生命周期方法来进行控制purerender,但Vue将此视为默认的优化。
    vue中实现数据绑定靠的是数据劫持(Object.defineProperty())+发布-订阅模式。在 Vue 应用中,组件的依赖是在渲染过程中自动追踪的,所以系统能精确知晓哪个组件需要被重渲染。你可以理解为每一个组件都已经自动获得了shouldComponentUpdate,并且没有上述的子树问题限制。
    https://www.jianshu.com/p/6e124ad23c68
    react 组件高效渲染: https://www.jianshu.com/p/100a55978253

    Service Worker有哪些作用

绝对定位、固定定位和z-index

  • 一旦给元素加上absolutefloat就相当于给元素加上了display:block
  • absolute元素覆盖正常文档流内元素(不用设z-index,自然覆盖)
  • 可以减少重绘和回流的开销(如absolute+ top:-9999em,或absolute + visibility:hidden,将动画效果放到absolute元素中)

    VDom以及key属性的作用, 为什么不建议用index或者随机数当做key

Vue2.x组件通信有哪些方式?

  • 父子组件通信
    父->子props,子->父 $on、$emit
    获取父子组件实例 $parent、$children
    Ref 获取实例的方式调用组件的属性或者方法
    Provide、inject 官方不推荐使用,但是写组件库时很常用
  • 兄弟组件通信
    Event Bus 实现跨组件通信 Vue.prototype.$bus = new Vue
    Vuex
  • 跨级组件通信
    Vuex
    $attrs、$listeners
    Provide、inject

    Flex 应用场景

  • 绝对居中

    1. .box{
    2. display: flex;
    3. width: 100%;
    4. height: 500px;
    5. background: #CCCCCC;
    6. justify-content: center; //主轴居中
    7. align-items: center; //交叉轴居中
    8. }
    9. .box div{
    10. width: 50px;
    11. height: 50px;
    12. background: red;
    13. border:2px solid #007AFF;
    14. }
    15. <div class="box">
    16. <div class="div1">你大爷</div>
    17. </div>
  • 图片展示, 指定宽度,不指定数量

    1. .box{
    2. display: flex;
    3. width: 100%;
    4. height: 500px;
    5. background: #CCCCCC;
    6. justify-content: flex-start;
    7. align-content: flex-start;/*交叉轴方向行间排列*/
    8. align-items: flex-start;
    9. flex-wrap: wrap; /*设置换行*/
    10. padding: 10px;
    11. }
    12. .box div{
    13. width: 20%;
    14. height: 50px;
    15. background: red;
    16. border:2px solid #007AFF;
    17. margin: 5px;/*间距*/
    18. box-sizing: border-box;
    19. }

    stick-footer

    1. <html lang="en">
    2. <head>
    3. <meta charset="UTF-8" />
    4. <title>stick footer</title>
    5. <style>
    6. html,
    7. body {
    8. height: 100%;
    9. width: 100%;
    10. margin: 0;
    11. padding: 0;
    12. }
    13. .wrapper {
    14. height: 100%;
    15. }
    16. .content {
    17. display: flex;
    18. justify-content: flex-start;
    19. align-items: flex-start;
    20. align-content: flex-start;
    21. flex-wrap: wrap;
    22. min-height: 100%;
    23. padding-bottom: 50px;
    24. }
    25. .content div {
    26. height: 200px;
    27. width: 20%;
    28. background-color: aquamarine;
    29. border: 1px solid blueviolet;
    30. box-sizing: border-box;
    31. }
    32. .footer {
    33. height: 50px;
    34. margin-top: -50px;
    35. }
    36. </style>
    37. </head>
    38. <body>
    39. <div class="wrapper">
    40. <div class="content">
    41. <div>tas</div>
    42. <div>tas</div>
    43. <div>tas</div>
    44. <div>tas</div>
    45. <div>tas</div>
    46. <div>tas</div>
    47. <div>tas</div>
    48. </div>
    49. </div>
    50. <div class="footer">
    51. footer
    52. </div>
    53. </body>
    54. </html>

    Vue3.0

    https://juejin.im/post/5e9faa8fe51d4546fe263eda

图片懒加载

  1. // 1. 图片懒加载原理
  2. // 给每张图片添加一个data-xxx的属性用于存放图片的src,检测到图片进入视野中的时候把data-xxx的属性赋给src
  3. // Promise 延时输出
  4. function delay(time: number) {
  5. return new Promise((resolve, reject) => {
  6. setTimeout(() => {
  7. resolve()
  8. }, time);
  9. })
  10. }
  11. const getTop = (evt: any) => {
  12. return evt.offsetTop
  13. }
  14. // 图片懒加载
  15. const lazyLoad = () => {
  16. let imags = document.querySelectorAll('img')
  17. // 可是区域高度
  18. const h = window.innerHeight
  19. // 滚动区域
  20. const s = document.body.scrollTop || document.documentElement.scrollTop
  21. for (let i = 0; i < imags.length; i++) {
  22. if (h + s < getTop(imags[i])) {
  23. setTimeout(() => {
  24. const temp = new Image()
  25. temp.src = imags[i].getAttribute('data-src') || ''
  26. temp.onload = () => {
  27. imags[i].src = temp.getAttribute('data-src') || ''
  28. }
  29. }, 2000)
  30. }
  31. }
  32. }
  33. // 每一次滚动都回去计算?
  34. window.onscroll = () => {
  35. lazyLoad()
  36. }

sessionStorage用途

敏感账号的一次性登陆,sessionId不想保存在cookie,就保存在sessionStorage。

new 做了什么

  1. 内部创建对象
  2. 将构造函数的this指向这个对象
  3. 执行构造函数赋值
  4. 返回这个对象实例

var obj = newObject(); // 创建一个空对象
obj.proto = F.prototype; // obj的proto指向构造函数的prototypevar result = F.call(obj); // 把构造函数的this指向obj,并执行构造函数把结果赋值给resultif (typeof(result) === ‘object’) {
objB = result; // 构造函数F的执行结果是引用类型,就把这个引用类型的对象返回给objB
} else {
objB = obj; // 构造函数F的执行结果是值类型,就返回obj这个空对象给objB
}

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

  1. 箭头函数简洁 清晰
  2. 箭头函数this被创建后不可改变(call, apply, bind也不行)
  3. 箭头函数的this指向执行上下文, 自身没有this
  4. 不可用作构造函数 不能被new 没有prototype
  5. 没有自己的arguments, 在上下文中
  6. 不能用作Generator函数, 不能使用yeild关键字

TS中常用的泛型

  1. Partial 部分的 type Partial = { [P in keyof T]?: T[P] };
  2. Required 必须的 type Required = { [P in keyof T]-?: T[P] };
  3. Mutable 可变的 type Mutable = { -readonly [P in keyof T]: T[P] };
  4. Readonly 只读的 type Mutable = { +readonly [P in keyof T]: T[P] };
  5. Omit 忽略 type Omit = Pick >
  6. Exclude 排除 type Exclude = T extends U ? never : T;
  7. Extract 提取 type Extract = T extends U ? T : never;
  8. Pick 挑选 type Pick = { [P in K]: T[P] };
  9. Record 记录 type Record = { [P in K]: T };

ts interface 和 type的区别

  1. 都可以被扩展 interface extends ; type &
  2. interface 可以声明合并

interface User{ name: string; age: numer }
interface User{ sex: string}
-> User{name: string; age: numer; sex: string}
3. type可以给基本数据类型定义别名, 联合声明类型, 元祖等;

  • type 语句中还可以使用 typeof 获取实例的 类型进行赋值

type Name = String
type a = string | number
type b = [string,number]

npm版本语义化控制

X.X.X 格式
主板本 . 向下兼容新增功能 . 补丁版本

Promise

Promise的三种状态:

:::success pending、fulfilled、rejected(未决定,履行,拒绝),同一时间只能存在一种状态,且状态一旦改变就不能再变。promise是一个构造函数,promise对象代表一项有两种可能结果(成功或失败)的任务,它还持有多个回调,出现不同结果时分别发出相应回调。 ::: 1.初始化,状态:pending
2.当调用resolve(成功),状态:pengding=>fulfilled
3.当调用reject(失败),状态:pending=>rejected

const PENDING = “pending”;//Promise会一直保持挂起状态,知道被执行或拒绝。
const FULFULLED = “fulfilled”;
const REJECTED = “rejected”;
class Promise{
constructor(exector){
let self = this;//缓存当前promise对象
self.status = PENDING;//初始状态,对promise对象调用state(状态)方法,可以查看其状态是“pending”、”resolved”、还是”rejected“
self.value = undefined;// fulfilled状态时 返回的信息
self.reason = undefined;// rejected状态时 拒绝的原因
self.onResolveCallBacks = [];// 存储resolve(成功)状态对应的onFulfilled函数
self.onRejectCallBacks = [];// 存储rejected(失败)状态对应的onRejected函数
let resolve = (value) => {//成功
if(self.status === PENDING){//如果成功则,状态由pending=>fulfilled
self.status = FULFULLED;
self.value = value;
self.onResolveCallBacks.forEach(cb=>cb(self.value));//执行发布
}
}
let reject = (reason) => {//失败
if(self.status === PENDING){//如果失败,则状态由pending=>rejected
self.status = REJECTED;
self.reason = reason;
self.onRejectCallBacks.forEach(cb=>cb(self.reason));//执行发布
}
}

try{
exector(resolve,reject)
}catch(e){
reject(e)
}
}
then(onFulfilled,onRejected){
let self=this;
if(self.status === FULFULLED){
onFulfilled(self.value);//成功值
}
if(self.status === REJECTED){
onFulfilled(self.reason);//拒绝原因
}
if(self.status === PENDING){
self.onResolveCallBacks.push(onFulfilled);//订阅发布
self.onRejectCallBacks.push(onRejected);//订阅发布
}
}
//promise的决议结果只有两种可能:完成和拒绝,附带一个可选的单个值。如果Promise完成,那么最终的值称为完成值;如果拒绝,那么最终的值称为原因。Promise只能被决议(完成或拒绝)一次。之后再次试图完成或拒绝的动作都会被忽略。
}

new Promise((resolve,reject)=>{
resolve(“挖坑妹”);
//异步处理
//处理结束后、调用resolve或reject
}).then((data)=>{
console.log(data);//“挖坑妹”
},(reason)=>{
console.log(reason);
})

3.promise的优缺点

优点:
1.Promise 分离了异步数据获取和业务逻辑,有利于代码复用。
2.可以采用链式写法
3.一旦 Promise 的值确定为fulfilled 或者 rejected 后,不可改变。

缺点:
代码冗余,语义不清。

二、为什么用Promise?

1.解决回调地狱

回调地狱:发送多个异步请求时,每个请求之间相互都有关联,会出现第一个请求成功后再做下一个请求的情况。我们这时候往往会用嵌套的方式来解决这种情况,但是这会形成”回调地狱“。如果处理的异步请求越多,那么回调嵌套的就越深。出现的问题:

1.代码逻辑顺序与执行顺序不一致,不利于阅读与维护。
2.异步操作顺序变更时,需要大规模的代码重构。
3.回调函数基本都是匿名函数,bug追踪困难。

const request = url => {
return new Promise((resolve,reject) => {
$.get(url,params => {
resolve(params)
});
});
};

request(url).then(params1 => {
return request(params1.url);
}).then(params2 => {
return request(params2.url);
}).then(params3 => {
console.log(params3);
}).catch(err => throw new Error(err));

2.解决异步
我们都知道js是单线程执行代码,导致js的很多操作都是异步执行(ajax)的,以下是解决异步的几种方式:
1.回调函数(定时器)。
2.事件监听。
3.发布/订阅。
4.Promise对象。(将执行代码和处理结果分开)
5.Generator。
6.ES7的async/await。

三、怎么用Promise?

promise有几种对象方法

1.then方法(异步执行)

当resolve(成功)/reject(失败)的回调函数

//onFulfilled 是用来接收promise成功的值
//onRejected 是用来接收promise失败的原因
promise.then(onFulfilled,onRejected)

2.resolve(成功)

调用onFulfilled

const promise = new Promise((resolve,reject)=>{
resolve(‘fulfilled’);//状态:pending=>fulfilled
});

promise.then(result =>{//onFulfilled调用
console.log(result);//‘fulfilled’
},result =>{//onRejected不调用

})

//注:resolve使用
Promise.resolve(‘hellow world’)相当于
//相当于
const promise = new Promise(resolve=>{
resolve(‘hellow world’);
})

3.reject(失败)

调用onRejected
const promise = new Promise((resolve,reject)=>{
reject(‘rejected’);//状态:pending=>rejected
});

promise.then(result =>{//onFulfilled不调用

},result =>{//onRejected调用
console.log(result);//‘rejected’
})

//注:reject使用
Promise.reject(‘err’)相当于
//相当于
const promise = new Promise((resolve,reject)=>{
reject(‘err’);
});

4.catch

链式写法中可以捕获前面then中发送的异常,这种写法的好处在于先执行promise操作,然后根据返回的结果(成功或失败)来调用onFulfilled(或者onRrejected)函数。

promise.then(onFulfilled).catch(onRrejected);

5.all

Promise.all接收一个promise对象数组为参数,处理并行异步操作会用到,但是需要全部为resolve才能调用。这种情况是几个任务可以并行执行
const promise1= new Promise((resolve,reject)=>{
resolve(‘promise1’);
});
const promise2= new Promise((resolve,reject)=>{
resolve(‘promise2’);
});
const promise3= new Promise((resolve,reject)=>{
resolve(‘promise3’);
});
Promise.all([promise1, promise2, promise3]).then(data => {
console.log(data);
// [‘promise1’, ‘promise2’, ‘promise3’] 结果顺序和promise实例数组顺序是一致的
}, err => {
console.log(err);
});
可以从一个promise对象派生出新的promise对象,我们可以要求代表着并行任务的两个promise对象合并成一个promise对象,由后者负责通知前面的那些任务都已完成。也可以要求代表着任务系列中首要任务的Promise对象派生出一个能代表任务系列中末任务的Promise对象,这样后者就能知道这一系列的任务是否均已完成。

6.race

Promise.race接收一个promise对象数组为参数,只要有一个promise对象进入Fulfilled或者Rejected状态的话,就会进行后面的处理。这可以解决多个异步任务的容错
function racePromise(time){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(time);
},time)
})
}
var startDate = Date.now();
Promise.race([
racePromise(5),
racePromise(50),
racePromise(500),
]).then(function(values){
console.log(values);
})

WebPack plugin 和 loader 区别

一、webpack的打包原理

  1. 识别入口文件
  2. 通过逐层识别模块依赖(Commonjs、amd或者es6的import,webpack都会对其进行分析,来获取代码的依赖)
  3. webpack做的就是分析代码,转换代码,编译代码,输出代码
  4. 最终形成打包后的代码

二、什么是loader
loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中

  1. 处理一个文件可以使用多个loader,loader的执行顺序和配置中的顺序是相反的,即最后一个loader最先执行,第一个loader最后执行
  2. 第一个执行的loader接收源文件内容作为参数,其它loader接收前一个执行的loader的返回值作为参数,最后执行的loader会返回此模块的JavaScript源码

三、什么是plugin
在webpack运行的生命周期中会广播出许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的API改变输出结果。
四、loader和plugin的区别
对于loader,它是一个转换器,将A文件进行编译形成B文件,这里操作的是文件,比如将A.scss转换为A.css,单纯的文件转换过程
plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务

let 和 var的区别

  1. var存在变量提升, let不存在
  2. 暂时性死区;只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。 :::warning 总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区” :::

    1. let 不允许重复定义
  3. let 存在块级作用域

    const 常量(内存地址不变) 值类型不可以变, 引用类型可以变

如何实现一个继承

  1. 如何实现一个继承 (Object.create & call)在 ES6 时代可以简单的通过 class & extends 实现继承,ES5 时代用如下方法

function A () {}
function B () {
A.call(this)
}

B.prototype = Object.create(A.prototype)
// B.prototype = new A() 不推荐

Object.entries() 和 for …in 的区别

object.entries() 返回自身可枚举属性; for …in 会返回原型链上的属性

深度克隆 解决循环引用

  1. function deepCopy(data, hash = new WeakMap()) {
  2. if(typeof data !== 'object' || data === null){
  3. throw new TypeError('传入参数不是对象')
  4. }
  5. // 判断传入的待拷贝对象的引用是否存在于hash中
  6. if(hash.has(data)) {
  7. return hash.get(data)
  8. }
  9. let newData = {};
  10. const dataKeys = Object.keys(data);
  11. /// for(let key in data){}
  12. dataKeys.forEach(value => {
  13. const currentDataValue = data[value];
  14. // 基本数据类型的值和函数直接赋值拷贝
  15. if (typeof currentDataValue !== "object" || currentDataValue === null) {
  16. newData[value] = currentDataValue;
  17. } else if (Array.isArray(currentDataValue)) {
  18. // 实现数组的深拷贝
  19. newData[value] = [...currentDataValue];
  20. } else if (currentDataValue instanceof Set) {
  21. // 实现set数据的深拷贝
  22. newData[value] = new Set([...currentDataValue]);
  23. } else if (currentDataValue instanceof Map) {
  24. // 实现map数据的深拷贝
  25. newData[value] = new Map([...currentDataValue]);
  26. } else {
  27. // 将这个待拷贝对象的引用存于hash中
  28. hash.set(data,data)
  29. // 普通对象则递归赋值
  30. newData[value] = deepCopy(currentDataValue, hash);
  31. }
  32. });
  33. return newData;
  34. }

Set WeakSet Map WeakMap

  • Set
    • 成员唯一、无序且不重复
    • [value, value],键值与键名是一致的(或者说只有键值,没有键名)
    • 可以遍历,方法有:add、delete、has
  • WeakSet
    • 成员都是对象
    • 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏
    • 不能遍历,方法有add、delete、has
  • Map
    • 本质上是键值对的集合,类似集合
    • 可以遍历,方法很多可以跟各种数据格式转换
  • WeakMap
    • 只接受对象作为键名(null除外),不接受其他类型的值作为键名
    • 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
    • 不能遍历,方法有get、set、has、delete

节流 防抖

防抖(连续的事件,停止触发后延迟触发)

  • 搜索框搜索输入。只需用户最后一次输入完,再发送请求
  • 手机号、邮箱验证输入检测
  • 窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。

    1. function debounce(fn, delay){
    2. let timer = null
    3. return function(){
    4. timer && clearTimeout(timer)
    5. timer = setTimeout(()=>{
    6. fn && fn.apply(this, arguments)
    7. }, delay )
    8. }
    9. }

    节流(间隔一段时间执行一次回调)

  • 滚动加载,加载更多或滚到底部监听

  • 谷歌搜索框,搜索联想功能
  • 高频点击提交,表单重复提交
    1. function throttle(fn, delay){
    2. let timer = null
    3. return function(){
    4. if(timer){ return }
    5. timer = setTimeout(()=>{
    6. fn && fn.apply(this, arguments)
    7. timer = null
    8. }, delay)
    9. }
    10. }

    函数 变量提升

    1. var b = 13
    2. function b(){
    3. b = 5;
    4. console.log(b);
    5. }
    6. b()

    P8

    1.简单做个自我介绍开始打断了我,嗯。百度的经历你不用介绍了,我在lBS6年,你的老大我很熟。
    2.我也来自我介绍下,我是阿里支付宝大前端负责人,我们的团队分工很多,你看你喜欢哪一个类型的。
    3.聊一下近一年大前端发展趋势,未来走向,如何关注的,然后请总结下端内前端的核心痛点,你怎么解决。
    4.刚听你说了很多,有性能有工程有基建。有几个核心的词我们分别聊一下吧。
    5.你说了JIT那内部DSL和外部DSL的本质区别是什么。你设计的话该考虑些什么,比如语法噪音?
    6.嗯 那内部DSL的解释器和编译器该如何设计呢?你对那种语言的相对了解呢。
    7.刚刚我们聊到C、LISP等,这里你能说下函数式编程的理解么?他是个数学问题么?
    8.听你说了很多,包括你聊到了SKI,那我们在往深了说C与LISP最最底层的核心本质区别是什么呢?
    9.刚刚你还说了 基于web ai。low code、node code 你能大概说下人工智能的权重、层、 逻辑回归、欠拟合与过拟合等核心的概念么。
    10.嗯,感觉你对AI确实了解的不深入还,那我们回归一个现实问题吧。假设给你时间去学习Tensorflow.js,完成组内今年的任务是根据同学们传递给你输入TSX代码,你如何通过AI知道他是一个什么样的组件?并生成代码 图片好说我们用CNN,代码呢?你用AST就可以么?
    11.嗯,你的思想还是对的。AI我们到这,对了你有系统学习过AI么?
    12.那AI吹得很响 最本质的目标是什么?那你觉得Tensorflow.js相对于Tensorflow最大坑是什么?
    13.好。我们回到端内,因为我们是支付宝。你觉得端内前端工程师的前途是什么?如果交给你来管理这个团队,你能为这个团队做什么。端内H5都有哪些技术形态,最迫切的问题是什么?
    14.为什么选择了创业?你有对外的产品么?这平时时间是怎么分配的。看到了你用了ServerLess。如果让你从头开发个对外的 ServerLess产品你如何做呢。
    15.最后你认为什么是大前端呢,到底什么才是技术深度,一个前端工程师的终极职业规划什么样。你来支付宝你希望做什么?
    16.我们的面试周期会非常漫长 应该会有6-8面。你能接受么?

js sleep

  1. function sleep1(delay) {
  2. var start = Date.now();
  3. while (Date.now() < start + delay);
  4. }
  1. const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time))

hash和history路由模式区别

hash和history的区别

  1. 触发hashChange事件 浏览器前进后退改变 URL、<a>标签改变 URL、window.location改变URL。

Vue的优点

vue框架的优点

require和import的区别

require和import的区别

用typescript写的代码执行的时候会更快吗?