“ ผู้ให้บริการที่ไม่รู้จัก: aProvider <- a” ฉันจะค้นหาผู้ให้บริการเดิมได้อย่างไร


100

เมื่อฉันกำลังโหลดแอปพลิเคชัน AngularJS เวอร์ชัน minified (ผ่าน UglifyJS) ฉันได้รับข้อผิดพลาดต่อไปนี้ในคอนโซล:

Unknown provider: aProvider <- a

ตอนนี้ฉันรู้แล้วว่านี่เป็นเพราะชื่อตัวแปรที่สับสน เวอร์ชันที่ไม่มีการเชื่อมต่อใช้งานได้ดี อย่างไรก็ตามฉันทำต้องการที่จะทำให้การใช้ชื่อ mangling ตัวแปรตามที่มันลดฮวบขนาดของไฟล์ที่ส่งออกของเรา JS

ด้วยเหตุนี้เราจึงใช้ngminในกระบวนการสร้างของเรา แต่ดูเหมือนจะไม่สามารถแก้ไขปัญหานี้ได้แม้ว่าจะให้บริการเราได้ดีในอดีตก็ตาม

ดังนั้นเพื่อแก้ปัญหานี้ฉันจึงเปิดใช้งานแผนที่แหล่งที่มาในงานที่น่าเกลียดของเรา พวกเขาสร้างขึ้นได้ดีและ Chrome ก็ทำได้โหลดแผนที่จากเซิร์ฟเวอร์ แต่ฉันยังคงได้รับข้อความแสดงข้อผิดพลาดที่ไม่เป็นประโยชน์เหมือนเดิมแม้ว่าฉันจะรู้สึกว่าตอนนี้ฉันควรจะเห็นชื่อเดิมของผู้ให้บริการแล้วก็ตาม

ฉันจะทำให้ Chrome ใช้แผนที่ต้นทางเพื่อบอกฉันได้อย่างไรว่าผู้ให้บริการรายใดเป็นปัญหาที่นี่หรืออีกวิธีหนึ่งฉันจะค้นหาผู้ให้บริการด้วยวิธีอื่นได้อย่างไร


คุณสามารถลองเพิ่มความคิดเห็นที่แตกต่างกันไปยังไฟล์ซอร์ส JS ทุกไฟล์ (หากยังไม่มีอยู่ในกรณีนี้) และใช้ตัวเลือก PreserveComments ของ UglifyJS ซึ่งจะช่วยให้คุณทราบว่าไฟล์ใดมีรหัสที่ไม่ถูกต้อง
JB Nizet

คุณบังเอิญใช้มัณฑนากรใช่หรือไม่? ฉันพบว่า ngmin ดูเหมือนจะเขียนมัณฑนากรไม่ถูกต้องเมื่อฉันเคยใช้มันในอดีตซึ่งส่งผลให้เกิดข้อผิดพลาดเหมือนของคุณ
dherman

@JBNizet: ฉันชอบแนวคิดนี้ แต่การเพิ่มคำสั่งนั้นให้กับตัวเลือกดูเหมือนจะไม่มีผลใด ๆ
Der Hochstapler

@dherman: คุณช่วยยกตัวอย่างมัณฑนากรให้ฉันได้ไหม? ฉันไม่แน่ใจว่าพวกเขาจะเป็นอย่างไรในบริบทนี้
Der Hochstapler

ดูgithub.com/gruntjs/grunt-contrib-uglify (ถ้าคุณใช้คำราม) ค่าของตัวเลือกควรเป็น "ทั้งหมด"
JB Nizet

คำตอบ:


193

ฉันยังอยากรู้ว่าฉันจะพบสถานที่ในซอร์สโค้ดของเราที่ทำให้เกิดปัญหานี้ได้อย่างไร แต่หลังจากนั้นฉันก็สามารถค้นหาปัญหาด้วยตนเองได้

มีการประกาศฟังก์ชันคอนโทรลเลอร์ในขอบเขตส่วนกลางแทนที่จะใช้การ.controller()โทรบนโมดูลแอปพลิเคชัน

จึงมีบางอย่างดังนี้:

function SomeController( $scope, i18n ) { /* ... */ }

มันใช้งานได้ดีสำหรับ AngularJS แต่เพื่อให้ทำงานได้อย่างถูกต้องด้วยการโกงฉันต้องเปลี่ยนเป็น:

var applicationModule = angular.module( "example" );
function SomeController( $scope, i18n ) { /* ... */ }
applicationModule.controller( "SomeController", [ "$scope", "i18n", SomeController ] );

