mm_tpl 2.9.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -177,7 +177,7 @@ const eachResult = tpl.render(`
177
177
 
178
178
  ```javascript
179
179
  // 设置全局数据
180
- Tpl.global_bag.siteName = "我的网站";
180
+ Tpl.global_bag.set('siteName', "我的网站");
181
181
 
182
182
  // 设置视图变量
183
183
  tpl.set({
@@ -746,8 +746,8 @@ const restored = tpl.escape(escaped, true);
746
746
  // 全局钩子单例,往里加的钩子对所有实例生效
747
747
  Tpl.global_hook.addAction('header', function (m) { return '<header>头部</header>'; });
748
748
 
749
- // 全局数据袋,渲染时注入模型的 globalBag 字段
750
- Tpl.global_bag.siteName = '我的网站';
749
+ // 全局数据袋(mm_vars 变量实例,varsAdmin('os') 共用),渲染时以 list() 副本注入模型的 globalBag 字段
750
+ Tpl.global_bag.set('siteName', '我的网站');
751
751
 
752
752
  // 全局语言包字典,lang_dir 与 langs 都为空时实例按深拷贝副本兜底复用
753
753
  Tpl.global_dict = { zh_cn: { name: '超级美眉' }, en: { name: 'MM' } };
@@ -977,7 +977,7 @@ tpl.clearCache('./zh_cn');
977
977
 
978
978
  #### 模板函数表
979
979
 
980
- 用 `setFunc` 注册模板函数,注册后每次渲染都注入模型,模板内直接按名调用;`removeFunc` 删除,`getFuncs` 取全部:
980
+ 用 `setFunc` 注册模板函数,注册后每次渲染都注入模型,模板内直接按名调用;`delFunc` 删除,`getFuncs` 取全部:
981
981
 
982
982
  ```javascript
983
983
  tpl.setFunc('$md', function (text) {
@@ -1024,7 +1024,7 @@ tpl.hook.addAction('header', function (m) { return '<header>实例级</header>';
1024
1024
  - `runHook(name, req, ...args)` - 执行函数钩子(runFunc 别名)
1025
1025
  - `escape(body, restore)` - 转义/还原字符
1026
1026
  - `setFunc(name, func)` - 注册模板函数,每次渲染都注入模型供模板内调用;内置函数与本次 `model` 的同名字段优先
1027
- - `removeFunc(name)` - 删除已注册的模板函数,返回是否存在并删除成功
1027
+ - `delFunc(name)` - 删除已注册的模板函数,返回是否存在并删除成功
1028
1028
  - `getFuncs()` - 获取模板函数表
1029
1029
 
1030
1030
  ### Tpl 多用户相关方法
package/README_EN.md CHANGED
@@ -175,7 +175,7 @@ const eachResult = tpl.render(`
175
175
 
176
176
  ```javascript
177
177
  // Set global data
178
- Tpl.global_bag.siteName = "My Website";
178
+ Tpl.global_bag.set('siteName', "My Website");
179
179
 
180
180
  // Set view variables
181
181
  tpl.set({
@@ -753,7 +753,7 @@ The module mounts no global variables; state shared across instances lives on st
753
753
  Tpl.global_hook.addAction('header', function (m) { return '<header>Header</header>'; });
754
754
 
755
755
  // Global data bag, injected into the model's globalBag field at render time
756
- Tpl.global_bag.siteName = 'My Website';
756
+ Tpl.global_bag.set('siteName', 'My Website');
757
757
 
758
758
  // Global language dictionary, used as a deep-copied fallback when both lang_dir and langs are empty
759
759
  Tpl.global_dict = { zh_cn: { name: 'MM Girl' }, en: { name: 'MM' } };
@@ -987,7 +987,7 @@ tpl.clearCache('./zh_cn');
987
987
 
988
988
  #### Template Function Table
989
989
 
990
- Register template functions with `setFunc`; they are injected into the model on every render and can be called by name inside templates. `removeFunc` deletes one and `getFuncs` returns them all:
990
+ Register template functions with `setFunc`; they are injected into the model on every render and can be called by name inside templates. `delFunc` deletes one and `getFuncs` returns them all:
991
991
 
992
992
  ```javascript
993
993
  tpl.setFunc('$md', function (text) {
@@ -1034,7 +1034,7 @@ tpl.hook.addAction('header', function (m) { return '<header>instance-level</head
1034
1034
  - `runHook(name, req, ...args)` - Execute function hooks (alias of runFunc)
1035
1035
  - `escape(body, restore)` - Escape/restore characters
1036
1036
  - `setFunc(name, func)` - Register a template function injected into the model on every render for templates to call; built-in functions and fields of the same name in this call's `model` take precedence
1037
- - `removeFunc(name)` - Delete a registered template function, returns whether it existed and was removed
1037
+ - `delFunc(name)` - Delete a registered template function, returns whether it existed and was removed
1038
1038
  - `getFuncs()` - Get the template function table
1039
1039
 
1040
1040
  ### Tpl Multi-user Methods
package/lib/hook.js CHANGED
@@ -1,59 +1,80 @@
1
- const {
2
- types
3
- } = require('util');
4
- const {
5
- TplUtil
6
- } = require('./util.js');
1
+ const mm_action = require('mm_action');
2
+ const mm_filter = require('mm_filter');
3
+ const mm_func = require('mm_func');
7
4
 
8
5
  /**
9
- * 钩子管理类
6
+ * 钩子管理类(组合门面)
7
+ *
8
+ * 组合 mm_action(动作钩子)、mm_filter(过滤钩子)、mm_func(函数钩子)
9
+ * 三个基础模块,对上保持原有 Hook 公共 API 不变:
10
+ * - addAction / delAction / runAction —— 委托给 mm_action
11
+ * - addFilter / delFilter / runFilter —— 委托给 mm_filter
12
+ * - addFunc / delFunc / runFunc —— 委托给 mm_func
13
+ *
14
+ * 三个基础模块实例均通过 xxxAdmin(scope) 的 Admin 工厂按作用域注册:
15
+ * - 相同作用域的 Hook 共用同一组钩子实例(Admin 按名称缓存),
16
+ * 默认作用域为 os(代表整个系统通用),外部模块可用
17
+ * require('mm_action').actionAdmin('os') 等方式直接拿到
18
+ * mm_tpl 全局钩子正在使用的实例,实现共用与统一管理;
19
+ * - 实例级钩子需要隔离时,由调用方通过 Hook.genScope() 显式生成
20
+ * 唯一作用域传入。
21
+ *
10
22
  * @class
11
23
  */
12
24
  class Hook {
25
+ /**
26
+ * 默认作用域(系统级通用),与其他模块的 xxxAdmin('os') 统一
27
+ */
28
+ static GLOBAL_SCOPE = 'os';
29
+
30
+ /**
31
+ * 生成唯一作用域名(tpl_hook_N),供需要实例级隔离的调用方使用
32
+ * @returns {string} 唯一作用域名称
33
+ */
34
+ static genScope() {
35
+ Hook._seq = (Hook._seq || 0) + 1;
36
+ return `tpl_hook_${Hook._seq}`;
37
+ }
38
+
13
39
  /**
14
40
  * 创建钩子实例
15
41
  * @param {Object} logger - 日志对象,需含 error 方法,缺省使用 console
42
+ * @param {string} [scope] - 作用域名称,缺省为 os(系统级通用);
43
+ * 相同作用域共用同一组钩子实例,需要隔离时传入 Hook.genScope() 生成的唯一作用域
16
44
  */
17
- constructor(logger) {
45
+ constructor(logger, scope) {
18
46
  /**
19
47
  * 日志对象
20
48
  */
21
49
  this._logger = logger || console;
22
- this._func = {};
23
- this._action = {};
24
- this._filter = {};
50
+ /**
51
+ * 作用域名称
52
+ */
53
+ this._scope = scope || Hook.GLOBAL_SCOPE;
54
+ this.scope = this._scope;
55
+ /**
56
+ * 动作钩子模块(Admin 按作用域注册/共用)
57
+ */
58
+ this.action = mm_action.actionAdmin(this._scope, { logger: this._logger });
59
+ /**
60
+ * 过滤钩子模块(Admin 按作用域注册/共用)
61
+ */
62
+ this.filter = mm_filter.filterAdmin(this._scope, { logger: this._logger });
63
+ /**
64
+ * 函数钩子模块(Admin 按作用域注册/共用)
65
+ */
66
+ this.func = mm_func.funcAdmin(this._scope, { logger: this._logger });
25
67
  }
26
- }
27
68
 
28
- /**
29
- * 排序钩子列表
30
- * @param {Array} list - 钩子列表
31
- * @returns {Array} 排序后的列表
32
- */
33
- Hook.prototype.sort = function (list) {
34
- return list.sort((o1, o2) => {
35
- return o2.sort - o1.sort
36
- });
37
- };
38
-
39
- /**
40
- * 获取函数别名
41
- * @param {Function} func - 钩子函数
42
- * @param {string} alias - 别名
43
- * @returns {string} 函数别名
44
- */
45
- Hook.prototype._getName = function (func, alias) {
46
- if (!alias) {
47
- var str = func.toString();
48
- var name = TplUtil.between(str, 'function ', '(').trim();
49
- if (name) {
50
- alias = name;
51
- } else {
52
- alias = TplUtil.md5((new Date()).toString()).substring(0, 8);
53
- }
69
+ /**
70
+ * 获取作用域名称
71
+ * 外部模块可用该作用域通过 xxxAdmin(scope) 拿到同一组钩子实例
72
+ * @returns {string} 作用域名称
73
+ */
74
+ getScope() {
75
+ return this._scope;
54
76
  }
55
- return alias;
56
- };
77
+ }
57
78
 
58
79
  /**
59
80
  * 添加函数钩子
@@ -64,16 +85,7 @@ Hook.prototype._getName = function (func, alias) {
64
85
  * @returns {string} 函数别名
65
86
  */
66
87
  Hook.prototype.addFunc = function (name, func, sort = 100, alias = "") {
67
- alias = this._getName(func, alias);
68
- var obj = {
69
- func,
70
- sort,
71
- alias
72
- }
73
- var arr = this._func[name] || [];
74
- arr.push(obj);
75
- this._func[name] = this.sort(arr);
76
- return alias;
88
+ return this.func.addFunc(name, func, sort, alias);
77
89
  };
78
90
 
79
91
  /**
@@ -85,20 +97,14 @@ Hook.prototype.addFunc = function (name, func, sort = 100, alias = "") {
85
97
  * @returns {string} 函数别名
86
98
  */
87
99
  Hook.prototype.addAction = function (name, func, sort = 100, alias = "") {
88
- alias = this._getName(func, alias);
89
- var obj = {
90
- func,
91
- sort,
92
- alias
93
- }
94
- var arr = this._action[name] || [];
95
- arr.push(obj);
96
- this._action[name] = this.sort(arr);
97
- return alias;
100
+ return this.action.addAction(name, func, sort, alias);
98
101
  };
99
102
 
100
103
  /**
101
104
  * 添加过滤钩子
105
+ * mm_tpl 约定过滤函数签名为 func(model, ret, ...args)(模型在前),
106
+ * mm_filter 约定为 func(value, ...args)(管道值在前),此处做参数顺序适配;
107
+ * 过滤函数返回假值时保留原值
102
108
  * @param {string} name - 钩子名称
103
109
  * @param {Function} func - 钩子函数
104
110
  * @param {number} sort - 执行顺序
@@ -106,101 +112,52 @@ Hook.prototype.addAction = function (name, func, sort = 100, alias = "") {
106
112
  * @returns {string} 函数别名
107
113
  */
108
114
  Hook.prototype.addFilter = function (name, func, sort = 100, alias = "") {
109
- alias = this._getName(func, alias);
110
- var obj = {
111
- func,
112
- sort,
113
- alias
114
- }
115
- var arr = this._filter[name] || [];
116
- arr.push(obj);
117
- this._filter[name] = this.sort(arr);
118
- return alias;
115
+ alias = mm_filter.Filter.getName(func, alias);
116
+ return this.filter.addFilter(name, function (value, model, ...args) {
117
+ return func(model, value, ...args);
118
+ }, sort, alias);
119
119
  };
120
120
 
121
121
  /**
122
122
  * 删除函数钩子
123
123
  * @param {String} name 钩子名称
124
124
  * @param {String} alias 函数别名
125
+ * @returns {boolean} 是否删除成功
125
126
  */
126
- Hook.prototype.delFunc = function (name, alias) {
127
- var arr = this._func[name];
128
- if (arr) {
129
- var list = [];
130
- for (var i = 0; i < arr.length; i++) {
131
- var item = arr[i];
132
- if (item.alias !== alias) {
133
- list.push(item);
134
- }
135
- }
136
- this._func[name] = list;
137
- }
127
+ Hook.prototype.delFunc = function (name, alias = "") {
128
+ return this.func.delFunc(name, alias);
138
129
  };
139
130
 
140
131
  /**
141
132
  * 删除动作钩子
142
133
  * @param {String} name 钩子名称
143
134
  * @param {String} alias 函数别名
135
+ * @returns {boolean} 是否删除成功
144
136
  */
145
- Hook.prototype.delAction = function (name, alias) {
146
- var arr = this._action[name];
147
- if (arr) {
148
- var list = [];
149
- for (var i = 0; i < arr.length; i++) {
150
- var item = arr[i];
151
- if (item.alias !== alias) {
152
- list.push(item);
153
- }
154
- }
155
- this._action[name] = list;
156
- }
137
+ Hook.prototype.delAction = function (name, alias = "") {
138
+ return this.action.delAction(name, alias);
157
139
  };
158
140
 
159
141
  /**
160
142
  * 删除过滤钩子
161
143
  * @param {String} name 钩子名称
162
144
  * @param {String} alias 函数别名
145
+ * @returns {boolean} 是否删除成功
163
146
  */
164
147
  Hook.prototype.delFilter = function (name, alias = "") {
165
- var arr = this._filter[name];
166
- if (arr) {
167
- var list = [];
168
- for (var i = 0; i < arr.length; i++) {
169
- var item = arr[i];
170
- if (item.alias !== alias) {
171
- list.push(item);
172
- }
173
- }
174
- this._filter[name] = list;
175
- }
148
+ return this.filter.delFilter(name, alias);
176
149
  };
177
150
 
178
151
  /**
179
152
  * 执行函数钩子
180
153
  * @param {string} name - 钩子名称
181
- * @param {Object} param - 参数
182
- * @param {Function} func - 钩子函数
154
+ * @param {Object} model - 模型对象
155
+ * @param {Object} req - 请求参数
156
+ * @param {...any} args - 附加参数
157
+ * @returns {Promise<Object>} 执行结果(最后一个真值)
183
158
  */
184
- Hook.prototype.runFunc = async function (name, model, req, ...args) {
185
- var ret;
186
- var list = this._func[name];
187
- if (list) {
188
- for (var i = 0; i < list.length; i++) {
189
- var item = list[i];
190
- try {
191
- var ret_new = item.func(model, req, ...args);
192
- if (types.isPromise(ret_new)) {
193
- ret_new = await ret_new;
194
- if (ret_new) {
195
- ret = ret_new;
196
- }
197
- }
198
- } catch (err) {
199
- this._logger.error(`[${this.constructor.name}] 执行函数钩子失败 ${item.alias}`, err);
200
- }
201
- }
202
- }
203
- return ret;
159
+ Hook.prototype.runFunc = function (name, model, req, ...args) {
160
+ return this.func.runFunc(name, model, req, ...args);
204
161
  };
205
162
 
206
163
  /**
@@ -208,19 +165,15 @@ Hook.prototype.runFunc = async function (name, model, req, ...args) {
208
165
  * @param {string} name - 钩子名称
209
166
  * @param {Object} model - 模型对象
210
167
  * @param {...any} args - 附加参数
211
- * @returns {Object} 执行结果
168
+ * @returns {string} 执行结果(各动作返回值按顺序字符串拼接)
212
169
  */
213
170
  Hook.prototype.runAction = function (name, model, ...args) {
171
+ var results = this.action.runAction(name, model, ...args);
214
172
  var ret = '';
215
- var list = this._action[name];
216
- if (list) {
217
- for (var i = 0; i < list.length; i++) {
218
- var o = list[i];
219
- try {
220
- ret += o.func(model, ...args);
221
- } catch (err) {
222
- this._logger.error(`[${this.constructor.name}] 执行动作钩子失败 ${o.alias}`, err);
223
- }
173
+ for (var i = 0; i < results.length; i++) {
174
+ var r = results[i];
175
+ if (r !== undefined && r !== null) {
176
+ ret += String(r);
224
177
  }
225
178
  }
226
179
  return ret;
@@ -235,59 +188,18 @@ Hook.prototype.runAction = function (name, model, ...args) {
235
188
  * @returns {Object} 过滤后的参数
236
189
  */
237
190
  Hook.prototype.runFilter = function (name, model, ret, ...args) {
238
- var list = this._filter[name];
239
- if (list) {
240
- for (var i = 0; i < list.length; i++) {
241
- var item = list[i];
242
- try {
243
- ret = item.func(model, ret, ...args) || ret;
244
- } catch (err) {
245
- this._logger.error(`[${this.constructor.name}] 执行过滤钩子失败 ${item.alias}`, err);
246
- }
247
- }
248
- }
249
- return ret;
191
+ return this.filter.runFilter(name, ret, model, ...args);
250
192
  };
251
-
252
193
  /**
253
194
  * 删除钩子
195
+ * 同时删除该别名下的函数钩子、动作钩子与过滤钩子
254
196
  * @param {string} name - 钩子名称
255
197
  * @param {string} alias - 函数别名
256
198
  */
257
- Hook.prototype.del = function (name, alias) {
258
- var arr = this._func[name];
259
- if (arr) {
260
- var list = [];
261
- for (var i = 0; i < arr.length; i++) {
262
- var item = arr[i];
263
- if (item.alias !== alias) {
264
- list.push(item);
265
- }
266
- }
267
- this._func[name] = list;
268
- }
269
- arr = this._action[name];
270
- if (arr) {
271
- var list = [];
272
- for (var i = 0; i < arr.length; i++) {
273
- var item = arr[i];
274
- if (item.alias !== alias) {
275
- list.push(item);
276
- }
277
- }
278
- this._action[name] = list;
279
- }
280
- arr = this._filter[name];
281
- if (arr) {
282
- var list = [];
283
- for (var i = 0; i < arr.length; i++) {
284
- var item = arr[i];
285
- if (item.alias !== alias) {
286
- list.push(item);
287
- }
288
- }
289
- this._filter[name] = list;
290
- }
199
+ Hook.prototype.del = function (name, alias = "") {
200
+ this.func.delFunc(name, alias);
201
+ this.action.delAction(name, alias);
202
+ this.filter.delFilter(name, alias);
291
203
  };
292
204
 
293
205
  /**
@@ -295,15 +207,9 @@ Hook.prototype.del = function (name, alias) {
295
207
  * @param {string} name - 钩子名称
296
208
  */
297
209
  Hook.prototype.clear = function (name) {
298
- if (name) {
299
- delete this._func[name];
300
- delete this._action[name];
301
- delete this._filter[name];
302
- } else {
303
- this._func = {};
304
- this._action = {};
305
- this._filter = {};
306
- }
210
+ this.func.clear(name);
211
+ this.action.clear(name);
212
+ this.filter.clear(name);
307
213
  };
308
214
 
309
- module.exports = Hook;
215
+ module.exports = Hook;
package/lib/tpl.js CHANGED
@@ -1,4 +1,7 @@
1
1
  const engine = require('art-template');
2
+ const {
3
+ varsAdmin
4
+ } = require('mm_vars');
2
5
  const {
3
6
  Base
4
7
  } = require('./base.js');
@@ -144,13 +147,19 @@ class Tpl extends Base {
144
147
  * 全局钩子实例
145
148
  * 所有引擎实例共享,与实例级 this.hook 一起参与执行,全局先执行、实例级后执行
146
149
  * 宿主应用注册跨实例钩子时使用,如 Tpl.global_hook.addFunc(...)
150
+ * 以默认作用域 Hook.GLOBAL_SCOPE(os,代表整个系统通用)在 mm_action/mm_filter/mm_func
151
+ * 的 Admin 工厂中注册,外部模块可用 actionAdmin('os') 等同名作用域取出同一组钩子实例共用
147
152
  */
148
- static global_hook = new Hook();
153
+ static global_hook = new Hook(null, Hook.GLOBAL_SCOPE);
149
154
  /**
150
- * 全局数据袋
151
- * 渲染时注入模型的 globalBag 字段,供模板跨实例共享数据
155
+ * 全局数据袋(mm_vars 变量实例)
156
+ * 以默认作用域 os(整个系统通用)通过 varsAdmin('os') 获取,
157
+ * 外部模块可用 require('mm_vars').varsAdmin('os') 取出同一实例,实现共用与统一管理。
158
+ * 写入用 set(key, value)、读取用 get(key)、删除用 del(key);
159
+ * 渲染时以 list() 副本注入模型的 globalBag 字段(模板内直接 m.globalBag.key 取值),
160
+ * 模板内的修改只影响本次渲染的副本,不会写回全局
152
161
  */
153
- static global_bag = {};
162
+ static global_bag = varsAdmin('os');
154
163
  /**
155
164
  * 全局语言包字典
156
165
  * 实例未配置任何语言包时的回退字典,形如 { 语言代码: 词条对象 }
@@ -198,6 +207,18 @@ class Tpl extends Base {
198
207
  */
199
208
  this.error = null;
200
209
 
210
+ /**
211
+ * 编译缓存:模板源码(+options 指纹)→ 已编译渲染函数(LRU)
212
+ * art-template 对字符串源码每次 render 都重新编译,区块页面(几十个模板/请求)
213
+ * 会把 CPU 耗在重复编译上;body 即缓存键,内容变更后键自然变化,无需手动失效
214
+ */
215
+ this._tpl_cache = new Map();
216
+
217
+ /**
218
+ * 编译缓存上限,超过后淘汰最久未命中的条目,可通过 config.cache_size 调整
219
+ */
220
+ this._tpl_cache_max = Number(this.config.cache_size) || 512;
221
+
201
222
  /**
202
223
  * 字符转义规则
203
224
  */
@@ -240,8 +261,10 @@ class Tpl extends Base {
240
261
 
241
262
  /**
242
263
  * 实例级钩子,仅作用于当前实例;与全局钩子 Tpl.global_hook 一起参与执行,全局先执行、实例级后执行
264
+ * 通过 Hook.genScope() 显式生成唯一作用域(tpl_hook_N)在 Admin 工厂中注册,实例间相互隔离;
265
+ * 注意不能省略作用域,否则会默认共用系统级 os 作用域,与全局钩子重复执行
243
266
  */
244
- this.hook = new Hook(this.config.logger);
267
+ this.hook = new Hook(this.config.logger, Hook.genScope());
245
268
 
246
269
  /**
247
270
  * 初始化
@@ -921,7 +944,7 @@ Tpl.prototype.setFunc = function (name, func) {
921
944
  * @param {string} name - 函数名
922
945
  * @returns {boolean} 是否存在并删除成功
923
946
  */
924
- Tpl.prototype.removeFunc = function (name) {
947
+ Tpl.prototype.delFunc = function (name) {
925
948
  if (this.funcs[name] === undefined) {
926
949
  return false;
927
950
  }
@@ -949,14 +972,45 @@ Tpl.prototype._renderMain = function (body, model, options) {
949
972
  if (body) {
950
973
  var m = Object.assign({}, {
951
974
  viewBag: this.viewBag,
952
- globalBag: Tpl.global_bag
975
+ globalBag: Tpl.global_bag.list()
953
976
  }, model);
954
977
  this.loadFunc(m);
955
- ret = this.engine.render(body, m, options);
978
+ ret = this._getCompiled(body, options)(m);
956
979
  }
957
980
  return ret;
958
981
  };
959
982
 
983
+ /**
984
+ * 获取已编译渲染函数(LRU 缓存)
985
+ * 未命中时经 engine.compile 编译后入缓存;命中时移至最新位。
986
+ * options 非空时并入键(编译结果可能受 rules 等选项影响),空 options 直接以源码为键。
987
+ * @param {string} body - 模板源码(调用方已完成转义)
988
+ * @param {Object} options - 配置参数
989
+ * @returns {Function} 渲染函数,入参为数据模型
990
+ */
991
+ Tpl.prototype._getCompiled = function (body, options) {
992
+ var key = body;
993
+ if (options !== undefined && options !== null && Object.keys(options).length) {
994
+ key = body + '\u0000' + JSON.stringify(options);
995
+ }
996
+ var cache = this._tpl_cache;
997
+ var fn = cache.get(key);
998
+ if (fn) {
999
+ // LRU 触碰:删除后重插,使其移到最新位
1000
+ cache.delete(key);
1001
+ cache.set(key, fn);
1002
+ return fn;
1003
+ }
1004
+ // 与 engine.render(body, m, options) 等价:art-template 的 render 内部即 compile(source, options)(data)
1005
+ fn = this.engine.compile(body, options);
1006
+ cache.set(key, fn);
1007
+ if (cache.size > this._tpl_cache_max) {
1008
+ // 淘汰最旧(Map 迭代序即插入序,首个键即最久未触碰)
1009
+ cache.delete(cache.keys().next().value);
1010
+ }
1011
+ return fn;
1012
+ };
1013
+
960
1014
  /**
961
1015
  * 套用本次渲染的临时选项
962
1016
  * options.style 与 options.lang 只对当前这次渲染生效,由 _restoreTempOptions 恢复
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mm_tpl",
3
- "version": "2.9.0",
3
+ "version": "2.11.0",
4
4
  "description": "Super Meimei Template Engine - High-performance template engine based on art-template with MVC pattern, hook system, theme switching and caching mechanism",
5
5
  "main": "index.js",
6
6
  "io": true,
@@ -43,8 +43,12 @@
43
43
  "homepage": "https://gitee.com/qiuwenwu91/mm_tpl#readme",
44
44
  "dependencies": {
45
45
  "art-template": "4.13.4",
46
+ "mm_action": "^1.1.0",
46
47
  "mm_corejs": "^1.1.6",
47
- "mm_lang": "^3.0.0"
48
+ "mm_filter": "^1.0.0",
49
+ "mm_func": "^1.0.0",
50
+ "mm_lang": "^3.0.0",
51
+ "mm_vars": "^1.0.1"
48
52
  },
49
53
  "engines": {
50
54
  "node": ">=12.0.0"