背景

笔者开源了一个小项目code-run,类似codepen的一个工具,其中代码编辑器使用的是微软的Monaco Editor,这个库是直接从VSCode的源码中生成的,只不过是做了一点修改让它支持在浏览器中运行,但是功能基本是和VSCode一样强大的,所以在笔者看来Monaco Editor等于VSCode的编辑器核心。

另外笔者是一个颜控,不管做什么项目,都热衷于配套一些好看的皮肤、主题,所以Moncao Editor仅仅内置了三种主题是远远满足不了笔者需求的,况且还都很丑,于是结合Monaco EditorVSCode的关系就很自然的想到,能不能直接复用VSCode的主题,接下来就给大家介绍一下笔者的探索之路。

ps.想直接了解如何实现的可以跳转到【具体实现】小节。

基本使用

先看一下Monaco Editor的基本使用,首先安装:

  1. npm install monaco-editor

然后引入:

  1. import * as monaco from 'monaco-editor'
  2. // 创建一个js编辑器
  3. const editor = monaco.editor.create(document.getElementById('container'), {
  4. value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
  5. language: 'javascript',
  6. theme: 'vs'
  7. })

这样就可以在container元素上创建一个js语言的编辑器,并且使用了内置的vs-dark主题。如果遇到报错或者语法提示不生效,那么可能需要配置一下worker文件的路径,可以参考官方示例browser-esm-webpack

自定义主题

Monaco Editor支持自定义主题,方法如下:

  1. // 定义主题
  2. monaco.editor.defineTheme(themeName, themeData)
  3. // 使用定义的主题
  4. monaco.editor.setTheme(themeName)

themeName是要自定义的主题名称,比如OneDarkProthemeData是一个对象,即主题数据,基本结构如下:

  1. {
  2. base: 'vs',// 要继承的基础主题,即内置的三个:vs、vs-dark、hc-black
  3. inherit: false,// 是否继承
  4. rules: [// 高亮规则,即给代码里不同token类型的代码设置不同的显示样式
  5. { token: '', foreground: '000000', background: 'fffffe' }
  6. ],
  7. colors: {// 非代码部分的其他部分的颜色,比如背景、滚动条等
  8. [editorBackground]: '#FFFFFE'
  9. }
  10. }

rules里面就是用来给代码进行高亮的,常见的tokenstring(字符串)、comment(注释)、keyword(关键词)等等,完整的请移步themes.ts,这些token是怎么确定的呢,Monaco Editor内置了一个语法着色器Monarch,本质是通过正则表达式来匹配,然后给匹配到的内容命名为一个token

可以直接在编辑器中查看代码某块对应的token,按F1或鼠标右键点击Command Palette,然后再找到并点击Developer: Inspect Tokens,接下来鼠标点哪一块代码,就会显示对应的信息,包括token类型,当前应用的颜色等。

踩坑

最开始的想法很简单,直接找到VSCode的主题文件,然后通过自定义主题来使用。

获取VSCode主题文件

有两种方法,如果某个主题已经在你的VSCode里安装并正在使用的话,那么可以按F1Command/Control + Shift + P或鼠标右键点击Command Palette/命令面板,接着找到并点击Developer:Generate Color Theme From Current Setting/开发人员:使用当前设置生成颜色主题,然后VSCode就会生成一份json数据,保存即可。

如果某个主题没有安装的话,那么可以去vscode主题商店搜索该主题,进入主题详情页面后点击右侧的Download Extension按钮即可下载该主题,下载完成后找到刚才下载的文件,文件应该是以.vsix结尾的,直接把该后缀改成.zip,然后解压缩,最后打开里面的/extension/themes/文件夹,里面的.json文件即主题文件,打开该文件复制json数据即可。

VSCode主题转换成Monaco Editor主题格式

上一步过后你应该可以发现VSCode主题的格式是这样的:

  1. {
  2. "$schema": "vscode://schemas/color-theme",
  3. "type": "dark",
  4. "colors": {
  5. "activityBar.background": "#282c34"
  6. },
  7. "tokenColors": [
  8. {
  9. "scope": "variable.other.generic-type.haskell",
  10. "settings": {
  11. "foreground": "#C678DD"
  12. }
  13. },
  14. {
  15. "scope": [
  16. "punctuation.section.embedded.begin.php",
  17. "punctuation.section.embedded.end.php"
  18. ],
  19. "settings": {
  20. "foreground": "#BE5046"
  21. }
  22. }
  23. ]
  24. }

