当前位置: 首页 > news >正文

Node.js的debug模块源码分析及在harmonyOS平台移植

Debug库 是一个小巧但功能强大的 JavaScript 调试工具库,可以帮助开发人员更轻松地进行调试,以便更快地发现和修复问题。它的主要特点是可以轻松地添加调试日志语句,同时在不需要调试时可以轻松地禁用它们,以避免在生产环境中对性能产生影响。我们在一些有名的三方库如socket.io,就能看到debug库的身影,说明它确实很常用。

Debug 库介绍

一个模仿Node.js核心调试技术的小型JavaScript调试实用程序。适用于Node.js和web浏览器。Debug库 是一个小巧但功能强大的 JavaScript 调试工具库,可以帮助开发人员更轻松地进行调试,以便更快地发现和修复问题。它的主要特点是可以轻松地添加调试日志语句,同时在不需要调试时可以轻松地禁用它们,以避免在生产环境中对性能产生影响。

debug库的github:GitHub - debug-js/debug: A tiny JavaScript debugging utility modelled after Node.js core's debugging technique. Works in Node.js and web browsers

它提供了一种比console.log()更方便的打印调试信息的方式。Debug 库包含了一个全局函数 debug(),通过调用这个函数可以创建一个具有指定名称的调试器对象。调试器对象提供了一些方法,可以用于在控制台中输出调试信息。同时,可以使用环境变量来控制调试器对象是否启用。

我移植发布的鸿蒙的debug库:

参见鸿蒙三方中心库:OpenHarmony三方库中心仓

简单使用

下面是一个 Debug 库的代码示例:

let debug = require('debug')('myapp');function myFunction() {debug('This is a debug message');debug('This is a debug message:%d',2);
}myFunction();

首先使用 require() 函数将 Debug 库引入到我们的代码中,并调用 debug() 函数创建一个名为 "myapp" 的调试器对象。然后,我们定义一个名为 myFunction() 的函数,在这个函数中调用调试器对象的 debug() 方法输出一条调试信息。

如果我们想要在控制台中看到这些调试信息,我们需要设置环境变量 DEBUG 的值为 "myapp",这可以在终端中使用命令行来完成:

$ DEBUG=myapp node myscript.js

这会启动名为 "myapp" 的调试器对象,并在控制台中输出所有与该名称相关联的调试信息。如果我们要禁用调试信息,只需要将 DEBUG 环境变量的值设置为空即可:

$ DEBUG= node myscript.js

这将禁用所有调试信息,避免在生产环境中对性能产生影响。

此外,js-debug库还支持设置不同的命名空间来过滤特定的调试信息。例如:

const debug1 = require('debug')('app:debug1')const debug2 = require('debug')('app:debug2')

然后在终端输入DEBUG=app:* node app.js,则会输出形如app:debug1 This is debug1 message!的信息。

上述示例是基于nodejs的命令行环境下的。

鸿蒙下的使用举例:

import debugModule from "@yyz116/debug" // debug()const debug = debugModule("MyApp:client"); debug.enable('') //为空则是禁用日志输出
debug.enable('MyApp:client'); //开启日志。注意,默认日志输出是关闭的,开启调试日志需要代码里调用enbale接口。不同于原库的环境变量方式debug('booting %s', 'Hello MyTest');
debug("this is a %d test", 5);debug("hello");

源码简析

源码很简单,代码量也不多,很容易看懂。首先看 index.js:

if (typeof process !== 'undefined' && process.type === 'renderer') {
module.exports = require('./browser.js');
} else {
module.exports = require('./node.js');
}

说明它同时支持浏览器环境和nodejs环境,通过if判断process对象是否存在来决定是使用哪个文件的导出。

由于我们是要移植到鸿蒙系统中的,于是node的那版就不看了,直接看browser.js源码:

/* eslint-env browser *//*** This is the web browser implementation of `debug()`.*/exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.storage = localstorage();
exports.destroy = (() => {let warned = false;return () => {if (!warned) {warned = true;console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');}};
})();/*** Colors.*/exports.colors = ['#0000CC','#0000FF',......'#FFCC33'
];/*** Currently only WebKit-based Web Inspectors, Firefox >= v31,* and the Firebug extension (any Firefox version) are known* to support "%c" CSS customizations.** TODO: add a `localStorage` variable to explicitly enable/disable colors*/// eslint-disable-next-line complexity
function useColors() {// NB: In an Electron preload script, document will be defined but not fully// initialized. Since we know we're in Chrome, we'll just detect this case// explicitlyif (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {return true;}// Internet Explorer and Edge do not support colors.if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {return false;}// Is webkit? http://stackoverflow.com/a/16459606/376773// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||// Is firebug? http://stackoverflow.com/a/398120/376773(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||// Is firefox >= v31?// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||// Double check webkit in userAgent just in case we are in a worker(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
}/*** Colorize log arguments if enabled.** @api public*/function formatArgs(args) {args[0] = (this.useColors ? '%c' : '') +this.namespace +(this.useColors ? ' %c' : ' ') +args[0] +(this.useColors ? '%c ' : ' ') +'+' + module.exports.humanize(this.diff);if (!this.useColors) {return;}const c = 'color: ' + this.color;args.splice(1, 0, c, 'color: inherit');// The final "%c" is somewhat tricky, because there could be other// arguments passed either before or after the %c, so we need to// figure out the correct index to insert the CSS intolet index = 0;let lastC = 0;args[0].replace(/%[a-zA-Z%]/g, match => {if (match === '%%') {return;}index++;if (match === '%c') {// We only are interested in the *last* %c// (the user may have provided their own)lastC = index;}});args.splice(lastC, 0, c);
}/*** Invokes `console.debug()` when available.* No-op when `console.debug` is not a "function".* If `console.debug` is not available, falls back* to `console.log`.** @api public*/
exports.log = console.debug || console.log || (() => {});/*** Save `namespaces`.** @param {String} namespaces* @api private*/
function save(namespaces) {try {if (namespaces) {exports.storage.setItem('debug', namespaces);} else {exports.storage.removeItem('debug');}} catch (error) {// Swallow// XXX (@Qix-) should we be logging these?}
}/*** Load `namespaces`.** @return {String} returns the previously persisted debug modes* @api private*/
function load() {let r;try {r = exports.storage.getItem('debug');} catch (error) {// Swallow// XXX (@Qix-) should we be logging these?}// If debug isn't set in LS, and we're in Electron, try to load $DEBUGif (!r && typeof process !== 'undefined' && 'env' in process) {r = process.env.DEBUG;}return r;
}/*** Localstorage attempts to return the localstorage.** This is necessary because safari throws* when a user disables cookies/localstorage* and you attempt to access it.** @return {LocalStorage}* @api private*/function localstorage() {try {// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context// The Browser also has localStorage in the global context.return localStorage;} catch (error) {// Swallow// XXX (@Qix-) should we be logging these?}
}module.exports = require('./common')(exports);const {formatters} = module.exports;/*** Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.*/formatters.j = function (v) {try {return JSON.stringify(v);} catch (error) {return '[UnexpectedJSONParseError]: ' + error.message;}
};

 代码量很少,注定移植是很简单的事情。这个文件中就定义了一系列的函数,挂载到了node的exports对象上面。至于功能是干什么的,简单的不用再说了。直接看这里:

module.exports = require('./common')(exports);const {formatters} = module.exports;

这里才是关键,核心的类和对象的实现是在common.js文件里的。

//common.js
/*** This is the common logic for both the Node.js and web browser* implementations of `debug()`.*/function setup(env) {createDebug.debug = createDebug;createDebug.default = createDebug;createDebug.coerce = coerce;createDebug.disable = disable;createDebug.enable = enable;createDebug.enabled = enabled;createDebug.humanize = require('ms');createDebug.destroy = destroy;Object.keys(env).forEach(key => {createDebug[key] = env[key];});/*** The currently active debug mode names, and names to skip.*/createDebug.names = [];createDebug.skips = [];/*** Map of special "%n" handling functions, for the debug "format" argument.** Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".*/createDebug.formatters = {};/*** Selects a color for a debug namespace* @param {String} namespace The namespace string for the debug instance to be colored* @return {Number|String} An ANSI color code for the given namespace* @api private*/function selectColor(namespace) {let hash = 0;for (let i = 0; i < namespace.length; i++) {hash = ((hash << 5) - hash) + namespace.charCodeAt(i);hash |= 0; // Convert to 32bit integer}return createDebug.colors[Math.abs(hash) % createDebug.colors.length];}createDebug.selectColor = selectColor;/*** Create a debugger with the given `namespace`.** @param {String} namespace* @return {Function}* @api public*/function createDebug(namespace) {let prevTime;let enableOverride = null;let namespacesCache;let enabledCache;function debug(...args) {// Disabled?if (!debug.enabled) {return;}const self = debug;// Set `diff` timestampconst curr = Number(new Date());const ms = curr - (prevTime || curr);self.diff = ms;self.prev = prevTime;self.curr = curr;prevTime = curr;args[0] = createDebug.coerce(args[0]);if (typeof args[0] !== 'string') {// Anything else let's inspect with %Oargs.unshift('%O');}// Apply any `formatters` transformationslet index = 0;args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {// If we encounter an escaped % then don't increase the array indexif (match === '%%') {return '%';}index++;const formatter = createDebug.formatters[format];if (typeof formatter === 'function') {const val = args[index];match = formatter.call(self, val);// Now we need to remove `args[index]` since it's inlined in the `format`args.splice(index, 1);index--;}return match;});// Apply env-specific formatting (colors, etc.)createDebug.formatArgs.call(self, args);const logFn = self.log || createDebug.log;logFn.apply(self, args);}debug.namespace = namespace;debug.useColors = createDebug.useColors();debug.color = createDebug.selectColor(namespace);debug.extend = extend;debug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.Object.defineProperty(debug, 'enabled', {enumerable: true,configurable: false,get: () => {if (enableOverride !== null) {return enableOverride;}if (namespacesCache !== createDebug.namespaces) {namespacesCache = createDebug.namespaces;enabledCache = createDebug.enabled(namespace);}return enabledCache;},set: v => {enableOverride = v;}});// Env-specific initialization logic for debug instancesif (typeof createDebug.init === 'function') {createDebug.init(debug);}return debug;}function extend(namespace, delimiter) {const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);newDebug.log = this.log;return newDebug;}/*** Enables a debug mode by namespaces. This can include modes* separated by a colon and wildcards.** @param {String} namespaces* @api public*/function enable(namespaces) {createDebug.save(namespaces);createDebug.namespaces = namespaces;createDebug.names = [];createDebug.skips = [];let i;const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);const len = split.length;for (i = 0; i < len; i++) {if (!split[i]) {// ignore empty stringscontinue;}namespaces = split[i].replace(/\*/g, '.*?');if (namespaces[0] === '-') {createDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$'));} else {createDebug.names.push(new RegExp('^' + namespaces + '$'));}}}/*** Disable debug output.** @return {String} namespaces* @api public*/function disable() {const namespaces = [...createDebug.names.map(toNamespace),...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace)].join(',');createDebug.enable('');return namespaces;}/*** Returns true if the given mode name is enabled, false otherwise.** @param {String} name* @return {Boolean}* @api public*/function enabled(name) {if (name[name.length - 1] === '*') {return true;}let i;let len;for (i = 0, len = createDebug.skips.length; i < len; i++) {if (createDebug.skips[i].test(name)) {return false;}}for (i = 0, len = createDebug.names.length; i < len; i++) {if (createDebug.names[i].test(name)) {return true;}}return false;}/*** Convert regexp to namespace** @param {RegExp} regxep* @return {String} namespace* @api private*/function toNamespace(regexp) {return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, '*');}/*** Coerce `val`.** @param {Mixed} val* @return {Mixed}* @api private*/function coerce(val) {if (val instanceof Error) {return val.stack || val.message;}return val;}/*** XXX DO NOT USE. This is a temporary stub function.* XXX It WILL be removed in the next major release.*/function destroy() {console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');}createDebug.enable(createDebug.load());return createDebug;
}module.exports = setup;

代码也比较简单。主要看三个函数,setup, createDebug函数和其内部的debug函数。

function setup(env) {createDebug.debug = createDebug;createDebug.default = createDebug;createDebug.coerce = coerce;createDebug.disable = disable;createDebug.enable = enable;createDebug.enabled = enabled;createDebug.humanize = require('ms');createDebug.destroy = destroy;Object.keys(env).forEach(key => {createDebug[key] = env[key];});/*** The currently active debug mode names, and names to skip.*/createDebug.names = [];createDebug.skips = [];
.....
}

JavaScript中的函数是一种特殊的对象,因此可以像普通对象一样被扩展属性和方法。这种行为是由JavaScript中的函数对象的特性所决定的。

在JavaScript中,函数也是一种对象,也可以拥有属性和方法。当我们定义一个函数时,实际上是在内存中创建了一个函数对象,并且该函数对象可以拥有自己的属性和方法。这意味着我们可以在函数外部对其进行扩展,添加新的属性和方法,或者修改已有的属性和方法。上述代码是给createDebug的函数扩展了一些属性和方法。这些属性会成为函数对象的一部分,并且可以被访问和使用。

JavaScript中函数对象的这种特性为我们提供了一定的灵活性,使得可以在函数对象上添加额外的信息或者功能,这在某些场景下是非常有用。

需要注意的是,尽管函数对象可以被扩展属性和方法,但这并不意味着推荐在实际开发中频繁地对函数对象进行扩展。通常情况下,函数应当专注于其原本的用途和功能,并遵循单一职责原则。

另外需要注意的是,createDebug是一个函数而不是一个类。在JavaScript中,函数和类都可以用来创建对象,但它们的语法和用法有所不同。createDebug函数被用作一个工厂函数,用于创建调试器对象。它返回一个包含内部状态和方法的对象,而不是一个类的实例。通过调用createDebug函数,我们可以得到一个具有特定功能和状态的调试器对象。在这种情况下,并没有使用JavaScript中的类(class)关键字来定义createDebug,而是直接定义了一个函数来实现相同的功能。

js在es6规范之前没有class的定义,但可以通过函数来实现类对象的行为,这种方式通常称为构造函数和原型继承。通过构造函数和原型继承的方式,模拟类和实现类对象的行为。如下面这一小段代码:

// 通过构造函数定义类
function Person(name, age) {this.name = name;this.age = age;
}// 在原型上定义方法
Person.prototype.sayHello = function() {console.log('Hello, my name is ' + this.name);
};// 创建类的实例
let person1 = new Person('Alice', 25);
let person2 = new Person('Bob', 30);// 调用实例方法
person1.sayHello(); // 输出:Hello, my name is Alice
person2.sayHello(); // 输出:Hello, my name is Bob

setup函数分析

在给定环境env下,通过调用setup(env)来设置和返回一个名为createDebug的函数。createDebug函数内部定义了一个名为debug的内部函数,以及一系列与debug相关的属性和方法。这种将函数作为返回值的写法是一种高阶函数的应用,通过内部函数的闭包特性,我们可以在外部函数中定义私有的属性和方法,并返回一个可供外部使用的函数。

接下来,让我们解释一下debug函数。debug是在createDebug函数内部定义的一个函数,用于创建一个带有给定namespace的调试器。它还包含一些内部状态和逻辑,用于控制调试器的输出行为。在这个代码中,通过闭包的方式实现了一些私有状态的存储和访问。

这种嵌套定义的写法充分利用了JavaScript中闭包的特性,可以有效地封装内部状态,并提供一个对外的接口。这种写法为代码的模块化和可维护性提供了一定的帮助。

简单来说,createDebug函数是用于设置调试功能的工厂函数,而debug函数是用于创建特定命名空间的调试器的函数。整个代码的结构是基于函数式编程和闭包的思想,通过高阶函数的方式来组织代码和管理状态,使得逻辑清晰,灵活性较高。

d.ts声明文件

为了便于在TypeScript 中能够使用js的代码,d.ts声明文件必不可少。以便在开发过程中进行类型检查、自动补全、跳转到定义等操作。

.d.ts声明文件(Declaration Files)是用来描述一个 JavaScript 模块、库或者框架的类型信息以及代码结构的文件。它的作用是为了在开发阶段能够让 TypeScript 或者其他支持类型检查的工具能够了解 JavaScript 代码中的类型信息和结构。

为什么需要.d.ts声明文件?主要是因为 JavaScript 是一种动态类型语言,而 TypeScript 是一种静态类型的超集。为了在 TypeScript 中能够对 JavaScript 模块进行类型检查和更好的代码提示,需要声明文件来提供类型信息。很多优秀的 JavaScript 模块和库都提供了官方或者社区维护的声明文件,以便让 TypeScript 或其他支持类型检查的工具能够更好地理解和协助开发者使用这些模块。

//index.d.ts
declare var debug: debug.Debug & { debug: debug.Debug; default: debug.Debug };
export = debug;declare namespace debug {interface Debug {(namespace: string): Debugger;coerce: (val: any) => any;disable: () => string;enable: (namespaces: string) => void;enabled: (namespaces: string) => boolean;formatArgs: (this: Debugger, args: any[]) => void;log: (...args: any[]) => any;selectColor: (namespace: string) => string | number;names: RegExp[];skips: RegExp[];formatters: Formatters;inspectOpts?: {hideDate?: boolean | number | null;colors?: boolean | number | null;depth?: boolean | number | null;showHidden?: boolean | number | null;};}type IDebug = Debug;interface Formatters {[formatter: string]: (v: any) => string;}type IDebugger = Debugger;interface Debugger {(formatter: any, ...args: any[]): void;color: string;diff: number;enabled: boolean;enable: (namespaces: string) => void;log: (...args: any[]) => any;namespace: string;destroy: () => boolean;extend: (namespace: string, delimiter?: string) => Debugger;}
}

interface Debug是对应 createDebug函数的接口,interface Debugger是对应内部的debug函数的接口声明。因为interface Debug 接口中包括了coercedisableenableenabled 等方法,这些方法与 createDebug 函数的行为和特性相符。

harmonyOS平台移植

搞懂了源码,移植就简单了。下面分享下在harmonyOS平台下的移植。

新建一harmony.js的文件,仿照实现以下内容:

/* eslint-env harmonyos */
/*** This is the harmonyos implementation of `debug()`.* author:yangyongzhen* blog: blog.scdn.net/qq8864*/let exp = {}exp.useColors = useColors;
exp.formatArgs = formatArgs;
exp.save = save;
exp.load = load;exp.storage = (function () {var data = {};return {setItem: function (key, item) { data[key] = item; },getItem: function (key) { return data[key]; },removeItem: function (key) { delete data[key]; },};
})();exp.destroy  = (() => {let warned = false;return () => {if (!warned) {warned = true;console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');}};
})();/*** Invokes `console.debug()` when available.* No-op when `console.debug` is not a "function".* If `console.debug` is not available, falls back* to `console.log`.** @api public*/
exp.log = console.log || (() => {});/*** Colors.*/exp.colors = ['#0000CC','#0000FF',......'#FFCC33'
];/*** Currently only WebKit-based Web Inspectors, Firefox >= v31,* and the Firebug extension (any Firefox version) are known* to support "%c" CSS customizations.** TODO: add a `localStorage` variable to explicitly enable/disable colors*/// eslint-disable-next-line complexity
function useColors() {// NB: In an Electron preload script, document will be defined but not fully// initialized. Since we know we're in Chrome, we'll just detect this case// explicitlyreturn false;
}/*** Colorize log arguments if enabled.** @api public*/function formatArgs(args) {args[0] = (this.useColors ? '%c' : '') +this.namespace +(this.useColors ? ' %c' : ' ') +args[0] +(this.useColors ? '%c ' : ' ');if (!this.useColors) {return;}const c = 'color: ' + this.color;args.splice(1, 0, c, 'color: inherit');// The final "%c" is somewhat tricky, because there could be other// arguments passed either before or after the %c, so we need to// figure out the correct index to insert the CSS intolet index = 0;let lastC = 0;args[0].replace(/%[a-zA-Z%]/g, match => {if (match === '%%') {return;}index++;if (match === '%c') {// We only are interested in the *last* %c// (the user may have provided their own)lastC = index;}});args.splice(lastC, 0, c);
}/*** Save `namespaces`.** @param {String} namespaces* @api private*/
function save(namespaces) {try {if (namespaces) {exp.storage.setItem('debug', namespaces);} else {exp.storage.removeItem('debug');}} catch (error) {// Swallow// XXX (@Qix-) should we be logging these?}
}/*** Load `namespaces`.** @return {String} returns the previously persisted debug modes* @api private*/
function load() {let r;try {r = exp.storage.getItem('debug');} catch (error) {// Swallow// XXX (@Qix-) should we be logging these?}// If debug isn't set in LS, and we're in Electron, try to load $DEBUGif (!r && typeof process !== 'undefined' && 'env' in process) {r = process.env.DEBUG;}return r;
}import {setup} from './common'var outs = setup(exp);const {formatters} = outs;/*** Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.*/formatters.j = function (v) {try {return JSON.stringify(v);} catch (error) {return '[UnexpectedJSONParseError]: ' + error.message;}
};export {outs};

发布到中心仓

写完单元测试用例,测试没问题,发布的过程很简单。参见:HarmonyOS 鸿蒙应用开发(九、还是蓝海,如何贡献第三方库)_鸿蒙开发第三方库社区-CSDN博客

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注博主,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新VIP学习资料,请关注猫哥公众号【猫青年】,回复“鸿蒙”获取

其他资源

分享7个实用又高效的 Node.js 工具库

npm--debug模块_npm debug-CSDN博客

GitHub - debug-js/debug: A tiny JavaScript debugging utility modelled after Node.js core's debugging technique. Works in Node.js and web browsers

相关文章:

Node.js的debug模块源码分析及在harmonyOS平台移植

Debug库 是一个小巧但功能强大的 JavaScript 调试工具库&#xff0c;可以帮助开发人员更轻松地进行调试&#xff0c;以便更快地发现和修复问题。它的主要特点是可以轻松地添加调试日志语句&#xff0c;同时在不需要调试时可以轻松地禁用它们&#xff0c;以避免在生产环境中对性…...

【Crypto | CTF】BUUCTF RSA2

天命&#xff1a;密码学越来越难了&#xff0c;看别人笔记都不知道写啥 天命&#xff1a;莫慌&#xff0c;虽然我不会推演法&#xff0c;但我可以用归纳法 虽然我不知道解题的推演&#xff0c;但我可以背公式啊哈哈哈 虽然我不会这题&#xff0c;但是我也能做出来 公式我不知…...

单片机学习笔记---红外遥控红外遥控电机调速(完结篇)

目录 低电平触发中断和下降沿触发中断的区别 红外遥控 Int0.c Int.h Timer0.c Timer0.h IR.c IR.h main.c 红外遥控电机调速 Timer1.c Timer.h Motor.c Motor.h main.c 上一节讲了红外发送和接收的工作原理&#xff0c;这一节开始代码演示&#xff01; 提前说…...

Linux第62步_备份移植好的所有的文件和文件夹

1、备份“my-tfa”目录下所有的文件和文件夹 1)、打开终端 输入“ls回车”&#xff0c;列出当前目录下所有的文件和文件夹 输入“cd linux回车”&#xff0c;切换“linux”目录下 输入“ls回车”&#xff0c;列出当前目录下所有的文件和文件夹 输入“cd atk-mp1/回车”&am…...

【xss跨站漏洞】xss漏洞前置知识点整理

xss漏洞成因 xss漏洞是一种前端javascript产生的漏洞。 我们网站基本都是会用到javascript编写一些东西&#xff0c;浏览器也能直接识别javascript。 如果有一个地方能够输入文字&#xff0c;但是他又没有过滤你的输入&#xff0c;那么自己或者他人看到你输入的javascript代…...

mac下mysql 常用命令

mysql启动命令 在Mac OS X启动和停止MySQL服务的命令&#xff0c; 启动MySQL服务 sudo /usr/local/mysql/support-files/mysql.server start 停止MySQL服务 sudo /usr/local/mysql/support-files/mysql.server stop 重启MySQL服务 sudo /usr/local/mysql/support-files/mys…...

2.21号qt

1.QMainWindow中常用的类 继承于QMainWindow类&#xff0c;原因该类提供了QWidget没有提供的成员函数。 菜单栏、工具栏、状态栏、浮动窗口&#xff08;铆接部件&#xff09;、核心部件 1.1 菜单栏 QMenuBar //创建菜单栏 QMenuBar 最多只能有一个 QMenuBar *mbar menu…...

什么是MVVM?MVC、MVP与MVVM模式的区别?

MVVM&#xff08;Model-View-ViewModel&#xff09;是一种软件架构模式&#xff0c;用于将用户界面&#xff08;View&#xff09;与业务逻辑&#xff08;Model&#xff09;分离&#xff0c;并通过ViewModel来连接两者。MVVM的目标是实现可测试性、可维护性和可复用性。 MVC&am…...

ElementUI组件的安装和使用

Element UI 是一款基于 Vue 2.0 的桌面端组件库&#xff0c;主要用于快速构建网站的前端部分。它提供了丰富的组件&#xff0c;如按钮、输入框、表格、标签页等&#xff0c;以及一些布局元素&#xff0c;如布局容器、分割线等。Element UI 的设计风格简洁&#xff0c;易于上手&…...

Laravel01 课程介绍以及Laravel环境搭建

Laravel01 课程介绍 1. Laravel2. mac开发环境搭建(通过Homebrew)3. 创建一个项目 1. Laravel 公司中面临着PHP项目与Java项目并行&#xff0c;所以需要我写PHP的项目&#xff0c;公司用的框架就是Laravel&#xff0c;所以在B站上找了一门课学习。 Laravel中文文档地址 https…...

面试redis篇-03缓存击穿

原理 缓存击穿:给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮 解决方案一:互斥锁 解决方案二:逻辑过期 提问与回答 面试官 :什么是缓存击穿 ? 怎么解决 ? 回答: 缓存击穿的意思…...

k8s容器以及基础设施优化

1.硬件系统选型&#xff1a;宿主机通用配置16c/32GB/4网卡队列 2.os优化&#xff1a;单机支持百万tcp并发&#xff0c;/etc/sysctl.conf,/etc/security/limits.conf 3.k8s&容器层优化&#xff1a;性能优化initContainer 4.kube-dns优化&#xff1a;增大--cache-size,设置…...

蓝桥杯备赛系列——倒计时50天!

蓝桥杯备赛系列 倒计时50天&#xff01; 前缀和和差分 知识点 **前缀和数组&#xff1a;**假设原数组用a[i]表示&#xff0c;前缀和数组用sum[i]表示&#xff0c;那么sum[i]表示的是原数组前i项之和&#xff0c;注意一般用前缀和数组时&#xff0c;原数组a[i]的有效下标是从…...

jenkins配置ssh的时候测试连接出现Algorithm negotiation fail

背景&#xff1a;当jenkins升级后&#xff0c;同时ssh插件也升级&#xff0c;测试ssh连接的时候 出现的问题&#xff1a; com.jcraft.jsch.JSchAlgoNegoFailException: Algorithm negotiation fail: algorithmName"server_host_key" jschProposal"ecdsa-sha2-n…...

思维模型整合

思维模型整合 4P--- 4C思考模型能力圈模型 4P— 4C思考模型 在竞争激烈的今天&#xff0c;每个赛道都有众多可以为客户提供相同价值的对手&#xff0c;而赛道中的佼佼者之所以能打败大部分人&#xff0c;可能并不是他们能比别人更能讨好大众&#xff0c;而是因为在这个赛道它有…...

代理模式笔记

代理模式 代理模式代理模式的应用场景先理解什么是代理&#xff0c;再理解动静态举例举例所用代码 动静态的区别静态代理动态代理 动态代理的优点代理模式与装饰者模式的区别 代理模式 代理模式在设计模式中是7种结构型模式中的一种&#xff0c;而代理模式有分动态代理&#x…...

手机中有哪些逆向进化的功能

手机中有哪些逆向进化的功能&#xff1f;逆向进化是指明明很优秀的很方便的功能&#xff0c;却因为成本或者其他工业原因莫名其妙地给取消了。 逆向进化1&#xff1a;可拆卸电池-变为不可拆卸电池。 智能手机为了追求轻薄等原因&#xff0c;所以移除了可拆卸电池功能。将电池…...

LeetCode24.两两交换链表中的节点

参考链接&#xff1a;代码随想录&#xff1a;LeetCode24.两两交换链表中的节点 我这里使用了3个变量进行暴力交换&#xff0c;简单快捷&#xff01;但是有一点想不明白&#xff0c;return这里只能写dh->next,写返回head就结果不对了&#xff01;但是后面又想明白了&#xff…...

Eureka注册中心(黑马学习笔记)

Eureka注册中心 假如我们的服务提供者user-service部署了多个实例&#xff0c;如图&#xff1a; 大家思考几个问题&#xff1a; order-service在发起远程调用的时候&#xff0c;该如何得知user-service实例的ip地址和端口&#xff1f; 有多个user-service实例地址&#xff0c…...

unity-firebase-Analytics分析库对接后数据不显示原因,及最终解决方法

自己记录一下unity对接了 FirebaseAnalytics.unitypackage&#xff08;基于 firebase_unity_sdk_10.3.0 版本&#xff09; 库后&#xff0c;数据不显示的原因及最终显示解决方法&#xff1a; 1. 代码问题&#xff08;有可能是代码写的问题&#xff0c;正确的代码如下&#xff…...

JWT(JSON Web Token)原理、应用与安全性分析

随着互联网的快速发展&#xff0c;Web应用的安全性越来越受到重视。在众多的安全认证技术中&#xff0c;JSON Web Token&#xff08;JWT&#xff09;凭借其简洁、自包含和传输安全的特点&#xff0c;被广泛应用于Web应用的用户身份验证和信息交换。 一、JWT的原理 JWT是一个开…...

Redis 缓存(Cache)

什么是缓存 缓存(cache)是计算机中的一个经典的概念在很多场景中都会涉及到。 核心思路就是把一些常用的数据放到触手可及(访问速度更快)的地方&#xff0c;方便随时读取。 这里所说的“触手可及”是个相对的概念 我们知道&#xff0c;对于硬件的访问速度来说&#xff0c;通常…...

ChatGPT回答模式

你发现了吗&#xff0c;ChatGPT的回答总是遵循这些类型方式。 目录 1.解释模式 2.类比模式 3.列举模式 4.限制模式 5.转换模式 6.增改模式 7.对比模式 8.翻译模式 9.模拟模式 10.推理模式 1.解释模式 ChatGPT 在回答问题或提供信息时&#xff0c;不仅仅给出…...

戏曲文化苑|戏曲文化苑小程序|基于微信小程序的戏曲文化苑系统设计与实现(源码+数据库+文档)

戏曲文化苑小程序目录 目录 基于微信小程序的戏曲文化苑系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、微信小程序前台 2、管理员后台 &#xff08;1&#xff09;戏曲管理 &#xff08;2&#xff09;公告信息管理 &#xff08;3&#xff09;公告类型管理…...

Mysql数据库主从集群从库Slave因为RelayLog过多过大引起服务器硬盘爆满生产事故实战解决

Mysql数据库主从集群从库slave因为RelayLog过多过大引起从库服务器硬盘爆满生产事故实战解决 一、MySQL数据库主从集群概念 MySQL数据库主从集群是一种高可用性和读写分离的数据库架构&#xff0c;它基于MySQL的复制&#xff08;Replication&#xff09;技术来同步数据。在主…...

QT基本组件

四、基本组件 Designer 设计师&#xff08;重点&#xff09; Qt包含了一个Designer程序&#xff0c;用于通过可视化界面设计开发界面&#xff0c;保存文件格式为.ui&#xff08;界面文件&#xff09;。界面文件内部使用xml语法的标签式语言。 在Qt Creator中创建文件时&#xf…...

uniapp实现全局悬浮框

uniapp实现全局悬浮框(按钮,页面,图片自行设置) 可拖动 话不多说直接上干货 1,在components新建组件(省去了每个页面都要引用组件的麻烦) 2,实现代码 <template><view class"call-plate" :style"top: top px;left: left px;" touchmove&quo…...

C语言特殊函数

静态函数 背景知识&#xff1a;普通函数都是跨文件可见的&#xff0c;即在文件 a.c 中定义的函数可以在 b.c 中使用。 静态函数&#xff1a;只能在定义的文件内可见的函数&#xff0c;称为静态函数。 语法 staitc void f(void) // 在函数头前面增加关键字 static &#xff…...

全栈开发(TS,React,Vue, Java, 移动端flutter)接单

个人主页 https://hz.minicv.net/ 技术栈 前端&#xff1a;NextJS React VueJS 后端&#xff1a;NestJS Java 移动端&#xff1a;Flutter 其他&#xff1a;SpringCloud Redis Kafka Zookeeper 项目案例 微行简历&#xff08; TS 全栈项目&#xff0c;一个极简的简历管理平…...

vue3使用百度地图

前情提要&#xff1a; 本文vue采用vue3框架&#xff0c;使用百度地图通过组件vue-baidu-map-3x&#xff1a; 组件官网&#xff1a;地图容器 | vue-baidu-map-3x 使用百度地图需要 申请百度地图AK秘钥 步骤&#xff1a;1.进入百度地图开放平台 | 百度地图API SDK | 地图开…...

莱州网站开发/app拉新推广

php中$_FIELS函数在php中上传一个文件建一个表单要比ASP中灵活的多。具体的看代码。然后upload.php中可以直接用$_FIELS,$_POST,$_GET等函数获取表单内容。当客户端提交后&#xff0c;我们获得了一个$_FILES数组$_FILES数组内容如下&#xff1a;$_FILES[‘myFile’][‘name’]:…...

推送网站建设/seo教程有什么

每个期刊或者会议都有自己的篇幅限制&#xff0c;比如近期准备试水的Signal Processing, Elsevier的期刊&#xff0c;要求单栏&#xff0c;双倍行距&#xff0c;所有文字&#xff0c;表格&#xff0c;图&#xff0c;参考文献加起来不超过30页&#xff0c;我觉得还好&#xff0c…...

网站后台如何上传附件/新网站应该怎么做seo

大家好&#xff0c;我是本期的实验室研究员——李卫涵。今天我将向大家介绍如何基于针对 Source Generator 来进行单元测试。接下来就让我们一起到实验室中一探究竟吧&#xff01; Source Generator 单元测试 Intro Source Generator 是 .NET 5.0 以后引入的一个在编译期间动态…...

淘宝优惠券发布网站怎么做/简阳seo排名优化课程

1.使用字符串作为条件查询 $user M(User); var_dump($user->where(id1 AND user"蜡笔小新")->select());//最终生成的 SQL 语句 SELECT * FROM think_user WHERE ( id1 AND user"蜡笔小新" ) 2.使用索引数组作为查询条件 $user M(User); $cond…...

广东智唯网站建设公司/营销新闻

一、 某某架构 1&#xff0e;从“层”上认识某某软件架构 软件业中Web最经典的架构必然是三层架构&#xff1a;表现层&#xff0c;业务层&#xff0c;数据层。那么让我们看看某某软件在三层架构上是如何实现的&#xff08;如图1&#xff09;&#xff1a; 层 项目 认识 表…...

qq企业邮箱注册申请/站长工具seo综合查询怎么用

这是Java EE框架开发技术一书中贯穿整本书的一个练习系统(作业)。代码一共有两个部分第一部分是第五章的作业&#xff0c;完成如下使用原生的jdbc代码进行数据库的连接界面和书本完全相同除部分js代码用jq代替外其他代码均相同课后扩展实现除头像上传外基本完成第二部分是最后一…...