หลังจากการทดสอบเพิ่มเติมฉันพบอินสแตนซ์ของคอนโทรลเลอร์จำนวนมากที่ทำให้เกิดปัญหาเช่นกัน นี่คือวิธีที่ฉันพบแหล่งที่มาของทั้งหมดด้วยตนเอง :

ก่อนอื่นฉันคิดว่ามันค่อนข้างสำคัญในการเปิดใช้งานการตกแต่งเอาต์พุตในตัวเลือกอัปลักษณ์ สำหรับงานฮึดฮัดของเราที่หมายถึง:

options : {
    beautify : true,
    mangle   : true
}

จากนั้นฉันก็เปิดเว็บไซต์โครงการใน Chrome โดยเปิด DevTools ซึ่งส่งผลให้เกิดข้อผิดพลาดเช่นเดียวกับการบันทึกด้านล่าง:

ป้อนคำอธิบายภาพที่นี่

วิธีในการติดตามการโทรที่เราสนใจคือวิธีที่ฉันทำเครื่องหมายด้วยลูกศร นี่คือproviderInjectorในinjector.js . คุณจะต้องวางเบรกพอยต์ที่มีข้อยกเว้น:

ป้อนคำอธิบายภาพที่นี่

เมื่อคุณเรียกใช้แอปพลิเคชันอีกครั้งเบรกพอยต์จะถูกโจมตีและคุณสามารถกระโดดขึ้นกลุ่มการโทรได้ จะมีสายเรียกเข้าจากinvokeในinjector.jsสตริง "โทเค็นการฉีดไม่ถูกต้อง":

ป้อนคำอธิบายภาพที่นี่

localsพารามิเตอร์ (mangled ไปdในรหัสของฉัน) ช่วยให้ความคิดที่ดีงามเกี่ยวกับการที่วัตถุที่อยู่ในแหล่งที่มาของปัญหาคือ:

ป้อนคำอธิบายภาพที่นี่

การgrepค้นหาแหล่งที่มาของเราอย่างรวดเร็วพบหลายกรณีmodalInstanceแต่เมื่อไปจากที่นั่นจะพบจุดนี้ได้ง่ายในแหล่งที่มา:

var ModalCreateEditMeetingController = function( $scope, $modalInstance ) {
};

ซึ่งต้องเปลี่ยนเป็น:

var ModalCreateEditMeetingController = [ "$scope", "$modalInstance", function( $scope, $modalInstance ) {
} ];

ในกรณีที่ตัวแปรไม่เก็บข้อมูลที่เป็นประโยชน์คุณยังสามารถกระโดดขึ้นไปอีกด้านบนสแต็กและคุณควรโทรออกinvokeซึ่งควรมีคำแนะนำเพิ่มเติม:

ป้อนคำอธิบายภาพที่นี่

ป้องกันไม่ให้เกิดเหตุการณ์เช่นนี้อีก

ตอนนี้คุณหวังว่าจะพบปัญหาแล้วฉันรู้สึกว่าควรพูดถึงวิธีที่ดีที่สุดเพื่อหลีกเลี่ยงไม่ให้เกิดขึ้นอีกในอนาคต

เห็นได้ชัดว่าคุณสามารถใช้คำอธิบายประกอบอาร์เรย์แบบอินไลน์ได้ทุกที่หรือใช้$injectคำอธิบายประกอบคุณสมบัติ (ขึ้นอยู่กับความชอบของคุณ) และพยายามอย่าลืมมันในอนาคต หากคุณทำเช่นนั้นตรวจสอบให้แน่ใจว่าได้เปิดใช้งานโหมดการฉีดพึ่งพาอย่างเข้มงวดเพื่อตรวจจับข้อผิดพลาดเช่นนี้ตั้งแต่เนิ่นๆ

ระวัง! ในกรณีที่คุณใช้ Angular Batarang StrictDI อาจไม่ได้ผลสำหรับคุณเนื่องจาก Angular Batarang จะฉีดรหัสที่ไม่ได้ระบุลงในของคุณ (Batarang ไม่ดี!)

หรือคุณอาจให้ng-annotateดูแลก็ได้ ฉันขอแนะนำให้ทำเช่นนั้นเนื่องจากจะช่วยขจัดความผิดพลาดที่อาจเกิดขึ้นในพื้นที่นี้ออกไปได้มากเช่น

  • ไม่มีคำอธิบายประกอบ DI
  • คำอธิบายประกอบ DI ไม่สมบูรณ์
  • คำอธิบายประกอบ DI ผิดลำดับ

