AngularJS Phonecat(步驟0-步驟5)

導(dǎo)言


最近在學(xué)AngularJS的實(shí)例教程PhoneCat Tutorial App,發(fā)現(xiàn)網(wǎng)上的中文教程都比較久遠(yuǎn),與英文版對(duì)應(yīng)不上,而且缺少組件和文件重構(gòu)兩節(jié)。所以決定自己整理一個(gè)中文簡(jiǎn)明教程,內(nèi)容較多,先整理0-5小節(jié)。

教程展示一個(gè)Angular應(yīng)用程序:

catalog_screen.png

涉及如下技術(shù):

視圖和模型的雙向數(shù)據(jù)綁定;

Karma和Protractor測(cè)試;

組件化和模塊化編程。

英文教程

配置


安裝Git

下載Git,并安裝。安裝后可以使用git命令行工具git bash,在教程中只用到兩個(gè)git命令:

git clone ... 從遠(yuǎn)處版本倉(cāng)庫(kù)克隆代碼到本地計(jì)算機(jī)

git checkout ...在本地計(jì)算機(jī)上查看(取出)特定版本(標(biāo)簽)的代碼

拷貝源碼

命令行中輸入:

git clone --depth=16 https://github.com/angular/angular-phonecat.git

然后打開項(xiàng)目文件:

cd angular-phonecat

注意:從現(xiàn)在開始,所有命令都是在angular-phonecat目錄下執(zhí)行。

安裝Node

下載Node.js,并安裝。所需Node的最低版本是 Node.js v4+,可以通過(guò)下面命令行確認(rèn)node版本:

node --version

安裝工具

npm install

該命令行會(huì)安裝package.jason規(guī)定的工具到node_modules文件夾,并下載AngularJS框架到app/bower_components。

下載的工具有:

Bower - 客戶端代碼包管理工具

Http-Server - 簡(jiǎn)單的本地靜態(tài)web服務(wù)器

Karma - 單元測(cè)試工具

Protractor - 端到端 (E2E) 測(cè)試工具

初步接觸項(xiàng)目

npm start: 開啟本地服務(wù)器

npm test: 運(yùn)行Karma單元測(cè)試工具

單元測(cè)試:npm test會(huì)自動(dòng)打開谷歌瀏覽器和火狐,點(diǎn)擊debug、再打開控制臺(tái)可以查看報(bào)錯(cuò)信息。測(cè)試成功時(shí),命令行窗口會(huì)返回success信息。

npm run update-webdriver: 安裝Protractor所需驅(qū)動(dòng)

npm run protractor: 運(yùn)行Protractor端到端測(cè)試

端到端測(cè)試:npm run update-webdriver、npm start、npm run protractor。測(cè)試成功時(shí),命令行窗口會(huì)返回success信息。

注意:輸入 npm start 后,應(yīng)該另外開一個(gè)命令行窗口(不能將服務(wù)器關(guān)閉,否則無(wú)法測(cè)試),再輸入 npm run protractor命令。

0 準(zhǔn)備


重置項(xiàng)目

git checkout -f step-0

該命令將重置phonecat項(xiàng)目的工作目錄,需要在每一學(xué)習(xí)步驟運(yùn)行此命令,將step-0的0改成相應(yīng)步驟的數(shù)字(如:2 AngularJS模板,則數(shù)字為2)。

啟動(dòng)服務(wù)器

npm start

在瀏覽器中輸入: http://localhost:8000/index.html,查看頁(yè)面內(nèi)容。

index.html

app/index.html:

<!doctype html>
<html lang="en" ng-app>
  <head>
    <meta charset="utf-8">
    <title>My HTML File</title>
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" />
    <script src="bower_components/angular/angular.js"></script>
  </head>
  <body>

    <p>Nothing here {{'yet' + '!'}}</p>

  </body>
</html>

代碼中,ng-app表示html元素會(huì)被Angular用作應(yīng)用程序的根(root)元素。這就是說(shuō),ng-app規(guī)定以整個(gè)html頁(yè)面還是部分元素作為Angular程序。

