跳到主要内容

yifangda

js

  • 数组的定义写法正确的是
    • var arr = new Array(1, 2, 3) // [1, 2, 3]
    • var arr = new Array(3) // [undefined, undefined, undefined]
    • var arr = new Array('1', '2') // ['1', '2']
    • var arr[] = new Array(2)(3)// Uncaught SyntaxError: Unexpected token '['

html

鼠标移动到button上,然后点击,这个过程发生了一连串的什么事件

btn mouseover -> mouseenter: hover -> mousedown: active

<!DOCTYPE html>

<head>
<style>
#app {
width: 100%;
height: 100vh;
background-color: red;
}

#btn:active {
background-color: blue;
}

#btn:hover {
background-color: yellow;
}
</style>
</head>

<body>
<div id="app">
<button id="btn">Click me</button>
</div>
<script>
var btn = document.getElementById('btn');

btn.addEventListener("click", function () {
console.log("Button clicked");
});

btn.addEventListener('mouseenter', function () {
console.log('mouseenter: hover');
});

btn.addEventListener('mouseover', function () {
console.log('btn mouseover');
})

btn.addEventListener('mousedown', function () {
console.log('mousedown: active');
})

</script>
</body>

</html>

css 定位

  • fixed 始终基于 body 定位吗?

    • position: fixed的元素是相对于视口(viewport)定位的
     <!DOCTYPE html>
    <html>
    <body>
    <div id="container">
    <div id="fixed">
    fixed
    </div>
    </div>
    </body>
    <style>
    #container {
    width: 200px;
    height: 200px;
    background-color: red;
    position: absolute;
    bottom:0;
    }

    #fixed {
    position: fixed;
    top: 0;
    right: 0;
    width: 50px;
    height: 50px;
    background-color: antiquewhite;
    }
    </style>
    </html>
  • relative 相对定位,移动后,不再占据原来的位置?

    • 相对定位的元素,移动后,仍然占据原来的位置,只是视觉上移动了,不会影响其他元素的位置。

Vue

  • vue 在哪个生命周期中无法获取 data: {} 中的数据: beforeCreate
  • vue 通过 provide 和 inject 可以实现爷孙组件通信
  • vue 通过 $emit 来实现触发父组件的事件
  • vue 通过 pros 来传递数据给子组件

Vue2.x object.defineProperty 如何做到 Tree Shaking 的

Vue2 没有静态标记,对 Tree Shaking 并没有过多的优化

Tree Shaking 原理

  • DCE(Dead Code elimination): 无用代码消除、
    • rollup + uglify
    • webpack + uglify
  • 依据 ES6 模块特性
    • 可以函数消除
    • 不可以类消除
  • Closure Compiler

Tree Shaking 的局限性

  • 只能处理静态的 ES6 模块: 像 CommonJS 的 require() 是动态的,编译时无法确定其依赖关系,所以无法被彻底 Tree shaking。

参考

Vue3 比 Vue2 快在哪里

Vue3 与 Vue2 的区别

  • Vue3使用 Composition API,Vue2使用 Options API
    • Options API: 通过不同的选项来组织代码,如:data, methods, computed, watch等。使用 mixins 来做代码的复用
      • mixins 带来的问题:mixins 元素多之后,会命名冲突,数据来源不清晰,代码不易维护
      • this 上下文的写法
    • Composition API: 组合优于继承的思想,通过组合不同的函数来实现代码的复用,通过 setup 函数来组织代码
      • setup 函数中的 this 指向 undefined,需要使用 this 的话,需要通过 getCurrentInstance() 来获取当前组件的实例

