mipengine / mip2

MIP (移动网页加速器)通过优化网页JS、控制资源加载顺序,达到加速网页的效果。
https://www.mipengine.org/
MIT License
184 stars 49 forks source link

MIP CLI 2.0 #624

Open chenqiushi opened 5 years ago

chenqiushi commented 5 years ago

要解决什么问题

  1. 第三方依赖过多(50+),导致安装、更新速度极慢
  2. 项目组织结构存在调整优化的空间
  3. 其他补充

描述一下你理想中的解决方案

  1. 整体使用 Typescript 重构,更新配套编译、lint 工具等
  2. 所有命令使用插件机制,统一入口进行调度,运行时安装、加载。
    • 官方插件。管理在 mip-cli/plugins 目录。其中使用频率最高的命令(如 build,dev,init)可以内置,其他插件运行时加载。
    • 自定义插件。用户自己管理,按照命名规则及目录规范,发布到 NPM

CLI 应提供一种方式(如一个 plugin 包 cli-shared-utils),暴露命令行常用接口(logger、spinner、server、env 等)给插件使用。

  1. 完善的单元测试,覆盖率不低于 90%
  2. 其他补充

描述你的备选方案 清晰准确的描述你考虑的备选方案

补充信息 补充其他信息,如截图等

clark-t commented 5 years ago

整体思路差不多,但是 utils 感觉没必要拆成包吧,直接跟着 CLI 一起升级感觉就可以了

clark-t commented 5 years ago

CLI 支持的指令

我觉得 CLI 可以分成 core 和 功能插件两部分,core 负责命令行的解析和插件调度,并且提供:

# 安装插件
mip2 install xxx
# 卸载插件
mip2 remove xxx
# 升级插件
mip2 update xxx/all
# 打印当前已安装的插件列表,可打印插件名和当前插件版本号、是否存在最新版等信息
mip2 list
# 查看当前 MIP2 版本
mip2 version

其他可能还缺少的部分可以补充。

插件的管理和发布

默认采用 npm 包的方式进行 MIP2 插件管理,比如 mip2-plugin-dev 则对应 mip2 dev -xxx -xxxmip2-plugin-dev-sf 则对应 mip2 dev sf --xxx ---xxx

为了插件开发时本地调试方便,应该要提供一种加载本地插件的方法。这个我们讨论一下。

插件使用方式

功能插件在安装好之后,可以通过 mip2 xxx xxxxx 的方式调用,比如 mip2-plugin-dev 的插件的命令为:

mip2 dev xxxxx

这样 CLI 会自动解析好相关参数并传递给对应的插件进行执行

插件的复用机制

在实践过程当中,发现一种情况比较常见,就是插件的复用机制。比如目前现有的 mip-cli-plugin-sf 在功能上依赖了 mip2-dev 的编译和路由功能。关于这一块我的想法是增加一个插件的复用机制,通过 webpack/tapable 的方式进行组织,由 dev 模块通过 tapable 机制主动暴露钩子、方法等等,然后由其插件主动进行控制。

所有的插件都挂载到一个 global.MIP2 的大对象上,通过 MIP2.require('xxx') 的方法去加载其他插件,CLI 会自动去查找插件并返回插件对象,如果不存在则提示用户下载或者自动下载。

插件是否需要跟 CLI 强耦合

这里主要问题在于,插件是需要继承自一个由 CLI 核心提供的共同基类呢,还是统一一个对象格式,由插件开发者自行按照格式实现即可。如果统一使用 CLI 提供的基类,那么插件是无法独立于 CLI 直接使用的,比如 mip2-build,在编译上线的项目当中其实可以直接使用这个包进行上线即可,并不需要先通过 CLI 加载执行 mip2-build 去进行组件上线编译。

所以这里估计也得讨论一下。

clark-t commented 5 years ago

DEV 功能升级

增量编译

目前 mip2 dev 只提供了全量编译的功能,目前开发的组件越来越多,在初始启动的时候会变得有些慢了,所以需要支持组件在 dev 模式下的增量编译。即通过 server 访问到对应组件的时候再将其添加到 webpack 的 entry 中进行编译;

proxy 机制

