一文解析node中的模块系统

一文解析node中的模块系统

两年前写过一篇文章介绍模块系统:理解前端模块概念:CommonJs与ES6Module。这篇文章的知识面都是针对刚入门的,比较浅显。在这也纠正文章的几个错误:

【模块】和【模块系统】 是两码事。模块是软件中的一个单元,而模块系统则是一套语法或者工具,模块系统能让开发者在项目中定义和使用模块。ECMAScript Module缩写是ESM,或者ESModule,而不是ES6Module。

关于模块系统的基础知识都在上一篇文章说的差不多了,所以这篇文章会重点关注模块系统的内部原理以及更加完整的介绍不同模块系统之间的区别,上一篇文章出现的内容在这就不再重复了。

模块系统

并不是所有编程语言都有内置的模块系统,JavaScript诞生之后的很长一段时间里都没有模块系统。

在浏览器环境中只能使用标签来引入不用的代码文件,这种方法共享一个全局作用域,可谓是问题多多;加上前端日新月异的发展,这种方法已经不满足当下的需求了。在没官方的模块系统出现前,前端社区自己创建第三方模块系统,用的较多的有:异步模块定义AMD、通用模块定义UMD等,当然最著名还得是CommonJS

由于Node.js它是一个JavaScript的运行环境,可以直接访问底层的文件系统。所以开发者通过它,并按照CommonJS规范实现了一套模块系统。

最开始,CommonJS只能用于Node.js平台,随着Browserify和Webpack之类的模块打包工具的出现,CommonJS也终于能在浏览器端运行了。

到2015年发布了ECMAScript6规范,才有了模块系统的正式标准,按照该标准打造出来的模块系统称为ECMAScript module简称【ESM】,由此ESM就开始统一了Node.js环境与浏览器环境。当然ECMAScript6只是提供了语法和语义,至于实现部分得由各浏览器服务厂商和Node开发者去努力。所以才有了令其他编程语言羡慕不已的babel神器,实现模块系统并不是一件容易的事,Node.js也是到了13.2版本才算是比较稳定的支持ESM。

但不管怎么样,ESM才是JavaScript的“亲儿子”,学习它一定不会有错!

模块系统的基本思路

在刀耕火种的年代中使用JavaScript开发应用,脚本文件只能通过script标签引入。其中遇到比较严重的问题就是缺乏命名空间机制,这意味着每个脚本都共享同一作用域。这个问题在社区中有一个比较好的解决方法:Revevaling module

const myModule = (() => {    const _privateFn = () => {}    const _privateAttr = 1    return {        publicFn: () => {},        publicAttr: 2    }})()console.log(myModule)console.log(myModule.publicFn, myModule._privateFn)

运行结果如下:

1.png

这个模式很简单,利用IIFE创建一个私有的作用域,同时使用return需要暴露的变量。而属于内部的变量(比如_privateFn、_privateAttr)是不能从外面的作用域访问的。

【revealing module】正是利用了这些特性,来隐藏私有的信息,同时把应该公布给外界的API导出。后面的模块系统也正是基于这样的思路开发的。

CommonJS

基于上面思路,来开发一个模块加载器。

首先编写一个加载模块内容的函数,并把这个函数包裹在私有作用域里面,然后通过eval()求值,以运行该函数:

function loadModule (filename, module, require) {  const wrappedSrc =     `(function (module, exports, require) {      ${fs.readFileSync(filename, 'utf8)}    }(module, module.exports, require)`  eval(wrappedSrc)}

和【revealing module】一样,把模块的源代码包裹在函数里面,区别在于,还把一系列变量(module, module.exports, require)传给该函数。

值得注意的是,通过【readFileSync】读取模块内容。一般来说,在调用涉及文件系统的API时,不应该使用同步版本。但此时不同,因为通过CommonJs系统来加载模块,本身就应该实现成同步操作,以确保多个模块能够按照正确的依赖顺序得到引入。

接着模拟require()函数,主要功能是加载模块。

