KeystoneJS 是一个优秀的并且非常有个性的 Node CMS,数据库使用 MongoDB,而手头的一个项目,因为主要数据库便是 MongoDB,结合当时的需求,评估后采用了 KeystoneJS 作为运营后台的基础。在较短的时间内,达到了不错的应用效果,满足了初期的各项业务迭代需求。
应用过程中,感受到 KeystoneJS 的文档算是优秀,但作为一个快速发展的开源项目,文档中有些缺失也在所难免,并且文档偏重于纯使用,若有更多改造的需求,分析源码便是个必修课。
为了更好的满足后续的业务改进需求,便计划分析下 KeystoneJS 的源码,也希望从中学习优秀框架的设计思想。
一. 沿调用栈(纵向)分析
1. 调用示例
-
keystone.js
keystone.import('models') ... keystone.start()
-
models/Project.js
var Project = new keystone.List('Project', { schema: { collection: 't_project' }, label: '项目', }) Project.add({ name: { type: String, required: true, label: '项目名称'}, pv: { type: Number, label: 'PV' } }) ... Project.register()
2. keystone 源码中的核心类型
List
-
lib/list.js
module.exports = function (keystone) { function List (key, options) { var defaultOptions = { schema: { collection: keystone.prefixModel(key), }, ... }; ... this.options = utils.options(defaultOptions, options) ... this.schema = new keystone.mongoose.Schema({}, this.options.schema); ... } ... // 笔记:依赖到 mongo Schema Object.defineProperty(this, 'nameIsVirtual', { get: function () { return this.model.schema.virtuals[this.mappings.name] ? true : false } }) ... List.prototype.add = require('./list/add') ... List.prototype.field = require('./list/field') ... return List }
-
lib/list/add.js
function add () { var add = function(obj, prefix) { ... var keys = Object.keys(obj) for(...keys) { // 笔记:addField(path, fieldOptions) addField(prefix + key, obj[key]) } } var addField = function (path, options) { ... this.uiElements.push({ type: 'field', field: this.field(path, options) }) }.bind(this) var args = Array.prototype.slice.call(arguments) ... _forEach(args, function (def) { ... add(def); } }
Field (belongs to List)
-
lib/list/field.js
function field (path, options) { ... if (options.type === String) { options.type = Field.Types.Number } ... // 笔记:new Field(list, path, options) var field = new options.type(this, path, options) ... return field } module.exports = field
Field Type
-
fields/types/number/NumberType.js
var FieldType = require('../Type'); ... function number (list, path, options) { this._nativeType = Number ... } util.inherits(number, FieldType)
-
fields/types/Type.js
function Field (list, path, options) { ... this.addToSchema(this.list.schema) ... } ... Field.prototype.addToSchema = function (schema) { var ops = (this._nativeType) ? _.defaults({ type: this._nativeType }, this.options) : this.options; // 笔记:listSchema.path(field.path, field.options) schema.path(this.path, ops) ... }
二. 沿搜索(横向)分析
1. 关键词 schema.
- 依赖 schema 的属性/方法
add()
path()
nested[]
virtual()
pre()
methods
paths[]
statics
collection
url
mimetype
size
- 依赖 schema 的模块
-
List
- list.js
- list/add.js
- list/buildSearchTextIndex.js
- list/declaresTextIndex.js
- list/expandColumns.js
- list/register.js
- 一些非基本 Field 类型的 field type, 如
NameType
,PasswordType
,SelectType
,RelationType
- 一些高级 Field 类型, 如
LocalFilesType
,LocationType
,MarkdownType
-
schemaPlugins
- autokey
- history
- sortable
- track
- lib/storage.js
-
2. 关键词 query.
及 { $
(query 相关)
- fields/types/number/NumberType.js
- lib/core/createItem.js
- lib/list/addSearchToQuery.js
- lib/list/apiForGet.js
- lib/list/pageinate.js
- lib/middleware/api.js
- lib/schemaPlugins/methods/getRelated.js
- lib/view.js
及 - fidleds/types/*Type.js
lib/list/addFiltersToQuery.js
lib/list/getSearchFilter.js
.exec
- lib/core/createItems.js
- lib/list/apiForGet.js
- lib/list/getUniqueValue.js
- lib/list/paginate.js
- lib/list/updateItem.js
- lib/schemaPlugins/autokey.js
- lib/schemaPlugins/methods/getRelated.js
- lib/schemaPlugins/sortable.js
- lib/session.js
- lib/view.js
网友评论