基于Rush.js的Monorepo实战

news2024/12/28 4:38:51

基于Rush.js的Monorepo入门实战

概述

Monorepo是一种软件开发模式,它将多个项目或组件存储在同一个代码库中,而不是将它们分散到多个库中。这样做可以方便跨项目的代码重用、版本控制、依赖管理等,被广泛应用于大型软件公司的开发流程中。

Rush.js 是一个用于管理多个包的工具,可以协调和优化它们之间的依赖关系。它提供了命令,例如初始化存储库、添加/删除项目、构建存储库、运行测试等。Rush.js 还支持增量构建、Git 工作流和预安装包等高级功能。它可以提高项目的开发效率和可维护性。

不同于一股脑说各种功能,我会以最简单的demo入手,然后逐步提出新的需求并解决,当然首先你要安装rush。这非常简单,从你的 shell 或命令行窗口输入这个命令:

npm install -g @microsoft/rush

项目模板:rush_monorepo_demo
觉得有用的话,记得点个 start 哦

新建

初始化一个仓库

使用 rush init初始化仓库,自动生成 .gitattributes .github .gitignore common rush.json等几个文件夹。

// 创建工程文件夹
❯ mkdir rush_demo
❯ cd rush_demo/
// 执行初始化命令
❯ rush init

Rush Multi-Project Build Tool 5.93.1 (unmanaged) - https://rushjs.io
Node.js version is 16.19.1 (LTS)

Starting "rush init"
Generating: /home/mfine/my_project/rush_demo/.github/workflows/ci.yml
Generating: /home/mfine/my_project/rush_demo/common/config/rush/.pnpmfile.cjs
Generating: /home/mfine/my_project/rush_demo/common/config/rush/.npmrc
Generating: /home/mfine/my_project/rush_demo/common/config/rush/.npmrc-publish
Generating: /home/mfine/my_project/rush_demo/common/config/rush/artifactory.json
Generating: /home/mfine/my_project/rush_demo/common/config/rush/build-cache.json
Generating: /home/mfine/my_project/rush_demo/common/config/rush/command-line.json
Generating: /home/mfine/my_project/rush_demo/common/config/rush/common-versions.json
Generating: /home/mfine/my_project/rush_demo/common/config/rush/experiments.json
Generating: /home/mfine/my_project/rush_demo/common/config/rush/pnpm-config.json
Generating: /home/mfine/my_project/rush_demo/common/config/rush/rush-plugins.json
Generating: /home/mfine/my_project/rush_demo/common/config/rush/version-policies.json
Generating: /home/mfine/my_project/rush_demo/common/git-hooks/commit-msg.sample
Generating: /home/mfine/my_project/rush_demo/.gitattributes
Generating: /home/mfine/my_project/rush_demo/.gitignore
Generating: /home/mfine/my_project/rush_demo/rush.json
~/my_project/rush_demo
❯ ls -a
.  ..  .gitattributes  .github  .gitignore  common  rush.json

添加一个工具包项目

初始化项目

对于新建项目最开始要做的就是添加项目,我建议每次只添加或验证单个项目。不然小心一小加太多调不通,不好debug。好了让我添加一个叫my-utils的项目,显然它是一些常用的工具方法。

在根目录下创建工具类项目的包
> mkdir tools
> cd tools
> mkdir my-utils
初始化仓库
> pnpm init

然后如下所示,可以看到最主要就是在根目录下的rush.json文件中的projects字段中指明包名和包所在的文件地址。


{
 // .......省略若干
  "projects": [
    {
      "packageName": "my-utils",
      "projectFolder": "tools/my-utils"
    }
  ]
}

配置package.json和rollup.config.js

下面我们开始先配置my-utils,使用的是rollup+ts+sass的方式,配置如下。

记住当package.json 文件发生变化时,请务必运行 rush update。也就是说当你添加了一下新的项目的时候或者从使用拉取新的更新之后,此时package.json 发生了变化就需要执行 rush update

此外一般情况下安装第三包的时候,我们也不使用 pnpm install等命令了。而会换成

rush add -p example-lib

例如(记得切换到对应的工程目录下执行):