function require(moduleName) {  const id = require.resolve(moduleName)  if (require.cache[id]) {    return require.cache[id].exports  }  // 模块的元数据  const module = {    exports: {},    id  }  // 更新缓存  require.cache[id] = module    // 载入模块  loadModule(id, module, require)    // 返回导出的变量  return module.exports}require.cache = {}require.resolve = (moduleName) => {  // 根据moduleName解析出完整的模块id}

(1)函数接收到moduleName后,首先解析出模块的完整路径,赋值给id。
(2)如果cache[id]为true,说明该模块已经被加载过了,直接返回缓存结果
(3)否则,就配置一套环境,用于首次加载。具体来说,创建module对象,包含exports(也就是导出内容),id(作用如上)
(4)将首次加载的module缓存起来
(5)通过loadModule从模块的源文件中读取源代码
(6)最后return module.exports返回想要导出的内容。

require是同步的

在模拟require函数的时候,有一个很重要的细节:require函数必须是同步的。它的作用仅仅是直接将模块内容返回而已,并没有用到回调机制。Node.js中的require也是如此。所以针对module.exports的赋值操作,也必须是同步的,如果用异步就会出问题:

// 出问题setTimeout(() => {    module.exports = function () {}}, 1000)

require是同步函数这一点对定义模块的方式有着非常重要的影响,因为它迫使我们在定义模块时只能使用同步的代码,以至于Node.js都为此,提供了大多数异步API的同步版本。

早期的Node.js有异步版本的require函数,但很快就移除了,因为这会让函数的功能变得十分复杂。

ESM

ESM是ECMAScript2015规范的一部分,该规范给JavaScript语言指定了一套官方的模块系统,以适应各种执行环境。

在Node.js中使用ESM

Node.js默认会把.js后缀的文件,都当成是采用CommonJS语法所写的。如果直接在.js文件中采用ESM语法,解释器会报错。

有三种方法可以在让Node.js解释器转为ESM语法:
1、把文件后缀名改为.mjs;
2、给最近的package.json文件添加type字段,值为“module”;
3、字符串作为参数传入--eval,或通过STDIN管道传输到node,带有标志--input-type=module
比如:

node --input-type=module --eval "import { sep } from 'node:path'; console.log(sep);"

不同类型模块引用

ESM可以被解析并缓存为URL(这也意味着特殊字符必须是百分比编码)。支持file:node:data:等的URL协议

file:URL
如果用于解析模块的import说明符具有不同的查询或片段,则会多次加载模块

// 被认为是两个不同的模块import './foo.mjs?query=1';import './foo.mjs?query=2';

data:URL
支持使用MIME类型导入:

text/javascript用于ES模块application/json用于JSONapplication/wasm用于Wasm

import 'data:text/javascript,console.log("hello!");';import _ from 'data:application/json,"world!"' assert { type: 'json' };

data:URL仅解析内置模块的裸说明符和绝对说明符。解析相对说明符不起作用,因为data:不是特殊协议,没有相对解析的概念。

导入断言
这个属性为模块导入语句添加了内联语法,以便在模块说明符旁边传入更多信息。

import fooData from './foo.json' assert { type: 'json' };const { default: barData } = await import('./bar.json', { assert: { type: 'json' } });

目前只支持JSON模块,而且assert { type: 'json' }语法是具有强制性的。

导入Wash模块
--experimental-wasm-modules标志下支持导入WebAssembly模块,允许将任何.wasm文件作为普通模块导入,同时也支持它们的模块导入。

// index.mjsimport * as M from './module.wasm';console.log(M)

使用如下命令执行:

node --experimental-wasm-modules index.mjs

顶层await

await关键字可以用在ESM中的顶层。

// a.mjsexport const five = await Promise.resolve(5)// b.mjsimport { five } from './a.mjs'console.log(five) // 5

异步引用

前面说过,import语句对模块依赖的解决是静态的,因此有两项著名的限制:

模块标识符不能等到运行的时候再去构造;模块引入语句,必须写在文件的顶端,而且不能套在控制流语句里;

然而,对于某些情况来说,这两项限制无疑是过于严格。就比如说有一个还算是比较常见的需求:延迟加载

在遇到一个体积很大的模块时,只想在真正需要用到模块里的某个功能时,再去加载这个庞大的模块。

为此,ESM提供了异步引入机制。这种引入操作,可以在程序运行的时候,通过import()运算符实现。从语法上看,相当于一个函数,接收模块标识符作为参数,并返回一个Promise,待Promise resolve后就能得到解析后的模块对象。

ESM的加载过程

用一个循环依赖的例子来说明ESM的加载过程:

// index.jsimport * as foo from './foo.js';import * as bar from './bar.js';console.log(foo);console.log(bar);// foo.jsimport * as Bar from './bar.js'export let loaded = false;export const bar = Bar;loaded = true;// bar.jsimport * as Foo from './foo.js';export let loaded = false;export const foo = Foo;loaded = true

先看看运行结果:

2.png

东盟商机最新AC版As2007  SP1 东盟商机最新AC版As2007 SP1

AS系统本次的主要更新和新开发的功能如下(暂不详述): 1、修复了普及版的一些大大小小的BUG 2、重新规划整个后台,使后台更加个性化、智能化、更加易用 3、重写了广告部分模块,使其更加专业化 4、重写了文章采集模块,添加了定时自动采集功能 5、添加了供求信息采集功能 6、重写了友情连接功能(原来的太简单了) 8、重写了生成HTML模块。(几个主要模块首页不用原来的生成方式,不再会被卡巴斯机杀毒软

东盟商机最新AC版As2007  SP1 0 查看详情 东盟商机最新AC版As2007  SP1

通过loaded可以观察到,foo和bar这两个模块都能log出加载完整的模块信息。而CommonJS却不一样,一定会有一个模块无法打印出完整加载后的样子。

我们深入加载过程,看看为什么会出现这样的结果。
加载过程可以分为三个阶段:

第一个阶段:解析第二个阶段:声明第三个阶段:执行

解析阶段:
解释器从入口文件出发(也就是index.js),解析模块之间的依赖关系,以图的形式展示出来,这张图也被称为依赖关系图。

在这个阶段只关注与import语句,并把这些语句想要引入的模块所对应的源码,给加载进来。并以深度解析的方式得到最后的依赖关系图。以上面例子说明:
1、从index.js开始,发现import * as foo from './foo.js'语句,从而去到foo.js文件中。
2、从foo.js文件继续解析,发现import * as Bar from './bar.js'语句,从而去到bar.js中。
3、从bar.js继续解析,发现import * as Foo from './foo.js'语句,形式循环依赖,但由于解释器已经在处理foo.js模块了,所以不会再进入其中,然后继续解析bar模块。
4、解析完bar模块后,发现没有import语句了,所以返回foo.js,并继续往下解析。一路都没有再次发现import语句,返回index.js。
5、在index.js中发现import * as bar from './bar.js',但由于bar.js已经解析过了,所以略过,继续往下执行。

最后通过深度优先的方式把依赖图完整的展示出来:

3.png

声明阶段:
解释器从得到的依赖图出发,从底到上的顺序对每个模块进行声明。具体来说,每到达一个模块,就寻找该模块所要导出的全部属性,并在内存中声明导出值的标识符。请注意,该阶段只作声明,不会进行赋值操作。
1、解释器从bar.js模块出发,声明loaded和foo的标识符。
2、向上回溯,到了foo.js模块,声明loaded和bar标识符。
3、到了index.js模块,但这个模块没有导出语句,所以没有声明任何标识符。

4.png

声明完所有导出标识符后,再走一遍依赖图,把import引入和export导出的关系连接起来。

5.png

可以看到,由import引进来的模块与export所导出值之间,建立了一种类似于const的绑定关系,引入方这一端是只能读而不能写。而且在index.js读取的bar模块,与在foo.js读取的bar模块实质是同一个实例。

所以这就是为什么在这个例子的结果中都能输出完整的解析结果的原因。

这跟CommonJS系统所用的方法有根本的区别。如果有某个模块要引入CommonJS模块,那么系统会对后者的整个exports对象做拷贝,从而将其中的内容复制到当前模块里面,这样的话,如果受引入的那个模块修改了自身的那一份变量,那么用户这边是看不到新值的。

执行阶段:
在这个阶段中,引擎才会去执行模块的代码。依然采用从底向上的顺序访问依赖图,并逐个执行访问到的文件。从bar.js文件开始执行,到foo.js,最后才是index.js。在这个过程中,逐步完善export表中标识符的值。

这套流程与CommonJS看似没有太大区别,但实际有着重大差异。由于CommonJS是动态的,因此它一边解析依赖图,一边执行相关的文件。所以只要看到一条require语句,就可以肯定的说,当程序来到这条语句时,已经把前面的代码都执行完了。因此,require语句不一定非要出现在文件的开头,而是可以出现在任意地方,而且,模块标识符也可以通过变量来构造。

但ESM不同,在ESM里,上述这三个阶段是彼此分离的,它必须先把依赖图完整地构造出来,然后才能执行代码,因此,引入模块与导出模块的操作,都必须是静态的,而不能等到执行代码的时候再去做。

ESM与CommonJS区别

除了前面提到的几个区别之外,还有一些区别是值得注意的:

强制的文件扩展名

在ESM中使用import关键字解析相对或绝对的说明符时,必须提供文件扩展名,还必须完全指定目录索引(‘./path/index.js’)。而CommonJS的require函数则允许省略这个扩展名。

严格模式

ESM是默认运行于严格模式之下,而且该严格模式是不能禁用。所以不能使用未声明的变量,也不能使用那些仅仅在非严格模式下才能使用的特性(例如with)。

ESM不支持CommonJS提供的某些引用

CommonJS中提供了一些全局变量,这些变量不能在ESM下使用,如果试图使用这些变量会导致ReferenceError错误。包括

requireexportsmodule.exports__filename__dirname

其中__filename指的是当前这个模块文件的绝对路径,__dirname则是该文件所在文件夹的绝对路径。这连个变量在构建当前文件的相对路径时很有帮助,所以ESM提供了一些方法去实现两个变量的功能。

在ESM中,可以使用import.meta对象来获取一个引用,这个引用指的是当前文件的URL。具体来说,就是通过import.meta.url来获取当前模块的文件路径,这个路径的格式类似file:///path/to/current_module.js。根据这条路径,构造出__filename__dirname所表达的绝对路径:

import { fileURLToPath } from 'url'import { dirname } from 'path'const __filename = fileURLToPath(import.meta.url)const __dirname = dirname(__filename)

而且还能模拟CommonJS中require()函数

import { createRequire } from 'module'const require = createRequire(import.meta.url)

this指向

在ESM的全局作用域中,this是未定义(undefined),但是在CommonJS模块系统中,它是一个指向exports的引用:

// ESMconsole.log(this) // undefined// CommonJSconsole.log(this === exports) // true

ESM加载CommonJS

上面提到过在ESM中可以模拟CommonJS的require()函数,以此来加载CommonJS的模块。除此之外,还可以使用标准的import语法引入CommonJS模块,不过这种引入方式只能把默认导出的东西给引进来:

import packageMain from 'commonjs-package' // 完全可以import { method } from 'commonjs-package' // 出错

而CommonJS模块的require总是将它引用的文件视为CommonJS。不支持使用require加载ES模块,因为ES模块具有异步执行。但可以使用import()从CommonJS模块中加载ES模块。

导出双重模块

虽然ESM已经推出了7年,node.js也已经稳定支持了,我们开发组件库的时候可以只支持ESM。但为了兼容旧项目,对CommonJS的支持也是必不可少的。有两种广泛使用的方法可以使得组件库同时支持两个模块系统的导出。

使用ES模块封装器

在CommonJS中编写包或将ES模块源代码转换为CommonJS,并创建定义命名导出的ES模块封装文件。使用条件导出,import使用ES模块封装器,require使用CommonJS入口点。举个例子,example模块中

// package.json{    "type": "module",    "exports": {        "import": "./wrapper.mjs",        "require": "./index.cjs"    }}

使用显示扩展名.cjs.mjs,因为只用.js的话,要么是被默认为CommonJS,要么"type": "module"会导致这些文件都被视为ES模块。

// ./index.cjsexport.name = 'name';// ./wrapper.mjsimport cjsModule from './index.cjs'export const name = cjsModule.name;

在这个例子中:

// 使用ESM引入import { name } from 'example'// 使用CommonJS引入const { name } = require('example')

这两种方式引入的name都是相同的单例。

隔离状态

package.json文件可以直接定义单独的CommonJS和ES模块入口点:

// package.json{    "type": "module",    "exports": {        "import": "./index.mjs",        "require": "./index.cjs"    }}

如果包的CommonJS和ESM版本是等效的,则可以做到这一点,例如因为一个是另一个的转译输出;并且包的状态管理被仔细隔离(或包是无状态的)

状态是一个问题的原因是因为包的CommonJS和ESM版本都可能在应用程序中使用;例如,用户的引用程序代码可以importESM版本,而依赖项require CommonJS版本。如果发生这种情况,包的两个副本将被加载到内存中,因此将出现两个不同的状态。这可能会导致难以解决的错误。

除了编写无状态包(例如,如果JavaScript的Math是一个包,它将是无状态的,因为它的所有方法都是静态的),还有一些方法可以隔离状态,以便在可能加载的CommonJS和ESM之间共享它包的实例:

如果可能,在实例化对象中包含所有状态。比如JavaScript的Date,需要实例化包含状态;如果是包,会这样使用:

import Date from 'date';const someDate = new Date();// someDate 包含状态;Date 不包含

new关键字不是必需的;包的函数可以返回新的对象,或修改传入的对象,以保持包外部的状态。

在包的CommonJS和ESM版本之间共享的一个或过个CommonJS文件中隔离状态。比如CommonJS和ESM入口点分别是index.cjs和index.mjs:

// index.cjsconst state = require('./state.cjs')module.exports.state = state;// index.mjsimport state from './state.cjs'export {    state}

即使example在应用程序中通过require和import使用example的每个引用都包含相同的状态;并且任一模块系统修改状态将适用二者皆是。

最后

如果本文对你有帮助,就点个赞支持下吧,你的「赞」是我持续进行创作的动力。

本文引用以下资料:

node.js官方文档Node.js Design Patterns

更多node相关知识,请访问:nodejs 教程!

以上就是一文解析node中的模块系统的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/835234.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
《vivo浏览器》恢复默认设置方法
上一篇 2025年11月27日 10:06:36
发现美丽:如何选择和使用电脑纯色壁纸提升你的工作空间
下一篇 2025年11月27日 10:06:42

相关推荐

  • 如何监控Linux进程内存泄漏 pmap与valgrind工具使用

    如何监控Linux进程内存泄漏 pmap与valgrind工具使用如何监控Linux进程内存泄漏 pmap与valgrind工具使用如何监控Linux进程内存泄漏 pmap与valgrind工具使用如何监控Linux进程内存泄漏 pmap与valgrind工具使用

    要监控linux进程的内存泄漏,首先使用pmap观察内存增长趋势,再用valgrind定位具体泄漏点。一、使用pmap -x 查看进程内存映射,重点关注anon列和总内存变化,通过定期刷新判断是否存在异常增长;二、利用valgrind –leak-check=full启动程序,分析报告中…

    2026年9月24日 用户投稿
    100
  • 华为Mate系列摄像头如何设置以优化动态摄影?动态拍摄调整指南

    华为Mate系列摄像头如何设置以优化动态摄影?动态拍摄调整指南华为Mate系列摄像头如何设置以优化动态摄影?动态拍摄调整指南华为Mate系列摄像头如何设置以优化动态摄影?动态拍摄调整指南华为Mate系列摄像头如何设置以优化动态摄影?动态拍摄调整指南

    答案是掌握专业模式下的快门速度、ISO和对焦设置,并结合AI辅助与防抖技术。具体而言,拍摄动态场景时应优先选择高速快门(如1/500秒以上)以凝固瞬间,配合AF-C连续对焦与追焦技巧确保主体清晰;在光线不足时适当提升ISO,但需权衡噪点与模糊的取舍;创造运动模糊效果则需降低快门速度(如1/30秒),…

    2026年9月24日 用户投稿
    400
  • mysql中是什么意思 mysql语法符号含义解析

    mysql 中的符号和关键字是与数据库交互的基本工具,正确使用它们可以提高工作效率和查询准确性。1. 逗号(,)用于分隔列表中的元素,如列名和值。2. 点号(.)用于访问表中的列或调用函数。3. 星号(*)用于选择所有列,但应避免使用以提高查询性能。4. 百分号(%)用于 like 操作中的模式匹配…

    2026年9月24日
    000
  • Spring Boot 测试中 403 错误排查与安全配置优化

    本文旨在解决 Spring Boot 控制器层测试中常见的 403 Forbidden 错误,特别是当安全配置限制了访问权限时。文章将深入分析 WebSecurityConfig 和 @WithMockUser 的使用,提供两种主要解决方案:通过临时放松安全限制进行测试,以及确保角色/权限配置的正确…

    2026年9月24日
    100
  • MAC怎么把App的语言单独设置成中文或英文_MAC单独设置App语言方法

    可通过终端命令临时设置或修改应用Info.plist文件永久更改macOS单个应用语言,支持中英文切换,不影响系统语言。 如果您希望在 macOS 系统中将某个应用程序的语言单独设置为中文或英文,而不影响系统整体语言,可以通过修改应用的本地化偏好来实现。此方法适用于支持多语言且遵循 macOS 本地…

    2026年9月24日
    000
  • 显卡降噪散热测试:七款RTX 4080非公版显卡谁更安静?

    选择RTX 4080显卡时,在性能相近的情况下,散热与噪音成为关键考量。1. 散热模组决定温度与风扇转速,进而影响噪音水平;2. 三风扇设计、大面积均热板及多热管(如6mm×8根)能有效提升散热效率;3. 七彩虹水神(Neptune)等一体水冷型号静音表现顶尖,高负载下亦可近乎无声;4. 映众冰龙、…

    2026年9月24日
    000
  • DeepCode— 港大实验室推出的多Agent代码生成平台

    DeepCode— 港大实验室推出的多Agent代码生成平台DeepCode— 港大实验室推出的多Agent代码生成平台DeepCode— 港大实验室推出的多Agent代码生成平台DeepCode— 港大实验室推出的多Agent代码生成平台

    ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ MiniMax Agent MiniMax平台推出的Agent智能体助手 334 查看详情 DeepCode是什么 deepcode是由香港大学数据智能实验室研发的一款基于多智能体架构的智能代码…

    2026年9月24日 用户投稿
    100
  • 时区错误怎样校准?时间同步完整解决方法

    时区错误怎样校准?时间同步完整解决方法时区错误怎样校准?时间同步完整解决方法时区错误怎样校准?时间同步完整解决方法时区错误怎样校准?时间同步完整解决方法

    时区错误和时间同步问题通常由系统时区设置错误、硬件时钟漂移或ntp服务异常导致。1.确保系统时间通过ntp服务准确同步,linux可使用timedatectl检查ntp状态并启用systemd-timesyncd或chronyd,windows则开启自动时间同步;2.正确设置本地时区,linux使用…

    2026年9月24日 用户投稿
    100
  • VSCode如何实现代码模式识别 VSCodeAI辅助重构的智能技巧

    ai辅助重构在vscode中依赖lsp解析代码结构并结合ai模型识别模式,1. 首先通过语言服务器协议(lsp)构建抽象语法树,获取变量、函数、作用域等语义信息;2. 然后利用大型语言模型(如github copilot)基于上下文和训练数据预测重构建议;3. 用户可通过右键菜单或快捷键(ctrl+…

    2026年9月24日
    800
  • FramePackLoop— AI视频生成工具,首尾连接生成循环视频

    FramePackLoop— AI视频生成工具,首尾连接生成循环视频FramePackLoop— AI视频生成工具,首尾连接生成循环视频FramePackLoop— AI视频生成工具,首尾连接生成循环视频FramePackLoop— AI视频生成工具,首尾连接生成循环视频

    ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ Q.AI视频生成工具 支持一分钟生成专业级短视频,多种生成方式,AI视频脚本,在线云编辑,画面自由替换,热门配音媲美真人音色,更多强大功能尽在QAI 73 查看详情 FramePackLoop是…

    2026年9月24日 用户投稿
    100
  • Flyway多数据库与多环境配置:实现测试与生产环境的灵活迁移管理

    本文深入探讨了Flyway在多数据库和多环境场景下的灵活配置策略,旨在解决开发、开发、测试与生产环境数据库迁移的挑战。文章首先分析了测试环境数据库选择的推荐方案,包括使用与生产一致的数据库服务或Testcontainers。随后,详细阐述了Flyway如何通过分离配置文件、编程化配置以及利用占位符来…

    2026年9月24日
    000
  • laravel怎么使用Str和Arr辅助类的常用方法_laravel Str/Arr辅助类常用方法教程

    Laravel的Str和Arr类提供字符串与数组处理方法,如Str::lower、Str::contains、Arr::get、Arr::pluck等,提升代码可读性与开发效率。 Laravel 提供了两个非常实用的辅助类 Str 和 Arr,用于处理字符串和数组。它们封装了许多常用操作,让代码更简…

    2026年9月24日
    000
  • VS Code微服务开发:Docker与Kubernetes集成

    VS Code通过Docker扩展实现本地容器化开发,支持自动生成Dockerfile、一键构建镜像及devcontainer环境一致性;2. Kubernetes扩展可连接集群并管理资源,结合Bridge to Kubernetes实现本地调试与集群网络集成;3. 使用Skaffold自动化构建部…

    2026年9月24日
    000
  • 使用正则表达式从JSON数组中提取JSON对象

    本文旨在提供一种使用Java正则表达式从包含多个JSON对象的JSON数组中提取单个JSON对象的方法。我们将详细介绍如何构建合适的正则表达式,并提供示例代码演示如何在Java中使用该表达式来实现JSON对象的提取,并对提取后的字符串进行优化处理,移除不必要的空白字符。 从JSON数组中提取JSON…

    2026年9月24日
    000
  • AI PC 新晋狠角色:5000 元价位 Arrow Lake 最优解 惠普战 66 2025 争当全能卷王

    AI PC 新晋狠角色:5000 元价位 Arrow Lake 最优解 惠普战 66 2025 争当全能卷王AI PC 新晋狠角色:5000 元价位 Arrow Lake 最优解 惠普战 66 2025 争当全能卷王AI PC 新晋狠角色:5000 元价位 Arrow Lake 最优解 惠普战 66 2025 争当全能卷王AI PC 新晋狠角色:5000 元价位 Arrow Lake 最优解 惠普战 66 2025 争当全能卷王

    在 5000 元级别的主流商务本市场,长久以来似乎都遵循着一套 ” 潜规则 “:追求性能就得牺牲便携,看重耐用又往往在外观和屏幕上妥协,想要全面的接口以及优质的售后服务,预算就得一加再加。但现在,一个 ” 新晋狠角色 ” 决意打破这一局面。 惠普商用产…

    2026年9月24日 用户投稿
    000
  • 智能平权下,燃油车如何升级?

    智能平权下,燃油车如何升级?智能平权下,燃油车如何升级?智能平权下,燃油车如何升级?智能平权下,燃油车如何升级?

    曾几何时,“智能驾驶是电动车的专属”成为汽车行业的共识。宝马、奔驰、奥迪等传统豪华品牌长期专注于机械精密性和驾驶质感,在智能化布局上尤为谨慎,一度被贴上保守与落后的标签。 与此同时,新能源品牌凭借智能化迅速打开市场缺口,成功构建起“电动即智能、燃油即传统”的认知框架,在舆论和市场销量中占据先机。 ☞…

    2026年9月24日 用户投稿
    000
  • mac怎么使用听写功能_mac听写输入开启方法

    首先启用高级听写功能,进入系统设置→键盘→听写,勾选“使用高级听写”并下载语言包;随后可设置快捷键(如双击Fn键)快速启动语音输入;在支持的应用中也可通过菜单栏“编辑→开始听写”直接调用;最后根据需要配置听写语言、自动纠正及连续听写选项以提升识别准确率。 如果您希望在Mac上通过语音输入文字以提高效…

    2026年9月24日
    200
  • 使用MySQL命令行客户端进行交互式管理

    使用MySQL命令行客户端进行交互式管理使用MySQL命令行客户端进行交互式管理使用MySQL命令行客户端进行交互式管理使用MySQL命令行客户端进行交互式管理

    mysql命令行客户端的常用命令包括:1. 使用mysql -u 用户名 -p命令连接数据库;2. 执行show databases;查看所有数据库;3. 使用use 数据库名;选择数据库;4. 使用select * from 表名;查询数据;5. 使用insert into 表名 (列1, 列2)…

    2026年9月24日 用户投稿
    500
  • 赋能AI未来!康盈半导体 AI 应用存储新品登陆 elexcon 2025 展会

    赋能AI未来!康盈半导体 AI 应用存储新品登陆 elexcon 2025 展会赋能AI未来!康盈半导体 AI 应用存储新品登陆 elexcon 2025 展会赋能AI未来!康盈半导体 AI 应用存储新品登陆 elexcon 2025 展会赋能AI未来!康盈半导体 AI 应用存储新品登陆 elexcon 2025 展会

    8 月 26 日,中国电子、嵌入式及半导体先进封测行业的风向标 ——elexcon2025 深圳国际电子展暨嵌入式展盛大开幕。作为本届展会的重磅环节之一,国产存储领军品牌康盈半导体携新而来,以 “小而不凡,速启 ai 未来” 为核心主题,正式发布 2025 年存储新品,同步拉开面向 ai 终端应用的…

    2026年9月24日 用户投稿
    000
  • Java中固定长度用户ID输入验证:解决int类型长度检查问题

    本文详细介绍了在Java程序中如何实现用户输入固定长度ID的验证机制。针对常见的int cannot be dereferenced错误,我们将探讨将ID作为字符串读取并进行长度及格式校验的最佳实践,并提供处理字母数字型和纯数字型ID的示例代码,确保数据输入的准确性和程序的健壮性。 引言:用户输入验…

    2026年9月24日
    500

发表回复

登录后才能评论
关注微信