前端技术摘要

前端技术总结罗列(持续更新中…)

1、localStorage、sessionStorage、cookie、session、indexDB… 跨页面共享策略?机制各有不同之处?如何跨页面通信?

postMessage、iframe、跨域策略、时效性…

图片1

session: 存于服务器端,安全,前端不可改,大小几乎不限制, 可存在于redis、SQL数据库中 可用于登录
cookie 4kb 不安全 默认20分钟 到期后会被清除 前后端都可设置 不设置的话自然会失效消失 可存储登录token 可被浏览器请求携带(默认携带跨域不携带),请求的时候带上凭证配置(credentials)<omit(不发送)、same-orgin(同源才发送 cookies)、include(必定全部发送)>: true server端也需要接受Access-Control-Allow-Credentials才可
sessionStorage 5MB+- 仅存在于当前会话页面 关闭后 会清除
localStorage 5MB+- 持久化存储 不主动清除不会消失
indexDB 大小默认50MB 不主动清除 除非手动 可以扩容 kv形式存储


2、React路由鉴权

包装组件后if return


3、策略模式

使用js策略模式优化代码


4、前端跨域问题解决

浏览器的一种在前端客户端进行的防护策略,规定必须同源同端口<协议、域名、端口>
解决<参考: 链接>:

  1. jsonp Script、jQuery、Axios
  2. 跨域资源共享(CORS)前后端共同实现 简单请求和非简单请求
  3. nginx反向代理接口跨域
  4. nodejs中间件代理跨域 使用node + express + http-proxy-middleware搭建一个proxy服务器
  5. node + vue|react + webpack + webpack-dev-server 进行proxy配置解决跨域访问资源 因为渲染和代理都在同一处不在跨域
  6. document.domain + iframe跨域 主域相同,子域不同的跨域应用场景 eg: weixin.qq.com和im.qq.com 2边都强制设置 document.domain = qq.com 即可,iframe子窗口则可以通过window.parent.xx获取主页面中的全局变量内容了
  7. location.hash + iframe跨域 不展开叙述
  8. window.name + iframe跨域 不展开叙述
  9. postMessage(data, origin) 主页面通过 iframeDom.contentWindow.postMessage('xx', '*')发送数据,子页面(iframe)通过window.onMessage监听即可, 也可以再发给父级页面 window.parent.postMessage, 父级页面再window.onMessage监听
  10. webSocket本身不存在跨域问题,所以我们可以利用webSocket来进行非同源之间的通信 socket.io前后端搭建ws服务实现跨页面数据传输 本质还是搭建服务器
  11. 使用chrome扩展插件解决跨域问题 针对特定浏览器内 解决跨域问题 因为本身扩展插件是不会被跨域策略拦截的 利用这一特性实现
  12. chrome版本号49之前的跨域设置 启动目标框里加上 --disable-web-security
  13. 前端chrome插件配置代理 + 后端接口代理请求服务实现

5、Js instanceof

用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上 参考URL
a instanceof A 用于检测 a是一个实例化对象 它的__proto__是否指向或者说是等于A的prototype


6、事件循环 任务队列 宏任务 微任务

  1. 同步任务 放入主线程中 eg: console.log(‘’);
  2. 异步任务 放入任务队列中(延迟性任务)
    • 宏任务 setTimeout、setInterval等(优先级低于微任务)
    • 微任务 Promise await async Object.observe MutationObserver process.nextTick(优先级最高)等
  3. 主线程先执行,然后微任务

7、http请求过程

  1. 浏览器输入网址DNS解析得到服务器IP,进行三次握手,建立TCP协议
  2. 客户端web浏览器向服务器发起请求,发送http请求和头信息发送
  3. 服务端应答客户端请求,响应头信息和浏览器所需内容
  4. 根据请求头信息是否包含keep-alive,决定是关闭TCP或保持TCP
  5. 最后客户端web浏览器得到服务端响应的结果

8、JSBridge原理