rush_demo/app on  dev [!]cd my-app-vue2

rush_demo/app/my-app-vue2 on  dev [!] via  v16.19.1 
❯ ls
README.md  babel.config.js  dist  jsconfig.json  my-app-vue2.build.error.log  my-app-vue2.build.log  node_modules  package.json  pnpm-lock.yaml  public  src  vue.config.js

rush_demo/app/my-app-vue2 on  dev [!] via  v16.19.1 
❯ rush add -p lodash

package.json

{
  "name": "my-utils",
  "version": "1.0.0",
  "description": "",
  "main": "./dist/index.js",
  "module": "./dist/index.esm.js",
  "umd": "./dist/index.umd.js",
  "types": "./dist/types/index.d.ts",
  "scripts": {
    "clean:dist": "rimraf dist",
    "build:types": "npm run clean:dist && tsc -b ./tsconfig.types.json",
    "dev": "npm run build:types && rollup --bundleConfigAsCjs -c ",
    "test": "node test/test.js",
    "pretest": "npm run build"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/plugin-transform-runtime": "^7.21.0",
    "@babel/preset-env": "^7.20.2",
    "@rollup/plugin-babel": "^6.0.3",
    "@rollup/plugin-commonjs": "^24.0.1",
    "@rollup/plugin-node-resolve": "^15.0.1",
    "rimraf": "^4.3.0",
    "rollup": "^3.18.0",
    "rollup-plugin-terser": "^7.0.2",
    "rollup-plugin-typescript2": "^0.34.1",
    "typescript": "^4.9.5"
  },
  "dependencies": {
    "@babel/core": "~7.21.0"
  }
}

roullup.config.js

import path from 'path' 
import resolve from '@rollup/plugin-node-resolve' 
import commonjs from '@rollup/plugin-commonjs' 
import rollupTypescript from 'rollup-plugin-typescript2' 
import babel from '@rollup/plugin-babel' 
import { DEFAULT_EXTENSIONS } from '@babel/core' 
import { terser } from 'rollup-plugin-terser' // 读取 package.json 配置 
import pkg from './package.json' assert { type: "json" }// 当前运行环境,可通过 cross-env 命令行设置
const env = process.env.NODE_ENV // umd 模式的编译结果文件输出的全局变量名称 
const name = 'RollupTsTemplate' 
const config = { 
    // 入口文件,src/index.ts 
    input: path.resolve(__dirname, 'src/index.ts'), 
    // 输出文件 
    output: [ 
        // commonjs 
        { 
            // package.json 配置的 main 属性 
            file: pkg.main, 
            format: 'cjs', 
        }, 
        // es module 
        { 
            // package.json 配置的 module 属性 
            file: pkg.module, 
            format: 'es', 
        }, 
        // umd 
        { 
            // umd 导出文件的全局变量 
            name, 
            // package.json 配置的 umd 属性 
            file: pkg.umd, 
            format: 'umd' 
        } 
    ], 
    plugins: [ 
        // 解析第三方依赖 
        resolve(), 
        // 识别 commonjs 模式第三方依赖 
        commonjs(),
        // rollup 编译 typescript 
        rollupTypescript(), 
        // babel 配置 
        babel({ 
            // 编译库使用 
            babelHelpers: 'runtime', 
            // 只转换源代码,不转换外部依赖 
            exclude: 'node_modules/**', 
            // babel 默认不支持 ts 需要手动添加 
            extensions: [ 
                ...DEFAULT_EXTENSIONS, 
                '.ts', 
            ], 
        }), 
    ] 
} 
// 若打包正式环境,压缩代码 
if (env === 'production') { 
    config.plugins.push(terser({ 
        compress: { 
            pure_getters: true, 
            unsafe: true, 
            unsafe_comps: true, 
            warnings: false 
        } 
    })) 
} 

export default config

添加代码

我们简单的添加几行代码:

tools/my-utils/src/math/index.ts

// add function
export function add(a: number, b: number) {
	return a + b
}

// minus function
export function minus(a: number, b: number) {
	return a - b
}

