pnpm + workspace + changesets

news2024/9/25 3:28:43

pnpm + workspace + changesets 构建你的 monorepo 工程

什么是monorepo?

什么是 monorepo?以及和 multirepo 的区别是什么?

关于这些问题,在之前的一篇**介绍 lerna** 的文章中已经详细介绍过,感兴趣的同学可以再回顾下。

简而言之,monorepo 就是把多个工程放到一个 git 仓库中进行管理,因此他们可以共享同一套构建流程、代码规范也可以做到统一,特别是如果存在模块间的相互引用的情况,查看代码、修改bug、调试等会更加方便。

什么是 pnpm?

pnpm 是新一代的包管理工具,号称是最先进的包管理器。按照官网说法,可以实现节约磁盘空间并提升安装速度创建非扁平化的 node_modules 文件夹两大目标,具体原理可以参考 pnpm 官网

pnpm 提出了 workspace 的概念,内置了对 monorepo 的支持,那么为什么要用 pnpm 取代之前的 lerna 呢?

这里我总结了以下几点原因:

  • lerna 已经不再维护,后续有任何问题社区无法及时响应
  • pnpm装包效率更高,并且可以节约更多磁盘空间
  • pnpm本身就预置了对monorepo的支持,不需要再额外第三方包的支持
  • one more thing,就是好奇心了

如何使用 pnpm 来搭建 menorepo 工程

安装 pnpm

$ npm install -g pnpm

⚠️v7版本的pnpm安装使用需要node版本至少大于v14.19.0,所以在安装之前首先需要检查下node版本。

工程初始化

为了便于后续的演示,先在工程根目录下新建 packages 目录,并且在 packages 目录下创建 pkg1pkg2 两个工程,分别进到 pkg1pkg2 两个目录下,执行 npm init 命令,初始化两个工程,package.json 中的 name 字段分别叫做 @qftjs/menorepo1@qftjs/monorepo2(PS:@qftjs是提前在npm上创建好的组织,没有的话需要提前创建)。

为了防止根目录被发布出去,需要设置工程工程个目录下 package.json配置文件的 private 字段为 true

为了实现一个完整的例子,这里我使用了 father-build 对模块进行打包,father-build 是基于 rollup 进行的一层封装,使用起来更加便捷。

在 pkg1 和 pkg2 的 src 目录下个创建一个 index.ts 文件:

// pkg1/src/index.ts
import pkg2 from '@qftjs/monorepo2';

function fun2() {
  pkg2();
  console.log('I am package 1');
}

export default fun2;

// pkg2/src/index.ts
function fun2() {
  console.log('I am package 2');
}

export default fun2;

分别在 pkg1 和 pkg2 下新增 .fatherrc.tstsconfig.ts 配置文件。

// .fatherrc.ts
export default {
  target: 'node',
  cjs: { type: 'babel', lazy: true },
  disableTypeCheck: false,
};

// tsconfig.ts
{
  "include": ["src", "types", "test"],
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "lib": ["dom", "esnext"],
    "importHelpers": true,
    "declaration": true,
    "sourceMap": true,
    "rootDir": "./",
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "alwaysStrict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "moduleResolution": "node",
    "baseUrl": "./",
    "paths": {
      "*": ["src/*", "node_modules/*"]
    },
    "jsx": "react",
    "esModuleInterop": true
  }
}

全局安装 father-build:

$ pnpm i -Dw father-build

最后在 pkg1 和 pkg2 下的 package.json 文件中增加一条 script:

{
  "scripts": {
    "build": "father-build"
  }
}

这样在 pkg1 或者 pkg2 下执行 build 命令就会将各子包的ts代码打包成js代码输出至 lib 目录下。

要想启动 pnpmworkspace 功能,需要工程根目录下存在 pnpm-workspace.yaml 配置文件,并且在 pnpm-workspace.yaml 中指定工作空间的目录。比如这里我们所有的子包都是放在 packages 目录下,因此修改 pnpm-workspace.yaml 内容如下:

packages:
  - 'packages/*'

初始化完毕后的工程目录结构如下:

.
├── README.md
├── package.json
├── packages
│   ├── pkg1
│   │   ├── package.json
│   │   ├── src
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   └── pkg2
│       ├── package.json
│       ├── src
│       │   └── index.ts
│       └── tsconfig.json
├── pnpm-workspace.yaml
└── tsconfig.root.json