<参考: 链接>:

  1. 注入API 注入API,是通过WebView,向JavaScript的Context(window)上注入对象或方法,js直接调用注入的方法,即可执行Native的功能
  2. 拦截URL Scheme wx://ht/xxx?data=aa JS发送URL Scheme请求
  3. 重写 prompt 等原生 JS 方法

9、Vue、React优缺点比较 渲染执行机制 生命周期

  1. xxx

10、Vue2和Vue3区别

  1. vue2的双向数据绑定是利用了es5 的一个API Object.defineProperty() 对数据进行劫持 结合发布订阅模式来实现的。vue3中使用了es6的proxyAPI对数据进行劫持后处理。
  2. Vue2是Vue.js的早期版本,已经经过多年实践检验,是稳定的版本。Vue3是Vue.js的最新版本,在保留Vue2的优点的同时,提供了更多的新特性和改进。
  3. Vue2使用虚拟DOM来提高渲染性能,并提供了组件化的开发模式。Vue3在此基础上进一步优化了虚拟DOM的实现,并引入了编译器和运行时编译,使得渲染性能更高、代码更小、更灵活。
  4. Vue2提供了响应式系统和组件化的开发模式,支持使用插件扩展功能。Vue3在此基础上提供了更多的开发工具,如可观察对象和组件代码分离等,使得开发更快捷、更灵活。
  5. 总的来说,Vue3在保留Vue2的优点的同时,提供了更快的运行速度、更小的体积和更多的灵活性,可以更好地满足开发人员的需求。

11、React Fiber原理和作用

  1. 概念:
    • React Fiber是React执行渲染时的一种新的调度策略,因JavaScript是单线程的,一旦组件开始更新,主线程就一直被React控制,这个时候如果再次执行交互操作,就会容易导致卡顿,React Fiber就是来解决这个问题的
    • React Fiber就是通过对象的形式来记录组件上需要做或者已经完成的更新等详细信息,一个组件可以对应多个Fiber
    • React Element树在第一次渲染的时候会创建一颗结构一模一样的的Fiber节点树(Fiber是个链表,有child和sibing属性FiberTree)。不同的React Element类型对应不同的Fiber节点类型。一个React Element的工作就由它对应的Fiber节点来负责。
    • React Fiber这种方式,渲染过程采用切片的方式,每执行一会儿,就歇一会儿。如果有优先级更高的任务到来以后呢,就会先去执行,降低页面发生卡顿的可能性,使得React对动画等实时性要求较高的场景体验更好。
  2. 参考:

12、React高版本(v.18)新增的方法及作用

  1. React18 通过其改进的渲染系统带来了并发能力,严格模式更新(更严格)
  2. flushSync函数来强制立即刷新更改(异步变同步)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import { flushSync } from "react-dom";
    const handleClick = () => {
    flushSync(() => {
    setA(a => a + 1);
    });
    // Re-render
    flushSync(() => {
    setB(b => b - 1);
    });
    // Re-render
    };
  3. 并发的React, useTransition, 可以使用 useTransition() 钩子来创建一个 transition。这个钩子返回一个函数来启动一个 transition,还有一个挂起的指示器来通知你 transition 的进度。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import { useTransition, useState } from "react";
    const App = () => {
    const [isPending, startTransition] = useTransition();
    const [value, setValue] = useState(0);
    function handleClick() {
    startTransition(() => {
    setValue((value) => value + 1);
    });
    }
    return (
    <div>
    {isPending && <Loader />}
    <button onClick={handleClick}>{value}</button>
    </div>
    );
    };
  4. Suspense组件等