// multiply function
export function multiply(a: number, b: number) {
	return a * b
}

// divide function
export function divide(a: number, b: number) {
	return a / b
}

构建项目

对于build这里直接使用 rush build命令,但是我们在这里只build my-utils这个项目,因此我们要使用如下命令:

rush_demo/app/my-app-vue2 on  dev [!] via  v16.19.1 
// 开始build
❯ rush build --to my-utils
Found configuration in /home/mfine/my_project/rush_demo/rush.json


Rush Multi-Project Build Tool 5.93.1 - https://rushjs.io
Node.js version is 16.19.1 (LTS)

Found configuration in /home/mfine/my_project/rush_demo/rush.json

Starting "rush build"

Analyzing repo state... DONE (0.08 seconds)

Executing a maximum of 12 simultaneous processes...

==[ my-utils ]=====================================================[ 1 of 1 ]==

/home/mfine/my_project/rush_demo/tools/my-utils/src/index.ts → ./dist/index.js, ./dist/index.esm.js, ./dist/index.umd.js...
created ./dist/index.js, ./dist/index.esm.js, ./dist/index.umd.js in 1s
"my-utils" completed with warnings in 3.73 seconds.



==[ SUCCESS WITH WARNINGS: 1 operation ]=======================================

--[ WARNING: my-utils ]--------------------------------------[ 3.73 seconds ]--


/home/mfine/my_project/rush_demo/tools/my-utils/src/index.ts → ./dist/index.js, ./dist/index.esm.js, ./dist/index.umd.js...
created ./dist/index.js, ./dist/index.esm.js, ./dist/index.umd.js in 1s


Operations succeeded with warnings.

rush build (3.83 seconds)

--to my-utils参数的意思如下:

假设我们刚刚克隆了 monorepo 仓库,现在想在项目 B 中进行开发,则需要构建 BB 依赖的所有项目。

我们可以这样做:

# 构建项目 B 以及 B 依赖的所有项目
$ rush build --to B

上面的命令选择了 A, BE 三个项目:

rush build --to B

详细参考:rush build

构建结束之后我们就要在其他项目中引用这个包,因此我们要先创建的应用项目。

添加一个应用项目

这个就和平常创建一个vue2项目差不多,剩余的流程和上述添加一个工具包也差不多。

rush_demo on  dev [!]mkdir app

rush_demo on  dev [!]cd app

rush_demo/app on  dev [!] 
❯ vue create my-app-vue2

下面我们将在这个vue2项目中引用 my-utils包。

添加同仓库下的包

注意这里使用的 **“workspace:*”**方式其实是一个偷懒的方法,他有一下弊端,具体参考这个文章。但是这里为了演示方便就这样用。添加好了之后别忘了 rush update哦。

{
  "name": "my-app-vue2",
......
  "dependencies": {
    .....
    "my-utils":"workspace:*",
    ......
  },
.......
}

然后我们就可以在项目中使用我们的工具包了:

app/my-app-vue2/src/components/HelloWorld.vue

<template>
  <div class="hello">
    <TestBtn></TestBtn>
  </div>
</template>

<script>
import { TestBtn } from 'vue2-ui';
import { add, divide, minus, multiply } from 'my-utils';
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  components: {
    TestBtn
  },
  mounted() {
    console.log(add(1,1));
    console.log(divide(1,1)); 
    console.log(minus(1,1));
    console.log(multiply(1,1));
  }
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped></style>

运行查看效果

这里我们可以直接使用pnpm 自带的命令运行就行 npm run serve

自动构建

配置command-line

