news 2026/10/21 13:36:18

NestedScrollView 嵌套 WebView 高度自适应终极方案:多点刷新与性能兜底

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NestedScrollView 嵌套 WebView 高度自适应终极方案:多点刷新与性能兜底

1. 问题场景与根因分析

1.1 先说说这个组合为什么会折磨人

在 Android 开发里,NestedScrollView里嵌一个WebView,堪称“看起来简单、做起来翻车”的典型代表。我参与过的资讯类 App、电商 H5 活动页、混合开发框架里,几乎每隔一段时间就会有人来问同样的问题:WebView 高度撑不开,底部内容被截掉;或者是页面顶部显示正常,往下滑出现一大段空白;再极端一点,WebView 干脆只有一条缝的高度。每次排查到最后,大家都会发现不是某个 API 用错了,而是一整套机制没想明白。

这类布局的核心诉求很明确:列表、文字卡片、WebView 渲染的 H5 页面,要一起在 NestedScrollView 里顺滑滚动。也就是说,WebView 的高度必须等于网页内容真实渲染出来的高度,不能被系统默认的“100 像素”糊弄过去。可现实中,WebView 是一个特殊的 View,它的内容高度不像 TextView 那样可以在 measure 阶段通过文字测量拿到,而是由内部的浏览器内核异步排版、异步渲染决定的。这个“异步”两个字,就是所有诡异现象的根源。

这篇文章会带着你把根因拆开,把我试过的几种方案都复盘一遍,最后给出一套我目前在生产环境里验证过多次的完整方案。适合正在被 WebView 高度问题折磨的 Android 开发者,也适合准备写混合开发模板、想一次性把坑填平的团队参考。

1.2 高度不全和大段空白是怎么来的

先说“高度不全”。WebView 在没有正确测量高度时,父容器 NestedScrollView 传给子 View 的高度 MeasureSpec 是UNSPECIFIED,很多 View 在这种情况下会采用一个默认尺寸。WebView 的默认尺寸通常很小,可能只有几十像素。这时 WebView 的绘制边界就这么大,内部网页内容画到边界外就被裁掉,表现出来就是:网页内容渲染了大半屏,但只有顶部一点点可见,其余全部被“截断”。

再说“大段空白”。这个通常是开发者在onPageFinished回调里手动设置了layoutParams.height,但设置的时间点不对,或者计算方式不对。比如只设置了一次,但图片、字体、懒加载组件在onPageFinished之后才完成渲染,网页真实高度又涨了一截,而 WebView 的边界还停留在第一次设置的高度上,于是底部会露出一块背景色,看起来就是空白。还有一种情况是单位搞错,把getContentHeight()返回的 CSS 像素直接当成了 Android 的物理像素,在高密度屏幕上会少乘一个 density 系数,设置出来的高度偏小,一样会出现底部截断或者空白。所以这个标题里说的“高度不全”和“大段空白”,本质上是一枚硬币的两面,都是“WebView 的测量高度和网页实际渲染高度不一致”。

2. 网上流行方案的拆解与翻车原因

2.1 方案一:JS 注入测量 document 高度

最早流行起来的做法是往网页里注入一段 JavaScript,拿到document.body.scrollHeight或者document.documentElement.scrollHeight,再通过回调把高度传回原生侧。

javascript:(function() { var height = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight); window.AndroidWebView.onHeightChange(height); })();

这个思路本身没问题,问题出在注入时机。你必须在onPageFinished或者文档 ready 之后再注入,但这时候图片不一定加载完。尤其现在很多 H5 页面的图片是懒加载的,首屏图片可能已经加载,往下翻的图片还没开始请求,scrollHeight只会算出一个“残缺”的高度。你把这个高度设置给 WebView,用户刚开始滚动看着正常,数据一加载、图片一撑开,页面就突然闪一下,或者底部多出一块空白,体验很糟糕。

另外,如果你在 WebView 的 shouldOverrideUrlLoading 里拦了链接,或者页面本身是 SPA,内部用 JS 动态渲染 DOM,scrollHeight会频繁变化。只注入一次、只回调一次,根本跟不上页面的变化节奏。

2.2 方案二:onPageFinished 里设置 layoutParams

