Prefacio
AngularJS es fácil de desarrollar, tiene muchas características y buenos efectos, lo que resulta en más aplicaciones, y algunas trampas van acompañadas de. Este artículo enumera algunos problemas comunes que AngularJS es propenso a los problemas. Echemos un vistazo juntos.
1. Estructura del directorio MVC
Angularjs, para ser franco, es un marco MVC. Su modelo no está tan claramente definido como el marco Backbone.js, pero su arquitectura es acertadamente. Cuando trabaja en un marco MVC, es común clasificarlo por tipo de archivo:
plantillas/ _login.html _feed.htmlapp/ app.js controladores/ logincontroller.js freedcontroller.js directivas/ freedEnentrydirective.js Servicios/ loginservice.js FeedService.js Filtros/ CapatalyFilter.js.JS
Parece que esta parece ser una estructura obvia, sin mencionar que Rails hace lo mismo. Sin embargo, una vez que la aplicación comienza a expandirse, esta estructura le hará abrir muchos directorios a la vez. Ya sea que esté utilizando sublime, Visual Studio o VIM combinado con Nerd Tree, invertirá mucho tiempo constantemente deslizándose hacia arriba y hacia abajo en el árbol de directorio.
A diferencia de dividir archivos por tipo, en cambio, podemos dividir archivos por características:
App/ App.js Feed/ _Feed.html FeedController.js FeedEntryDirective.js FeedService.js Login/ _login.html logincontroller.js loginservice.js compartido/ capacateFilter.js
Esta estructura de directorio nos hace más fácil encontrar todos los archivos relacionados con una característica y, por lo tanto, acelerar nuestro progreso de desarrollo. Si bien puede ser controvertido poner archivos .html y .js en un solo lugar, el tiempo ahorrado es más valioso.
2. Módulo
Es muy común poner todo bajo el módulo principal. Para aplicaciones pequeñas, no hay problema al principio, pero pronto encontrará que algo está haciendo trampa.
var app = angular.module ('app', []); app.service ('myservice', function () {// código de servicio}); app.controller ('myctrl', function ($ scope, myservice) {// código de controlador});Después de esto, una estrategia común es clasificar objetos del mismo tipo.
var Services = Angular.module ('Services', []); Services.Service ('myService', function () {// código de servicio}); controladores var = angular.module ('controladores', ['servicios']); controladores.controller ('myctrl', function ($ scope, myservice) {// código de controlador}); var app = angular.module ('app', ['controladores', 'servicios']);Este método es similar a la estructura del directorio mencionada en la primera parte anterior: no es lo suficientemente bueno. Según la misma filosofía, se puede clasificar por características, lo que conduce a la escalabilidad.
var SharedServicesModule = Angular.module ('SharedServices', []); SharedServices.Service ('NetworkService', function ($ http) {}); var loginmodule = angular.module ('Login', ['SharedServices']); LoginModule.Service ('LoginService', function (NetworkService) {}); LoginModule.Controller ('loginctrl', function ($ scope, loginservice) {}); var app = angular.module ('app', ['SharedServices', 'Login']);Cuando desarrollamos una aplicación grande, es posible que no sea todo contenido en una sola página. Poner el mismo tipo de características en un módulo hace que sea más fácil reutilizar módulos en las aplicaciones.
3. Inyección de dependencia
La inyección de dependencia es uno de los mejores patrones en AngularJS, lo que hace que las pruebas sean más simples y es clara para confiar en cualquier objeto especificado. El método de inyección de AngularJS es muy flexible. La forma más fácil es simplemente pasar el nombre de dependencia a la function del módulo:
var app = angular.module ('app', []); app.controller ('mainctrl', function ($ scope, $ timeOut) {$ timeOut (function () {console.log ($ scope);}, 1000);}); Aquí, es obvio que MainCTRL depende de $scope y $timeout .
Todo es hermoso hasta que esté listo para implementarlo en producción y desea optimizar su código. Si usa uglifyjs, el ejemplo anterior se convertirá en este:
var app = angular.module ("app", []); app.controller ("mainctrl", function (e, t) {t (function () {console.log (e)}, 1e3)})¿Cómo saben AngularJS de quién depende MainCtrl? AngularJS proporciona una solución muy simple, a saber, las dependencias que pasan en una matriz, el último elemento de la matriz es una función, y todas las dependencias se utilizan como parámetros.
app.controller ('mainctrl', ['$ scope', '$ timeOut', function ($ scope, $ timeOut) {$ timeOut (function () {console.log ($ scope);}, 1000);}]);Hacerlo hará que el código se simplifique, y AngularJS sabe cómo interpretar estas dependencias explícitas:
app.controller ("mainctrl", ["$ scope", "$ timeOut", function (e, t) {t (function () {console.log (e)}, 1e3)}]))3.1 Dependencia global
Esto a menudo sucede al escribir programas AngularJS: un objeto tiene una dependencia, y este objeto se une al alcance global, lo que significa que esta dependencia está disponible en cualquier código AngularJS, pero esto destruye el modelo de inyección de dependencia y causa algunos problemas, especialmente durante el proceso de prueba.
El uso de AngularJS facilita la encapsulación de estas dependencias globales en los módulos, por lo que se pueden inyectar como módulos estándar AngularJS.
Underscore.js es una gran biblioteca que simplifica el código JavaScript en un estilo funcional, y puede convertirlo en un módulo de las siguientes maneras:
var subscore = angular.module ('underscore', []); subscore.factory ('_', function () {return window._; // Underscore ya debe cargarse en la página}); var app = angular.module ('app', ['subderscore']); app.controller ('mainctrl', ['$ scope', '_', function ($ scope, _) {init = function () {_.Keys ($ scope);} init ();}]);Este enfoque permite que la aplicación continúe desarrollándose en el estilo de inyección de dependencia de AngularJS, y también puede cambiar el subrayado durante la fase de prueba.
Esto puede parecer trivial e innecesario, pero si su código está usando Stricts (y debe usarse), entonces esto es necesario.
IV. Expansión del controlador
El controlador es carne y papas angularjs, y si no tiene cuidado, agregará demasiada lógica, especialmente al principio. El controlador nunca debe operar el DOM o mantener el selector DOM, ahí es donde necesitamos usar instrucciones y modelo NG. Del mismo modo, la lógica comercial debería existir en el servicio, no en el controlador.
Los datos también deben almacenarse en el servicio a menos que ya estén vinculados a $ alcance. El servicio en sí es singleton y existe durante toda la vida de la aplicación, pero el controlador es transitorio entre los estados de la aplicación. Si los datos se guarda en el controlador, cuando se instancia nuevamente, necesita volver a realizar los datos de algún lugar. Incluso si los datos se almacenan en LocalStorage, la velocidad de búsqueda es un orden de magnitud más lenta que las variables de JavaScript.
AngularJS funciona bien cuando sigue el principio de responsabilidad única (SRP). Si el controlador es el coordinador entre la vista y el modelo, debe contener la menor lógica posible, lo que también facilitará las pruebas.
5. Servicio vs Factory
Casi todos los desarrolladores de Angularjs están preocupados por estos sustantivos cuando es principiante, lo que realmente no lo merece, ¡porque son solo azúcar sintáctica para casi lo mismo!
Aquí están sus definiciones en el código fuente de AngularJS:
function factory (name, factoryfn) {return proveedor (name, {$ get: factoryfn}); } servicio de función (nombre, constructor) {return fábrica (nombre, ['$ inyector', function ($ injector) {return $ injector.instantiate (constructor);}]);} Desde el código fuente, puede ver que el servicio simplemente llama a la función factory , y este último llama a la función provider . De hecho, AngularJS también proporciona encapsulación adicional provider para algunos valores, constantes y decoración, lo que no conduce a una confusión similar, y su documentación es muy clara.
Dado que el servicio solo llama a la función factory , ¿cuál es la diferencia? La pista está en $injector.instantiate : en esta función, $injector crea una nueva instancia en el constructor service .
Aquí hay un ejemplo que muestra cómo un service y una factory hacen lo mismo:
var app = angular.module ('app', []); app.service ('HelloWorldService', function () {this.hello = function () {return "Hello World";};}); app.factory ('helloworldFactory', function () {return {hello: function () {return "hello world";}}}); Cuando se inyecta helloWorldService o helloWorldFactory en el controlador, todos tienen un método de saludo que devuelve "Hello World". El constructor del service se instancia una vez cuando se declara, y factory se pasa cada vez que se inyecta, pero todavía solo hay una instancia factory . Todos providers son singletons.
Como puedes hacer lo mismo, ¿por qué necesitas dos estilos diferentes? En comparación con service , factory proporciona más flexibilidad porque puede devolver las funciones, que se pueden crear después. Esto atiende al concepto de patrones de fábrica en la programación orientada a objetos, donde una fábrica puede ser un objeto que puede crear otros objetos.
app.factory ('helloFactory', function () {return function (name) {this.name = name; this.hello = function () {return "Hello" + this.name;};};};}); Aquí hay un ejemplo de un controlador, que usa service y dos factory , helloFactory devuelve una función que establece el valor de name cuando se crea un nuevo objeto.
app.controller ('helloctrl', function ($ scope, helloworldservice, helloworldFactory, helloFactory) {init = functer () {helloWorldService.hello (); // 'hola world' helloworldfactory.hello (); // 'hola world' new helloFactory ('lectores'). init ();});Cuando son principiantes, es mejor usar el servicio.
Factory también es útil al diseñar una clase con muchos métodos privados:
app.factory ('privateFactory', function () {var privatefunc = function (name) {return name.split (""). inverso (). Join (""); // invierte el nombre}; return {hello: function (name) {return "hello" + privateFunc (name);}};}); Con este ejemplo, podemos hacer que el método privateFunc sea inaccesible para la API pública de privateFactory . Este patrón se puede hacer en service , pero es más fácil en factory .
6. Batarang no se usa
Batarang es un excelente complemento Chrome para desarrollar y probar aplicaciones AngularJS.
Batarang proporciona la capacidad de explorar modelos, lo que nos da la capacidad de observar cómo AngularJS está unido al alcance, lo cual es muy útil al manejar instrucciones y aislar un rango de valores unidos.
Batarang también proporciona un gráfico de dependencia, que es útil si estamos expuestos a una base de código no probada, que puede determinar en qué servicios deben centrarse.
Finalmente, Batarang proporciona análisis de rendimiento. Angular se puede usar como un paquete y tiene un buen rendimiento, pero a veces no es tan suave para una aplicación llena de instrucciones personalizadas y lógica compleja. Usando la herramienta de rendimiento de Batarang, puede observar directamente qué función ejecuta el tiempo más largo en un ciclo de resumen. Las herramientas de rendimiento también pueden mostrar un árbol de reloj completo, que es útil cuando tenemos muchos observadores.
7. Demasiados observadores
En el punto anterior, mencionamos que AngularJS se puede usar como un paquete y tiene un buen rendimiento. Dado que las verificaciones de datos sucios deben completarse en un ciclo de resumen, una vez que el número de observadores crece a aproximadamente 2000, este ciclo causará problemas de rendimiento significativos. (No se puede decir que el número 2000 cause una caída de rendimiento significativa, pero este es un buen valor empírico. En la versión de lanzamiento AngularJS 1.3, ya hay algunos cambios que permiten un control estricto de los ciclos de resumen).
La siguiente "Expresión de función de ejecución inmediata (IIFE)" imprimirá el número de todos los observadores en la página actual. Simplemente puede pegarlo en la consola y observar los resultados. Esta IVED se deriva de la respuesta de Jared en StackOverflow:
(function () {var root = $ (document.getElementsByTagName ('Body')); var Watchers = []; var f = function (element) {if (element.data (). Hasnownproperty ('$ scope')) {angular.foreach (element.data (). $ Scope. $$ Watchers, function (Watcher) {Watcher.push (Push (push (); Angular.ForEach (elementoDe esta manera, se obtiene el número de observadores, combinado con el árbol de relojes en la sección de rendimiento de Batarang, debe ver dónde existe el código duplicado o dónde existe datos constantes y también los relojes propios.
Cuando hay datos sin cambios y desea usar AngularJS para plantparlo, puede considerar usar BindOnce. BindOnce es una directiva simple que le permite usar plantillas en AngularJS, pero no se suma al reloj, lo que asegura que el número de relojes no crecerá.
8. Rango limitado de $ alcance
La herencia basada en prototipos JavaScript tiene una diferencia sutil con la herencia basada en la clase en objetos orientados a objetos, lo que generalmente no es un problema, pero esta sutileza se manifiesta cuando se usa $scope . En AngularJS, cada $scope hereda el $scope , que se llama $rootScope al más alto nivel. ( $scope es algo diferente de las directivas tradicionales. Tienen un cierto alcance de acción y solo heredan propiedades explícitamente declaradas).
Debido a las características de la herencia prototipo, no es importante compartir datos entre las clases de padres e hijos, pero si no tiene cuidado, es fácil utilizar mal la propiedad de un $scope principal.
Por ejemplo, necesitamos mostrar un nombre de usuario en una barra de navegación, que se ingresa en el formulario de inicio de sesión. El siguiente intento debería funcionar:
<div ng-concontroller = "navctrl"> <span> {{user}} </span> <div ng ng-controller = "loginctrl"> <span> {{user}} </span> <input ng-model = "user"> </put> </div> </div>Entonces la pregunta es ...: el modelo NG del usuario se establece en la entrada de texto. Cuando el usuario ingresa al contenido, ¿qué plantilla se actualizará? navctrl o loginctrl, o todo?
Si elige Loginctrl, entonces puede haber entendido cómo funciona la herencia prototipo.
La cadena prototipo no funciona cuando recuperas el literal. Si NAVCTRL también se actualiza al mismo tiempo, es necesario recuperar la cadena prototipo; Pero si el valor es un objeto, esto sucederá. (Recuerde, en JavaScript, las funciones, las matrices y los objetos son todos objetos)
Entonces, para obtener el comportamiento esperado, debe crear un objeto en NavcTrl, al que Loginctrl puede hacer referencia.
<Div ng-Controller = "Navctrl"> <span> {{user.name}} </span> <div ng-concontroller = "loginctrl"> <span> {{user.name}} </span> <input ng-model = "user.name"> </put> </iv> </iv>> Ahora, dado que el usuario es un objeto, la cadena prototipo funcionará, y la plantilla NavcTrl y $scope y loginCtrl se actualizarán.
Esto parece un ejemplo bastante artificial, pero este problema puede surgir fácilmente cuando usa ciertas instrucciones para crear un $scope para niños, como ngRepeat .
9. Pruebas manuales
Dado que TDD puede no ser la forma en que todos los desarrolladores prefieren, realizan pruebas manuales cuando verifican si el código funciona o afecta algo más.
No tiene sentido no probar la aplicación AngularJS. AngularJS está diseñado para hacerlo comprobable desde cero, y la inyección de dependencia y los módulos ngmock son pruebas de esto. El equipo central de AngularJS ha desarrollado numerosas herramientas que pueden llevar las pruebas al siguiente nivel.
9.1 Protractor
Las pruebas unitarias son la base del trabajo de prueba, pero teniendo en cuenta la complejidad cada vez más de la aplicación, las pruebas de integración están más cerca de la situación real. Afortunadamente, el equipo central de AngularJS ha proporcionado las herramientas necesarias.
Hemos establecido Protractor, un probador de extremo a extremo para simular las interacciones del usuario, lo que puede ayudarlo a verificar la salud de su programa AngularJS.
El protractor utiliza el marco de prueba de Jasmine para definir las pruebas. El protractor tiene una API muy robusta para diferentes comportamientos de interacción de páginas.
Tenemos algunas otras herramientas de prueba de extremo a extremo, pero la ventaja de Protractor es que comprende cómo trabajar con el código AngularJS, especialmente en el ciclo $ Digest.
9.2 Karma
Una vez que hayamos completado la redacción de pruebas de integración con Protractor, el siguiente paso es ejecutar las pruebas. Esperar que se ejecuten las pruebas, especialmente las pruebas de integración, es una leve tristeza para cada desarrollador. El equipo central de Angularjs también se sintió extremadamente angustiado, por lo que desarrollaron el karma.
Karma es un probador que ayuda a desactivar los bucles de retroalimentación. Karma puede hacer esto porque ejecuta pruebas cuando se cambia el archivo especificado. Karma también ejecutará pruebas en múltiples navegadores, y diferentes dispositivos también pueden apuntar al servidor Karma, lo que puede cubrir mejor los escenarios de aplicaciones del mundo real.
10. Use jQuery
JQuery es una biblioteca genial, con un desarrollo multiplataforma estandarizado, y casi se ha convertido en una necesidad para el desarrollo web moderno. Sin embargo, a pesar de tantas características excelentes de jQuery, su filosofía no es consistente con los angulares.
AngularJS es un marco para construir aplicaciones, mientras que JQuery es una biblioteca que simplifica "Operaciones de documentos HTML, procesamiento de eventos, animaciones y AJAX". Esta es la diferencia más básica entre los dos. AngularJS está comprometido con la arquitectura del programa y no tiene nada que ver con las páginas HTML.
Para comprender mejor cómo construir un programa AngularJS, deje de usar jQuery. JQuery permite a los desarrolladores pensar en los problemas con los estándares HTML existentes, pero como dice la documentación, "AngularJS le permite expandir el término HTML en su aplicación".
Las operaciones DOM solo deberían hacerse en instrucciones, pero eso no significa que solo puedan encapsularse con jQuery. Antes de usar jQuery, siempre debe pensar si esta función ya está proporcionada por AngularJS. Es realmente poderoso crear herramientas poderosas cuando las instrucciones dependen entre sí.
Pero una gran jQuery es una necesidad cuando puede llegar el día, pero presentarlo en primer lugar es un error común.
Resumir
AngularJS es un excelente marco que siempre está mejorando con la ayuda de la comunidad. Aunque AngularJS sigue siendo un concepto en evolución, espero que las personas puedan seguir las convenciones mencionadas anteriormente para evitar los problemas encontrados en el desarrollo de aplicaciones AngularJS. Espero que el contenido de este artículo sea útil para todos. Si tiene alguna pregunta, puede dejar un mensaje para comunicarse. Gracias por su apoyo a Wulin.com.