打开 common/config/rush/command-line.json,添加如下内容或者在原有的build下增加 注释后的内容。

  . . .
  "commands": [
    {
      "name": "build",
      "commandKind": "bulk",
      "summary": "Build projects and watch for changes",
      "description": "For details, see the article \"Using watch mode\" on the Rush website: https://rushjs.io/",

      // 使用增量构建(重要)
      "incremental": true,
      "enableParallelism": true,
      // 启用“监听模式”
      "watchForChanges": true
    },
  . . .

运行(直接引用)

执行 rush build --to-except my-app-vue2 ,构建 my-app-vue2 依赖的所有项目,但不包括项目 my-app-vue2(排除my-app-vue2是因为我们下面单独以dev的方式启动它)。效果如下:

在这里插入图片描述

下面我们修改 my-utils下的内容,看看会怎么样。

增加一个平方函数:

// square function
export function square(a: number) {
  return a * a
}

控制台响应如下:

在这里插入图片描述

启动 my-app-vue2,已经可以在项目中使用刚刚添加的函数了。

在这里插入图片描述

后台如下:
在这里插入图片描述

运行(间接引用)

我们再创建一个项目,该项目被 my-utils引用。然后更新这个项目看看效果。可以看到我们创建了一个叫 mat的项目来负责矩阵运算。如下我们在 my-utils项目中使用

import { Matrix } from 'mat';

//......
export function mat_add(mat1: number[][], mat2: number[][]):Matrix|void {
  return new Matrix(mat1).add(new Matrix(mat2));
}

然后我们在 my-app-vue2中使用。
在这里插入图片描述

结果:

在这里插入图片描述

现在我们改动一个 mat项目,增加如下内容:

在这里插入图片描述

控制台变化如下:

在这里插入图片描述

先检测到了 mat项目改动了,然后开始构建mat

在这里插入图片描述

然后发现 my-utils引用了 mat所以自动构建 my-utils。最后刷新 my-app-vue2就可以更新mat的改动。

在这里插入图片描述

总结

利用rushjs我们可以实现自动构建,不用根据依赖关系一个个手动完成构建过程,并且rushjs是增量构建的,会自动判断那些项目受影响从而自动构建。

发布包

手动多包发布

rush change

在 Rush monorepo 中,rush change 是发包流程的起点,其产物 <branchname>-<timestamp>.json(后文用 changefile.json 代替)会被 rush version 以及 rush publish 消费。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lHK0kxql-1682047472350)(assets/image-20230414162057989.png)]
changefile.json 生成流程如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-obMcJC54-1682047472350)(assets/1460000040989037.png)]

  1. 检测当前分支与目标分支(通常是 master)的差异,筛选出存在变更的项目(基于 git diff 命令);
  2. 针对筛选出来的每一个项目通过交互式命令行询问一些信息(如版本更新策略以及更新的内容简要描述);
  3. 基于上述信息在 common/changes 目录下生成对应 package 的 changefile.json。
    在这里插入图片描述

type: none 的特性使得我们可以将已开发完毕但不需要跟随下一次发布周期的 package 提前合入 master,直到该 pacakge 出现 type 不为 none 的 changefile.json。

rush version 与 rush publish

rush versionrush publish --apply 则会基于生成的 changefile.json 进行版本号的更新。同时注意这两个命令会消费changelfile.json文件。

级联发布

前面有提到在更新版本号时,除了更新当前需要被发布的 package 的版本号,也可能更新其上层 package 的版本号,这取决于上层 package 在 package.json 中如何引用当前 package 的。

如下所示,@modern-js/plugin-tailwindcss(上层 package) 通过 "workspace:^1.0.0" 的形式引入 @modern-js/utils(底层 package)。

package.json(@modern-js/plugin-tailwindcss)

{
  "name": "@modern-js/plugin-tailwindcss",
  "version": "1.0.0",
  "dependencies": {
    "@modern-js/utils": "workspace:^1.0.0"
  }
}

package.json(@modern-js/utils)

{
  "name": "@modern-js/utils",
  "version": "1.0.0"
}
  • @modern-js/utils 更新至 1.0.1 ,Rush 在更新版本号时不会更新 @modern-js/plugin-tailwindcss 的版本号。因为 ^1.0.0 兼容 1.0.1,从语义的角度出发,@modern-js/plugin-tailwindcss 不需要更新版本号,直接安装 @modern-js/plugin-tailwindcss@1.0.0 是可以获取到 @modern-js/utils@1.0.1
  • @modern-js/utils 更新至 2.0.0 ,Rush 在更新版本号时会更新 @modern-js/plugin-tailwindcss 的版本号至 1.0.1。因为 ^1.0.0 不兼容 2.0.0,更新 @modern-js/plugin-tailwindcss 版本至 1.0.1 才可引用到最新的 @modern-js/utils@2.0.0,此时 @modern-js/plugin-tailwindcss 的 package.json 内容如下:
{
  "name": "@modern-js/plugin-tailwindcss",
  "version": "1.0.1",
  "dependencies": {
   // 引用版本号也发生了变化 
    "@modern-js/utils": "workspace:^2.0.0"
  }
}

