最新要闻
- 动态焦点:暴雪:《暗黑》系列能成功多亏了韩国玩家热情和爱戴
- 全球观点:朱雀二号遥一运载火箭发射失利:已查明飞行故障 通过归零评审
- 全球热头条丨《雷霆沙赞2》豆瓣开分6.5:加朵女神加分、剧情被批幼稚低级
- 张兰被曝国外欠债9.8亿,海外家庭信托被追债,拼命带货疑为还债
- 肯德基全家桶被曝吃出生的炸鸡!店家回应是锅出现故障
- 世界观速讯丨8万元会成爆款吗?宝骏悦也实车曝光:像吉姆尼、能跑303公里
- 每日热文:印度男子因新娘高三成绩不好要求退婚 还要退5千彩礼:网友看笑
- 热门看点:女生被拍同学勇敢对峙让男子删除 想保护好自己的朋友:网友称赞勇敢
- 1.5mm!iPhone 15 Pro Max将打破最薄边框纪录:CAD外观渲染图曝光 更帅了
- 全球微头条丨没有科技与狠活 :依能天然苏打水2.3元发车 无糖无气0卡
- 03月18日09时福建漳州疫情数据 阳了以后为什么会腰疼?应该怎么办?
- 天天亮点!汽车降价潮蔓延!成都豪撒1亿购车补贴 汽车流通协会称武汉汽车降价不公平
- 【天天热闻】俞敏洪称下辈子宁愿当没钱的流浪汉:自己周围的企业家都在没日没夜的干活
- 比亚迪出海再下一城!乌兹比克斯坦三车齐发:宋PLUS 22万起售
- 【全球速看料】差距有多大?一图看懂蔚来、小鹏、理想汽车2022年第四季度财报:老大变了
- 每日速读!水晶球档杆绝无仅有!韩系豪华电动车捷尼赛思GV60上市:28.58万起
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
报道:插件化架构设计(2):插件化从设计到实践该考量的问题汇总
根据《插件式可扩展架构设计心得》精读扩展版
(资料图)
怎么实现插件化模式
插件模式本质是一种设计思想,并没有一个一成不变或者是万金油的实现。但我们经过长期的代码实践,其实已经可以总结出一套方法论来指导插件体系的实现,并且其中的一些实现细节是存在社区认可度比较高的“最佳实践”的。
插件化架构定义
插件化架构又称微核架构,指的是软件的内核相对较小,主要功能和业务逻辑都通过插件实现。插件化架构一般有两个核心的概念:内核和插件。
内核(pluginCore)通常只包含系统运行的最小功能;
插件(plugin)则是互相独立的模块,一般只会提供单一的功能。
内核除了插件的管理功能,还会将要完成的所有业务进行抽象,抽象出最小粒度的基础接口,供插件方来调用。这样,插件开发的效率将会极大的提高。比方说,浏览器就是一个典型的插件化架构,浏览器是内核,页面是插件,这样通过不同的URL地址加载不同的页面,来提供非常丰富的功能。而且,我们开发网页的时候,浏览器会提供很多API和能力,这些接口通过 window来挂载, 比如,DOM、BOM、Event、Location等等。
插件三要素
设计一个完善的插件化架构的系统,包含三要素:
plugCore:插件内核,提供插件运行时,管理插件的加载、运行、卸载等生命周期(类比浏览器);
pluginAPI:插件运行时需要的基础接口(类比浏览器例子,相当于window);
plugin:相互独立的模块,提供了单一的功能(类比浏览器例子,相当于不同的网页)。
怎么把系统拆解为插件三要素?
实现一套插件模式的第一步,永远都是先定义出你需要插件化来帮助你解决的问题是什么。
这往往是具体问题具体分析的,并总是需要你对当前系统的能力做一定程度的抽象。
解决问题前首先要定义问题
比如 Babel,他的核心功能是将一种语言的代码转化为另一种语言的代码,他面临的问题就是,他无法在设计时就穷举语法类型,也不了解应该如何去转换一种新的语法,因此需要提供相应的扩展方式。为此,他将自己的整体流程抽象成了parse、transform、generate 三个步骤,并主要面向 parse 和 transform 提供了插件方式做扩展性支持。
在 parse 这层,他核心要解决的问题是怎么去做分词,怎么去做词义语法的理解。
在 transform 这层要做的则是,针对特定的语法树结构,应该如何转换成已知的语法树结构。
很明显,babel 他很清楚地定义了 parse 和 transform 两层的插件要完成的事情。
当然也有人可能会说,为什么我一定要定义清楚问题呢,插件体系本来就是为未来的不确定性服务的。这样的说法对,也不对。
计算机程序永远是面向确定性的,我们需要有明确的输入格式,明确的输出格式,明确的可以依赖的能力。
解决问题一定是在已知的一个框架内的。这就引出了定义问题的一门艺术——如何赋予不确定以确定性,在不确定中寻找确定。
说人话,就是“抽象”,这也是为什么最开始我会以过度设计作为引子。
Babel 主要解决的问题是把新语法的代码在不改变逻辑的情况下如何转换成旧语法的代码,简单来说就是 code => code 的一个问题。
但是需要转什么,怎么转,这些是会随着语法规范不断更新变化的,因此需要使用插件模式来提升其未来可拓展性。
我们当下要解决的问题也许是如何转换 es6 新语法的内容,以及 JSX 这种框架定制的 DSL。我们当然可以简单地串联一系列的正则处理,但是你会发现每一个插件都会有大量重复的识别分析类逻辑,不但加大了运行开销,同时也很难避免互相影响导致的问题。
Babel 选择了把解析与转换两个动作拆开来,分别使用插件来实现。解析的插件要解决的问题是如何解析代码,把 Code 转化为 AST。这个问题对于不同的语言又可以拆解为相同的两个事情,如何分词,以及如何做词义解析。当然词义解析还能是如何构筑上下文、如何产出 AST 节点等等,就不再细分了。最终形成的就是下图这样的模式,插件专注解决这几个细分问题。转换这边的,则可分为如何查找固定 AST 节点,以及如何转换,最终形成了 Visitor 模式,这里就不再详细说了。那么我们再思考一下,如果未来 ES7、8、9(相对于设计场景的未来)等新语法出炉时,是不是依然可以使用这样的模式去解决问题呢?看起来是可行的。
这就是前面所说的在不确定中寻找确定性,尽可能减少系统本身所面临的不确定,通过拆解问题去限定问题。
插件核心所包含内容
插件调度
基础组件(引用自建或第三方)
基础服务
http服务
数据格式转换
时间转换等
事件总线
其它
自动加载插件文件(异步)
插件系统核心作为插件的环境依赖,为插件提供基本的服务、插件调度、事件和其他的一些基本功能。
插件之间的相互调用可以通过插件核心进行传递,且这个调用是解耦的,不影响插件内部的逻辑。
插件核心所提供的基础组件、业务组件来自于外部依赖,所以插件核心是一个相对精简的核心,可以通过外部依赖来扩展插件核心的功能。
插件化架构可以链接多个子系统,而做到开闭原则。
即插件核心和接口不变,系统可以持续接入新插件,来丰富系统的功能。
由于新接入的插件是一个独立的子系统,它可独立开发,运行和进行版本管理,不会因为接入的系统复杂而增加接入成本。试想,在一个非插件化的系统中,业务系统就算经过良好设计,随着功能模块的增多,代码量激增,暂且不考虑系统构建和测试,我们想要给系统加入新的功能,甚至是修复已有功能的BUG,都会越来越困难和低效,但插件化架构的系统,增加新功能,不是在一个庞大系统代码库中,而是在一个较小的系统或代码仓库中,因此不管已有系统多复杂,开发新的功能的接入复杂度始终一样。同时,开发编译或修复测试一个插件,也比针对整个系统要简单得多。
插件式开发架构要领
不管基于何种语言进行插件式开发框架的设计,有一些共同的要点需要具备。
插件运行主体
基于插件模式进行开发的软件,一般会存在一个运行主体。这个载体作为应用的主入口,并根据各类插件的配置信息,将编译或打包后的插件加载到主体环境中并执行。开发新的插件,无需调整现有运行主体的代码和二进制包。
插件的注入、配置和初始化
插件配置信息
配置信息即插件的描述信息,可以在代码中设置,也可以通过XML文件实现,方式不同,目的一致。
插件名称
插件版本号
插件描述信息
依赖的其他插件清单
插件的注入及初始化
插件的注入及初始化一般借助于继承插件基类,并实现插件框架中指定好的标准接口。通过继承插件基类,实现插件的注入;通过实现标准的初始化、启动、关闭等标准接口,实现插件的生命周期管理工作。
插件通信机制
插件通信机制是一种通用概念。当各插件间协同完成一个功能时,彼此进行协调互助的一种机制。交互的形式有很多种,一种是插件对外开放自己的接口
基于虚拟服务总线形式的通信机制
基于虚拟服务总线形式的通信机制,每个插件都有自己的开放接口,这些接口会被注册到虚拟服务总线上,其他插件通过虚拟服务总线,获取到其他插件的接口服务。此处涉及到的内容是面向接口编程。
插件间消息通信
插件间消息通信属于一种开发人员可以自定义的扩展方式,插件运行主体无法定义所有的消息类型及消息的处理方法。
所以用户可以通过约定消息形式以及自定义消息响应函数,实现插件间的通信。但是这样其实增强了插件之间的耦合度,不是特别推荐。笔者建议应用层插件尽量只依赖通用服务型插件及主体运行程序,业务插件保持独立。
插件架如何协同-调度问题
当正式开始设计我们的插件架构时,我们所要思考的问题往往离不开以下几点。整个设计过程其实就是为每一点选择合适的方案,最后形成一套插件体系。这几点分别是:
如何注入、配置、初始化插件
插件如何影响系统
插件输入输出的含义与可以使用的能力
复数个插件之间的关系是怎么样的
面就针对每个点详细解释一下
如何注入、配置、初始化插件
注入、配置、初始化其实是几个分开的事情。但都同属于 Before 的事情,所以就放在一起讲了。
注入
注入,其实本质上就是如何让系统感知到插件的存在。
注入的方式一般可以分为声明式 和 编程式。
声明式就是通过配置信息,告诉系统应该去哪里去取什么插件,系统运行时会按照约定与配置去加载对应的插件。类似 Babel,可以通过在配置文件中填写插件名称,运行时就会去 modules 目录下去查找对应的插件并加载。
编程式的就是系统提供某种注册 API,开发者通过将插件传入 API 中来完成注册。
两种对比的话
声明式主要适合自己单独启动不用接入另一个软件系统的场景,这种情况一般使用编程式进行定制的话成本会比较高,但是相对的,对于插件命名和发布渠道都会有一些限制。
编程式则适合于需要在开发中被引入一个外部系统的情况。当然也可以两种方式都进行支持。
配置
然后是插件配置,配置的主要目的是实现插件的可定制,因为一个插件在不同使用场景下,可能对于其行为需要做一些微调,这时候如果每个场景都去做一个单独的插件那就有点小题大作了。配置信息一般在注入时一起传入,很少会支持注入后再进行重新配置。
初始化
配置如何生效其实也和插件初始化的有点关联,初始化这事可以分为方式和时机两个细节来讲,我们先讲讲方式。常见的方式我大概列举两种。
工厂模式,一个插件暴露出来的是一个工厂函数,由调用者或者插件架构来将提供配置信息传入,生成插件实例。
运行时传入,插件架构在调度插件时会通过约定的上下文把配置信息给到插件。
工厂模式模式初始化
拿 babel 来举例吧。
functiondeclare,Rextendsbabel.PluginObj=babel.PluginObj>(builder:(api:BabelAPI,options:O,dirname:string)=>R,):(api:object,options:O|null|undefined,dirname:string)=>R;
上面代码中的 builder 呢就是我们说到的工厂函数了,他最终将产出一个 Plugin 实例。builder 通过 options 获取到配置信息,并且这里设计上还支持通过 api 设置一些运行环境信息,不过这并不是必须的,所以不细说了。简化一下就是:
typeTPluginFactory=(options:OPTIONS)=>PLUGIN;
所以初始化呢,自然也可以是通过调用工厂函数初始化、初始化完成后再注入、不需要初始化三种。一般我们不选择初始化完成后再注入,因为解耦的诉求,我们尽量在插件中只做声明。是否使用工厂模式则看插件是否需要初始化这一步骤。大部分情况下,如果你决定不好,还是推荐优先选择工厂模式,可以应对后面更多复杂场景。初始化的时机也可以分为注入即初始化、统一初始化、运行时才初始化。很多情况下 注入即初始化、统一初始化 可以结合使用,具体的区分我尝试通过一张表格来对应说明:
注入即初始化 | 统一初始化 | 运行时才初始化 | |
---|---|---|---|
是否是纯逻辑型 | 都可以使用 | 是 | |
是否需要预挂载或修改系统 | 是 | 不是 | |
插件初始化是否有相互依赖关系 | 不是 | 是 | 不是 |
插件初始化是否有性能开销 | 都可以使用 | 不是 |
另外还有个问题也在这里提一下,在一些系统中,我们可能依赖许多插件组合来完成一件复杂的事情,为了屏蔽单独引入并配置插件的复杂性,我们还会提供一种 Preset 的概念,去打包多个插件及其配置。使用者只需要引入 Preset 即可,不用关心里面有哪些插件。例如 Babel 在支持 react 语法时,其实要引入 syntax-jsx transform-react-jsx transform-react-display-name transform-react-pure-annotationsd 等多个插件,最终给到的是 preset-react这样一个包。
插件如何影响系统
插件对系统的影响我们可以总结为三方面:行为、交互、UI。
UI:我们通过系统 API 创建了一个状态栏组件。我们通过配置信息构建了一个 配置页。
交互:我们通过注册命令,增加了一项指令交互。
逻辑:我们新增了一项插入当前时间的能力逻辑。
单独一个插件可能只涉及其中一点。根据具体场景,有些方面也不必去影响,比如一个逻辑引擎类型的系统,就大概率不需要展示这块的东西啦。
所以我们在设计一个插件架构时呢,也主要就从这三方面是否会被影响考虑即可。那么插件又怎么去影响系统呢,这个过程的前提是插件与系统间建立一份契约,约定好对接的方式。这份契约可以包含文件结构、配置格式、API 签名。还是结合 VSCode 的例子来看看:
文件结构:沿用了 NPM 的传统,约定了目录下 package.json 承载元信息。
配置格式:约定了 main 的配置路径作为代码入口,私有字段 contributes 声明命令与配置。
API 签名:约定了扩展必须提供 activate 和 deactivate 两个接口。并提供了 vscode 下各项 API 来完成注册。
UI 和 交互的定制逻辑,本质上依赖系统本身的实现方式。这里重点讲一下一般通过哪些模式,去调用插件中的逻辑。
直接调用
这个模式很直白,就是在系统的自身逻辑中,根据需要去调用注册的插件中约定的 API,有时候插件本身就只是一个 API。比如上面例子中的 activate 和 deactivate 两个接口。这种模式很常见,但调用处可能会关注比较多的插件处理相关逻辑。
钩子机制(事件机制)
系统定义一系列事件,插件将自己的逻辑挂载在事件监听上,系统通过触发事件进行调度。上面例子中的 clock.insertDateTime 命令也可以算是这类,是一个命令触发事件。在这个机制上,webpack 是一个比较明显的例子,我们来看一个简单的 webpack 插件:
//一个JavaScript命名函数。functionMyExampleWebpackPlugin(){};//在插件函数的prototype上定义一个`apply`方法。MyExampleWebpackPlugin.prototype.apply=function(compiler){//指定一个挂载到webpack自身的事件钩子。compiler.plugin("webpacksEventHook",function(compilation/*处理webpack内部实例的特定数据。*/,callback){console.log("Thisisanexampleplugin!!!");//功能完成后调用webpack提供的回调。callback();});};
这里的插件就将“在 console 打印 This is an example plugin!!!”这一行为注册到了 webpacksEventHook 这个钩子上,每当这个钩子被触发时,会调用一次这个逻辑。这种模式比较常见,webpack 也专门做了一份封装服务这个模式,https://github.com/webpack/tapable。通过定义了多种不同调度逻辑的钩子,你可以在任何系统中植入这款模式,并能满足你不同的调度需求(调度模式我们在下一部分中详细讲述)。
const{SyncHook,SyncBailHook,SyncWaterfallHook,SyncLoopHook,AsyncParallelHook,AsyncParallelBailHook,AsyncSeriesHook,AsyncSeriesBailHook,AsyncSeriesWaterfallHook}=require("tapable");
钩子机制适合注入点多,松耦合需求高的插件场景,能够减少整个系统中插件调度的复杂度。成本就是额外引了一套钩子机制了,不算高的成本,但也不是必要的。
使用者调度机制
这种模式本质就是将插件提供的能力,统一作为系统的额外能力对外透出,最后又系统的开发使用者决定什么时候调用。
例如 JQuery 的插件会注册 fn 中的额外行为,或者是 Egg 的插件可以向上下文中注册额外的接口能力等。这种模式我个人认为比较适合又需要定制更多对外能力,又需要对能力的出口做收口的场景。如果你希望用户通过统一的模式调用你的能力,那大可尝试一下。你可以尝试使用新的 Proxy 特性来实现这种模式。
不管是系统对插件的调用还是插件调用系统的能力,我们都是需要一个确定的输入输出信息的,这也是我们上面 API 签名所覆盖到的信息。我们会在下一部分专门讲一讲。
插件输入输出的含义与可以使用的能力
插件与系统间最重要的契约就是 API 签名,这涉及了可以使用哪些 API,以及这些 API 的输入输出是什么。
可以使用的能力
是指插件的逻辑可以使用的公共工具,或者可以通过一些方式获取或影响系统本身的状态。能力的注入我们常使用的方式是参数、上下文对象或者工厂函数闭包。
提供的能力类型主要有下面四种:
纯工具:不影响系统状态
获取当前系统状态
修改当前系统状态
API 形式注入功能:例如注册 UI,注册事件等
对于需要提供哪些能力,一般的建议是根据插件需要完成的工作,提供最小够用范围内的能力,尽量减少插件破坏系统的可能性。在部分场景下,如果不能通过 API 有效控制影响范围,可以考虑为插件创造沙箱环境,比如插件内可能会调用 global 的接口等。
输入输出
当我们的插件是处在我们系统一个特定的处理逻辑流程中的(常见于直接调用机制或钩子机制),我们的插件重点关注的就是输入与输出。此时的输入与输出一定是由逻辑流程本身所处的逻辑来决定的。输入输出的结构需要与插件的职责强关联,尽量保证可序列化能力(为了防止过度膨胀以及本身的易读性),并根据调度模式有额外的限制条件(下面会讲)。如果你的插件输入输出过于复杂,可能要反思一下抽象是否过于粗粒度了。
另外还需要对插件逻辑保证异常捕捉,防止对系统本身的破坏。
还是 Babel Parser 那个例子。
{parseExprAtom(refExpressionErrors:?ExpressionErrors):N.Expression;getTokenFromCode(code:number):void;//内部再调用finishToken来影响逻辑updateContext(prevType:TokenType):void;//内部通过修改this.state来改变上下文信息}
意料之中的输入,坚信不疑的输出
复数个插件之间的关系是怎么样的
Each plugin should only do a small amount of work, so you can connect them like building blocks. You may need to combine a bunch of them to get the desired result.
这里我们讨论的是,在同一个扩展点上注入的插件,应该以什么形式做组合。常见的形式如下:
覆盖式
只执行最新注册的逻辑,跳过原始逻辑
管道式
输入输出相互衔接,一般输入输出是同一个数据类型。
洋葱圈式
在管道式的基础上,如果系统核心逻辑处于中间,插件同时关注进与出的逻辑,则可以使用洋葱圈模型。
这里也可以参考 koa 中的中间件调度模式https://github.com/koajs/compose
constmiddleware=async(...params,next)=>{//beforeawaitnext();//after};
集散式
集散式就是每一个插件都会执行,如果有输出则最终将结果进行合并。这里的前提是存在方案,可以对执行结果进行 merge。
另外调度还可以分为 同步 和 异步 两个方式,主要看插件逻辑是否包含异步行为。同步的实现会简单一点,不过如果你不能确定,那也可以考虑先把异步的一起考虑进来。类似https://www.npmjs.com/package/neo-async这样的工具可以很好地帮助你。如果你使用了 tapble,那里面已经有相应的定义。
另外还需要注意的细节是:
顺序是先注册先执行,还是反过来,需要给到明确的解释或一致的认知。
同一个插件重复注册了该怎么处理。
总结
当你跟着这篇文章的思路,把这些问题都思考清楚之后,想必你的脑海中一定已经有了一个插件架构的雏形了。剩下的可能是结合具体问题,再通过一些设计模式去优化开发者的体验了。个人认为设计一个插件架构,是一定逃不开针对这些问题的思考的,而且只有去真正关注这些问题,才能避开炫技、过度设计等面向未来开发时时常会犯的错误。当然可能还差一些东西,一些推荐的实现方式也可能会过时,这些就欢迎大家帮忙指正啦。
参考文章:
插件化结构利与弊https://blog.csdn.net/xfxyy_sxfancy/article/details/44274379
精读《插件化思维》https://zhuanlan.zhihu.com/p/35997606
插件式开发架构综述https://www.modb.pro/db/131158
转载本站文章《插件化架构设计(2):插件化从设计到实践该考量的问题汇总》,请注明出处:https://www.zhoulujun.cn/html/webfront/engineer/Architecture/8907.html
关键词:
世界热讯:Linux学习笔记
报道:插件化架构设计(2):插件化从设计到实践该考量的问题汇总
【天天新要闻】Vins 前端中高效的去畸变的方式解析
动态焦点:暴雪:《暗黑》系列能成功多亏了韩国玩家热情和爱戴
全球观点:朱雀二号遥一运载火箭发射失利:已查明飞行故障 通过归零评审
全球热头条丨《雷霆沙赞2》豆瓣开分6.5:加朵女神加分、剧情被批幼稚低级
【全球独家】万字血书Vue—Vue的核心概念
张兰被曝国外欠债9.8亿,海外家庭信托被追债,拼命带货疑为还债
Ocelot使用与设置路由Routing
环球速递!arthas排查线上问题真是太好用了!
肯德基全家桶被曝吃出生的炸鸡!店家回应是锅出现故障
世界快播:C++ class struct
环球视点!Windows OpenGL ES 图像 GPUImageLookupFilter
世界观速讯丨8万元会成爆款吗?宝骏悦也实车曝光:像吉姆尼、能跑303公里
每日热文:印度男子因新娘高三成绩不好要求退婚 还要退5千彩礼:网友看笑
世界观焦点:CSS学习笔记
热门看点:女生被拍同学勇敢对峙让男子删除 想保护好自己的朋友:网友称赞勇敢
1.5mm!iPhone 15 Pro Max将打破最薄边框纪录:CAD外观渲染图曝光 更帅了
全球微头条丨没有科技与狠活 :依能天然苏打水2.3元发车 无糖无气0卡
03月18日09时福建漳州疫情数据 阳了以后为什么会腰疼?应该怎么办?
当前要闻:为什么文件删除了但磁盘空间没有释放?
微博图床被废,自己动手丰衣足食。
【聚看点】Source Generator初探
4、AOP
天天亮点!汽车降价潮蔓延!成都豪撒1亿购车补贴 汽车流通协会称武汉汽车降价不公平
【天天热闻】俞敏洪称下辈子宁愿当没钱的流浪汉:自己周围的企业家都在没日没夜的干活
比亚迪出海再下一城!乌兹比克斯坦三车齐发:宋PLUS 22万起售
世界聚焦:调查显示民众预期英国央行将继续加息
【全球速看料】差距有多大?一图看懂蔚来、小鹏、理想汽车2022年第四季度财报:老大变了
每日速读!水晶球档杆绝无仅有!韩系豪华电动车捷尼赛思GV60上市:28.58万起
世界速讯:你对Linux窗口管理程序Tmux了解吗
【全球聚看点】还买什么汉兰达!全新大七座SUV福特锐界L开售:22.98万历史新低
【世界速看料】超市6500元招聘引学生排队投简历 负责人:已收到五十多份
世界看点:南宁市2023年事业单位统一考试简章发布 337个岗位共招1764人
【当前独家】读Java性能权威指南(第2版)笔记20_垃圾回收G
何小鹏:王凤英一周工作七天、让大家很卷
何小鹏谈竞争对手降价:油车一定会反击、小鹏将降低25%生产成本
springboot跨域问题解决方案
天天亮点!听闻索尼PS5 Pro主机明年发售后:老玩家们集体不干了
环球通讯!日本女大胃王菅原初代患肠癌病逝:曾10分钟吞399碗荞麦面
全球热点评!美国一核电站承认150万升核污染水泄漏:已隐瞒数月
导演郭帆都看不下去!众筹1亿的《流浪地球2》周边 为啥要偷工减料?
说句话就能做表格、PPT!微软把GPT-4塞进办公套件 我慌了
每日热门:喉结左侧有个硬疙瘩_左侧睾丸里有个小疙瘩是什么
AIGC的下一站是什么?
速读:vue2前端导出带背景色表格 xlsx xlsx-style
环球新资讯:Attention与SelfAttention
五角大楼官员表示:太阳系中可能存在外星母舰探测地球
环球消息!每日机构分析:3月17日
fiddler:The system proxy was changed.Click to reenable capturing
iPhone 15 Pro Max屏幕边框窄爆:将打破小米13纪录
99元 联想YOGA新款M5无线鼠标上架:鹅卵石设计
张裕葡小萄赤霞珠甜红葡萄酒2支到手39.9元:酒香浓郁
世界热讯:手机百度文库的下载券怎么用啊 为什么(百度文库不能用下载券)
网红店半天妖烤鱼被曝垃圾桶捞回食材上桌!合肥市监局:全市门店停业
天天微头条丨赛博2077支持DLSS3 iGame RTX 40显卡实战:性能2倍提升
河南三月飞雪 突降大雪竟与人工增雨有关
中金所就30年期国债期货合约征求意见
世界观速讯丨铭瑄发布旗舰级MGG RTX 4080、4070 Ti:丧心病狂5风扇、9热管
【焦点热闻】AMD份额涨不动了 专家称Intel的麻烦已结束:CPU竞争力更强
每日视点!山东一公司疑设卑劣人员从业跟踪岗:你去哪我就发函到哪
微头条丨铲屎官注意!研究表明养宠物或影响睡眠
热点评!你买过大船货吗?男子电动滑板车藏84个SSD入境被海关查获
全球新消息丨如何在Docker下部署nacos并注册Java服务
每日讯息!记录--vue中封装一个右键菜单组件(复制粘贴即可使用)
【新要闻】一汽奔腾销量惨淡,靠预售23.58万起的奔腾M9有望逆转吗?
天天快资讯:车主注意:新一轮国内油价降了!加满一箱油将少花4元
焦点要闻:全球最大集装箱船将在宁波舟山港开启首航:比航母还长
今日热讯:接二连三胜!长三乙火箭成功发射高分十三号02星
环球快资讯:《三体》动画播放量破5亿!豆瓣评分暴跌至3.9 差评率高达86%
票房已突破45亿!《满江红》宣布密钥再次延期
全球热门:“315”曝光:带货直播间水军泛滥,该如何应对?
天天观速讯丨债市日报:3月17日
消防车被小车挡道 业主联系未果合力掀翻!车主不干了
滚动:让越野车无路可走!极氪001成功挑战原路虎越野基地
世界热门:座椅加热1299元!smart回应硬件订阅:预埋硬件算送的 成本没进卖车价
环球快播:何炅再提《快乐大本营》:引发网友感慨
全球实时:1个案例读懂——游戏产品如何用 A/B 测试做增长
当前消息!Tailwind CSS 备忘清单_开发速查表分享
python __new__方法与单例模式
【当前热闻】前端有边界,但低代码没有
【快播报】Application Loader及Transporter App上传ipa外、可以在Windows上架iOS APP工具
当前播报:腊肉的保质期多长?
法兴银行:欧洲央行加息对欧元区国家息差的影响越来越小
支持小米、OPPO!三星查询手机OLED屏幕网站上线:你用的啥屏?
通讯!第二代骁龙7+首次支持双5G双卡双通:4.4Gbps网速、Wi-Fi翻倍
观察:性能提升达2倍!真我GT Neo5 SE官宣搭载第二代骁龙7+
索尼背水一战!PS5 Pro已在路上
环球今热点:刹车失灵?福特全球召回近130万辆汽车
世界热资讯!使用代理以及搭建代理池
河南突降大雪:农户40亩葡萄棚被压塌、郑州多个仓库倒塌损失惨重
天天快看点丨卢伟冰现身高通发布会!Redmi全球首发第二代骁龙7+
动态焦点:性能提升2倍!高通正式发布第二代骁龙7+移动平台
环球快消息!前端使用EasyWasmPlayer.js接入hls协议h265编码视频
当前速讯:[EF Core] EF Core Code-First 移除外键 —— 重写SQL生成器
当前快看:带发行版 Logo 的系统信息显示工具
天天快报!歌曲.在我心里没有谁能够代替你原唱_在我心里没有谁能代替你简谱歌谱
文心一言申请测试企业达7.6万 百度股价上涨近15%
锦心似玉林世显是什么身份?锦心似玉林世显的出场有什么作用吗?
人世间冯化成哪集出轨的?人世间冯化成最后的结局是什么?