雙大括號(hào)(Double-curly)綁定表達(dá)式:

Nothing here {{'yet'+'!'}}

這一行展示了Angular模板應(yīng)用的兩個(gè)核心功能:{{ }}進(jìn)行綁定,簡(jiǎn)單表達(dá)式'yet'+'!'可以用于綁定。

程序結(jié)構(gòu)如下:

tutorial_00.png

1 靜態(tài)模板


重置項(xiàng)目

git checkout -f step-1

跳到步驟1,后面不再講這一步,每次都要重置,只需要改變數(shù)字。

index.html

app/index.html:

<ul>
    <li>
        <span>Nexus S</span>
        <p>
        Fast just got faster with Nexus S.
        </p>
    </li>
    <li>
        <span>Motorola XOOM? with Wi-Fi</span>
        <p>
        The Next, Next Generation tablet.
        </p>
    </li>
</ul>
<p>Total number of phones: 2</p>

靜態(tài)的HTML,這節(jié)沒(méi)什么內(nèi)容,直接進(jìn)入下一部分。

2 AngularJS模板


視圖和模板

視圖是模型通過(guò)HTML模板渲染之后的映射。這意味著,不論模型什么時(shí)候發(fā)生變化,AngularJS會(huì)實(shí)時(shí)更新結(jié)合點(diǎn),隨之更新視圖。

app/index.html:

<html ng-app>
<head>
  ...
  <script src="lib/angular/angular.js"></script>
  <script src="js/controllers.js"></script>
</head>
<body ng-controller="PhoneListCtrl">
  <ul>
    <li ng-repeat="phone in phones">
      {{phone.name}}
    <p>{{phone.snippet}}</p>
    </li>
  </ul>
</body>
</html>
  • ng-repeat="phone in phones"是一個(gè)AngularJS迭代器。這個(gè)迭代器告訴AngularJS用第一個(gè)<code>li</code>標(biāo)簽作為模板為列表中的每一部手機(jī)創(chuàng)建一個(gè)<code>li</code>元素。

  • {{phone.name}}和{{phone.snippet}}是我們應(yīng)用的一個(gè)數(shù)據(jù)模型引用,這些我們?cè)赑honeListCtrl控制器里面都設(shè)置好了。

tutorial_02.png

模型和控制器

在PhoneListCtrl控制器里面初始化了數(shù)據(jù)模型(這里只是一個(gè)包含了數(shù)組的函數(shù),數(shù)組中存儲(chǔ)的對(duì)象是手機(jī)數(shù)據(jù)列表)。

app/js/controller.js:

function PhoneListCtrl($scope) {
  $scope.phones = [
    {"name": "Nexus S",
     "snippet": "Fast just got faster with Nexus S."},
    {"name": "Motorola XOOM? with Wi-Fi",
     "snippet": "The Next, Next Generation tablet."},
    {"name": "MOTOROLA XOOM?",
     "snippet": "The Next, Next Generation tablet."}
  ];
}

單元測(cè)試

describe('PhoneListController', function() {

  beforeEach(module('phonecatApp'));

  it('should create a `phones` model with 3 phones', inject(function($controller) {
    var scope = {};
    var ctrl = $controller('PhoneListController', {$scope: scope});

    expect(scope.phones.length).toBe(3);
  }));

});

向命令行輸入

npm test

如果未裝谷歌或火狐,要修改karma.conf.js文件,否則無(wú)法正常測(cè)試。

3 組件


什么是組件

控制器+模板-->組件

一個(gè)簡(jiǎn)單的例子:

angular.
  module('myApp').
  component('greetUser', {
    template: 'Hello, {{$ctrl.user}}!',
    controller: function GreetUserController() {
      this.user = 'world';
    }
  });

可以在視圖中引入<code><<greet-user></greet-user></code>,Angular將它擴(kuò)展為DOM子樹,由模板生成結(jié)構(gòu),控制器進(jìn)行管理。
默認(rèn)情況下,組件使用$ CTRL作為控制器的別名。

在代碼中使用組件

app/index.html:

<html ng-app="phonecatApp">
<head>
  ...
  <script src="bower_components/angular/angular.js"></script>
  <script src="app.js"></script>
  <script src="phone-list.component.js"></script>
</head>
<body>

  <!-- 使用自定義組件渲染手機(jī)列表 -->
  <phone-list></phone-list>

</body>
</html>

app/app.js:

// 定義主模塊 `phonecatApp`
angular.module('phonecatApp', []);

app/phone-list.component.js:

// 注冊(cè)組件 `phoneList`(模板+控制器)
angular.
  module('phonecatApp').
  component('phoneList', {
    template:
        '<ul>' +
          '<li ng-repeat="phone in $ctrl.phones">' +
            '<span>{{phone.name}}</span>' +
            '<p>{{phone.snippet}}</p>' +
          '</li>' +
        '</ul>',
    controller: function PhoneListController() {
      this.phones = [
        {
          name: 'Nexus S',
          snippet: 'Fast just got faster with Nexus S.'
        }, {
          name: 'Motorola XOOM? with Wi-Fi',
          snippet: 'The Next, Next Generation tablet.'
        }, {
          name: 'MOTOROLA XOOM?',
          snippet: 'The Next, Next Generation tablet.'
        }
      ];
    }
  });

使用組件的好處:

  • 讓index.html更簡(jiǎn)潔;

  • 更好地分離視圖和模型,修改index.html時(shí)不會(huì)不小心破壞組件;

  • 組件可以單獨(dú)測(cè)試;

  • 組件可以復(fù)用

tutorial_03.png

組件測(cè)試

app/phone-list.component.spec.js:

describe('phoneList', function() {

  // 加載主模板
  beforeEach(module('phonecatApp'));

  // 測(cè)試控制器
  describe('PhoneListController', function() {

    it('should create a `phones` model with 3 phones', inject(function($componentController) {
      var ctrl = $componentController('phoneList');

      expect(ctrl.phones.length).toBe(3);
    }));

  });

});

4 文件夾和文件管理


我們?cè)谶@一節(jié)重構(gòu)文件,讓代碼結(jié)構(gòu)更清晰,方便開發(fā)者快速查找到所需功能或片段。

讓每個(gè)功能/實(shí)體擁有自己的文件。

1)為什么?

為了簡(jiǎn)單起見(jiàn),開發(fā)者可能把所有代碼都在一個(gè)文件中,或者將同一類型的代碼放入同一個(gè)文件(例如在一個(gè)文件中放所有控制器,在另一文件中放所有部件,在第三個(gè)文件中放所有服務(wù))。

這似乎在一開始很好地工作,但隨著應(yīng)用程序代碼的增長(zhǎng),這種結(jié)構(gòu)會(huì)成為一種負(fù)擔(dān)維護(hù)。隨著添加越來(lái)越多的功能,文件將變得越來(lái)越大,我們將難以找到自己所需代碼。

2)怎么做?

將每個(gè)功能/實(shí)體(比如一個(gè)獨(dú)立的控制器、一個(gè)獨(dú)立的組件)放到單獨(dú)的文件中。

比如,phone-list功能,文件結(jié)構(gòu)如下:

app/
  phone-list/
    phone-list.component.js
    phone-list.component.spec.js
  app.js

按功能模塊組織代碼,而不是按功能組織代碼。

1)為什么?

模塊化結(jié)構(gòu)的好處之一是代碼重用 - 不僅在同一應(yīng)用程序內(nèi),但在其他應(yīng)用程序也可以重用。

代碼重用的最后一個(gè)阻礙是:每個(gè)功能/部分需要聲明自己、將自己注冊(cè)到所有相關(guān)的模塊。比如將組件注冊(cè)到主模塊,我們?cè)谛马?xiàng)目中復(fù)用該組件,就需要修改組件代碼中的主模塊名字。這樣影響了功能的封裝,復(fù)用需要修改組件內(nèi)部代碼。

以phoneList組件為例:

angular.
  module('phonecatApp').     //phoneList組件將自己注冊(cè)到主模塊phonecatApp(這樣子,每次測(cè)試phonelist,spec文件會(huì)先加載phonecatApp模塊。)
  component('phoneList', ...);//phoneList組件聲明自己

假設(shè)我們需要開發(fā)另一個(gè)項(xiàng)目的手機(jī)列表。簡(jiǎn)單復(fù)制phoneList/目錄到新項(xiàng)目,并在新項(xiàng)目index.html文件引入該腳本,就搞定了?
好吧,沒(méi)那么簡(jiǎn)單。新項(xiàng)目中沒(méi)有phonecatApp模塊,我們需要把代碼中所有的“phonecatApp”改為新項(xiàng)目主模塊的名稱。這樣子既費(fèi)力,而且容易出錯(cuò)。

2)怎么做?

更好的辦法是新增一個(gè)phonelist功能模塊,將phonelist組件注冊(cè)到這個(gè)模塊上,(英語(yǔ)原文:在每個(gè)功能/部分中聲明自己和需要所有相關(guān)模塊),在主模塊(phonecatApp)中聲明各功能模塊的依賴關(guān)系。

改變后的phonelist目錄:

app/
  phone-list/
    phone-list.module.js  //增加phonelist模塊
    phone-list.component.js
    phone-list.component.spec.js
  app.module.js

app/phone-list/phone-list.module.js 模塊文件:

angular.module('phoneList', []);// 定義 `phoneList` 模塊

app/phone-list/phone-list.component.js 組件文件:

angular.
  module('phoneList').// 將 `phoneList`組件注冊(cè)到 `phoneList` 模塊上
  component('phoneList', {...});

app/app.module.js 主模塊文件(由于app/app.js 現(xiàn)在只包含主模塊,我們給它一個(gè) .module后綴):

// 定義主模塊 `phonecatApp`
angular.module('phonecatApp', [  
  'phoneList' // 將`phoneList` 模塊加入依賴關(guān)系數(shù)組,這樣主模塊就可以訪問(wèn)注冊(cè)到`phoneList`模塊上的組件
]);

這樣,在新項(xiàng)目中復(fù)用代碼,只需要直接復(fù)制phonelist目錄、在新項(xiàng)目主模塊中添加phonelist模塊的依賴關(guān)系。

外部HTML模板

1)為什么?

組件的模板讓我們了解數(shù)據(jù)布局并將HTML代碼片段展示給用戶。在步驟3中,我們使用字符串的來(lái)編寫內(nèi)聯(lián)模板,但這種方式并不理想的,尤其是對(duì)于較大的模板。更好的方式是使用.html文件編寫HTML代碼,這樣在編輯器寫代碼更順暢(例如特定的HTML顏色突出顯示和自動(dòng)完成),也能讓組件更簡(jiǎn)潔易讀。

2)怎么做?

使用外部模板重構(gòu)phoneList組件,在組件中用模板url屬性指定需要加載的模板,并將模板放在phone-list/ 目錄下。

增加外部模板:

將HTML代碼復(fù)制到app/phone-list/phone-list.template.html中。

修改組件代碼:

app/phone-list/phone-list.component.js:
angular.
module('phoneList').
component('phoneList', {
  // 注意:url關(guān)聯(lián)到 `index.html`
  templateUrl: 'phone-list/phone-list.template.html',
  controller: ...
});

當(dāng)創(chuàng)建phoneList組件的一個(gè)實(shí)例時(shí),phone-list.component.js會(huì)通過(guò)http請(qǐng)求得到app/phone-list/phone-list.template.html模板。

使用外部模板雖然好,但會(huì)導(dǎo)致http請(qǐng)求增加。所以,Angular還通過(guò)$templateRequest$templateCache來(lái)管理外部模板。

文件目錄最終布局

app/
  phone-list/
    phone-list.component.js
    phone-list.component.spec.js
    phone-list.module.js
    phone-list.template.html
  app.css
  app.module.js
  index.html

測(cè)試

之前phonelist組件進(jìn)行單元測(cè)試時(shí),需要加載主模塊,主模塊代碼增長(zhǎng)會(huì)影響測(cè)試效率。現(xiàn)在只需要加載phonelist模塊,這樣更加載的內(nèi)容更少、測(cè)試更快。

