any-loader - 数据加载器中间件
any-loader 旨在为 node.js 和其他的 javascript 提供一个可定制程度较高的数据加载器中间件类库。本身并不实现任何数据加载器的实现逻辑,只界定了数据流走向的标准接口 newLoadStrem -> setup -> beforeLoad -> doLoad -> afterLoad ,调用顺序(不可逆),以及此过程中的异常错误处理机制。
any-loader 支持并实现了以下编程特性:
- 基于AOP设计,支持异步(Promise)。
- 中间件形态,不干涉业务逻辑和底层实现。
- 使用OOP进行扩展,使用继承和方法重载,来进行子类的开发,并提供丰富的方法以控制的粒度。
- 接口基于 Promise 封装,向后兼容 async/await 语法
- 数据流(LoadStream)部分,使用 fp 编程,数据流持有的 input, output 等数据,只在接口中流转,结束后即作废。 Loader 本身无状态,不持有过程数据。
本类库代码优先发布 码云 - Gitee,github只用作ci,用中国人自己的代码仓库,专业私有云代码托管。
安装说明
npm install any-loader
yarn add any-loader
使用说明
一般实现了自己的 Loader 后,使用时主要使用 load
方法:
{ // do something... } const loader = ;loader;
load
方法,主要按照以下流程执行:
| ? | ? | ?
当 load
过程发生错误时,不管用户在实际代码层面抛出(和reject)任何数据或者异常,进入到 Loader 层面,会将其再次包裹成一个 LoadStreamError
的对象。
决定 Loader 在处理是否将异常抛出,由方法 isThrowError
决定。
Loader 可重载的方法说明
// or
LoadStream 初始化时的接口,Loader的默认行为,该接口执行过程中的异常错误不会被抛出和reject,不管什么情况都会朝下一个接口去执行(可通过重载 isThrowError
方法来改变其是否抛出setup的异常)。
但在setup中使用 throw
可能会导致 setup 内的结构控制失效,所以请确定的确了解 throw
可能带来的影响。
load 前置接口,该接口中的抛出错误和reject会阻止下一个接口的执行。
load 的实际执行方法,继承的子类应该优先重载该方法以实现 Loader 具体的逻辑。
但目前来说,beforeLoader, doLoad, afterLoad 并没有明确区别,只是先后执行的顺序。
load 的后置接口,参考 beforeLoad
和 doLoad
在 newLoadStream
时候,生成作为 load
的 input
输入参数实例,可重载这个方法,以返回实际开发场景中所需要的输入实例。
但最好返回类实例或者一个 plainObject,而不要是值类型(string,boolean, number),函数也不推荐。
在 newLoadStream
时候,生成用于承载 load
的 output
输出实例,可重载这个方法,以返回实际开发场景中所需要的输出实例。
同 newInput
,最好返回一个类实例或者 plainObject。
并且该方法的参数中的 input
必然是基于用户重载 newInput
返回的结果。
用于判断是否将某个过程,或者某些特定的 Error 抛出(或reject),用户可重载该方法,使 Loader 彻底成为静默的模式,或者根据 Error 类来决定是否抛出。
这几个方法的重载,也会对 Loader 的形成产生比较大的影响,属于 Loader 比较基础的方法群, 但一般而言,直接重载 newInput
和 newOutput
能满足大多数场景使用需求。
不推荐 对 newLoadStream
和 load
方法重载。
简单的例子
// 默认形态下,input, output 是标准 {} { return { const image = ; image { outputimage = this; outputwidth = thiswidth; outputheight = thisheight; ; }; image { ; }; imagesrc = inputurl; }; } const loader = ; loader;
// 我们先定义了一个远程的URL类,或者你的项目本身就有类似的设定 { // .... } { return '...'; } // 再顶一个远程的图片类 { thisurl = remoteUrl; // 这是一个RemoteURL的实例 thisisLoad = false; thisimage = null; thiserror = null; } { thisisLoad = true; thisimage = image; } { thiserror = error; } // 我们将 RemoteURL 的实例,作为 LoadStream 的 input { return this; } { // 到这里时,input已经变为 RemoteURL 的实例 return input; } // input => RemoteURL, output => RemoteImage { return { const image = ; image { output; ; }; image { output; ; }; imagesrc = input; }; } // 调用代码 const loader = ; loader;
注意事项
在 setup
beforeLoad
doLoad
afterLoad
中,异常的处理,和异步的异常处理,需要特别注意:
{ if true throw '就是任性抛出错误'; // 因为上面已经抛出错误了,所以后面这里就不会再被执行到了, // setup 本身默认是不会抛出异常的,这就会导致 setup 的逻辑没有完全被执行 } { return { // 这里我偷个懒,以 timeout 模拟一个异步请求 ; }; }
在将一个 new Promise
的实例提供给别人使用时,存在许多技术黑洞和坑(不同JS平台的实现和当前ES的Promise实现的版本的缺陷导致),所以本类库已经转用 bluebird 所提供的 Promise。
mjs和commonjs的问题
mjs 和 commonjs 两个规范这是一个很头疼的问题。
index.js
目前以默认的 commonjs 作为基础输出(用rollup构建)。
也即,如下的代码,都是生效的:
const Loader = ; // 仅在 nodejs 环境下 ; // babel 或者 ES 环境下
如果项目使用了 babel-runtime 的,可以考虑引用 any-loader/Loader
,只针对 babel 环境。
相应的,在对应项目中的 babel 设置,要针对 exclude 进行如下的设置:
webpack.config.js
const babel = test : /\.m?jsx?$/ { // 针对 mjs ,不算在排除的范围内 if /.mjs$/ return false; return /node_modules/; } use : loader : 'babel-loader' options: presets: /* your presets */ plugins: /* your plugins */ ;
dist/any-loader.bundle.js
dist/any-loader.bundle.umd.js
打包所有类库为一个文件dist/any-loader.without-bluebird.js
dist/any-loader.without-bluebird.umd.js
不包含 bluebird
TODO
- 是否将 deepmerge 替换为 lodash/merge ,并打包集成。