您现在的位置是: 首页 > 成语教学 成语教学

chromium浏览器-chromeium

tamoadmin 2024-10-21 人已围观

简介背景作为一位前端同学肯定对vscode不陌生,相信每位同学电脑上也都有五花八门的个性化配置,那么我们是借助什么东西做到的呢?那就是它丰富的插件生态。本次将讲述插件基本原理并从一个简单的case了解如何制作一个的vscode插件是什么实现了vscodeElectronvscode底层通过electron开发实现,electron的核心构成分别是:chromium、nodejs、native-apiC

chromium浏览器-chromeium

背景

作为一位前端同学肯定对vscode不陌生,相信每位同学电脑上也都有五花八门的个性化配置,那么我们是借助什么东西做到的呢?那就是它丰富的插件生态。本次将讲述插件基本原理并从一个简单的case了解如何制作一个的vscode插件

是什么实现了vscodeElectron

vscode底层通过electron开发实现,electron的核心构成分别是:chromium、nodejs、native-api

Chromium(ui视图):通过web技术栈编写实现ui界面,其与chrome的区别是开放开源、无需安装可直接使用(可以简单理解chromium是beta体验版chrome,新特性会优先在chromium中体验并在稳定后更新至chrome中)。

Nodejs(操作桌面文件系统):通过node-gyp编译,主要用来操作文件系统和调用本地网络。

Native-API(操作系统纬度api):使用Nodejs-C++Addon调用操作系统API(Nodejs-C++Addon插件是一种动态链接库,采用C/C++语言编写,可以通过require()将插件加载进NodeJS中进行使用),可以理解是对Nodejs接口的能力拓展。

Electron多进程:

主进程(main):每一个Electron应用只会启动一个主进程。

渲染进程(render):主进程会通过Chromium的api创建任意多个web页面,每一个工作区(workbench)对应一个进程,同时是BrowserWindow实例,由于chromeium(chrome)也是多进程的,所以每个页面都单独运行在各自的渲染进程中。

例:

//主进程const{ipcMain}=require('electron');//主进程响应事件ipcMain.on('main_msg',(event,arg)=>{console.log(arg);//pingevent.reply('renderer-msg-reply','pong');})//渲染进程(子进程)const{ipcRenderer}=require('electron');//渲染进程响应事件ipcRenderer.on('renderer-msg-reply',(event,arg)=>{console.log(arg);//pong})//触发主进程响应事件ipcRenderer.send('main_msg','ping');

对于vscode还会有一些其他的进程,比如:

插件进程(Extension):fork渲染进程,每个插件都运行在一个NodeJS宿主环境中,即插件间共享进程

Debug进程:一个特殊的插件进程。

Search进程:搜索是密集型任务,单独占用一个进程。

。。。

通俗意义上,electron就是给你搞了一个Chrome浏览器的壳子,只是比传统网页多了一个访问桌面文件的功能。

vscode插件加载基本原理

插件的结构├──extensions----------------------------------vscode内置插件├──src│├──main.js--------------------------------入口文件│├──bootstrap-fork.js----------------------衍生子进程(渲染进程)│├──vs││└──workbench-------------------------工作台││├──base│││├──browser----------------------浏览器api,可操作dom│││├──common-----------------------公共js代码│││├──node-------------------------nodejsapi││├──code│││├──electron-browser-------------electron渲染进程│││├──electron-main----------------electron主进程插件加载过程初始化插件服务

在插件初始化构造函数中通过_initialize初始化插件服务。

//src/vs/workbench/services/extensions/electron-browser/extensionService.ts//通过监听生命周期函数,创建ExtensionHostManagerexportclassExtensionServiceextendsAbstractExtensionServiceimplementsIExtensionService{constructor(){this._lifecycleService.when(LifecyclePhase.Ready).then(()=>{//rescheduletoensurethisrunsafterrestoringviewlets,panels,andeditorsrunWhenIdle(()=>{this._initialize();//初始化插件服务},50/*maxdelay*/);});}}//src/vs/workbench/services/extensions/common/abstractExtensionService.ts//启动初始化插件服务方法protectedasync_initialize():Promise<void>{perf.mark('code/willLoadExtensions');this._startExtensionHosts(true,[]);//...}private_startExtensionHosts(isInitialStart:boolean,initialActivationEvents:string[]):void{//创建插件进程,分别为LocalProcessExtensionHost(本地插件,如个人插件)、RemoteExtensionHost(远程插件,如WSLRemote)、WebWorkerExtensionHost(webworker进程)constextensionHosts=this._createExtensionHosts(isInitialStart);extensionHosts.forEach((extensionHost)=>{//创建ExtensionHostManagerconstprocessManager:IExtensionHostManager=createExtensionHostManager(this._instantiationService,extensionHost,isInitialStart,initialActivationEvents,this._acquireInternalAPI());processManager.onDidExit(([code,signal])=>this._onExtensionHostCrashOrExit(processManager,code,signal));processManager.onDidChangeResponsiveState((responsiveState)=>{this._onDidChangeResponsiveChange.fire({isResponsive:responsiveState===ResponsiveState.Responsive});});this._extensionHostManagers.push(processManager);});}fork渲染进程