13、React Redux 原理

  1. action
    • 事件处理函数,有多个
    • action 是一个事件处理器,结构可以是switch case的模式 定义数据合成
    • store.dispatch() 将 action 传到 store
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      import { USER_INFO } from "../constants/actionTypes";
      import store from '../store/store'
      export const switchUser = (data) => {
      return dispatch => {
      // do somethings...
      dispatch({
      type: USER_INFO,
      payload: {
      ...data,
      }
      });
      }
      }
  2. reducer
    • Reducers 指定了应用状态的变化如何响应 actions并发送到 store 的
    • reducer函数主要接收2个参数,state和action switch action.type 做出state数据合成并返回处理后的
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      import { USER_INFO } from "../constants/actionTypes";
      const redUserInfo = (state = {
      userId: 1,
      userName: ''
      }, action) => {
      if (action === undefined) {
      return state
      }
      switch (action.type) {
      case USER_INFO:
      return {
      ...state,
      ...action.payload,
      }
      case USER_SET:
      return {
      ...state,
      ...action.payload,
      set: true,
      }
      default:
      return state
      }

      }
  3. store
    • redux 提供一个 createStore方法 传入 reducers 并返回新的store对象
    • code:
      1
      2
      3
      4
      import { createStore } from 'redux'
      import reducers from '../reducers/index'
      let store = createStore(reducers, ...initState)
      export default store
  4. Provider
    • Provider 其实就只是一个外层容器,它的作用就是通过配合 connect 来达到跨层级跨组件共享传递数据。
      1
      2
      3
      4
      5
      6
      import store from '../store/store'
      <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
      <About></About>
      </PersistGate>
      </Provider>
  5. connect
    • connect 的作用是连接React组件与 Redux store
    • 它接收上面 Provider 提供的 store 里面的 state 和 dispatch,并返回一个对象,以属性形式传给我们的容器组件的props对象内
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      import { connect } from 'react-redux';
      import { add, del, switchUser } from '../../store/action/numAction';
      function About(props) {
      const { add, del, item, switchUser } = props
      const test = () => { add(item) }
      const test1 = () => { del(item) }
      return (
      <div>
      <h3>我是About的内容</h3>
      <div>{item}</div>
      <button onClick={() => test()}>
      增加
      </button>
      <button onClick={() => test1()}>
      减少
      </button>
      </div>
      )
      }
      const mapStateToProps = state => {
      return { userInfo: { ...state.userInfo } }
      }
      const mapDispatchToProps = dispatch => {
      return {
      add(data){ dispatch(add(data)); },
      switchUser: data => dispatch(switchUser(data));,
      }
      }
      export default connect(mapStateToProps, mapDispatchToProps)(About);

14、发布订阅

  1. new event() on emit
  2. …

15、ES6新增内容

  1. 解构赋值,展开运算符(形参剩余量…rest),默认值定义,Promise异步方案,symbol数据类型,箭头函数
  2. ES6新增了数组操作方法includes、map、forEach、findIndex、filter、some、every、reduce、

16、判断当前web环境是否支持某个css属性

  1. 例如 判断是否支持css { position: sticky }CSS.supports("position", "sticky"): boolean
  2. …

17、Map和Object区别

  1. 键的区别(Map可以是任意数据类型,Object只能string,或者symbol)
  2. …

18、Map和Set区别

  1. 共同点

    1. 它们的内容都是可迭代对象
    2. 都有delete、has、clear等方法
  2. 不同点

    1. Map值是双元数组,key可以为任何数据,这点区别于普通Object只能是数字或字符串(Symbol)
    2. Set值是单个可迭代对象,key = value,因此可以直接add而不需要set(k, v)的方法

19、Js链式调用

概念: 所谓的链式调用,简单粗暴的讲,就是在一个实例对象调用完一个方法后,在后边可以一直去调用其他方法,例如,Promise.then()的方法就是一个很好的例子,他可以在后边一直的.then下去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/* 简单的链式调用 */
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
info() {
console.log(`${this.age}的${this.name}`);
return this
}
start() {
console.log('开始起床!');
return this
}
eat() {
console.log('开始吃饭');
return this
}
school() {
console.log('开始上学!');
return this
}
sleep() {
console.log('开始睡觉');
return this
}
}

const person = new Person('小红', 36);
person.info().start().eat().school().sleep();
// 36的小红
// 开始起床!
// 开始吃饭
// 开始上学!
// 开始睡觉

