Flutter学习LogUtil封装与实现实例详解

目录
  • 一. 为什么要封装打印类
  • 二. 需要哪些类
  • 三. 打印输出的抽象类
  • 四. 格式化日志内容
    • 格式化堆栈
    • 堆栈裁切工具类
    • 格式化堆栈信息
    • 格式化JSON
  • 五. 需要用到的常量
  • 六. 为了控制多个打印器的设置做了一个配置类
  • 七. Log的管理类
  • 九. 调用LogUtil
  • 十. 定义一个Flutter 控制台打印输出的方法
  • 十一. 现在使用前初始化log打印器一次
    • 使用

一. 为什么要封装打印类

虽然 flutter/原生给我们提供了日志打印的功能,但是超出一定长度以后会被截断

Json打印挤在一起看不清楚

堆栈打印深度过深多打印一些不需要的东西

实现 log 的多种展示方式

二. 需要哪些类

为了可以实现对日志的多种内容格式化和各种显示输出所以抽出来以下几个类

  • 一些常量的字符串表示
  • 对日志内容的打印输出抽象类
  • 对日志内容格式化的抽象类
  • 日志工具的config类
  • 日志工具的管理类
  • 日志工具的Util类

三. 打印输出的抽象类

打印类核心的功能就是打印日志 所以它有一个方法就是打印的方法
而我们要打印输出的内容有 当前 log等级 log的tag 需要打印的数据 当前堆栈信息 亦或是获取的Json数据

/// 日志打印输出的接口类
abstract class IHCLogPrint {
  void logPrint({
    required LogType type,
    required String tag,
    required String message,
    StackTrace? stackTrace,
    Map<String, dynamic>? json,
  });
}

四. 格式化日志内容

这里定义一个IHCLogFormatter抽象类

///格式化的接口类
abstract class IHCLogFormatter<T> {
  String format(T data);
}

格式化堆栈

堆栈的格式例如这样

#0 LogUtil._logPrint (package:com.halfcity.full_flutter_app/utils/log/log_util.dart:104:42)
#1 LogUtil._logPrint (package:com.halfcity.full_flutter_app/utils/log/log_util.dart:104:42)
#2 LogUtil._logPrint (package:com.halfcity.full_flutter_app/utils/log/log_util.dart:104:42)
....

会返回来很多无用的数据 而我们实际用到的也不过前五层就可以了
所以需要一个工具来剔除无用的数据和当前自己的包名

堆栈裁切工具类

class StackTraceUtil {
  ///正则表达式 表示#+数字+空格的格式
  static final RegExp _startStr = RegExp(r'#\d+[\s]+');
  ///正则表达式表示 多个非换行符+ (非空) 正则表达式中()代表子项 如果需要正则()需要转义\( \)
  ///了解更多 https://www.runoob.com/regexp/regexp-syntax.html
  static final RegExp _stackReg = RegExp(r'.+ \(([^\s]+)\)');
  /// 把StackTrace 转成list 并去除无用信息
  /// [stackTrace] 堆栈信息
  ///#0      LogUtil._logPrint (package:com.halfcity.full_flutter_app/utils/log/log_util.dart:104:42)
  static List<String> _fixStack(StackTrace stackTrace) {
    List tempList = stackTrace.toString().split("\n");
    List<String> stackList = [];
    for (String str in tempList) {
      if (str.startsWith(_startStr)) {
        //又是#号又是空格比较占地方 这里省去了 如果你不想省去直接传入str即可
        stackList.add(str.replaceFirst(_startStr, ' '));
      }
    }
    return stackList;
  }
  ///获取剔除忽略包名及其其他无效信息的堆栈
  /// [stackTrace] 堆栈
  /// [ignorePackage] 需要忽略的包名
  static List<String> _getRealStackTrack(
      StackTrace stackTrace, String ignorePackage) {
    ///由于Flutter 上的StackTrack上的不太一样,Android返回的是list flutter返回的是StackTrack 所以需要手动切割 再处理
    List<String> stackList = _fixStack(stackTrace);
    int ignoreDepth = 0;
    int allDepth = stackList.length;
    //倒着查询 查到倒数第一包名和需要屏蔽的包名一致时,数据往上的数据全部舍弃掉
    for (int i = allDepth - 1; i > -1; i--) {
      Match? match = _stackReg.matchAsPrefix(stackList[i]);
      //如果匹配且第一个子项也符合  group 0 表示全部 剩下的数字看子项的多少返回
      if (match != null &&
          (match.group(1)!.startsWith("package:$ignorePackage"))) {
        ignoreDepth = i + 1;
        break;
      }
    }
    stackList = stackList.sublist(ignoreDepth);
    return stackList;
  }
  /// 裁切堆栈
  /// [stackTrace] 堆栈
  /// [maxDepth] 深度
  static List<String> _cropStackTrace(List<String> stackTrace, int? maxDepth) {
    int realDeep = stackTrace.length;
    realDeep =
        maxDepth != null && maxDepth > 0 ? min(maxDepth, realDeep) : realDeep;
    return stackTrace.sublist(0, realDeep);
  }
  ///裁切获取到最终的stack 并获取最大深度的栈信息
  static getCroppedRealStackTrace(
      {required StackTrace stackTrace, ignorePackage, maxDepth}) {
    return _cropStackTrace(
        _getRealStackTrack(stackTrace, ignorePackage), maxDepth);
  }
}