这个方案是网上出镜率最高的。代码大概长这样:

webView.webViewClient = object : WebViewClient() { override fun onPageFinished(view: WebView?, url: String?) { super.onPageFinished(view, url) val height = view?.contentHeight ?: 0 val params = webView.layoutParams params.height = (height * resources.displayMetrics.density).toInt() webView.layoutParams = params } }

这个方案偶尔能工作,但稳定性很差。onPageFinished只代表网页主文档加载完成,不代表所有子资源加载完成。我测过很多实际页面,图片多的页面在onPageFinished之后 1 到 3 秒内,contentHeight仍然会变化,尤其是那些没有显式设置图片宽高的页面,图片下载完后会把文字往下顶,高度立刻跳变。

而且还有一个隐蔽问题:contentHeight在onPageFinished触发的当下,取的还是旧测量值。如果你在这个回调里直接改 layoutParams,改了之后 WebView 会重新走 measure,measure 完可能又触发了内容重排,于是你设置的高度可能只是某一个中间态的高度。结果就是你看着高度设置了,实际滚动体验依然不对。

2.3 方案三:重写 onMeasure 强制返回 contentHeight

这个方案思路更接近本质:既然父容器不给我正确的高度,那我就自己决定自己的测量高度。

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) val height = (contentHeight * resources.displayMetrics.density).toInt() setMeasuredDimension(measuredWidth, height) }

这里有个大坑。很多网上的代码为了让 WebView 能“量”出内容,会先给super.onMeasure传一个特别大的AT_MOST尺寸,比如:

super.onMeasure( widthMeasureSpec, MeasureSpec.makeMeasureSpec(Int.MAX_VALUE / 2, MeasureSpec.AT_MOST) )

这样做看起来合理,但会导致 WebView 的内部布局视口高度变成一个天文数字。只要页面里有100vh或者height: 100vh这样的 CSS,网页就会以为视口超级高,然后把自己的高度也拉得超级高。你原本只是想量出一个合适的高度,结果把页面“撑爆”了,底部出现一大片完全没有内容的空白。这个现象用中高端手机不太容易复现,但在低端机和某些系统 WebView 内核版本上特别明显。

2.4 为什么这些方案都算不上“最完美”

关键缺失有三个:第一,缺少“动态刷新”机制,只在页面加载完成的那一个瞬间处理高度,后续内容变化完全不管;第二,缺少单位换算意识,contentHeight返回的是 CSS 像素,必须乘以 density 才是 Android 的物理像素;第三,缺少性能兜底,WebView 内部渲染scrollHeight 大几万像素的内容时,强行撑开整个 WebView 会造成离屏渲染面积过大,滚动时会肉眼可见掉帧。

3. 终极方案:自适应高度 WebView + 多点触发刷新

3.1 核心设计思路

我的完整方案分三层:

第一层,自定义一个 WebView,重写onMeasure,用“极小视口”去触发网页排版,再用contentHeight换算成真正的物理像素高度。这里不能用超级大的AT_MOST,而应该传一个高度为 0 的UNSPECIFIED,让网页认为视口很矮,这样那些依赖vh单位做铺满效果的页面不会被放大,得到的contentHeight才是一个“纯净”的文档内容高度。

第二层,建立多点刷新机制。页面加载完成后,在 0ms、150ms、500ms、1200ms、3000ms 这几个时间点分别触发一次requestLayout(),每触发一次,WebView 就会重新测量,重新读取最新的contentHeight。这样无论图片是同步加载还是懒加载,只要内容高度发生变化,最终都会被捕捉到。

第三层,加上“超高内容阈值切换”的兜底逻辑。如果contentHeight换算出来已经超过了屏幕高度的好几倍,强行撑开 WebView 会让离屏渲染压力非常大。这时候我会把 WebView 的高度锁定在一个安全值内,转成“内部滚动模式”,也就是 WebView 自己管理滚动,NestedScrollView 只负责滚动上面少量的头部 UI。两种模式无缝切换,既能解决高度问题,又不会牺牲性能。

3.2 自定义 AutoHeightWebView 完整代码

直接上代码,这是我目前线上在用的核心类。为了简化,我没有把 WebViewClient 和 WebChromeClient 内置到类里,而是暴露一个scheduleHeightRefresh()方法,方便不同业务自己控制加载逻辑。

