Asp.net MVC Bundle 的使用与扩展

一、Asp.net 自带Bundle的使用:

1. 在Globale中注册与配置

BundleConfig.RegisterBundles(BundleTable.Bundles);

public class BundleConfig

    {

        // For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725

        public static void RegisterBundles(BundleCollection bundles)

        {

            // Use the development version of Modernizr to develop with and learn from. Then, when you're

            // ready for production, use the build tool at http://modernizr.com to pick only the tests you need.

            bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(

                        "~/Scripts/modernizr-*"));



            bundles.Add(new ScriptBundle("~/bundles/jquery").Include(

                        "~/Scripts/jquery-{version}.js"));



#if !DEBUG

            BundleTable.EnableOptimizations = true;

#endif

        }

    }

2. 页面上使用

@Scripts.Render("~/bundles/jquery")

 

二、扩展使用-动态Bundle

要求达到以下目标:1. 支持动态页面上的Bundle,而不必每次在Global中添加Bundle。2.支持Javascript混淆

1. 扩展方法

public static class Extension

    {

        public static IHtmlString Script(this HtmlHelper helper, params string[] urls)

        {

            var bundleDirectory = "~/bundles/" + MakeBundleName("js", urls);

            var bundle = BundleTable.Bundles.GetBundleFor(bundleDirectory);

            if (bundle == null)

            {

                var transform = new JavascriptObfuscator();

                bundle = new ScriptBundle(bundleDirectory).Include(urls);

                bundle.Transforms.Add(transform);

                BundleTable.Bundles.Add(bundle);

            }

            return Scripts.Render(bundleDirectory);

        }



        public static IHtmlString Style(this HtmlHelper helper, params string[] urls)

        {

            var bundleDirectory = "~/bundles/" + MakeBundleName("css", urls);

            var bundle=BundleTable.Bundles.GetBundleFor(bundleDirectory);

            if (bundle == null)

            {

                bundle = new StyleBundle(bundleDirectory).Include(urls);

                BundleTable.Bundles.Add(bundle);

            }

            return Styles.Render(bundleDirectory);

        }



        private static string MakeBundleName(string type, params string[] urls)

        {

            var array =

                urls.SelectMany(url => url.Split('/'))

                    .SelectMany(url => url.Split('.'))

                    .Distinct()

                    .Except(new[] {"~", type});



            return string.Join("-", array);

        }

    }

JavascriptObfuscator 类的实现

public class JavascriptObfuscator : IBundleTransform

    {

        public void Process(BundleContext context, BundleResponse response)

        {

            var p = new ECMAScriptPacker(ECMAScriptPacker.PackerEncoding.Normal, true, false);



            response.Content = p.Pack(response.Content);

        }

    }

2. 页面上使用

@Html.Style("~/Scripts/JQueryUI2/themes/smoothness/jquery.ui.theme.css", "~/Scripts/JQueryUI2/themes/smoothness/jquery.ui.menu.css")

 @Html.Script("~/Scripts/JQueryUI2/ui/jquery.ui.core.js", "~/Scripts/JQueryUI2/ui/jquery.ui.position.js", "~/Scripts/JQueryUI2/ui/jquery.ui.widget.js", "~/Scripts/JQueryUI2/ui/jquery.ui.menu.js")

附录:javascript混淆器代码