Vue3 比 Vue2 快在哪里

  • Vue3 使用 Proxy 代替 Object.defineProperty

    • Object.defineProperty 会在初始化的时候,就做深层递归
    • 使用 Proxy 会在访问数据的时候,才做深层递归,只有访问到的数据才具有响应式
    const reactive = new Proxy(obj, {
    get(target, key) {
    // do something
    if(typeof target[key] === 'object') {
    return reactive(target[key])
    }
    },
    set(target, key, value) {
    // do some,thing
    },
    deleteProperty(target, key) {
    // do something
    }
    }
  • PatchFlag 标记静态节点,减少不必要的 dom 操作

  • HoistStatic

    • 静态提升,将静态节点提升到 render 函数外部,减少 render 函数的执行次数
    • 合并相邻静态节点: 当静态节点数量达到一定程度时,会将相邻的静态节点合并成一个静态节点,减少静态节点的数量(类似编译器预解析的原理)
    • 拿空间换时间
  • CacheHandler 缓存事件处理函数,减少不必要的事件处理函数的创建

  • Tree Shaking: 编译时,根据不同的情况(v-if, v-model, input等存在的情况),引入不同的API

  • SSR 优化: 静态节点直接输出,绕过 vdom

  • Vue3 对 Ts 的支持更加了友好

为什么要使用 VDom

  • 跨平台,如:web, weex, 小程序等
  • 虚拟 dom 可以实现 diff 算法,减少 dom 操作,提高性能

Vue3 生命周期: beforeCreate -> created -> beforeMount -> mounted -> beforeUpdate -> updated -> beforeUnmount -> unmounted

Vue2 生命周期: beforeCreate -> created -> beforeMount -> mounted -> beforeUpdate -> updated -> beforeDestroy -> destroyed

前端页面停留1小时后点击,后端返回 401, vue 如何处理此种情况的超时?在增删改查这些操作都有可能出现此种情况

通过拦截器(interceptors)来拦截请求和响应并处理不同状态码的情况, 通过路由守卫来处理登录状态失效或者未登录的情况。

  1. 通过 axios 的响应拦截,处理不同状态码的情况。
axios.interceptors.request.use(config => {
// do something before request is sent
return config
}, error => {
// do something with request error
return Promise.reject(error))
})

axios.interceptors.response.use(response => {
// 处理响应数据
return response
}, error => {
// 处理401的情况
if (error.response.status === 401) {
// 跳转到登录页面
router.push('/login')
}
return Promise.reject(error)
})
  1. 通过路由守卫处理登录状态失效,跳转到登录页面
router.beforeEach((to, from, next) => {
// 判断是否登录
const isAuthenticated = checkAuthenticated()
if (to.path !== '/login' && !isAuthenticated) {
next('/login')
} else {
next()
}
})

编程

通过父组件和子组件 实现在父组件中输入框输入一个数字n, 在子组件中渲染一个表格, 表格有n行n列

123
654
789
  1. 父组件有输入框
  2. 子组件检查输入的数字是否合法 合法范围为[0, 10], 如果不合法,将父组件中的背景颜色设置为红色
  3. 子组件渲染表格
<!-- vue2.x的实现方式 -->
<!-- Cube 子组件-->
<template>
<div>
<p>父组件num: {{ num }}</p>
<table class="bordered-table">
<tbody>
<tr v-for="(row, index) in tableData" :key="index">
<td v-for="(val, j) in row" :key="j">{{ val }}</td>
</tr>
</tbody>
</table>
</div>
</template>

<script>
export default {
name: 'Cube',
props: {
num: {
type: Number,
required: true,
default: 0,
},
},
data() {
return {
tableData: [],
}
},
watch: {
num(newValue) {
const newValueNum = parseInt(newValue)
if (isNaN(newValueNum)) {
return
}
if (this.checkNum(newValueNum) === false) {
this.$emit('error', '请输入符合条件的数字: 0-10')
} else {
this.$emit('pass')
}
this.setTableData(this.genTableData(newValueNum))
},
},
created() {
this.setTableData(this.genTableData(this.num))
},
methods: {
setTableData(data) {
this.tableData = data
},
genTableData(newValue) {
const newValueNum = parseInt(newValue)
if (isNaN(newValueNum)) {
return []
}
const tableData = []
for (let i = 0; i < newValueNum; i++) {
const tempArr = []
if ((i + 1) % 2 === 0) {
for (let j = newValueNum; j > 0; j--) {
tempArr.push(i * newValueNum + j)
}
} else {
for (let j = 1; j <= newValueNum; j++) {
tempArr.push(i * newValueNum + j)
}
}
tableData.push(tempArr)
}
return tableData
},
checkNum(newValue) {
const newValueNum = parseInt(newValue)
if (newValueNum < 0 || newValueNum > 10) {
return false
}
return true
},
},
}
</script>

