自己看的

95 阅读22分钟

一、 js相关

1. 防抖节流

  • 防抖:函数被触发后等待一段时间再执行,在这段时间内,再次触发事件,会重新执行。【应用:搜索框输入、窗口调整大小、动态加载无限滚动】
function debounce(fn,delay){
    let timer = null
    return function(...arguments){
        clearTimeout(timer)
        timer = setTimeout(()=>{
          fn.apply(this,arguments)      
        },delay)
    }
}
  • 节流:在一段时间内,只会执行一次【应用:鼠标事件、连续按键】
function throttle(fn,delay){
  let flag = false
  return function(...arguments){
    if(!flag){
        let flag = true
        fn.apply(this,arguments)
        setTimeout(()=>{
            flag = false
        },delay)
    }
  }
}

2. 原型原型链

要知道: 构造函数 实例对象

构造函数与普通函数没什么区别只是首字母大写(如Person),

function Person(){

}

函数都具有一个prototype属性。那么他的原型就是Person.prototype。 原型也有一个constructor属性指向构造函数。 构造函数 通过 new 创建实例。实例具有__proto__ 属性指向原型。 找一个属性,从实例通过__proto__属性向他的原型上找,如果没找到,原型再上他的原型也就是Object上去找,直到找到null为止。

3. 深拷贝 浅拷贝

  • 对于基本数据类型没有深浅拷贝的概念。
  • 对于引用数据类型来说,浅拷贝是复制了引用地址,深拷贝则是开辟了一块新的内存区域。

深拷贝实现方式

JOSN.parse(JSON.stringfy(obj))
递归的方式
structuredClone() web新API

4. Es6语法

  • let const
  • 模版字符串
  • 扩展运算符 ...
  • 解构赋值
  • Map Set Symbol
  • Proxy
  • Reflect
  • Promise
  • Calss
  • 模块
  • 剩余参数 ...rest
  • 默认参数
  • for...in for...of
  • 箭头函数
  • Array.from() Array.of()

5. Es6新增数据类型

1.let const
2.Set 中的对象是强引用的,可存储原始数据类型和引用数据类型
3.Map
4.WeakSet 中的对象是弱引用的,会被垃圾回收机制回收。并且只能存储引用数据类型
5.WeakMap
6.Symbol 独一无二的

6. 循环方法

原始循环
map      返回的是判断条件数组
forEach  没有返回值 直接修改原数组
reduce
filter
some
every
for in
for of
while
do while

7. 怎么验证数据类型

  • typeof 对于数组、Object、null 都会判断为Object其他正常
typeof 'ss'           // string
typeof function(){}  //function
  • instanceof 判断对象的类型 内部机制是在原型链中能否找到原型。📢:null是基本类型
2 instanceof Number //false
[] instanceof Array
console.log({} instanceof Object)
  • Object.prototype.toString.call()
let a = Object.prototype.toString.call()
a.call(null)  // [object Null]

8. 事件循环

因为js是单线程的,为了解决这个问题。有了同步异步的概念。先执行同步任务,同步任务执行完执行异步任务。异步任务又分为微任务和宏任务。先执行微任务,微任务执行完会执行一个宏任务。宏任务执行完会看有没有微任务,如果有就执行微任务,没有继续执行下一个宏任务。

微任务:Promise MutationObserver

Promise new Promise(()=>{
    console.log('这是同步')
}).then(()=>{
    console.log('这是异步')
})

宏任务:setTimeout、 setInterval 、requestAnimateFrame 、XMLHttpRequest

requestAnimateFrame它允许你创建平滑的动画。这个函数会在浏览器的下一次重绘之前调用你的回调函数,从而确保动画的流畅性。由于 requestAnimationFrame 会在浏览器准备好绘制下一帧时执行回调,因此它非常适合用于执行视觉更新

9. 闭包

首先是一个嵌套函数,在内部函数中调用了其外部的变量,造成了在外部函数销毁之后,这个变量依旧没有销毁。

优点:

  • 保护变量:将变量封装在函数内部,外部不可以访问和修改。
  • 延长生命周期: 外部函数销毁后仍然存在。
  • 实现模块化

缺点

  • 占用内存
  • 性能消耗

