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

[Vite]vite-plugin-react和vite-plugin-react-swc插件原理了解

[Vite]vite-plugin-react和vite-plugin-react-swc插件原理了解

共同的作用

  1. JSX 支持:插件为 React 应用程序中的 JSX 语法提供支持,确保它可以被正确地转换为 JavaScript。
  2. Fast Refresh:提供热更新功能,当应用程序在开发服务器上运行时,可以快速地看到更改的效果,而不需要手动刷新页面。
  3. 装饰器支持:如果项目中使用了 TypeScript 装饰器,插件会正确处理它们。
  4. Source Maps:生成源代码映射,以便于在浏览器中调试源代码。
  5. Virtual DOM 的导入:确保 React 和 ReactDOM 的虚拟 DOM 导入被正确处理。
  6. 开发模式与生产模式的区分:在开发模式下,插件会提供更多的辅助功能,如 React 快速刷新;而在生产模式下,它会专注于代码的最小化和优化。
  7. ESLint 集成:可选地集成 ESLint,以在开发过程中提供代码质量和一致性的检查。
  8. 配置 Rollup:在构建过程中,插件可能会配置 Rollup 以适应 React 应用程序的特定需求。
  9. 兼容性:确保 React 应用程序可以在目标浏览器中运行,即使这些浏览器不支持最新的 JavaScript 特性。
  10. 自定义配置:允许用户通过 Vite 配置文件传递自定义选项给插件,例如指定 JSX 工厂函数或模式等。

vite-plugin-react

源码地址

https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/src/index.ts

逻辑解读

  1. 类型导入:代码开始部分导入了 TypeScript 类型,用于确保插件的类型正确性。
  2. 依赖懒加载loadBabel 函数用于按需加载 Babel,因为 Babel 只在开发模式下或当特定的插件被使用时才需要。
  3. 配置选项Options 接口定义了插件的配置选项,允许用户自定义包括 includeexcludejsxImportSourcejsxRuntime 等。
  4. 插件数组导出viteReact 函数返回一个插件数组,这些插件在 Vite 构建过程中执行不同的任务。
  5. 创建 Babel 插件配置viteBabel 插件对象配置了如何使用 Babel 来转换 React 代码,包括 JSX 支持和快速刷新。
  6. React 快速刷新viteReactRefresh 插件对象处理与 React 快速刷新相关的逻辑,包括在 index.html 中注入必要的脚本。
  7. 构建配置:在 config 函数中,插件可以修改 Vite 配置,例如设置 esbuild 选项或 optimizeDeps
  8. 配置解析configResolved 函数在 Vite 配置解析完成后调用,用于确定是否处于生产模式、项目根路径等。
  9. 转换函数transform 函数是 Vite 插件中的核心,用于实际的代码转换工作。它使用 Babel 来转换 JSX 语法,并在开发模式下添加 React 快速刷新的包装器。
  10. 快速刷新逻辑:根据配置和代码内容,插件决定是否需要对代码进行快速刷新包装。
  11. Babel 插件加载loadPlugin 函数用于加载 Babel 插件,确保插件的异步加载。
  12. Babel 选项创建createBabelOptions 函数用于创建 Babel 的选项对象,它可以从用户配置或默认值中初始化。
  13. 编译器检测hasCompilerhasCompilerWithDefaultRuntime 函数用于检测 Babel 插件列表中是否包含特定的编译器插件。
  14. 构建时警告处理silenceUseClientWarning 函数用于抑制 Rollup 的某些警告。
  15. 静态资源服务resolveIdload 函数用于处理静态资源的请求,例如 React 快速刷新运行时脚本。
  16. 转换 HTMLtransformIndexHtml 函数用于修改 index.html,注入快速刷新的脚本。
  17. 插件 APIViteReactPluginApi 类型定义了插件可以提供的 API,允许其他插件通过 reactBabel 钩子来修改 Babel 配置。
  18. 默认导出:最后,viteReact 函数作为默认导出,使其可以在 Vite 配置中使用。