<style scoped>
.bordered-table {
border-collapse: collapse;
border: 1px solid #eee;
}

.bordered-table td {
border: 1px solid #fff;
padding: 10px;
}
</style>

<!-- CubeParent 父组件 -->
<template>
<div id="cube">
<input v-model="num" />
<p>{{ msg }}</p>
<Cube :num="num" @error="error" @pass="pass" />
</div>
</template>

<script>
import Cube from './Cube.vue'

export default {
name: 'CubeParent',
components: {
Cube,
},
data() {
return {
num: 5,
msg: '',
}
},
methods: {
error(msg) {
this.msg = msg
document.getElementById('cube').style.backgroundColor = 'red'
},
pass() {
this.msg = ''
document.getElementById('cube').style.backgroundColor = 'yellow'
},
},
}
</script>

<style>
#cube {
background-color: yellow;
}
</style>
<!-- vue3.x的实现方式 -->
<!-- Cube 子组件-->
<template>
<p>父组件num: {{ num }}</p>
<table class="bordered-table">
<tbody>
<tr v-for="(row, index) in tableData" :key="index">
<td v-for="(val, j) in row" :key="j">{{ val }}</td>
</tr>
</tbody>
</table>
</template>

<script>
import { watch, ref, onMounted, getCurrentInstance } from 'vue'

export default {
name: 'Cube',
props: {
num: {
type: Number,
required: true,
default: 0
}
},
setup(props) {
const tableData = ref([])
const instance = getCurrentInstance()

watch(() => props.num, (newValue) => {
const newValueNum = parseInt(newValue)
console.log('newValueNum :', newValueNum, 'newValue: ', newValue)
if (isNaN(newValueNum)) {
instance.emit('error', '请输入符合条件的数字: 0-10')
return
}
if (checkNum(newValueNum) === false) {
// 触发父组件的事件
instance.emit('error', '请输入符合条件的数字: 0-10')
return
} else {
instance.emit('pass')
}

setTableData(genTableData(newValueNum))
})

const genTableData = (newValue) => {
const newValueNum = parseInt(newValue)
console.log('newValueNum :', newValueNum)
if (isNaN(newValueNum)) {
return []
}
return new Array(newValueNum).fill(0).map((_, index) => {
const tempArr = Array.from({length: newValueNum}, (val, j) => {
const base = index * newValueNum
return (index+1) % 2 === 0 ? base + newValueNum - j : base + j + 1
})
return tempArr
})
}

const setTableData = (data) => {
tableData.value = data
}

const checkNum = (newValue) => {
const newValueNum = parseInt(newValue)
// console.log('newValueNum :', newValueNum)
if (newValueNum < 0 || newValueNum > 10) {
return false
}
return true
}

onMounted(() => {
console.log('Cube mounted: ', props.num)
setTableData(genTableData(props.num))
})

return {
tableData,
}
},
}
</script>

<style scoped>
.bordered-table {
border-collapse: collapse;
border: 1px solid #eee;
}

.bordered-table td {
border: 1px solid #fff;
padding: 10px;
}
</style>


<!-- CubeParent 父组件 -->
<template>
<div id="cube">
<input v-model="num" typ="number"/>
<p>{{ msg }}</p>
<Cube :num="num" @error="error" @pass="pass"/>
</div>
</template>

<script>
import Cube from './Cube.vue';
import { ref } from 'vue';

