split off jquery from other JS and add CDN option #1766

jQuery (and UI and Migrate) are now loaded separately from the rest of
the JavaScript. This adds at least one HTTP request more but has some
advantages:

* browsers can cache it independently
* the cache is only invalidated when versions update
* we do not apply any transformations (replacements, minimizing, etc) on
  this code anymore which makes our dispatcher faster for the other JS
* browsers seem to load (not execut) both (jquery and other) parallel,
  which might increase download speed a bit

This split allowed for the introduction of a new config: jquerycdn. When
enabled the 3 jquery files are loaded from jQueries CDN. This adds
another two HTTP requests but:

* since it's another host those files do not apply to the 4 request per
  host limit and can be loaded (not executed) in paralell which might
  increase download speeds a bit
* the CDN is distributed worldwide which means files are requested from
  the closest location, increasing the download speeds
* since these files/CDN are very popular, chances are high that people
  already have them cached in their browsers, reducing the download time
  to 0 and effectiely halving the javascript needed to download

The option currently defaults to 'off', but I would argue 'on' would be
the better default.
This commit is contained in:
Andreas Gohr 2016-11-26 14:29:40 +01:00
parent 5928c8e710
commit 61537d4730
7 changed files with 87 additions and 4 deletions

View File

@ -158,6 +158,7 @@ $conf['readdircache'] = 0; //time cache in second for the readdir
/* Network Settings */
$conf['dnslookups'] = 1; //disable to disallow IP to hostname lookups
$conf['jquerycdn'] = 0; //use a CDN for delivering jQuery?
// Proxy setup - if your Server needs a proxy to access the web set these
$conf['proxy']['host'] = '';
$conf['proxy']['port'] = '';

View File

@ -119,6 +119,24 @@ function getInterwiki() {
return $wikis;
}
/**
* Returns the jquery script versions defined in lib/scripts/jquery/versions
*
* @return array
*/
function getJqueryVersions() {
$versions = array();
$lines = file(DOKU_INC . 'lib/scripts/jquery/versions');
foreach($lines as $line ) {
$line = preg_replace('/#.*$/', '', $line);
list($key, $val) = explode('=', $line, 2);
$key = trim($key);
$val = trim($val);
$versions[$key] = $val;
}
return $versions;
}
/**
* returns array of wordblock patterns
*

View File

@ -409,7 +409,30 @@ function tpl_metaheaders($alt = true) {
$script .= 'var JSINFO = '.$json->encode($JSINFO).';';
$head['script'][] = array('type'=> 'text/javascript', '_data'=> $script);
// load external javascript
// load jquery
$jqver = getJqueryVersions();
if($conf['jquerycdn']) {
$head['script'][] = array(
'type' => 'text/javascript', 'charset' => 'utf-8', '_data' => '',
'src' => sprintf('http://code.jquery.com/jquery-%s.min.js', $jqver['JQ_VERSION'])
);
$head['script'][] = array(
'type' => 'text/javascript', 'charset' => 'utf-8', '_data' => '',
'src' => sprintf('http://code.jquery.com/jquery-migrate-%s.min.js', $jqver['JQM_VERSION'])
);
$head['script'][] = array(
'type' => 'text/javascript', 'charset' => 'utf-8', '_data' => '',
'src' => sprintf('https://code.jquery.com/ui/%s/jquery-ui.min.js', $jqver['JQUI_VERSION'])
);
} else {
$jqmod = md5(join('-', $jqver));
$head['script'][] = array(
'type' => 'text/javascript', 'charset' => 'utf-8', '_data' => '',
'src' => DOKU_BASE . 'lib/exe/jquery.php' . '?tseed=' . $jqmod
);
}
// load our javascript dispatcher
$head['script'][] = array(
'type'=> 'text/javascript', 'charset'=> 'utf-8', '_data'=> '',
'src' => DOKU_BASE.'lib/exe/js.php'.'?t='.rawurlencode($conf['template']).'&tseed='.$tseed

42
lib/exe/jquery.php Normal file
View File

@ -0,0 +1,42 @@
<?php
if(!defined('DOKU_INC')) define('DOKU_INC', dirname(__FILE__) . '/../../');
if(!defined('NOSESSION')) define('NOSESSION', true); // we do not use a session or authentication here (better caching)
if(!defined('NL')) define('NL', "\n");
if(!defined('DOKU_DISABLE_GZIP_OUTPUT')) define('DOKU_DISABLE_GZIP_OUTPUT', 1); // we gzip ourself here
require_once(DOKU_INC . 'inc/init.php');
// MAIN
jquery_out();
/**
* Delivers the jQuery JavaScript
*
* We do absolutely nothing fancy here but concatenating the different files
* and handling conditional and gzipped requests
*
* uses cache or fills it
*/
function jquery_out() {
$cache = new cache('jquery', '.js');
$files = array(
DOKU_INC . 'lib/scripts/jquery/jquery.min.js',
DOKU_INC . 'lib/scripts/jquery/jquery-ui.min.js',
DOKU_INC . 'lib/scripts/jquery/jquery-migrate.min.js',
);
$cache_files = $files;
$cache_files[] = __FILE__;
// check cache age & handle conditional request
// This may exit if a cache can be used
$cache_ok = $cache->useCache(array('files' => $cache_files));
http_cached($cache->cache, $cache_ok);
$js = '';
foreach($files as $file) {
$js .= file_get_contents($file)."\n";
}
stripsourcemaps($js);
http_cached_finish($cache->cache, $js);
}

View File

@ -45,10 +45,7 @@ function js_out(){
// array of core files
$files = array(
DOKU_INC."lib/scripts/jquery/jquery$min.js",
DOKU_INC.'lib/scripts/jquery/jquery.cookie.js',
DOKU_INC."lib/scripts/jquery/jquery-ui$min.js",
DOKU_INC."lib/scripts/jquery/jquery-migrate$min.js",
DOKU_INC.'inc/lang/'.$conf['lang'].'/jquery.ui.datepicker.js',
DOKU_INC."lib/scripts/fileuploader.js",
DOKU_INC."lib/scripts/fileuploaderextended.js",

View File

@ -175,6 +175,7 @@ $lang['renderer__plugin'] = '%s (plugin)';
/* Network Options */
$lang['dnslookups'] = 'DokuWiki will lookup hostnames for remote IP addresses of users editing pages. If you have a slow or non working DNS server or don\'t want this feature, disable this option';
$lang['jquerycdn'] = 'Should the jQuery and jQuery UI script files be loaded from the code.jquery.com CDN? This adds additional HTTP requests, but users may already have the files cached.';
/* Proxy Options */
$lang['proxy____host'] = 'Proxy servername';

View File

@ -221,6 +221,7 @@ $meta['readdircache'] = array('numeric');
$meta['_network'] = array('fieldset');
$meta['dnslookups'] = array('onoff');
$meta['jquerycdn'] = array('onoff');
$meta['proxy____host'] = array('string','_pattern' => '#^(|[a-z0-9\-\.+]+)$#i');
$meta['proxy____port'] = array('numericopt');
$meta['proxy____user'] = array('string');