1.pnpm+workspace+changesets构建你的源码monorepo工程
2.arcgis api 3.x for js 入门开发系列五地图态势标绘(附源码下载)
3.Keil开发软件使用介绍
pnpm+workspace+changesets构建你的monorepo工程
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的支持,不需要再额外第三方包的支持
onemorething,就是好奇心了?
如何使用pnpm来搭建menorepo工程安装pnpm$?npm?install?-g?pnpmv7版本的pnpm安装使用需要node版本至少大于v..0,所以在安装之前首先需要检查下node版本。
工程初始化为了便于后续的屏共享源码演示,先在工程根目录下新建packages目录,并且在packages目录下创建pkg1和pkg2两个工程,分别进到pkg1和pkg2两个目录下,执行npminit命令,初始化两个工程,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.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ pkg2();console.log('I?am?package?1');}export?default?fun2;//?pkg2/src/index.tsfunction?fun2()?{ console.log('I?am?package?2');}export?default?fun2;分别在pkg1和pkg2下新增.fatherrc.ts和tsconfig.ts配置文件。
//?.fatherrc.tsexport?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目录下。
要想启动pnpm的workspace功能,需要工程根目录下存在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安装依赖包一般分以下几种情况:
全局的公共依赖包,比如打包涉及到的rollup、typescript等
pnpm提供了-w,--workspace-root参数,可以将依赖包安装到工程的根目录下,作为所有?三三聊源码package的公共依赖。
比如:
$?pnpm?install?react?-w如果是一个开发依赖的话,可以加上-D参数,表示这是一个开发依赖,会装到pacakage.json中的devDependencies中,比如:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ pkg2();console.log('I?am?package?1');}export?default?fun2;0给某个package单独安装指定依赖
pnpm提供了--filter参数,可以用来对特定的package进行某些操作。
因此,如果想给pkg1安装一个依赖包,比如axios,可以进行如下操作:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ pkg2();console.log('I?am?package?1');}export?default?fun2;1需要注意的是,--filter参数跟着的是package下的package.json的name字段,并不是目录名。
关于--filter操作其实还是很丰富的,比如执行pkg1下的scripts脚本:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ pkg2();console.log('I?am?package?1');}export?default?fun2;2filter后面除了可以指定具体的包名,还可以跟着匹配规则来指定对匹配上规则的包进行操作,比如:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ pkg2();console.log('I?am?package?1');}export?default?fun2;3此命令会执行所有package下的build命令。具体的用法可以参考filter文档。
模块之间的相互依赖
最后一种就是我们在开发时经常遇到的场景,比如pkg1中将pkg2作为依赖进行安装。
基于pnpm提供的workspace:协议,可以方便的在packages内部进行互相引用。比如在pkg1中引用pkg2:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ pkg2();console.log('I?am?package?1');}export?default?fun2;4此时我们查看pkg1的package.json,可以看到dependencies字段中多了对@qftjs/monorepo2的引用,以workspace:开头,拼拼乐源码后面跟着具体的版本号。
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ pkg2();console.log('I?am?package?1');}export?default?fun2;5在设置依赖版本的时候推荐用workspace:*,这样就可以保持依赖的版本是工作空间里最新版本,不需要每次手动更新依赖版本。
当pnpmpublish的时候,会自动将package.json中的workspace修正为对应的版本号。
只允许pnpm当在项目中使用pnpm时,如果不希望用户使用yarn或者npm安装依赖,可以将下面的这个preinstall脚本添加到工程根目录下的package.json中:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ pkg2();console.log('I?am?package?1');}export?default?fun2;6preinstall脚本会在install之前执行,现在,只要有人运行npminstall或yarninstall,就会调用only-allow去限制只允许使用pnpm安装依赖。
Release工作流在workspace中对包版本管理是一个非常复杂的工作,遗憾的是pnpm没有提供内置的解决方案,一部分开源项目在自己的项目中自己实现了一套包版本的管理机制,比如Vue3、Vite等。
pnpm推荐了两个开源的版本控制工具:
changesets
rush
这里我采用了changesets来做依赖包的管理。选用changesets的主要原因还是文档更加清晰一些,个人感觉上手比较容易。
按照changesets文档介绍的,changesets主要是做了两件事:
Changesetsholdtwokeybitsofinformation:aversiontype(followingsemver),andchangeinformationtobeaddedtoachangelog.
简而言之就是管理包的version和生成changelog。
配置changesets安装
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ pkg2();console.log('I?am?package?1');}export?default?fun2;7初始化
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ pkg2();console.log('I?am?package?1');}export?default?fun2;8执行完初始化命令后,会在工程的换货源码根目录下生成.changeset目录,其中的config.json作为默认的changeset的配置文件。
修改配置文件如下:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ pkg2();console.log('I?am?package?1');}export?default?fun2;9说明如下:
changelog:changelog生成方式
commit:不要让changeset在publish的时候帮我们做gitadd
linked:配置哪些包要共享版本
access:公私有安全设定,内网建议restricted,开源使用public
baseBranch:项目主分支
updateInternalDependencies:确保某包依赖的包发生upgrade,该包也要发生versionupgrade的衡量单位(量级)
ignore:不需要变动version的包
___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH:在每次version变动时一定无理由patch抬升依赖他的那些包的版本,防止陷入major优先的未更新问题
如何使用changesets一个包一般分如下几个步骤:
为了便于统一管理所有包的发布过程,在工程根目录下的pacakge.json的scripts中增加如下几条脚本:
编译阶段,生成构建产物
//?pkg2/src/index.tsfunction?fun2()?{ console.log('I?am?package?2');}export?default?fun2;0清理构建产物和node_modules
//?pkg2/src/index.tsfunction?fun2()?{ console.log('I?am?package?2');}export?default?fun2;1执行changeset,开始交互式填写变更集,这个命令会将你的包全部列出来,然后选择你要更改发布的包
//?pkg2/src/index.tsfunction?fun2()?{ console.log('I?am?package?2');}export?default?fun2;2执行changesetversion,修改发布包的版本
//?pkg2/src/index.tsfunction?fun2()?{ console.log('I?am?package?2');}export?default?fun2;3这里需要注意的是,版本的选择一共有三种类型,分别是patch、minor和major,严格遵循semver规范。
这里还有个细节,如果我不想直接发release版本,而是想先发一个带tag的prerelease版本呢(比如beta或者rc版本)?
这里提供了两种方式:
手工调整
这种方法最简单粗暴,但是比较容易犯错。
首先需要修改包的版本号:
//?pkg2/src/index.tsfunction?fun2()?{ console.log('I?am?package?2');}export?default?fun2;4然后运行:
//?pkg2/src/index.tsfunction?fun2()?{ console.log('I?am?package?2');}export?default?fun2;5注意发包的时候不要忘记加上--tag参数。
通过changeset提供的Prereleases模式
利用官方提供的Prereleases模式,通过preenter<tag>命令进入先进入pre模式。
常见的tag如下所示:
名称功能alpha是内部测试版,一般不向外部发布,会有很多Bug,一般只有测试人员使用beta也是测试版,这个阶段的版本会一直加入新的功能。在Alpha版之后推出rcReleaseCandidate)系统平台上就是发行候选版本。RC版不会再加入新的功能了,主要着重于除错//?pkg2/src/index.tsfunction?fun2()?{ console.log('I?am?package?2');}export?default?fun2;6之后在此模式下的changesetpublish均将默认走beta环境,下面在此模式下任意的进行你的开发,举一个例子如下:
//?pkg2/src/index.tsfunction?fun2()?{ console.log('I?am?package?2');}export?default?fun2;7完成版本发布之后,退出Prereleases模式:
//?pkg2/src/index.tsfunction?fun2()?{ console.log('I?am?package?2');}export?default?fun2;8构建产物后发版本
//?pkg2/src/index.tsfunction?fun2()?{ console.log('I?am?package?2');}export?default?fun2;9规范代码提交代码提交规范对于团队或者公司来说是非常重要的,养成良好的代码提交规范可以方便回溯,有助于对本次提交进行review,如果单纯的只是要求团队成员遵循某些代码提交规范,是很难形成强制约束的,现在我们就尝试通过工具来约束代码提交规范。
使用commitizen规范commit提交格式commitizen的作用主要是为了生成标准化的commitmessage,符合Angular规范。
一个标准化的commitmessage应该包含三个部分:Header、Body和Footer,其中的Header是必须的,Body和Footer可以选填。
//?.fatherrc.tsexport?default?{ target:?'node',cjs:?{ ?type:?'babel',?lazy:?true?},disableTypeCheck:?false,};0Header部分由三个字段组成:type(必需)、scope(可选)、subject(必需)
Typetype必须是下面的其中之一:
feat:增加新功能
fix:修复bug
docs:只改动了文档相关的内容
style:不影响代码含义的改动,例如去掉空格、改变缩进、增删分号
refactor:代码重构时使用,既不是新增功能也不是代码的bud修复
perf:提高性能的修改
test:添加或修改测试代码
build:构建工具或者外部依赖包的修改,比如更新依赖包的版本
ci:持续集成的配置文件或者脚本的修改
chore:杂项,其他不需要修改源代码或不需要修改测试代码的修改
revert:撤销某次提交
scope
用于说明本次提交的影响范围。scope依据项目而定,例如在业务项目中可以依据菜单或者功能模块划分,如果是组件库开发,则可以依据组件划分。
subject
主题包含对更改的简洁描述:
注意三点:
使用祈使语气,现在时,比如使用"change"而不是"changed"或者”changes“
第一个字母不要大写
末尾不要以.结尾
Body
主要包含对主题的进一步描述,同样的,应该使用祈使语气,包含本次修改的动机并将其与之前的行为进行对比。
Footer
包含此次提交有关重大更改的信息,引用此次提交关闭的issue地址,如果代码的提交是不兼容变更或关闭缺陷,则Footer必需,否则可以省略。
使用方法:
commitizen和cz-conventional-changelog如果需要在项目中使用commitizen生成符合AngularJS规范的提交说明,还需要安装cz-conventional-changelog适配器。
//?.fatherrc.tsexport?default?{ target:?'node',cjs:?{ ?type:?'babel',?lazy:?true?},disableTypeCheck:?false,};1工程根目录下的package.json中增加一条脚本:
//?.fatherrc.tsexport?default?{ target:?'node',cjs:?{ ?type:?'babel',?lazy:?true?},disableTypeCheck:?false,};2接下来就可以使用$pnpmcommit来代替$gitcommit进行代码提交了,看到下面的效果就表示已经安装成功了。
commitlint&&husky前面我们提到,通过commitizen&&c
arcgis api 3.x for js 入门开发系列五地图态势标绘(附源码下载)
关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js: esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类的介绍,还有就是在线例子: esri 官网在线例子,这个也是学习 arcgis api 3.x 的好素材。
内容概览
基于arcgis api 的 Draw 工具基本绘制 拓展 Draw 工具的绘制 源代码 demo 下载
本篇实现地图态势标绘功能模块
截图如下
本篇核心的在于调用 arcgis api 的 Draw 工具: Draw
构造函数:
一般来说,传参 map 对象进来就行,其他参数可选的,用默认的就行,除非你想专门设置。 默认的可绘制图形类型常量 Constants:
可以设置绘制的符号样式:
其中,activate 函数可以激活触发绘制的行为,绘制结束之后在绘制结束事件里面获取 geometry:
地图态势标绘实现的思路:利用 arcgis api 的 Draw工具实现普通的点线面绘制,但是对于燕尾箭头、集结地、弧线、曲线、简单箭头等特殊的军事态势需要自定义来绘制了,所以需要拓展 Draw 工具才能实现,也是本篇的精华所在。
1是继承拓展 Draw 的文件目录;2是实现态势标绘模块的 js 文件。 首先,需要在 map.html 页面引用进来:
其中,paths 代表需要引用的路径。
其次,在 map.js 文件的初始化里面引用拓展的 js 文件 DrawEx 以及 DrawExt:
最后,在工具栏菜单的态势标绘菜单响应事件里面调用 plot.js 即可:
其次,触发调用 Draw 绘制:
最后,添加绘制图形在地图上展示:
demo源码下载 下载提取码:g5cy
Keil开发软件使用介绍
Keil ARM MDK是一款专为单片机/嵌入式开发设计的高效工具,它整合了全面的开发环境、调试器和C/C++编译器。下面将逐步介绍如何使用这款工具进行项目开发。
首先,确保安装了对应芯片的补丁,通过Keil打开后找到相关选项进行安装。
接着,新建一个工程,选择适合的ARM Cortex-M架构芯片,并添加项目所需源代码文件。
在配置工程阶段,打开各个选项卡进行详细设置。Device选项卡用于选择目标MCU,Target选项卡则关注ARM Compiler版本和MicroLIB的选择。Output选项卡用于指定输出文件类型,Listing用于生成代码详细信息。User选项卡允许自定义编译前后的操作,如执行脚本。
C/C++选项卡是核心配置,包括预处理器符号和编译选项,Include Paths确保编译器能找到头文件。Asm和Linker选项卡用于处理汇编代码和链接设置,Debug选项则涉及在线调试配置。
在Utilities选项卡中,务必注意“Reset and Run”和“Programming Algorithm”的设置,以确保程序正常运行和代码烧录的顺利进行。
以上步骤是Keil ARM MDK的基本使用流程,根据实际项目需求进行调整。如有任何疑问,欢迎查阅官方文档或寻求帮助。祝您开发顺利!