/// <summary>

    /// Packs a javascript file into a smaller area, removing unnecessary characters from the output.

    /// </summary>

    public class ECMAScriptPacker : IHttpHandler

    {

        /// <summary>

        /// The encoding level to use. See http://dean.edwards.name/packer/usage/ for more info.

        /// </summary>

        public enum PackerEncoding { None = 0, Numeric = 10, Mid = 36, Normal = 62, HighAscii = 95 };



        private PackerEncoding encoding = PackerEncoding.Normal;

        private bool fastDecode = true;

        private bool specialChars = false;

        private bool enabled = true;



        string IGNORE = "$1";



        /// <summary>

        /// The encoding level for this instance

        /// </summary>

        public PackerEncoding Encoding

        {

            get { return encoding; }

            set { encoding = value; }

        }



        /// <summary>

        /// Adds a subroutine to the output to speed up decoding

        /// </summary>

        public bool FastDecode

        {

            get { return fastDecode; }

            set { fastDecode = value; }

        }



        /// <summary>

        /// Replaces special characters

        /// </summary>

        public bool SpecialChars

        {

            get { return specialChars; }

            set { specialChars = value; }

        }



        /// <summary>

        /// Packer enabled

        /// </summary>

        public bool Enabled

        {

            get { return enabled; }

            set { enabled = value; }

        }



        public ECMAScriptPacker()

        {

            Encoding = PackerEncoding.Normal;

            FastDecode = true;

            SpecialChars = false;

        }



        /// <summary>

        /// Constructor

        /// </summary>

        /// <param name="encoding">The encoding level for this instance</param>

        /// <param name="fastDecode">Adds a subroutine to the output to speed up decoding</param>

        /// <param name="specialChars">Replaces special characters</param>

        public ECMAScriptPacker(PackerEncoding encoding, bool fastDecode, bool specialChars)

        {

            Encoding = encoding;

            FastDecode = fastDecode;

            SpecialChars = specialChars;

        }



        /// <summary>

        /// Packs the script

        /// </summary>

        /// <param name="script">the script to pack</param>

        /// <returns>the packed script</returns>

        public string Pack(string script)

        {

            if (enabled)

            {

                script += "\n";

                script = basicCompression(script);

                if (SpecialChars)

                    script = encodeSpecialChars(script);

                if (Encoding != PackerEncoding.None)

                    script = encodeKeywords(script);

            }

            return script;

        }



        //zero encoding - just removal of whitespace and comments

        private string basicCompression(string script)

        {

            ParseMaster parser = new ParseMaster();

            // make safe

            parser.EscapeChar = '\\';

            // protect strings

            parser.Add("'[^'\\n\\r]*'", IGNORE);

            parser.Add("\"[^\"\\n\\r]*\"", IGNORE);

            // remove comments

            parser.Add("\\/\\/[^\\n\\r]*[\\n\\r]");

            parser.Add("\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\/");

            // protect regular expressions

            parser.Add("\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)", "$2");

            parser.Add("[^\\w\\$\\/'\"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?", IGNORE);

            // remove: ;;; doSomething();

            if (specialChars)

                parser.Add(";;[^\\n\\r]+[\\n\\r]");

            // remove redundant semi-colons

            parser.Add(";+\\s*([};])", "$2");

            // remove white-space

            parser.Add("(\\b|\\$)\\s+(\\b|\\$)", "$2 $3");

            parser.Add("([+\\-])\\s+([+\\-])", "$2 $3");

            parser.Add("\\s+");

            // done

            return parser.Exec(script);

        }



        WordList encodingLookup;

        private string encodeSpecialChars(string script)

        {

            ParseMaster parser = new ParseMaster();

            // replace: $name -> n, $$name -> na

            parser.Add("((\\$+)([a-zA-Z\\$_]+))(\\d*)",

                new ParseMaster.MatchGroupEvaluator(encodeLocalVars));



            // replace: _name -> _0, double-underscore (__name) is ignored

            Regex regex = new Regex("\\b_[A-Za-z\\d]\\w*");



            // build the word list

            encodingLookup = analyze(script, regex, new EncodeMethod(encodePrivate));



            parser.Add("\\b_[A-Za-z\\d]\\w*", new ParseMaster.MatchGroupEvaluator(encodeWithLookup));



            script = parser.Exec(script);

            return script;

        }



        private string encodeKeywords(string script)

        {

            // escape high-ascii values already in the script (i.e. in strings)

            if (Encoding == PackerEncoding.HighAscii) script = escape95(script);

            // create the parser

            ParseMaster parser = new ParseMaster();

            EncodeMethod encode = getEncoder(Encoding);



            // for high-ascii, don't encode single character low-ascii

            Regex regex = new Regex(

                    (Encoding == PackerEncoding.HighAscii) ? "\\w\\w+" : "\\w+"

                );

            // build the word list

            encodingLookup = analyze(script, regex, encode);



            // encode

            parser.Add((Encoding == PackerEncoding.HighAscii) ? "\\w\\w+" : "\\w+",

                new ParseMaster.MatchGroupEvaluator(encodeWithLookup));



            // if encoded, wrap the script in a decoding function

            return (script == string.Empty) ? "" : bootStrap(parser.Exec(script), encodingLookup);

        }



        private string bootStrap(string packed, WordList keywords)

        {

            // packed: the packed script

            packed = "'" + escape(packed) + "'";



            // ascii: base for encoding

            int ascii = Math.Min(keywords.Sorted.Count, (int)Encoding);

            if (ascii == 0)

                ascii = 1;



            // count: number of words contained in the script

            int count = keywords.Sorted.Count;



            // keywords: list of words contained in the script

            foreach (object key in keywords.Protected.Keys)

            {

                keywords.Sorted[(int)key] = "";

            }

            // convert from a string to an array

            StringBuilder sbKeywords = new StringBuilder("'");

            foreach (string word in keywords.Sorted)

                sbKeywords.Append(word + "|");

            sbKeywords.Remove(sbKeywords.Length - 1, 1);

            string keywordsout = sbKeywords.ToString() + "'.split('|')";



            string encode;

            string inline = "c";



            switch (Encoding)

            {

                case PackerEncoding.Mid:

                    encode = "function(c){return c.toString(36)}";

                    inline += ".toString(a)";

                    break;

                case PackerEncoding.Normal:

                    encode = "function(c){return(c<a?\"\":e(parseInt(c/a)))+" +

                        "((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))}";

                    inline += ".toString(a)";

                    break;

                case PackerEncoding.HighAscii:

                    encode = "function(c){return(c<a?\"\":e(c/a))+" +

                        "String.fromCharCode(c%a+161)}";

                    inline += ".toString(a)";

                    break;

                default:

                    encode = "function(c){return c}";

                    break;

            }



            // decode: code snippet to speed up decoding

            string decode = "";

            if (fastDecode)

            {

                decode = "if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\\\w+'};c=1;}";

                if (Encoding == PackerEncoding.HighAscii)

                    decode = decode.Replace("\\\\w", "[\\xa1-\\xff]");

                else if (Encoding == PackerEncoding.Numeric)

                    decode = decode.Replace("e(c)", inline);

                if (count == 0)

                    decode = decode.Replace("c=1", "c=0");

            }



            // boot function

            string unpack = "function(p,a,c,k,e,d){while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p;}";

            Regex r;

            if (fastDecode)

            {

                //insert the decoder

                r = new Regex("\\{");

                unpack = r.Replace(unpack, "{" + decode + ";", 1);

            }



            if (Encoding == PackerEncoding.HighAscii)

            {

                // get rid of the word-boundries for regexp matches

                r = new Regex("'\\\\\\\\b'\\s*\\+|\\+\\s*'\\\\\\\\b'");

                unpack = r.Replace(unpack, "");

            }

            if (Encoding == PackerEncoding.HighAscii || ascii > (int)PackerEncoding.Normal || fastDecode)

            {

                // insert the encode function

                r = new Regex("\\{");

                unpack = r.Replace(unpack, "{e=" + encode + ";", 1);

            }

            else

            {

                r = new Regex("e\\(c\\)");

                unpack = r.Replace(unpack, inline);

            }

            // no need to pack the boot function since i've already done it

            string _params = "" + packed + "," + ascii + "," + count + "," + keywordsout;

            if (fastDecode)

            {

                //insert placeholders for the decoder

                _params += ",0,{}";

            }

            // the whole thing

            return "eval(" + unpack + "(" + _params + "))\n";

        }



        private string escape(string input)

        {

            Regex r = new Regex("([\\\\'])");

            return r.Replace(input, "\\$1");

        }



        private EncodeMethod getEncoder(PackerEncoding encoding)

        {

            switch (encoding)

            {

                case PackerEncoding.Mid:

                    return new EncodeMethod(encode36);

                case PackerEncoding.Normal:

                    return new EncodeMethod(encode62);

                case PackerEncoding.HighAscii:

                    return new EncodeMethod(encode95);

                default:

                    return new EncodeMethod(encode10);

            }

        }



        private string encode10(int code)

        {

            return code.ToString();

        }



        //lookups seemed like the easiest way to do this since 

        // I don't know of an equivalent to .toString(36)

        private static string lookup36 = "0123456789abcdefghijklmnopqrstuvwxyz";



        private string encode36(int code)

        {

            string encoded = "";

            int i = 0;

            do

            {

                int digit = (code / (int)Math.Pow(36, i)) % 36;

                encoded = lookup36[digit] + encoded;

                code -= digit * (int)Math.Pow(36, i++);

            } while (code > 0);

            return encoded;

        }



        private static string lookup62 = lookup36 + "ABCDEFGHIJKLMNOPQRSTUVWXYZ";



        private string encode62(int code)

        {

            string encoded = "";

            int i = 0;

            do

            {

                int digit = (code / (int)Math.Pow(62, i)) % 62;

                encoded = lookup62[digit] + encoded;

                code -= digit * (int)Math.Pow(62, i++);

            } while (code > 0);

            return encoded;

        }



        private static string lookup95 = "¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ";



        private string encode95(int code)

        {

            string encoded = "";

            int i = 0;

            do

            {

                int digit = (code / (int)Math.Pow(95, i)) % 95;

                encoded = lookup95[digit] + encoded;

                code -= digit * (int)Math.Pow(95, i++);

            } while (code > 0);

            return encoded;

        }



        private string escape95(string input)

        {

            Regex r = new Regex("[\xa1-\xff]");

            return r.Replace(input, new MatchEvaluator(escape95Eval));

        }



        private string escape95Eval(Match match)

        {

            return "\\x" + ((int)match.Value[0]).ToString("x"); //return hexadecimal value

        }



        private string encodeLocalVars(Match match, int offset)

        {

            int length = match.Groups[offset + 2].Length;

            int start = length - Math.Max(length - match.Groups[offset + 3].Length, 0);

            return match.Groups[offset + 1].Value.Substring(start, length) +

                match.Groups[offset + 4].Value;

        }



        private string encodeWithLookup(Match match, int offset)

        {

            return (string)encodingLookup.Encoded[match.Groups[offset].Value];

        }



        private delegate string EncodeMethod(int code);



        private string encodePrivate(int code)

        {

            return "_" + code;

        }



        private WordList analyze(string input, Regex regex, EncodeMethod encodeMethod)

        {

            // analyse

            // retreive all words in the script

            MatchCollection all = regex.Matches(input);

            WordList rtrn;

            rtrn.Sorted = new StringCollection(); // list of words sorted by frequency

            rtrn.Protected = new HybridDictionary(); // dictionary of word->encoding

            rtrn.Encoded = new HybridDictionary(); // instances of "protected" words

            if (all.Count > 0)

            {

                StringCollection unsorted = new StringCollection(); // same list, not sorted

                HybridDictionary Protected = new HybridDictionary(); // "protected" words (dictionary of word->"word")

                HybridDictionary values = new HybridDictionary(); // dictionary of charCode->encoding (eg. 256->ff)

                HybridDictionary count = new HybridDictionary(); // word->count

                int i = all.Count, j = 0;

                string word;

                // count the occurrences - used for sorting later

                do

                {

                    word = "$" + all[--i].Value;

                    if (count[word] == null)

                    {

                        count[word] = 0;

                        unsorted.Add(word);

                        // make a dictionary of all of the protected words in this script

                        //  these are words that might be mistaken for encoding

                        Protected["$" + (values[j] = encodeMethod(j))] = j++;

                    }

                    // increment the word counter

                    count[word] = (int)count[word] + 1;

                } while (i > 0);

                /* prepare to sort the word list, first we must protect

                    words that are also used as codes. we assign them a code

                    equivalent to the word itself.

                   e.g. if "do" falls within our encoding range

                        then we store keywords["do"] = "do";

                   this avoids problems when decoding */

                i = unsorted.Count;

                string[] sortedarr = new string[unsorted.Count];

                do

                {

                    word = unsorted[--i];

                    if (Protected[word] != null)

                    {

                        sortedarr[(int)Protected[word]] = word.Substring(1);

                        rtrn.Protected[(int)Protected[word]] = true;

                        count[word] = 0;

                    }

                } while (i > 0);

                string[] unsortedarr = new string[unsorted.Count];

                unsorted.CopyTo(unsortedarr, 0);

                // sort the words by frequency

                Array.Sort(unsortedarr, (IComparer)new CountComparer(count));

                j = 0;

                /*because there are "protected" words in the list

                  we must add the sorted words around them */

                do

                {

                    if (sortedarr[i] == null)

                        sortedarr[i] = unsortedarr[j++].Substring(1);

                    rtrn.Encoded[sortedarr[i]] = values[i];

                } while (++i < unsortedarr.Length);

                rtrn.Sorted.AddRange(sortedarr);

            }

            return rtrn;

        }



        private struct WordList

        {

            public StringCollection Sorted;

            public HybridDictionary Encoded;

            public HybridDictionary Protected;

        }



        private class CountComparer : IComparer

        {

            HybridDictionary count;



            public CountComparer(HybridDictionary count)

            {

                this.count = count;

            }



            #region IComparer Members



            public int Compare(object x, object y)

            {

                return (int)count[y] - (int)count[x];

            }



            #endregion

        }

        #region IHttpHandler Members



        public void ProcessRequest(HttpContext context)

        {

            // try and read settings from config file

            if (System.Configuration.ConfigurationSettings.GetConfig("ecmascriptpacker") != null)

            {

                NameValueCollection cfg =

                    (NameValueCollection)

                    System.Configuration.ConfigurationSettings.GetConfig("ecmascriptpacker");

                if (cfg["Encoding"] != null)

                {

                    switch (cfg["Encoding"].ToLower())

                    {

                        case "none":

                            Encoding = PackerEncoding.None;

                            break;

                        case "numeric":

                            Encoding = PackerEncoding.Numeric;

                            break;

                        case "mid":

                            Encoding = PackerEncoding.Mid;

                            break;

                        case "normal":

                            Encoding = PackerEncoding.Normal;

                            break;

                        case "highascii":

                        case "high":

                            Encoding = PackerEncoding.HighAscii;

                            break;

                    }

                }

                if (cfg["FastDecode"] != null)

                {

                    if (cfg["FastDecode"].ToLower() == "true")

                        FastDecode = true;

                    else

                        FastDecode = false;

                }

                if (cfg["SpecialChars"] != null)

                {

                    if (cfg["SpecialChars"].ToLower() == "true")

                        SpecialChars = true;

                    else

                        SpecialChars = false;

                }

                if (cfg["Enabled"] != null)

                {

                    if (cfg["Enabled"].ToLower() == "true")

                        Enabled = true;

                    else

                        Enabled = false;

                }

            }

            // try and read settings from URL

            if (context.Request.QueryString["Encoding"] != null)

            {

                switch (context.Request.QueryString["Encoding"].ToLower())

                {

                    case "none":

                        Encoding = PackerEncoding.None;

                        break;

                    case "numeric":

                        Encoding = PackerEncoding.Numeric;

                        break;

                    case "mid":

                        Encoding = PackerEncoding.Mid;

                        break;

                    case "normal":

                        Encoding = PackerEncoding.Normal;

                        break;

                    case "highascii":

                    case "high":

                        Encoding = PackerEncoding.HighAscii;

                        break;

                }

            }

            if (context.Request.QueryString["FastDecode"] != null)

            {

                if (context.Request.QueryString["FastDecode"].ToLower() == "true")

                    FastDecode = true;

                else

                    FastDecode = false;

            }

            if (context.Request.QueryString["SpecialChars"] != null)

            {

                if (context.Request.QueryString["SpecialChars"].ToLower() == "true")

                    SpecialChars = true;

                else

                    SpecialChars = false;

            }

            if (context.Request.QueryString["Enabled"] != null)

            {

                if (context.Request.QueryString["Enabled"].ToLower() == "true")

                    Enabled = true;

                else

                    Enabled = false;

            }

            //handle the request

            TextReader r = new StreamReader(context.Request.PhysicalPath);

            string jscontent = r.ReadToEnd();

            r.Close();

            context.Response.ContentType = "text/javascript";

            context.Response.Output.Write(Pack(jscontent));

        }



        public bool IsReusable

        {

            get

            {

                if (System.Configuration.ConfigurationSettings.GetConfig("ecmascriptpacker") != null)

                {

                    NameValueCollection cfg =

                        (NameValueCollection)

                        System.Configuration.ConfigurationSettings.GetConfig("ecmascriptpacker");

                    if (cfg["IsReusable"] != null)

                        if (cfg["IsReusable"].ToLower() == "true")

                            return true;

                }

                return false;

            }

        }



        #endregion

    }

 

总结:

1. 通过继承IBundleTransform接口能够对Asp.net MVC中自带Bundle类进行扩展。

2. 可以动态添加Bundle,而不必在Globale中一次性全都添加。

3. 除了Asp.net MVC自带的Bundle外,还有其他的Bundle插件,例如Bundle Transformer: YUI 1.8.0

 

 

本文摘自:http://blog.csdn.net/leewhoee/article/details/19107013#t3

你可能感兴趣的:(asp.net)