应用场景

  • 自动执行函数
  • 防抖节流
  • 函数柯里化
  • 链式调用
1.自动执行函数
let say = (function(){
  let val = 'hello world';
  function say(){
    console.log(val);
  }
  return say;
})()
2.防抖 节流

3. 函数柯里化 
函数柯里化(Currying)是一种将多个参数的函数转换为一系列接受单个参数的函数的过程。举个简单的例子,我们有一个原始函数add(a, b, c),我们可以将它柯里化为addCurried(a)(b)(c)的形式。
//柯里化前
function add(a, b, c) {
  return a + b + c;
}
console.log(add(1, 2, 3)); //6
//柯里化后
function addCurried1(a) {
  return function (b) {
    return function (c) {
      return a + b + c;
    };
  };
}
//箭头函数简写
const addCurried2 = (a) => (b) => (c) => a + b + c;
console.log(addCurried1(1)(2)(3)); //6
console.log(addCurried2(1)(2)(3)); //6
4. 链式调用

原文

10. http https

  • http:明文传输 https: ssl加密
  • http:端口80 https:端口443
  • https经过CA证书认证 需要付费

11. localStorage sessionStorage cookie

  • localStorage: 生命周期永久 内存5MB。浏览器关闭还存在。 没有网络的本地缓存使用也可以。
  • sessionStorage: 生命周期会话结束 内存5MB 两个页面不能共享 仅在当前浏览器会话期间有效,标签页或窗口关闭即失效
  • cookie: 用于服务端通信 内存4KB 安全性问题

12. this指向

  • 全局作用域 this 指向全局(浏览器window node指向global)
  • 作为普通函数调用时 指向window
  • 作为对象的方法调用时 指向对象
  • 构造函数中的this,指向new创建的实例
  • 事件处理程序的this 指向触发事件的元素
  • 箭头函数的this执向外部作用域的this
  • .call .apply .bind 可以显式的绑定this的值。

13. 箭头函数用途

  • 作为对象方法 不需要绑定this
  • 是匿名函数 简化回调函数
  • 箭头函数不可以用于构造函数、因为没有原型prototype、也没有this指向

14. Promise

解决了回调地狱的问题

三个状态

fullfilled(完成) pending(进行中) rejected(失败)

链式调用

.then((resolve,reject)=>{}).catch().finaly()
resolve("成功") reject('失败')

race all allsettled

  • race 不管执行失败还是成功 只要有一个返回结果就结束

  • all 如果都成功返回,会返回一个新的Promise对象(包含所有已解决结果的数组) 如果有一个失败 返回第一个失败的原因

  • allsettled 等待所有都执行完,返回数组中的每个元素都有一个对象 包含status value

15. 解决跨域

什么是跨域? 因为同源策略,协议、域名、端口 只要不一致就会发生跨域

  • JSONP 只适合get请求