Monaco Editor的主题格式有一点区别,那是不是可以写一个转换方法把它转换成下面这样呢:

  1. {
  2. base: 'vs',
  3. inherit: false,
  4. rules: [
  5. { token: 'variable.other.generic-type.haskell', foreground: '#C678DD' },
  6. { token: 'punctuation.section.embedded.begin.php', foreground: '#BE5046' },
  7. { token: 'punctuation.section.embedded.end.php', foreground: '#BE5046' }
  8. ],
  9. colors: {
  10. "activityBar.background": "#282c34"
  11. }
  12. }

当然可以,这也不难,但是最后当你使用这个自定义的主题后会发现,没有效果,为什么呢,去Monarch看一下对应语言的解析配置后就会发现,压根就没有VSCode主题里定义的这些token,有效果才奇怪,那怎么办呢,自己扩展这个解析的配置吗,笔者最开始就是这么做的,写正则表达式嘛,应该也不是很难,为此,笔者还把Monarch文档完整翻译了一遍Monarch中文,但是当笔者在VSCode里看到如下效果时:

果断放弃,这显然是要进行语义分析才行,否则谁知道abc是个变量。

其实在VSCode里语法高亮使用的是TextMate,而在Monaco Editor里使用的是Monarch,两者压根不是一个东西,为什么Monaco Editor不使用TextMate,而是要开发一个新的东西呢,原因是VSCode使用的是vscode-textmate来解析TextMate语法,这个库依赖一个Oniguruma正则表达式库,而这个正则表达式库是使用C语言开发的,当然不支持在浏览器上运行。

退而求其次

既然VSCode的主题不能直接使用,那么就只能能用多少用多少,因为Monaco Editor内置的主题token就只有那么多,那么把它所有的token颜色换成VSCode的主题颜色不就行了吗,虽然语义高亮没有,但是总比默认主题好看。实现也很简单,首先colors部分的基本可以直接使用,而token部分可以通过上面介绍的方法Developer: Inspect TokensVSCode里找到对应代码块的颜色,复制到Monaco Editor主题的对应token上即可,比如笔者转换后的OneDarkPro的实际效果如下:

VSCode里的效果如下:

只可粗看,不要细究。

这个事情也有人已经做了,可以参考这个仓库monaco-themes,里面帮你转换了一些常见的主题,可以拿来直接使用。

新的曙光

就在笔者已经放弃在Monaco Editor中直接使用VSCode主题的想法后,无意间发现codesandboxleetcode两个网站中的编辑器主题效果和VSCode中基本一致,而且可以明显的看到在leetcode中切换主题请求的文件:

基本和VSCode主题格式是一样的,这就说明在Monaco Editor中使用VSCode主题是可以实现的,那么问题就变成了怎么实现。

实现

不得不说,这方面资料真的很少,相关文章基本没有,百度搜索结果里只有一两个相关的链接,不过也足以解决问题了,相关链接详见文章尾部。

主要使用的是monaco-editor-textmate这个工具(所以除了百度谷歌之外,github也是一个很重要的搜索引擎啊),先安装:

  1. npm i monaco-editor-textmate

npm应该会同时帮你再安装monaco-textmateonigasmmonaco-editor这几个包,monaco-editor自不必说,我们自己都装了,其他两个可以自行检查一下,如果没有的话需要自行安装。

工具介绍

简单介绍一下这几个包。

onigasm

这个库就是用来解决上述浏览器不支持C语言编写的Oniguruma的问题,解决方法是把Oniguruma编译为WebAssemblyWebAssembly是一种中间格式,可以把非js代码编译成.wasm格式的文件,然后浏览器就可以加载并运行它了,WebAssembly已经是WEB的标准之一了,随着时间的推移,相信兼容性也不是问题。

monaco-textmate

这个库是在VSCode使用的vscode-textmate库的基础上修改的, 以便让它在浏览器上使用。主要作用是解析TextMate语法,这个库依赖前面的onigasm

monaco-editor-textmate

这个库的主要作用是帮我们把monaco-editormonaco-textmate关联起来,内部首先会加载对应语言的TextMate语法文件,然后调用monaco.languages.setTokensProvider方法来自定义语言的token解析器。