格式化堆栈信息

class StackFormatter implements ILogFormatter<List<String>> {
  @override
  String format(List<String> stackList) {
    ///每一行都设置成单独的 字符串
    StringBuffer sb = StringBuffer();
    ///堆栈是空的直接返回
    if (stackList.isEmpty) {
      return "";
      ///堆栈只有一行那么就返回 - 堆栈
    } else if (stackList.length == 1) {
      return "\n\t-${stackList[0].toString()}\n";
      ///多行堆栈格式化
    } else {
      for (int i = 0; i < stackList.length; i++) {
        if (i == 0) {
          sb.writeln("\n\t┌StackTrace:");
        }
        if (i != stackList.length - 1) {
          sb.writeln("\t├${stackList[i].toString()}");
        } else {
          sb.write("\t└${stackList[i].toString()}");
        }
      }
    }
    return sb.toString();
  }
}

格式化JSON

class JsonFormatter extends ILogFormatter<Map<String, dynamic>> {
  @override
  String format(Map<String, dynamic> data) {
    ///递归调用循环遍历data 在StringBuffer中添加StringBuffer
    String finalString = _forEachJson(data, 0);
    finalString = "\ndata:$finalString";
    return finalString;
  }
  /// [data]  传入需要格式化的数据
  /// [spaceCount]  需要添加空格的长度 一个数字是两个空格
  /// [needSpace] 需不需要添加空格
  /// [needEnter] 需不需要回车
  String _forEachJson(dynamic data, int spaceCount,
      {bool needSpace = true, needEnter = true}) {
    StringBuffer sb = StringBuffer();
    int newSpace = spaceCount + 1;
    if (data is Map) {
      ///如果它是Map走这里
      ///是否需要空格
      sb.write(buildSpace(needSpace ? spaceCount : 0));
      sb.write(needEnter ? "{\n" : "{");
      data.forEach((key, value) {
        ///打印输出 key
        sb.write("${buildSpace(needEnter ? newSpace : 0)}$key: ");
        ///递归调用看value是什么类型 如果字符长度少于30就不回车显示
        sb.write(_forEachJson(value, newSpace,
            needSpace: false,
            needEnter: !(value is Map ? false : value.toString().length < 50)));
        ///不是最后一个就加,
        if (data.keys.last != key) {
          sb.write(needEnter ? ",\n" : ",");
        }
      });
      if (needEnter) {
        sb.writeln();
      }
      sb.write("${buildSpace(needEnter ? spaceCount : 0)}}");
    } else if (data is List) {
      ///如果他是列表 走这里
      sb.write(buildSpace(needSpace ? spaceCount : 0));
      sb.write("[${needEnter ? "\n" : ""}");
      for (var item in data) {
        sb.write(_forEachJson(item, newSpace,
            needEnter: !(item.toString().length < 30)));
        ///不是最后一个就加的,
        if (data.last != item) {
          sb.write(needEnter ? ",\n" : ",");
        }
      }
      sb.write(needEnter ? "\n" : "");
      sb.write("${buildSpace(needSpace?spaceCount:0)}]");
    } else if (data is num || data is bool) {
      ///bool 或者数组不加双引号
      sb.write(data);
    } else if (data is String) {
      ///string 或者其他的打印加双引号 如果他是回车就改变他 按回车分行会错乱
      sb.write("\"${data.replaceAll("\n", r"\n")}\"");
    } else {
      sb.write("$data");
    }
    return sb.toString();
  }
  ///构造空格
  String buildSpace(int deep) {
    String temp = "";
    for (int i = 0; i < deep; i++) {
      temp += "  ";
    }
    return temp;
  }
}