更新了版本号,还需要发布至 npm。此时需要 rush publish 增加 --include-all 参数,配置该参数后 rush publish 检查到仓库中存在 shouldPublish: true 的 package 的版本新于 npm 版本时,会将该 package 发布。

发布到npm仓库

工作流如下:

  1. git add [files] (提交更改到暂存区)
  2. rush change
  3. rush publish --apply --publish --registry registryUrl

此处注意如果不注意消费了 changefile 或者项目的 package.json配置了 publishConfig 字段,那么后面配置的仓库地址就会失效。

同意注意–apply会消费 changefile文件

发布到私有仓库

和发布到npm仓库相同,只不过地址改为私有地址或者 package.json配置 publishConfig 字段

生成 change file
在这里插入图片描述
推送到私有仓库

在这里插入图片描述
在这里插入图片描述

规范化

commit前自动格式化代码

主要是通过 git hooks实现,同样的husky也可以实现相同效果。这里使用rush自带功能。

pre-commit 配置

你可以按照如下方式使用它。

  1. common/git-hooks 目录下添加该文件,并在 Git 上提交。
  2. 当开发者执行 rush install 时,Rush 将会拷贝该文件到 .git/hooks/commit-msg 目录下。
  3. 当你执行 git commit 时,Git 讲找到该脚本并调用它。
  4. 如果 commit 消息过短,脚本会返回非零状态码,Git 显示 Invalid commit message 提示并且拒绝操作。

使用 Rush 来安装这个钩子脚本需要避免使用 Husky 等独立解决方案。注意 Husky 预期你的仓库在根目录上有一个 package.jsonnode_modules 目录,并且 Husky 将会执行每个 Git 操作的 shell 命令(即使未使用的钩子);使用 Rush 来安装钩子可以避免这些限制。

**注意:**如果你需要卸载钩子,可以删除你的 .git/hooks/ 目录下的文件。

common/git-hooks/pre-commit
在这里插入图片描述

至于如何开启 prettier 功能 参考这个连接即可:启用 Prettier

配置rush prettier命令