การทำให้คำอธิบายประกอบเป็นปัจจุบันเป็นเพียงความเจ็บปวดที่ตูดและคุณไม่ควรทำถ้าทำได้โดยอัตโนมัติ ng-annotate ทำอย่างนั้น

มันควรบูรณาการอย่างเป็นขั้นตอนการสร้างของคุณด้วยเสียงฮึดฮัด-ng-บันทึกย่อและอึก-ng-คำอธิบายประกอบ


12
นี่คือการเขียนที่ยอดเยี่ยมเขียนด้วยความระมัดระวัง ฉันเพิ่งพบปัญหานี้ดูเหมือนว่าจะเป็นปัญหาที่อยู่ลึกลงไปใน ngmin ที่ไหนสักแห่ง เคล็ดลับของคุณช่วยให้ฉันรู้ว่าควรดูที่ไหน ในที่สุดฉันก็แค่ "array-ified" พารามิเตอร์เชิงมุมทั้งหมดของฉันปัญหาก็หายไป การสร้างก่อนหน้านี้ทั้งหมด ng-minified ทำได้ดีและไม่มีอะไรเปลี่ยนแปลงมากนัก ฉันไม่ได้เพิ่มฟังก์ชั่นส่วนกลางใด ๆ - มันหยุดทำงานอย่างลึกลับโดยการยุ่งเกี่ยวกับคอนโทรลเลอร์ / คำสั่ง / บริการ / ตัวกรอง?
zenocon

นี่เป็นแหล่งความช่วยเหลือที่ดี ฉันไม่รู้ว่าคุณต้องใช้ไวยากรณ์อาร์เรย์ (อินไลน์) สำหรับฟังก์ชันอื่น ๆ เช่นการแก้ไขเราเตอร์, .run, .config ฯลฯ
VDest

4
ในกรณีของฉันมันเป็นตัวควบคุมในคำสั่ง หากในตัวแปร 'd' คุณจะเห็น $ attr นั่นอาจเป็นปัญหาเดียวกัน คุณควรห่อพารามิเตอร์ในวงเล็บอาร์เรย์สำหรับตัวควบคุมคำสั่งภายใน controller: ["$ scope", function ($ scope) {... }] แทน controller: function ($ scope) {... }
alex naumov

ขอบคุณมากสำหรับการเขียนและการแก้ปัญหาของคุณโดยใช้สัญกรณ์การฉีด / อาร์เรย์ที่ปลอดภัยสำหรับการอ้างอิงฟังก์ชัน var ฉันก็มีข้อผิดพลาดนี้เช่นกันและเนื่องจากวิธีแก้ปัญหาของคุณฉันจึงสามารถก้าวต่อไปได้ คุณร็อค!
Frankie Loscavio

1
ทุกครั้งที่มีปัญหานี้ฉันอ่านอีกครั้งและต้องการโหวตเรื่องนี้อีกครั้ง Btw นี่คือวิธีการตั้งค่าเวอร์ชันอึกuglify({ output : { beautify : true }})
Eugene Gluhotorenko

30

การเขียนของ Oliver Salzburg นั้นยอดเยี่ยมมาก โหวตแล้ว

เคล็ดลับสำหรับทุกคนที่อาจมีข้อผิดพลาดนี้ ของฉันเกิดจากการลืมส่งในอาร์เรย์สำหรับตัวควบคุมคำสั่ง:

ไม่ดี

return {
    restrict: "E",
    scope: {                
    },
    controller: ExampleDirectiveController,
    templateUrl: "template/url/here.html"
};

ดี

return {
    restrict: "E",
    scope: {                
    },
    controller: ["$scope", ExampleDirectiveController],
    templateUrl: "template/url/here.html"
};

2
นี่มันช่างหน้าด้าน ... Uglify ไม่ได้ทำให้ฉันเป็นเช่นนี้จนกว่าจะมีการอัปเดตล่าสุด!
SamMorrowDrums

ปัญหาของฉันเหมือนเดิม แต่ปรากฎว่าสิ่งที่ฉันต้องเพิ่มคือ/* @ngInject */ก่อนฟังก์ชัน ดูเหมือนว่าจะทำส่วนการฉีดที่ซับซ้อนโดยไม่จำเป็นต้องพิมพ์แต่ละโมดูลที่รวมอยู่ (ฉันใช้ Yeoman)
Nicholas Blasgen