<head> 
<title>JSONP Example</title> 
<script> // 定义回调函数 
function handleResponse(data) { console.log(data); // 处理返回的数据 } 
</script> 
</head> 
<body> 
<!-- 使用 JSONP 获取数据 --> 
<script src="https://example.com/api?callback=handleResponse"></script> 
</body>
  • CORS

是一种HTTP请求头的机制,该机制允许服务器标示除了他以外的origin,使得浏览器允许这些origin访问其资源。服务端设置了Access-Control-Allow-Origin就开启了CORS

  • nginx 反向代理

  • node服务器 去请求接口

  • proxy 前端项目proxy 将接口请求地址也指向前端服务器 这样都是从前端服务器去请求的 就不会有跨域

  • websocket

16. 事件冒泡捕获

17. echarts渲染出来是什么

canvas 或者 svg

18. let var const

  • 变量提升 var在未声明之前 就可以使用变量 但是是undefined
  • 暂时性死区 let const 只有在声明变量的那一行代码出现 才可以使用
  • 块级作用域 let const 存才块级作用域
  • 重复声明 let const 不可以重复声明,并且const不可以改变,但是对象的属性可以改变。

19. 如何判断这是一个对象

Object.prototype.toString.call()
obj instanceof Object
obj.constructor === Object
typeof someValue === 'object' && someValue !== null;

20. 改变this指向的方法有哪些? call apply bind

  • call()会立即执行 不能复用 接收两个参数:要绑定的上下文对象 和要传给函数的参数
  • apply()接收的两个参数:要绑定的上下文对象 和数组(数组里是要传递给函数的参数)
  • bind() 会创建一个新的函数,将原始上下文绑定到指定上下文,可以复用。接受两个参数:要绑定的上下文对象、传给原始函数的参数

二、css

1. 弹窗层级问题

在React里要让弹窗在最外层使用createPortal

2. position

  • absolute:元素会脱离文档流,定位是相对于离它最近的且不是static定位的父元素而言,若该元素没有设置宽度,则宽度由元素里面的内容决定,且宽度不会影响父元素,定位为absolute后,原来的位置相当于是空的,下面的的元素会来占据。
  • Relative:元素仍处于文档流中,定位是相对于原本自身的位置,若没有设置宽度,则宽度为父元素的宽度,该元素的大小会影响父元素的大小。

3. css选择器优先级

  • !important 规则:如果样式规则中包含 !important 声明,则它将覆盖所有非 !important 的样式,不论其他选择器的优先级如何。
  • 内联样式:在元素的 style 属性中定义的样式具有最高的优先级。
  • ID 选择器:#id 的优先级高于其他类型的选择器。
  • 类选择器、属性选择器和伪类:.class, [type], :not() 等具有中等优先级。
  • 元素选择器和伪元素:element, ::before, ::after 等具有较低的优先级。
  • 通用选择器:* 和 / 具有最低的优先级。
  • 继承样式:继承的样式优先级最低。

4. 居中

  • flex align-items justify-content
  • display:inline-block + vertial-align:middle
  • 伪元素

5.

Pixi.js spine.js
flex grid
sass less
@keyframes transition
GSAP
umi

6. @keyframe

常用于

  • 颜色渐变
@keyframes changeColor {
  from {
    color: blue;
  }
  to {
    color: red;
  }
}

div {
  animation-name: changeColor;
  animation-duration: 3s;
}
  • 旋转
@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

img {
  animation-name: rotate;
  animation-duration: 2s;
  animation-iteration-count: infinite;
}
  • 淡入淡出
@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

element {
  animation-name: fadeIn;
  animation-duration: 1s;
}
  • 跳动动画
@keyframes bounce {
  0%, 20%, 50%, 80%, 100% {
    transform: translateY(0);
  }
  40% {
    transform: translateY(-30px);
  }
  60% {
    transform: translateY(-15px);
  }
}

.ball {
  animation-name: bounce;
  animation-duration: 1s;
  animation-timing-function: ease;
  animation-iteration-count: infinite;
}
  • 滑动进入视图
@keyframes slideIn {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0);
  }
}

.slide-in {
  animation-name: slideIn;
  animation-duration: 0.5s;
  animation-fill-mode: forwards; /* 保持动画结束状态 */
}
  • 闪烁效果
@keyframes blink {
  50% {
    opacity: 0;
  }
}

.blink {
  animation-name: blink;
  animation-duration: 1s;
  animation-iteration-count: infinite;
}
  • 放大缩小
@keyframes scale {
  from {
    transform: scale(1);
  }
  to {
    transform: scale(1.5);
  }
}

.zoom {
  animation-name: scale;
  animation-duration: 2s;
  animation-timing-function: ease-in-out;
}
  • 左右摇摆
@keyframes swing {
  0%, 100% {
    transform: rotate(0deg);
  }
  50% {
    transform: rotate(20deg);
  }
}

.swing {
  animation-name: swing;
  animation-duration: 1s;
  animation-iteration-count: infinite;
}

sass less

可以嵌套 可以使用变量 @mixin可以定义重用的片段 可以继承 支持循环 但是需要转为css

grid

gird适用于一些网格布局,比如卡片集合,例如腾讯视频。

通过grid-template-column和gird-template-row,去划分列和行的子元素所占大小。其中

  • repeat repeat(3,100px)为重复子元素
  • auto-fill repeat(auto-fill,100px) 一行或者一列的数量不固定,自动分配。
  • fr grid-template-column:1fr 2fr 3fr;类似于flex布局的flex:1 可以平均分成任意份。
  • minmax grid-template-column:1fr minmax(150px,1fr)
  • auto grid-template-column:100px auto 100px; 第二列的宽度会随着屏幕宽度改变而改变
  • grid-gap 各个子元素间隔 是row-gap和column-gap的合集