安装依赖包

使用 pnpm 安装依赖包一般分以下几种情况:

  • 全局的公共依赖包,比如打包涉及到的 rolluptypescript

pnpm 提供了 -w, --workspace-root 参数,可以将依赖包安装到工程的根目录下,作为所有 package 的公共依赖。

比如:

$ pnpm install react -w

如果是一个开发依赖的话,可以加上 -D 参数,表示这是一个开发依赖,会装到 pacakage.json 中的 devDependencies 中,比如:

$ pnpm install rollup -wD
  • 给某个package单独安装指定依赖

pnpm 提供了 –filter 参数,可以用来对特定的package进行某些操作。

因此,如果想给 pkg1 安装一个依赖包,比如 axios,可以进行如下操作:

$ pnpm add axios --filter @qftjs/monorepo1

需要注意的是,--filter 参数跟着的是package下的 package.jsonname 字段,并不是目录名。

关于 --filter 操作其实还是很丰富的,比如执行 pkg1 下的 scripts 脚本:

$ pnpm build --filter @qftjs/monorepo1

filter 后面除了可以指定具体的包名,还可以跟着匹配规则来指定对匹配上规则的包进行操作,比如:

$ pnpm build --filter "./packages/**"

此命令会执行所有 package 下的 build 命令。具体的用法可以参考**filter**文档。

  • 模块之间的相互依赖

最后一种就是我们在开发时经常遇到的场景,比如 pkg1 中将 pkg2 作为依赖进行安装。

基于 pnpm 提供的 workspace:协议,可以方便的在 packages 内部进行互相引用。比如在 pkg1 中引用 pkg2:

$ pnpm install @qftjs/monorepo2 -r --filter @qftjs/monorepo1

此时我们查看 pkg1 的 package.json,可以看到 dependencies 字段中多了对 @qftjs/monorepo2 的引用,以 workspace: 开头,后面跟着具体的版本号。

{
  "name": "@qftjs/monorepo1",
  "version": "1.0.0",
  "dependencies": {
    "@qftjs/monorepo2": "workspace:^1.0.0",
    "axios": "^0.27.2"
  }
}

在设置依赖版本的时候推荐用 workspace:*,这样就可以保持依赖的版本是工作空间里最新版本,不需要每次手动更新依赖版本。

pnpm publish 的时候,会自动将 package.json 中的 workspace 修正为对应的版本号。

只允许pnpm

当在项目中使用 pnpm 时,如果不希望用户使用 yarn 或者 npm 安装依赖,可以将下面的这个 preinstall 脚本添加到工程根目录下的 package.json中:

{
  "scripts": {
    "preinstall": "npx only-allow pnpm"
  }
}

preinstall 脚本会在 install 之前执行,现在,只要有人运行 npm installyarn install,就会调用 only-allow 去限制只允许使用 pnpm 安装依赖。

Release工作流

workspace 中对包版本管理是一个非常复杂的工作,遗憾的是 pnpm 没有提供内置的解决方案,一部分开源项目在自己的项目中自己实现了一套包版本的管理机制,比如 Vue3Vite 等。

pnpm 推荐了两个开源的版本控制工具:

  • changesets
  • rush

这里我采用了 changesets 来做依赖包的管理。选用 changesets 的主要原因还是文档更加清晰一些,个人感觉上手比较容易。

按照 **changesets 文档**介绍的,changesets主要是做了两件事:

Changesets hold two key bits of information: a version type (following semver), and change information to be added to a changelog.

简而言之就是管理包的version生成changelog

配置changesets

  • 安装
$ pnpm add -Dw @changesets/cli
  • 初始化
$ pnpm changeset init

执行完初始化命令后,会在工程的根目录下生成 .changeset 目录,其中的 config.json 作为默认的 changeset 的配置文件。

修改配置文件如下:

{
  "$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json",
  "changelog": "@changesets/cli/changelog",
  "commit": false,
  "linked": [["@qftjs/*"]],
  "access": "public",
  "baseBranch": "main",
  "updateInternalDependencies": "patch",
  "ignore": [],
  "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
      "onlyUpdatePeerDependentsWhenOutOfRange": true
  }
}