common/config/rush/command-line.json

  . . .
  "commands": [
    {
      "name": "prettier",
      "commandKind": "global",
      "summary": "Used by the pre-commit Git hook. This command invokes Prettier to reformat staged changes.",
      "safeForSimultaneousRushProcesses": true,

      "autoinstallerName": "rush-prettier",

      // 它将会唤起 common/autoinstallers/rush-prettier/node_modules/.bin/pretty-quick
      "shellCommand": "pretty-quick --staged"
    }
    . . .

配置perttier配置文件

rush_monorepo_demo/.prettierignore


#-------------------------------------------------------------------------------------------------------------------
# 保持与 .gitignore 同步
#-------------------------------------------------------------------------------------------------------------------

👋 (此处将你的 .gitignore 文件内容复制粘贴过来) 👋

#-------------------------------------------------------------------------------------------------------------------
# Prettier 通用配置
#-------------------------------------------------------------------------------------------------------------------

# Rush 文件
common/changes/
common/scripts/
common/config/
CHANGELOG.*

# 包管理文件
pnpm-lock.yaml
yarn.lock
package-lock.json
shrinkwrap.json

# 构建产物
dist
lib

# 在 Markdown 中,Prettier 将会对代码块进行格式化,这会影响输出
*.md

rush_monorepo_demo/.prettierrc.js

// 配置可参考 https://prettier.io/en/configuration.html
module.exports = {
  // 使用较大的打印宽度,因为 Prettier 的换行设置似乎是针对没有注释的 JavaScript.
  printWidth: 110,

  // 使用 .gitattributes 来管理换行
  endOfLine: 'auto',

  // 单引号代替双引号
  singleQuote: true,

  // 对于 ES5 而言, 尾逗号不能用于函数参数,因此使用它们只能用于数组
  trailingComma: 'none'
};

规范提交

还是利用 git hooks实现

添加 pre-push hook
在这里插入图片描述

  • node common/scripts/install-run-rush.js change -v 的意思是检查有没有提供 change file文件( change file 由rush change命令产生)
  • node common/scripts/install-run-rush.js publish -a 的意思是检查通过之后直接模拟发布(会消费change file 以及提升版本号)。

总结

此章节主要是利用 githooks 进行一下自动化操作以达到规范代码和提交功能。但不建议做更深的自动化,比如自动publish ,发版操作应该实在项目进入测试阶段之后开始发版,一开始开发阶段完全没有发版的必要,大家依靠git 进行同步代码即可。

ps:添加或者修改hook后记得 rush update!!!

自定义指令

如果你的工具链有特殊的模式或功能,你可以将它们作为自定义指令或 Rush 工具的参数来暴露出来。或者你就是单纯的不想敲那么多字想利用简单的命令替代一个长命令,就可以用这个实现。

比如我每次启动项目都要 run build --to-eccept my-app-vue2我觉得太烦了,想直接 rush te你就可以这样配置。

common/config/rush/command-line.json

  "commands": [
        {
          "name": "te",
          "commandKind": "global",
          "summary": "build my-app-vue2 project's dependencies and do not build my-app-vue2",
          "safeForSimultaneousRushProcesses": true,
          "shellCommand": "rush build:watch --to-except my-app-vue2"
        },
    ]

或者你在每个项目的 package.json 定义了一个新的 script 命令,你想运行rush 命令直接自动调用每个项目中的命令,这样就不用去每个项目下手动调用,那你就可以这样配置。

  "commands": [
    {
      "commandKind": "bulk",
      "name": "build:watch",
      "summary": "used by build when file changes",
      "description": "watch file changes and build",
       // 是否并行化构建
       "enableParallelism": true,
       // 一下表示自动检测变化 进行增量构建 非必须
      "incremental": true,
      "watchForChanges": true
    }
  ],

同时你看可以自定义参数类型来进行个性化构建,比如生成不通语言版本,这里就不项目阐述了,具体参考这里:

自定义指令和参数

自动安装器(Autoinstallers)

它的作用如下:

  • 如果你有一些包需要在执行rush install之前运行,那么你就要可以使用 Autoinstallers 将包直接安装好,后续不需要 rush install 也可以直接使用。

  • 当你启动插件功能的时候,你也必须要配置Autoinstallers。

  • 如果你配置的自定义命令需要一个 npm包的依赖

使用方法:

  1. 创建一个文件 自动安装器的文件夹

    rush init-autoinstaller --name my-autoinstaller

  2. 编辑启动的 package.json文件添加依赖

  3. 更新 shrinkwrap file

    创建或更新 common/autoinstallers/my-autoinstaller/pnpm-lock.yaml*

    这个文件应该被提交并被git追踪

    rush update-autoinstaller --name my-autoinstaller

  4. 提交更新文件到git

    git add common/autoinstallers/my-autoinstaller/

    git commit -m “Updated autoinstaller”

具体详细使用案例可以参考 :启用 Prettier

详细文档参考:Autoinstallers

总结

总的来说 rushjs的核心功能就是增量思想,每次构建只构建改变的部分。此外rushjs 可以做到快速迭代,在npm包还没有稳定的情况下。直接把包放到仓库中有任何修改都可以及时更新到使用的项目中,节约之前反复publish和拉取的过程。

项目模板:rush_monorepo_demo
觉得有用的话,记得点个 start 哦

参考

从0到1搭建 Rollup + TypeScript 模板工程

rushjs官方文档

[基于 Rush 的 Monorepo 多包发布实践](https://segmentfault.com/a/1190000040988970)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/442828.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

动力节点Vue3笔记——Vue程序初体验

目录 一、Vue程序初体验 1.1 下载并安装vue.js 1.2 第一个Vue程序 1.3 Vue的data配置项 1.4 Vue的template配置项 一、Vue程序初体验 可以先不去了解Vue框架的发展历史、Vue框架有什么特点、Vue是谁开发的&#xff0c;对我们编写Vue程序起不到太大的作用&#xff0c;…

C语言从入门到精通第4天(1~3天的扩展)

1~3天的扩展 拓展数据类型sizeof关键字整型数据的打印格式字符类型的输出格式ASCII码表转义字符进制 拓展数据类型 在开发过程中我们需要处理各种类型的数据&#xff0c;C语言处理基本类型还有其他的类型&#xff1a; sizeof关键字 C语言中可以通过sizeof关键字获取某个数据…

Java源码(三)SpringBoot Web容器应用上下文

思维导图 本文主要分析ServletWebServerApplicationContext源码 SpringBoot Web容器应用上下文&#xff08;这是个人的翻译, 如有不足之处还望指出, 大佬勿喷!!!&#xff09; 1.自我思考及复盘 备注&#xff1a; 自我思考及复盘是为了养成带着问题阅读源码及阅读完源码后总结…

echarts 雷达图

Echarts 常用各类图表模板配置 注意&#xff1a; 这里主要就是基于各类图表&#xff0c;更多的使用 Echarts 的各类配置项&#xff1b; 以下代码都可以复制到 Echarts 官网&#xff0c;直接预览&#xff1b; 图标模板目录 Echarts 常用各类图表模板配置一、雷达图二、环形图三…

27 个Python数据科学库实战案例 (附代码)

为了大家能够对人工智能常用的 Python 库有一个初步的了解&#xff0c;以选择能够满足自己需求的库进行学习&#xff0c;对目前较为常见的人工智能库进行简要全面的介绍。 1、Numpy NumPy(Numerical Python)是 Python的一个扩展程序库&#xff0c;支持大量的维度数组与矩阵运算…

US News退榜风波后,发布最新美国最佳法学院和医学院排名

从2022年11月开始&#xff0c;美国权威排名机构US News不断陷入风波。耶鲁大学法学院率先宣布退出US News法学院排名&#xff0c;先是法学院&#xff0c;后是医学院&#xff0c;包括哈佛大学大学、斯坦福大学、哥伦比亚大学和加州大学伯克利分校等名校也纷纷宣布退出。 这些老…

【C语言】const关键字的作用

文章目录 一. const修饰变量二. const修饰指针三. const修饰函数参数 一. const修饰变量 被 const 修饰的变量具有常属性&#xff0c;这里的常属性指的是变量的值不能被修改 int main() {// const可以写在类型之前&#xff0c;也可以写在类型之后int const a 10;a 20;// er…

【操作系统——内存的基本知识,逻辑地址到物理地址的转换,操作系统对内存空间的分配和回收以及扩充和保护】

文章目录 内存的基本知识什么是内存&#xff1f;内存有什么作用&#xff1f;进程运行的基本原理知识回顾 内存的管理概念内存空间的扩充内存空间的分配与回收连续分配管理方式动态分区的算法&#xff1a; 内存的基本知识 什么是内存&#xff1f;内存有什么作用&#xff1f; 内…

Linux日志分析

日志的分类 内核以及系统日志 内核及系统日志由系统服务 rsyslog 统一管理&#xff0c;主配置文件为/etc/rsyslog.conf&#xff0c;主程序/sbin/rsyslogd Linux 操作系统本身和大部分服务器程序的日志文件都默认放在目录/var/log/下。 系统日志基于rsyslog服务&#xf…

【LeetCode】剑指 Offer 63. 股票的最大利润 p304 -- Java Version

题目链接&#xff1a;https://leetcode.cn/problems/gu-piao-de-zui-da-li-run-lcof/ 1. 题目介绍&#xff08;63. 股票的最大利润&#xff09; 假设把某股票的价格按照时间先后顺序存储在数组中&#xff0c;请问买卖该股票一次可能获得的最大利润是多少&#xff1f; 【测试用…

自然语言处理 —— 02 基于规则的词法分析

一、什么是词法分析? 词:是自然语言中能够独立运用的最小单位,是语言信息处理的基本单位。 词法分析:是词汇层的分析技术。主要包括词的识别、形态分析、词性标注等任务。 1. 词的识别 将句子转换成词序列【就是分词?】 2. 形态分析 词的构成、形态变化、词形还原。 …

物联网定位技术|实验报告|实验三 PDM定位算法

目录 1. 实验目标 2. 实验要求 3. 算法介绍 3.1 PDM算法 4. 算法实现 第一步&#xff1a;将数据读入内存 第二步&#xff1a;判断锚节的个数 第三步&#xff1a;将所有的两点间的距离关系读入距离矩阵 第四步&#xff1a;利用最短路径算法 第五步&#xff1a;构造节点跳数矩…

JVM-0419~20

类的加载 类的加载过程&#xff08;生命周期&#xff09; 类的加载分几步&#xff1f; 按照Java虚拟机规范&#xff0c;从class文件到加载到内存中的类&#xff0c;到类卸载出内存为止&#xff0c;它的整个生命周期包括如下7个阶段&#xff1a; 基本数据类型在虚拟机里面都预…

【智能座舱系列】-华为发布全球首款车载光场屏 开拓车载视觉体验新航道

4月16日,2023华为智能汽车解决方案“内赋智能,焕发新生”新品发布会在上海举行,华为正式发布全新车载娱乐屏品类:HUAWEI xScene 光场屏。该产品采用独创的光学引擎技术,具有大画幅、景深感、低晕动、眼放松等特点,大幅提升车内视觉体验。 华为光场屏:私人影院装入车内,…

非常详细的阻抗测试基础知识

编者注&#xff1a;为什么要测量阻抗呢&#xff1f;阻抗能代表什么&#xff1f;阻抗测量的注意事项... ...很多人可能会带着一系列的问题来阅读本文。不管是数字电路工程师还是射频工程师&#xff0c;都在关注各类器件的阻抗&#xff0c;本文非常值得一读。全文13000多字&#…

2. 算法分析

2. 算法分析 研究算法的最终目的就是如何花更少的时间&#xff0c;如何占用更少的内存去完成相同的需求。 我们要计算算法时间耗费情况&#xff0c;首先我们得度量算法的执行时间&#xff0c;那么如何度量呢&#xff1f; 2.1 算法的时间复杂度分析 事后分析估算方法&#x…

关于web安全测试在功能测试中的应用

关于web安全测试在功能测试中的应用 一、安全基本概念1.1实施安全评估1.1.1资产等级划分1.1.2威胁分析1.1.3风险分析1.1.4 安全方案 1.2 安全原则 二、我的安全测试模型三、安全测试在功能测试中的应用3.1 更改url3.2 逻辑缺陷&#xff1a;3.3 破坏流程顺序3.4 接口提示信息3.5…

HNU-计算机系统-讨论课5

WARNING&#xff1a; 本题为开放性题目&#xff0c;所设计的也仅仅是一个可能的模型而已&#xff0c;再考虑到个人水平有限。在呈现效果上难免会有缺漏以及可行性的缺陷。故请批判性地接收&#xff01; 所以如果知识有错误或者缺漏&#xff0c;请一定要指出&#xff0c;您的建…

计算机类大学生竞赛经验分享

如果你是作为一个科班出生的学习编程方向的一名在校大学生&#xff0c;那么如果对竞赛感兴趣&#xff0c;可以看看这篇文章 本人作为一名前端开发方向的学生&#xff0c;将自己这几年的参赛经验和比赛相关类型介绍分享给大家 首先我们要知道&#xff0c;竞赛分为三种大概的方向…

《类和对象》(上篇)

本文主要对类和对象有一个初步的了解。 文章目录 前言1、类的引入和定义2、类的访问限定符及封装2.1 访问限定符2.2 封装 3 、类的作用域4 、类的实例化5 、类对象的模型5.1 类对象的大小5.2 类对象存储方式 6、this 指针6.1 引子6.2 特性6.3 this指针的一个问题 前言 C语言是面…