第二种,异步任务的处理方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/* 如果加上异步代码那该如何实现 */
function People(name) {
this.name = name;
// 创建一个数组模拟任务队列
this.queue = [];

// 用延迟器开启一个事件的总线
setTimeout(() => {
// 首次会调用next
console.log('首次会调用next')
this.next();
}, 0);

// 将this返回
return this;
}

People.prototype.next = function() {
// 将队列中的第一个任务找出
const fn = this.queue.shift();
// 如果有任务的话就进行调用
fn && fn();
}
People.prototype.sayHello = function() {
const fn = () => {
console.log('您好,我叫' + " " + this.name);
this.next();
}
// 将该任务添加到队列当中
this.queue.push(fn);
return this;
}
People.prototype.eat = function(time) {
const that = this;
const fn = () => {
setTimeout(function() {
console.log(`吃饭花费了 ${time}s`);
that.next();
}, time * 1000);
}

that.queue.push(fn);
return that;
}
People.prototype.taskList = function() {
const fn = () => {
console.log(this.queue);
}
this.queue.push(fn);
return this;
}

const people = new People('rex');
people.sayHello().eat(2).taskList().queue;

20、Set集合forEach遍历会导致无限循环问题

在调用forEach遍历Set集合的时候,如果其中1个值被删除后又被重新添加到集合,那么该访问会重新再来1次, 形成无限循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const set = new Set([1, 2])
const newSet = new Set(set);

console.log(set);
console.log(newSet);

newSet.forEach((item: key值, index: <索引不是从0开始 而是 === item(key值)>, s: Set) => {
console.log(set);
console.log(item, index, s)
newSet.delete(2);
// newSet.add(2);
newSet.add(new Date().getTime());
console.log('我在遍历');
});
console.log(newSet);
console.log(set);

21、Js柯里化

柯里化是一种函数的转换。是指将一个函数从可调用的 f(a, b, c) 转换为 f(a)(b)(c)。

解决<参考: 链接>:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function sum(num) {
//function s(innerVar){
//num += innerVar;
// arguments.callee 是一个指向正在执行的函数的指针,因此可以用它来实现对函数的调用,使用 arguments.callee 代替函数名,可以确保无论怎样调用函数都不会出问题。
// 注意点: "use strict" 下 不可用 使用下面写的第二种
//return arguments.callee;
//}
// 不过,可以使用命名函数表达式来达成相同的结果
// 以上代码创建了一个名为 f()的命名函数表达式,然后将它赋值给变量 s。即便把函数赋值给了另一个变量,作用域内函数的名字 f 仍然有效
const s = (function f(innerVar){
num += innerVar;
return f;
});
s.toString = function(){
return num;
}
return s;
}
console.log( sum(3)(2)(1)(4) );
alert(sum(3)(2)(1)(4));
console.log( sum(1) )
console.log( sum(1) * 1 )

解析:
s function 主要用来保存数据的累加 并 返回函数 让其能够继续被调用
s.toString 主要用来实现静态方法,能够访问函数内部作用域内的变量(闭包特性)
最后返回s方法
sum(1) ===> 会把num = 1放到toString中返回,没有调用到s
sum(1)(2) ===> 运行了s函数(会把2放到s函数的位参innerVar位置),然后对num进行累加处理, num = num + innerVar, 且继续返回成可调用的s方法,接着s.toString会返回闭包中的最新num结果,以供静态调用(例如当函数进行比较时运算会调用toString方法,<数据基本类型比较时可能会发生转换, 具体可参考资料>),例如alert,或者console.log( sum(1)(2) * 1 ) 使之发生数据转换得到静态化的结果值,实现功能


22、Js函数的指针(this)

普通函数、箭头函数<()=>{}>的指针