子元素的属性: 如下是基于网格线的

  • grid-column-start:1
  • grid-column-end:3 结合上面的就是占据着格子1和2
  • grid-row-start
  • grid-row-end

flex

三、react

1. 常用hooks

useState
useEffect
useMemo
useRef
useCallback
useReducer
useContext
useImplementHandle

2. useState是异步的吗

useState是异步的

3. 多个setState()

执行多个 会合并,只执行一次 如果想要作用多次 可以使用 setState((count)=>count+1)

setState怎么更新对象 setState({...obj,name:'yy'})

4. useEffect和useLayoutEffect区别,useLayoutEffect用处

  • useLayoutEffect 在DOM更新之后,页面渲染之前执行。是同步的。useLayoutEffect会立即执行,可能会阻塞浏览器的绘制。适用于需要DOM的尺寸或位置执行操作。
// 使用 useLayoutEffect 读取 DOM 尺寸 
useLayoutEffect(() => { 
const elementWidth = document.getElementById('myElement').offsetWidth; 
// 基于 DOM 尺寸执行操作 
}, []);
  • useEffect 是组件渲染到屏幕之后执行的。React收集他们的效果批量执行。

5. useMemo和useCallback区别 useMemo是浅比较吗

  • useMemo缓存的是计算结果
  • useCallback缓存的是函数

useMemo是浅比较,如果处理对象可以

useMemo(()=>{
    
},[JSON.stringfy({name:'t',age:12})])

6.为什么不推荐大量使用useMemo?

  • 内存消耗
  • 代码可读性差
  • 调试困难

7.怎么让父组件改变,子组件不频繁更新

React.memo(),来记忆化子组件。这会防止子组件在父组件的props没有变化时进行不必要的渲染。

8.Hooks闭包陷阱

在函数中能使用外部的变量,React Hooks中主要有两种情况: useState() useEffect中。

  • 在useState()的参数只会在组件挂载时执行一次,在useState中使用闭包,变量会被缓存。
const [count,setCount] = useState(0)
const handleCount = ()=>{
    setTimeout(()=>{
        setCount(count+1)
    },[])   
}

可以通过接收回调作为参数的方式,因为prevCount参数是函数作用域内的局部变量,不会受到外部影响。

const [count,setCount] = useState(0)
const handleCount = ()=>{
    setTimeout(()=>{
        setCount(prevCount=>prevCount+1)
    },[])   
}
  • useEffect 因为useEffect中的函数在每次组件更新都会执行一次,如果我们在useEffect中使用闭包,那么闭包中的变量也会被缓存。通过useEffect来更新。
useEffect(() => {
  const timer = setInterval(() => {
    console.log(count);
  }, 1000);
  return () => clearInterval(timer);
}, [count]);

9. hooks模拟生命周期

// 1.componentDidMount
useEffect(() => {
  // 类似 componentDidMount
  initializeApp();
}, []);
// 2.componentDidUpdate
useEffect(() => {
  // 类似 componentDidUpdate
  updateApp();
}, [dependency1, dependency2]);

// 3.componentWillUnmount
useEffect(() => {
  return () => {
    // 类似 componentWillUnmount
    cleanupApp();
  };
}, []);

10. redux

单向数据流 状态state和变更状态的逻辑处理函数reducer是分开的,通过store去统一处理。 三个核心

  • action 是一个js对象 两个属性type唯一标识 playaload 要存的状态值。当应用程序发生某个事件,通过dispatch(action)去通知redux应用中状态要发生改变。
  • reducer 是一个纯函数 接收原状态state和action 返回新的状态值
  • store 是一个js对象 里面存储着state 和 reducer

11. react怎么优化 提高性能

  • 代码层面
    • React.memo进行组件记忆 (对两个组件props进行浅比较)
    • 组件懒加载
    • 使用React.Fragement 避免额外标记
    • 不要使用内联函数
    • 避免使用内联样式
    • 唯一key
    • 优化条件渲染
    • 事件防抖节流
    • useMemo useCallback
    • 用css动画替换js动画

链接

