Is there a way to “hot swap” JavaScript code within the browser?

后端 未结 6 1404
南方客
南方客 2020-12-28 09:16

Is there any tool that enables you to \"hot swap\" JavaScript contents while executing a webpage?

I am looking for something similar to what HotSpot does for Java,

6条回答
  •  一整个雨季
    2020-12-28 09:24

    Since I had a similar problem to solve I wrote a small js lib to hotswap javascript, css and image files. It´s of course open source on github: hotswap.js

    Hope it helps.

    Update: I have attached the full lib source here. To use it simply copy the content into a file (e.g.: hotswap.js) and insert the script tag into your website like this:

    
    

    API:

    // refresh .js files
    hotswap.refreshAllJs(arrExcludedFiles);
    hotswap.refreshJs(arrIncludedFiles);
    
    // refresh .css files
    hotswap.refreshAllCss(arrExcludedFiles);
    hotswap.refreshCss(arrIncludedFiles);
    
    // refresh images
    hotswap.refreshAllImg(arrExcludedFiles);
    hotswap.refreshImg(arrIncludedFiles);
    
    // show a gui (this is optional and not required for hotswap to work) (Click on the "H").
    hotswap.createGui();
    
    // Examples:
    // refresh all .js files
    hotswap.refreshAllJs();
    
    // refresh main.css only
    hotswap.refreshCss( ["main.js"] );
    
    // refresh all images (img tags) except "dont-refreh-me.png".
    hotswap.refreshAllImg( ["dont-refreh-me.png"] );
    

    Full source (v. 0.2.0):

    I had to remove all comments to make it under the 30000 char answer limit. The inline html + css is ugly I know, but I wanted to keep this within on single .js file.

    (function() {
        var root = this;
        var previousHotswap = root.hotswap;
        var hotswap = function()
        {
            if (!(this instanceof hotswap))
            {
                return new hotswap();
            }
            else
            {
                return this;
            }
        };
        root.hotswap = hotswap();
        hotswap.prototype.VERSION = '0.2.0';
        hotswap.prototype.RND_PARAM_NAME = 'hs982345jkasg89zqnsl';
        hotswap.prototype.FILE_REMOVAL_DELAY = 400;
        hotswap.prototype.CSS_HTML_PREFIX = 'hs982345jkasg89zqnsl';
        hotswap.prototype._prefix = false;
        hotswap.prototype._prefixCache = [];
        hotswap.prototype._guiCache = {};
        hotswap.prototype._guiGuiRefreshInterval = null;
        hotswap.prototype._guiHtml = '' +
            ''+
            '    
    '+ '
    '+ ' '+ '
    H
    '+ '
    '+ '
    '+ '
      '+ '
    • CSS
    • '+ '
    • '+ '
    '+ '
      '+ '
    • JS
    • '+ '
    • '+ '
    '+ '
      '+ '
    • IMG
    • '+ '
    • '+ '
    '+ '
    '+ ' '+ '
    '; var xGetElementById = function(sId){ return document.getElementById(sId) }, xGetElementsByTagName = function(sTags){ return document.getElementsByTagName(sTags) }, xAppendChild = function(parent, child){ return parent.appendChild(child) }, xCloneNode = function(node){ return document.cloneNode(node) }, xCreateElement = function(sTag){ return document.createElement(sTag) }, xCloneNode = function(ele, deep){ return ele.cloneNode(deep) }, xRemove = function(ele) { if( typeof ele.parentNode != "undefined" && ele.parentNode ) { ele.parentNode.removeChild( ele ); } }, xAddEventListener = function(ele, sEvent, fn, bCaptureOrBubble) { if( xIsEmpty(bCaptureOrBubble) ) { bCaptureOrBubble = false; } if (ele.addEventListener) { ele.addEventListener(sEvent, fn, bCaptureOrBubble); return true; } else if (ele.attachEvent) { return ele.attachEvent('on' + sEvent, fn); } else { ele['on' + sEvent] = fn; } }, xStopPropagation = function(evt) { if (evt && evt.stopPropogation) { evt.stopPropogation(); } else if (window.event && window.event.cancelBubble) { window.event.cancelBubble = true; } }, xPreventDefault = function(evt) { if (evt && evt.preventDefault) { evt.preventDefault(); } else if (window.event && window.event.returnValue) { window.eventReturnValue = false; } }, xContains = function(sHaystack, sNeedle) { return sHaystack.indexOf(sNeedle) >= 0 }, xStartsWith = function(sHaystack, sNeedle) { return sHaystack.indexOf(sNeedle) === 0 }, xReplace = function(sHaystack, sNeedle, sReplacement) { if( xIsEmpty(sReplacement) ) { sReplacement = ""; } return sHaystack.split(sNeedle).join(sReplacement); }, xGetAttribute = function(ele, sAttr) { var result = (ele.getAttribute && ele.getAttribute(sAttr)) || null; if( !result ) { result = ele[sAttr]; } if( !result ) { var attrs = ele.attributes; var length = attrs.length; for(var i = 0; i < length; i++) if(attrs[i].nodeName === sAttr) result = attrs[i].nodeValue; } return result; }, xSetAttribute = function(ele, sAttr, value) { if(ele.setAttribute) { ele.setAttribute(sAttr, value) } else { ele[sAttr] = value; } }, xGetParent = function(ele) { return ele.parentNode || ele.parentElement; }, xInsertAfter = function( refEle, newEle ) { return xGetParent(refEle).insertBefore(newEle, refEle.nextSibling); }, xBind = function(func, context) { if (Function.prototype.bind && func.bind === Function.prototype.bind) { return func.bind(context); } else { return function() { if( arguments.length > 2 ) { return func.apply(context, arguments.slice(2)); } else { return func.apply(context); } }; } }, xIsEmpty = function(value) { var ret = true; if( value instanceof Object ) { for(var i in value){ if(value.hasOwnProperty(i)){return false}} return true; } ret = typeof value === "undefined" || value === undefined || value === null || value === ""; return ret; }, xAddClass = function(ele, sClass) { var clazz = xGetAttribute( ele, "class" ); if( !xHasClass(ele, sClass) ) { xSetAttribute( ele, "class", clazz + " " + sClass ); } }, xRemoveClass = function(ele, sClass) { var clazz = xGetAttribute( ele, "class" ); if( xHasClass(ele, sClass) ) { xSetAttribute( ele, "class", xReplace( clazz, sClass, "" ) ); } }, xHasClass = function(ele, sClass) { var clazz = xGetAttribute( ele, "class" ); return !xIsEmpty(clazz) && xContains( clazz, sClass ); }; hotswap.prototype._recreate = function( type, xcludedFiles, xcludeComparator, nDeleteDelay, bForceRecreation ) { if( typeof nDeleteDelay == "undefined") { nDeleteDelay = 0; } if( typeof bForceRecreation == "undefined") { bForceRecreation = false; } var tags = this._getFilesByType(type, xcludedFiles, xcludeComparator); var newTags = []; var removeTags = []; var i, src, detected, node, srcAttributeName; for(i=0; i 0 ) { for(var i=0; i < removeTags.length; i++) { xSetAttribute(removeTags[i], "data-hotswap-deleted", "1"); } setTimeout( function() { for(var i=0; i < removeTags.length; i++) { xRemove(removeTags[i]); } }, nDeleteDelay); } else { for(var i=0; i < removeTags.length; i++) { xRemove(removeTags[i]); } } }; hotswap.prototype._reload = function( type, xcludedFiles, xcludeComparator ) { var tags = this._getFilesByType(type, xcludedFiles, xcludeComparator); var i, src, node, srcAttributeName; for(i=0; i 0 ) { this.refreshCss( activeFiles['css'] ); } if( activeFiles['js'].length > 0 ) { this.refreshJs( activeFiles['js'] ); } if( activeFiles['img'].length > 0 ) { this.refreshImg( activeFiles['img'] ); } }, hotswap.prototype._guiRefreshStart = function( nSeconds ) { if( this._guiGuiRefreshInterval !== null ) { this._guiRefreshStop(); } var self = this; this._guiGuiRefreshInterval = setInterval( xBind(this._guiRefreshSelected, this), nSeconds * 1000 ); xAddClass( xGetElementById(this.CSS_HTML_PREFIX+"-submit-start"), "inactive" ); xRemoveClass( xGetElementById(this.CSS_HTML_PREFIX+"-submit-stop"), "inactive" ); }, hotswap.prototype._guiRefreshStop = function() { if( this._guiGuiRefreshInterval !== null ) { clearInterval(this._guiGuiRefreshInterval); } this._guiGuiRefreshInterval = null; xRemoveClass( xGetElementById(this.CSS_HTML_PREFIX+"-submit-start"), "inactive" ); xAddClass( xGetElementById(this.CSS_HTML_PREFIX+"-submit-stop"), "inactive" ); } hotswap.prototype.guiRefreshFilesList = function() { this._guiCreateFilesList(); } }).call(this);

提交回复
热议问题