看一下它的使用示例:

  1. import { loadWASM } from 'onigasm'
  2. import { Registry } from 'monaco-textmate'
  3. import { wireTmGrammars } from 'monaco-editor-textmate'
  4. export async function liftOff() {
  5. await loadWASM(`path/to/onigasm.wasm`)
  6. const registry = new Registry({
  7. getGrammarDefinition: async (scopeName) => {
  8. return {
  9. format: 'json',
  10. content: await (await fetch(`static/grammars/css.tmGrammar.json`)).text()
  11. }
  12. }
  13. })
  14. const grammars = new Map()
  15. grammars.set('css', 'source.css')
  16. grammars.set('html', 'text.html.basic')
  17. grammars.set('typescript', 'source.ts')
  18. monaco.editor.defineTheme('vs-code-theme-converted', {});
  19. var editor = monaco.editor.create(document.getElementById('container'), {
  20. value: [
  21. 'html, body {',
  22. ' margin: 0;',
  23. '}'
  24. ].join('\n'),
  25. language: 'css',
  26. theme: 'vs-code-theme-converted'
  27. })
  28. await wireTmGrammars(monaco, registry, grammars, editor)
  29. }

具体实现

看完前面的使用示例后,接下来我们详细看一下如何使用。

加载onigasm

首先我们要做的是加载onigasmwasm文件,这个文件需要首先被加载,且加载一次就可以了,所以我们在编辑器初始化前进行加载:

  1. import { loadWASM } from 'onigasm'
  2. const init = async () => {
  3. await loadWASM(`${base}/onigasm/onigasm.wasm`)
  4. // 创建编辑器...
  5. }
  6. init()

onigasm.wasm文件可以在/node_modules/onigasm/lib/目录下找到,然后复制到项目的/public/onigasm/目录下,这样可以通过http进行请求。

创建作用域映射

接下来创建语言id到作用域名称的映射:

  1. const grammars = new Map()
  2. grammars.set('css', 'source.css')

其他语言的作用域名称可以在各种语言的语法列表这里找到,比如想知道css的作用域名称,我们进入css目录,然后打开package.json文件,可以看到其中有一个grammars字段:

  1. "grammars": [
  2. {
  3. "language": "css",
  4. "scopeName": "source.css",
  5. "path": "./syntaxes/css.tmLanguage.json",
  6. "tokenTypes": {
  7. "meta.function.url string.quoted": "other"
  8. }
  9. }
  10. ]

language就是语言idscopeName就是作用域名称。常见的如下:

  1. const scopeNameMap = {
  2. html: 'text.html.basic',
  3. pug: 'text.pug',
  4. css: 'source.css',
  5. less: 'source.css.less',
  6. scss: 'source.css.scss',
  7. typescript: 'source.ts',
  8. javascript: 'source.js',
  9. javascriptreact: 'source.js.jsx',
  10. coffeescript: 'source.coffee'
  11. }

注册语法映射

再接着注册TextMate的语法映射关系,这样可以通过作用域名称来加载并创建对应的语法:

  1. import {
  2. Registry
  3. } from 'monaco-textmate'
  4. // 创建一个注册表,可以从作用域名称来加载对应的语法文件
  5. const registry = new Registry({
  6. getGrammarDefinition: async (scopeName) => {
  7. return {
  8. format: 'json',// 语法文件格式,有json、plist
  9. content: await (await fetch(`${base}grammars/css.tmLanguage.json`)).text()
  10. }
  11. }
  12. })

语法文件和前面的作用域名称一样,也是在各种语言的语法列表这里找,同样以css语言为例,还是看它的package.jsongrammars字段:

  1. "grammars": [
  2. {
  3. "language": "css",
  4. "scopeName": "source.css",
  5. "path": "./syntaxes/css.tmLanguage.json",
  6. "tokenTypes": {
  7. "meta.function.url string.quoted": "other"
  8. }
  9. }
  10. ]

path字段就是对应的语法文件的路径,我们把这些json文件复制到项目的/public/grammars/目录下,这样就可以通过fetch来请求到。

定义主题

前面介绍过,Monaco Editor的主题格式和VSCode的格式是有点不一样的,所以需要进行转换,转换可以自己实现,也可以直接使用monaco-vscode-textmate-theme-converter这个工具,它可以同时转换多个本地文件:

  1. // convertTheme.js
  2. const converter = require('monaco-vscode-textmate-theme-converter')
  3. const path = require('path')
  4. const run = async () => {
  5. try {
  6. await converter.convertThemeFromDir(
  7. path.resolve(__dirname, './vscodeThemes'),
  8. path.resolve(__dirname, '../public/themes')
  9. );
  10. } catch (error) {
  11. console.log(error)
  12. }
  13. }
  14. run()