说明如下:

  • changelog: changelog 生成方式
  • commit: 不要让 changesetpublish 的时候帮我们做 git add
  • linked: 配置哪些包要共享版本
  • access: 公私有安全设定,内网建议 restricted ,开源使用 public
  • baseBranch: 项目主分支
  • updateInternalDependencies: 确保某包依赖的包发生 upgrade,该包也要发生 version upgrade 的衡量单位(量级)
  • ignore: 不需要变动 version 的包
  • ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: 在每次 version 变动时一定无理由 patch 抬升依赖他的那些包的版本,防止陷入 major 优先的未更新问题

如何使用changesets

一个包一般分如下几个步骤:

为了便于统一管理所有包的发布过程,在工程根目录下的 pacakge.jsonscripts 中增加如下几条脚本:

  1. 编译阶段,生成构建产物
{
  "build": "pnpm --filter=@qftjs/* run build"
}
  1. 清理构建产物和 node_modules
{
  "clear": "rimraf 'packages/*/{lib,node_modules}' && rimraf node_modules"
}
  1. 执行 changeset,开始交互式填写变更集,这个命令会将你的包全部列出来,然后选择你要更改发布的包
{
  "changeset": "changeset"
}
  1. 执行 changeset version,修改发布包的版本
{
  "version-packages": "changeset version"
}

这里需要注意的是,版本的选择一共有三种类型,分别是 patchminormajor,严格遵循 semver 规范。

这里还有个细节,如果我不想直接发 release 版本,而是想先发一个带 tagprerelease版本呢(比如beta或者rc版本)?

这里提供了两种方式:

  • 手工调整

这种方法最简单粗暴,但是比较容易犯错。

首先需要修改包的版本号:

{
  "name": "@qftjs/monorepo1",
  "version": "1.0.2-beta.1"
}

然后运行:

$ pnpm changeset publish --tag beta

注意发包的时候不要忘记加上 --tag 参数。

  • 通过 changeset 提供的 Prereleases 模式
    利用官方提供的 Prereleases 模式,通过 pre enter <tag> 命令进入先进入 pre 模式。

常见的tag如下所示:

名称功能
alpha是内部测试版,一般不向外部发布,会有很多Bug,一般只有测试人员使用
beta也是测试版,这个阶段的版本会一直加入新的功能。在Alpha版之后推出
rcRelease Candidate) 系统平台上就是发行候选版本。RC版不会再加入新的功能了,主要着重于除错
$ pnpm changeset pre enter beta

之后在此模式下的 changeset publish 均将默认走 beta 环境,下面在此模式下任意的进行你的开发,举一个例子如下:

# 1-1 进行了一些开发...
# 1-2 提交变更集
pnpm changeset
# 1-3 提升版本
pnpm version-packages # changeset version
# 1-4 发包
pnpm release # pnpm build && pnpm changeset publish --registry=...
# 1-5 得到 1.0.0-beta.1

# 2-1 进行了一些开发...
# 2-2 提交变更集
pnpm changeset
# 2-3 提升版本
pnpm version-packages
# 2-4 发包
pnpm release
# 2-5 得到 1.0.0-beta.2

完成版本发布之后,退出 Prereleases 模式:

$ pnpm changeset pre exit
  1. 构建产物后发版本
{
  "release": "pnpm build && pnpm release:only",
  "release:only": "changeset publish --registry=https://registry.npmjs.com/"
}

规范代码提交

代码提交规范对于团队或者公司来说是非常重要的,养成良好的代码提交规范可以方便回溯,有助于对本次提交进行review,如果单纯的只是要求团队成员遵循某些代码提交规范,是很难形成强制约束的,现在我们就尝试通过工具来约束代码提交规范。

使用commitizen规范commit提交格式

commitizen 的作用主要是为了生成标准化的 commit message,符合 Angular 规范。

一个标准化的 commit message 应该包含三个部分:Header、Body 和 Footer,其中的 Header 是必须的,Body 和 Footer 可以选填。

<type>(<scope>): <subject>
// 空一行
<body>
// 空一行
<footer>

Header 部分由三个字段组成:type(必需)、scope(可选)、subject(必需)

  • Type type 必须是下面的其中之一:

    • feat: 增加新功能
    • fix: 修复 bug
    • docs: 只改动了文档相关的内容
    • style: 不影响代码含义的改动,例如去掉空格、改变缩进、增删分号
    • refactor: 代码重构时使用,既不是新增功能也不是代码的bud修复
    • perf: 提高性能的修改
    • test: 添加或修改测试代码
    • build: 构建工具或者外部依赖包的修改,比如更新依赖包的版本
    • ci: 持续集成的配置文件或者脚本的修改
    • chore: 杂项,其他不需要修改源代码或不需要修改测试代码的修改
    • revert: 撤销某次提交
  • scope

