欢迎光临
我们一直在努力

前端八股文面经大全:拓竹科技前端一面(2026-03-15)·面经深度解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

前言

大家好,我是木斯佳。

相信很多人都感受到了,在AI浪潮的席卷之下,前端领域的门槛在变高,纯粹的“增删改查”岗位正在肉眼可见地减少。曾经热闹非凡的面经分享,如今也沉寂了许多。但我们都知道,市场的潮水退去,留下的才是真正在踏实准备、努力沉淀的人。学习的需求,从未消失,只是变得更加务实和深入。

这个专栏的初衷很简单:拒绝过时的、流水线式的PDF引流贴,专注于收集和整理当下最新、最真实的前端面试资料。我会在每一份面经和八股文的基础上,尝试从面试官的角度去拆解问题背后的逻辑,而不仅仅是提供一份静态的背诵答案。无论你是校招还是社招,目标是中大厂还是新兴团队,只要是真实发生、有价值的面试经历,我都会在这个专栏里为你沉淀下来。 在这里插入图片描述

温馨提示:市面上的面经鱼龙混杂,甄别真伪、把握时效,是我们对抗内卷最有效的武器。

面经原文内容

📍面试公司:拓竹科技 🕐面试时间:近期,用户上传于2026-03-15 💻面试岗位:前端一面

