整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          Web 開發(fā)中使用了 Vim 作為主編輯器之后...

          Web 開發(fā)中使用了 Vim 作為主編輯器之后......

          選擇編輯器時(shí),想必很多人對 vim 嗤之以鼻,但實(shí)際上,從一定角度來看,你也能發(fā)現(xiàn)別樣的風(fēng)采。本文的目的并不在于強(qiáng)推薦大家使用 vim,而是通過作者將 Vim 作為 Web 開發(fā)項(xiàng)目的主編輯器案例中,讓我們發(fā)現(xiàn)一些不同之處。

          作者 | Fidel Sanchez-Bueno

          譯者 | 彎月,責(zé)編 | 屠敏

          出品 | CSDN(ID:CSDNnews)

          以下為譯文:

          在學(xué)習(xí)編程的過程中,選擇最適合自己的編輯器或IDE(下文的編輯器指代兩者)是每個(gè)程序員都會經(jīng)歷的過程。對于我而言,大約從9年前開始學(xué)習(xí)Python,我還記得當(dāng)時(shí)每周都會換一種編輯器,一開始是IDLE,后來我記得還試過Boa Constructor、Komodo和Notepad++等。

          在尋找最佳編輯器的過程中,你會了解到程序員之間的編輯器之戰(zhàn),并且會對那些Vim和Emacs的笑話會心一笑。

          也就是在這個(gè)時(shí)候,你會嘗試學(xué)習(xí)Vim或者Emacs,從此就開始了愛麗絲的探險(xiǎn)之旅。

          從這篇文章的標(biāo)題就可以看出來,我選擇的編輯器是Vim,但我并沒打算向你推薦Vim,也不會去說服你花費(fèi)無數(shù)時(shí)間,改變自己的開發(fā)環(huán)境來使用Vim。

          這篇文章的目的是分享我在使用Vim作為Web開發(fā)項(xiàng)目的主編輯器時(shí),做出的一些能夠提高效率的定制。

          Web開發(fā)的Vim插件

          作為Web程序員,大部分時(shí)間都在編寫HTML、CSS和JavaScript文件,根據(jù)個(gè)人喜好或項(xiàng)目需要,你可能還會使用一些框架(如Angular、Vue或React)以及babel、webpack、grunt等各種工具。

          我個(gè)人會盡可能減少插件的使用,僅在插件能帶來非常大的好處,而且能真正改進(jìn)工作流程的時(shí)候才會使用。

          目前我安裝的插件如下:

          • Emmet.vim

          • indentline和vim-jsx-pretty

          • vim-commentary

          • ALE(eslint和prettier)

          Emmet.vim

          Emmet是高速輸入和編輯代碼的絕佳工具,只需要輸入一行代碼就可以創(chuàng)建一整段復(fù)雜的HTML。

          indentline和vim-jsx-pretty

          這兩個(gè)插件可以改進(jìn)Vim的視覺樣式。indentline可以添加豎線來顯示縮進(jìn)級別,vim-jsx-pretty能給JSX代碼添加高亮,很適合編寫ReactJS等代碼時(shí)使用。

          vim-commentary

          這個(gè)插件可以方便地注釋掉一段代碼或者取消注釋,只需選中代碼并鍵入<g-c>即可。

          ALE(eslint和prettier)

          ALE(Asynchronous Lint Engine)可以調(diào)用linter和代碼修整工具,極大地提高工作效率,屬于那種不用不知道,用了絕對不后悔的插件。我使用ALE主要是為了調(diào)用prettier。

          實(shí)時(shí)預(yù)覽(熱重載)

          實(shí)時(shí)查看修改的效果能夠極大地改善工作流程。很多React或Gatsby等項(xiàng)目已經(jīng)內(nèi)置了該功能,但如果僅僅是創(chuàng)建一個(gè)簡單的網(wǎng)頁(HTML、CSS和JavaScript),那么Atom、Brackets或VSCode等編輯器可以把編輯中的頁面的實(shí)時(shí)預(yù)覽并列顯示在另一個(gè)窗口中。

          像我這種愛鉆牛角尖的人很希望在Vim中也使用該功能。盡管有幾個(gè)插件能實(shí)現(xiàn),但我決定選擇另一種方式。

          我決定實(shí)現(xiàn)一個(gè)不依賴于編輯器的方案。基本思路就是,運(yùn)行一個(gè)本地服務(wù)器,監(jiān)視文件的改動,每當(dāng)文件更新時(shí)就刷新服務(wù)器上的頁面。

          聽起來似乎很復(fù)雜,但實(shí)際上非常簡單,只需要在項(xiàng)目文件夾中安裝并運(yùn)行browser-sync即可。

          我假設(shè)你已經(jīng)安裝了nodejs,所以只需要在全局安裝browser-sync。

          npm install -g browser-sync

          安裝完browser-sync之后,就能在任何文件夾中運(yùn)行,創(chuàng)建一個(gè)本地服務(wù)器,然后自動顯示文件夾內(nèi)的index.html。

          browser-sync start --server --files .

          如果你使用的是Linux,bane可以創(chuàng)建一個(gè)別名來簡化啟動服務(wù)器的過程。打開主目錄下的.bashrc文件,添加如下內(nèi)容:

          # Command line alias to start the browser-sync server
          alias serve="browser-sync start --server --files ."

          我更進(jìn)一步,允許局域網(wǎng)內(nèi)的其他機(jī)器訪問我的服務(wù)器,這樣就能在別的設(shè)備上進(jìn)行測試:

          # browser-sync config
          # Get the current local IP address
          export SERVER_IP=`hostname -I`

          # The command alias to start the browser-sync server
          alias serve="browser-sync start --server --files . --no-notify --host $SERVER_IP --port 9000"

          感謝閱讀!

          原文:https://dev.to/fidelve/using-vim-as-your-main-editor-for-web-development-5a73

          作者:Fidel Sanchez-Bueno,化學(xué)工程師,自學(xué)成才的程序員。

          本文為 CSDN 翻譯,轉(zhuǎn)載請注明來源出處。

          【End】

          為一個(gè)現(xiàn)代的代碼編輯器,Atom有著各種流行編輯器都有的特性,功能上非常豐富,支持各種編程語言的代碼高亮與大多數(shù)其他編輯器相比,Atom的語言支持已經(jīng)算是覆蓋非常全面了。另外,它的代碼補(bǔ)全功能(也叫Snippets)也非常好用,你只需輸入幾個(gè)字符即可展開成各種常用代碼,可以極大提高編程效率。Atom預(yù)裝了四種UI和八種語法主題,包括深色和淺色。如果找不到您要查找的內(nèi)容,您還可以安裝由Atom社區(qū)創(chuàng)建的主題或創(chuàng)建自己的主題。

          Atom幫助您更快地編寫代碼。在一個(gè)窗口中輕松瀏覽和打開單個(gè)文件,整個(gè)項(xiàng)目或多個(gè)項(xiàng)目。

          使用默認(rèn)插件,從v1.5.1開始,在某些方面支持以下語言:HTML,CSS,Less,Sass,GitHub Flavored Markdown,C / C ++,C#,Go,Java,Objective-C,JavaScript,JSON,CoffeeScript ,Python,PHP,Ruby,Ruby on Rails,shell腳本,Clojure,Perl,Git,Make,Property List(Apple),TOML,XML,YAML,Moustache,Julia和SQL。

          Atom1.38.0顯著變化:

          • #18471 - 切換注釋光標(biāo)位置。
          • #19136 - 現(xiàn)有窗口中的“打開文件”。
          • #19138 - 從桌面啟動Atom時(shí)計(jì)算env變量。
          • #18499 - 支持contentRegex for TextMate語法。
          • #19206 - 調(diào)用activate()時(shí),將面板標(biāo)志設(shè)置為焦點(diǎn)。
          • #19192 - 修復(fù)reset-font-size。
          • #18705 - 啟用autoFocus選項(xiàng)以接受要關(guān)注窗格創(chuàng)建的元素。
          • #19231 - 為開放操作選擇現(xiàn)有窗口時(shí)跳過開發(fā)模式窗口。
          • #19272 - 在線上打開文件時(shí)改善定位。
          • #19280 - 當(dāng)它沒有參數(shù)時(shí)修復(fù)atom.open()。
          • #19279 - 改進(jìn)ERB和EJS中注釋字符的處理。
          • #19354 - 在提示在config.onDidChange回調(diào)中重新啟動之前,等待配置文件加載
          • atom / fuzzy-finder#383 - 處理來自多個(gè)項(xiàng)目的正確相似結(jié)果。
          • atom / fuzzy-finder#385 - 導(dǎo)航到一行后確保光標(biāo)位置居中。
          • atom / markdown-preview#559 - 停止使用roaster渲染降價(jià)。
          • atom / tree-view#1180 - 添加用于移動沖突條目的UI。
          • atom / language-go#156 - 添加對Go模塊和校驗(yàn)和文件的支持。
          • atom / language-html#227 - 改進(jìn)EJS和ERB中的評論處理。
          • atom / language-hyperlink#27 - 允許單個(gè)匹配的parens。
          • atom / language-javascript#644 - 允許折疊switch_default。
          • atom / language-json#68 - 添加tree-sitter json語法。
          • atom / language-json#73 - 范圍鏈接為字符串并為“添加范圍”
          • atom / language-python#288 - 為樹保護(hù)語法添加更多范圍。
          • atom / language-python#297 - 在樹形保護(hù)語法中標(biāo)記正式函數(shù)參數(shù)。
          • atom / language-python#298 - 樹形語法中的Tokenize子類列表名稱。
          • atom / language-python#300 - 允許折疊if if語句沒有elif或者。

          原子1.38.1的變化:

          • 修復(fù)了在10.12之前的macOS版本上使用Tree Sitter語法時(shí)發(fā)生崩潰的問題。(#19497)
          • 修復(fù)了JSON語法中的回歸,突出顯示了將超鏈接樣式應(yīng)用于非超鏈接atom / language-json#76

          語言的JSON

          • V1.0.2 ... v1.0.4

          原子1.38.2更改日志:

          • 修復(fù)了窗口中的回歸:reset-font-size,不允許重置為默認(rèn)大小。https://github.com/atom/atom/issues/19521

          tom是一個(gè)著名的開源編輯器,是由Chris Wanstrath在2008年作為其個(gè)人的編外項(xiàng)目發(fā)展而來。據(jù)說在今年(2022)年底,這款編輯器也將進(jìn)入關(guān)停狀態(tài)。而且目前大部分程序員都把VS Code作為其最主要的開發(fā)工作,但是Atom本身的設(shè)計(jì)和代碼實(shí)現(xiàn)都是非常優(yōu)秀的,通過閱讀它的源碼,我們還是可以學(xué)到很多相關(guān)的編程技巧。

          主流程分析

          atom的代碼結(jié)構(gòu)非常清晰,整個(gè)項(xiàng)目可以分為兩個(gè)部分,一個(gè)是atom本身的代碼,另一個(gè)是atom的插件。atom本身的代碼又可以分為兩部分,一個(gè)是atom的核心業(yè)務(wù)邏輯,另一個(gè)是atom的UI代碼。核心業(yè)務(wù)邏輯主要

          是用來設(shè)置環(huán)境變量,調(diào)度窗口、調(diào)度系統(tǒng)資源等等。UI代碼則主要負(fù)責(zé)處理atom的界面,比如菜單欄,工具欄,狀態(tài)欄等等。

          作為使用electron框架編寫的應(yīng)用程序,整體都是使用js來寫的(早期是使用coffee來編寫的),可以從其目錄中看到,整個(gè)項(xiàng)目的目錄結(jié)構(gòu)如下:

          |-src // 核心業(yè)務(wù)邏輯
          |-|-main-process
          |-|-|-atom-application.js
          |-|-|-atom-environment.js
          |-|-|-atom-window.js
          |-static // UI代碼
          |-packages // 其它擴(kuò)展包
          ...
          

          眾所周知,用Electron框架寫成的應(yīng)用,都可以分為主線程和渲染進(jìn)程。對應(yīng)到atom中,主線程的代碼都是在src/main-process目錄下,而渲染線程的代碼則是直接src目錄下。靜態(tài)UI資源則在static目錄下。

          我們先從主線程的入口代碼開始看起,代碼位于src/main-process/main.js路徑下:

          // 命令行工具入口,
          const args = yargs(process.argv)
            // Don't handle --help or --version here; they will be handled later.
            .help(false)
            .version(false)
            .alias('d', 'dev')
            .alias('t', 'test')
            .alias('r', 'resource-path').argv;
          // 下面省略大量代碼,主要用于處理命令行參數(shù),用來專門處理使用命令行打開atom的情況
          // 真正的入口
          const start = require(path.join(resourcePath, 'src', 'main-process', 'start'));
          start(resourcePath, devResourcePath, startTime);
          

          可以從上面代碼看出,其實(shí)真正的處理入口還是在start函數(shù)中(src/main-process/start.js):

          module.exports = function start(resourcePath, devResourcePath, startTime) {
            // 處理錯誤情況
            process.on('uncaughtException', function(error = {}) {
            });
            process.on('unhandledRejection', function(error = {}) {
            });
            // 初始化各種參數(shù)
            app.commandLine.appendSwitch('enable-experimental-web-platform-features');
            const args = parseCommandLine(process.argv.slice(1));
            const previousConsoleLog = console.log;
            console.log = nslog;
            args.resourcePath = normalizeDriveLetterName(resourcePath);
            args.devResourcePath = normalizeDriveLetterName(devResourcePath);
            atomPaths.setAtomHome(app.getPath('home'));
            atomPaths.setUserData(app);
            const config = getConfig();
            const colorProfile = config.get('core.colorProfile');
            if (colorProfile && colorProfile !== 'default') {
              app.commandLine.appendSwitch('force-color-profile', colorProfile);
            }
            if (handleStartupEventWithSquirrel()) {
              return;
            } else if (args.test && args.mainProcess) {
              // 處理測試情況
              app.setPath(
                'userData',
                temp.mkdirSync('atom-user-data-dir-for-main-process-tests')
              );
              console.log = previousConsoleLog;
              app.on('ready', function() {
                const testRunner = require(path.join(
                  args.resourcePath,
                  'spec/main-process/mocha-test-runner'
                ));
                testRunner(args.pathsToOpen);
              });
              return;
            }
            const releaseChannel = getReleaseChannel(app.getVersion());
            let appUserModelId = 'com.squirrel.atom.' + process.arch;
            if (releaseChannel !== 'stable') {
              appUserModelId += `-${releaseChannel}`;
            }
            // 這個(gè)方法可以防止win10在任務(wù)欄中顯示重復(fù)的atom圖標(biāo)
            app.setAppUserModelId(appUserModelId);
            app.on('open-file', addPathToOpen);
            app.on('open-url', addUrlToOpen);
            // 當(dāng)應(yīng)用關(guān)閉的時(shí)候,需要上報(bào)一些數(shù)據(jù)
            app.on('will-finish-launching', () =>
              startCrashReporter({
                uploadToServer: config.get('core.telemetryConsent') === 'limited',
                releaseChannel
              })
            );
            if (args.userDataDir != null) {
              app.setPath('userData', args.userDataDir);
            } else if (args.test || args.benchmark || args.benchmarkTest) {
              app.setPath('userData', temp.mkdirSync('atom-test-data'));
            }
            app.on('ready', function() {
              app.removeListener('open-file', addPathToOpen);
              app.removeListener('open-url', addUrlToOpen);
              // 構(gòu)造一個(gè)atomApplication對象
              const AtomApplication = require(path.join(
                args.resourcePath,
                'src',
                'main-process',
                'atom-application'
              ));
              // 并將之前的參數(shù)傳入
              AtomApplication.open(args);
            });
          };
          

          從上面代碼可以看出,前置處理也是各種參數(shù)的初始化,以及為了便于測試,做的一些定制處理。在應(yīng)用初始化結(jié)束后,就會動態(tài)加載應(yīng)用模塊,構(gòu)造 AtomApplication 實(shí)例。可以注意到,這里使用按需加載的目的是希望能夠在需要的時(shí)候才會去加載對應(yīng)的模塊,這樣可以減少內(nèi)存的占用。

          接著,我們來看一下atom-application.js的代碼,這塊代碼量比較大,是整個(gè)atom的核心代碼,我們先來看一下整體的結(jié)構(gòu):

          // 是一個(gè)單列模式, 繼承自`EventEmitter`模塊,主要因?yàn)閮?nèi)部會大量應(yīng)用事件處理機(jī)制來分發(fā)邏輯。
          class AtomApplication extends EventEmitter {
              static open(options) {
                  // 初始化一些參數(shù)
                  // 創(chuàng)建一個(gè)atomApplication對象
                  // 并將之前的參數(shù)傳入
                  return new AtomApplication(options);
              }
              exit(status) {
                  app.exit(status);
                }
                constructor(options){}
              async initialize(options) {}
          }
          

          程序啟動的入口只有AtomApplication.open這一個(gè)方法,這個(gè)方法會創(chuàng)建一個(gè)AtomApplication對象,然后調(diào)用它的initialize方法,層層遞進(jìn),再調(diào)用創(chuàng)建窗口、加載配置等方法,最終完成程序的啟動。

          其中,比較值得注意的是使用了一個(gè)叫做event-kit的模塊。它是一個(gè)事件處理器模塊,提供了一個(gè)事件處理器的抽象,可以讓我們更容易地處理事件。最重要的作用是它實(shí)現(xiàn)了CompositeDisposable類,可以在需要的時(shí)候,釋放資源。雖然javascript是一個(gè)有垃圾回收機(jī)制的語言,但是如果沒有手動釋放一些資源的話,會造成大量的內(nèi)存占用。

          在使用過程中,也十分簡單

          class AtomApplication extends EventEmitter {
            // 省略其它代碼
            constructor(options) {
                // 省略其它代碼
                this.disposable = new CompositeDisposable();
            }
            async destroy() {
              const windowsClosePromises = this.getAllWindows().map(window => {
                window.close();
                return window.closedPromise;
              });
              await Promise.all(windowsClosePromises);
              // 在銷毀的時(shí)候統(tǒng)一釋放
              this.disposable.dispose();
            }
            // 注冊事件處理函數(shù)
            handleEvents() {
                // 省略其它代碼,
              // 在注冊事件回調(diào)的時(shí)候,直接將對象添加到disposable的依賴中去
              this.disposable.add(
                ipcHelpers.on(app, 'before-quit', async event => {...})
              );
            }
          }
          

          擴(kuò)展機(jī)制

          atom作為一個(gè)編輯器,它的擴(kuò)展機(jī)制是非常重要的。和其他的IDE類似,擴(kuò)展機(jī)制也是使用的微內(nèi)核模式(或者插件模式)來實(shí)現(xiàn)。微內(nèi)核架構(gòu)是一種十分常見的軟件架構(gòu),它將應(yīng)用系統(tǒng)分為兩個(gè)部分:一個(gè)微內(nèi)核和一組外部的插件。微內(nèi)核負(fù)責(zé)管理插件,提供插件之間的通信機(jī)制,以及提供一些基礎(chǔ)的服務(wù)。插件則負(fù)責(zé)提供具體的功能。這樣的架構(gòu)可以讓我們更容易地?cái)U(kuò)展軟件的功能,而不需要修改軟件的核心代碼。

          在atom中,插件主要是通過package類來實(shí)現(xiàn)的。package是atom擴(kuò)展的基本單元,它可以包含一些功能,比如語法高亮、代碼提示、代碼格式化等等,也提供了讓第三方開發(fā)者擴(kuò)展的能力。那么這些擴(kuò)展是如何加載的呢?我們先來看一下package-manager.js的代碼:

          // 可以加載、激活、停用、卸載包
          // 加載包讀取并解析包的元數(shù)據(jù)和資源,例如快捷鍵、菜單、樣式表等
          // 激活包注冊加載的資源并調(diào)用包的主模塊的`activate()`方法
          // 停用包取消注冊包的資源并調(diào)用包的主模塊的`deactivate()`方法
          // 卸載包從包管理器中完全移除
          // 可以通過`core.disabledPackages`配置項(xiàng)和調(diào)用`enablePackage()/disablePackage()`方法來啟用/禁用包
          class PackageManager {
            preloadPackage(packageName, pack) {
              ...
            }
            loadPackages() {
              ...
            }
            enablePackage(packageName) {
              ...
            }
            // 觸發(fā)事件,用來注冊回調(diào)
            onDidActivatePackage(callback) {
            }
          }
          

          這個(gè)包管理器類PackageManager,可以管理擴(kuò)展包的整個(gè)生命周期,主要負(fù)責(zé)包的加載、卸載、更新等操作。而所有的包都綁定在主內(nèi)核的atom.packages這個(gè)全局變量上,我們可以通過這個(gè)變量來訪問應(yīng)用上加載的所有擴(kuò)展。

          那么packageManager是如何負(fù)責(zé)管理包的安裝和卸載呢?:

          class PackageManager {
              constructor(packages) {
                  this.packages = packages;
              }
              getPackages() {
                  return this.packages;
              }
              getPackage(name) {
                  return this.packages.find(pkg => pkg.name === name);
              }
              // 禁用包,從內(nèi)存中將包去除,然后通知應(yīng)用程序或者擴(kuò)展來執(zhí)行禁用操作
              async deactivatePackage(name, suppressSerialization) {
                  const pack = this.getLoadedPackage(name);
                  if (pack == null) {
                    return;
                  }
                  if (!suppressSerialization && this.isPackageActive(pack.name)) {
                    this.serializePackage(pack);
                  }
                  const deactivationResult = pack.deactivate();
                  if (deactivationResult && typeof deactivationResult.then === 'function') {
                    await deactivationResult;
                  }
                  delete this.activePackages[pack.name];
                  delete this.activatingPackages[pack.name];
                  this.emitter.emit('did-deactivate-package', pack);
                }
          }
          

          比如在擴(kuò)展ui-watcher中,就可以在監(jiān)聽到did-deactivate-package事件后,執(zhí)行一些清理操作:

          watchForPackageChanges() {
              this.subscriptions.add(
                atom.packages.onDidDeactivatePackage(pack => {
                  // This only handles packages - onDidChangeActiveThemes handles themes
                  const watcher = this.watchedPackages.get(pack.name);
                  if (watcher) watcher.destroy();
                  this.watchedPackages.delete(pack.name);
                })
              );
          }
          

          Package類,則包含了包的基礎(chǔ)信息,包括鍵位設(shè)置、配置、樣式等,并且有完整的生命周期。

          class Package {
              constructor(params) {
                this.config = params.config;
                this.packageManager = params.packageManager;
                this.styleManager = params.styleManager;
                this.commandRegistry = params.commandRegistry;
                this.keymapManager = params.keymapManager;
                this.notificationManager = params.notificationManager;
                this.grammarRegistry = params.grammarRegistry;
                this.themeManager = params.themeManager;
                this.menuManager = params.menuManager;
                this.contextMenuManager = params.contextMenuManager;
                this.deserializerManager = params.deserializerManager;
                this.viewRegistry = params.viewRegistry;
                this.emitter = new Emitter();
                // 此處省略大量的細(xì)節(jié)
              }
              preload() {
                  // do something
              }
              load() {
                  // do something
              }
              unload() {
                  // do something
              }
              activate() {
                  // do something
              }
              deactivate() {
                  // do something
              }
              finishLoading() {
                  // do something
              }
          }
          

          Package(擴(kuò)展)實(shí)例本身要和主應(yīng)用進(jìn)行通信,atom是直接通過全局對象的方式進(jìn)行調(diào)用的,這樣做的好處是不用考慮通信的問題,但是也有一些弊端,比如不方便重構(gòu)等。

          在應(yīng)用入口處,會將PackageManager實(shí)例掛載在應(yīng)用實(shí)例上。后續(xù)我們可以通過atom.packages來訪問包管理器實(shí)例,從而獲取包的信息。

          // atom-application.js
          this.packages = new PackageManager({
              ... // 一堆的配置
          });
          this.packages.initialize(...);
          

          而在渲染進(jìn)程中,可以通過在window上掛載atom對象來訪問包管理器實(shí)例,從而獲取所有擴(kuò)展包的信息,進(jìn)行預(yù)加載操作。

          // initialize-application-window.js
          / 初始化 AtomEnvironment
          global.atom = new AtomEnvironment({
            clipboard,
            applicationDelegate: new ApplicationDelegate(),
            enablePersistence: true
          });
          TextEditor.setScheduler(global.atom.views);
          // 初始化應(yīng)用窗口
          global.atom.preloadPackages();
          // ... 省略大量代碼
          module.exports = function({ blobStore }) {
           // 省略大量代碼
           // 在startEditorWindows內(nèi)部,當(dāng)窗口初始化完成后,會正式調(diào)用`loadPackages`方法來加載所有的擴(kuò)展包
           return global.atom.startEditorWindow().then(function() {
              // Workaround for focus getting cleared upon window creation
              const windowFocused = function() {
                window.removeEventListener('focus', windowFocused);
                setTimeout(() => document.querySelector('atom-workspace').focus(), 0);
              };
              window.addEventListener('focus', windowFocused);
              ipcRenderer.on('environment', (event, env) => updateProcessEnv(env));
            });
          }
          

          總體而言,atom的擴(kuò)展機(jī)制還是比較簡單的,在各種擴(kuò)展的生命周期中,都可以通過事件來進(jìn)行通信,從而實(shí)現(xiàn)各種功能。這樣一種實(shí)現(xiàn),其實(shí)也可以在我們?nèi)粘9ぷ鬟^程中加以借鑒。


          主站蜘蛛池模板: 波多野结衣一区视频在线| 国产成人高清视频一区二区| 久久免费精品一区二区| 中文字幕一区日韩精品| 日韩一区二区在线观看| 中文字幕AV一区二区三区人妻少妇| 亚洲福利精品一区二区三区| 亚洲av成人一区二区三区| 精品国产一区二区三区2021| 精品国产AV无码一区二区三区| 少妇激情AV一区二区三区| 精品国产AⅤ一区二区三区4区 | 精品无码国产一区二区三区麻豆| 久久精品一区二区国产| 精品一区二区三区免费视频 | 亚洲色偷偷偷网站色偷一区| 国产精品一区二区久久不卡| 日韩一区二区三区在线观看| 国产99视频精品一区| 精品无码人妻一区二区三区品| 综合久久一区二区三区| 国产色精品vr一区区三区| 一区二区三区高清在线| 一区二区三区在线看| 国产A∨国片精品一区二区| 国产精品亚洲产品一区二区三区 | 国产精品免费视频一区| 国产成人一区二区三区精品久久| 亚洲字幕AV一区二区三区四区| 3d动漫精品一区视频在线观看| 无码少妇一区二区三区| 精品一区二区三区在线成人| 无码人妻精品一区二区蜜桃网站| 国产香蕉一区二区精品视频| 色一情一乱一区二区三区啪啪高| 97se色综合一区二区二区| 亚洲色一区二区三区四区| 精品不卡一区中文字幕| 夜夜精品无码一区二区三区| 国产成人无码一区二区三区在线 | 日韩视频一区二区在线观看|