总结来说,@vitejs/plugin-react 插件的主要功能是为 React 应用程序提供 Vite 构建和开发服务器的集成支持。它包括 JSX 语法的转换、React 组件的快速刷新(热更新)、以及对 React 运行时的配置。通过插件的配置选项,用户可以根据项目需求定制化插件的行为。

// eslint-disable-next-line import/no-duplicates
import type * as babelCore from '@babel/core'
// eslint-disable-next-line import/no-duplicates
import type { ParserOptions, TransformOptions } from '@babel/core'
import { createFilter } from 'vite'
import type {BuildOptions,Plugin,PluginOption,ResolvedConfig,UserConfig,
} from 'vite'
import {addClassComponentRefreshWrapper,addRefreshWrapper,preambleCode,runtimeCode,runtimePublicPath,
} from './fast-refresh'// lazy load babel since it's not used during build if plugins are not used
let babel: typeof babelCore | undefined
async function loadBabel() {if (!babel) {babel = await import('@babel/core')}return babel
}export interface Options {include?: string | RegExp | Array<string | RegExp>exclude?: string | RegExp | Array<string | RegExp>/*** Control where the JSX factory is imported from.* https://esbuild.github.io/api/#jsx-import-source* @default 'react'*/jsxImportSource?: string/*** Note: Skipping React import with classic runtime is not supported from v4* @default "automatic"*/jsxRuntime?: 'classic' | 'automatic'/*** Babel configuration applied in both dev and prod.*/babel?:| BabelOptions| ((id: string, options: { ssr?: boolean }) => BabelOptions)
}export type BabelOptions = Omit<TransformOptions,| 'ast'| 'filename'| 'root'| 'sourceFileName'| 'sourceMaps'| 'inputSourceMap'
>/*** The object type used by the `options` passed to plugins with* an `api.reactBabel` method.*/
export interface ReactBabelOptions extends BabelOptions {plugins: Extract<BabelOptions['plugins'], any[]>presets: Extract<BabelOptions['presets'], any[]>overrides: Extract<BabelOptions['overrides'], any[]>parserOpts: ParserOptions & {plugins: Extract<ParserOptions['plugins'], any[]>}
}type ReactBabelHook = (babelConfig: ReactBabelOptions,context: ReactBabelHookContext,config: ResolvedConfig,
) => voidtype ReactBabelHookContext = { ssr: boolean; id: string }export type ViteReactPluginApi = {/*** Manipulate the Babel options of `@vitejs/plugin-react`*/reactBabel?: ReactBabelHook
}const reactCompRE = /extends\s+(?:React\.)?(?:Pure)?Component/
const refreshContentRE = /\$Refresh(?:Reg|Sig)\$\(/
const defaultIncludeRE = /\.[tj]sx?$/
const tsRE = /\.tsx?$/export default function viteReact(opts: Options = {}): PluginOption[] {// Provide default values for Rollup compat.let devBase = '/'const filter = createFilter(opts.include ?? defaultIncludeRE, opts.exclude)const jsxImportSource = opts.jsxImportSource ?? 'react'const jsxImportRuntime = `${jsxImportSource}/jsx-runtime`const jsxImportDevRuntime = `${jsxImportSource}/jsx-dev-runtime`let isProduction = truelet projectRoot = process.cwd()let skipFastRefresh = falselet runPluginOverrides:| ((options: ReactBabelOptions, context: ReactBabelHookContext) => void)| undefinedlet staticBabelOptions: ReactBabelOptions | undefined// Support patterns like:// - import * as React from 'react';// - import React from 'react';// - import React, {useEffect} from 'react';const importReactRE = /\bimport\s+(?:\*\s+as\s+)?React\b/const viteBabel: Plugin = {name: 'vite:react-babel',enforce: 'pre',config() {if (opts.jsxRuntime === 'classic') {return {esbuild: {jsx: 'transform',},}} else {return {esbuild: {jsx: 'automatic',jsxImportSource: opts.jsxImportSource,},optimizeDeps: { esbuildOptions: { jsx: 'automatic' } },}}},configResolved(config) {devBase = config.baseprojectRoot = config.rootisProduction = config.isProductionskipFastRefresh =isProduction ||config.command === 'build' ||config.server.hmr === falseif ('jsxPure' in opts) {config.logger.warnOnce('[@vitejs/plugin-react] jsxPure was removed. You can configure esbuild.jsxSideEffects directly.',)}const hooks: ReactBabelHook[] = config.plugins.map((plugin) => plugin.api?.reactBabel).filter(defined)if (hooks.length > 0) {runPluginOverrides = (babelOptions, context) => {hooks.forEach((hook) => hook(babelOptions, context, config))}} else if (typeof opts.babel !== 'function') {// Because hooks and the callback option can mutate the Babel options// we only create static option in this case and re-create them// each time otherwisestaticBabelOptions = createBabelOptions(opts.babel)}},async transform(code, id, options) {if (id.includes('/node_modules/')) returnconst [filepath] = id.split('?')if (!filter(filepath)) returnconst ssr = options?.ssr === trueconst babelOptions = (() => {if (staticBabelOptions) return staticBabelOptionsconst newBabelOptions = createBabelOptions(typeof opts.babel === 'function'? opts.babel(id, { ssr }): opts.babel,)runPluginOverrides?.(newBabelOptions, { id, ssr })return newBabelOptions})()const plugins = [...babelOptions.plugins]const isJSX = filepath.endsWith('x')const useFastRefresh =!skipFastRefresh &&!ssr &&(isJSX ||(opts.jsxRuntime === 'classic'? importReactRE.test(code): code.includes(jsxImportDevRuntime) ||code.includes(jsxImportRuntime)))if (useFastRefresh) {plugins.push([await loadPlugin('react-refresh/babel'),{ skipEnvCheck: true },])}if (opts.jsxRuntime === 'classic' && isJSX) {if (!isProduction) {// These development plugins are only needed for the classic runtime.plugins.push(await loadPlugin('@babel/plugin-transform-react-jsx-self'),await loadPlugin('@babel/plugin-transform-react-jsx-source'),)}}// Avoid parsing if no special transformation is neededif (!plugins.length &&!babelOptions.presets.length &&!babelOptions.configFile &&!babelOptions.babelrc) {return}const parserPlugins = [...babelOptions.parserOpts.plugins]if (!filepath.endsWith('.ts')) {parserPlugins.push('jsx')}if (tsRE.test(filepath)) {parserPlugins.push('typescript')}const babel = await loadBabel()const result = await babel.transformAsync(code, {...babelOptions,root: projectRoot,filename: id,sourceFileName: filepath,// Required for esbuild.jsxDev to provide correct line numbers// This crates issues the react compiler because the re-order is too important// People should use @babel/plugin-transform-react-jsx-development to get back good line numbersretainLines: hasCompiler(plugins)? false: !isProduction && isJSX && opts.jsxRuntime !== 'classic',parserOpts: {...babelOptions.parserOpts,sourceType: 'module',allowAwaitOutsideFunction: true,plugins: parserPlugins,},generatorOpts: {...babelOptions.generatorOpts,decoratorsBeforeExport: true,},plugins,sourceMaps: true,})if (result) {let code = result.code!if (useFastRefresh) {if (refreshContentRE.test(code)) {code = addRefreshWrapper(code, id)} else if (reactCompRE.test(code)) {code = addClassComponentRefreshWrapper(code, id)}}return { code, map: result.map }}},}// We can't add `react-dom` because the dependency is `react-dom/client`// for React 18 while it's `react-dom` for React 17. We'd need to detect// what React version the user has installed.const dependencies = ['react', jsxImportDevRuntime, jsxImportRuntime]const staticBabelPlugins =typeof opts.babel === 'object' ? opts.babel?.plugins ?? [] : []if (hasCompilerWithDefaultRuntime(staticBabelPlugins)) {dependencies.push('react/compiler-runtime')}const viteReactRefresh: Plugin = {name: 'vite:react-refresh',enforce: 'pre',config: (userConfig) => ({build: silenceUseClientWarning(userConfig),optimizeDeps: {include: dependencies,},resolve: {dedupe: ['react', 'react-dom'],},}),resolveId(id) {if (id === runtimePublicPath) {return id}},load(id) {if (id === runtimePublicPath) {return runtimeCode}},transformIndexHtml() {if (!skipFastRefresh)return [{tag: 'script',attrs: { type: 'module' },children: preambleCode.replace(`__BASE__`, devBase),},]},}return [viteBabel, viteReactRefresh]
}viteReact.preambleCode = preambleCodeconst silenceUseClientWarning = (userConfig: UserConfig): BuildOptions => ({rollupOptions: {onwarn(warning, defaultHandler) {if (warning.code === 'MODULE_LEVEL_DIRECTIVE' &&warning.message.includes('use client')) {return}if (userConfig.build?.rollupOptions?.onwarn) {userConfig.build.rollupOptions.onwarn(warning, defaultHandler)} else {defaultHandler(warning)}},},
})const loadedPlugin = new Map<string, any>()
function loadPlugin(path: string): any {const cached = loadedPlugin.get(path)if (cached) return cachedconst promise = import(path).then((module) => {const value = module.default || moduleloadedPlugin.set(path, value)return value})loadedPlugin.set(path, promise)return promise
}function createBabelOptions(rawOptions?: BabelOptions) {const babelOptions = {babelrc: false,configFile: false,...rawOptions,} as ReactBabelOptionsbabelOptions.plugins ||= []babelOptions.presets ||= []babelOptions.overrides ||= []babelOptions.parserOpts ||= {} as anybabelOptions.parserOpts.plugins ||= []return babelOptions
}function defined<T>(value: T | undefined): value is T {return value !== undefined
}function hasCompiler(plugins: ReactBabelOptions['plugins']) {return plugins.some((p) =>p === 'babel-plugin-react-compiler' ||(Array.isArray(p) && p[0] === 'babel-plugin-react-compiler'),)
}// https://gist.github.com/poteto/37c076bf112a07ba39d0e5f0645fec43
function hasCompilerWithDefaultRuntime(plugins: ReactBabelOptions['plugins']) {return plugins.some((p) =>p === 'babel-plugin-react-compiler' ||(Array.isArray(p) &&p[0] === 'babel-plugin-react-compiler' &&p[1]?.runtimeModule === undefined),)
}

vite-plugin-react-swc

官方地址:vite-plugin-react-swc

核心:它使用SWC来替代Babel进行打包,速度快了很多。

源码地址

https://github.com/vitejs/vite-plugin-react-swc/blob/main/src/index.ts

逻辑解读

  1. 导入依赖:代码开始部分导入了 Node.js 的内置模块和第三方库,如 fspathurl 等,以及 @swc/core 和 Vite 的类型定义。
  2. 定义插件选项Options 类型定义了插件的配置选项,包括 jsxImportSourcetsDecoratorspluginsdevTargetparserConfig
  3. react 函数:这是一个工厂函数,用于创建 Vite 插件数组。它接受用户配置并返回配置好的插件对象。
  4. 处理 HMR:代码检查了服务器是否启用了热模块替换(HMR),如果没有启用,则设置 hmrDisabled 标志。
  5. 创建插件对象
    • 对象 vite:react-swc:resolve-runtime 用于解析 React 刷新运行时的路径,并提供相应的代码。
    • 对象 vite:react-swc 包含了多个属性和方法,用于处理开发服务器上的特定行为,如 configconfigResolvedtransformIndexHtmltransform
  6. React 快速刷新:在 transform 方法中,插件会检查代码是否包含 React 组件或刷新相关的代码。如果是,它将修改代码以支持 React 快速刷新。
  7. Source Map 处理:对于支持快速刷新的代码,插件会修改 Source Map,以确保源代码映射正确。
  8. 构建时的配置:当插件应用于构建时,它会配置 SWC 以使用特定的目标和插件选项进行代码转换。
  9. transformWithOptions 函数:这是一个异步函数,用于执行实际的代码转换。它接受文件 ID、代码、目标、选项和 React 配置,然后调用 SWC 的 transform 方法。
  10. silenceUseClientWarning 函数:这个函数用于抑制 Rollup 的警告,特别是与 "use client" 相关的警告。
  11. 导出默认:最后,react 函数作为默认导出,使其可以在 Vite 配置中使用。
import { readFileSync } from "fs";
import { dirname, join } from "path";
import { fileURLToPath } from "url";
import { SourceMapPayload } from "module";
import {Output,ParserConfig,ReactConfig,JscTarget,transform,
} from "@swc/core";
import { PluginOption, UserConfig, BuildOptions } from "vite";
import { createRequire } from "module";const runtimePublicPath = "/@react-refresh";const preambleCode = `import { injectIntoGlobalHook } from "__PATH__";
injectIntoGlobalHook(window);
window.$RefreshReg$ = () => {};
window.$RefreshSig$ = () => (type) => type;`;const _dirname =typeof __dirname !== "undefined"? __dirname: dirname(fileURLToPath(import.meta.url));
const resolve = createRequire(typeof __filename !== "undefined" ? __filename : import.meta.url,
).resolve;
const reactCompRE = /extends\s+(?:React\.)?(?:Pure)?Component/;
const refreshContentRE = /\$Refresh(?:Reg|Sig)\$\(/;type Options = {/*** Control where the JSX factory is imported from.* @default "react"*/jsxImportSource?: string;/*** Enable TypeScript decorators. Requires experimentalDecorators in tsconfig.* @default false*/tsDecorators?: boolean;/*** Use SWC plugins. Enable SWC at build time.* @default undefined*/plugins?: [string, Record<string, any>][];/*** Set the target for SWC in dev. This can avoid to down-transpile private class method for example.* For production target, see https://vitejs.dev/config/build-options.html#build-target* @default "es2020"*/devTarget?: JscTarget;/*** Override the default include list (.ts, .tsx, .mts, .jsx, .mdx).* This requires to redefine the config for any file you want to be included.* If you want to trigger fast refresh on compiled JS, use `jsx: true`.* Exclusion of node_modules should be handled by the function if needed.*/parserConfig?: (id: string) => ParserConfig | undefined;
};const isWebContainer = globalThis.process?.versions?.webcontainer;const react = (_options?: Options): PluginOption[] => {let hmrDisabled = false;const options = {jsxImportSource: _options?.jsxImportSource ?? "react",tsDecorators: _options?.tsDecorators,plugins: _options?.plugins? _options?.plugins.map((el): typeof el => [resolve(el[0]), el[1]]): undefined,devTarget: _options?.devTarget ?? "es2020",parserConfig: _options?.parserConfig,};return [{name: "vite:react-swc:resolve-runtime",apply: "serve",enforce: "pre", // Run before Vite default resolve to avoid syscallsresolveId: (id) => (id === runtimePublicPath ? id : undefined),load: (id) =>id === runtimePublicPath? readFileSync(join(_dirname, "refresh-runtime.js"), "utf-8"): undefined,},{name: "vite:react-swc",apply: "serve",config: () => ({esbuild: false,optimizeDeps: {include: [`${options.jsxImportSource}/jsx-dev-runtime`],esbuildOptions: { jsx: "automatic" },},}),configResolved(config) {if (config.server.hmr === false) hmrDisabled = true;const mdxIndex = config.plugins.findIndex((p) => p.name === "@mdx-js/rollup",);if (mdxIndex !== -1 &&mdxIndex >config.plugins.findIndex((p) => p.name === "vite:react-swc")) {throw new Error("[vite:react-swc] The MDX plugin should be placed before this plugin",);}if (isWebContainer) {config.logger.warn("[vite:react-swc] SWC is currently not supported in WebContainers. You can use the default React plugin instead.",);}},transformIndexHtml: (_, config) => [{tag: "script",attrs: { type: "module" },children: preambleCode.replace("__PATH__",config.server!.config.base + runtimePublicPath.slice(1),),},],async transform(code, _id, transformOptions) {const id = _id.split("?")[0];const refresh = !transformOptions?.ssr && !hmrDisabled;const result = await transformWithOptions(id,code,options.devTarget,options,{refresh,development: true,runtime: "automatic",importSource: options.jsxImportSource,},);if (!result) return;if (!refresh) return result;const hasRefresh = refreshContentRE.test(result.code);if (!hasRefresh && !reactCompRE.test(result.code)) return result;const sourceMap: SourceMapPayload = JSON.parse(result.map!);sourceMap.mappings = ";;" + sourceMap.mappings;result.code = `import * as RefreshRuntime from "${runtimePublicPath}";${result.code}`;if (hasRefresh) {sourceMap.mappings = ";;;;;;" + sourceMap.mappings;result.code = `if (!window.$RefreshReg$) throw new Error("React refresh preamble was not loaded. Something is wrong.");
const prevRefreshReg = window.$RefreshReg$;
const prevRefreshSig = window.$RefreshSig$;
window.$RefreshReg$ = RefreshRuntime.getRefreshReg("${id}");
window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;${result.code}window.$RefreshReg$ = prevRefreshReg;
window.$RefreshSig$ = prevRefreshSig;
`;}result.code += `
RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => {RefreshRuntime.registerExportsForReactRefresh("${id}", currentExports);import.meta.hot.accept((nextExports) => {if (!nextExports) return;const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate("${id}", currentExports, nextExports);if (invalidateMessage) import.meta.hot.invalidate(invalidateMessage);});
});
`;return { code: result.code, map: sourceMap };},},options.plugins? {name: "vite:react-swc",apply: "build",enforce: "pre", // Run before esbuildconfig: (userConfig) => ({build: silenceUseClientWarning(userConfig),}),transform: (code, _id) =>transformWithOptions(_id.split("?")[0], code, "esnext", options, {runtime: "automatic",importSource: options.jsxImportSource,}),}: {name: "vite:react-swc",apply: "build",config: (userConfig) => ({build: silenceUseClientWarning(userConfig),esbuild: {jsx: "automatic",jsxImportSource: options.jsxImportSource,tsconfigRaw: {compilerOptions: { useDefineForClassFields: true },},},}),},];
};const transformWithOptions = async (id: string,code: string,target: JscTarget,options: Options,reactConfig: ReactConfig,
) => {const decorators = options?.tsDecorators ?? false;const parser: ParserConfig | undefined = options.parserConfig? options.parserConfig(id): id.endsWith(".tsx")? { syntax: "typescript", tsx: true, decorators }: id.endsWith(".ts") || id.endsWith(".mts")? { syntax: "typescript", tsx: false, decorators }: id.endsWith(".jsx")? { syntax: "ecmascript", jsx: true }: id.endsWith(".mdx")? // JSX is required to trigger fast refresh transformations, even if MDX already transforms it{ syntax: "ecmascript", jsx: true }: undefined;if (!parser) return;let result: Output;try {result = await transform(code, {filename: id,swcrc: false,configFile: false,sourceMaps: true,jsc: {target,parser,experimental: { plugins: options.plugins },transform: {useDefineForClassFields: true,react: reactConfig,},},});} catch (e: any) {const message: string = e.message;const fileStartIndex = message.indexOf("╭─[");if (fileStartIndex !== -1) {const match = message.slice(fileStartIndex).match(/:(\d+):(\d+)]/);if (match) {e.line = match[1];e.column = match[2];}}throw e;}return result;
};const silenceUseClientWarning = (userConfig: UserConfig): BuildOptions => ({rollupOptions: {onwarn(warning, defaultHandler) {if (warning.code === "MODULE_LEVEL_DIRECTIVE" &&warning.message.includes("use client")) {return;}if (userConfig.build?.rollupOptions?.onwarn) {userConfig.build.rollupOptions.onwarn(warning, defaultHandler);} else {defaultHandler(warning);}},},
});export default react;

相关文章:

[Vite]vite-plugin-react和vite-plugin-react-swc插件原理了解

[Vite]vite-plugin-react和vite-plugin-react-swc插件原理了解 共同的作用 JSX 支持&#xff1a;插件为 React 应用程序中的 JSX 语法提供支持&#xff0c;确保它可以被正确地转换为 JavaScript。Fast Refresh&#xff1a;提供热更新功能&#xff0c;当应用程序在开发服务器上…...

记一次使用“try-with-resources“的语法导致的BUG

背景描述 最近使用try-catch的时候遇到了一个问题&#xff0c;背景是这样的&#xff1a;当第一次与数据库建立连接以后执行查询完毕并没有手动关闭连接&#xff0c;但是当我第二次获取连接的时候报错了&#xff0c;显示数据库连接失败&#xff0c;连接已经关闭。 org.postgres…...

用Excel处理数据图像,出现交叉怎么办?

一、问题描述 用excel制作X-Y散点图&#xff0c;意外的出现了4个交叉点&#xff0c;而实际上的图表数据是没有交叉的。 二、模拟图表 模拟部分数据&#xff0c;并创建X-Y散点图&#xff0c;数据区域&#xff0c;X轴数据是依次增加的&#xff0c;因此散点图应该是没有交叉的。…...

SpringBoot | 大新闻项目后端(redis优化登录)

该项目的前篇内容的使用jwt令牌实现登录认证&#xff0c;使用Md5加密实现注册&#xff0c;在上一篇&#xff1a;http://t.csdnimg.cn/vn3rB 该篇主要内容&#xff1a;redis优化登录和ThreadLocal提供线程局部变量&#xff0c;以及该大新闻项目的主要代码。 redis优化登录 其实…...

ESP32——物联网小项目汇总

商品级ESP32智能手表 [文章链接] 用ESP32&#xff0c;做了个siri&#xff1f;&#xff01;开源了&#xff01; [文章链接]...

flutter:监听路由的变化

问题 当从路由B页面返回路由A页面后&#xff0c;A页面需要进行数据刷新。因此需要监听路由变化 解决 使用RouteObserver进行录音监听 创建全局变量&#xff0c;不在任何类中 final RouteObserver<PageRoute> routeObserver RouteObserver<PageRoute>();在mai…...

Linux多进程和多线程(六)进程间通信-共享内存

多进程(六) 共享内存共享内存的创建 示例: 共享内存删除 共享内存映射 共享内存映射的创建解除共享内存映射示例:写入和读取共享内存中的数据 写入: ### 读取: 大致操作流程: 多进程(六) 共享内存 共享内存是将分配的物理空间直接映射到进程的⽤户虚拟地址空间中, 减少数据在…...

ruoyi后台修改

一、日志文件过大分包 \ruoyi-admin\src\main\resources\logback.xml <!-- 系统日志输出 --> <appender name"file_info" class"ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}/sys-info.log</file><!…...

macOS查看系统日志的方法

1、command空格键打开搜索框&#xff0c;输入‘控制台’并打开 2、选择日志报告&#xff0c;根据日期打开自己需要的文件就可以...

数字信号处理及MATLAB仿真(3)——采样与量化

今天写主要来编的程序就是咱们AD变换的两个步骤。一个是采样&#xff0c;还有一个是量化。大家可以先看看&#xff0c;这一过程当中的信号是如何变化的。信号的变换图如下。 先说说采样&#xff0c;采样是将连续时间信号转换为离散时间信号的过程。在采样过程中&#xff0c;连续…...

云端AI大模型群体智慧后台架构思考

1 大模型的调研 1.1 主流的大模型 openai-chatgpt 阿里巴巴-通义千问 一个专门响应人类指令的大模型。我是效率助手&#xff0c;也是点子生成机&#xff0c;我服务于人类&#xff0c;致力于让生活更美好。 百度-文心一言&#xff08;千帆大模型&#xff09; 文心一言"…...

算法系列--分治排序|再谈快速排序|快速排序的优化|快速选择算法

前言:本文就前期学习快速排序算法的一些疑惑点进行详细解答,并且给出基础快速排序算法的优化版本 一.再谈快速排序 快速排序算法的核心是分治思想,分治策略分为以下三步: 分解:将原问题分解为若干相似,规模较小的子问题解决:如果子问题规模较小,直接解决;否则递归解决子问题合…...

强化学习编程实战-1-一个及其简单的强化学习实例(多臂赌博机)

1.1 多臂赌博机 一台拥有K个臂的机器&#xff0c;玩家每次可以摇动K个臂中的一个&#xff0c;摇动后&#xff0c;会吐出数量不等的金币&#xff0c;吐出金币的数量服从一定的概率分布&#xff0c;而且不同臂的概率分布不同。 多臂赌博机的问题是&#xff1a;假设玩家共有N次摇地…...

Golang语法规范和风格指南(一)——简单指南

1. 前引 一个语言的规范的学习是重要的&#xff0c;直接关系到你的代码是否易于维护和理解&#xff0c;同时学习好对应的语言规范可以在前期学习阶段有效规避该语言语法和未知编程风格的冲突。 这里是 Google 提供的规范&#xff0c;有助于大家在开始学习阶段对 Golang 进行一…...

数据机构记录顺序表-笔记1

一、线性表的基本概念 数据元素&#xff1a;线性表中的基本单位&#xff0c;每个元素都是线性表的一部分。 数据项&#xff1a;数据元素的具体值。 存储位置&#xff1a;线性表中的元素在内存中的具体存储位置。 线性表按存储结构可以分为顺序表和链表两大类&#xff1a; 1.1…...

考研必备~总结严蔚敏教授《数据结构》课程的重要知识点及考点

作者主页&#xff1a;知孤云出岫 目录 1. 基本概念1.1 数据结构的定义1.2 抽象数据类型 (ADT) 2. 线性表2.1 顺序表2.2 链表 3. 栈和队列3.1 栈3.2 队列 4. 树和二叉树4.1 树的基本概念4.2 二叉树 5. 图5.1 图的基本概念5.2 图的遍历 6. 查找和排序6.1 查找6.2 排序 7. 重点考…...

【数据分享】国家级旅游休闲街区数据(Excel/Shp格式/免费获取)

之前我们分享过从我国文化和旅游部官网整理的2018-2023年我国50个重点旅游城市星级饭店季度经营状况数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff01;文化和旅游部官网上也分享有很多与旅游相关的常用数据&#xff0c;我们基于官网发布的名单文件整理得到全国…...

Linux开发:进程间通过Unix Domain Socket传递数据

进程间传递数据的方式有很多种,Linux还提供一种特殊的Socket用于在多进程间传递数据,就是Unix Domain Socket(UDS)。 虽然通过普通的Socket也能做到在多进程间传递数据,不过这样需要通过协议栈层的打包与拆包,未免有些浪费效率,通过UDS,数据仅仅通过一个特殊的sock文件…...

Redis基础教程(九):redis有序集合

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…...

Servlet与Servlet容器

什么是Servlet? Servlet是Java EE&#xff08;现称Jakarta EE&#xff09;中的一个组件&#xff0c;通常用于创建动态Web内容。Servlet是运行在Web服务器上的Java程序&#xff0c;它处理客户端的请求并生成响应。Servlet的核心功能是处理HTTP请求和响应。下面是一个servlet例…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...