五. 需要用到的常量

///常量
//log的type
enum LogType {
  V, //VERBOSE
  E, //ERROR
  A, //ASSERT
  W, //WARN
  I, //INFO
  D, //DEBUG
}
int logMaxLength=1024;
///log的type 字符串说明
List logTypeStr = ["VERBOSE", "ERROR", "ASSERT", "WARN", "INFO", "DEBUG"];
///log的type 数字说明(匹配的Android原生,ios暂不清楚)
List&lt;int&gt; logTypeNum = [2, 6, 7, 5, 4, 3];

六. 为了控制多个打印器的设置做了一个配置类

class LogConfig {
  ///是否开启日志
  bool _enable = false;
  ///默认的Tag
  String _globalTag = "LogTag";
  ///堆栈显示的深度
  int _stackTraceDepth = 0;
  ///打印的方式
  List<ILogPrint>? _printers;
  LogConfig({enable, globalTag, stackTraceDepth, printers}) {
    _enable = enable;
    _globalTag = globalTag;
    _stackTraceDepth = stackTraceDepth;
    _printers?.addAll(printers);
  }
  @override
  String toString() {
    return 'LogConfig{_enable: $_enable, _globalTag: $_globalTag, _stackTraceDepth: $_stackTraceDepth, _printers: $_printers}';
  }
  get enable => _enable;
  get globalTag => _globalTag;
  get stackTraceDepth => _stackTraceDepth;
  get printers => _printers;
}

七. Log的管理类

class LogManager {
  ///config
  late LogConfig _config;
  ///打印器列表
  List&lt;ILogPrint&gt; _printers = [];
  ///单例模式
  static LogManager? _instance;
  factory LogManager() =&gt; _instance ??= LogManager._();
  LogManager._();
  ///初始化 Manager方法
  LogManager.init({config, printers}) {
    _config = config;
    _printers.addAll(printers);
    _instance = this;
  }
  get printers =&gt; _printers;
  get config =&gt; _config;
  void addPrinter(ILogPrint print) {
    bool isHave = _printers.any((element) =&gt; element == print);
    if (!isHave) {
      _printers.add(print);
    }
  }
  void removePrinter(ILogPrint print) {
    _printers.remove(print);
  }
}

九. 调用LogUtil