export default {
name: 'CubeParent',
components: {
Cube
},
setup() {
const num = ref(5)
const msg = ref('')
const error = (_msg) => {
console.log('error: ', _msg, 'msg: ', msg)
msg.value = _msg
document.getElementById('cube').style.backgroundColor = 'red'
}
console.log('msg: ', msg)

const pass = () => {
msg.value = ''
document.getElementById('cube').style.backgroundColor = 'yellow'
}

return {
num,
msg,
error,
pass
}
},
}
</script>

<style>
#cube {
background-color: yellow;
}
</style>

yaofangwang

渲染流程 和 光栅化流程

  • 如何触发光栅化的
    • GPU 渲染
  • 如何触发层叠上下文
    • 明确定位属性:z-index(只对指定了 positioned 属性的元素有效)
    • 明确定位属性:position: absolute, fixed,float
    • 定义透明,滤镜等属性:filter,
    • CSS 滤镜的元素:opacity(opacity < 1)
    • 3d 动画:transform: translate3d(x, y, z)
    • 是 flex 和 grid 容器的子元素,并且 z-index 值不为 auto

代码演示

<!DOCTYPE html>
<html>
<style>
#app {
width: 100%;
height: 10vh;
background-color: red;
}

#layer1 {
/* 产生层叠 */
/* position: fixed; */
/* right: 0; */

/* position: absolute;
top: 100px; */
/* z-index: 1; */

position: relative;
z-index: 5;


background-color: aqua;
width: 300px;
}

#layer2 {
position: relative;
top: -50%;
z-index: 1;
background-color: yellow;
width: 300px;
}


#layer3 {
/* 因为独立层出来后,才能对其进行滤镜处理 */
filter: grayscale(100%);
background-color: green;
}

#translate3d-layer {
transform: perspective(500px) rotateY(45deg) translate3d(100px,50px,0px);
background-color: rgb(125, 125, 216);
}

/* 透明度, 形成层得上下文,整体才能做透明度的处理, opacity 属性值要小于1才会独立成层 */
#opacity-layer {
opacity: 0.5;
background-color: rgb(125, 125, 216);
}

#flex-container {
display: flex;
}

/* flex 子元素,且 z-index 不为 auto,形成层叠上下文*/
#flex-container div {
z-index: 0;

margin-top: 100px;
width: 100px;
height: 100px;
margin-top: 100px;
background-color: aqua;
}
</style>

<body>
<div id="app">
<div id="layer1">
layer1
</div>

<div id="layer2">
layer2
</div>

<div id="layer3">
layer3
</div>

<div id="translate3d-layer">
translate3d-layer
</div>

<div id="opacity-layer">
opacity-layer
</div>

<div id="flex-container">
<div id="flex-item1">flex-item1</div>
<div id="flex-item2">flex-item2</div>
<div id="flex-item3">flex-item3</div>
</div>
</div>
</body>

</html>

可以通过 Edge 来查看层叠上下文

  • 项目中的性能优化,有没有量化的指标

  • 浏览器缓存

    • HTTP 缓存
    • SessionStorage
    • localStorage

quanpengyou

1.有大型网站的前端架构设计经验;

2.熟练掌握Web前端技术,如HTML5、JavaScript或TypeScript、CSS3等

4.熟悉模块化、前端编译和构建工具,如Yarn、Webpack、Gulp等

3.掌握基本IOS/安卓开发,有已上线的项目经验; react native 和 flutter 的区别

5.了解XSS、CSRF等前端安全相关知识,并有相应的处理经验;

kaiyuanzhongguo

1、前端架构设计与开发; 2、制定前端技术架构标准与开发规范,实现多产品的前端技术融合与对接; 3、带领前端工程师完成前端研发任务,解决遇到的关键技术难题; 4、在前端技术及架构先进性,推进前端工程化、自动化和工具化建设,确保前端交付成果质量。