用于说明本次提交的影响范围。scope 依据项目而定,例如在业务项目中可以依据菜单或者功能模块划分,如果是组件库开发,则可以依据组件划分。

  • subject

主题包含对更改的简洁描述:

注意三点:

  1. 使用祈使语气,现在时,比如使用 “change” 而不是 “changed” 或者 ”changes“
  2. 第一个字母不要大写
  3. 末尾不要以.结尾
  • Body

主要包含对主题的进一步描述,同样的,应该使用祈使语气,包含本次修改的动机并将其与之前的行为进行对比。

  • Footer

包含此次提交有关重大更改的信息,引用此次提交关闭的issue地址,如果代码的提交是不兼容变更或关闭缺陷,则Footer必需,否则可以省略。

使用方法:

commitizencz-conventional-changelog

如果需要在项目中使用 commitizen 生成符合 AngularJS 规范的提交说明,还需要安装 cz-conventional-changelog 适配器。

$ pnpm install -wD commitizen cz-conventional-changelog

工程根目录下的 package.json 中增加一条脚本:

"scripts": {
  "commit": "cz"
}

接下来就可以使用 $ pnpm commit 来代替 $ git commit 进行代码提交了,看到下面的效果就表示已经安装成功了。

img

commitizen-交互图.png

commitlint && husky

前面我们提到,通过 commitizen && cz-conventional-changelog 可以规范我们的 commit message,但是同时也存在一个问题,如果用户不通过 pnpm commit 来提交代码,而是直接通过 git commit 命令来提交代码,就能绕开 commit message 检查,这是我们不希望看到的。

因此接下来我们使用 commitlint 结合 husky 来对我们的提交行为进行约束。在 git commit 提交之前使用 git 钩子来验证信息,阻止不符合规范的commit 提交。

安装 commitlinthusky

$ pnpm install -wD @commitlint/cli @commitlint/config-conventional husky

在工程根目录下增加 commitlint.config.js 配置文件,指定 commitlint 的校验配置文件:

module.exports = { extends: ['@commitlint/config-conventional'] };

husky 配置(husky的每个版本配置不一样,具体可以参考**官方文档**,当前的husky是v8.0.1)。

工程根目录下的 package.json 中增加一条 script:

"scripts": {
  "postinstall": "husky install"
}

该脚本会在执行完 $ pnpm install 之后自动执行,进行 husky 的初始化,执行完毕后就会在根目录下创建一个 .husky 目录。

执行如下命令新增一个husky的hook:

$ npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

当我们通过 git commit 提交不符合规范的代码,就会出现如下报错,并且自动退出提交流程。

img

husky校验.png

代码规范检查

良好的代码编写规范对团队的可持续发展起着至关重要的作用,因此接下来我会配置 eslint 对代码进行统一的规范校验,配合 lint-staged 可以对已经提交的代码进行校验。

首选需要安装 eslintlint-stage

$ pnpm install -wD eslint lint-staged @typescript-eslint/parser @typescript-eslint/eslint-plugin

在根成根目录下添加 .eslintrc 配置文件:

module.exports = {
  'parser': '@typescript-eslint/parser',
  'plugins': ['@typescript-eslint'],
  'rules': {
    'no-var': 'error',// 不能使用var声明变量
    'no-extra-semi': 'error',
    '@typescript-eslint/indent': ['error', 2],
    'import/extensions': 'off',
    'linebreak-style': [0, 'error', 'windows'],
    'indent': ['error', 2, { SwitchCase: 1 }], // error类型,缩进2个空格
    'space-before-function-paren': 0, // 在函数左括号的前面是否有空格
    'eol-last': 0, // 不检测新文件末尾是否有空行
    'semi': ['error', 'always'], // 在语句后面加分号
    'quotes': ['error', 'single'],// 字符串使用单双引号,double,single
    'no-console': ['error', { allow: ['log', 'warn'] }],// 允许使用console.log()
    'arrow-parens': 0,
    'no-new': 0,//允许使用 new 关键字
    'comma-dangle': [2, 'never'], // 数组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号,always-multiline多行模式必须带逗号,单行模式不能带逗号
    'no-undef': 0
  },
  'parserOptions': {
    'ecmaVersion': 6,
    'sourceType': 'module',
    'ecmaFeatures': {
      'modules': true
    }
  }
};

