2013-05-02 43 views
6

Tôi sử dụng uglifyjs để rút gọn một tập hợp các tệp được nối, hoạt động tốt nhưng không đủ tốt. Các lib xây dựng sử dụng không gian tên, vì vậy các lớp học, chức năng và các hằng số được lưu trữ trong một biến namespace root:Mangle lớp lồng nhau và biến với uglifyjs

(function() { 
    var root = { api:{}, core:{}, names:{} }; 

    /* util.js file */ 
    root.names.SOME_LONG_NAMED_CONST='Angel'; 

    /* Person.js file */ 
    root.core.Person = function(name) { this.name = name }; 

    /* API.js with the functions we want to expose */ 
    root.api.perform = function(param_for_api) { /* do something */ } 

    window.lib_name.perform = root.api.perform; 

})(); 

mà được giảm bớt với phiên bản không-để-minimal

(function(){var a={api:{},core:{},names:{}};a.names.SOME_LONG_NAMED_CONST="Angel",a.core.Person=function(a){this.name=a},a.api.perform=function(){},window.lib_name.perform=a.api.perform})(); 

Tôi hiểu làm xấu đi có thể nghĩ var gốc đó là cấu trúc dữ liệu phải được giữ nguyên và không thể thay đổi được. Có cách nào để cho uglify mangle các tên lồng nhau trong không gian tên gốc?

Trả lời

8

Khi bạn giảm thiểu Javascript, bạn chỉ có thể thay đổi tên biến, api, lõi và tên không phải là biến mà là thuộc tính của đối tượng. Nếu chúng được thay đổi bởi trình tối ưu hóa, bạn có khả năng sẽ nhận được kết quả không mong muốn. Điều gì nếu trong mã của bạn, bạn sẽ gọi

root["api"].perform = function()... 

hoặc thậm chí một cái gì đó giống như

function doIt(section, method, argument) { 
    root[section][method](argument); 
} 
doIt('api','perform', 101); 

Tất cả JS hoàn toàn hợp pháp, nhưng một minimizer không bao giờ có thể tìm ra những gì đang xảy ra.

+2

Có, tôi hiểu bạn đối số. Tôi có thể làm gì khác không? – whadar

+0

Câu trả lời rất hay. – SoonDead

+0

@Jan Developer có thể quyết định điều này, chứ không phải uglifyjs. Tôi có thể cấu hình uglifyjs về điều đó: 'không xâu chuỗi', hoặc, 'không chạm vào các biến mà tôi đã nói với bạn'. Tôi đã sử dụng một obfuscator và nó đã được hoàn toàn uglifying tất cả mọi thứ mà không có một vấn đề. –

4

Ngoài điểm @JanMisker (hoàn toàn hợp lệ), việc ghi lại thuộc tính không an toàn vì chúng có thể được tiếp xúc với mã ngoài phạm vi của việc rút gọn.

Mặc dù tự chức năng thực hiện có một phạm vi, và nếu mã duy nhất là

(function() { 
    var root = { api:{}, core:{}, names:{} }; 
    root.names.SOME_LONG_NAMED_CONST='Angel'; 
    alert(root.names.SOME_LONG_NAMED_CONST); // some code that does something 
})(); 

Đúng là ngoài chức năng, không có cách nào để truy cập vào các đối tượng gốc, vì vậy việc viết lại các tên thuộc tính là an toàn, và đoạn mã sau sẽ cho kết quả giống nhau:

(function() { 
    var a = { b:{}, c:{}, d:{} }; 
    a.d.e='Angel'; 
    alert(a.d.e); 
})(); 

Nhưng ngay cả khi bạn đang ở trong phạm vi cá nhân của bạn, bạn có thể truy cập, và quan trọng hơn gán cho các biến từ một phạm vi bên ngoài! Hãy tưởng tượng điều này:

(function() { 
    var root = { api:{}, core:{}, names:{} }; 
    root.api.perform = function(param_for_api) { /* do something */ } 
    window.lib_name = root.api; 
})(); 

Bạn không chỉ trưng bày một chức năng mà là một đối tượng có chức năng trên đó. Và chức năng sẽ hiển thị từ bất kỳ nơi nào cửa sổ hiển thị.

Vì vậy, ví dụ văn bản sau đây trong giao diện điều khiển javascript sẽ mang lại kết quả khác nhau có và không có việc rút gọn:

window.lib_name.perform(asdf); 

Với việc rút gọn bạn sẽ phải viết:

window.lib_name.f(asdf); 

Hoặc một cái gì đó tương tự.

Hãy nhớ rằng luôn có thể có mã bên ngoài việc rút gọn của bạn.Nó không phải là rất quan trọng để có JS tối thiểu tuyệt đối, nhưng nếu nó là rất quan trọng đối với một số lý do (ví dụ: người ngoài hành tinh bắt cóc con gái riêng của bạn, và cách duy nhất để có cô ấy là để giảm bớt dưới 100 ký tự hoặc vì vậy), bạn có thể thay thế một tên thuộc tính dài không mong muốn thành một tên ngắn hơn, chỉ cần chắc chắn rằng nó sẽ không bị lộ ra ở bất kỳ đâu, và không được truy cập thông qua ký hiệu mảng kết hợp (root['api']).

+1

Mặc dù con gái chưa bị bắt cóc :) nhu cầu về lồng ghép lồng nhau là rất quan trọng cho việc rút gọn. Tôi nghĩ rằng kích thước mã sẽ có thể được giảm hơn 20%. lưu ý rằng tôi cố ý sử dụng window.lib_name.perform = root.api.perform nên việc rút gọn sẽ vẫn làm việc – whadar

+0