任职资格: 3、JavsScript、CSS、Html功底扎安,精通前端対览器渲染机制; 4、掌握主流前端框架特点、差异性及适用场景,精通 Reactjs 框架,或精通vue且有意愿转为react; 5、掌握NodeJs、WebPack、Gulp等前端环境与构建工具,对前端工程化有深入的理解; 6、精通前端安全処理Helment、XSS防御、CSP、ETag、session泄露; 7、沟通能力、团队协作能力,积极主动,责任心强,对技术有更高的追求且乐于分享。

  • react 的原理
  • webpack 的热更新原理
  • 实现 webpack loader,将 md 文件转为 html 文件
  • 常用的 ES6 和 TS 有哪些
  • 实现扁平化函数
const flattenDeep = (arr) => {
return arr.reduce((acc, currentVal) => {
return Array.isArray(currentVal) ?
acc.concat(flattenDeep(currentVal)) : acc.concat(currentVal);
}, []); // 注意这里传入了一个空数组作为初始值
}

console.log(flattenDeep([1, [2, [3, [4]], 5]]));
// 输出: [1, 2, 3, 4, 5]
  • http 和 websocket 有什么不同
  • http 和 https 有什么不同
  • es5 和 es6 的继承有什么区别
  • proxy 跟普通的对象有什么不同
  • 为什么在react class 组件中,需要绑定 this, 普通函数需要,箭头函数不需要
    • 函数的执行上下文(execution context)决定了this的值。当我们将一个函数作为事件处理函数传递给DOM元素时,该函数将在全局作用域或undefined作为执行上下文,而不是组件实例。

quanpengyou

quanpengyou 二

  • 流媒体

  • C端客服,和服务器通信的方式除了http 和websocket还有什么方式

    • 实现会话通信和流媒体实时通讯
      • WebRTC(Web 实时通信):是一个支持网页浏览器进行实时语音对话或视频对话的技术,是一个支持网页浏览器进行实时语音对话或视频对话的技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立点对点(Peer-to-Peer)的连接,实现浏览器之间的音视频通信。
      • WebSocket:是一种在单个 TCP 连接上进行全双工通信的协议。
      • RTMP(Real Time Messaging Protocol 实时消息传输协议):是一种实时流传输协议,基于 TCP 协议实现,主要用于流媒体服务器和 Flash 客户端之间进行音视频和数据通信。
  • 设计模式

    • 观察者模式

      • Vue Object.defineProperty() 实现数据劫持
      • Vue EventBus: $emit 发布、$on 订阅、$off 取消订阅
    • 单例模式

      • 全局状态 Store: Vuex、Redux
      • 全局 Model
      • 全局配置对象:主题
        • 实现一个 Storage
          • 判断是否存在 Storage 实例,如果存在,直接返回,如果不存在,创建一个 Storage 实例
          • Storage 实例中,存储一个对象,用来存储数据
          • Storage 实例中,提供 setItem 和 getItem 方法,用来设置和获取数据
          • Storage 实例中,提供 clear 方法,用来清空数据
          • Storage 实例中,提供 removeItem 方法,用来删除数据
      • 业务:一个购物车
    • 装饰器模式

      • react HOC 高阶组件: 扩展组件功能,例如添加打印鼠标的坐标功能
        • 应用场景:交易类的软件,获取时间和价格
    • 工厂模式:用于创建结构类似的对象

      • React.createElement()

      • 组件动态创建

        ...
        class ComponentFactory {
        createComponent(type) {
        switch(type) {
        case 'button':
        return new Button();
        case 'input':
        return new Input();
        }
        }
        }
        ...
    • 适配器模式

      • API 数据适配器,统一数据结构
    • 策略模式

      • 根据用户购买历史和行为,推荐不同的商品推荐策略,如热门推荐、相关推荐。将不同的策略封装,根据不同的用户需求和条件,选择不同的策略
  • ES6 语法

  • vue 如何做优化的

  • 技术难点,如何解决

  • Webpack 原理

  • 前端工程化: 打包、性能优化、CI/CD、

    • 性能监控、埋点、如何快速查错
    • lint 工具代码检查
    • 测试,自动化测试
    • 安全检测
  • 跨域

  • 重排 重绘

  • 物理像素和逻辑像素

    • 物理像素是实际的点阵
    • 逻辑像素是虚拟的,1px可能在不同分辨率下对应不同物理像素
  • SSR

    • SEO
    • 语义化
  • CSS 框架

  • Vue 和 React 上层框架

    • Nuxt.js
    • Next.js
    • UmiJS (react)
    • 数据流框架
      • DvaJS: 基于 redux 和 redux-saga 的数据流方案