运行node ./convertTheme.js命令后,就会把你放在vscodeThemes目录下所有VSCode的主题文件转换成Monaco Editor的主题文件并输出到public/themes目录下,然后我们在代码里直接通过fetch来请求主题文件并使用defineTheme方法定义主题即可:

  1. // 请求OneDarkPro主题文件
  2. const themeData = await (
  3. await fetch(`${base}themes/OneDarkPro.json`)
  4. ).json()
  5. // 定义主题
  6. monaco.editor.defineTheme('OneDarkPro', themeData)

设置token解析器

经过前面这些准备工作,最后一步要做的是设置Monaco Editortoken解析器,默认使用的是内置的Monarch,我们要换成TextMate的解析器,也就是monaco-editor-textmate做的事情:

  1. import {
  2. wireTmGrammars
  3. } from 'monaco-editor-textmate'
  4. import * as monaco from 'monaco-editor'
  5. let editor = monaco.editor.create(document.getElementById('container'), {
  6. value: [
  7. 'html, body {',
  8. ' margin: 0;',
  9. '}'
  10. ].join('\n'),
  11. language: 'css',
  12. theme: 'OneDarkPro'
  13. })
  14. await wireTmGrammars(monaco, registry, grammars, editor)

问题1

上一步后应该可以看到VSCode的主题在Monaco Editor上生效了,但是多试几次可能会发现偶尔会失效,原因是Monaco Editor内置的语言是延迟加载的,并且加载完后也会同样注册一个token解析器,所以会把我们的给覆盖掉,详见issuesetTokensProvider unable to override existing tokenizer

一种解决方法是去除内置的语言,这可以使用monaco-editor-webpack-plugin

安装:

  1. npm install monaco-editor-webpack-plugin -D

Vue项目配置如下:

  1. // vue.config.js
  2. const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
  3. module.exports = {
  4. configureWebpack: {
  5. plugins: [
  6. new MonacoWebpackPlugin({
  7. languages: []
  8. })
  9. ]
  10. }
  11. }

languages选项用来指定要包含的语言,我们直接设为空,啥也不要。

然后修改Monaco Editor的引入方式为:

  1. import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'

最后需要手动注册我们需要的语言,因为所有内置语言都被去除了嘛,比如我们要使用js语言的话:

  1. monaco.languages.register({id: 'javascript'})

这种方法虽然可以完美解决该问题,但是很大的一个副作用是语法提示不生效了,因为只有包含了内置的htmlcsstypescript时才会去加载对应的worker文件,没有语法提示笔者也是无法接受的,所以最后笔者使用了一种比较lowhack方式:

  1. // 插件配置
  2. new MonacoWebpackPlugin({
  3. languages: ['css', 'html', 'javascript', 'less', 'pug', 'scss', 'typescript', 'coffee']
  4. })
  5. // 注释掉语言注册语句
  6. // monaco.languages.register({id: 'javascript'})
  7. // 当worker文件被加载了后再wire
  8. let hasGetAllWorkUrl = false
  9. window.MonacoEnvironment = {
  10. getWorkerUrl: function (moduleId, label) {
  11. hasGetAllWorkUrl = true
  12. if (label === 'json') {
  13. return './monaco/json.worker.bundle.js'
  14. }
  15. if (label === 'css' || label === 'scss' || label === 'less') {
  16. return './monaco/css.worker.bundle.js'
  17. }
  18. if (label === 'html' || label === 'handlebars' || label === 'razor') {
  19. return './monaco/html.worker.bundle.js'
  20. }
  21. if (label === 'typescript' || label === 'javascript') {
  22. return './monaco/ts.worker.bundle.js'
  23. }
  24. return './monaco/editor.worker.bundle.js'
  25. },
  26. }
  27. // 循环检测
  28. let loop = () => {
  29. if (hasGetAllWorkUrl) {
  30. Promise.resolve().then(async () => {
  31. await wireTmGrammars(monaco, registry, grammars, editor)
  32. })
  33. } else {
  34. setTimeout(() => {
  35. loop()
  36. }, 100)
  37. }
  38. }
  39. loop()