❓面试问题:

  • 自我介绍

  • 动态主题怎么实现的?CSS变量 除了主题颜色,对黑暗模式兼容怎么设计? less的作用,为什么选择less? 原子CSS和less这些有什么区别,怎么理解?

  • 动态拼装是怎么实现的?这些卡片有层级上的概念吗? 自己去实现一个拖拽怎么实现?怎么判断拖拽时选中的是哪个对象呢? 把addEventListener放在哪呢?e.target和e.currentTarget的区别?

  • useMemo用在哪些场景? 改变父组件的state子组件会跟着更新吗?会渲染?会刷新?会执行? props变了子组件就会更新吗?有什么方法可以让子组件不更新呢?

  • diff算法的理解?他的更新的时间复杂度是怎样的,讲原因? 是深度优先搜索还是广度优先搜索?

  • useRef和useState的区别?

  • 怎么理解闭包?他有什么用呢?怎么销毁掉这个变量?

  • 用过JS哪些异步处理方式?(promise、await、generator、定时器) 点击事件算异步吗? 哪些是宏任务,哪些是微任务?宏任务和微任务的区别?

  • 跨域是什么东西?为什么要有跨域这东西? 如何定向让某个域名a访问b?(CORS、JSONP、iframe+postmessage)这些一般在什么时候用?

  • 某个项目有部署吗?数据库用的什么? transport层发送是通过什么形式发送? 怎么理解nextjs这个框架? 从aisdk拿到数据我是怎么渲染的,怎么去显示消息? tanquery在前端吗?

  • 手撕代码:防抖

  • 平常会怎么用AI?agent用的多吗?

  • 来源:牛客网 小橘_Reflection

    💡 木木有话说

    这是一场非常全面的面试,收录的原因是目前我在很多大厂的面经里,css的问题比重比前两年降低了很多。AI时代+各种UI库的加持下,手写复杂css变得越来越少。但实际上在我了解的圈子里,很多前后端、全栈在前端领域差异较大的还真就是css,很多框架或者语法糖,后端掌握的很快,唯独css调试很多后端是比较头疼的。所以说前端同学们不要放下相关的基本功。这在实际项目里也是很重要的。


    📝 拓竹科技前端一面·深度解析

    🎯 面试整体画像

    维度特征
    公司定位 拓竹科技 – 3D打印/智能制造领域
    面试风格 实战细节型 + 原理追问型 + 场景延伸型
    难度评级 ⭐⭐⭐⭐(四星,覆盖广且深入)
    考察重心 CSS工程化、拖拽实现、React优化、Diff算法、闭包、异步、跨域、AI使用

    🔍 逐题深度解析

    二、CSS主题与工程化

    问题:动态主题怎么实现的?CSS变量

    /* 1. CSS变量实现动态主题 */
    :root {
    –primary-color: #1890ff;
    –bg-color: #ffffff;
    –text-color: #333333;
    }

    .theme-dark {
    –primary-color: #ff4d4f;
    –bg-color: #141414;
    –text-color: rgba(255, 255, 255, 0.85);
    }

    .button {
    background-color: var(–primary-color);
    color: var(–text-color);
    }

    /* 2. JavaScript切换主题 */
    function toggleTheme() {
    document.documentElement.classList.toggle('theme-dark')
    }

    /* 3. 黑暗模式兼容 */
    @media (prefers-color-scheme: dark) {
    :root {
    –primary-color: #ff4d4f;
    –bg-color: #141414;
    –text-color: rgba(255, 255, 255, 0.85);
    }
    }

    问题:less的作用?为什么选择less?

    // 1. less的作用
    // – 变量定义
    @primary-color: #1890ff;
    @border-radius: 4px;

    // – 嵌套
    .nav {
    ul { list-style: none; }
    li { display: inline-block; }
    }

    // – 混合(Mixin)
    .border-radius(@radius) {
    -webkit-border-radius: @radius;
    -moz-border-radius: @radius;
    border-radius: @radius;
    }

    .button {
    .border-radius(4px);
    }

    // – 函数
    @base: 5%;
    @filler: @base * 2;

    // 2. 为什么选择less
    // – 学习曲线平缓,接近CSS语法
    // – 功能丰富,满足大部分场景
    // – 生态成熟,工具支持好
    // – 可与CSS-in-JS方案结合

    问题:原子CSS和less的区别

    /* 1. 原子CSS(如Tailwind) */
    /* HTML中直接使用工具类 */
    <div class="bg-blue-500 text-white p-4 rounded">
    按钮
    </div>

    /* 2. 传统CSS/Less */
    /* 定义样式类 */
    .button {
    background-color: #1890ff;
    color: white;
    padding: 1rem;
    border-radius: 4px;
    }

    /* 3. 区别对比 */
    | 维度 | 原子CSS | Less/Sass |
    |——|———|———–|
    | 思想 | 组合原子类 | 抽象组件样式 |
    | 体积 | 固定,可purge | 随组件增长 |
    | 学习成本 | 需记忆类名 | 熟悉CSS即可 |
    | 灵活性 | 受限于预设 | 完全自由 |
    | 维护性 | 无需命名 | 需规划命名 |
    | 适用场景 | 快速开发 | 复杂定制 |


    三、拖拽实现

    问题:动态拼装怎么实现?卡片有层级吗?

    // 1. 拖拽实现
    function Draggable() {
    const [position, setPosition] = useState({ x: 0, y: 0 })
    const [dragging, setDragging] = useState(false)
    const dragRef = useRef()

    // 事件监听放在document上,保证拖动流畅
    useEffect(() => {
    const handleDrag = (e) => {
    if (!dragging) return
    setPosition({
    x: e.clientX – offset.x,
    y: e.clientY – offset.y
    })
    }

    const handleDragEnd = () => {
    setDragging(false)
    }

    document.addEventListener('mousemove', handleDrag)
    document.addEventListener('mouseup', handleDragEnd)

    return () => {
    document.removeEventListener('mousemove', handleDrag)
    document.removeEventListener('mouseup', handleDragEnd)
    }
    }, [dragging])

    const handleMouseDown = (e) => {
    const offset = {
    x: e.clientX – position.x,
    y: e.clientY – position.y
    }
    setDragging(true)
    }

    return (
    <div
    ref={dragRef}
    onMouseDown={handleMouseDown}
    style={{
    position: 'absolute',
    left: position.x,
    top: position.y,
    zIndex: dragging ? 100 : 1 // 拖拽时提高层级
    }}
    >
    可拖拽卡片
    </div>
    )
    }

    问题:怎么判断拖拽时选中哪个对象?事件监听放哪?target和currentTarget区别?

    // 1. 判断选中对象
    // 方案1:记录所有可拖拽元素
    document.querySelectorAll('.draggable').forEach(el => {
    el.addEventListener('mousedown', (e) => {
    currentDragElement = e.target.closest('.draggable')
    })
    })

    // 方案2:事件委托
    container.addEventListener('mousedown', (e) => {
    const dragElement = e.target.closest('.draggable')
    if (dragElement) {
    currentDragElement = dragElement
    }
    })

    // 2. e.target vs e.currentTarget
    // e.target:触发事件的实际元素
    // e.currentTarget:绑定事件监听的元素

    container.addEventListener('click', (e) => {
    console.log('target:', e.target) // 点击的button
    console.log('currentTarget:', e.currentTarget) // container
    })

    // 3. 事件监听位置
    // – 具体元素:每个元素单独监听(性能差,动态元素需重新绑定)
    // – 父容器委托:性能好,动态元素自动支持
    // – document:适合全局拖拽

    // 推荐:用父容器委托
    dragContainer.addEventListener('mousedown', (e) => {
    const item = e.target.closest('.drag-item')
    if (item) {
    startDrag(item, e)
    }
    })


    四、React性能优化

    问题:useMemo用在哪些场景?

    // 1. useMemo适用场景

    // 1.1 复杂计算
    const expensiveValue = useMemo(() => {
    return data.filter(item => item.value > 100)
    .map(item => item.name)
    .join(', ')
    }, [data])

    // 1.2 保持引用稳定(配合memo)
    const userInfo = useMemo(() => ({
    name: user.name,
    age: user.age
    }), [user])

    // 1.3 避免子组件不必要的渲染
    <Child data={useMemo(() => ({ id, name }), [id, name])} />

    // 2. 错误用法
    // ❌ 简单计算不需要
    const value = useMemo(() => a + b, [a, b]) // 没必要

    // ❌ 依赖频繁变化
    const value = useMemo(() => compute(), [Date.now()]) // 每次都会重新计算

    问题:父组件state变化,子组件会更新吗?如何阻止?

    // 1. 默认行为
    function Parent() {
    const [count, setCount] = useState(0)

    return (
    <div>
    <button onClick={() => setCount(c => c + 1)}>点击</button>
    <Child /> {/* 每次count变化,Child都会重新渲染 */}
    </div>
    )
    }

    // 2. props不变,子组件也会更新
    // 因为父组件重新渲染,默认会重新渲染所有子组件

    // 3. 阻止子组件更新的方法

    // 3.1 React.memo
    const Child = React.memo(function Child() {
    console.log('Child渲染')
    return <div>Child</div>
    })

    // 3.2 useMemo缓存组件
    const child = useMemo(() => <Child />, [])

    // 3.3 使用key
    <Child key={stableId} /> // key变化才重新渲染

    // 3.4 组件拆分
    // 将频繁变化的state隔离到子树

    // 4. 特别注意
    // 即使Child用了memo,如果传入的函数/对象每次变化,也会重新渲染
    // 需要用useCallback/useMemo保持引用稳定


    五、Diff算法

    问题:diff算法的时间复杂度?深度优先还是广度优先?

    // 1. 传统diff(树的最小编辑距离)
    // 时间复杂度:O(n^3)(不可接受)

    // 2. React优化后的diff
    // 时间复杂度:O(n)
    // 基于三个假设:
    // – 不同类型的元素产生不同树(直接替换)
    // – 通过key标识稳定节点
    // – 同层比较,不跨层

    // 3. 比较策略
    // 3.1 广度优先?深度优先?
    // React使用深度优先,但做了优化:
    // – 同层比较
    // – 有key时复用节点

    // 3.2 比较流程
    function diff(oldNode, newNode) {
    if (!oldNode) return insert(newNode)
    if (!newNode) return delete(oldNode)

    if (oldNode.type !== newNode.type) {
    // 类型不同,直接替换
    return replace(oldNode, newNode)
    }

    // 类型相同,比较属性
    updateProps(oldNode, newNode)

    // 比较子节点(核心)
    if (newNode.props.children) {
    diffChildren(oldNode.props.children, newNode.props.children)
    }
    }

    // 3.3 diffChildren算法
    function diffChildren(oldChildren, newChildren) {
    // 双指针遍历 + key匹配
    let oldStart = 0, oldEnd = oldChildren.length – 1
    let newStart = 0, newEnd = newChildren.length – 1

    while (oldStart <= oldEnd && newStart <= newEnd) {
    // 比较节点,移动指针
    }

    // 时间复杂度 O(n) 因为假设了key的存在
    }


    六、useRef vs useState

    问题:useRef和useState的区别?

    // 1. 核心区别
    // useState:变化触发重新渲染
    // useRef:变化不触发重新渲染

    // 2. useState
    const [count, setCount] = useState(0)
    // 每次setCount都会重新渲染组件

    // 3. useRef
    const countRef = useRef(0)
    // 修改countRef.current不会重新渲染
    countRef.current++ // 不触发渲染

    // 4. 使用场景
    // useState:需要在UI上显示的状态
    const [name, setName] = useState('') // 需要显示在input中

    // useRef:不需要显示的数据
    const timerRef = useRef() // 定时器ID
    const prevCountRef = useRef() // 上一次的值
    const inputRef = useRef() // DOM引用

    // 5. 读取时机
    // useState:只能在渲染时读取
    // useRef:可以在任何时候读取

    function Component() {
    const [count, setCount] = useState(0)
    const countRef = useRef(0)

    const handleClick = () => {
    setCount(c => c + 1)
    countRef.current++

    console.log(count) // 0(闭包中的旧值)
    console.log(countRef.current) // 1(最新值)
    }
    }


    七、闭包

    问题:怎么理解闭包?有什么用?怎么销毁?

    // 1. 闭包定义
    // 函数 + 词法环境的引用

    function createCounter() {
    let count = 0 // 被内部函数引用

    return function() {
    count++ // 内部函数引用外部变量
    return count
    }
    }

    const counter = createCounter()
    console.log(counter()) // 1
    console.log(counter()) // 2

    // 2. 闭包的用途
    // 2.1 私有变量
    function createUser(name) {
    let _name = name
    return {
    getName: () => _name,
    setName: (newName) => { _name = newName }
    }
    }

    // 2.2 防抖节流
    function debounce(fn, delay) {
    let timer
    return function(…args) {
    clearTimeout(timer)
    timer = setTimeout(() => fn.apply(this, args), delay)
    }
    }

    // 2.3 函数柯里化
    function add(x) {
    return function(y) {
    return x + y
    }
    }

    // 3. 如何销毁闭包
    // 3.1 解除引用
    counter = null // 不再引用内部函数,闭包被垃圾回收

    // 3.2 避免意外闭包
    // 在useEffect中及时清理
    useEffect(() => {
    const timer = setInterval(() => {
    console.log(count) // 闭包捕获count
    }, 1000)

    return () => clearInterval(timer) // 清理
    }, [count])

    // 3.3 内存泄漏场景
    // 在事件监听中未移除
    element.addEventListener('click', function handler() {
    // 引用了外部大对象
    })
    // 需要removeEventListener


    八、异步处理

    问题:JS异步处理方式?点击事件算异步吗?宏任务微任务?

    // 1. JS异步处理方式
    // 1.1 回调函数
    setTimeout(() => {}, 1000)
    fs.readFile('file.txt', (err, data) => {})

    // 1.2 Promise
    fetch('/api/data')
    .then(res => res.json())
    .then(data => console.log(data))
    .catch(err => console.log(err))

    // 1.3 async/await
    async function fetchData() {
    try {
    const res = await fetch('/api/data')
    const data = await res.json()
    console.log(data)
    } catch (err) {
    console.log(err)
    }
    }

    // 1.4 Generator
    function* generator() {
    const data = yield fetch('/api/data')
    console.log(data)
    }

    // 1.5 定时器
    setTimeout(() => {}, 1000)
    setInterval(() => {}, 1000)

    // 2. 点击事件算异步吗?
    // 点击事件属于异步,但不是宏任务/微任务
    // 它属于Web API,被放入任务队列

    // 3. 宏任务 vs 微任务
    // 宏任务:setTimeout、setInterval、I/O、UI渲染
    // 微任务:Promise.then、MutationObserver、queueMicrotask

    // 4. 执行顺序
    console.log('1') // 同步

    setTimeout(() => {
    console.log('2') // 宏任务
    }, 0)

    Promise.resolve().then(() => {
    console.log('3') // 微任务
    })

    console.log('4') // 同步

    // 输出:1, 4, 3, 2

    // 5. 点击事件顺序
    button.addEventListener('click', () => {
    console.log('click')
    })

    // 当用户点击时,回调会进入任务队列
    // 在所有同步代码和微任务之后执行


    九、跨域

    问题:跨域是什么?为什么要有跨域?如何解决?

    // 1. 跨域是什么?
    // 浏览器同源策略:协议、域名、端口不同

    // 同源:https://example.com:443/page1 和 https://example.com:443/page2
    // 跨域:https://api.example.com 和 https://www.example.com

    // 2. 为什么要有跨域?
    // – 安全隔离:防止恶意网站读取另一个网站的数据
    // – 保护用户隐私:限制访问Cookie等敏感信息
    // – CSRF防护:限制恶意请求

    // 3. 解决方案

    // 3.1 CORS(最常用)
    // 后端设置响应头
    res.setHeader('Access-Control-Allow-Origin', 'https://example.com')
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST')
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
    res.setHeader('Access-Control-Allow-Credentials', 'true')

    // 3.2 JSONP(只支持GET)
    function jsonp(url, callback) {
    const script = document.createElement('script')
    const callbackName = 'callback_' + Date.now()

    window[callbackName] = (data) => {
    callback(data)
    delete window[callbackName]
    document.body.removeChild(script)
    }

    script.src = `${url}?callback=${callbackName}`
    document.body.appendChild(script)
    }

    // 3.3 iframe + postMessage
    // 父页面
    iframe.contentWindow.postMessage(data, 'https://child.com')

    // 子页面
    window.addEventListener('message', (e) => {
    if (e.origin === 'https://parent.com') {
    console.log(e.data)
    }
    })

    // 3.4 代理服务器
    // webpack devServer
    proxy: {
    '/api': 'http://localhost:3000'
    }

    // 3.5 适用场景
    // – CORS:最通用,后端配合
    // – JSONP:老旧项目,只支持GET
    // – iframe+postMessage:跨域窗口通信
    // – 代理:开发环境,绕过跨域


    十、项目部署与Next.js

    问题:项目部署、数据库、transport层、Next.js、数据渲染

    // 1. 部署方式
    // – Vercel(Next.js最佳搭档)
    // – 自建服务器(Nginx + PM2)
    // – 云服务(AWS、阿里云)

    // 2. 数据库
    // – MySQL/PostgreSQL(关系型)
    // – MongoDB(文档型)
    // – Redis(缓存)

    // 3. transport层
    // – HTTP/REST
    // – GraphQL
    // – WebSocket
    // – gRPC

    // 4. Next.js理解
    // 4.1 核心特性
    // – SSR/SSG/ISR多种渲染模式
    // – 文件路由系统
    // – API Routes
    // – 图片优化
    // – 零配置

    // 4.2 优势
    // – SEO友好
    // – 首屏加载快
    // – 开发体验好

    // 5. 从aisdk获取数据渲染
    import { useChat } from 'ai/react'

    function Chat() {
    const { messages, input, handleInputChange, handleSubmit } = useChat()

    return (
    <div>
    {messages.map(m => (
    <div key={m.id}>
    <div>{m.role}: {m.content}</div>
    </div>
    ))}

    <form onSubmit={handleSubmit}>
    <input
    value={input}
    onChange={handleInputChange}
    placeholder="输入消息…"
    />
    </form>
    </div>
    )
    }

    // 6. TanQuery在前端
    import { useQuery } from '@tanstack/react-query'

    function Posts() {
    const { data, isLoading, error } = useQuery({
    queryKey: ['posts'],
    queryFn: () => fetch('/api/posts').then(res => res.json())
    })

    if (isLoading) return <div>加载中…</div>
    if (error) return <div>错误:{error.message}</div>

    return (
    <ul>
    {data.map(post => (
    <li key={post.id}>{post.title}</li>
    ))}
    </ul>
    )
    }


    十一、手撕:防抖

    问题:实现防抖函数

    // 1. 基础版
    function debounce(func, wait) {
    let timer
    return function(…args) {
    clearTimeout(timer)
    timer = setTimeout(() => {
    func.apply(this, args)
    }, wait)
    }
    }

    // 2. 支持immediate
    function debounce(func, wait, immediate = false) {
    let timer

    return function(…args) {
    const callNow = immediate && !timer

    clearTimeout(timer)

    timer = setTimeout(() => {
    timer = null
    if (!immediate) {
    func.apply(this, args)
    }
    }, wait)

    if (callNow) {
    func.apply(this, args)
    }
    }
    }

    // 3. 带取消功能
    function debounce(func, wait, immediate = false) {
    let timer

    const debounced = function(…args) {
    const callNow = immediate && !timer

    clearTimeout(timer)

    timer = setTimeout(() => {
    timer = null
    if (!immediate) {
    func.apply(this, args)
    }
    }, wait)

    if (callNow) {
    func.apply(this, args)
    }
    }

    debounced.cancel = function() {
    clearTimeout(timer)
    timer = null
    }

    return debounced
    }

    // 4. 使用示例
    const search = debounce((keyword) => {
    console.log('搜索:', keyword)
    }, 500)

    search('a')
    search('ab')
    search('abc') // 只有这个会执行

    // 5. React中使用
    function SearchInput() {
    const [value, setValue] = useState('')

    const debouncedSearch = useMemo(
    () => debounce((val) => console.log('搜索:', val), 500),
    []
    )

    const handleChange = (e) => {
    setValue(e.target.value)
    debouncedSearch(e.target.value)
    }

    return <input value={value} onChange={handleChange} />
    }


    十二、AI使用

    问题:平常会怎么用AI?agent用的多吗?

    // 1. AI使用场景

    // 1.1 代码生成
    // – 生成重复性代码(表单、表格)
    // – 生成单元测试
    // – 生成文档注释

    // 1.2 问题排查
    // – 复制错误信息让AI分析
    // – 代码审查建议

    // 1.3 学习辅助
    // – 解释复杂概念
    // – 生成学习大纲

    // 1.4 原型开发
    // – 用自然语言生成初始代码
    // – 快速搭建Demo

    // 2. 常用AI工具
    // – GitHub Copilot(代码补全)
    // – Cursor(AI编辑器)
    // – ChatGPT(问答)
    // – Claude(长上下文)

    // 3. Agent使用
    // 3.1 什么是Agent
    // – 能自主规划、调用工具的AI
    // – 可执行复杂任务

    // 3.2 Agent应用场景
    // – 自动化测试生成
    // – 代码重构建议
    // – 项目文档生成

    // 3.3 如何用好AI
    // – 提供清晰上下文
    // – 分步拆解复杂任务
    // – 验证AI输出
    // – 沉淀prompt模板

    // 4. 注意事项
    // – 不依赖AI,要理解代码
    // – 敏感代码不直接贴给AI
    // – 验证AI输出的准确性


    📚 知识点速查表

    知识点核心要点
    CSS变量 动态主题、黑暗模式、prefers-color-scheme
    Less 变量、嵌套、混合、函数
    原子CSS 工具类、与传统CSS对比
    拖拽实现 事件委托、target/currentTarget、层级管理
    useMemo 复杂计算、引用稳定、优化子组件
    组件更新 React.memo、useMemo、useCallback
    Diff算法 O(n)、同层比较、key的作用
    useRef/useState 渲染触发、引用稳定
    闭包 词法环境、私有变量、内存释放
    异步 宏任务/微任务、执行顺序
    跨域 同源策略、CORS、JSONP、postMessage
    Next.js SSR/SSG、文件路由、API Routes
    防抖 定时器、immediate、cancel
    AI使用 Copilot、代码生成、问题排查

    📌 最后一句:

    拓竹科技的这场面试,覆盖了前端开发的方方面面,从CSS工程化到React性能优化,从拖拽实现到跨域原理,再到AI使用。每个问题都在考察原理理解和实战经验的结合。能答好这些,说明你已经具备了独立应对复杂前端开发的能力。

    赞(0)
    未经允许不得转载:171主机测评 » 前端八股文面经大全:拓竹科技前端一面(2026-03-15)·面经深度解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址