class LogUtil {
  static const String _ignorePackageName = "log_demo/utils/log";
  static void V(
      {String? tag,
      dynamic? message,
      LogConfig? logConfig,
      StackTrace? stackTrace,
      Map<String, dynamic>? json}) {
    _logPrint(
        type: LogType.V,
        tag: tag ??= "",
        logConfig: logConfig,
        message: message,
        json: json,
        stackTrace: stackTrace);
  }
  static void E(
      {String? tag,
      dynamic? message,
      LogConfig? logConfig,
      StackTrace? stackTrace,
      Map<String, dynamic>? json}) {
    _logPrint(
        type: LogType.E,
        tag: tag ??= "",
        message: message,
        logConfig: logConfig,
        json: json,
        stackTrace: stackTrace);
  }
  static void I(
      {String? tag,
      dynamic? message,
      LogConfig? logConfig,
      StackTrace? stackTrace,
      Map<String, dynamic>? json}) {
    _logPrint(
        type: LogType.I,
        tag: tag ??= "",
        message: message,
        json: json,
        stackTrace: stackTrace);
  }
  static void D(
      {String? tag,
      dynamic? message,
      LogConfig? logConfig,
      StackTrace? stackTrace,
      Map<String, dynamic>? json}) {
    _logPrint(
        type: LogType.D,
        tag: tag ??= "",
        logConfig: logConfig,
        message: message,
        json: json,
        stackTrace: stackTrace);
  }
  static void A(
      {String? tag,
      LogConfig? logConfig,
      dynamic? message,
      StackTrace? stackTrace,
      Map<String, dynamic>? json}) {
    _logPrint(
        type: LogType.A,
        tag: tag ??= "",
        message: message,
        logConfig: logConfig,
        json: json,
        stackTrace: stackTrace);
  }
  static void W(
      {String? tag,
      dynamic? message,
      LogConfig? logConfig,
      StackTrace? stackTrace,
      Map<String, dynamic>? json}) {
    _logPrint(
        type: LogType.W,
        tag: tag ??= "",
        message: message,
        logConfig: logConfig,
        json: json,
        stackTrace: stackTrace);
  }
  static Future<void> _logPrint({
    required LogType type,
    required String tag,
    LogConfig? logConfig,
    dynamic message,
    StackTrace? stackTrace,
    Map<String, dynamic>? json,
  }) async {
    ///如果logConfig为空那么就用默认的
    logConfig ??= LogManager().config;
    if (!logConfig?.enable) {
      return;
    }
    StringBuffer sb = StringBuffer();
    ///打印当前页面
    if (message.toString().isNotEmpty) {
      sb.write(message);
    }
    ///如果传入了栈且 要展示的深度大于0
    if (stackTrace != null && logConfig?.stackTraceDepth > 0) {
      sb.writeln();
      String stackTraceStr = StackFormatter().format(
          StackTraceUtil.getCroppedRealStackTrace(
              stackTrace: stackTrace,
              ignorePackage: _ignorePackageName,
              maxDepth: logConfig?.stackTraceDepth));
      sb.write(stackTraceStr);
    }
    if (json != null) {
      sb.writeln();
      String body = JsonFormatter().format(json);
      sb.write(body);
    }
    ///获取有几个打印器
    List<ILogPrint> prints = logConfig?.printers ?? LogManager().printers;
    if (prints.isEmpty) {
      return;
    }
    ///遍历打印器 分别打印数据
    for (ILogPrint print in prints) {
      print.logPrint(type: type, tag: tag, message: sb.toString());
    }
  }
}

十. 定义一个Flutter 控制台打印输出的方法