问题2

笔者遇到的另外一个问题是,转换后有些主题的默认颜色并未设置,所以都是黑色,很丑:

这个问题的解决方法是可以给主题的rules数组添加一个空的token,用来作为没有匹配到的默认token

  1. {
  2. "rules": [
  3. {
  4. "foreground": "#abb2bf",
  5. "token": ""
  6. }
  7. ]
  8. }

foreground的色值可以取colors选项里的editor.foreground的值,要手动修改每个色值比较麻烦,可以在之前的转换主题的步骤里顺便进行,会在下一个问题里一起解决。

问题3

monaco-vscode-textmate-theme-converter这个包本质算是nodejs环境下的工具,所以想在纯前端环境下使用不太方便,另外它对于非标准json格式的VSCode主题转换时会报错,因为很多主题格式是.jsonc,内容是带有很多注释的,所以都需要自己先进行检查并修改,不是很方便,基于这两个问题,笔者fork了它的代码,然后修改并分成了两个包,分别对应nodejs浏览器环境,详见https://github.com/wanglin2/monaco-vscode-textmate-theme-converter

所以我们可以替换掉monaco-vscode-textmate-theme-converter,改成安装笔者的:

  1. npm i vscode-theme-to-monaco-theme-node -D

使用方式基本是一样的:

  1. // 只要修改引入为笔者的包即可
  2. const converter = require('vscode-theme-to-monaco-theme-node')
  3. const path = require('path')
  4. const run = async () => {
  5. try {
  6. await converter.convertThemeFromDir(
  7. path.resolve(__dirname, './vscodeThemes'),
  8. path.resolve(__dirname, '../public/themes')
  9. );
  10. } catch (error) {
  11. console.log(error)
  12. }
  13. }
  14. run()

现在就可以直接转换.jsonc文件,而且输出统一为.json文件,另外内部会自动添加一个空的token作为没有匹配到的默认token,效果如下:

最佳实践

VSCode主题除了代码主题外,一般还包含编辑器其他部分的主题,比如标题栏、状态栏、侧边栏、按钮等等,所以我们也可以在页面应用这些样式,达到整个页面的主题也能随编辑器代码主题一起切换的效果,这样能让页面整体更加协调,具体的实现上,我们可以使用CSS变量,先把页面所有涉及到的颜色都定义成CSS变量,然后在切换主题时根据主题的colors选项里的指定字段来更新变量即可,具体使用哪个字段来对应页面的哪个部分可以根据实际情况来确定,VSCode主题的所有可配置项可以在theme-color这里找到。效果如下:

总结

本文完整详细的介绍了笔者对于Monaco Editor编辑器主题的探索,希望能给有主题定制需求的小伙伴们一点帮助,完整的代码请参考本项目源码:code-run

参考链接

文章:monaco使用vscode相关语法高亮在浏览器上显示

文章:codesandbox是如何解决主题的问题

文章:闲谈Monaco Editor-自定义语言之Monarch

讨论:如何在Monaco Editor中使用VSC主题?

讨论:使用WebAssembly来支持TextMate语法