通过 creat-nuxt-app 可以看到有哪些 UI 可以用

  create-nuxt-app v5.0.0
✨ Generating Nuxt.js project in nuxt-app-demo
? Project name: nuxt-app-demo
? Programming language: TypeScript
? Package manager: Yarn
? UI framework:
None
Ant Design Vue
❯ BalmUI
Bootstrap Vue
Buefy
Chakra UI
Element
Oruga
Primevue
Tachyons
Tailwind CSS
Windi CSS
Vant
View UI
Vuetify.js

xiyin

  • 输入url到浏览器中,回车会发生什么

  • 0.1 + 0.2 等于什么?如何避免

  • 实现深拷贝 和 浅拷贝

    • 对象中值类型,引用类型(Array/Function)
    • JSON.Stringfy 和 JSON.Parse 实现深拷贝有什么副作用
  • forEach 和 map 的应用场景

  • 闭包

  • 团队如何保持高效,如何复用代码

    • 例如,列表页、搜索框和页尾
  • 做过的性能优化

  • 事件循环机制

  • 原生事件给一个按钮添加两个点击事件

  • TreeShake 原理

SmartX

  • OAuth2.0 授权

    • 授权码模式
      • 授权中心中注册应用,得到 client_id 和 client_secret, 存储在第三方客户端服务器中
      • 将应用 redirect_url 配置到授权中心的后台中,白名单后才可以被跳转
      • 授权客户端:该页面轮询查找授权服务器查看用户是否扫码成功
      • 如果获取到用户确认登录,返回 code,重定向到 rediret_url 上,同时附上 code
      • 第三方客户端 将 code 传输给第三方客户端服务器,服务器将 client_id、client_secret、code 发送给授权中心
      • 授权服务器根据 code 请求颁发 token 给第三方客户端服务器,服务器将 token 返回给第三方客户端
    • 微信扫码登录,通过长轮询的方式去获取用户扫码的结果
      • 授权登录,返回 wx-code

        通过判断返回的状态码,来判断用户扫码结果,未扫码,使用 setTimeout 继续轮询

      function o(e) {
    jQuery.ajax({
    type: "GET",
    url: _ + "/connect/l/qrconnect?uuid=091Etqc61AGRGa1Y" + (e ? "&last=" + e : ""),
    success: function (e, n, s) {
    switch (r) {
    case 404:
    setTimeout(o, 100, r);
    break;
    case 403:
    setTimeout(o, 2e3, r);
    break;
    case 408:
    setTimeout(o, 2e3);
    break;
    }
    },
    error: function (e, t, n) {
    var s = window.wx_errcode;
    408 == s ? (c("connect_qrconnect_longpull_error_408", .01),
    setTimeout(o, 5e3)) : (c("connect_qrconnect_longpull_error_others", .01),
    setTimeout(o, 5e3, s))
    }
    })
    }
  • SSO

    • CAS (Central Authentication Service)
    • OIDC(openId Auth2)
    • LDAP (Light Directory Access Portocol),中文名轻量目录访问协议,
    • 单点登录中,系统A登录后,系统B(跨域),如何获取到系统A的登录状态
  • 适配问题,如何解决的。适配浏览器和手机的标准是什么?如何定义这个标准?

  • 打印顺序


const promise = new Promise((resolve, reject) => {
console.log(2)
reject(3)
console.log(4)
})

promise.then(() => {
console.log('then1')
}).catch(() => {
console.log('error1')
}).then(() => {
console.log('then2')
}).catch(() => {
console.log('error2')
})