1、只有一个参数的时候,参数可以不加小括号,没有参数或2个及以上参数的,必须加上小括号
2、返回语句只有一条的时候可以不写{}和return,会自动加上return的,返回多条语句时必须加上{}和return
3、箭头函数在返回对象的时候必须在对象外面加上小括号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 'use strict';
// 1、普通函数this指向:
// 指向它的调用者,如果没有调用者则默认指向Window
// 2、箭头函数指向:
// 指向箭头函数定义的时候所处的对象,而不是其所使用的时候所处的对象,默认指向父级的this
// 综上:箭头函数没有自己的this,它的this是继承来的,默认指向它定义的时候所处的对象
const fMap = {
name() {
// this = 对象本身fMap
console.log(this);
},
age: (e) => {
// this = window
console.log(this);
},
sex: function() {
// this => fMap
this.xx = 'xx';
function the4() {
// this => window
console.log(this);
};
const the5 = () => {
// this => sex的this = fMap
console.log(this);
}
the4();
the5();
}
}
fMap.name();
fMap.age();
fMap.sex();
// 定义构造函数 如果函数是构造函数,那this将指的新的对象
function Persion() {
// this指向p
this.age = 0;
console.log(this);
setInterval(function () {
// this 指向Window
this.age++;
console.log(this);
}, 1000);
setInterval(() => {
// ★ 箭头函数从自己的作用域链上一层继承的this,所以这个this也指向p
this.age++;
console.log(this);
}, 1000);
}
// Persion();// this -> window
const p = new Persion(); // this => p;
p.name = 'rex';
console.log(p);

总结:

  • 普通function函数this指向:

    • 指向它的调用者,如果没有调用者则默认指向Window
  • 箭头函数this指向:

    • 指向箭头函数定义的时候所处的对象(默认指向父级的this)
    • 如果没有父级程序 或者 父级程序没有指向 箭头函数的this指向是window
    • 箭头函数没有自己的this,它的this是继承来的
  • 箭头函数和普通函数的区别

    • 箭头函数是匿名函数,普通函数可以是匿名函数也可以是具名函数
    • 箭头函数不能作为构造函数使用,不能使用new关键字
    • 箭头函数没有原型,所以没有prototype属性
    • call、apply、和bind无法改变箭头函数的this指向,但可以改变普通函数的this指向
    • 箭头函数没有arguments对象,如果有外层函数,则继承外层函数的arguments,没有外层函数则会报错,箭头函数用的是rest参数(形式为: …rest)
    • 箭头函数没有Generator,不能使用yield关键字
    • 箭头函数没有自己的this

23、Vue3 composition API(组合式API)

  1. 类似于React的HOC高阶组件、Hooks
  2. 将组件的功能抽象为函数或对象的形式, 提高组件的可复用性和可维护性

24、函数式编程

  1. xxx
  2. …

25、CI/CD

代码-编译-多环境测试部署-生产-回滚
<参考: 链接>:

  1. 持续集成(CI)
  2. 持续部署(CD)

26、DevOps

DevOps是一种思想,是一种文化,主要强调软件开发测试运维的一体化,目标是减少各个部门之间的沟通成本从而实现软件的快速高质量的发布。CI/CD是指持续集成发布部署,是一套流程实现软件的构建测试部署的自动化。DevOps与CICD紧密相关,是理论与实践的结合,DevOps要实现人员一体化,必须要借助CICD工具来自动化整个流程。

  1. xxx
  2. …

27、ES6 Class的get set

get set static

不要和this.xx重名

  1. get vars(): string|xx —> 只读 类似于 computed 返回值
  2. set vars(value): void —> 同computed 可以监听数据变动做出适当的相应
  3. static method(): void —> 定义静态方法, 不能在类的实例上调用静态方法, 而应该通过类本身调用。

28、Object.defineProperty

  1. Object.defineProperty(对象, '某成员属性', { ...配置项 })

  2. 主要配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {
    writable: 是否可重写

    value: 当前值

    get: 读取时内部调用的函数

    set: 写入时内部调用的函数

    enumerable: 是否可以遍历

    configurable: 是否可再次修改配置项
    }

29、前端性能优化(多角度)

  1. 加载优化
  2. CSS优化
  3. 图片优化
  4. 脚本优化
  5. 渲染优化
  6. …