12. 项目怎么优化

  • 减少http请求 使用雪碧图
  • 压缩资源文件
  • 使用CDN
  • 按需加载和代码分割 webpack rollup进行代码分割 实现按需加载
  • 优化图片
  • 优化字体
  • 预加载
  • 减少第三方库
  • 懒加载
  • SSR 服务端渲染

13. Fiber

又称协程,因为js是单线程的,为了不阻塞。将大的耗时长的任务拆分成多个小任务,先会执行一个小任务,如果任务队列里没有其他任务继续执行小任务。这面用到了requestIdleCallback去监测浏览器中是否有任务在执行。

14. diff算法

虚拟DOM 本质上是js对象,用来表示DOM结构, 实现跨平台。 通过事务处理机制将多次DOM修改结果一次更新到页面上,减少渲染次数、减少重绘重排 在代码渲染到页面之前,vue或者react会将代码转为一个对象来描述真实DOM,最终渲染到页面。每次变化之前,都会缓存一下虚拟DOM,变化时,将缓存的和新的进行对比。 diff算法 现在前端框架的思想是无需手动操作DOM

对比新旧两棵虚拟DOM树的差异,将更新的补丁作用于真实DOM,以最小的成本完成视图更新。 策略:

  • 基于树:节点跨层级移动的操作较少可以忽略
  • 基于组件:如果组件是同一类型则进行树对比,如果不是则放入补丁,只要父组件类型不同就会重新渲染。
  • 基于节点:同一层级的子节点,可以通过key区分,节点重新排序会涉及到插入、移动、删除,所以消耗大,通过key,React可以直接移动DOM节点、降低内存消耗

15. 合成事件

为了实现跨浏览器,React自己又封装了合成事件。采用驼峰规则。 原生js事件是绑定到DOM元素上,如果过多对性能有消耗。 React并不会对每个元素都绑定一个事件处理器,而是将所有事件存在数组中,委托给一个统一的处理器。React16之前事件冒泡到document上再执行 17之后冒泡到根节点上再执行

16 ahooks

  • useRequest
  • useMount 组件初始化执行
  • useUnmount 组件卸载时
  • useDebounce 防抖
  • useThrottle 节流
  • useCountDown 倒计时
  • useHover
  • useMouse
  • useLongPress

17. react-router

路由描述了URL和UI的单向映射关系,无需刷新。

  • HashRouter: 在url后加上#,http://127.0.0.1:5500/home/#/page1 hash值改变,触发hashchange事件监听url变化,从而进行DOM操作,模拟跳转。

  • BrowserRouter: 允许操作浏览器的会话记录 依赖于 HTML5 的 history API

有哪些组件?

  • Routes
  • Route [path element key]
  • Link

Hooks?

  • useNavigate/useHistory
  • useLocation
  • useParams

路由传参?

四、 问答题

1.vue和React区别

jsx和模板

diff算法

响应式原理

MVVM

  1. 多语言怎么搞
  2. 权限校验
  3. 4x4 的16个格子,有三种房子由多个方格组成可以理解成俄罗斯方块那种不是规则的正方形(1)实现拖拽把房子放到指定位置(考虑靠近吸附过去) (2)有3只小猪(小猪的大小不超过一个格子)怎么判断房子把小猪完全遮盖住了 (1)计算房子的中心点(div) 到要放置位置的中心点 的距离如果在某个范围区间(并且为了实现离得距离较近也吸附过去,这个范围还需要去加一些固定的px比如10px) (2)遍历所有格子获取格子的中心点位置,再看猪的中心点位置 与格子中心点的偏移量

5.遇到的难点

  1. Google登录
    1. 新年迎财神canvas

