User scripts can be made to load faster with the help of caching. However, this is experimental, let me know if you face any issues on the talk page.
Add the following snippet of minified code to the top of your your common JavaScript page:
// Enable caching for resource loads, see [[User:SD0001/Making_user_scripts_load_faster]], @revision 6
if(!/\bnocache=\b/.test(location.href)){let e=(e,t,n)=>(e=e.replace(/special:mypage/i,"User:"+mw.config.get("wgUserName")),$.get("https://"+t+"/w/api.php?titles="+e+"&origin=*&format=json&formatversion=2&uselang=content&maxage=86400&smaxage=86400&action=query&prop=revisions|info&rvprop=content&rvlimit=1").then((r=>{let o=r.query.pages0];if(o.missing)return;let a=o.revisions0].content;if(n&&"text/javascript"!==n||"javascript"!==o.contentmodel){if("text/css"!==n||"css"!==o.contentmodel)return $.Deferred().reject('Refused to load "'+e+'"@'+t+": content type mismatch");mw.loader.addStyleTag(a)}else{let e=document.createElement("script");e.innerHTML=a,document.head.appendChild(e)}}))),t=e=>{let t=/^(?:(?:https:)?\/\/(.*))?\/w\/index.php/.exec(e),n=/\btitle=([^=?&]*)/.exec(e);return t&&n&&/\baction=raw\b/.test(e)&&/\bctype=/.test(e)?n1],t1||mw.config.get("wgServerName")]:null};window.importScript=t=>{e(encodeURIComponent(t),mw.config.get("wgServerName"),"text/javascript")},window.importStyleSheet=t=>{e(encodeURIComponent(t),mw.config.get("wgServerName"),"text/css")};let n=mw.loader.load;mw.loader.load=function(r,o){let a=t(r);a?e(a0],a1],o):n.apply(mw.loader,[...arguments])};let r=mw.loader.getScript;mw.loader.getScript=function(n){let o=t(n);return o?e(o0],o1],"text/javascript"):r.apply(mw.loader,[...arguments])}}
If you don't like this ugly blob of incomprehensible code, you can instead add the fully and pretty version (but this takes up a lot of lines):
Unminified full code (readable version)
|
---|
// Enable caching for resource loads, see [[User:SD0001/Making_user_scripts_load_faster]], @revision 6
if (!/\bnocache=\b/.test(location.href)) { // Don't enable if nocache=1 url parameter is given
let loadResource = (page, sitename, ctype) => {
page = page.replace(/special:mypage/i, 'User:' + mw.config.get('wgUserName'));
return $.get(
'https://' + sitename + '/w/api.php?titles=' + page + // page is already URL-encoded
'&origin=*&format=json&formatversion=2&uselang=content&maxage=86400&smaxage=86400' +
'&action=query&prop=revisions|info&rvprop=content&rvlimit=1'
).then((apiResponse) => {
let apiPage = apiResponse.query.pages0];
if (apiPage.missing) {
return;
}
let content = apiPage.revisions0].content;
if ((!ctype || ctype === 'text/javascript') && apiPage.contentmodel === 'javascript') {
let scriptTag = document.createElement('script');
scriptTag.innerHTML = content;
document.head.appendChild(scriptTag);
} else if (ctype === 'text/css' && apiPage.contentmodel === 'css') {
mw.loader.addStyleTag(content);
} else {
return $.Deferred().reject('Refused to load "' + page + '"@' + sitename + ': content type mismatch');
}
});
};
let getSiteTitle = (url) => {
let siteRgx = /^(?:(?:https:)?\/\/(.*))?\/w\/index.php/.exec(url),
titleRgx = /\btitle=([^=?&]*)/.exec(url);
if (siteRgx && titleRgx && /\baction=raw\b/.test(url) && /\bctype=/.test(url)) {
return titleRgx1], siteRgx1 || mw.config.get('wgServerName')];
} else return null;
};
window.importScript = (page) => {
loadResource(encodeURIComponent(page), mw.config.get('wgServerName'), 'text/javascript');
};
window.importStyleSheet = (page) => {
loadResource(encodeURIComponent(page), mw.config.get('wgServerName'), 'text/css');
};
let oldMwLoaderLoad = mw.loader.load;
mw.loader.load = function(url, type) {
let linkParts = getSiteTitle(url);
if (linkParts) {
loadResource(linkParts0], linkParts1], type);
} else {
oldMwLoaderLoad.apply(mw.loader, [...arguments]);
}
};
let oldMwLoaderGetScript = mw.loader.getScript;
mw.loader.getScript = function(url) {
let linkParts = getSiteTitle(url);
if (linkParts) {
return loadResource(linkParts0], linkParts1], 'text/javascript');
} else {
return oldMwLoaderGetScript.apply(mw.loader, [...arguments]);
}
};
}
|
(The two code snippets are functionally the very same).
Note that the code block necessarily needs to be at the top of your common.js page for it to take effect.
It replaces the existing implementations of mw.loader.load
, mw.loader.getScript
, importScript
and importStyleSheet
. No changes are needed in the way you normally add new user scripts.
The effect can be seen by keeping the network tab of your browser dev tools open and navigating through different pages. (Don't reload the same page – that's seen as a hard reload in some browsers and causes bypassing of cache.) Also ensure you have the "Disable cache" option unchecked in the devtools. In Chrome, you should see that most script fetches are resolved by the disk cache itself which is very fast.
loadResource function:
s-maxage=86400, max-age=86400, public
while the one set by index.php is private, max-age=0, s-maxage=0
.uselang=content
attribute enables public caching in
Varnish, etc. See
phab:T97096. However, no public caching takes place for requests by users with ability to view revision-deleted or suppressed content (ref:
[1]).origin=*
causes the API to set the Access-Control-Allow-Origin: *
header to avoid CORS issue while loading cross-wiki scripts.User scripts can be made to load faster with the help of caching. However, this is experimental, let me know if you face any issues on the talk page.
Add the following snippet of minified code to the top of your your common JavaScript page:
// Enable caching for resource loads, see [[User:SD0001/Making_user_scripts_load_faster]], @revision 6
if(!/\bnocache=\b/.test(location.href)){let e=(e,t,n)=>(e=e.replace(/special:mypage/i,"User:"+mw.config.get("wgUserName")),$.get("https://"+t+"/w/api.php?titles="+e+"&origin=*&format=json&formatversion=2&uselang=content&maxage=86400&smaxage=86400&action=query&prop=revisions|info&rvprop=content&rvlimit=1").then((r=>{let o=r.query.pages0];if(o.missing)return;let a=o.revisions0].content;if(n&&"text/javascript"!==n||"javascript"!==o.contentmodel){if("text/css"!==n||"css"!==o.contentmodel)return $.Deferred().reject('Refused to load "'+e+'"@'+t+": content type mismatch");mw.loader.addStyleTag(a)}else{let e=document.createElement("script");e.innerHTML=a,document.head.appendChild(e)}}))),t=e=>{let t=/^(?:(?:https:)?\/\/(.*))?\/w\/index.php/.exec(e),n=/\btitle=([^=?&]*)/.exec(e);return t&&n&&/\baction=raw\b/.test(e)&&/\bctype=/.test(e)?n1],t1||mw.config.get("wgServerName")]:null};window.importScript=t=>{e(encodeURIComponent(t),mw.config.get("wgServerName"),"text/javascript")},window.importStyleSheet=t=>{e(encodeURIComponent(t),mw.config.get("wgServerName"),"text/css")};let n=mw.loader.load;mw.loader.load=function(r,o){let a=t(r);a?e(a0],a1],o):n.apply(mw.loader,[...arguments])};let r=mw.loader.getScript;mw.loader.getScript=function(n){let o=t(n);return o?e(o0],o1],"text/javascript"):r.apply(mw.loader,[...arguments])}}
If you don't like this ugly blob of incomprehensible code, you can instead add the fully and pretty version (but this takes up a lot of lines):
Unminified full code (readable version)
|
---|
// Enable caching for resource loads, see [[User:SD0001/Making_user_scripts_load_faster]], @revision 6
if (!/\bnocache=\b/.test(location.href)) { // Don't enable if nocache=1 url parameter is given
let loadResource = (page, sitename, ctype) => {
page = page.replace(/special:mypage/i, 'User:' + mw.config.get('wgUserName'));
return $.get(
'https://' + sitename + '/w/api.php?titles=' + page + // page is already URL-encoded
'&origin=*&format=json&formatversion=2&uselang=content&maxage=86400&smaxage=86400' +
'&action=query&prop=revisions|info&rvprop=content&rvlimit=1'
).then((apiResponse) => {
let apiPage = apiResponse.query.pages0];
if (apiPage.missing) {
return;
}
let content = apiPage.revisions0].content;
if ((!ctype || ctype === 'text/javascript') && apiPage.contentmodel === 'javascript') {
let scriptTag = document.createElement('script');
scriptTag.innerHTML = content;
document.head.appendChild(scriptTag);
} else if (ctype === 'text/css' && apiPage.contentmodel === 'css') {
mw.loader.addStyleTag(content);
} else {
return $.Deferred().reject('Refused to load "' + page + '"@' + sitename + ': content type mismatch');
}
});
};
let getSiteTitle = (url) => {
let siteRgx = /^(?:(?:https:)?\/\/(.*))?\/w\/index.php/.exec(url),
titleRgx = /\btitle=([^=?&]*)/.exec(url);
if (siteRgx && titleRgx && /\baction=raw\b/.test(url) && /\bctype=/.test(url)) {
return titleRgx1], siteRgx1 || mw.config.get('wgServerName')];
} else return null;
};
window.importScript = (page) => {
loadResource(encodeURIComponent(page), mw.config.get('wgServerName'), 'text/javascript');
};
window.importStyleSheet = (page) => {
loadResource(encodeURIComponent(page), mw.config.get('wgServerName'), 'text/css');
};
let oldMwLoaderLoad = mw.loader.load;
mw.loader.load = function(url, type) {
let linkParts = getSiteTitle(url);
if (linkParts) {
loadResource(linkParts0], linkParts1], type);
} else {
oldMwLoaderLoad.apply(mw.loader, [...arguments]);
}
};
let oldMwLoaderGetScript = mw.loader.getScript;
mw.loader.getScript = function(url) {
let linkParts = getSiteTitle(url);
if (linkParts) {
return loadResource(linkParts0], linkParts1], 'text/javascript');
} else {
return oldMwLoaderGetScript.apply(mw.loader, [...arguments]);
}
};
}
|
(The two code snippets are functionally the very same).
Note that the code block necessarily needs to be at the top of your common.js page for it to take effect.
It replaces the existing implementations of mw.loader.load
, mw.loader.getScript
, importScript
and importStyleSheet
. No changes are needed in the way you normally add new user scripts.
The effect can be seen by keeping the network tab of your browser dev tools open and navigating through different pages. (Don't reload the same page – that's seen as a hard reload in some browsers and causes bypassing of cache.) Also ensure you have the "Disable cache" option unchecked in the devtools. In Chrome, you should see that most script fetches are resolved by the disk cache itself which is very fast.
loadResource function:
s-maxage=86400, max-age=86400, public
while the one set by index.php is private, max-age=0, s-maxage=0
.uselang=content
attribute enables public caching in
Varnish, etc. See
phab:T97096. However, no public caching takes place for requests by users with ability to view revision-deleted or suppressed content (ref:
[1]).origin=*
causes the API to set the Access-Control-Allow-Origin: *
header to avoid CORS issue while loading cross-wiki scripts.