fork渲染进程,并加载extensionHostProcess。由于vscode考虑插件可能会影响启动性能和IDE自身的稳定性,所以通过进程隔离来解决这个问题,插件进程fork渲染进程,保证每个插件都运行在一个nodejs宿主环境中,不影响IDE及其启动时间。

//src/vs/workbench/services/extensions/common/extensionHostManager.ts//启动fork渲染进程classExtensionHostManagerextendsDisposable{constructor(){this._proxy=this._extensionHost.start()!.then();}}//src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.tsclassLocalProcessExtensionHostimplementsIExtensionHost{publicstart():Promise<IMessagePassingProtocol>|null{//...constopts={env:objects.mixin(objects.deepClone(process.env),{//加载插件进程,指明插件进程入口AMD_ENTRYPOINT:'vs/workbench/services/extensions/node/extensionHostProcess',}),}//衍生子进程(渲染进程)this._extensionHostProcess=fork(getPathFromAmdModule(require,'bootstrap-fork'),['--type=extensionHost'],opts);}}初始化插件激活逻辑//src/vs/workbench/services/extensions/node/extensionHostProcess.tsimport{startExtensionHostProcess}from"vs/workbench/services/extensions/node/extensionHostProcessSetup";startExtensionHostProcess().catch((err)=>console.log(err));//src/vs/workbench/services/extensions/node/extensionHostProcessSetup.tsexportasyncfunctionstartExtensionHostProcess():Promise<void>{constextensionHostMain=newExtensionHostMain(renderer.protocol,initData,hostUtils,uriTransformer);}//src/vs/workbench/services/extensions/common/extensionHostMain.tsexportclassExtensionHostMain{constructor(){//必须在创建extensionService之后再调用initialize,因为initialize本身会依赖extensionService的实例this._extensionService=instaService.invokeFunction(accessor=>accessor.get(IExtHostExtensionService));this._extensionService.initialize();}}插件激活//src/vs/workbench/api/node/extHost.services.tsimport{ExtHostExtensionService}from'vs/workbench/api/node/extHostExtensionService';//注册插件服务registerSingleton(IExtHostExtensionService,ExtHostExtensionService);

继承AbstractExtHostExtensionService

//src/vs/workbench/api/node/extHostExtensionService.tsexportclassExtHostExtensionServiceextendsAbstractExtHostExtensionService{//...}//src/vs/workbench/api/common/extHostExtensionService.tsabstractclassAbstractExtHostExtensionServiceextendsDisposable{constructor(){this._activator=this._register(newExtensionsActivator());}//根据activationEvent事件名激活插件,如onCommandprivate_activateByEvent(activationEvent:string,startup:boolean):Promise<void>{returnthis._activator.activateByEvent(activationEvent,startup);}}加载流程简单实战

背景:实现选择指定目录右键自动生成lynx页面基本目录结构的插件。

目标拆解:

选择自定义目录,添加右键点击菜单

输入lynx页面名称

按照模版生成对应文件

环境准备

nodejs

vscode

安装Yeoman和VSCodeExtensionGenerator

//渲染进程(子进程)const{ipcRenderer}=require('electron');//渲染进程响应事件ipcRenderer.on('renderer-msg-reply',(event,arg)=>{console.log(arg);//pong})//触发主进程响应事件ipcRenderer.send('main_msg','ping');0

初始化项目工程

//渲染进程(子进程)const{ipcRenderer}=require('electron');//渲染进程响应事件ipcRenderer.on('renderer-msg-reply',(event,arg)=>{console.log(arg);//pong})//触发主进程响应事件ipcRenderer.send('main_msg','ping');1具体实现//渲染进程(子进程)const{ipcRenderer}=require('electron');//渲染进程响应事件ipcRenderer.on('renderer-msg-reply',(event,arg)=>{console.log(arg);//pong})//触发主进程响应事件ipcRenderer.send('main_msg','ping');2

