企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
其实每次看到这些代码的时候,我就迫不及待的想知道:什么时候才能把它们改成动态获取的。 ~~~ this.phones = [{ name: 'Nexus S', snippet: 'Fast just got faster with Nexus S.', age: 1 }, { name: 'Motorola XOOM™ with Wi-Fi', snippet: 'The Next, Next Generation tablet.', age: 2 }, { name: 'MOTOROLA XOOM™', snippet: 'The Next, Next Generation tablet.', age: 3 }]; ~~~ 很简单的道理,我们的应用上线后,总不能靠改源代码来新增或是删除手机信息吧。本节中,我们将使用一个叫做`$http`的服务,来实现数据的动态获取。 #XHR 我们先看一下什么是XHR: XMLHttpRequest是一个浏览器接口,使得Javascript可以进行HTTP(S)通信。 最早,微软在IE5引进了这个接口。 因为它太有用,其他浏览器也模仿部署了,ajax操作因此得以诞生。 如果以前我们接触过jquery,相信你对XHR已经不再陌生。 没有XHR以前,我们的浏览器向服务器发送数据的话,只能重新触发一个新的URL,比如我们在form标签中写入action的地址,然后在点击submit按钮时,就会将form中的数据提交到action规定好的地址上。 有了XHR,我们可以做到在不刷新页面的前提下,使用JS在后面偷偷的向服务器传送数据了。当然了,更重要的,这使我们的浏览器真正的在不刷新页面的前提下动了起来。 html5中,XHR已经发展到XMLHttpRequest Level 2,我们可以阅读这篇文章来对XHR有个更深入的认识: http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html 认识了XHR后,我们共同学习下`angularjs`为我们封装好的XHR ---- `$http`。 # $http 前面,我们说$http可以在后台请求数据。那么这句话应该是基于以下前提的:服务器上需要存在数据。 ## 数据准备 在这,我们已经准备好了本节需要的数据: 你需要打开:http://guide.mengyunzhi.com/angular-phonecat/app/phones/ ,然后找到里面的`phones.json`,将其下载(点击打开,全选,复制,再粘贴到自己的文件中)后,保存至当前程序的根目录下的`phones`文件夹中。 最终,你的目录结构应该是这样的: ~~~ ├── app.moudle.js ├── bower_components │   ├── angular │   ├── bootstrap │   └── jquery ├── index.html ├── phones │   └── phones.json ├── test.html └── yun-zhi ├── hello-yunzhi.component.js ├── hello-yunzhi.template.html ├── phone-list.component.js ├── phone-list.template.html └── yun-zhi.module.js ~~~ TODO:当然了,你还可以使用以下的GIT命令来查看我们为你准备好的代码,并将其直接复制到文件夹中。 ## 请求数据 请求数据以前,我们需要简单的讲一下什么是依赖注入(DI Dependency Injection)。在学习ThinkPHP的时候,我们学过命名空间。 比如以下Hello类 ~~~ <?php namespace app\yunzhi; class Hello { } ~~~ 我们如果在自己的类中去实例化Hello,需要先use,然后才可以直接NEW。 ~~~ <?php .... use app\yunzhi\Hello; .... public function index() { $Hello = new Hello(); } ~~~ 否则。ThinkPHP会告诉我们,找不到这个`Hello`。 这样做的好处是什么呢? 我们虽然规定了很多类,但是ThinkPHP只有在使用到他们的时候,才将它们加载到内存,从而提升了运行的效率。当然了,有了命名空间后,我们再也不用担心类名重复的问题了。 angularjs使用一种叫做依赖注入的方法来解决这个问题,其实我们可以简单的理解为是use在angularjs中的表现形式而已。 ~~~ angular. module('yunZhi'). component('phoneList', { templateUrl: 'yun-zhi/phone-list.template.html', controller: function PhoneListController($http) { console.dir($http); this.orderProp = 'age'; } }); ~~~ 没错,`$http`做为`controller`的参数传入后,我们就可以在方法体中使用这个`$http`了。 我们在控制台中,看看它的庐山真面目。 ![](https://box.kancloud.cn/2016-07-26_5796fd12785bf.png) 没错,正如你看到的,它是一个`function`,有人说老师它怎么是个`function`呢,为什么不是个`object`呢?这是由于在`javascript`中,对象与我们以前学习的有所区别。以前的其它的面向对象的语言中,我们一般先去建立一个`class`,然后实例化这个`class`后,得到一个对象。但javascript没有`class`这个东西。并不是说它不够强大,因为`function`已经完全可以替代`class`。总之,我们需要记住的是。如果我们看到的是一个object,那么这个object,并不是基于哪个类创建的。如果我们看到的返回值是一个function,那么你可以暂时认为它是一个对象。 当然了,你也可以使用下面的代码,再简单的测试一下,它可以有助于你的理解。 ~~~ angular. module('yunZhi'). component('phoneList', { templateUrl: 'yun-zhi/phone-list.template.html', controller: function PhoneListController($http) { // 实例化一个对象。 var a = new Object(); console.log(a); // 实例化一个对象的另一种方法 var b = {}; console.log(b); // 定义一个函数(这个才是以前我们理解的类) var man = function(name){ var self = {}; // 定义一个对象,用来给它添加属性 self.name = name, // 属性1 self.sex = 1, // 属性2 self.sayHello = function(){ // 方法1 alert('hello' + self.name); } self.helloYunzhi = function(){ console.log('hello yunzhi'); } return self; }; // 实例化前面我们定义好的函数man var zhangsan = new man('zhangsan'); console.dir(zhangsan); // 调用方法 zhangsan.sayHello(); zhangsan.helloYunzhi(); console.dir($http); this.orderProp = 'age'; } }); ~~~ 测试: ![](https://box.kancloud.cn/2016-07-26_5796fd129319b.png) 有点晕是吧,没关系。反正知道它是一个function就可以了。这个function中,有属性还有小function。我们可以调用它的属性,也可以调用它的小function。 ## 调用$http 前面,我们发现`$http`中有个属性是`get`,这个属性的类型是`function`,这个`function`接收两个参数,分别为`url`和`config`。 简单猜一下,应该可以知道:用get的方式向url请求数据,config是本次请求数据的配置信息。 > 官方地址:https://docs.angularjs.org/api/ng/service/$http , https://docs.angularjs.org/api/ng/service/$http#get ![](https://box.kancloud.cn/2016-07-26_5796fd12b0f64.png) 原来config是个选填项,那我们暂时不写。 返回值是个HttpPromise的家伙。 1 `$http.get(url);` 2 ~~~ var url = 'phones/phones.json'; var HttpPromise = $http.get(url); console.dir(HttpPromise); ~~~ ![](https://box.kancloud.cn/2016-07-26_5796fd12d1fca.png) 原来返回的类型是Promise,里面有两个方法供我们使用,一个是success,另一个是error。 > 其实Promise是为了解决同步链式操作的一个特定的类型,我们以后会深入的学习它。 下面,我们分别使用这个两个方法,来定义数据正确与错误传输。 ~~~ var HttpPromise = $http.get(url); HttpPromise.success(function(response){}); HttpPromise.error(function(response){}); ~~~ ~~~ var HttpPromise = $http.get(url); HttpPromise.success(function(response){ console.log(response); }); HttpPromise.error(function(response){ // 返回错误码,或是超时触发。 }); ~~~ ![](https://box.kancloud.cn/2016-07-26_5796fd12e9b6b.png) 没错,成功请求后,数据被传入success方法的第一个参数中。 ~~~ angular. module('yunZhi'). component('phoneList', { templateUrl: 'yun-zhi/phone-list.template.html', controller: function PhoneListController($http) { var url = 'phones/phones.json'; var HttpPromise = $http.get(url); HttpPromise.success(function(response){ console.log(response); }); HttpPromise.error(function(response){ // 返回错误码,或是超时时触发。 }); this.orderProp = 'age'; } }); ~~~ ### then() 实际使用中,我们还可以使用Promise的then方法。这个方位位于Promise的父类中,用JS的说法,是位于其原型链上。 ![](https://box.kancloud.cn/2016-07-26_5796fd130bd61.png) 下面,我们使用then方法进行改写: `$http.get(url).then(function(){}, function(){});` 我们给它传两个参数,第一个参数:请求成功后执行的function。第二个参数:请求失败后执行的function。 ~~~ $http.get(url).then( function(response){ }, function(response){ } ); ~~~ 发现了吧,其实上面两种写法只是断行不行,代码没有任何的区别。所以以后当我们再看到这种写法的时候,需要心里很清楚:function(){}作为一个对数传进去了,这个function将会被执行。 ~~~ angular. module('yunZhi'). component('phoneList', { templateUrl: 'yun-zhi/phone-list.template.html', controller: function PhoneListController($http) { var url = 'phones/phones.json'; $http.get(url).then( function(response){ console.log(response); }, function(response){ // 失败的时候被执行,在此省略 } ); this.orderProp = 'age'; } }); ~~~ ![](https://box.kancloud.cn/2016-07-26_5796fd1325bbc.png) 此时,我们发现,和直接使用success方法不同,then方法的response中,不但返回了数据,还返回状态码等信息。当然了,此时如果我们想获取数据,那么需要使用`reponse.data`,以后,我们将更多的使用then方法。 ## 赋值 数据取回来了,下一步,我们将其传给这个controller。 我们把then()中失败执行的代码删除掉后: ~~~ angular. module('yunZhi'). component('phoneList', { templateUrl: 'yun-zhi/phone-list.template.html', controller: function PhoneListController($http) { var url = 'phones/phones.json'; $http.get(url).then( function(response){ this.phones = response.data; } ); this.orderProp = 'age'; } }); ~~~ 测试: ![](https://box.kancloud.cn/2016-07-26_5796fd133ea05.png) 发现竟然没有生成任何数据。 这是由于this的作用域不同导致的。 我们再简单测试一下: ~~~ angular. module('yunZhi'). component('phoneList', { templateUrl: 'yun-zhi/phone-list.template.html', controller: function PhoneListController($http) { this.name = 'father'; var url = 'phones/phones.json'; $http.get(url).then( function(response){ this.phones = response.data; this.name = 'son'; console.log('son:' + this.name); } ); console.log('father: ' + this.name); this.orderProp = 'age'; } }); ~~~ 可能我们期待的结果是: son: son father: son 但实际上却是这样: ![](https://box.kancloud.cn/2016-07-26_5796fd135a8e1.png) 发现我们认为的子函数中,没有将name覆盖为son,而且执行顺序也和我们想的完全不一致。至于执行顺序,这是由于js异步处理的原因。在这里我更想说的是,在$http.get()中的this.name,并不是我们的子函数,也是function参数中的一部分,并不是我们心里想的this。 鉴于此,我们进行如下改写: ~~~ angular. module('yunZhi'). component('phoneList', { templateUrl: 'yun-zhi/phone-list.template.html', controller: function PhoneListController($http) { var self = this; var url = 'phones/phones.json'; $http.get(url).then( function(response){ self.phones = response.data; } ); this.orderProp = 'age'; } }); ~~~ 测试: ![](https://box.kancloud.cn/2016-07-26_5796fd1371913.png) 没错,这才是我们想要的。 ## 为压缩代码做准备 我们在进行开发时,往往会看到两个js版本,一个是.js,另一个是.min.js。前面的看着很方便,但代码体积大。后面的看着不方便,但代码体积小。一个是人能看的懂的,另一个是机器能看的懂的。在正式的生产环境下,项目上线前我们会对js代码进行压缩。 那么都是什么被压缩了呢? 1、删除空格 2、删除注释 3、删除换行 4、将变量名变短 比如: ~~~ var yunzhi = function(){ var self = {}; self.name = 'yunzhidreamer'; return self; }; var zhangsan = new Yunzhi(); name = zhangsan.name; ~~~ 经过四步压缩后,会变成这个样子。 ~~~ var yunzhi=function(){var a={};return a.name="yunzhidreamer",a},zhangsan=new Yunzhi;name=zhangsan.name; //# sourceMappingURL=test.min.js.map ~~~ 那如果参数中包含$http呢? 我们共同来测试一下: ~~~ var yunzhi = function($http){ var self = {}; self.http = $http; self.name = 'yunzhidreamer'; return self; }; var http = new Object(); var zhangsan = new Yunzhi(); name = zhangsan.name; ~~~ 压缩后: ~~~ var yunzhi=function(a){var b={};return b.http=a,b.name="yunzhidreamer",b},http=new Object,zhangsan=new Yunzhi;name=zhangsan.name; //# sourceMappingURL=test.min.js.map ~~~ 没错,`$http`会做为一个普通的参数被压缩。但是angularjs却只认识$http,比如: 我们的`phone-list.component.js`压缩前: ~~~ angular. module('yunZhi'). component('phoneList', { templateUrl: 'yun-zhi/phone-list.template.html', controller: function PhoneListController($http) { var self = this; var url = 'phones/phones.json'; $http.get(url).then( function(response){ self.phones = response.data; } ); this.orderProp = 'age'; } }); ~~~ 压缩后: ~~~ angular.module("yunZhi").component("phoneList",{templateUrl:"yun-zhi/phone-list.template.html",controller:function(a){var b=this,c="phones/phones.json";a.get(c).then(function(a){b.phones=a.data}),this.orderProp="age"}}); //# sourceMappingURL=test.min.js.map ~~~ 此时,我们使用压缩后的代码,就会报一个这样错误的信息: ![](https://box.kancloud.cn/2016-07-26_5796fd138c459.png) 是的,由于$http变成了a,angularjs又不认识这个a,所以报错了。 但我们发现,像angular.module这个字眼,却没有被压缩。这是由于压缩工具很聪明的知道类还类的属性,以及做为字符串传入的值,是不能随便压缩的。基于此,我们对现有的代码进行改写,让压缩对我们不造成影响。 我们使用定义controller的第二种写法 `controller: []` ~~~ angular. module('yunZhi'). component('phoneList', { templateUrl: 'yun-zhi/phone-list.template.html', controller: ['$http', function PhoneListController($http) { var self = this; self.orderProp = 'age'; $http.get('phones/phones.json').then(function(response) { self.phones = response.data; }); } ] }); ~~~ 上面的写法等同于下面这样: ~~~ angular. module('yunZhi'). component('phoneList', { templateUrl: 'yun-zhi/phone-list.template.html', controller: ['$http', function PhoneListController(param1) { var self = this; self.orderProp = 'age'; param1.get('phones/phones.json').then(function(response) { self.phones = response.data; }); } ] }); ~~~ 是的,在实际的执行中,param1会被做为$http来执行。所以即使压缩后的代码是这个样子:`controller:["$http",function(a){`也不会影响到程序的正确执行了。 前台这门学问,真的是越学感觉它越伟大,angularjs当然也是一样。