30、SSR(NEXT.js、NUXT.js)

  1. Vue、React服务端渲染 Server Side Rendering
  2. …

31、算法: 冒泡排序

  1. xxx
  2. …

32、求2个数组之间的【交集】【并集】【差集】

  1. xxx
  2. …

33、Webpack、Vite、Rollup工作原理

  1. Webpack 需要查找依赖,打包所有的模块,然后才能提供服务,更新速度会随着代码体积增加越来越慢

    1. 解析入口文件搜集依赖形成抽象语法关系树AST
    2. 接着解析AST加载模块,根据不同模块使用加载器进行处理
    3. 编译模块,变成可执行的js代码
    4. 打包输出(1或者多个文件 可以是css、js、等)
    5. 优化和插件系统(混淆、加密等)
  2. Vite 使用原生 ESModule 通过 script 标签动态导入,访问页面的时候加载到对应模块编译并响应

  3. 项目打包的时候最终还是需要打包成静态资源的,打包工具 Rollup


34、React组件重新渲染(刷新)

<参考: 链接>:

  1. 类组件 this.forceUpdate();

  2. 功能组件 函数组件:

    1. 调用App.render

    2. 用自己的新实例替换状态对象setUser({ ...user });

    3. 让一个空的状态变量触发更新

      1
      2
      const [, updateState] = React.useState();
      const forceUpdate = React.useCallback(() => updateState({}), []);

35、Keep-alive 缓存组件的一种解决方案

<参考: 链接>:

  1. vue 内置组件 keep-alive动态缓存方案

  2. 在组件切换过程中将状态保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性

  3. keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。一般用法为下

  4. 例如路由,按需设置,性能达到最为均衡的状态

  5. 可以结合router来处理路由页面的缓存,在router中设置router的元信息meta : keepAlive: true

    • <keep-alive> 包裹组件(路由) 进行缓存
      1
      2
      3
      4
      5
      <keep-alive>
      <router-view v-if="$route.meta.keepAlive"></router-view>
      </keep-alive>
      // 这里不需要缓存
      <router-view v-if="!$route.meta.keepAlive"></router-view>
    • 路由配置
      1
      2
      3
      4
      5
      6
      7
      {
      path:"/list",
      component:HomeBookList,
      meta: {
      keepAlive: true // 需要缓存
      }
      }
  1. keep-alive支持三个属性,分别是

    1. include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
    2. exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
    3. max - 数字。最多可以缓存多少组件实例。
  2. 通过设置: include = 变量数组, 然后监听路由 watch(to, from),判断去到指定的路由的时候,改变这个变量数组,去掉缓存即可,

  3. keep-alive 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated

    1. activated在缓存组件激活时调用
    2. deactivated在缓存组件失活时调用,取代原本的destroyed

36、路由守卫

  1. 全局守卫 router/index.js
    1. router.beforeEach(to, from, next) 全局前置路由守卫,每次路由切换之前被调用,可以做**权限拦截 **需要调用next()
    2. router.afterEach(to, from) 全局后置路由守卫,每次路由切换之后调用,注意没有next 可用于切换document.title
    3. router.beforeResolve 和 beforeEach类似 也属于全局守卫 区别是 在所有组件内守卫和异步路由组件被解析之后 导航确认之前 会调用
  2. 独享守卫 router/index.js
    1. router.beforeEnter(to, from, next)针对某个路由单独设置的守卫 独享路由守卫,只有前置,没有后置,写在routes配置项里,路由进入之前会调用
  3. 组件内守卫 Demo.vue
    1. router.beforeRouteEnter(to, from, next) 在渲染该组件的对应路由被 confirm 前调用 不可以访问本组件this对象 但是可以在next回调首个形参里面得到vm来访问this, 然后可以return false 来做拦截
    2. router.beforeRouteUpdate(to, from, next) 在当前路由改变,且该组件被复用时调用 eg: /item/:id 之间跳转的时候 会被调用 切换路由动态参数的时候
    3. router.beforeRouteLeave(to, from, next) 导航离开该组件的对应路由时调用