25

ใช้ ng -rict-di กับ ng-app

หากคุณกำลังใช้เชิงมุม 1.3 คุณสามารถช่วยตัวเองโลกของการบาดเจ็บโดยใช้ngStrictDiสั่งกับ ngApp:

<html lang="en" ng-app="myUglifiablyGreatApp" ng-strict-di>

ตอนนี้ - การลดขนาดล่วงหน้า - สิ่งใดก็ตามที่ไม่ใช้คำอธิบายประกอบจะทำให้คอนโซลของคุณระเบิดและคุณสามารถเห็นชื่อของ friggin โดยไม่ต้องล่าสัตว์ผ่านสแต็กเทรซ

ตามเอกสาร:

แอปพลิเคชันจะล้มเหลวในการเรียกใช้ฟังก์ชันที่ไม่ใช้คำอธิบายประกอบฟังก์ชันที่ชัดเจน (และไม่เหมาะสำหรับการลดขนาด)

หนึ่งข้อแม้ก็เพียง แต่ตรวจพบว่ามีมีคำอธิบายประกอบไม่ว่าคำอธิบายประกอบจะเสร็จสมบูรณ์

ความหมาย:

['ThingOne', function(ThingA, ThingB) {  }]

จะจับไม่ได้ว่า ThingB ไม่ได้เป็นส่วนหนึ่งของคำอธิบายประกอบ

เครดิตสำหรับเคล็ดลับนี้จะมอบให้แก่ผู้ใช้ng-annotateซึ่งขอแนะนำสำหรับ ngMin ที่เลิกใช้แล้วในขณะนี้


สิ่งนี้ต้องการการโหวตเพิ่มขึ้น เหมาะสำหรับการดีบักแอปที่ไม่เคยใช้ ngInject หรือไวยากรณ์อาร์เรย์สตริง
Michael Pearson

11

ในการลดขนาดเชิงมุมสิ่งที่คุณต้องทำคือเปลี่ยนการประกาศของคุณเป็นโหมด "การประกาศอาร์เรย์" "เช่น:

จาก:

var demoApp= angular.module('demoApp', []);
demoApp.controller(function demoCtrl($scope) {
} );

ถึง

var demoApp= angular.module('demoApp', []);
demoApp.controller(["$scope",function demoCtrl($scope) {
}]);

จะประกาศบริการโรงงานได้อย่างไร?

demoApp.factory('demoFactory', ['$q', '$http', function ($q, $http) {
    return {
          //some object
    };
}]);

ฉันรู้ว่า. นั่นเป็นเหตุผลที่เราใช้ ngmin ฉันสงสัยว่ามันมีปัญหากับบางส่วนของแหล่งที่มาของเราหรือการอ้างอิง ซึ่งเป็นสาเหตุที่ฉันพยายามหาต้นตอของปัญหานี้
Der Hochstapler

1
คำแนะนำของฉันคือให้คุณสร้างรหัสของคุณด้วยวิธีนี้ ดังนั้นคุณสามารถใช้ minifier ใดก็ได้
Dalorzo

3
ฉันกำลังสร้างโค้ดของเราด้วยวิธีนี้ แต่เรามีการพึ่งพาภายนอกที่ไม่มี ngmin ได้แก้ไขปัญหานี้ได้ดีสำหรับเราในอดีต ฉันถือว่าการเปลี่ยนแปลงล่าสุดสร้างปัญหานี้ ตอนนี้ฉันต้องการค้นหาแหล่งที่มาของปัญหานี้เพื่อที่ฉันจะได้แก้ไขได้อย่างถูกต้องในโค้ดของเราการพึ่งพาของเราหรืออาจเป็นใน ngmin เอง
Der Hochstapler

เนื่องจากปัญหาดูเหมือนเฉพาะเจาะจงมากสำหรับส่วนประกอบหรือรหัสเฉพาะจึงยากที่จะให้คำแนะนำอย่างน้อยก็จากจุดสิ้นสุดของฉัน
Dalorzo

ngmin ไม่ต้องการให้คุณใช้โหมดการประกาศอาร์เรย์ แต่จะเพิ่มการประกาศที่ไร้ประโยชน์มากมาย
Nanocom

8

ฉันเพิ่งมีปัญหาเดียวกันและแก้ไขได้โดยเพียงแค่แทนที่ ngmin (ตอนนี้เลิกใช้งานแล้ว) ด้วย ng-annotate สำหรับงานบิลด์ฮึดฮัดของฉัน