手把手教你实现在Monaco Editor中使用VSCode主题的更多相关文章

  1. Monaco Editor 中的 Keybinding 机制

    一.前言 前段时间碰到了一个 Keybinding 相关的问题,于是探究了一番,首先大家可能会有两个问题:Monaco Editor 是啥?Keybinding 又是啥? Monaco Editor: ...

  2. 【软工】[技术博客] 用Monaco Editor打造接近vscode体验的浏览器IDE

    [技术博客] 用Monaco Editor打造接近vscode体验的浏览器IDE 官方文档与重要参考资料 官方demo 官方API调用样例 Playground 官方API Doc,但其搜索框不支持模 ...

  3. 手把手教你如何在Ubuntu系统中安装Pycharm

    前几天带大家一起安装了Ubuntu14.04系统,没来得及上车的伙伴可以戳这篇文章:手把手教你在VMware虚拟机中安装Ubuntu14.04系统.今天小编带大家一起在Ubuntu14.04中安装Py ...

  4. 手把手教你在 TKE 集群中实现简单的蓝绿发布和灰度发布

    概述 如何在腾讯云 Kubernetes 集群实现蓝绿发布和灰度发布?通常要向集群额外部署其它开源工具来实现,比如 Nginx Ingress,Traefik 等,或者让业务上 Service Mes ...

  5. 手把手教你在容器服务 TKE 中使用动态准入控制器

    在 TKE 中使用动态准入控制器 原理概述 动态准入控制器 Webhook 在访问鉴权过程中可以更改请求对象或完全拒绝该请求,其调用 Webhook 服务的方式使其独立于集群组件,具有非常大的灵活性, ...

  6. 手把手教你实现栈以及C#中Stack源码分析

    定义 栈又名堆栈,是一种操作受限的线性表,仅能在表尾进行插入和删除操作. 它的特点是先进后出,就好比我们往桶里面放盘子,放的时候都是从下往上一个一个放(入栈),取的时候只能从上往下一个一个取(出栈), ...

  7. 手把手教你在CSDN博客中插入图片之剑走偏锋系列

    1.在博客园注册账号.你没有看错,就是博客园,在图像上传方面博客园比CSDN这个垃圾强太多了. 2.在博客园进入随笔撰写编辑模块,点击上传图像按钮(点最黄的那个,别点错了). 3.弹出如下窗口 ,点击 ...

  8. 手把手教你写Sublime中的Snippet

    手把手教你写Sublime中的Snippet Sublime Text号称最性感的编辑器, 并且越来越多人使用, 美观, 高效 关于如何使用Sublime text可以参考我的另一篇文章, 相信你会喜 ...

  9. 菜鸟-手把手教你把Acegi应用到实际项目中(8)-扩展UserDetailsService接口

    一个能为DaoAuthenticationProvider提供存取认证库的的类,它必须要实现UserDetailsService接口: public UserDetails loadUserByUse ...

随机推荐

  1. Elasticsearch 索引策略

    Elasticsearch 7.6 索引生命周期 es的生命周期就对应了索引的策略,比如我们在使用elk的时候,由于数据量较大,时间比较久远的数据就没有那么有价值了,因此就需要定期的清除这些历史数据, ...

  2. 2021.07.19 P2624 明明的烦恼(prufer序列,为什么杨辉三角我没搞出来?)

    2021.07.19 P2624 明明的烦恼(prufer序列,为什么杨辉三角我没搞出来?) [P2624 HNOI2008]明明的烦恼 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn ...

  3. mmdetection 绘制PR曲线

    参考:https://github.com/xuhuasheng/mmdetection_plot_pr_curve 适用于COCO数据集 import os import mmcv import n ...

  4. asp.net core MVC 添加静态文件

    ASP.net Core 中添加插件需要 1.将文件放在wwwroot文件夹下(根目录文件夹,没有的话需要创建) 2.需要在project.json中的dependencies添加如下依赖 " ...

  5. 动态SQL常用标签

    动态 SQL 目的:为了摆脱在不同条件拼接 SQL 语句的痛苦 在不同条件在生成不同的SQL语句 本质上仍然是SQL语句,不过是多了逻辑代码去拼接SQL,只要保证SQL的正确性按照格式去排列组合 可以 ...

  6. [洛谷] P2010 [NOIP2016 普及组] 回文日期

    点击查看代码 #include<bits/stdc++.h> using namespace std; int data1, data2, ans = 0, sum; int d[13] ...

  7. spring盒springMVC整合父子容器问题:整合Spring时Service层为什么不做全局包扫描详解

    整合Spring时Service层为什么不做全局包扫描详解 一.Spring和SpringMVC的父子容器关系 1.讲问题之前要先明白一个关系 一般来说,我们在整合Spring和SpringMVC这两 ...

  8. 网络协议OSI模型-TCP/IP-三次握手

    OSI模型 在制定计算机网络标准方面,起着重大作用的两大国际组织是:国际电信联盟电信标准化部门,与国际 标准组织(ISO),虽然它们工作领域不同,但随着科学技术的发展,通信与信息处理之间的界限开始 变 ...

  9. Web Api源码(路由注册)

    这篇文章只是我学习Web API框架的输出,学习方法还是输出倒逼输入比较行得通,所以不管写的好不好,坚持下去,肯定有收获.篇幅比较长,仔细思考阅读下来大约需要几分钟. 做.NET开发有好几年时间了,从 ...

  10. MongoDB是什么?非关系型数据库的优点?安装使用教程

    哈喽!大家好,我是小奇,一位热爱分享的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 书接上回,由于球姐都有孩子了, ...