前端老铁必看:搞定回流重绘性能翻倍,DocumentFragment加Vue keep-alive真香实战
做电商或者后台管理系统的兄弟肯定遇到过这种场景:用户在一个很长的商品列表页,滚到第50条,点进去看详情,看完返回,卧槽,列表回到顶部了,之前筛选的条件也全没了,用户当场崩溃,产品经理提着刀来找你。在这个例子中,切换到Detail再切回List,List组件不会被销毁,它的所有状态(滚动位置、筛选条件、表单数据)都还在。比如你把背景色从红色改成蓝色,元素的位置大小都没变,浏览器只需要重新填充颜色就行

前端老铁必看:搞定回流重绘性能翻倍,DocumentFragment加Vue keep-alive真香实战
前端老铁必看:搞定回流重绘性能翻倍,DocumentFragment加Vue keep-alive真香实战
开篇先唠两句
兄弟们,有没有遇到过这种尴尬时刻?页面卡成PPT,鼠标点一下半天没反应,用户在那疯狂点击,结果直接给你整出个"页面无响应"。你打开任务管理器一看,好家伙,CPU占用直接飙到90%,风扇转得跟直升机似的。这时候你心里肯定在骂:我代码明明没写错啊,逻辑都对,怎么就跑成这德行了?
我跟你说,这种情况十之八九是**回流(Reflow)和重绘(Repaint)**这俩兄弟在搞事情。今天咱不整那些虚头巴脑的理论,就聊怎么让页面丝滑如德芙。很多干了三四年的前端,你问他回流重绘到底啥关系,他照样给你懵圈。没关系,看完这篇,保证你出去跟人吹牛逼都有底气。
回流重绘这俩兄弟到底咋回事
浏览器渲染流水线你得先整明白
要搞清楚这俩货,得先知道浏览器是怎么把代码变成你屏幕上看到的画面的。这个过程大概分这么几步:
- 解析HTML → 构建DOM树
- 解析CSS → 构建CSSOM树
- DOM + CSSOM → 合成渲染树(Render Tree)
- 布局(Layout) → 计算每个节点的大小和位置
- 绘制(Paint) → 把像素画到屏幕上
- 合成(Composite) → 图层合并,最终呈现
这里面第4步就是回流,第5步就是重绘。简单说,回流就是浏览器重新计算元素的位置和大小,重绘就是重新填充像素颜色。
DOM一变浏览器就得重新算布局,这就是回流
回流这玩意儿特别烦人。你想啊,页面里一个元素的位置变了,它后面的元素是不是也得跟着动?就像排队,前面的人突然往后退一步,后面所有人都要挪位置。所以回流的成本是几何级增长的,牵一发而动全身。
比如你这么写:
// 糟糕的做法 - 触发三次回流
const box = document.getElementById('box');
box.style.width = '100px'; // 第一次回流
box.style.height = '100px'; // 第二次回流
box.style.margin = '10px'; // 第三次回流
浏览器每次读到一行样式修改,都得重新算一遍布局。三次修改,三次回流,性能直接炸裂。
样式一变但布局不变,那叫重绘,开销小点但也不能瞎搞
重绘就温柔多了。比如你把背景色从红色改成蓝色,元素的位置大小都没变,浏览器只需要重新填充颜色就行,不需要重新计算布局。
// 这只会触发重绘,不会触发回流
box.style.backgroundColor = 'red';
box.style.color = 'blue';
但是!重绘虽然比重流便宜,也不是不要钱。如果你每秒重绘几百次,照样卡死你。而且重绘一定发生在回流之后,如果你触发了回流,必然跟着触发重绘,这就是双重打击。
为啥说回流比重绘更费性能,给你算笔账就懂了
咱们来算笔账。假设页面有1000个DOM节点:
- 重绘一次:浏览器需要遍历渲染树,重新绘制受影响的区域,大概需要5ms
- 回流一次:浏览器不仅要遍历,还要重新计算几何信息,然后还要重绘,大概需要50ms
看起来差10倍?太天真了!回流还有个副作用——它会强制刷新渲染队列。啥意思呢?现代浏览器为了优化性能,会把一堆样式修改攒在一起,等到关键时刻(比如读取布局信息)再统一处理。但如果你在修改样式的过程中,突然去读取了某个元素的offsetHeight,浏览器为了保证数据准确,必须立即执行之前积压的所有回流操作。
// 致命代码 - 强制同步回流
function killPerformance() {
const boxes = document.querySelectorAll('.box');
for (let i = 0; i < boxes.length; i++) {
const box = boxes[i];
// 修改样式
box.style.width = '100px';
// 立即读取布局信息 - 强制回流!
console.log(box.offsetHeight); // 这里触发回流
}
}
// 1000个元素就是1000次回流,页面直接卡死
哪些操作会触发回流,踩过的坑都给你们列出来
我整理了个清单,这些操作都会触发回流,平时能躲就躲:
元素几何属性相关的读写:
// 读取这些属性会强制回流
elem.offsetLeft/offsetTop/offsetWidth/offsetHeight
elem.clientLeft/clientTop/clientWidth/clientHeight
elem.scrollLeft/scrollTop/scrollWidth/scrollHeight
elem.getBoundingClientRect()
elem.getComputedStyle()
修改这些样式会触发回流:
width, height, padding, margin, border-width
position, top, left, right, bottom
display, float, clear, overflow
font-size, line-height, vertical-align
DOM操作:
appendChild, removeChild, insertBefore
innerHTML, outerHTML
cloneNode
窗口操作:
window.resize
window.scroll
实战避坑指南:
// ❌ 错误示范 - 循环中读取offsetHeight
function badExample() {
const container = document.getElementById('list');
for (let i = 0; i < 1000; i++) {
const item = document.createElement('div');
item.className = 'item';
container.appendChild(item); // 每次都可能触发回流
// 致命一击!
console.log(container.offsetHeight);
}
}
// ✅ 正确做法 - 先批量操作,最后统一读取
function goodExample() {
const container = document.getElementById('list');
const fragment = document.createDocumentFragment(); // 等会讲这神器
for (let i = 0; i < 1000; i++) {
const item = document.createElement('div');
item.className = 'item';
fragment.appendChild(item); // 操作fragment不触发回流
}
container.appendChild(fragment); // 只触发一次回流
// 所有操作完再读取
console.log(container.offsetHeight);
}
DocumentFragment这玩意儿有多香
一次性插入一堆DOM节点,别傻傻地循环append
兄弟们,我见过太多人写这种代码了:
// 菜鸟写法 - 卡到怀疑人生
function renderList(data) {
const list = document.getElementById('list');
list.innerHTML = ''; // 清空,触发回流
data.forEach(item => {
const li = document.createElement('li');
li.textContent = item.name;
li.className = 'list-item';
// 每次appendChild都触发回流!
list.appendChild(li);
});
}
// 如果data有1000条,就是1000次回流,页面直接假死
这种写法在数据量小的时候看不出来,一旦超过500条,用户就能明显感觉到卡顿。为啥?因为每次appendChild都会改变DOM结构,浏览器就要重新计算布局。
这货就是个临时容器,不会触发多次回流
DocumentFragment是个啥?简单说就是个轻量级的文档对象,它存在于内存中,不属于主DOM树。你对它进行任何操作,都不会触发浏览器的回流或重绘,直到你把它塞进真正的DOM里。
// 使用DocumentFragment优化
function renderListOptimized(data) {
const list = document.getElementById('list');
list.innerHTML = ''; // 还是清空,但只触发一次
// 创建一个文档片段
const fragment = document.createDocumentFragment();
data.forEach(item => {
const li = document.createElement('li');
li.textContent = item.name;
li.className = 'list-item';
// 操作fragment,零回流!
fragment.appendChild(li);
});
// 一次性插入,只触发一次回流
list.appendChild(fragment);
}
看到没?同样是1000条数据,第一种写法1000次回流,第二种只有2次(清空+插入),性能差距能有几百倍!
实际代码对比给你看,性能差距能吓你一跳
咱们来个真实的性能测试:
// 测试代码
function performanceTest() {
const data = Array.from({length: 5000}, (_, i) => ({name: `Item ${i}`}));
// 测试1:直接操作DOM
console.time('直接操作DOM');
const list1 = document.createElement('div');
data.forEach(item => {
const div = document.createElement('div');
div.textContent = item.name;
list1.appendChild(div); // 每次触发回流
});
document.body.appendChild(list1);
console.timeEnd('直接操作DOM'); // 大概 150-300ms
// 测试2:使用DocumentFragment
console.time('使用Fragment');
const list2 = document.createElement('div');
const fragment = document.createDocumentFragment();
data.forEach(item => {
const div = document.createElement('div');
div.textContent = item.name;
fragment.appendChild(div); // 零回流
});
list2.appendChild(fragment); // 只触发一次
document.body.appendChild(list2);
console.timeEnd('使用Fragment'); // 大概 10-20ms
// 测试结果:性能提升 10-30倍!
}
// 更狠的对比 - 带样式计算
function heavyPerformanceTest() {
const data = Array.from({length: 1000}, (_, i) => i);
// 菜鸟版 - 强制同步回流
console.time('菜鸟版');
const container = document.createElement('div');
document.body.appendChild(container);
data.forEach(i => {
const div = document.createElement('div');
div.style.width = `${i}px`;
container.appendChild(div);
// 致命错误!读取导致强制回流
const height = container.offsetHeight;
});
console.timeEnd('菜鸟版'); // 可能超过 1000ms
// 大神版 - 批量操作
console.time('大神版');
const container2 = document.createElement('div');
const fragment = document.createDocumentFragment();
data.forEach(i => {
const div = document.createElement('div');
div.style.width = `${i}px`;
fragment.appendChild(div);
});
container2.appendChild(fragment);
document.body.appendChild(container2);
// 最后才读取
const height = container2.offsetHeight;
console.timeEnd('大神版'); // 大概 20-30ms
// 差距:50倍以上!
}
创建 fragment 的几种写法,哪种更顺手你自己挑
除了标准的createDocumentFragment,还有几种变体写法:
// 写法1:标准写法
const fragment = document.createDocumentFragment();
// 写法2:使用模板元素(HTML5)
const template = document.createElement('template');
template.innerHTML = `
<div class="item">Item 1</div>
<div class="item">Item 2</div>
`;
const fragment2 = template.content; // 这也是个DocumentFragment
// 写法3:cloneNode大法(适合静态结构)
const templateNode = document.createElement('div');
templateNode.innerHTML = '<span class="tag"></span>';
const fragment3 = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
// cloneNode比createElement快一点
const clone = templateNode.firstChild.cloneNode(true);
clone.textContent = `Tag ${i}`;
fragment3.appendChild(clone);
}
// 写法4:Range对象(高级玩法)
const range = document.createRange();
const fragment4 = range.createContextualFragment(`
<div>HTML字符串直接转fragment</div>
<p>适合从接口返回的HTML</p>
`);
性能对比:
function compareCreationMethods() {
const count = 10000;
// 方法1:createDocumentFragment + createElement
console.time('createElement');
const f1 = document.createDocumentFragment();
for (let i = 0; i < count; i++) {
const div = document.createElement('div');
div.textContent = 'test';
f1.appendChild(div);
}
console.timeEnd('createElement');
// 方法2:cloneNode
console.time('cloneNode');
const template = document.createElement('div');
template.innerHTML = '<div>test</div>';
const f2 = document.createDocumentFragment();
for (let i = 0; i < count; i++) {
const clone = template.firstChild.cloneNode(true);
f2.appendChild(clone);
}
console.timeEnd('cloneNode');
// 方法3:innerHTML(最慢,但写起来爽)
console.time('innerHTML');
const f3 = document.createDocumentFragment();
let html = '';
for (let i = 0; i < count; i++) {
html += '<div>test</div>';
}
const temp = document.createElement('div');
temp.innerHTML = html;
while (temp.firstChild) {
f3.appendChild(temp.firstChild);
}
console.timeEnd('innerHTML');
// 通常结果:cloneNode > createElement >> innerHTML
}
别以为这技术过时了,2026年照样能打的优化手段
有人说:“现在都2026年了,还用DocumentFragment?直接上虚拟DOM啊!” 兄弟,虚拟DOM是框架层面的事,原生DOM操作该优化还得优化。而且有些场景框架帮不了你,比如:
// 场景1:富文本编辑器的光标定位
function insertNodesAtCursor(nodes) {
const selection = window.getSelection();
const range = selection.getRangeAt(0);
// 必须原生操作,框架插不上手
const fragment = document.createDocumentFragment();
nodes.forEach(node => fragment.appendChild(node));
range.deleteContents();
range.insertNode(fragment);
// 重新定位光标
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
}
// 场景2:Web Component内部优化
class MyList extends HTMLElement {
connectedCallback() {
const data = JSON.parse(this.getAttribute('data') || '[]');
const shadow = this.attachShadow({mode: 'open'});
const fragment = document.createDocumentFragment();
const ul = document.createElement('ul');
data.forEach(item => {
const li = document.createElement('li');
li.innerHTML = `
<span class="title">${item.title}</span>
<span class="desc">${item.desc}</span>
`;
ul.appendChild(li);
});
fragment.appendChild(ul);
// 一次性添加样式+DOM
const style = document.createElement('style');
style.textContent = `
ul { list-style: none; padding: 0; }
li { padding: 10px; border-bottom: 1px solid #eee; }
.title { font-weight: bold; color: #333; }
.desc { color: #666; font-size: 14px; }
`;
fragment.appendChild(style);
shadow.appendChild(fragment); // 只触发一次渲染
}
}
// 场景3:Canvas/WebGL叠加层
function createOverlayElements(count) {
const container = document.getElementById('canvas-overlay');
const fragment = document.createDocumentFragment();
for (let i = 0; i < count; i++) {
const marker = document.createElement('div');
marker.className = 'map-marker';
marker.style.cssText = `
position: absolute;
left: ${Math.random() * 100}%;
top: ${Math.random() * 100}%;
transform: translate(-50%, -50%);
`;
marker.dataset.id = i;
fragment.appendChild(marker);
}
container.appendChild(fragment);
}
Vue keep-alive 缓存组件的正确打开方式
列表跳详情页再回来,数据没了是不是很崩溃
做电商或者后台管理系统的兄弟肯定遇到过这种场景:用户在一个很长的商品列表页,滚到第50条,点进去看详情,看完返回,卧槽,列表回到顶部了,之前筛选的条件也全没了,用户当场崩溃,产品经理提着刀来找你。
传统的解决方案是啥?用Vuex或者Pinia把列表状态存起来,返回时再恢复。但这玩意儿写起来麻烦,还要处理各种边界情况。其实Vue早就给你准备好了解决方案——keep-alive。
keep-alive 就是给组件上个存档点,状态给你保住
keep-alive是Vue内置的一个抽象组件,它能把包裹的动态组件缓存起来,而不是销毁重建。简单说就是给组件拍了个快照,下次再显示时直接恢复,而不是重新创建。
<!-- 基础用法 -->
<template>
<div>
<button @click="currentTab = 'List'">列表</button>
<button @click="currentTab = 'Detail'">详情</button>
<!-- 包裹动态组件 -->
<keep-alive>
<component :is="currentTab"></component>
</keep-alive>
</div>
</template>
<script>
import List from './List.vue'
import Detail from './Detail.vue'
export default {
components: { List, Detail },
data() {
return {
currentTab: 'List'
}
}
}
</script>
在这个例子中,切换到Detail再切回List,List组件不会被销毁,它的所有状态(滚动位置、筛选条件、表单数据)都还在。
include 和 exclude 咋用,别整个项目全缓存那要炸
如果你无脑给所有组件加keep-alive,内存会爆炸。特别是那种大数据量的表格组件,缓存十个八个内存就扛不住了。所以得用include和exclude精确控制。
<template>
<div>
<!-- 只缓存List组件,Detail不缓存 -->
<keep-alive include="List">
<component :is="currentTab"></component>
</keep-alive>
<!-- 或者缓存多个,用逗号分隔 -->
<keep-alive include="List,UserProfile,Settings">
<router-view></router-view>
</keep-alive>
<!-- 用数组更灵活 -->
<keep-alive :include="cachedViews">
<router-view></router-view>
</keep-alive>
<!-- 排除特定组件 -->
<keep-alive exclude="RealTimeChart,HeavyMap">
<component :is="dynamicComp"></component>
</keep-alive>
</div>
</template>
<script>
export default {
data() {
return {
currentTab: 'List',
// 动态控制缓存列表
cachedViews: ['List', 'UserProfile']
}
},
methods: {
// 根据用户权限动态调整缓存
updateCache() {
if (this.isAdmin) {
this.cachedViews.push('AdminPanel');
} else {
this.cachedViews = this.cachedViews.filter(v => v !== 'AdminPanel');
}
}
}
}
</script>
配合路由元信息使用(最佳实践):
// router/index.js
const routes = [
{
path: '/list',
component: () => import('@/views/List.vue'),
meta: {
keepAlive: true, // 需要缓存
title: '商品列表'
}
},
{
path: '/detail/:id',
component: () => import('@/views/Detail.vue'),
meta: {
keepAlive: false, // 不需要缓存
title: '商品详情'
}
},
{
path: '/realtime',
component: () => import('@/views/Realtime.vue'),
meta: {
keepAlive: false, // 实时数据不需要缓存
title: '实时监控'
}
}
]
// App.vue
<template>
<keep-alive :include="cachedViews">
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</template>
<script>
export default {
computed: {
cachedViews() {
// 从路由配置中提取需要缓存的组件名
return this.$router.options.routes
.filter(route => route.meta?.keepAlive)
.map(route => route.component?.name)
.filter(Boolean);
}
}
}
</script>
配合 activated 和 deactivated 生命周期玩出花来
被keep-alive缓存的组件,会多出两个生命周期钩子:activated(激活时)和deactivated(停用时)。这俩钩子特别有用,比如:
<script>
export default {
name: 'ProductList',
data() {
return {
list: [],
scrollTop: 0,
isLoading: false
}
},
// 第一次创建时调用
created() {
console.log('created - 组件创建');
this.fetchData();
},
// 每次激活时调用(包括第一次)
activated() {
console.log('activated - 组件激活');
// 恢复滚动位置
this.$refs.scrollContainer.scrollTop = this.scrollTop;
// 检查数据是否需要刷新(比如离开超过5分钟)
if (Date.now() - this.lastLeaveTime > 5 * 60 * 1000) {
this.refreshData();
}
// 开启定时刷新
this.startAutoRefresh();
// 上报埋点
this.trackEvent('page_view', { page: 'product_list' });
},
// 每次停用时调用
deactivated() {
console.log('deactivated - 组件停用');
// 保存滚动位置
this.scrollTop = this.$refs.scrollContainer.scrollTop;
this.lastLeaveTime = Date.now();
// 清理定时器,防止内存泄漏
this.stopAutoRefresh();
// 关闭WebSocket连接
this.disconnectWebSocket();
// 暂停视频播放
if (this.$refs.videoPlayer) {
this.$refs.videoPlayer.pause();
}
},
// 组件销毁时调用(keep-alive组件很少走到这里)
beforeDestroy() {
console.log('beforeDestroy - 组件销毁');
this.stopAutoRefresh();
},
methods: {
fetchData() {
// 初始加载数据
this.isLoading = true;
api.getProductList().then(res => {
this.list = res.data;
this.isLoading = false;
});
},
refreshData() {
// 局部刷新,不重置整个列表
api.getProductList({ refresh: true }).then(res => {
// 智能合并数据,保留用户已选中的项
this.mergeList(res.data);
});
},
startAutoRefresh() {
// 每30秒刷新一次数据
this.refreshTimer = setInterval(() => {
this.refreshData();
}, 30000);
},
stopAutoRefresh() {
if (this.refreshTimer) {
clearInterval(this.refreshTimer);
this.refreshTimer = null;
}
},
disconnectWebSocket() {
if (this.ws) {
this.ws.close();
this.ws = null;
}
}
}
}
</script>
动态组件加 keep-alive 的组合拳怎么打
实际项目中,经常遇到这种需求:Tab切换,每个Tab里都有复杂的表格或图表,要求切换时不重新加载。这时候就得动态组件+keep-alive组合拳。
<template>
<div class="dashboard">
<div class="tabs">
<button
v-for="tab in tabs"
:key="tab.name"
:class="{ active: currentTab === tab.name }"
@click="currentTab = tab.name"
>
{{ tab.label }}
</button>
</div>
<!-- 高级用法:max限制缓存数量,LRU淘汰 -->
<keep-alive
:include="cachedComponents"
:max="5" <!-- 最多缓存5个,超出时销毁最久未使用的 -->
>
<component
:is="currentTabComponent"
:key="currentTab"
v-bind="currentTabProps"
></component>
</keep-alive>
</div>
</template>
<script>
import SalesChart from './components/SalesChart.vue'
import UserTable from './components/UserTable.vue'
import OrderList from './components/OrderList.vue'
import RealtimeMap from './components/RealtimeMap.vue'
export default {
components: { SalesChart, UserTable, OrderList, RealtimeMap },
data() {
return {
currentTab: 'SalesChart',
tabs: [
{ name: 'SalesChart', label: '销售趋势', cache: true },
{ name: 'UserTable', label: '用户列表', cache: true },
{ name: 'OrderList', label: '订单管理', cache: true },
{ name: 'RealtimeMap', label: '实时地图', cache: false } // 实时数据不缓存
],
// 每个Tab的专属配置
tabConfigs: {
SalesChart: { dateRange: '7d', type: 'line' },
UserTable: { pageSize: 50, filters: {} },
OrderList: { status: 'all', sortBy: 'date' }
}
}
},
computed: {
currentTabComponent() {
return this.currentTab
},
currentTabProps() {
return this.tabConfigs[this.currentTab] || {}
},
// 动态计算需要缓存的组件
cachedComponents() {
return this.tabs
.filter(tab => tab.cache)
.map(tab => tab.name)
}
},
watch: {
currentTab(newVal, oldVal) {
// 记录切换行为,用于分析用户习惯
this.$analytics.track('tab_switch', {
from: oldVal,
to: newVal,
timestamp: Date.now()
});
}
}
}
</script>
这俩技术放一起能擦出啥火花
大批量渲染列表时用 fragment 减少回流
想象一个场景:后台管理系统,用户点击"加载更多",一次塞进来500条数据。如果用普通写法,浏览器直接卡死。这时候fragment+分页加载组合拳:
<template>
<div class="infinite-list" ref="listContainer" @scroll="handleScroll">
<div v-for="item in displayList" :key="item.id" class="item">
<img :src="item.avatar" loading="lazy">
<div class="content">
<h3>{{ item.title }}</h3>
<p>{{ item.desc }}</p>
</div>
</div>
<div v-if="loading" class="loading">加载中...</div>
<div v-if="noMore" class="no-more">没有更多了</div>
</div>
</template>
<script>
export default {
data() {
return {
allData: [], // 所有数据
displayCount: 50, // 当前显示数量
loading: false,
noMore: false,
batchSize: 50 // 每批加载数量
}
},
computed: {
displayList() {
return this.allData.slice(0, this.displayCount);
}
},
methods: {
// 初始加载
async loadInitialData() {
const res = await fetch('/api/large-list');
this.allData = res.data; // 假设有2000条
// 先渲染前50条,让用户立刻看到内容
this.displayCount = this.batchSize;
// 剩余数据用requestIdleCallback延迟渲染,不阻塞主线程
if ('requestIdleCallback' in window) {
requestIdleCallback(this.renderRemainingData, { timeout: 2000 });
} else {
setTimeout(this.renderRemainingData, 0);
}
},
// 空闲时渲染剩余数据(分批插入,避免单次回流过大)
renderRemainingData(deadline) {
const total = this.allData.length;
const container = this.$refs.listContainer;
// 使用fragment批量插入
const batchInsert = (count) => {
const fragment = document.createDocumentFragment();
const start = this.displayCount;
const end = Math.min(start + count, total);
>欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
>推荐:[DTcode7](https://blog.csdn.net/black_cat7?spm=1010.2135.3001.5343)的博客首页。
一个做过前端开发的产品经理,经历过睿智产品的折磨导致脱发之后,励志要翻身农奴把歌唱,<font color='red'>一边打入敌人内部一边持续提升自己</font>,为我们广大开发同胞谋福祉,<font color='red'>坚决抵制睿智产品折磨我们码农兄弟!</font>
***
<table><tr><th><b><font color='red'>专栏系列(点击解锁)</font></b><th><b><font color='red'>学习路线(点击解锁)</font></b><th><b><font color='red'>知识定位</font></b><tr><td><a href=https://blog.csdn.net/black_cat7/category_11327978.html target=_blank rel='noopener noreferrer '>《微信小程序相关博客》</a>
<td>持续更新中~<td align=left>结合微信官方原生框架、uniapp等小程序框架,记录请求、封装、tabbar、UI组件的学习记录和使用技巧等<tr><td><a href=https://blog.csdn.net/black_cat7/category_12588918.html target=_blank rel='noopener noreferrer '>《AIGC相关博客》</a>
<td>持续更新中~<td align=left>AIGC、AI生产力工具的介绍,例如stable diffusion这种的AI绘画工具安装、使用、技巧等总结<tr><td rowspan=4><a href=https://blog.csdn.net/black_cat7/category_12663459.html target=_blank rel='noopener noreferrer '>《HTML网站开发相关》</a>
<td><a href=https://blog.csdn.net/black_cat7/category_12674047.html target=_blank rel='noopener noreferrer '>《前端基础入门三大核心之html相关博客》</a><td align=left>前端基础入门三大核心之html板块的内容,<mark>入坑前端或者辅助学习的必看知识</mark><tr>
<td><a href=https://blog.csdn.net/black_cat7/category_11352548.html target=_blank rel='noopener noreferrer '>《前端基础入门三大核心之JS相关博客》</a><td align=left>前端JS是JavaScript语言在网页开发中的应用,负责实现交互效果和动态内容。它与HTML和CSS并称前端三剑客,共同构建用户界面。<br>通过操作DOM元素、响应事件、发起网络请求等,JS使页面能够响应用户行为,实现数据动态展示和页面流畅跳转,是现代Web开发的核心
<tr><td><a href=https://blog.csdn.net/black_cat7/category_12674041.html target=_blank rel='noopener noreferrer '>《前端基础入门三大核心之CSS相关博客》</a><td align=left>介绍前端开发中遇到的CSS疑问和各种奇妙的CSS语法,同时收集精美的CSS效果代码,用来丰富你的web网页<tr>
<td><a href=https://blog.csdn.net/black_cat7/category_12674050.html target=_blank rel='noopener noreferrer '>《canvas绘图相关博客》</a><td align=left>Canvas是HTML5中用于绘制图形的元素,通过JavaScript及其提供的绘图API,开发者可以在网页上绘制出各种复杂的图形、动画和图像效果。Canvas提供了高度的灵活性和控制力,使得前端绘图技术更加丰富和多样化
<tr><td><a href=https://blog.csdn.net/black_cat7/category_11343005.html target=_blank rel='noopener noreferrer'>《Vue实战相关博客》</a><td>持续更新中~<td align=left>详细总结了常用UI库elementUI的使用技巧以及Vue的学习之旅<tr>
<td><a href=https://blog.csdn.net/black_cat7/category_12674092.html target=_blank rel='noopener noreferrer '>《python相关博客》</a><td>持续更新中~<td align=left>Python,简洁易学的编程语言,强大到足以应对各种应用场景,是编程新手的理想选择,也是专业人士的得力工具
<tr><td><a href=https://blog.csdn.net/black_cat7/category_11463502.html target=_blank rel='noopener noreferrer '>《sql数据库相关博客》</a><td>持续更新中~<td align=left>SQL数据库:高效管理数据的利器,学会SQL,轻松驾驭结构化数据,解锁数据分析与挖掘的无限可能
<tr><td><a href=https://blog.csdn.net/black_cat7/category_12671914.html target=_blank rel='noopener noreferrer '>《算法系列相关博客》</a><td>持续更新中~<td align=left>算法与数据结构学习总结,通过JS来编写处理复杂有趣的算法问题,提升你的技术思维
<tr><td rowspan=3><a href=https://blog.csdn.net/black_cat7/category_12463107.html target=_blank rel='noopener noreferrer '>《IT信息技术相关博客》</a><td>持续更新中~<td align=left>作为信息化人员所需要掌握的底层技术,涉及软件开发、网络建设、系统维护等领域的知识
<tr><td><a href=https://blog.csdn.net/black_cat7/category_12652565.html target=_blank rel='noopener noreferrer '>《信息化人员基础技能知识相关博客》</a><td align=left>无论你是开发、产品、实施、经理,只要是从事信息化相关行业的人员,都应该掌握这些信息化的基础知识,可以不精通但是一定要了解,避免日常工作中贻笑大方
<tr><td><a href=https://blog.csdn.net/black_cat7/category_12674074.html target=_blank rel='noopener noreferrer '>《信息化技能面试宝典相关博客》</a><td align=left>涉及信息化相关工作基础知识和面试技巧,提升自我能力与面试通过率,扩展知识面
<tr><td><a href=https://blog.csdn.net/black_cat7/category_11376833.html target=_blank rel='noopener noreferrer'>《前端开发习惯与小技巧相关博客》</a><td>持续更新中~<td align=left>罗列常用的开发工具使用技巧,如 Vscode快捷键操作、Git、CMD、游览器控制台等<tr><td><a href=https://blog.csdn.net/black_cat7/category_12271105.html target=_blank rel='noopener noreferrer '>《photoshop相关博客》</a>
<td>持续更新中~<td align=left>基础的PS学习记录,含括PPI与DPI、物理像素dp、逻辑像素dip、矢量图和位图以及帧动画等的学习总结<tr><td><a href=https://blog.csdn.net/black_cat7/category_11412314.html target=_blank rel='noopener noreferrer '>日常开发&办公&生产【实用工具】分享相关博客》</a><td>持续更新中~<td align=left>分享介绍各种开发中、工作中、个人生产以及学习上的工具,丰富阅历,给大家提供处理事情的更多角度,学习了解更多的便利工具,如Fiddler抓包、办公快捷键、虚拟机VMware等工具</table> <br>
> 吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤 <br>
非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!

更多推荐


所有评论(0)