KeystoneJS 是一個優(yōu)秀的并且非常有個性的 Node CMS,數(shù)據(jù)庫使用 MongoDB官辈,而手頭的一個項目,因為主要數(shù)據(jù)庫便是 MongoDB拴念,結合當時的需求钧萍,評估后采用了 KeystoneJS 作為運營后臺的基礎。在較短的時間內(nèi)政鼠,達到了不錯的應用效果风瘦,滿足了初期的各項業(yè)務迭代需求。
應用過程中公般,感受到 KeystoneJS 的文檔算是優(yōu)秀万搔,但作為一個快速發(fā)展的開源項目,文檔中有些缺失也在所難免官帘,并且文檔偏重于純使用瞬雹,若有更多改造的需求,分析源碼便是個必修課刽虹。
為了更好的滿足后續(xù)的業(yè)務改進需求酗捌,便計劃分析下 KeystoneJS 的源碼,也希望從中學習優(yōu)秀框架的設計思想涌哲。
一. 沿調用棧(縱向)分析
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