main:指定了插件的入口函数。

activationEvents:指定触发事件,当指定事件发生时才触发插件执行。需额外关注*这个特殊的插件类型,因为他在初始化完成后就会触发插件执行,并不需要任何自定义触发事件。

contributes:描述插件的拓展点,用于定义插件要扩展vscode哪部分功能,如commands命令面板、menus资源管理面板等。

声明指令

初始化插件项目成功后会看到上图的目录结构,其中我们需要重点关注src目录和package.json文件,其中src目录下的extension.ts文件为入口文件,包含activate和deactivate分别作为插件启动和插件卸载时的生命周期函数,可以将逻辑直接写在两个函数内也可抽象后在其中调用。

同时我们希望插件在适当的时机启动activate或关闭deactivate,vscode也给我们提供了多种onXXX的事件作为多种执行时机的入口方法。那么我们该如何使用这些事件呢?

事件列表

//渲染进程(子进程)const{ipcRenderer}=require('electron');//渲染进程响应事件ipcRenderer.on('renderer-msg-reply',(event,arg)=>{console.log(arg);//pong})//触发主进程响应事件ipcRenderer.send('main_msg','ping');3

如何使用这些事件呢?我们以onCommand为例。首先需要在package.json文件中注册activationEvents和commands。

//渲染进程(子进程)const{ipcRenderer}=require('electron');//渲染进程响应事件ipcRenderer.on('renderer-msg-reply',(event,arg)=>{console.log(arg);//pong})//触发主进程响应事件ipcRenderer.send('main_msg','ping');4

然后在extension.ts文件的activate方法中编写自定义逻辑。

//渲染进程(子进程)const{ipcRenderer}=require('electron');//渲染进程响应事件ipcRenderer.on('renderer-msg-reply',(event,arg)=>{console.log(arg);//pong})//触发主进程响应事件ipcRenderer.send('main_msg','ping');5添加目录右键点击事件//渲染进程(子进程)const{ipcRenderer}=require('electron');//渲染进程响应事件ipcRenderer.on('renderer-msg-reply',(event,arg)=>{console.log(arg);//pong})//触发主进程响应事件ipcRenderer.send('main_msg','ping');6唤起组件名称输入面板//渲染进程(子进程)const{ipcRenderer}=require('electron');//渲染进程响应事件ipcRenderer.on('renderer-msg-reply',(event,arg)=>{console.log(arg);//pong})//触发主进程响应事件ipcRenderer.send('main_msg','ping');7//渲染进程(子进程)const{ipcRenderer}=require('electron');//渲染进程响应事件ipcRenderer.on('renderer-msg-reply',(event,arg)=>{console.log(arg);//pong})//触发主进程响应事件ipcRenderer.send('main_msg','ping');8根据输入面板创建模版文件//渲染进程(子进程)const{ipcRenderer}=require('electron');//渲染进程响应事件ipcRenderer.on('renderer-msg-reply',(event,arg)=>{console.log(arg);//pong})//触发主进程响应事件ipcRenderer.send('main_msg','ping');9可优化点

增加模版类型

通过下载模版替代写入字符串文本

End

谢谢支持

以上便是本次分享的全部内容,希望对你有所帮助^_^

喜欢的话别忘了?分享、点赞、收藏?三连哦~。

欢迎关注公众号?ELab团队?收获大厂一手好文章~

我们来自字节跳动,是旗下大力教育前端部门,负责字节跳动教育全线产品前端开发工作。

我们围绕产品品质提升、开发效率、创意与前沿技术等方向沉淀与传播专业知识及案例,为业界贡献经验价值。包括但不限于性能监控、组件库、多端技术、Serverless、可视化搭建、音视频、人工智能、产品设计与营销等内容。

欢迎感兴趣的同学在评论区或使用内推码内推到作者部门拍砖哦?

字节跳动校/社招投递链接:

内推码:GDUVRJJ

原文:移动浏览器的开发会更加容易吗?—— Chrome 中的 content 模块

要说这给问题,可以的吧,一般设置里都有的,自己可以熟悉下,不过我用这么多浏览器的经验告诉我qq浏览器就能做到的9.0就能够做到这些的。

更新后的它的功能更加的完善了,速度比以前更加的快了,至少比别的要快,使用的Chromeiumv4.3的双内核,性能全面提升,大幅度优化卡顿问题,下载功能更加的强了,速度快,而且稳定,采用的冷启动速度,也更加的快了,瞬间释放内存,特别棒,自己用起来非常方便,好用,没有那么多的麻烦的是事情了。

