利益无关,刚好看到umi3发布了,其中的微内核特性挺吸引我的,于是就趁机看了一下,一探究竟。最近一次听说微内核还是鸿蒙系统,虽然还没看到影子。。。
umi3的代码组织的主要核心就是插件体系,插件驱动整个内核运转,跟webpack有点像,但是又借鉴了babel的配置方式,有preset和plugin,其中preset里面又可以嵌套preset和plugin,在运行的时候会同时扫描umi配置文件、传入的opts配置、pkg的配置、process.env等等去读取插件,umi3从编译、打包到cli的运转都是通过运行插件去完成这一些的功能,对于它自身的核心包而言,就是负责去管理注册调度这些插件,你可以认为它整合核心是非常小的,离开了插件,甚至很多内置的api没办法调用。
umi3的插件完成的功能非常多,同时也管理很好。整个插件的调用管理采用状态机的方式,就是你在当前阶段只能完成某些操作,例如注册plugin和preset就只能在插件注册和pereset注册的阶段,其他情况下是不允许注册的。
插件还可以校验配置,校验的配置是挂载在插件下的,这样具体的插件文件只能加一个校验配置,虽然有些限制,但是我们可以配置多个插件啊,逃:)。配置的校验是在读取配置的时候,遍历所有插件,然后根据当前配置的key去查看当前插件是否有校验配置,如果有,就进行配置的校验。在内部初始化插件的时候,会调用插件方法,插件方法本身可能会通过注入hook和method等,其中注入的hook是至关重要的,因为在applyPlugin的时候,说白了,就是去触发我们通过插件注册的hook。其中,触发的时候,会因为触发类型的不同,例如有add、modify、event三种,不同类型触发hook的方式会导致相同key的hooks(复数)调用的方式不同,可能concat返回值或者modify等等,这些借助于webpack的tapable去组织。
例如,umi dev这个命令,就是注册dev的command,然后在你调用umi dev的时候,调用你传入的方法,去初始化.umi文件夹,调用bundler编译等等。除了内置插件外,其他功能,你都可以通过添加插件或者自己编写插件去完成。
再讲两点,在umi3的新版本里面看到两个特性,支持 路由组件的导出扩展属性和css智能模块化,这里就顺便再讲下。
umi3中css可以智能被是否需要模块化,原理很简单,通过ast分析,如下的代码中import中,specifiers的长度不为空,其中一项为style,那么我们需要改造一下source
import style from '.a.scss' 复制代码
在source.value添加modules的loader,source.value = ${value}?${opts.flag || 'modules'}
;,改造之后的代码如下,默认是modules,当然我们还可以传入opts.flag传入自己自定义的loader
import style from '.a.scss?modules' 复制代码
比如按照以下这么写,路由上会多一个 title
属性,这个umi3发布中提到的一个功能,具体怎么实现呢?
function HomePage() { return <h1>Home Page</h1>; } HomePage.title = 'Home Page'; export default HomePage; 复制代码
原理很简单,通过分析AST,找到默认导出,再找到所有的表达式,如果表达式是赋值表达式,而且左边是对象,右边的赋值是字符串、数字、布尔值,则把属性导出来。
对umi3的理解暂时就这么多,可能有理解偏差或者鄙陋,欢迎指出。