app/phone-list/phone-list.component.spec.js:

describe('phoneList', function() {

  // Load the module that contains the `phoneList` component before each test
  beforeEach(module('phoneList'));

  ...

});

5 搜索框--過(guò)濾迭代器


phone-list模板

app/phone-list/phone-list.template.html:

<div class="container-fluid">
  <div class="row">
    <div class="col-md-2">
      <!--Sidebar content-->

      Search: <input ng-model="$ctrl.query" />

    </div>
    <div class="col-md-10">
      <!--Body content-->

      <ul class="phones">
        <li ng-repeat="phone in $ctrl.phones | filter:$ctrl.query">
          <span>{{phone.name}}</span>
          <p>{{phone.snippet}}</p>
        </li>
      </ul>

    </div>
  </div>
</div>
  • 添加了一個(gè)<input>標(biāo)簽,并且使用AngularJS的$filter函數(shù)來(lái)處理ngRepeat指令的輸入。
  • 數(shù)據(jù)綁定:輸入框和過(guò)濾器綁定"$ctrl.query",當(dāng)用戶向輸入框輸入值時(shí),過(guò)濾器可以馬上獲取該值。
  • 搜索功能:filter函數(shù)使用query的值過(guò)濾數(shù)據(jù),得到匹配query的手機(jī)數(shù)組。迭代器會(huì)根據(jù)filter生成的手機(jī)數(shù)組來(lái)自動(dòng)更新視圖。
tutorial_05.png

端到端測(cè)試

e2e-tests/scenarios.js:

describe('PhoneCat Application', function() {

  describe('phoneList', function() {

    beforeEach(function() {
      browser.get('index.html');
    });

    it('should filter the phone list as a user types into the search box', function() {
      var phoneList = element.all(by.repeater('phone in $ctrl.phones'));
      var query = element(by.model('$ctrl.query'));

      expect(phoneList.count()).toBe(3);

      query.sendKeys('nexus');
      expect(phoneList.count()).toBe(1);

      query.clear();
      query.sendKeys('motorola');
      expect(phoneList.count()).toBe(2);
    });

  });

});

命令行輸入:npm run protractor,自動(dòng)進(jìn)行測(cè)試。

6-7節(jié):AngularJS Phonecat (步驟6-步驟7)
8-9節(jié):AngularJS Phonecat (步驟8-步驟9)
10-12節(jié):AngularJS Phonecat(步驟10-步驟12)
13-14節(jié):AngularJS Phonecat(步驟13-步驟14)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,460評(píng)論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,067評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,467評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,468評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,184評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,582評(píng)論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,616評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,794評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,343評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,096評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,291評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,863評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,513評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,941評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,190評(píng)論 1 291
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,026評(píng)論 3 396
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,253評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,692評(píng)論 25 708
  • 導(dǎo)言 最近在學(xué)AngularJS的實(shí)例教程PhoneCat Tutorial App,發(fā)現(xiàn)網(wǎng)上的中文教程都比較久遠(yuǎn)...
    minxuan閱讀 1,589評(píng)論 0 6
  • 導(dǎo)言 最近在學(xué)AngularJS的實(shí)例教程PhoneCat Tutorial App,發(fā)現(xiàn)網(wǎng)上的中文教程都比較久遠(yuǎn)...
    minxuan閱讀 1,660評(píng)論 3 6
  • 對(duì)不起,我終究還是辜負(fù)了自己。 把自己關(guān)在小房間里,看著外面的細(xì)雨凄迷,淚水沽沽而落。以前特別介意新年的交替不要落...
    尺素合寸心閱讀 416評(píng)論 0 0
  • 一、今天,我豐盛,也許我永遠(yuǎn)也不知道下一步生活會(huì)給我什么,但我始終堅(jiān)信踏出去的每一步,都能夠收獲豐富的人生閱歷~我...
    仰望天空的魚兒_jiating閱讀 172評(píng)論 0 0