class ConsolePrint extends ILogPrint {
  @override
  void logPrint(
      {required LogType type,
      required String tag,
      required String message,
      StackTrace? stackTrace,
      Map<String, dynamic>? json}) {
    ///如果要开启颜色显示 那么就是1000
    ///如果不开启颜色显示 那么就是1023
    int _maxCharLength = 1000;
    //匹配中文字符以及这些中文标点符号 。 ? ! , 、 ; : “ ” ‘ ' ( ) 《 》 〈 〉 【 】 『 』 「 」 ﹃ ﹄ 〔 〕 … — ~ ﹏ ¥
    RegExp _chineseRegex = RegExp(r"[\u4e00-\u9fa5|\u3002|\uff1f|\uff01|\uff0c|\u3001|\uff1b|\uff1a|\u201c|\u201d|\u2018|\u2019|\uff08|\uff09|\u300a|\u300b|\u3008|\u3009|\u3010|\u3011|\u300e|\u300f|\u300c|\u300d|\ufe43|\ufe44|\u3014|\u3015|\u2026|\u2014|\uff5e|\ufe4f|\uffe5]");
    ///用回车做分割
    List<String> strList = message.split("\n");
    ///判断每句的长度 如果长度过长做切割
    for (String str in strList) {
      ///获取总长度
      int len = 0;
      ///获取当前长度
      int current = 0;
      ///获取截断点数据
      List<int> entry = [0];
      ///遍历文字 查看真实长度
      for (int i = 0; i < str.length; i++) {
        //// 一个汉字再打印区占三个长度,其他的占一个长度
        len += str[i].contains(_chineseRegex) ? 3 : 1;
        ///寻找当前字符的下一个字符长度
        int next = (i + 1) < str.length
            ? str[i + 1].contains(_chineseRegex)
                ? 3
                : 1
            : 0;
        ///当前字符累计长度 如果达到了需求就清空
        current += str[i].contains(_chineseRegex) ? 3 : 1;
        if (current < _maxCharLength && (current + next) >= _maxCharLength) {
          entry.add(i);
          current = 0;
        }
      }
      ///如果最后一个阶段点不是最后一个字符就添加上
      if (entry.last != str.length - 1) {
        entry.add(str.length);
      }
      ///如果所有的长度小于1023 那么打印没有问题
      if (len < _maxCharLength) {
        _logPrint(type, tag, str);
      } else {
        ///按照获取的截断点来打印
        for (int i = 0; i < entry.length - 1; i++) {
          _logPrint(type, tag, str.substring(entry[i], entry[i + 1]));
        }
      }
    }
  }
  _logPrint(LogType type, String tag, String message) {
    ///前面的\u001b[31m用于设定SGR颜色,后面的\u001b[0m相当于一个封闭标签作为前面SGR颜色的作用范围的结束点标记。
    /// \u001b[3 文字颜色范围 0-7 标准颜色 0是黑色 1是红色 2是绿色 3是黄色 4是蓝色 5是紫色 6蓝绿色 是 7是灰色 范围之外都是黑色
    /// \u001b[9 文字颜色范围 0-7 高强度颜色 0是黑色 1是红色 2是绿色 3是黄色 4是蓝色 5是紫色 6蓝绿色 是 7是灰色 范围之外都是黑色
    /// 自定义颜色 \u001b[38;2;255;0;0m 表示文字颜色 2是24位 255 0 0 是颜色的RGB 可以自定义颜色
    /// \u001b[4 数字 m 是背景色
    /// \u001b[1m 加粗
    /// \u001b[3m 斜体
    /// \u001b[4m 下划线
    /// \u001b[7m 黑底白字
    ///\u001b[9m 删除线
    ///\u001b[0m 结束符
    //////详情看 https://www.cnblogs.com/zt123123/p/16110475.html
    String colorHead = "";
    String colorEnd = "\u001b[0m";
    switch (type) {
      case LogType.V:
        // const Color(0xff181818);
        colorHead = "\u001b[38;2;187;187;187m";
        break;
      case LogType.E:
        colorHead = "\u001b[38;2;255;0;6m";
        break;
      case LogType.A:
        colorHead = "\u001b[38;2;143;0;5m";
        break;
      case LogType.W:
        colorHead = "\u001b[38;2;187;187;35m";
        break;
      case LogType.I:
        colorHead = "\u001b[38;2;72;187;49m";
        break;
      case LogType.D:
        colorHead = "\u001b[38;2;0;112;187m";
        break;
    }
    /// 这里是纯Flutter项目所以在控制台打印这样子是可以有颜色的 如果是flutter混编 安卓原生侧打印\u001b 可能是一个乱码也没有变色效果
    /// 如果你不想只在调试模式打印 你可以把debugPrint换成print
    debugPrint("$colorHead$message$colorEnd");
    /// 如果原生侧有封装log工具直接 写一个methodChannel 传参数就好 ,如果没有,可以调用原生的log打印 传入 level tag 和message
    /// kDebugMode 用这个可以判断是否在debug模式下
    /// if(kDebugMode){
    /// 在debug模式下打印日志
    //  bool? result=await CustomChannelUtil.printLog(level:logTypeNum[type.index],tag:tag,message:message);
    /// }
  }
}