因为目前看,多进程沙盒模型会是浏览器的趋势所在,这样可能会使得浏览器的竞争出现一些变化。Chromium 是个开源项目,从道义上讲 Google 这么做是满足人心的,最重要的,我认为这么做和 Google 的根本利益没有产生冲突,相反,从打造更完善的 Web 平台角度来讲,这么做是服务于 Google 的长期利益的。以下翻译了Content 组件Content 模块 High-Level Overview"content" 的代码在 src\content, 是使用多进程沙盒浏览器模型渲染页面的所需要的核心代码。它包含Web平台所有的功能(如HTML5) 和 GPU 加速. 但是它并不包括 Chrome 的功能,如 extensions,autofill,spelling 等。目的是让嵌入者可以从 content 开始构建一个浏览器,然后再从Chrome中选取功能点。为什么会有 content随着Chromeium 代码的增长,许多功能不可避免的被放到了错误的位置,导致软件层次之间产生本不应存在的冲突和依赖。就连开发人员也很难指出什么是最好的方式了,因为这些API(当他们存在是)和和功能代码都被放在相同的目录下了。为了避免这个的继续发生,同时也为了更加清晰的吧多进程渲染的核心代码隔离开来,大家最终达成一致意见,将Chrome的核心代码移到 src\content 目录下(并不是 chrome)。Content 和 Chrome 的关系从上述讨论可以看出, content 应该只包含渲染页面的核心代码。Chrome 功能使用 content 提供的 APIs 来接受 IPC 消息 和 他们所需事件的 notify。 How to Add New Features (without bloating RenderView/RenderViewHost/WebContents) 描述了如何做。比如,下表罗列了一些只属于 Chrome 的功能,他们并不在 content 中。这意味着 content 的代码不应该需要了解这些功能的任何信息,只需要提供这些功能所依赖的普通 APIs 。ExtensionsChromeFrameSpellCheckAutofillPrerenderingSafe BrowsingTranslate对于一定要供应商提供的在线网络服务,这部分交互功能希望完全在 content 模块之外实现。比如上面列表中的 Safe Browsing, Translate, Sync 和 Autofill 都需要各种不同的网络服务才能够工作, chrome 层是封装这些功能的最好地方。对那些个别的案例,我们需要在 content 模块中添加网络服务代码来实现 HTML5 功能,嵌入者(chrome)必须完整定义要连接的端点,典型地可以通过注入服务的 URL。我们不不希望这些策略性的代码被添加到 content,再强调一下,保持 content 的通用性。架构图下图展示了各个模块所在的层次关系。一个模块可以包含更低层次的模块代码,但是不能包含比他层次更高的代码。这一点通过依赖规则强制保证。每个模块都可以实现内嵌的 APIs 来方便底层模块可以通过这些API来调用他们。比如 WebKit API 和 content API。Content APIcontent 中的代码可以通过Content API间接调用 Chrome。 当可能的时候,Chome 的功能试图通过过滤 IPC 消息和监听事件的方式 hook 进来,参考 How to Add New Features (without bloating RenderView/RenderViewHost/WebContents) 。当上下文不全的时候(比如缺少从WebKit返回的回调)或者当回调只是一次性的,我们还可以通过嵌入者( chrome )实现ContentClient 接口。ContentClient 在所有进程中都可以使用, 一些进程还可以拥有自己的回调API,比如 ContentBrowserClient, ContentRendererClient, ContentPluginClient.当前状态和路线图当前content 没有依赖于 chrome。现在我们有一个基于 content 编译出来的基本的浏览器 content shell,它可以在所有平台上使用 content 来渲染页面。 这样就允许工作在 web 平台和核心代码的发人员只需要构建、测试 content 而不是 chrome 了。我们还有 content 的单元测试 content_unittests 和集成测试 content_browsertests。 content 是以一个动态链接库的形式构建的,所以可以加快构建的过程。 类似于 WebKit API,我们已经围绕 content 封装了一层 API。这样就隔离了嵌入者(chrome)和 content 的内部工作,并且使得实现那些嵌入者调用 content 内部方法的工作变得更加清晰。 Content API 的工作已经接近尾声了。 当这件事情做完之后,它也会对隔离 chrome 功能之间的依赖关系有所帮助。比如说,如果有人打算在 content 之上新增 拼写检查和翻译的功能,他们应该可以在不需要扩展的情况下完成构建。