目前 dev 的 proxy 只是基于源码替换 URL 的 proxy,应该升级成真正的 proxy,从目前的反馈来看,通过 proxy 来解决开发时跨域问题的需求还是很多的;

clark-t commented 5 years ago

VALIDATE 功能升级

449

chenqiushi commented 5 years ago

mip2-plugin-dev 则对应 mip2 dev -xxx -xxx,mip2-plugin-dev-sf 则对应 mip2 dev sf --xxx ---xxx

这一块 mip2 dev sf 应该是属于 mip2 dev 的子命令,可以同属一个插件 mip2-plugin-dev 来管理。不然如果有 mip2 dev sf2 , mip2 dev sf3 不是得发布 3 个插件。

所有的插件都是 mip2-plugin-<name> 的形式

clark-t commented 5 years ago

dev 可以集成自己的 a b c 命令,也可以外部插件扩展 sf,我的意思是这样子的,就跟 CLI 允许内置插件和扩展插件一样,可以增加插件灵活性

clark-t commented 5 years ago

经讨论,明确了几个问题:

插件复用机制

每个插件模块对外暴露的是一个对象,对象的接口格式由 CLI 或者一个公用的 utils 包来进行统一;插件以依赖的形式去调用其他插件,而不是以 MIP2.require('xxx') 的方式去管理,CLI 只负责命令解析和插件调度、插件升级卸载等等;

插件不强依赖 CLI

插件统一对外暴露的对象里面必须提供的 run 方法用于接收参数,并且插件的功能是完整的,不依赖 CLI 提供任何功能,因此可以直接通过 const plugin = require('xxx') ; plugin.run({xxx}) 的方式进行具体功能的直接使用

chenqiushi commented 5 years ago

目录组织形式初定如下(具体随时修改):

mip2/packages
    |-- mip-cli
    |   |-- cli
    |   |   |-- bin
    |   |       `-- mip2
    |   |   |-- lib
    |   |   |-- utils
    |   |   |-- ...
    |   |   |-- README.md
    |   |   `-- package.json
    |   |-- cli-plugin-dev
    |   |   |-- src
    |   |   |-- README.md
    |   |    `-- package.json
    |   |-- cli-plugin-build
    |   |   |-- src
    |   |   |-- README.md
    |   |    `-- package.json
    |   |-- cli-plugin-validate
    |   |   |-- src
    |   |   |-- README.md
    |   |    `-- package.json
    |   |-- cli-plugin-utils
    |   |   |-- src
    |   |   |-- README.md
    |   |    `-- package.json
    |   |-- ...
    |   `-- ...
    |
    `-- mip

其中 ,mip-cli/cli 模块是命令行工具本身,主要完成:

cli-plugin-<name> 是官方插件,相互之间可以进行调度,具体方式 @clark-t 可以补充

Plugins 之间存在一些需要公用的方法和模块,如 logger env exit 等,抽离出来在 cli-plugins-utils 里进行管理,开发插件时可以引入避免冗余。

Plugins 可以独自管理发布,单个 plugin 能够以 nodejs 模块的形式引入,以编程的方式使用,如:

const build = require('mip-cli-plugin-build');

build(foo, bar);

也可以安装 mip2 CLI,通过命令行工具进行调度:

npm install mip2

mip2 build
clark-t commented 5 years ago

为避免命名上的冲突,我觉得 cli-plugins-utils 应该命名为 cli-utils 会比较好点,npm 包名为 mip-cli-utils; utils 除了提供公用方法之外,还提供 TS 的类型定义之类,统一约束 CLI 插件对外暴露的格式;

插件我认为应该对外暴露的是一个对象而不是方法:

{
  command,
  run({/* 参数 */}),
  /* api */
  /* hooks */
}

其中 plugins.run({/ ... /}) 就等价于秋实君上面提到的 build(foo, bar) 的调用方式

clark-t commented 5 years ago

目前有个问题,比如在开发模式下除了最基础的 webpack 和 server 之外,可能还需要 proxy、autoreload、autoopen 等功能,但这些不是必须的,比如 autoopen 功能目前在 1.0 里面几乎是默认不开启的,所以这些功能是否也可以插件化?也就是插件的插件,然后给开发者提供配置方法比如通过 mip.config.js 去决定是否使用某些插件