lint-staged 是 Git 里的概念,表示暂存区,lint-staged 表示只检查暂存区中的文件。

package.json 中增加如下配置:

"lint-staged": {
    "*.ts": [
      "eslint --fix",
      "git add"
    ]
}

husky 中增加 pre-commit 校验:

$ npx husky add .husky/pre-commit "npx --no-install lint-staged"

示例代码

更多精彩文章,欢迎关注我的公众号:前端架构师笔记

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

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

相关文章

nacos学习积累

官方文档&#xff1a;https://nacos.io/zh-cn/docs/quick-start.html 1 注册中心简介 注册中心对比和选型&#xff1a;Zookeeper、Eureka、Nacos、Consul和ETCD 如果消费者直接连接的提供者。这样做的问题是&#xff0c;若提供者出现宕机&#xff0c;或消费者存在高并发情况&…

SSRF漏洞

前言 作者简介&#xff1a;不知名白帽&#xff0c;网络安全学习者。 博客主页&#xff1a;不知名白帽的博客_CSDN博客-网络安全,CTF,内网渗透领域博主 网络安全交流社区&#xff1a;https://bbs.csdn.net/forums/angluoanquan 目录 SSRF漏洞原理 产生CSRF的函数 SSRF中常见手…

MySQL原理探索——27 主库出问题了,从库怎么办

在前面的第24、25和26篇文章中&#xff0c;介绍了 MySQL 主备复制的基础结构&#xff0c;但这些都是一主一备的结构。 大多数的互联网应用场景都是读多写少&#xff0c;因此你负责的业务&#xff0c;在发展过程中很可能先会遇到读性能的问题。而在数据库层解决读性能问题&#…

Atlassian Bamboo Enterprise Crack

Atlassian Bamboo Enterprise Crack Bamboo Server是专业组织如持续集成、安装和运输的选择。 从代码到安装的连续运输。 在一个工作流中集中发布自动生成、测试和发布。 构建&#xff1a;专注于编码&#xff0c;依靠Bamboo作为自己的CI&#xff0c;构建一个主机!创建多阶段构建…

3.8.cuda运行时API-使用cuda核函数加速yolov5后处理

目录 前言1. Yolov5后处理2. 后处理案例2.1 cpu_decode2.2 gpu_decode 总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习精简…

【*2200线段树Pushup】CF1567 E

Problem - E - Codeforces 题意&#xff1a; 思路&#xff1a; 维护这些信息即可 Code&#xff1a; #include <bits/stdc.h>#define int long longusing namespace std;const int mxn2e510; const int mxe2e510; const int mod1e97; const int Inf1e18;struct info{in…

【C语言】gcc编译时报错 fatal error: stdio.h: 没有那个文件或目录

零、问题 在Ubuntu20.04.6中使用GCC编译一个HelloWorld代码时遇到如下问题&#xff1a; 首先确认了&#xff0c;自己单词没有拼写错。 然后再检查GCC的版本&#xff0c;确实没问题&#xff1a; 我用的是Ubuntu20.04.6的版本。 壹、解决 没有标准的头文件需要安装build-es…

和鲸社区数据分析每周挑战【第九十七期:技术博客文本分析】

和鲸社区数据分析每周挑战【第九十七期&#xff1a;技术博客文本分析】 文章目录 和鲸社区数据分析每周挑战【第九十七期&#xff1a;技术博客文本分析】一、背景描述二、数据说明三、问题描述四、数据导入五、数据探索性分析六、对文章标题进行文本分类预测1、数据预处理2、逻…

C++万字自学笔记

[TOC] 一、 C基础 C的IDE有CLion、Visual Studio、DEV C、eclipse等等&#xff0c;这里使用CLion进行学习。 0. C初识 0.1 第一个C程序 编写一个C程序总共分为4个步骤 创建项目创建文件编写代码运行程序 #include <iostream>int main() {using namespace std;cout…

新手如何快速安装电脑监控软件?