首先有一张新年的海报,然后去往中间的空缺位置添加财神(每个人生成的财神都是不同的),

  • 3. H5输入框

    输入昵称,在安卓手机上输入法是先把输入内容添加到输入法上,最后确定才添加到输入框中。 而在iphone输入法中是打一点就会添加到输入框中。然后拼音输入就会有一个阻断。刚开始想去监听键盘的开始和结束事件然后做一些逻辑处理,但是还是不行。最后查了好多文章,最后用的antd的组件的输入框替换了原生的input。

    1. H5适配
    • 在iphone手机上,视频自动弹出 需要添加safria的属性webkit-playsinline="true" playsInline={true}
    • 键盘打开关闭后就会把当前页面内容顶上去并且底部出现留白,最开始用的absplute后来发现需要改成fixed
    • 选择男主是一个图片然后还有文字介绍,在不同机型上的适配 用了@media去对特殊机型进行了一些处理
    1. H5图片加载、字体加载问题

    图片质量很大每次加载都很慢,首先进行了一个降低质量的处理,但是还是有些慢,然后就把图片都放到CDN上去加载了。然后又做了预加载的处理(封装一个公用方法,在里面通过new Image去创建图片),在用到这个图片的地方,浏览器就会去读缓存中的资源。字体是一个日文字体,特别大大概2.4G,首先找了一个库,他会进行一些按需加载的处理,只加载用到的字体,大小在20MB左右。但是还会出现字体闪烁的问题,刚一进来加载的是手机的默认字体然后再变为日语字体。然后在这个上面又写了一样相同的代码然后把这部分内容挤出屏幕。这样也实现了一个预加载去读取浏览器的缓存。

    1. lastID的方式向下滑动加载更多

滚动加载更多,通过lastID的方式。但是发现每次lastdID的值都是初始值。刚开始以为是请求太过频繁, 就通过加loading还有加了个节流。但是这个lastId还不变。因为useEffect执行会产生闭包,里面 用到的state变量就不是最新的。后来通过将lastID存到useRef中,因为引用值不会保存在组件的状态中。

7. live2d

8. 你对AI怎么看

9. 与没遇到过干不完了

10. 优点

五、 ts

1. ts泛型

不预先设置类型 在使用的时候指定类型。

五、webpack vite

  • vite常用配置项
    • root 根目录
    • base 开发或生产环境的公共基础路径
    • plugins 第三方插件
    • publicDir
    • resolve
      • alias
      • extensions
    • build

vite为什么打包块

vite由两部分组成

  • 一个开发服务器 基于原生ES模块,速度惊人的模块热更新HMR
  • 一套构建指令 使用Rollup打包代码,且是预配置的,可输出用于生产环境的高度优化过的静态资源

github.com/bailicangdu…

转盘抽奖

  1. vuex state mutations store中的值不能直接修改 通过mutation修改 actions 执行异步任务 getter 类似于计算属性 module 拆分成不同的模块

  2. vue2 vue3区别

  3. Object.defineProperty Proxy

  4. 生命周期

  • beforeCreate 实例初始化之前 此时(注册组件使用到的store等)
  • created 新的Vue实例被创建后 (初始的数据处理、异步请求等)
  • beforeMount 挂载之前调用,相关的render函数首次被调用,此时可以访问根节点 dom渲染完成
  • onMounted 有了相关的DOM环境 在这里执行节点的DOM操作
  • beforeUpdate 在数据更新时同时在虚拟DOM重新渲染和打补丁之前调用,可以访问之前的状态和DOM
  • onUpdated 数据更新完毕,虚拟DOM重新渲染和打补丁完成 可执行操作、触发组件动画
  • beforeUnmount 清除定时器 解绑全局事件
  • onUnmounted 组件卸载之后 此时组件的DOM结构已经被拆卸 可释放组件用过的资源

beforeCreate 和 created 被 setup 替代

  1. Option API 和 Composition API
  • Option API vue早期的编写方式 通过定义一个options对象进行组件的配置,包含props、data、methods、computed、watch。 优点 结构清晰、易于理解 适合小型项目。
  • Composition API 以函数的形式组织代码,提高代码的可维护性和重用性 更好结合ts。 适合大型项目
  1. vue3更新
  • Proxy替代了Object.defineProperty实现响应式
  • ts
  • Composition API
  • 改进 Vue cli,支持升级到vue3
  • 移除一些修饰符、不常用的API
  1. Proxy和Object.defineProperty的区别
  • 实现方式:Proxy是Es6新增特性 使用了代理机制。 Object.defineProperty Es5中引入,使用getter和setter实现的
  • 作用对象:Proxy可以代理整个对象(包括对象的属性、数组的所有元素),Object.defineProperty只能代理对象上定义的属性
  • 监听属性:Proxy可以监听到新增属性和删除属性的操作,而Object.defineProperty只能监听到已经定义的属性。
  • 性能:Proxy因为是Es6属性 所以更高效。
  1. watch
  2. 组件通信

