From ed0f239c967f35fa709581b7ec74fced5f1b5a1d Mon Sep 17 00:00:00 2001 From: Sukka Date: Thu, 14 Oct 2021 17:51:03 +0800 Subject: [PATCH] perf(tag/helper): memoize (#4789) --- lib/plugins/helper/css.js | 6 +++++- lib/plugins/helper/date.js | 3 ++- lib/plugins/helper/feed_tag.js | 24 ++++++++++++++---------- lib/plugins/helper/js.js | 6 +++++- lib/plugins/helper/list_tags.js | 20 +++++++++++++++++++- lib/plugins/helper/mail_to.js | 6 +++++- lib/plugins/helper/open_graph.js | 7 ++++--- lib/plugins/helper/search_form.js | 4 +++- lib/plugins/helper/tagcloud.js | 20 +++++++++++++++++++- lib/plugins/tag/index.js | 24 ++++++++++++++++++++++++ lib/plugins/tag/post_link.js | 5 ++--- lib/plugins/tag/post_path.js | 5 ++--- package.json | 1 + 13 files changed, 105 insertions(+), 26 deletions(-) diff --git a/lib/plugins/helper/css.js b/lib/plugins/helper/css.js index 94c436e10a..85ccdc9f62 100644 --- a/lib/plugins/helper/css.js +++ b/lib/plugins/helper/css.js @@ -1,6 +1,7 @@ 'use strict'; const { htmlTag, url_for } = require('hexo-util'); +const { default: moize } = require('moize'); const flatten = function(arr, result = []) { for (const i in arr) { @@ -35,4 +36,7 @@ function cssHelper(...args) { return result; } -module.exports = cssHelper; +module.exports = moize(cssHelper, { + maxSize: 10, + isDeepEqual: true +}); diff --git a/lib/plugins/helper/date.js b/lib/plugins/helper/date.js index f811e7f826..8235e5fff4 100644 --- a/lib/plugins/helper/date.js +++ b/lib/plugins/helper/date.js @@ -2,6 +2,7 @@ const moment = require('moment-timezone'); const { isMoment } = moment; +const { default: moize } = require('moize'); const isDate = value => typeof value === 'object' && value instanceof Date && !isNaN(value.getTime()); @@ -93,4 +94,4 @@ exports.full_date = fullDateHelper; exports.relative_date = relativeDateHelper; exports.time_tag = timeTagHelper; exports.moment = moment; -exports.toMomentLocale = toMomentLocale; +exports.toMomentLocale = moize.shallow(toMomentLocale); diff --git a/lib/plugins/helper/feed_tag.js b/lib/plugins/helper/feed_tag.js index b14d3803b3..a72af70f12 100644 --- a/lib/plugins/helper/feed_tag.js +++ b/lib/plugins/helper/feed_tag.js @@ -1,15 +1,15 @@ 'use strict'; const { url_for } = require('hexo-util'); +const { default: moize } = require('moize'); const feedFn = (str = '') => { if (str) return str.replace(/2$/, ''); return str; }; -function feedTagHelper(path, options = {}) { - const { config } = this; - const title = options.title || config.title; +function makeFeedTag(path, options = {}, configFeed, configTitle) { + const title = options.title || configTitle; if (path) { if (typeof path !== 'string') throw new TypeError('path must be a string!'); @@ -26,16 +26,15 @@ function feedTagHelper(path, options = {}) { return ``; } - if (config.feed) { - const { feed } = config; - if (feed.type && feed.path) { - if (typeof feed.type === 'string') { - return ``; + if (configFeed) { + if (configFeed.type && configFeed.path) { + if (typeof configFeed.type === 'string') { + return ``; } let result = ''; - for (const i in feed.type) { - result += ``; + for (const i in configFeed.type) { + result += ``; } return result; } @@ -44,4 +43,9 @@ function feedTagHelper(path, options = {}) { return ''; } +function feedTagHelper(path, options = {}) { + const { config } = this; + return moize.deep(makeFeedTag.bind(this))(path, options, config.feed, config.title); +} + module.exports = feedTagHelper; diff --git a/lib/plugins/helper/js.js b/lib/plugins/helper/js.js index cdc0c60c41..920ecd973f 100644 --- a/lib/plugins/helper/js.js +++ b/lib/plugins/helper/js.js @@ -1,6 +1,7 @@ 'use strict'; const { htmlTag, url_for } = require('hexo-util'); +const { default: moize } = require('moize'); /* flatten() to be replaced by Array.flat() after Node 10 has reached EOL */ @@ -37,4 +38,7 @@ function jsHelper(...args) { return result; } -module.exports = jsHelper; +module.exports = moize(jsHelper, { + maxSize: 10, + isDeepEqual: true +}); diff --git a/lib/plugins/helper/list_tags.js b/lib/plugins/helper/list_tags.js index 6d56850515..f9970f6c0b 100644 --- a/lib/plugins/helper/list_tags.js +++ b/lib/plugins/helper/list_tags.js @@ -1,6 +1,7 @@ 'use strict'; const { url_for, escapeHTML } = require('hexo-util'); +const { default: moize } = require('moize'); function listTagsHelper(tags, options) { if (!options && (!tags || !Object.prototype.hasOwnProperty.call(tags, 'length'))) { @@ -92,4 +93,21 @@ function listTagsHelper(tags, options) { return result; } -module.exports = listTagsHelper; +function listTagsHelperFactory(tags, options) { + const transformArgs = () => { + if (!options && (!tags || !Object.prototype.hasOwnProperty.call(tags, 'length'))) { + options = tags; + tags = this.site.tags; + } + + return [tags.toArray(), options]; + }; + + return moize(listTagsHelper.bind(this), { + maxSize: 5, + isDeepEqual: true, + transformArgs + }).call(this, tags, options); +} + +module.exports = listTagsHelperFactory; diff --git a/lib/plugins/helper/mail_to.js b/lib/plugins/helper/mail_to.js index 5443388106..8eb51a8944 100644 --- a/lib/plugins/helper/mail_to.js +++ b/lib/plugins/helper/mail_to.js @@ -2,6 +2,7 @@ const { htmlTag } = require('hexo-util'); const qs = require('querystring'); +const { default: moize } = require('moize'); function mailToHelper(path, text, options = {}) { if (Array.isArray(path)) path = path.join(','); @@ -33,4 +34,7 @@ function mailToHelper(path, text, options = {}) { return htmlTag('a', attrs, text); } -module.exports = mailToHelper; +module.exports = moize(mailToHelper, { + maxSize: 10, + isDeepEqual: true +}); diff --git a/lib/plugins/helper/open_graph.js b/lib/plugins/helper/open_graph.js index f3668382e2..9506bb00e3 100644 --- a/lib/plugins/helper/open_graph.js +++ b/lib/plugins/helper/open_graph.js @@ -2,7 +2,8 @@ const { parse, resolve } = require('url'); const { isMoment, isDate } = require('moment'); -const { encodeURL, prettyUrls, htmlTag, stripHTML, escapeHTML, Cache } = require('hexo-util'); +const { encodeURL, prettyUrls, htmlTag, stripHTML, escapeHTML } = require('hexo-util'); +const { default: moize } = require('moize'); const localeMap = { 'en': 'en_US', @@ -20,8 +21,8 @@ const localeMap = { 'tr': 'tr_TR', 'vi': 'vi_VN' }; -const localeCache = new Cache(); -const localeToTerritory = str => localeCache.apply(str, () => { + +const localeToTerritory = moize.shallow(str => { if (str.length === 2 && localeMap[str]) return localeMap[str]; if (str.length === 5) { diff --git a/lib/plugins/helper/search_form.js b/lib/plugins/helper/search_form.js index ea81c2c4c4..f90a99b6d7 100644 --- a/lib/plugins/helper/search_form.js +++ b/lib/plugins/helper/search_form.js @@ -1,5 +1,7 @@ 'use strict'; +const { default: moize } = require('moize'); + function searchFormHelper(options = {}) { const { config } = this; const className = options.class || 'search-form'; @@ -8,4 +10,4 @@ function searchFormHelper(options = {}) { return `
${button ? `` : ''}
`; } -module.exports = searchFormHelper; +module.exports = moize.deep(searchFormHelper); diff --git a/lib/plugins/helper/tagcloud.js b/lib/plugins/helper/tagcloud.js index f8dff38d0a..158b84a57e 100644 --- a/lib/plugins/helper/tagcloud.js +++ b/lib/plugins/helper/tagcloud.js @@ -1,6 +1,7 @@ 'use strict'; const { Color, url_for } = require('hexo-util'); +const { default: moize } = require('moize'); function tagcloudHelper(tags, options) { if (!options && (!tags || !Object.prototype.hasOwnProperty.call(tags, 'length'))) { @@ -74,4 +75,21 @@ function tagcloudHelper(tags, options) { return result.join(separator); } -module.exports = tagcloudHelper; +function tagcloudHelperFactory(tags, options) { + const transformArgs = () => { + if (!options && (!tags || !Object.prototype.hasOwnProperty.call(tags, 'length'))) { + options = tags; + tags = this.site.tags; + } + + return [tags.toArray(), options]; + }; + + return moize(tagcloudHelper.bind(this), { + maxSize: 5, + isDeepEqual: true, + transformArgs + }).call(this, tags, options); +} + +module.exports = tagcloudHelperFactory; diff --git a/lib/plugins/tag/index.js b/lib/plugins/tag/index.js index 57e73aca15..842a2d6f50 100644 --- a/lib/plugins/tag/index.js +++ b/lib/plugins/tag/index.js @@ -1,5 +1,7 @@ 'use strict'; +const { default: moize } = require('moize'); + module.exports = ctx => { const { tag } = ctx.extend; @@ -52,3 +54,25 @@ module.exports = ctx => { tag.register('youtube', require('./youtube')); }; + +// Use WeakMap to track different ctx (in case there is any) +const moized = new WeakMap(); + +module.exports.postFindOneFactory = function postFindOneFactory(ctx) { + if (moized.has(ctx)) { + return moized.get(ctx); + } + + const moizedPostFindOne = moize(createPostFindOne(ctx), { + isDeepEqual: true, + maxSize: 20 + }); + moized.set(ctx, moizedPostFindOne); + + return moizedPostFindOne; +}; + +function createPostFindOne(ctx) { + const Post = ctx.model('Post'); + return Post.findOne.bind(Post); +} diff --git a/lib/plugins/tag/post_link.js b/lib/plugins/tag/post_link.js index a5df3fa23d..f27cec0c4e 100644 --- a/lib/plugins/tag/post_link.js +++ b/lib/plugins/tag/post_link.js @@ -2,6 +2,7 @@ const { encodeURL, escapeHTML } = require('hexo-util'); const { resolve } = require('url'); +const { postFindOneFactory } = require('./'); /** * Post link tag @@ -10,8 +11,6 @@ const { resolve } = require('url'); * {% post_link slug [title] [escape] %} */ module.exports = ctx => { - const Post = ctx.model('Post'); - return function postLinkTag(args) { const error = `Post not found: ${args.join(' ') || 'Invalid post_link'}`; const slug = args.shift(); @@ -24,7 +23,7 @@ module.exports = ctx => { escape = 'true'; } - const post = Post.findOne({slug}); + const post = postFindOneFactory(ctx)({ slug }); if (!post) return error; let title = args.length ? args.join(' ') : post.title; diff --git a/lib/plugins/tag/post_path.js b/lib/plugins/tag/post_path.js index e156c9deaa..0b81ed69e9 100644 --- a/lib/plugins/tag/post_path.js +++ b/lib/plugins/tag/post_path.js @@ -2,6 +2,7 @@ const { resolve } = require('url'); const { encodeURL } = require('hexo-util'); +const { postFindOneFactory } = require('./'); /** * Post path tag @@ -10,13 +11,11 @@ const { encodeURL } = require('hexo-util'); * {% post_path slug %} */ module.exports = ctx => { - const Post = ctx.model('Post'); - return function postPathTag(args) { const slug = args.shift(); if (!slug) return; - const post = Post.findOne({slug}); + const post = postFindOneFactory(ctx)({ slug }); if (!post) return; const link = encodeURL(resolve(ctx.config.root, post.path)); diff --git a/package.json b/package.json index 087fb7eb6a..a1c1dd1e3d 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "hexo-util": "^2.4.0", "js-yaml": "^4.0.0", "micromatch": "^4.0.2", + "moize": "^6.1.0", "moment": "^2.22.2", "moment-timezone": "^0.5.21", "nanocolors": "^0.2.12",