鸿蒙应用开发中的函数基础与实践指南

发布时间:2026/11/7 16:53:39

鸿蒙应用开发中的函数基础与实践指南 1. 鸿蒙应用开发中的函数基础函数作为编程语言中最基础也最重要的概念之一在鸿蒙应用开发中扮演着核心角色。对于零基础开发者来说理解函数的本质和正确使用方法是迈向高效开发的第一步。1.1 为什么需要函数在鸿蒙应用开发中函数主要解决三个核心问题代码复用避免重复编写相同逻辑的代码块逻辑封装将特定功能封装成独立单元降低复杂度模块化开发便于团队协作和代码维护以鸿蒙的页面跳转功能为例如果不使用函数每次跳转都需要重复编写相同的Intent设置代码而通过函数封装后只需调用navigateTo(targetPage)即可实现跳转逻辑。1.2 鸿蒙中的函数特点鸿蒙基于TypeScript/JavaScript和Java两种开发范式函数特性也有所不同ArkTS(基于TS)中的函数特点支持箭头函数和传统函数定义强类型支持需显式声明参数和返回值类型支持async/await异步处理支持函数作为一等公民可赋值给变量Java开发范式中的函数特点严格的面向对象方法定义必须属于某个类支持重载(overload)但不支持默认参数访问控制更严格(public/private等)2. 函数的定义方式2.1 ArkTS中的函数定义基础函数定义function greet(name: string): string { return Hello, ${name}!; }箭头函数const greet (name: string): string Hello, ${name}!;异步函数async function fetchData(url: string): PromiseData { const response await http.get(url); return response.data; }2.2 Java开发范式中的函数定义基本方法定义public class Utils { public static String greet(String name) { return Hello, name !; } }带参数校验的方法public void showToast(Context context, String message) { if (context null || TextUtils.isEmpty(message)) { throw new IllegalArgumentException(参数不能为空); } // 显示Toast的实现 }2.3 特殊函数类型回调函数事件处理Button({ type: ButtonType.Capsule }) { Text(点击) }.onClick(() { // 点击回调函数 console.log(按钮被点击); })生命周期函数struct MyComponent { aboutToAppear() { // 组件即将显示时执行 } aboutToDisappear() { // 组件即将消失时执行 } }3. 函数的实际应用3.1 UI交互中的函数应用表单验证函数function validateForm(formData: FormData): boolean { if (!formData.username || formData.username.length 4) { showToast(用户名至少4个字符); return false; } if (!formData.password || formData.password.length 6) { showToast(密码至少6个字符); return false; } return true; }页面跳转封装function navigateTo(page: string, params?: object) { router.push({ url: pages/${page}, params: params || {} }); }3.2 数据处理函数API请求封装async function requestT(config: { url: string, method?: GET | POST, data?: object }): PromiseT { try { const response await http.request({ url: config.url, method: config.method || GET, data: config.data }); return response.data; } catch (error) { console.error(请求失败:, error); throw error; } }本地数据缓存const storageKey app_data; function saveData(data: object): void { try { const jsonStr JSON.stringify(data); localStorage.setStorageSync(storageKey, jsonStr); } catch (error) { console.error(保存数据失败:, error); } } function loadData(): object | null { try { const jsonStr localStorage.getStorageSync(storageKey); return jsonStr ? JSON.parse(jsonStr) : null; } catch (error) { console.error(读取数据失败:, error); return null; } }4. 函数高级技巧与最佳实践4.1 函数参数处理默认参数function createUser( name: string, age: number 18, isAdmin: boolean false ) { // ... }剩余参数function sum(...numbers: number[]): number { return numbers.reduce((total, num) total num, 0); }对象参数解构function createRectangle({ width 100, height width, color black }: RectangleOptions) { // ... }4.2 函数组合与柯里化函数组合function composeT(...fns: Function[]): (x: T) T { return (x: T) fns.reduceRight((v, f) f(v), x); } const processText compose( (s: string) s.toUpperCase(), (s: string) s.trim(), (s: string) s.replace(/\s/g, ) ); const result processText( hello world ); // HELLO WORLD简单柯里化function multiply(a: number): (b: number) number { return (b: number) a * b; } const double multiply(2); console.log(double(5)); // 104.3 性能优化技巧防抖函数function debounceT extends Function(fn: T, delay: number): T { let timer: number | null null; return function(this: any, ...args: any[]) { if (timer) { clearTimeout(timer); } timer setTimeout(() { fn.apply(this, args); timer null; }, delay); } as unknown as T; } // 使用示例 const search debounce((query: string) { console.log(搜索:, query); }, 300);节流函数function throttleT extends Function(fn: T, interval: number): T { let lastTime 0; return function(this: any, ...args: any[]) { const now Date.now(); if (now - lastTime interval) { fn.apply(this, args); lastTime now; } } as unknown as T; } // 使用示例 const handleScroll throttle(() { console.log(处理滚动事件); }, 200);5. 常见问题与调试技巧5.1 函数作用域问题典型问题for (var i 0; i 5; i) { setTimeout(function() { console.log(i); // 总是输出5 }, 100); }解决方案// 使用let替代var for (let i 0; i 5; i) { setTimeout(function() { console.log(i); // 0,1,2,3,4 }, 100); } // 或使用闭包 for (var i 0; i 5; i) { (function(j) { setTimeout(function() { console.log(j); // 0,1,2,3,4 }, 100); })(i); }5.2 this指向问题典型问题class Button { text: string 点击; handleClick() { console.log(this.text); // 运行时this可能不是Button实例 } } const button new Button(); const clickHandler button.handleClick; clickHandler(); // 错误this为undefined解决方案// 使用箭头函数 class Button { text: string 点击; handleClick () { console.log(this.text); // 正确指向Button实例 } } // 或使用bind class Button { text: string 点击; constructor() { this.handleClick this.handleClick.bind(this); } handleClick() { console.log(this.text); } }5.3 异步处理陷阱典型问题async function fetchData() { const data1 await getData1(); const data2 await getData2(); // 不必要的顺序等待 return { data1, data2 }; }优化方案async function fetchData() { // 并行请求 const [data1, data2] await Promise.all([ getData1(), getData2() ]); return { data1, data2 }; }5.4 调试技巧使用console的进阶技巧function complexCalculation(input: number) { console.time(calculation); console.group(计算过程); // 中间步骤 console.log(输入值:, input); const step1 input * 2; console.log(第一步结果:, step1); const step2 Math.sqrt(step1); console.log(第二步结果:, step2); console.groupEnd(); console.timeEnd(calculation); return step2; }使用debugger语句function troubleshoot(data: any) { // 设置断点 debugger; // 复杂处理逻辑 const result processData(data); return result; }6. 鸿蒙特有函数实践6.1 自定义组件方法定义组件方法Component struct MyComponent { private count: number 0; // 公共方法 public increment(): void { this.count; } // 私有方法 private reset(): void { this.count 0; } build() { Column() { Text(计数: ${this.count}) Button(增加).onClick(() this.increment()) Button(重置).onClick(() this.reset()) } } }6.2 使用系统能力函数调用设备能力async function takePicture(): Promiseimage.PixelMap { try { const camera await camera.getCamera(getContext(this)); const photo await camera.takePhoto(); return photo.pixelMap; } catch (error) { console.error(拍照失败:, error); throw error; } }使用系统服务function vibrate(duration: number 200): void { try { vibrator.startVibration({ type: time, duration: duration }); } catch (error) { console.error(振动失败:, error); } }6.3 鸿蒙API封装实践统一API错误处理async function safeCallT(fn: () PromiseT): Promise[T | null, Error | null] { try { const result await fn(); return [result, null]; } catch (error) { console.error(调用失败:, error); return [null, error]; } } // 使用示例 const [data, error] await safeCall(() someApiCall()); if (error) { // 处理错误 }带重试机制的请求async function withRetryT( fn: () PromiseT, maxRetries: number 3, delay: number 1000 ): PromiseT { let lastError: Error | null null; for (let i 0; i maxRetries; i) { try { return await fn(); } catch (error) { lastError error; if (i maxRetries - 1) { await new Promise(resolve setTimeout(resolve, delay)); } } } throw lastError || new Error(未知错误); }7. 测试与文档7.1 函数单元测试使用Jest测试普通函数// utils.ts export function add(a: number, b: number): number { return a b; } // utils.test.ts import { add } from ./utils; describe(add function, () { it(should add two numbers correctly, () { expect(add(2, 3)).toBe(5); }); it(should handle negative numbers, () { expect(add(-1, 1)).toBe(0); }); });测试异步函数// api.ts export async function fetchUser(id: string): PromiseUser { const response await http.get(/users/${id}); return response.data; } // api.test.ts import { fetchUser } from ./api; describe(fetchUser, () { it(should fetch user data, async () { const mockUser { id: 1, name: 张三 }; http.get.mockResolvedValue({ data: mockUser }); const user await fetchUser(1); expect(user).toEqual(mockUser); expect(http.get).toHaveBeenCalledWith(/users/1); }); });7.2 函数文档注释ArkTS文档注释/** * 计算两个数的和 * param a 第一个加数 * param b 第二个加数 * returns 两个数的和 * example * const result add(2, 3); // 5 */ function add(a: number, b: number): number { return a b; }Java开发范式文档注释/** * 格式化显示时间 * param timestamp 时间戳毫秒 * param pattern 格式模式默认yyyy-MM-dd HH:mm:ss * return 格式化后的时间字符串 * throws IllegalArgumentException 如果时间戳为负数 */ public static String formatTime(long timestamp, String pattern) { // 实现代码 }7.3 性能测试与分析简单性能测试function testPerformance(fn: Function, iterations: number 1000) { const start performance.now(); for (let i 0; i iterations; i) { fn(); } const end performance.now(); const avgTime (end - start) / iterations; console.log(平均执行时间: ${avgTime.toFixed(4)}ms); console.log(总执行时间: ${(end - start).toFixed(2)}ms); console.log(执行次数: ${iterations}); } // 使用示例 testPerformance(() { // 测试的函数调用 heavyCalculation(); }, 10000);内存使用分析function checkMemoryUsage() { const used process.memoryUsage(); console.log(内存使用情况:); for (let key in used) { console.log(${key}: ${Math.round(used[key] / 1024 / 1024 * 100) / 100} MB); } } // 在函数调用前后检查内存 checkMemoryUsage(); someMemoryIntensiveFunction(); checkMemoryUsage();8. 项目实战构建工具函数库8.1 项目结构设计/src /utils array.ts # 数组相关工具函数 date.ts # 日期处理函数 device.ts # 设备相关功能 dom.ts # UI操作辅助函数 network.ts # 网络请求封装 storage.ts # 数据存储封装 validate.ts # 验证函数 index.ts # 统一导出入口 /types # 类型定义 /tests # 单元测试8.2 核心工具函数实现日期格式化函数// date.ts /** * 日期格式化 * param date Date对象或时间戳 * param format 格式字符串默认yyyy-MM-dd * example * formatDate(new Date(), yyyy年MM月dd日 HH:mm:ss) */ export function formatDate( date: Date | number, format: string yyyy-MM-dd ): string { const d date instanceof Date ? date : new Date(date); const map: Recordstring, string { yyyy: d.getFullYear().toString(), MM: (d.getMonth() 1).toString().padStart(2, 0), dd: d.getDate().toString().padStart(2, 0), HH: d.getHours().toString().padStart(2, 0), mm: d.getMinutes().toString().padStart(2, 0), ss: d.getSeconds().toString().padStart(2, 0) }; return format.replace(/yyyy|MM|dd|HH|mm|ss/g, match map[match]); }深度对象合并// object.ts /** * 深度合并对象 * param target 目标对象 * param sources 源对象多个 * returns 合并后的新对象 */ export function deepMergeT(target: T, ...sources: PartialT[]): T { if (!sources.length) return target; const result: any Array.isArray(target) ? [...target] : { ...target }; for (const source of sources) { if (!source || typeof source ! object) continue; for (const key in source) { if (source.hasOwnProperty(key)) { const targetValue result[key]; const sourceValue (source as any)[key]; if (isObject(targetValue) isObject(sourceValue)) { result[key] deepMerge({ ...targetValue }, sourceValue); } else { result[key] sourceValue ! undefined ? sourceValue : targetValue; } } } } return result; } function isObject(value: any): boolean { return value typeof value object !Array.isArray(value); }8.3 在鸿蒙项目中使用安装依赖npm install myproject/utils在ArkTS中使用import { formatDate, deepMerge } from myproject/utils; // 使用日期格式化 const now new Date(); console.log(formatDate(now, yyyy年MM月dd日)); // 使用对象合并 const defaultConfig { a: 1, b: { c: 2 } }; const userConfig { b: { d: 3 }, e: 4 }; const merged deepMerge(defaultConfig, userConfig);在Java开发范式中使用// 需要将工具库编译为HarmonyOS Library import com.myproject.utils.DateUtils; import com.myproject.utils.ObjectUtils; // 使用日期格式化 String formatted DateUtils.formatDate(new Date(), yyyy-MM-dd); // 使用对象合并 MapString, Object defaultConfig new HashMap(); MapString, Object userConfig new HashMap(); MapString, Object merged ObjectUtils.deepMerge(defaultConfig, userConfig);9. 函数优化与重构9.1 识别需要优化的函数常见优化信号函数体过长超过50行参数过多超过5个嵌套层级过深超过3层职责不单一做多件不相关的事重复代码片段示例需要优化的函数function processUserData(user: User) { // 验证用户数据 if (!user.name || user.name.length 2) { throw new Error(无效的用户名); } if (!user.email || !user.email.includes()) { throw new Error(无效的邮箱); } // 格式化数据 user.name user.name.trim(); user.email user.email.toLowerCase(); // 保存到数据库 const db getDatabase(); const result db.insert(users, user); // 发送欢迎邮件 const mailService getMailService(); mailService.send({ to: user.email, subject: 欢迎加入, body: 尊敬的${user.name}欢迎注册我们的服务 }); return result; }9.2 函数拆分重构优化后的版本// 验证逻辑提取 function validateUser(user: User): void { if (!user.name || user.name.length 2) { throw new Error(无效的用户名); } if (!user.email || !user.email.includes()) { throw new Error(无效的邮箱); } } // 格式化逻辑提取 function formatUser(user: User): User { return { ...user, name: user.name.trim(), email: user.email.toLowerCase() }; } // 数据库操作提取 async function saveUserToDB(user: User): PromiseResult { const db getDatabase(); return db.insert(users, user); } // 邮件发送提取 async function sendWelcomeEmail(user: User): Promisevoid { const mailService getMailService(); await mailService.send({ to: user.email, subject: 欢迎加入, body: 尊敬的${user.name}欢迎注册我们的服务 }); } // 重构后的主函数 async function processUserData(user: User): PromiseResult { validateUser(user); const formattedUser formatUser(user); const result await saveUserToDB(formattedUser); await sendWelcomeEmail(formattedUser); return result; }9.3 性能关键函数优化缓存优化示例function createCachedFunctionT extends Function(fn: T): T { const cache new Mapstring, any(); return function(this: any, ...args: any[]) { const cacheKey JSON.stringify(args); if (cache.has(cacheKey)) { console.log(从缓存中获取结果); return cache.get(cacheKey); } const result fn.apply(this, args); cache.set(cacheKey, result); return result; } as unknown as T; } // 使用示例 function expensiveCalculation(x: number, y: number): number { console.log(执行复杂计算...); return x * y Math.sqrt(x) Math.pow(y, 2); } const cachedCalculation createCachedFunction(expensiveCalculation); console.log(cachedCalculation(2, 3)); // 执行计算 console.log(cachedCalculation(2, 3)); // 从缓存获取算法优化示例// 优化前O(n^2)复杂度 function findDuplicates(arr: number[]): number[] { const duplicates: number[] []; for (let i 0; i arr.length; i) { for (let j i 1; j arr.length; j) { if (arr[i] arr[j] !duplicates.includes(arr[i])) { duplicates.push(arr[i]); } } } return duplicates; } // 优化后O(n)复杂度 function findDuplicatesOptimized(arr: number[]): number[] { const seen new Setnumber(); const duplicates new Setnumber(); for (const num of arr) { if (seen.has(num)) { duplicates.add(num); } else { seen.add(num); } } return Array.from(duplicates); }10. 鸿蒙函数开发进阶10.1 使用装饰器增强函数日志装饰器function log(target: any, key: string, descriptor: PropertyDescriptor) { const originalMethod descriptor.value; descriptor.value function(...args: any[]) { console.log(调用方法: ${key}, 参数:, args); const result originalMethod.apply(this, args); console.log(方法 ${key} 返回结果:, result); return result; }; return descriptor; } class Calculator { log add(a: number, b: number): number { return a b; } } const calc new Calculator(); calc.add(2, 3); // 会自动打印调用日志性能监控装饰器function measurePerformance( target: any, key: string, descriptor: PropertyDescriptor ) { const originalMethod descriptor.value; descriptor.value async function(...args: any[]) { const start performance.now(); const result await originalMethod.apply(this, args); const end performance.now(); console.log(方法 ${key} 执行耗时: ${(end - start).toFixed(2)}ms); return result; }; return descriptor; } class DataService { measurePerformance async fetchData() { // 模拟网络请求 await new Promise(resolve setTimeout(resolve, 500)); return { data: 示例数据 }; } }10.2 函数式编程实践使用高阶函数处理列表// 定义类型 type PredicateT (item: T) boolean; type MapperT, U (item: T) U; type ReducerT, U (accum: U, item: T) U; // 列表操作函数 function filterT(array: T[], predicate: PredicateT): T[] { return array.filter(predicate); } function mapT, U(array: T[], mapper: MapperT, U): U[] { return array.map(mapper); } function reduceT, U(array: T[], reducer: ReducerT, U, initial: U): U { return array.reduce(reducer, initial); } // 使用示例 const numbers [1, 2, 3, 4, 5]; const evenNumbers filter(numbers, n n % 2 0); const squaredNumbers map(numbers, n n * n); const sum reduce(numbers, (total, n) total n, 0);函数管道function pipeT(...fns: Function[]): (value: T) any { return (value: T) fns.reduce((v, fn) fn(v), value); } // 使用示例 const processText pipe( (s: string) s.trim(), (s: string) s.toUpperCase(), (s: string) s.replace(/[^A-Z]/g, ) ); const result processText( Hello, 123 World! ); // HELLOWORLD10.3 鸿蒙API的响应式封装将回调API转为Promisefunction promisifyT(fn: (callback: (error: any, result?: T) void) void): PromiseT { return new Promise((resolve, reject) { fn((error, result) { if (error) { reject(error); } else { resolve(result!); } }); }); } // 使用示例 async function getLocation(): PromiseLocation { return promisify(geolocation.getCurrentLocation); } // 在组件中使用 async function updateLocation() { try { const location await getLocation(); console.log(当前位置:, location); } catch (error) { console.error(获取位置失败:, error); } }创建响应式数据流class ObservableT { private subscribers: ((value: T) void)[] []; constructor(private value: T) {} get(): T { return this.value; } set(newValue: T): void { this.value newValue; this.notify(); } subscribe(callback: (value: T) void): () void { this.subscribers.push(callback); callback(this.value); return () { this.subscribers this.subscribers.filter(sub sub ! callback); }; } private notify(): void { for (const sub of this.subscribers) { sub(this.value); } } } // 在鸿蒙组件中使用 Component struct CounterComponent { private count new Observable(0); build() { Column() { Text(计数: ${this.count.get()}) .onAppear(() { this.count.subscribe(value { // 当count变化时自动更新文本 this.$update(); }); }) Button(增加) .onClick(() this.count.set(this.count.get() 1)) } } }
延伸阅读

更多相关文章

2026/11/7 16:53:39

Docker多阶段构建实战:镜像从GB级瘦身到百MB级

我接手过不少团队的项目,见过最夸张的一个 Java 服务,打完镜像居然有 3 个多 G。当时第一反应是这哥们儿是不是把源码、编译中间产物、甚至本地的.git目录全塞进去了。后来一查,Dockerfile 写得很规矩,就是FROM maven一顿mvn pack…

2026/11/7 16:53:39

Hugo+GitHub Pages+Obsidian技术博客搭建全攻略

1. 为什么选择这套技术栈搭建开发博客?在技术写作领域,持续输出高质量内容的关键在于最小化写作之外的摩擦成本。经过多年实践,我发现这套组合能完美平衡以下几个核心需求:内容与工具分离:Hugo作为静态网站生成器&…

2026/11/7 16:49:55

江苏省代办电工证在哪考?外省证怎么转过来?

江苏省代办电工证在哪考?外省证怎么转过来? 之前考的证在外省能不能转过来?这几乎是每个想考江苏省电工证或者已经在外地拿到证但想在江苏执业的朋友最关心的问题。特别是像驻马店的应届工程类毕业生,毕业后想在江苏发展,却发现之前在外省考的证可能不被…

2026/11/7 16:49:55

松山湖低压电工证培训真实经历:学历不高也能考,这几点必须知道

松山湖低压电工证培训真实经历:学历不高也能考,这几点必须知道 你是不是也担心学历不高,连低压电工证都报不上名?其实不然,我之前在商丘做建筑工,学历只有初中,也能顺利考下低压电工证,今天就用我的 真实经历 告诉你, 松山湖低压电工证培训…

2026/11/7 16:49:55

电工证怎么考?学电工证有什么条件?上岗必备的干货全在这里

电工证怎么考?学电工证有什么条件?上岗必备的干货全在这里 你是不是正被“急需特种作业证才能上岗找工作”这句话压得喘不过气?别急,今天这波干货,专为像你这样的济源房建工程从业者量身定制。学电工证有什么条件?怎么考?怎么查?怎么避免掉坑?看完这…

2026/11/7 16:49:55

香港电工证去哪考怎么考 香港电工证电子证书怎么拿

香港电工证去哪考怎么考 香港电工证电子证书怎么拿 实操考试心里没底怕挂科?很多人在报考特种作业操作证时都有这种想法,尤其是像【香港电工证去哪考】这样听起来有点复杂的证书。如果你是河南中小施工企业的负责人,或者是想进入建筑、电力行业打工的人,…

2026/11/7 16:49:55

本溪满族电工证怎么考 含金量到底高不高?过来人告诉你答案

本溪满族电工证怎么考 含金量到底高不高?过来人告诉你答案 你是不是也在为“本溪满族电工证怎么考”发愁?听说报名渠道五花八门,怕被中介坑钱、怕花冤枉钱还考不上?别急,今天咱就来聊聊这个事,带你一步步搞懂电工证到底怎么考,含金量是不是真那么高,…

2026/11/7 14:54:47

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/7 14:50:12

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/7 0:03:08

郑州报考避坑指南:电工证和钳工证怎么选?实操考试心里没底怕挂科

郑州报考避坑指南:电工证和钳工证怎么选?实操考试心里没底怕挂科 实操考试心里没底怕挂科?你不是一个人。特别是在郑州这样的城市,很多施工企业负责人和一线作业人员在报考电工证和钳工证时都面临着同样的困惑。到底该考哪个证?哪个更适合自己?哪个证书…

2026/11/7 0:03:08

广西电工证办理流程全解析:外省证怎么转?在哪考最靠谱?

广西电工证办理流程全解析:外省证怎么转?在哪考最靠谱? 之前考的证在外省能不能转过来?这个问题我遇到太多了,今天就拿广西电工证办理流程来具体说说,别再走弯路了。广西电工证的报考、复审、继续教育这些环节,如果你不了解,真的会吃大亏,尤其对于从…

2026/11/7 0:03:08

电气电工证都有哪些保姆级教程

电气电工证都有哪些保姆级教程 你是不是正愁不知道去哪报名,怕被中介坑?别急,这篇保姆级教程专为河南初次报考人员准备,帮你搞清楚 电气电工证都有哪些 ,以及如何避开那些坑。 电气电工证都有哪些?别被五花八门的证书搞懵了…

2026/11/7 14:54:47

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/7 14:50:12

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