越来越多的管理者选择使用电脑监控软件&#xff0c;许多新手不知道具体怎样安装&#xff0c;本期将为大家介绍下具体的安装流程。 电脑监控软件购买之后&#xff0c;会提供网址和账号密码&#xff0c;登录后需要先添加员工信息&#xff0c;有三种方法&#xff1a; &#xff0…

Android性能优化(bin启动优化)

我们平时会在android里面写个bin程序来干点活&#xff0c;但是有时候我们会发现很奇怪的现象&#xff0c;我明明很早就启动这个bin了&#xff0c;但是过了很久bin程序的main函数才被调用~。这个是为啥呢&#xff1f;主要有2个原因&#xff1a; 一.bin程序依赖的so库太多&#…

steam搬砖项目,csgo游戏搬砖熟练操作后,可以月入过万~

科思创业汇 大家好&#xff0c;这里是科思创业汇&#xff0c;一个轻资产创业孵化平台。赚钱的方式有很多种&#xff0c;我希望在科思创业汇能够给你带来最快乐的那一种&#xff01; 网上创业创造了一批赚钱的人&#xff0c;年收入从几十万到几百万不等&#xff0c;营业额从几…

基于springboot房屋租赁管理系统

开发工具&#xff1a;IDEA&#xff0c;jdk1.8 服务器&#xff1a;tomcat9.0 数据库&#xff1a;mysql5.7 前端&#xff1a;jsp、bootstrap 技术&#xff1a; springbootmybatis-plus 系统主要分前台和后台&#xff0c;分租客、房东、管理员三个角色 系统功能介绍说明&…

nodejs 高级编程-通信

一、通信基本原理 通信必要条件 主机之间需要有传输介质主机上必须有网卡设备主机之间需要协商网络速率 二、网络通讯方式 常见的通讯方式 交换机通讯路由器通讯 如何建立多台主机互连&#xff1f; 如何定位局域网中的其他主机&#xff1f; 通过Mac地址来唯一标识一台主机…

hcip笔记---ospf的LSA限制和不规则区域

有关ACL&#xff1a;例如&#xff1a;1.1.1.0 0.0.0.255这个网段以及后面跟随的通配符&#xff0c;通配符和反掩码长得很像&#xff0c;同时都是用0标识不可变&#xff0c;1标识可变&#xff0c;但反掩码里的1和0必须连续出现&#xff0c;而通配符则不需要遵循这个规则&#xf…

深入思考Sui的独特性如何构建出跨时代的产品

近日&#xff0c;我们与Mysten Labs产品总监Janet Wu面对面探讨了Web3的产品开发过程&#xff0c;了解了她对Sui上最激动人心的产品用例的看法&#xff0c;以及她对该行业未来的展望。 您能简单介绍一下在Mysten Labs担任产品总监意味着什么吗&#xff1f; 对我而言&#xff…

0基础学习VR全景平台篇 第59篇:专业版功能-跨账号复制

功能位置示意 一、本功能将用在哪里&#xff1f; 跨账号复制&#xff0c;是指将本账号中已发布的VR漫游作品一键复制给其他账号使用。 复制成功后&#xff0c;其他账号中也会生成同样的作品以及获得相关的全景、音频、图片、视频等素材。 并且原作品和复制品可以独立编辑&am…

K8s为什么需要calico? calico 原理深入理解.

文章目录 为什么需要calico&#xff1f;-网络插件”千千万”&#xff0c;为何k8s要用calicocalico的架构calico Pod 跨node通信tunl0 的作用&#xff1f;为什么所有pod的默认网关都是169.254.1.1 &#xff1f;什么是ARP 代理&#xff1f;jksj BGP模式的calico工作原理calico BG…

vue3 报错解决:找不到模块‘xxx.vue’或其相应的类型声明。(Vue 3 can not find module)

src下面建立一个xx.d.ts的文件 declare module *.vue {import { ComponentOptions } from vueconst componentOptions: ComponentOptionsexport default componentOptions }

使用3DEXPERIENCE平台有效管理设计变更,随时处理问题

临时处理设计变更&#xff0c;电脑却不在身边怎么办?借助3DEXPERIENCE平台&#xff0c;我们可以轻松的用手机打开模型&#xff0c;还能够随时随地查看其他人的工作进展并进行审批。 在工作过程中&#xff0c;工程师小A发现&#xff0c;装配体的零件强度有点弱&#xff0c;小A…