ดูเหมือนว่าเชิงมุมของ Yeoman ได้รับการอัปเดตให้ใช้ ng-annotate เช่นกัน: https://github.com/yeoman/generator-angular/commit/3eea4cbeb010eeaaf797c17604b4a3ab5371eccb

อย่างไรก็ตามหากคุณใช้รุ่นเก่าของ yeoman angular เหมือนฉันให้แทนที่ ng-min ด้วย ng-annotate ใน package.json ของคุณ:

-    "grunt-ngmin": "^0.0.3",
+    "grunt-ng-annotate": "^0.3.0",

เรียกใช้npm install(แล้วเลือกnpm prune) และตามการเปลี่ยนแปลงในการกระทำGruntfile.jsที่จะแก้ไข


7

เพื่อที่จะทราบว่าชื่อตัวแปรดั้งเดิมคืออะไรคุณสามารถเปลี่ยนวิธีการ uglify ทำให้ตัวแปรมีการเปลี่ยนแปลงได้:

../node_modules/grunt-contrib-uglify/node_modulesuglify-js/lib/scope.js

SymbolDef.prototype = {
  unmangleable: [...],
  mangle: function(options) {
    [...]
    this.mangled_name = s.next_mangled(options, this)+"_orig_"+this.orig[0].name;
    [...]
  }
};

และตอนนี้ข้อผิดพลาดนั้นชัดเจนมากขึ้น

Error: [$injector:unpr] Unknown provider: a_orig_$stateProvider
http://errors.angularjs.org/1.3.7/$injector/unpr?p0=a_orig_%24stateProvider
at eval (eval at <anonymous> (http://example.com/:64:17), <anonymous>:3155:20)

แก้ไข

เห็นได้ชัดเลยตอนนี้ ...

Gruntfile.js

uglify: {
  example: {
    options: {
      beautify: true,
      mangle: true
    },
    [...]
  },
  [...]
}

../node_modules/grunt-contrib-uglify/node_modulesuglify-js/lib/scope.js

var numberOfVariables = 1;
SymbolDef.prototype = {
  unmangleable: [...],
  mangle: function(options) {
    [...]
    this.mangled_name = s.next_mangled(options, this)+"_orig_"+this.orig[0].name+"_"+numberOfVariables++;
    [...]
  }
};

ตอนนี้แต่ละตัวแปรจะกลายเป็นค่าที่ไม่ซ้ำกันซึ่งมีต้นฉบับด้วย ... เพียงแค่เปิดจาวาสคริปต์ที่ย่อขนาดแล้วค้นหา "a_orig_ $ stateProvider_91212" หรืออะไรก็ตาม ... คุณจะเห็นมันในบริบทดั้งเดิม ...

ไม่ง่ายไปกว่านี้แล้ว ...


4

อย่าลืมresolveทรัพย์สินของเส้นทางด้วย นอกจากนี้ยังต้องกำหนดให้เป็นอาร์เรย์:

$routeProvider.when('/foo', {
    resolve: {
        bar: ['myService1', function(myService1) {
            return myService1.getThis();
        }],
        baz: ['myService2', function(myService2) {
            return myService2.getThat();
        }]
    }
});

สิ่งนี้เกิดขึ้นกับฉันเมื่อฉันเพิ่มการแก้ไขจำนวนมากในเส้นทางของฉัน คุณอาจช่วยฉันได้หลายชั่วโมงในการแก้ไขจุดบกพร่องที่เจ็บปวดขอบคุณ
Paul McClean

3

ด้วยเครื่องกำเนิดไฟฟ้าอึก - เชิงมุม:

   /** @ngInject */
    function SomeController($scope, myCoolService) {

}

เขียน/ ** @ngInject * /ก่อนแต่ละตัวควบคุมบริการคำสั่ง


2

การแก้ไขที่รวดเร็วและสกปรกสำหรับสิ่งนี้หากคุณไม่ต้องการให้ Uglify ทำการ mangle / ย่อชื่อตัวแปรของคุณคือการตั้งค่า mangle = false ใน Gruntfile ของคุณ

    uglify: {
        compile: {
            options: {
                mangle   : false,
                ...
            },
        }
    }

วิธีนี้อาจช่วยแก้ปัญหาได้ แต่ขนาดบิลด์ผลลัพธ์จะใหญ่ขึ้นเนื่องจากปิดใช้งาน mangle
NotABot

ยังเล็กกว่าไม่น่าเกลียดเลย
mjwrazor
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.