代码题

1.将如下json渲染为DOM

const treeData = {
  id: 1,
  label: '根节点',
  children: [
    {
      id: 2,
      label: '子节点1',
      children: [
        { id: 3, label: '子节点1-1' },
        { id: 4, label: '子节点1-2' }
      ]
    },
    {
      id: 5,
      label: '子节点2'
    }
  ]
};

创建节点的方式

function renderTree(tree, parentElement) {
  // 为当前节点创建一个容器元素
  const nodeElement = document.createElement('div');
  nodeElement.textContent = tree.label;

  // 如果有子节点,为每个子节点递归调用 renderTree 函数
  if (tree.children && tree.children.length > 0) {
    tree.children.forEach(child => {
      const childElement = renderTree(child, document.createElement('div'));
      nodeElement.appendChild(childElement);
    });
  }

  // 将当前节点的元素添加到父元素中
  parentElement.appendChild(nodeElement);
  return nodeElement;
}

拼接字符串的方式

function renderTreeAsString(tree) {
  let html = '';

  function buildHtml(node) {
    // 基本的节点 HTML 结构
    html += `<div>${node.label}`;
    
    // 如果存在子节点,递归为每个子节点生成 HTML
    if (node.children && node.children.length > 0) {
      html += '<ul>';
      node.children.forEach(child => {
        html += `<li>${buildHtml(child)}</li>`; // 递归调用
      });
      html += '</ul>';
    }
    
    html += '</div>';
  }

  buildHtml(tree); // 从根节点开始构建 HTML
  return html;
}

// 将树结构数据转换为字符串形式的 HTML
const treeHtml = renderTreeAsString(treeData);

// 将生成的 HTML 字符串添加到页面的指定容器中
document.getElementById('tree-container').innerHTML = treeHtml;

2. 冒泡排序

// 冒泡
var arr = [123,203,23,13,34,65,65,45,89,13,1];
for(var i=0; i<arr.length-1; i++){
	//每一轮比较要比多少次
	for(var j=0; j<arr.length-1-i; j++){
	    //如果第一个比第二个大,就交换他们两个位置
	    if(arr[j]>arr[j+1]){
	        var temp = arr[j];
	        arr[j] = arr[j+1];
	        arr[j+1] = temp;
	    }
	}    
}
console.log(arr); //(11) [1, 13, 13, 23, 34, 45, 65, 65, 89, 123, 203]

3. 数组去重

let arr = [{ id: 1, name: 'aaa' }, { id: 1, name: 'aaa' }, { id: 2, name: 'bbb' }]


function getUnique(arr) {
    let nameMap = new Map([])
    arr.map((item) => {
        if (!nameMap.has(item.name)) {
            nameMap.set(JSON.stringify(item), item)
        }
    })
    console.log(Array.from(nameMap.values()))
}

function getUniqueWithSet(arr) {
    let str = arr.map((item) => {
        return JSON.stringify(item)
    })
    let setArr = Array.from(new Set(str))
    return setArr.map((item) => {
        return JSON.parse(item)
    })

}

4. 实现一个flat方式

5. 自定义useHover

const useHover = ()=>{
    const [isHover,setIsHover] = useState(false)
    const ref = useRef(null)
    const handleMouseEnter=()=>{
        setIsHover(true)
    }
    const handleMouseLeave=()=>{
        setIsHover(false)
    }
    useEffect(()=>{
        const element = ref.current
        if(element){
            element.addEventListener('mouseenter',handleMouseEnter)
            element.addEventListener('mouseleave',handleMouseLeave)
        }
        return ()=>{
            if(element){
                element.removeEventListener('mouseenter',handleMouseEnter)
                element.removeEventListener('mouseleave',handleLeave)
            }
        }
    },[])
    return {ref,isHover}
}

function myComp =()=>{
const {ref,isHover} = useHover()
    return(
        <div ref={ref}>
            {isHover?"悬停中":"未悬停"}
        </div>
    )
}

6.js实现随机抽奖 , a奖牌的概率为75% b奖品的概率为20% c奖牌为5%