Vấn đề là các cách gõ năng động và về cơ bản tất cả mọi thứ về các đối tượng trong javascript là nên nghiêm khắc rằng uglifyjs chỉ không thể Chắc chắn 100% những gì bạn phơi bày chỉ bằng cách xem kịch bản của bạn. Ngoài ra @JanMisker nêu ra một điểm về chuỗi. Chúng có thể được xây dựng động và sau đó được sử dụng như một bộ chỉ mục cho một mảng kết hợp. Không có cách nào để uglifyjs thực hiện một cách an toàn việc rút gọn cho điều đó. Bạn sẽ phải làm điều đó bằng tay. – SoonDead

+0

Đóng cửa có thể thực hiện (ADVANCED_OPTIMIZATIONS). – miracle2k

1

như @ Jan-Misker giải thích trong câu trả lời của mình, mang tên tài sản mang tên KHÔNG là ý tưởng hay vì nó có khả năng phá vỡ mã của bạn.

Tuy nhiên, bạn có thể workaround nó bằng cách xác định tên thuộc tính như các biến địa phương, và sửa đổi tất cả .properties tới [phím], để làm cho kích thước file nhỏ hơn:

(function() { 
    var API = 'api'; 
    var CORE = 'core'; 
    var NAMES = 'names'; 
    var SLNC = 'SOME_LONG_NAMED_CONST'; 

    var root = {}; 
    root[API]={}; 
    root[CORE]={}; 
    root[NAMES]={}; 

    /* util.js file */ 
    root[NAMES][SLNC] ='Angel'; 

    /* Person.js file */ 
    root[CORE].Person = function(name) { this.name = name }; 

    /* API.js with the functions we want to expose */ 
    root[API].perform = function(param_for_api) { /* do something */ } 

    window.lib_name.perform = root[API].perform; 

})(); 

Bởi vì bây giờ tất cả các thuộc tính đã trở thành một địa phương biến, làm xấu đi js sẽ mangle/rút ngắn tên biến và kết quả bạn kích thước tập tin tổng thể giảm:

!function(){var a="api",b="core",c="names",d="SOME_LONG_NAMED_CONST",e={};e[a]={},e[b]={},e[c]={},e[c][d]="Angel",e[b].Person=function(a){this.name=a},e[a].perform=function(){},window.lib_name.perform=e[a].perform}(); 

Tuy nhiên, giảm kích thước tập tin không có nghĩa là bạn sẽ nhận được ngắn hơn thời gian tải trên máy chủ thực, bởi vì thường của chúng tôi http vận chuyển được gzipped, hầu hết lại các kiến ​​nghị sẽ được nén bởi máy chủ http của bạn và nó thực hiện một công việc tốt hơn con người.

1

Phiên bản mới nhất của uglify (hôm nay) có mangling thuộc tính đối tượng, xem v2.4.18. Nó cũng hỗ trợ các tệp được dành riêng để loại trừ cả thuộc tính đối tượng và biến mà bạn không muốn bị xáo trộn. Kiểm tra nó ra.

Sử dụng --mangle-props tùy chọn và --reserved-file filename1.json filename2.json vv ....

+0

Tôi nghĩ rằng sẽ dễ dàng nói xấu hơn với 'mangle chỉ cái này' thay vì' mangle ngoại trừ điều này' bởi vì sau này mỗi từ khóa mới bạn tạo có thể bị xáo trộn vô tình (quên chèn vào danh sách loại trừ) –

4

Tôi đã cố gắng sử dụng --mangle-props của UglifyJS2 và có thể cho bạn biết: 'nó làm cho một mớ hỗn độn'.

Như ai đó chỉ ra: 'Nhà phát triển nên quyết định những gì thuộc tính để mangle, không uglifyjs'

tôi tiếp cận vấn đề bằng các tùy chọn này:

--mangle-props 
--mangle-regexp="/_$/" 

Các regex phù hợp với bất kỳ tài sản với một gạch dưới ở cuối.

Bạn đã yêu cầu mangle tên lồng nhau trong không gian tên gốc. Vì vậy, mã của bạn:

(function() { 
    var root = { api:{}, core:{}, names:{} }; 

    root.names.SOME_LONG_NAMED_CONST_='Angel'; 

    root.core.Person_ = function(name) { this.name = name }; 

    root.api.perform_ = function(param_for_api) { } 

    window.lib_name.perform = root.api.perform; 
})(); 

sẽ cho kết quả này:

(function() { 
    var n = { 
     api: {}, 
     core: {}, 
     names: {} 
    }; 
    n.names.a = "Angel"; 
    n.core.b = function(n) { 
     this.name = n; 
    }; 
    n.api.c = function(n) {}; 
    window.lib_name.perform = n.api.c; 
})(); 

Command: uglifyjs --beautify --mangle --mangle-props --mangle-regex="/_$/" -- file.js

Nếu bạn muốn mangle cấp độ đầu tiên của namespace gốc (api, core, names) chỉ cần đặt một dấu gạch dưới trên chúng (api_, core_, names_) , bạn có quyền kiểm soát;)

Chỉ cần ghi chú bên: khi bạn đang mangling tài sản có thể sử dụng bởi các tập tin js khác, bạn nên mangle tất cả các tập tin cùng với lệnh tương tự, do đó, cùng một định danh sẽ được sử dụng trên tất cả các tập tin.

+0

Tốt. Mặc dù tôi thích các dấu gạch dưới hàng đầu (phần còn lại từ ngày C++), và mangle với '/^_ /' – cmroanirgo

+1

Tôi thấy, nhưng tôi đã làm nó với dấu gạch dưới vì nó dễ gõ hơn (hoàn thành mã hoạt động tốt hơn). –

Các vấn đề liên quan