37、闭包

  1. 什么是「闭包」。

    • 「函数」和「函数内部能访问到的变量」的总和,就是一个闭包。
  2. 「闭包」的作用是什么。

    • 可以用来封装插件、柯里化…
  3. 闭包副作用

    1. 内存泄漏 -> 坑没空着的了
    2. 内存溢出(扩展内容非闭包导致) -> 一个坑2个人爆炸了

38、JavaScript中window.onload和document.onload有什么区别?

<参考: 链接>:

  1. document.onload

    • 在加载图像和其他外部内容之前将其触发。文件。在window.onload之前触发onload事件。
  2. window.onload

    • 加载整个页面(包括图像,脚本,css等)完成时,它将被触发。
  3. $(document).ready() || DOMContentLoaded

    • 当页面 DOM 加载完成后,ready() 里的函数便会立即执行
  4. document.onload 和 document.ready

    1. onload 文档元素加载完毕后执行 只能执行一次 会覆盖 需要等待所有资源全部加载完成,
    2. ready 或者 DOMContentLoaded 不会覆盖 都会执行 执行顺序在load之前,只需等待DOM树结构完成 即可触发
  5. document.body.onload

    1. 需要在body标签内引用才有效,body内容加载完成
  6. 总结:

    1. 使用window.onload 和 DOMContentLoaded 即可
    2. DOMContentLoaded - load - beforeunload - unload
    3. DOMContentLoaded 只能用 addEventListener

39、JavaScript中事件委托(代理)的作用 JQ bind和on的区别 addEventListener和普通on区别 捕获顺序 冒泡顺序 怎么取消冒泡

事件委托是利用事件冒泡,只指定一个事件处理程序来管理某一类型的所有事件

  1. delegate 执行事件委托 指定元素类型

    • ul.delegate(li, clickFunc)
  2. bind不能给不存在的元素绑定例如click事件 on可以代理也可以给未来的元素添加 bind只能给符合条件的元素本身添加事件

  3. 捕获行为是由上往下,而冒泡阶段是自下往上

  4. 事件流的三个阶段:冒泡阶段、捕获阶段、目标阶段

  5. on会被覆盖 addEventListener(>=IE9) 不会被覆盖


40、Webpack、Vite 工作流程

  1. xxx
  2. …

41、Js call apply bind 区别

  1. xxx
  2. …

42、Vue代码优化(下文续有性能优化)

  1. Object.freeze()
  2. 减少ES6代码,降低webpack编译的代码量

43、flex布局、多端兼容、响应式布局

  1. rem原理 计算方法

    1
    2
    3
    4
    const b = window.devicePixelRatio//dpr
    const c = '某个自己设置的值,我们公司为375px' //切图稿的统一宽度
    const d = document.documentElement.clientWidth//视口宽度
    const e = '某个自己设置的值,我们公司为37.5px' //根元素font-size大小

    第一种方法是将px转化成rem再除以e

    第二种方法非要用px作单位的话就把值除以c再乘以d

  1. webpack 插件 npm install px2rem-loader lib-flexible --save-dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
// ...
module: {
rules: [{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader', {
loader: 'px2rem-loader',
options: {
remUni: 75, // 适合750的设计稿 1rem = 75px
remPrecision: 8 // px转rem小数点保留的位置
}
}]
}]
}
}

在项目中引入lib-flexible

<script src="lib-flexible/flexible.js"></script>

lib-flexible应最早执行

