zoukankan      html  css  js  c++  java
  • AngularJS的思考

    AngularJS实践

    什么是AngularJS

    AngularJS的核心理念是什么? 在我看来,Angualr的核心思想是:Template + Scope => HTML, Template就是各种可以复用的模板,Scope就是数据.

    WEB前端原本是DOM构成一个树形结构,然后数据杂乱分布. 开发者用JQuery之类的工具在DOM上添加各种各样的数据(data, trigger, listener)等.当网页的结构复杂起来之后,各种数据之间的关联错综复杂,到最后几乎没有人可以说清楚这个庞然大物里存在多少关联. 复杂的WEB应用里有大量同类型的节点,也导致JQuery里的$("#id")到处存在,维护性极差.

    AngularJS从另外一个角度来解决,将数据组织成树状结构,每一个数据节点绑定对应的模板之后就渲染出对应的HTML. 所有的数据,包括函数,触发器都绑定在这棵树上, 每一个节点的渲染过程只跟该节点上的数据以及绑定的模板有关,需要协调的关系只能通过这棵树向上追溯. 比如两个模块需要共享一个用户登录状态时,可以将用户状态定义在两个元素上节点的共同祖先节点上,这样生成两个元素节点时都引用祖先的数据,就可以生成统一的HTML了.

    这是从上往下生成HTML的过程,那么如果祖先节点上的状态发生了变化,怎么让子孙节点也同步变化呢. AngularJS提供了一个检查机制, 模板是不会变化的,而HTML的生成只与模板和数据相关. 因此AngularJS沿着数据树根节点往下检查,一旦某个数据节点发生了变化,就立刻重新渲染这个节点.

    为了能及时进行检查, AngularJS绑定了WEB页面上的大部分操作, 比如按键等. 一旦有这些操作发生时, AngularJS都会开始脏检查, 这样可以确保页面的不断更新. 当然这一切是建立在现在浏览器处理能力越来越强的基础上的.

    AngularJS与JQuery

    AngularJS也需要操作DOM,因此AngularJS有一个JQuery的子集JQLite来做DOM操作. 但AngularJS的理念是模块化和封闭化, 每一个节点的渲染封闭在该节点的DATA和Template内部, 只能通过父节点了解外部变化. 这个与JQuery那种完全扁平的结构是冲突的, 使用JQuery,我们可以在随意的位置修改HTML上任意一个节点. 这种方便性就是导致最后页面无法维护的原因.

    正因为这个理念的原因, AngularJS和JQuery其实是最好不要共存的. 在使用AngularJS的很多时候我们都会有忍不住拿出JQuery来的冲动, 明明看起来很容易完成的任务, 被AngularJS封装起来后仿佛变得复杂了很多. 这个时候更建议大家重新看一下自己的设计是否需要调整. AngularJS是一个全局的理念, 不是可以一半JQuery一半AngularJS (当然有些大神能够很好的将项目划分开, 也就尽信书不如无书了, 这个当然没有绝对的方案).

    Angular中controller是用来将view上的操作与实际业务逻辑挂钩,如果需要对dom进行操作时,directive才是合适的选择。directive中有两个非常重要的概念:compile和link

    • compile
      compile是一个预处理过程,directive在compile是与实际状态无关的,compile的目的就是为了得到最终的link函数。
    • link
      link函数负责在得到具体的scope之后渲染出最终的HTML页面。

    常见问题

    一些技巧

    Server端初始化页面参数

    我们经常会遇到的问题是需要从Server得到整个页面的一些初始化条件,然后再用AngularJS在页面端运行整个App。 有一个解决方案是在页面端通过Ajax去Server请求。不过既然整个页面都是从Server请求来的,这个Ajax请求未免有点多余。
    这里提供一个解决方案,利用页面渲染过程在页面上写入启动参数。

    我的实际做法如下:

    在页面模板上写一个angular service:(我这里用的是Jade渲染引擎,读者只关注里面的js code就好)

        script.
            var serverInit = angular.module("serverInit", []);
            serverInit.factory("serverParams", function(){
                    var params = !{JSON.stringify(serverParams)};
                    return params;
                });
    

    然后在server端渲染时给出参数:

    	res.render('page', {serverParams : {
    	        Id : "51955"
    	    }});
    

    这样在页面端我就可以使用这个初始化参数了

        var app = angular.module("testApp", ['serverInit']);
        app.controller("testController", function(serverParams){
                     var id = serverParams.Id;
        }
    

    Angular Mocks测试时不拦截Http请求

    在测试Angular程序时,我们经常会使用到AngularMocks库,这个库会用$httpBackend拦截被测试代码中的$http请求,这样可以测试代码是否如预期发送出了http请求,并且返回伪造的结果。

    但如果我们不需要请求被拦截,而是需要请求发送到真实Server怎么办?StackOverflow上有一个网友给出了一个精彩的方案:
    首先在测试代码文件中写一个新的angular module。

    	angular.module('httpReal', ['ng'])
    	    .config(['$provide', function($provide) {
    	        $provide.decorator('$httpBackend', function() {
    	            return angular.injector(['ng']).get('$httpBackend');
    	        });
    	    }])
    	    .service('httpReal', ['$rootScope', function($rootScope) {
    	        this.submit = function() {
    	            $rootScope.$digest();
    	        };
    	    }]);
    

    然后在需要测试代码中注入httpReal Service, 这个Service会将真正的$httpBackend替换回来!这样angularMocks的拦截功能就无效了。

    	describe('my service', function() {
    	    var myService, httpReal;
    	
    	    beforeEach(module('myModule', 'httpReal'));
    	
    	    beforeEach(inject(function( _myService_, _httpReal_ ) {
    	        myService = _myService_;
    	        httpReal = _httpReal_;
    	    }));
    	
    	    it('should return valid data', function(done) {
    	        myService.remoteCall().then(
    	            function(data) {
    	                expect(data).toBeDefined();
    	                done();
    	            }, function(error) {
    	                expect(false).toBeTruthy();
    	                done();
    	            });
    	
    	        httpReal.submit();
    	    });
    	});
    

    注意这个submit(),因为在AngularJS中,使用了$q来返回promise,而promise的触发是需要在$scope做脏检查的时候做的,而在UnitTest需要手动调用一次$digest去触发。

  • 相关阅读:
    微服务实战——微服务架构选型SpringCloud / Dubbo / K8S比较(一)
    微服务实战——Spring Cloud + Zuul Gateway + Eureka集成
    微服务实战——SpringCloud与Feign集成
    微服务实战——高可用的SpringCloudConfig
    制作自己的网站第二步***在Linux上装上需要的软件以及部署项目配置**
    Eclipse打war包方法以及Eclipse移植项目时JDK版本不匹配Project facet Java version 1.7 is not supported
    Eclipse移植项目时JDK版本不匹配Project facet Java version 1.7 is not supported
    个人网站开发***云服务器+Linux+域名***
    SaaS 系统架构,Spring Boot 动态数据源实现!
    Spring Security 是如何在 Servlet 应用中执行的?
  • 原文地址:https://www.cnblogs.com/lkiversonlk/p/4934295.html
Copyright © 2011-2022 走看看