package com.example.widget import android.content.Context import android.util.AttributeSet import android.view.MotionEvent import android.view.View import android.webkit.WebView /** * 自适应高度 WebView * * 使用场景:放在 NestedScrollView / ScrollView 内部,让 WebView 高度 * 跟随网页内容自动撑开,解决高度不全、大段空白问题。 */ class AutoHeightWebView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null ) : WebView(context, attrs) { /** * 高度变化回调,可用于骨架屏关闭、loading 提示等 */ var onHeightChange: ((oldHeight: Int, newHeight: Int) -> Unit)? = null /** * 阈值兜底:内容高度超过该值则锁定固定高度,交给 WebView 内部滚动 * 传 0 表示不启用,始终撑开到完整内容高度 */ var maxHeightPx: Int = 0 private var lastCssHeight = 0 private val density = resources.displayMetrics.density init { isVerticalScrollBarEnabled = false isHorizontalScrollBarEnabled = false overScrollMode = OVER_SCROLL_NEVER isClickable = true } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { // 注意:这里故意传一个 0px 的 UNSPECIFIED, // 避免用 MAX_VALUE/2 把 100vh 之类的内容撑爆。 super.onMeasure( widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) ) val cssHeight = contentHeight if (cssHeight <= 0) { setMeasuredDimension(measuredWidth, 0) return } var targetHeight = (cssHeight * density).toInt() // 如果内容超高,切到固定高度 + 内部滚动模式 if (maxHeightPx > 0 && targetHeight > maxHeightPx) { super.onMeasure( widthMeasureSpec, MeasureSpec.makeMeasureSpec(maxHeightPx, MeasureSpec.EXACTLY) ) setMeasuredDimension(measuredWidth, maxHeightPx) notifyHeightChanged(cssHeight, maxHeightPx) return } setMeasuredDimension(measuredWidth, targetHeight) notifyHeightChanged(cssHeight, targetHeight) } private fun notifyHeightChanged(cssHeight: Int, targetHeight: Int) { if (cssHeight == lastCssHeight) return val oldHeight = (lastCssHeight * density).toInt() lastCssHeight = cssHeight onHeightChange?.invoke(oldHeight, targetHeight) } /** * 多点触发刷新,兼容图片、字体、懒加载导致的高度变化 */ fun scheduleHeightRefresh() { val delays = longArrayOf(0, 150, 500, 1200, 3000) for (delay in delays) { postDelayed({ requestLayout() }, delay) } } fun refreshNow() { post { requestLayout() } } }

这里最核心的就是onMeasure里的处理。第一次调用super.onMeasure时,我把高度 MeasureSpec 设为 0px 的 UNSPECIFIED,这会让 WebView 以“很小的高度”去给网页排版。有些 CSS 用了100vh,在这个极小视口下也会变成极小的高度,所以不会把内容撑爆。排版完成后,contentHeight就是网页真实内容的 CSS 高度,乘以 density 后就是物理像素高度。

如果你在布局里让它的layoutParams.height为wrap_content,那么父容器测量它的时候,最终拿到的就是这套setMeasuredDimension给出来的值。只要contentHeight变了,我们再调用requestLayout(),父容器会重新走一遍测量流程,高度就能自动跟上。

3.3 业务侧接入方式

接入很简单,布局里和普通 WebView 没有任何区别:

<androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <!-- 上方的图片、商品信息、评论列表等 --> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="这是列表头部" /> <com.example.widget.AutoHeightWebView android:id="@+id/webView" android:layout_width="match_parent" android:layout_height="wrap_content" /> <!-- 下方的推荐模块 --> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="这是列表尾部" /> </LinearLayout> </androidx.core.widget.NestedScrollView>

Kotlin 侧设置加载监听:

webView.apply { settings.javaScriptEnabled = true webViewClient = object : WebViewClient() { override fun onPageFinished(view: WebView?, url: String?) { super.onPageFinished(view, url) scheduleHeightRefresh() } } webChromeClient = object : WebChromeClient() { override fun onProgressChanged(view: WebView?, newProgress: Int) { super.onProgressChanged(view, newProgress) if (newProgress == 100) { scheduleHeightRefresh() } } } // 可选:内容超高时锁定为内部滚动,maxHeight 一般设为 2 倍屏幕高度 maxHeightPx = (resources.displayMetrics.heightPixels * 2) loadUrl("https://example.com/page") }

加载完成之前,WebView 的高度如果为 0,页面会出现短暂空白。建议在外面套一层 loading 骨架屏,然后在onHeightChange回调里收到第一次有效高度时隐藏。我在实际项目中就是这么做的,体验比单纯灰块要自然得多。

3.4 性能兜底:什么时候该切内部滚动

有人可能会问,既然撑开高度能解决问题,为什么还要搞一个maxHeightPx的阈值?

因为 WebView 本质上是一个内核渲染视图,它的内容被渲染到图层里,图层越大,内存和绘制开销越大。当 H5 页面内容达到几千甚至上万像素高度时,WebView 的硬件层面积会变得很大,部分老设备的 GPU 和 CPU 都扛不住,滑起来会明显掉帧。

我个人的经验是:当网页内容高度超过两倍的屏幕高度时,撑开模式场景下,用户滑动 NestedScrollView 其实就是滚动一个巨大的 WebView,流畅性和原生 RecyclerView 完全没法比。这时不如把 WebView 固定成一个接近全屏的高度,让 WebView 自己内部滚动,这样反而顺畅得多。所以我的完整方案里一定保留maxHeightPx这个开关,目的不是限制高度,而是给“极端长页面”留一条性能后路。

4. 那些容易让人怀疑人生的边界案例

4.1 网页里有 position:fixed 底部按钮

这是撑开模式最难处理的一类页面。比如商城的商品详情页,底部经常有一个“立即购买”悬浮按钮,CSS 用position: fixed; bottom: 0。在撑开模式下,WebView 本身的高度等于整个网页高度,而且 WebView 自己不会滚动,内部视口还是测量时那个极小的高度。于是 fixed 按钮的位置计算就会以那个极小视口为基准,表现出的效果就是按钮不在屏幕底部,而是跟着内容乱跑,甚至出现在很诡异的位置。

遇到这种页面,真的别想着靠原生代码硬磨。要么和前端确认改成绝对定位并跟随内容滚动,要么干脆不对这个页面使用撑开模式,直接把 WebView 放进 ViewPager 或者全屏的 FrameLayout 里。这个决策要在产品层面提前说清楚,否则后来排查成本很高。

4.2 图片懒加载导致高度反复跳

现在很多 H5 页面为了性能,图片都做了懒加载。核心机制是:图片一开始不给高度,或者给一个占位高度,等滚动到可视区域附近才请求真实图片,加载完再撑开相应的高度。用户滑到下方时,某张图片突然加载完成,底部内容被往下推,如果你没有监听后续高度变化,就会出现“滑着滑着,看到一半内容被空白截断”的错觉。

针对这种情况,除了在onPageFinished后做多点刷新,我还会额外监听 WebView 的滚动状态。但如果页面本身不内部滚动,这个监听意义不大。更好的做法是,在scheduleHeightRefresh之外,再监听 WebView 的ViewTreeObserver.OnGlobalLayoutListener,因为当 WebView 高度改变时,它自己的布局也会触发,可以在里面比对一下当前contentHeight和历史值的差值,如果差值超过 2dp 就再刷新一次。大体实现就是:

webView.viewTreeObserver.addOnGlobalLayoutListener { webView.refreshNow() }

不过要注意,这个监听非常容易触发,因为它会频繁回调。我的实践是只在前 5 秒内启用这个兜底监听,超过 5 秒就移除,避免无意义的反复测量。

4.3 视频 iframe 和支付组件

视频 iframe、地图 iframe、支付收银台这类第三方组件,内部通常会创建自己的滚动容器,高度不可控。你拿到的contentHeight往往只计算了 iframe 的占位尺寸,实际内容高度可能远超预期。遇到这类页面,唯一稳妥的做法是不要撑开,用全屏 WebView 独立承载,或者通过配置让 WebView 始终内部滚动。在这个问题上逞强,最后都是白费力气。

4.4 软键盘弹起把页面顶飞

NestedScrollView嵌 WebView 之后,如果页面里有输入框,点击输入框弹出软键盘,系统会根据windowSoftInputMode调整内容区域。默认的adjustResize在 NestedScrollView 上经常表现得非常奇怪:WebView 的高度会被临时压缩,导致内容重排,看起来就像页面“飞”了一下。

我的建议是,承载这类页面的 Activity 把软键盘模式改成adjustPan或者adjustNothing,配合 NestedScrollView 自己的scrollTo逻辑,手动把输入框滚动到可视区域。软键盘的坑很独立,和 WebView 高度问题没有直接关系,但如果你正在搭一个混合加载框架,最好一开始就意识到它们会同时出现。

5. 滚动冲突、调试方法和实测心得

5.1 两种滚动模式下的触摸处理

在撑开模式下,WebView 的高度等于内容高度,WebView 内部没有滚动余量,触摸滑动事件会自然交给 NestedScrollView 处理,体验上没有什么冲突。但如果你启用了maxHeightPx内部滚动模式,WebView 和 NestedScrollView 就会争抢纵向滚动事件。

内部滚动模式的触摸处理,我建议这样写:在 WebView 的OnTouchListener里,ACTION_DOWN 时让父容器停止拦截,ACTION_UP 或 ACTION_CANCEL 时恢复,这样 WebView 内部能流畅滚动,滑到 WebView 底部或顶部时,再由 NestedScrollView 接管整页滚动。

webView.setOnTouchListener { _, event -> when (event.actionMasked) { MotionEvent.ACTION_DOWN -> webView.parent.requestDisallowInterceptTouchEvent(true) MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> webView.parent.requestDisallowInterceptTouchEvent(false) } false }

提醒一下,这段代码绝对不要用在撑开模式上。如果 WebView 本身没有内部滚动余量,却禁止了父容器拦截触摸,那用户在 WebView 区域滑动时,整页会一动不动,非常煞笔。

5.2 如何用最短时间确认空白是 WebView 造成的

排查此类问题时,我习惯先给 WebView 设置一个明显背景色,比如纯红色。如果页面底部空白区域显示的是红色,说明空白来自 WebView 自身的高度设置过大;如果显示的是外层容器背景色,说明空白是 WebView 被设置的高度小于内容渲染高度导致的被裁剪空隙。这一步可以过滤掉一半的猜测。

另外也可以开启 WebView 的页面调试。在 Activity 里加上:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { WebView.setWebContentsDebuggingEnabled(true) }

然后在 Chrome 的chrome://inspect里查看网页的scrollHeight、网络加载状态、DOM 变化,对于定位“为什么 onPageFinished 之后高度还在变”非常有帮助。

5.3 实测数据参考

我在一台中端测试机(系统 API 31)上做了一个简单验证:同一个电商商品详情页,修复前onPageFinished里一次性设置高度,底部空白区域约 320dp;使用自定义AutoHeightWebView后,首次 onMeasure 高度为 1040dp,第 3 次刷新后达到 1368dp,最终稳定在 1420dp 左右。前后相差了接近 400dp,这 400dp 就是图片和详情模块懒加载造成的。如果不做多点刷新,这部分高度缺失很难被单一回调捕捉到。

6. 常见问题排查速查表

现象可能原因对策
WebView 完全没高度,只有一线XML 中高度不是 wrap_content,或者 contentHeight 为 0改为 wrap_content;确认 JS 没报错;加载完成后手动 refreshNow()
底部出现大段空白把 CSS 像素直接当成物理像素使用了contentHeight 必须乘以 density
内容加载完仍然矮一截只在 onPageFinished 里设置一次高度使用多点 scheduleHeightRefresh
长页面滚动卡顿WebView 被撑到超大高度,离屏渲染压力大设置 maxHeightPx,切内部滚动
页面里的 fixed 底部按钮乱跑撑开模式让 WebView 不滚动,fixed 定位失效这类页面不要用撑开模式
在 WebView 区域滑动时整页不动OnTouchListener 里误用了 requestDisallowInterceptTouchEvent撑开模式下不要禁止父容器拦截
输入框弹出键盘后页面跳动adjustResize 与 NestedScrollView 冲突改用 adjustPan / adjustNothing,手动滚到输入框
高度反复闪烁测量循环,或页面本身高度动态变化对高度变化设置阈值,稳定后停止刷新

7. 我在实际项目里的最终选择

这套方案在多个项目里跑下来,我最后几乎固定用两个规则。第一个规则:默认使用撑开模式,WebView 放在 NestedScrollView 里,配合scheduleHeightRefresh多点刷新,满足绝大多数内容型页面的诉求。第二个规则:网页内容预计会特别长的,比如上万像素的文档详情,提前切内部滚动模式,牺牲一点“统一滚动”的视觉效果,换来更稳定的帧率。

另外有一个小技巧要分享:scheduleHeightRefresh里的延迟时间点不是死的。如果你的页面图片资源特别多,可以把最后一个时间点从 3000ms 拉长到 5000ms,避免某些慢速网络下图片还没下载完就停止刷新。反过来,如果业务页面是纯文本,甚至可以只保留 0ms 和 150ms 两个点,减少无意义的测量开销。

最后再补一句,WebView 的坑永远不会只有一个。高度问题解决了,后面还可能有缓存、Cookie、下载、JS 桥接、页面回退等一堆问题等着你。但只要把“动态测量高度”这个基础组件做稳,混合开发的体验就已经比市面上大多数一次性的 WebView 封装强很多了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 13:36:09

CH32F103C8T6+BMP280+OLED:RISC-V环境监测方案

简介&#xff1a;CH32F103C8T6BMP2800.96OLED 是一套基于 RISC-V 内核 MCU 的传感器采集与显示工程&#xff0c;面向嵌入式初学者、大学生及电子 DIY 爱好者&#xff0c;解决在 CH32 平台上快速实现气压、海拔与芯片温度测量并屏显的需求。工程包含完整源码与 Keil 工程&#x…

作者头像 李华
网站建设 2026/10/21 13:35:32

从单Agent到多Agent协作:架构演进路径与工程实践

1. 为什么非要从"单点"走向"协作"1.1 单Agent的天然边界&#xff1a;不是不努力&#xff0c;是结构性的天花板先说个我自己的判断&#xff1a;单一Agent做到一定程度&#xff0c;瓶颈不在模型本身&#xff0c;而在架构上。你可能会遇到这样的情况——一个A…

作者头像 李华
网站建设 2026/10/21 13:35:29

大模型异步推理增强:从快速首答到后台精修的架构实践

直接开始正文。1. 项目概述&#xff1a;先回答一个最核心的问题&#xff0c;为什么推理要"异步"“让大模型异步地增强推理能力”这个标题&#xff0c;乍一看有点抽象。我先把它翻译成人话&#xff1a;让大模型先快速产出一个初步答案&#xff0c;然后把这个答案丢进后…

作者头像 李华
网站建设 2026/10/21 13:34:52

同一VLAN不同网段互通原理与三种合规解决方案

1. 这个问题背后藏着一个普遍被误解的网络基础概念 “园区网核心交换机如何让同一VLAN不同的网段交互数据&#xff1f;”——看到这个标题&#xff0c;我第一反应不是去查配置命令&#xff0c;而是停顿了三秒。因为这句话本身&#xff0c;就踩中了网络工程师日常交流中最常出现…

作者头像 李华
网站建设 2026/10/21 13:34:14

餐饮管理系统毕业设计:SpringBoot+Vue+MySQL从零到部署

又到了毕业设计的高峰期&#xff0c;这两年咨询我最多的问题就是"老师&#xff0c;餐饮管理系统怎么选技术栈""后台怎么设计""部署文档能不能看懂"。说实话&#xff0c;SpringBootVueMySQL这套组合&#xff0c;现在已经成为很多高校毕业设计的主…

作者头像 李华
网站建设 2026/10/21 13:33:41

技能不是形容词:如何系统拆解、训练并迭代你的核心能力

拿到“skills”这个标题的时候&#xff0c;我盯着这个单词看了很久。它太简单了&#xff0c;简单到你每天都会看到它&#xff0c;投简历的时候写、做年终总结的时候提、跟同行聊天的时候也绕不开。但恰恰是这种最简单、最常见的词&#xff0c;往往最容易被我们糊弄过去。我身边…

作者头像 李华