这样就可以实现PX到REM单位的自动转化了~

  1. rem 计算原理:
    1. const w = 计算当前视口宽度(document.documentElement.clientWidth) eg: 375px
    2. const designWidth = 750px
    3. const dzr = w / designWidth eg: 0.5 // 设备缩放比
    4. 给html设置font-size = dzr => // 0.5
    5. 设计图 10px => 10px * dzr = 5px
    6. 设计图 12px => 12px * dzr = 6px
    7. dzr = 0.3
    8. 设计图 *12px => 12px * 0.3 = 3.6px*
    9. 非常不好计算, 因此可以在初始的时候 放大100倍 利于计算
    10. 给html设置font-size = dzr * 100 = 30px
    11. 设计图 12px => 12px * 0.3 * 100 = 360px
    12. 1rem = 0.3 x 100 x 1px = 300px
    13. 12px = (1rem / 100) * 12
    14. 0.12rem
    15. 总结: 先计算1个单位像素的比例 html: 1个单位 = 缩放比,1rem = 缩放比 x 单位量
    16. 但是方便计算 会倍率 否则rem: 0.3 x 12px,不利于计算
    17. 因此先x100 html set 缩放比*100 再除100 就是 rem: 1个html x 12 相当于12个单位的量
    18. rem = 12 / 100 = 0.12rem 因为整体x了100倍 所以直接除以100即可

44、created、mounted和this.$nextTick()的区别

  1. created: 页面渲染成htlm之前运行 可以做一些初始化操作 生命周期运行1次
  2. mounted: 页面渲染成html后运行 可以操作一些静态dom元素 生命周期运行1次
  3. this.$nextTick(fn) fn中可以对动态dom进行操作 生命周期运行多次

45、浏览器垃圾回收的基本算法

  1. 引用计数法,为0时,释放 有缺陷: 互为引用
  2. 标记清除法,先标记 后 回收, 从根部[globalThis]找有无使用处,无则回收

46、vue2性能优化

解决<参考: 链接>:

  1. Deferred 延迟 分批渲染
  2. xxx

47、websocket

  1. soket.io
  2. xxx

48、Web Worker

  1. 浏览器提供的一种通信方式(API),由主线程main script 和 worker script进行通信的 有利于性能提升
  2. xxx

49、算法

  1. 二分法排序
  2. 冒泡排序
  3. xxx

50、原型链的理解

  1. 原型链的理解prototype
  2. xxx

51、Vue组件重新渲染(刷新)

  1. :key 更改会刷新
  2. v-if 会刷新
  3. main.js 使用 Vue.forceUpdate()后 组件中使用this.$forceUpdate
  4. 刷新页面有路由的话 刷当前 this.$router.go(0)、 返回上一页 go(-1) 去另外的路由页 push(‘/login’) 或 push({ name: ‘login’ })

52、在多个文件中import同一个文件,webpack会多次打包吗

  1. 不会
  2. 不同文件中多次import同一个文件,webpack并不会多次打包,只会在打包后的文件中会多次引用打包后的该文件对应的函数。
  3. 参考

53、React高阶组件HOC

  1. 实际上就是一个函数或者一个类 接收一个组件作为参数 然后生成新的组件 其中包含了参数组件体
  2. …

54、TypeScript中type与interface的区别

  1. interface用于定义一个新的对象的结构跟类型
    type用于给一个已存在的对象取名或者赋予别名

  2. interface可以重复定义一个对象,最终的结果是合集的状态

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    interface Person {
      name: string
      age: number
    }

    interface Person {
      address: string
    }

    type Person2 = {
      name: string
      age: number
    }

    type Person3 = Person2 & {
      address: string
    }
  3. type可以定义联合类型,比如:

1
type language = 'cn'|'en'|'jp'

55、JWT是什么?

  1. 是一种用于在前端web端进行认证和授权的令牌机制、在前端存储传递 json web tokens 来实现用户身份校验和访问控制
  2. header、payload、signature,三段式.隔开
  3. http请求头可带上、cookie也可以带上、?token也可带上

56、简述HTTP

  1. 主要分为以下7步骤:DNF->IP 建立TCP、发起请求、服务端接收处理请求、服务端返回结果、客户端接收结果、客户端处理结果、关闭连接,复杂情况还需要加上重定向等
  2. 三次握手 1客户端发给服务端带有一把确认的钥匙,2服务端收到了你的钥匙并给你客户端发起一个确认,3.客户端从服务端得到了钥匙的确认信息