// 2 4 error1 then2
// 先打印 4 ,再打印 error1,再打印 then2:
// console.log(2),reject(3),console.log(4)这三条语句被看作是同一个宏任务(macrotask)中的一部分,它们被立即执行。
  • 代码分析 和 debug

  • H5

  • TS:never unknown any 如何使用

    • never 表示永远不会发生的,使用场景:抛出异常,死循环

    • unknown 表示未知类型,会进行类型检查,使用场景:未知类型的变量,需要进行类型检查或类型断言才能对其进行操作

      let a: unknown;
      let b: string;
      b = a; // error: Type 'unknown' is not assignable to type 'string'.

      // unknown没做类型判断就赋值,会出错。如果是any就不会类型检查,不会出错。

      // 断言为 string 就不会错
      b = a as string;

      // 做类型判断也不会错
      if(typeof a === 'string') {
      b = a;
      }
    • any 表示任意类型,关闭了类型检查,过度使用会导致类型不安全和难以维护

  • 模板字符串

    • 函数调用
    const fn = (strings, ...args) => {
    console.log('strings :>> ', strings, 'args: ', args);
    let ans = ''
    // console.log('args :>> ', args.entries()); entries: key/value iterator
    for (const [index, arg] of args.entries()) {
    console.log('arg: ', arg, ' index: ', index);
    ans += strings[index] + arg
    console.log('ans :>> ', ans);
    debugger
    }
    ans += strings[strings.length - 1]
    return ans
    };

    const [a, b] = [1, 2];
    // fn`${a+b} ${b} ${a}` // 3 2 1
    // expect(fn`${a} + ${b} = ${a + b}`).toEqual('1 + 2 = 3')

    module.exports = { fn };

tanji

  • react 用 useHooks 后,对开发的整个生态链有什么影响

  • react 渲染的优化

    • React.memo 函数式组件的优化
    • useMemo 避免重复计数,useCallback 避免重复渲染函数
    • 合并状态更新:React.useReducer 替代 useState, 合并多个状态
    • 参考 vdom, 使用虚拟列表:react-window/react-virtualized,渲染可视区域列表
    • 按需加载:React.lazy 和 Suspense 避免一次性加载所有组件,懒加载
    • React.PureCompoetnent 纯函数:自动进行浅层的 props 和 state 比较,避免不必要的重复渲染
  • react Lazy 实现原理:

    • React.lazy 的目标是 代码分割和按需加载
    • 基于动态导入 import(),Lazy(() => import('./component')
    • 必须结合 Suspense 组件使用
  • 加快 webpack 构建速度的方法

    • 多进程打包
    • hash 缓存
    • dll
  • 两个同源的窗口,如何通讯

    • SharedWorker: 需要编写监听和发送,较为复杂的通信(web worker),独立线程
    • BroadcastChannel:同源的窗口之间可以相互通信。 web API
    const channel = new BroadcastChannel('test');
    channel.postMessage('Hello world');

    channel.onmessage = (event) => {
    console.log(event.data);
    };
    • 总结:其实就是广播和监听
  • webpack 中的 loader 和 plugin 区别 (见 webpackAndBabel)

    • Loader 处理非 js 文件,转换为 js 模块,可以以管道的方式链式调用
    // 对 less 文件使用 less-loader
    module.exports = {
    module: {
    rules: [
    {
    test: /\.less$/, // 对非 js 的一类文件转换为 js 模块
    use: ['style-loader', 'css-loader', 'less-loader'],
    },
    ],
    },
    };
    • Plugin
      • 增强和扩展功能:文件压缩、资源注入、代码分割
      • 生命周期钩子:压缩代码、修改输出路径
  • 浏览器存储的方式

    • sessionStorage
    • localStorage
    • cookie
    • IndexedDB
  • 什么情况会内存泄漏

    • 闭包滥用
    • 定时器未清除
    • 事件监听未清除
    • 循环引用