十一. 现在使用前初始化log打印器一次

 Widget build(BuildContext context) {
    LogManager.init(
        config: LogConfig(enable: true, globalTag: "TAG", stackTraceDepth: 5),
        printers: [ConsolePrint()]);

使用

///打印堆栈
    LogUtil.I(tag: "test", stackTrace: StackTrace.current);
///打印json
    LogUtil.E(tag: "JSON", json: json);
///打印信息
    LogUtil.V(tag: "LogText", message: message);

以上就是Flutter学习LogUtil封装与实现实例详解的详细内容,更多关于Flutter LogUtil 封装实现的资料请关注我们其它相关文章!

(0)

相关推荐

  • flutter使用tauri实现一个一键视频转4K软件

    目录 前言 开发原因 工作原理 开发过程 前言 先说结论,tauri是一个非常优秀的前端桌面开发框架,但是,rust门槛太高了. 一开始我是用electron来开发的,但是打包后发现软件运行不是很流畅,有那么一点卡顿.于是为了所谓的性能,我尝试用tauri来做我的软件.在学了两星期的rust后,我发现rust真的太难学了,最后硬是边做边查勉强做出来了. 软件运行起来是比electron做的丝滑很多,但是写rust真的是很痛苦,rust的写法和其他语言是截然不同的,在不知道之前我是个rust吹,觉

  • Flutter实现一个支持渐变背景的Button示例详解

    目录 Flutter中的按钮 不完美的地方 在child中处理 外面套一个wrapper MaterialStateProperty MaterialStatesController 边距问题 EnhancedButton Flutter中的按钮 自Flutter 1.20 新增了ButtonStyleButton 系列按钮,可以说非常好用了,默认样式比之前漂亮了许多,扩展性也增加了很多.按钮样式统一由ButtonStyle这个类提供,支持根据各种状态(MaterialState)变化的属性,也

  • Flutter 假异步的实现示例

    就像 android 有 handle 一样,消息队列这东西好像还真是系统必备,Flutter 也有自己的消息队列,只不过队列直接封装在了 Dart 的线程类型 Isolate 里面了,不过 Flutter 还是提供了 Futrue 这个 API 来专门来操作各种消息,以及实现基于消息队列的假异步 Flutter 的"异步"机制 这里的异步是加了引号的,可见此异步非真异步,而是假异步.Flutter 的 异步 不是开新线程,而是往所属线程的 消息队列 中添加任务,当然大家也可以按上文那

  • Flutter 异步编程之单线程下异步模型图文示例详解

    目录 一. 本专栏图示概念规范 1. 任务概念规范 2. 任务的状态 3. 时刻与时间线 4.同步与异步 二.理解单线程中的异步任务 1. 任务的分配 2.异步任务特点 3. 异步任务完成与回调 三. Dart 语言中的异步 1.编程语言中与异步模型的对应关系 2.Dart 编程中的异步任务 3.当前任务分析 四.异步模型的延伸 1. 单线程异步模型的局限性 2. 多线程与异步的关系 3. Dart 中如何解决单线程异步模型的局限性 一. 本专栏图示概念规范 本专栏是对 异步编程 的系统探索,会

  • Android Flutter实现精灵图的使用详解

    目录 前言 如何使用精灵图 自定义实现加载 Flame加载精灵图 前言 在日常开发中遇到的图片展示一般是静态图和Gif图两种形式(静态和动态的不同).与此同时当需要对图片做效果时让其动起来,常用方案是Gif图播放或者是帧动画(多种静态图轮询播放).但在游戏开发中还有一种动图表现形式叫做Sprite图(雪碧图),其在前端开发中也是很常见.为什么需要使用精灵图,因为每张图片显示都需要去发起请求获取,若页面图片数量较多(一个页面有几十个小图)并发请求将是一个大数量级,可能会造成页面加载速度降低,精灵图

  • 详解flutter中常用的container layout实例

    目录 简介 Container的使用 旋转Container Container中的BoxConstraints 总结 简介 在上一篇文章中,我们列举了flutter中的所有layout类,并且详细介绍了两个非常常用的layout:Row和Column. 掌握了上面两个基本的layout还是不够的,如果需要应付日常的layout使用,我们还需要掌握多一些layout组件.今天我们会介绍一个功能强大的layout:Container layout. Container的使用 Container是一

  • Flutter学习LogUtil封装与实现实例详解

    目录 一. 为什么要封装打印类 二. 需要哪些类 三. 打印输出的抽象类 四. 格式化日志内容 格式化堆栈 堆栈裁切工具类 格式化堆栈信息 格式化JSON 五. 需要用到的常量 六. 为了控制多个打印器的设置做了一个配置类 七. Log的管理类 九. 调用LogUtil 十. 定义一个Flutter 控制台打印输出的方法 十一. 现在使用前初始化log打印器一次 使用 一. 为什么要封装打印类 虽然 flutter/原生给我们提供了日志打印的功能,但是超出一定长度以后会被截断 Json打印挤在一

  • AngularJS封装$http.post()实例详解

      AngularJS封装$http.post()实例详解 用了不是很长的时间跟了一个移动APP项目,用的是ionic + AngularJS + cordova框架,其间遇到过挺多问题,其中一个就是对Ajax的封装问题. 其实针对封装问题一直以来就没停止过谈论,不同的项目也有不同的需求,举个典型的例子,我在做这个项目的时候因为一开始没有考虑封装问题所以被批评了,而我的一个朋友却因为封装了受到了批评--很尴尬不是么. 那么对于是否要封装这个问题,究竟该怎么界定?其实这不是一个很复杂的问题,归根到

  • springbatch的封装与使用实例详解

    Spring Batch官网介绍: A lightweight, comprehensive batch framework designed to enable the development of robust batch applications vital for the daily operations of enterprise systems.(一款轻量的.全面的批处理框架,用于开发强大的日常运营的企业级批处理应用程序.) springbatch 主要实现批量数据的处理,我对bat

  • 公共Hooks封装文件下载useDownloadFile实例详解

    目录 引言 项目环境 封装前提:各方法对比 封装分解:下载核心代码 封装分解:用户体验设计 useDownloadFile.js完整代码 引言 对于经常需要开发企业管理后台的前端开发来说,必不可少的需要使用表格对于数据进行操作,在对于现有项目进行代码优化时,封装一些公共的Hooks. 本篇文章为useDownloadFile.js 基于个人项目环境进行封装的Hooks,仅以本文介绍封装Hooks思想心得,故相关代码可能不适用他人 项目环境 Vue3.x + Ant Design Vue3.x +

  • Flutter快速制作一个水印组件实例详解

    目录 正文 通过child属性将水印叠加给子组件 createWatermark方法 Watermark组件的完整代码 正文 项目开发的过程中,经常会遇到添加水印的需求,其作用无非就是防止重要信息通过截图外传.(虽然我觉得并没有什么卵用,但领导的需求是不容质疑的) 那么,作为一线码农的我,也只能屁颠屁颠的开搞. 通过child属性将水印叠加给子组件 水印组件,既然是组件,就是需要发扬Flutter套娃的精神,通过child属性将水印叠加给子组件. class Watermark extends

  • jQuery学习笔记之Ajax用法实例详解

    本文实例讲述了jQuery学习笔记之Ajax用法.分享给大家供大家参考,具体如下: 一.Ajax请求 1.jQuery.ajax(options) 通过 HTTP 请求加载远程数据.jQuery 底层 AJAX 实现.简单易用的高层实现见 .get,.post 等. .ajax()返回其创建的XMLHttpRequest对象.大多数情况下你无需直接操作该对象,但特殊情况下可用于手动终止请求..ajax() 只有一个参数:参数 key/value 对象,包含各配置及回调函数信息.详细参数选项见下.

  • Flask框架学习笔记之模板操作实例详解

    本文实例讲述了Flask框架学习笔记之模板操作.分享给大家供大家参考,具体如下: flask的模板引擎是Jinja2. 引入模板的好处是增加程序的可读性和易维护性,从而不用将一堆html代码塞在视图函数中. 还是以hello world为例.最基础的调用模板修饰文本. # 根网址 @app.route('/') def index(): # return render_template("index.html") # 可以给模板传入文本content修饰 content = "

  • 微信小程序 封装http请求实例详解

    微信小程序 封装http请求 最近看了一下微信小程序,大致翻了一下,发现跟angular很相似的,但是比angular简单的很多具体可参考官方文档 https://mp.weixin.qq.com/debug/wxadoc/dev/framework/app-service/page.html?t=2017112 下面将封装http请求服务部分的服务以及引用部分 // 本服务用于封装请求 // 返回的是一个promisepromise var sendRrquest = function (ur

  • Java编程学习的几个典型实例详解

    一.模拟酒店房间管理系统,需要如下几个功能: 1.1 in 房间号 客人名字 入住功能 1.2 out 房间号 退房功能 1.3 search 房间号 查询房间状态 如果房间号为-1 则输出所有房间状态 1.4 quit 或 exit 退出 提示:酒店所有的房间用二维数组来实现 代码实现如下: import java.util.Scanner; public class HotelDemo { //写在类里面,则每个方法都可以访问到,避免了参数传递的繁琐: static int h=5,w=10

  • Vue3中使用typescript封装axios的实例详解

    这个axios封装,因为是用在vue3的demo里面的,为了方便,在vue3的配置里面按需加载element-plus 封装axios http.ts import axios, { AxiosRequestConfig, AxiosRequestHeaders, AxiosResponse } from 'axios' import { IResponseData } from '@/types' import { ElMessage, ElLoading, ILoadingInstance

随机推荐