ng2組件

組件基礎

組件用來包裝特定的功能,應用程序的有序運行依賴于組件之間的協同工作。
組件是angular應用的最小邏輯單元,模塊則是在組件之上的一層抽象。所有angular組件都可以獨立存在,也意味著任何組件都可以作為根組件被引導,也可以被路由加載,或在其他組件中使用。不過一個組件不能單獨被啟動,它必須被包裝到模塊里,然后通過Bootstrap模塊接口引導入口模塊來啟動angular應用。

創建組件的步驟

創建angular組件三步驟:

  1. @angular/core中引入Component裝飾器
  2. 建立一個普通的類,并用@Component修飾它
  3. @Component中,設置selector自定義標簽和template模板
//contactItem.component.ts
import { Component } from '@angular/core';
@Component({
    selector:'contact-item',
    template:`
        <div>
            <p>張三</p>
            <p>13800000</p>
        </div>
    `
})
export class ContactItemComponent {}

以上代碼創建了一個最簡單的組件。使用這個組件需要在HTML中添加<contact-item>自定義標簽,然后angular便會在此標簽中插入ContactItemComponent組件中指定的模板。

<div>
    <contact-item></contact-item>
</div>

最終會被渲染成:

<div>
    <contact-item>
        <div>
            <p>張三</p>
            <p>13800000</p>
        </div>
    </contact-item>
</div>

組件基礎構成

組件裝飾器

@Component是TypeScript的語法,它是一個裝飾器,任何一個angular的組件類都會用這個裝飾器修飾,如果移除了這個裝飾器,它將不再是angular的組件。

組件元數據

ContactItemComponent這個組件中的@Component裝飾器部分,使用到了大部分組件需要的元數據:用于定義組件標簽名的selector;用于定義組件宿主元素模板的template

1.selector

selector是用于定義組件在HTML代碼中匹配的標簽,它將成為組件的命名標記。通常情況下都需要設置selector,特殊情況下也可以忽略,不指定時默認為匹配div元素,但不建議這樣做。selector的命名方式建議用“烤肉串式”命名,即采用小寫字母并以“-”分隔。

2.template

template是為組件指定一個內聯模板。使用ES6的多行字符串``語法能夠創建多行模板。

3.templateUrl

templateUrl是為組件指定一個外部模板的URL地址。

@Component({
    templateUrl:'app/component/contact-item.html'
})

每個組件只能指定一個模板,可以使用templatetemplateUrl的引入方式。

4.styles

styles是為組件指定內聯樣式。

@Component({
    styles:[
        `
        li:last-child{
            border-bottom:none;
        }
        `
    ]
})
5.styleUrls

styleUrls是為組件指定一系列用于該組件的外聯樣式表文件。

@Component({
    styleUrls:['app/list/item.component.css']
})

stylesstyleUrls允許同時指定。如果同時指定,styles中的樣式會先被解析,然后才會解析styleUrls中的樣式,換句話說,styles的樣式會被styleUrls的樣式覆蓋。另外,也可以在模板的DOM節點上直接寫樣式,它的優先級最高。

模板

每個組件都必須設置一個模板,angular才能將組件內容渲染到DOM上,這個DOM元素叫做宿主元素。組件可以與宿主元素交互,交互的形式包括:

  • 顯示數據:在模板中可以使用{{}}來顯示組件的數據。
//contactItem.component.ts
import { Component } from '@angular/core';
@Component({
    selector:'contact-item',
    template:`
        <div>
            <p>{{name}}</p>
            <p>{{phone}}</p>
        </div>
    `
})
export class ContactItemComponent{
    name:string='張三';
    phone:string='13800000';
}
  • 雙向數據綁定:[(ngModel)]="property"
  • 監聽宿主元素事件以及調用組件方法:(click)="addContact()"

組件與模塊

組件通過與其他組件協作,完成一個完整的功能特性。這樣的功能特性通常會封裝到一個模塊里。
模塊是在組建之上的一層抽象,組件以及指令、管道、服務、路由等都能通過模塊去組織。

模塊的構成

angular提供了@NgModule裝飾器來創建模塊,一個應用可以有多個模塊,但有且只有一個根模塊,其他模塊叫做特性模塊。根模塊是啟動應用的入口模塊,根模塊必須通過bootstrap元數據來指定應用的根組件,然后通過bootstrapModule()方法來啟動應用。

//app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-brower';
import { ContactItemComponent } from './contactItem.component';
@NgModule({
    imports:[BrowserModule],
    declarations:[ContactItemComponent],
    bootstrap:[ContactItemComponent]
})
export class AppModule {}

//app.ts
import { platformBrowseerDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);

bootstrap這個元數據用于指定應用的根組件,NgModule主要的元數據如下。

  • declarations:用于指定屬于這個模塊的視圖類,即指定哪些部件組成了這個模塊。angular有組件、指令和管道三種視圖類,這些視圖類只能屬于一個模塊,不能再次聲明屬于其他模塊的類。
  • exports:導出視圖類。當該模塊被引入到外部模塊時,這個屬性指定了外部模塊可以使用該模塊的哪些視圖類,所以它的值類型跟declarations一致(組件、指令和管道)。
  • imports:引入該模塊依賴的其他模塊或路由,引入后模塊里的組件模板才能引用外部對應的組件、指令和管道。
  • providers:指定模塊依賴的服務,引入后該模塊中的所有組件都可以使用這些服務。
視圖類引入

NgModule提供了declarations這個元數據來將指令、組件或管道等視圖類引入到當前模塊。
在上面的AppModule中,通過declarationsContactItemComponent組件引入到了AppModule模塊中,使得所有屬于AppModule模塊的其它組件的模板都可以使用<contact-item>

//app.module.ts
@NgModule({
    declarations:[
        AppComponent,
        ListComponent,
        ListItemComponent,
        DetailComponent,
        CollectionComponent,
        EditComponent,
        HeaderComponent,
        FooterComponent,
        PhonePipe,
        BtnClickDirective
    ]
    //...
})
export class AppModule {}

如果在ListComponent組件的模板代碼list.component.html中,使用到了HeaderComponentFooterComponentListItemComponent這三個組件,這個時候必須在ListComponent所屬的模塊(即AppModule)中,通過declarations引入這三個組件和ListItemComponent組件后才能在模板中使用他們。

<!--list.component.html-->
<!--組件中制定了HeaderComponent,才能使用my-header標簽-->
<my-header title="所有聯系" [isShowCreateButton]="true"></my-header>
<ul class="list">
    <li *ngFor="let contact of contacts">
        <list-item [contact]="contact" (routerNavigate)="routerNavigate($event)"></list-item>
    </li>
</ul>
<my-footer></my-footer>

ngFor是angular的內置指令,在引入BrowserModule的時候就已經引入了常用的內置指令。

導出視圖類以及導入依賴模塊

有時候模塊中的組件、指令或管道,可能也會在其他模塊中使用。可以使用export元數據對外暴露這些組件、指令或管道。

//contact.module.ts
import { NgModule } from '@angular/core';
import { ContactItemComponent } from './contactItem.component';
@NgModule({
    declarations:[ContactItemComponent],
    exports:[ContactItemComponent] //導出組件
})
export class ContactModule {}

//message.module.ts
import { NgModule } from '@angular/core';
import { ContactModule } from './contact.module';
import { SomeOtherComponent } from './someother.component';
@NgModule({
    //在SomeOtherComponent組件的模板中可以使用到<contact-item>組件了
    declarations:[SomeOtherComponent],
    imports:[ContactModule] //導入模塊
})
export class MessageModule {}
服務引入

服務通常用于處理業務邏輯及相關的數據。引入服務有兩種方式:一種是通過@NgModuleproviders,另一種是通過@Componentproviders

//app.module.ts
import { ContactService } from './shared';
//...
@NgModule({
    //...
    providers:[ContactService],
    bootstrap:[AppComponent]
})
export class AppModule {}

所有被包含在AppModule中的組件,都可以使用到這些服務。同樣,在組件中也可以用providers來引入服務,該組件及其子組件都可以公用這些引入的服務。

組件交互

組件交互就是組件通過一定的方式來訪問其他組件的屬性或方法,從而實現數據雙向流動。組件交互包括父子組件交互和一些非父子關系組件的交互。組件交互有很多方式,非父子關系的組件可以通過服務來實現數據交互通信。

組件的輸入輸出屬性

angular提供了輸入(@Input)和輸出(@Output)語法來處理組件數據的流入流出。

//item.component.ts
export class ListItemComponent implements OnInit {
    @Input() contact:any={};
    @Output() routerNavigate=new EventEmitter<number>();
}
<!--list.component.html-->
<li *ngFor="let contact of contacts">
    <list-item [contact]="contact" (routerNavigate)="routerNavigate($event)">
    </list-item>
</li>

ListItemComponent組件的作用是顯示單個聯系人的信息。由于聯系人列表數據是在ListCommponent組建中維護的,在顯示單個聯系人時,需要給ListItemComponent傳入單個聯系人數據。另外在單擊單個聯系人時,需要跳轉到此聯系人的明細信息,需要子組件通知父組件進行跳轉,[contact](routerNavigate)的輸入輸出變量,用于滿足上述功能。
這里的輸入輸出是以當前組件角度去說的,contactrouterNavigate分別是ListItemComponent組件的輸入和輸出屬性。輸出屬性一般是以事件的形式,將數據通過EventEmitter拋出去。
除了使用@Input@Output修飾外,還可以在組件的元數據中使用inputsoutput來設置輸入輸出屬性,設置的值必須為字符串數組,元素的名稱需要和成員變量相對應。

@Component({
    inputs:['contact'],
    outputs:['routerNavigate']
})
export class ListItemComponent implements OnInit {
    contact:any={};
    routerNavigate=new EventEmitter<number>();
}

父組件向子組件傳遞數據

父組件的數據通過子組件的輸入屬性流入子組件,在子組件完成接收或攔截,以此實現了數據由上而下的傳遞。

父到子組件間的數據傳遞

父組件LIstComponent將獲取到聯系人的數據,通過屬性綁定的方式流向子組件ListItemComponent

//list.component.ts
import { Component,OnInit } from '@angular/core';
@Component({
    selector:'list',
    template:`
        <ul class="list">
            <li *ngFor="let contact of contacts">
                <list-item [contact]="contact"></list-item>
            </li>
        </ul>
    `
})
export class ListComponent implements OnInit {
    this.contacts=data;//data為獲取到的聯系人數據
}
//item.component.ts
import { Component,OnInit,Input } from '@angular/core';
@Component({
    selector:'list-item',
    template:`
        <div class="contact-info">
            <label class="contact-name">{{contact.name}}</label>
            <span class="contact-tel">{{contact.telNum}}</span>
        </div>
    `
})
export class ListItemComponent implements OnInit {
    @Input() contact:any={};
}

在之前的例子中,app.module.ts中已經通過@NgModule的元數據declarations將子組件ListItemComponent的實例引入到AppModule中,使得所有屬于AppModule中的其他組件都可以使用ListItemComponent組件,因此在父組件ListComponent中可直接引用該子組件。將每個聯系人對象通過屬性綁定的方式綁定到屬性contact中來供子組件來引用,數據由上而下流入子組件,在子組件中通過@Input裝飾器進行數據的接收。

攔截輸入屬性

子組件可以攔截輸入屬性的數據并進行相應的處理。有兩種攔截處理方式。

1.setter攔截輸入屬性

gettersetter通常配套使用,用來對屬性進行相關約束。他們提供了一些屬性讀寫的封裝。setter可以對屬性進行再封裝處理,對復雜的內部邏輯通過訪問權限控制來隔絕外部調用,以避免外部的錯誤調用影響到內部的狀態。同時也把內部復雜的邏輯結構封裝成高度抽象可被簡單調用的屬性,再通過getter返回要設置的屬性值。
通過輸入輸出屬性可以實現父子組件的數據交互,還可以通過setter來攔截來自父組件的數據源,并進行處理,使數據的輸出更合理。

//ListComponent.ts使用前面的例子
//ListItem.component.ts
@Component({
    selector:'list-item',
    template:`
        <div>
            <label class="contact-name">{{contactObj.name}}</label>
            <span class="contact-tel">{{contactObj.telNum}}</span>
        </div>
    `
})
export class ListItemComponent implements OnInit{
    _contact:object={};
    @Input()
    set contactObj(contact:object){
        this._contact.name=(contact.name&&contact.name.trim())||'no name seet';
        this._contact.telNum=contact.telNum||'000-000';
    }
    get contactObj(){ return this._contact; }
}

這里通過setter的方式設置一個contactObj屬性對象,其作用是對通過@Input修飾符獲取的數據contact進行二次處理,再通過getter返回這個contactObj對象。這樣處理的作用是使得聯系人不會出現nullundefined的情況。gettersetter其實是在組件類的原型對象上設置了一個contactObj屬性。

Object.defineProperty(ListItemComponent.prototype,"contactObj",{
    //...
})
2.ngOnChanges監聽數據變化

ngOnChanges用于及時響應angular在屬性綁定中發生的數據變化,該方法接收一個對象參數,包含當前值和變化前的值。在ngOnInit之前,或者當數據綁定的輸入屬性的值發生變化時會觸發。ngOnChanges是組件的生命周期鉤子之一。

//父組件detail.component.ts
import { Component } from '@angular/core';
@Component({
    selector:'detail',
    template:`
        <a class="edit" (click)="editContact()">編輯</a>
        <change-log [contact]="detail"></change-log>
    `
})
export class DetailComponent implements OnInit{
    detail:any={};
    //完成聯系人編輯修改
    editContact(){
        //...
        this.detail=data;//修改后的數據
    }
}
//子組件changelog.component.ts
import { Component,Input,OnChanges,SimpleChanges } from '@angular/core';
@Component({
    selector:'change-log',
    template:`
        <h4>Change log:</h4>
        <ul>
            <li *ngFor="let change of changes">{{change}}</li>
        </ul>
    `
})
export class ChangeLogComponent implements OnChanges{
    @Input() contact:any={};
    changes:string[]=[];
    ngOnChanges(changes:{[propKey:string]:SimpleChanges}){
        let log:string[]=[];
        for(let propName in changes){
            let changedProp=changes[propName],
                from=JSON.stringify(changedProp.previousValue),
                to=JSON.stringify(changedProp.currentValue);
            log.push(`${propName} changed from ${from} to ${to}`);
        }
        this.changes.push(log.join(', '));
    }
}

SimpleChanges類是angular的一個基礎類,用于處理數據的前后變化,其包含兩個重要成員變量,分別是previousValuecurrentValuepreviousValue是獲取變化前的數據,currentValue是獲取變化后的數據。
ngOnChanges當且僅當組件輸入數據變化時被調用,“輸入數據”指的是通過@Input裝飾器顯式指定的那些數據。

子組件向父組件傳遞數據

使用事件傳遞是子組件向父組件傳遞數據的最常用方式。子組件需要實例化一個用來訂閱和觸發自定義事件的EventEmitter類,這個實例對象是一個由裝飾器@Output修飾的輸出屬性,當有用戶操作行為發生時該事件會被觸發,父組件則通過事件綁定的方式來訂閱來自子組件觸發的事件。

//collection.component.ts  父組件
import { Component } from '@angular/core';
@Component({
    selector:'collection',
    template:`
        <contact-collect [contact]="detail" (onCollect)="collectTheContact($event)">
        </contact-collect>
    `
})
export class CollectionComponent implements OnInit{
    detail:any={};
    collectTheContact(){
        this.detail.collection==0?this.detail.collection=1:this.detail.collection=0;
    }
}

父組件CollectionComponent通過綁定自定義事件onCollect訂閱來自子組件觸發的事件。當有來自子組件對應的事件被觸發,在父組件中能夠監聽到該事件。

//contactCollect.component.ts  子組件
import { Component,EventEmitter,Input,Output } from '@angular/core';
@Component({
    selector:'contact-collect',
    template:`
        <i [ngClass]="{collected:contact.collection}" (click)="collectTheContact()"收藏</i>
    `
})
export class ContactCollectComponent {
    @Input() contact:any={};
    @Output() onCollect=new EventEmitter<boolean>();
    collectTheContact(){
        this.onCollect.emit();
    }
}

其他組件交互方式

父子組件間數據傳遞的實現還有兩種方式:

  • 父組件通過局部變量獲取子組件引用
  • 父組件使用@ViewChild獲取子組件的引用
通過局部變量實現數據交互

模板局部變量可以獲取子組件的實例引用。通過創建模板局部變量的方式,來實現父組件與子組件數據交互,即在父組件的模板中為子組件創建一個局部變量,這個父組件可以通過這個局部變量來獲得讀取子組件公共成員變量和函數的權限。模板局部變量的作用域范圍僅存在于定義該模板局部變量的子組件。

//父組件collection.component.ts
import { Component } from '@angular/core';
@Component({
    selector:'collection',
    template:`
        <contact-collect (click)="collectTheContact()" #collect></contact-collect>
    `
})
export class CollectionComponent {}
//子組件contactcollect.component.ts
import { Component } from '@angular/core';
@Component({
    selector:'contact-collect',
    template:`
        <i [ngClass]="{collect:detail.collection}">收藏</i>
    `
})
export class ContactCollectComponent{
    detail:any={};
    collectTheContact() {
        this.deetail.collection==0?this.detail.collection=1:this.detail.collection=0;
    }
}

這里通過在標簽元素<contact-collect>中放置一個局部變量collect,用來獲取子組件的實例引用,來訪問子組件中的成員變量和方法。

使用@ViewChild實現數據交互

當父組件需要獲取到子組件中變量或方法的讀寫權限時,可以通過@ViewChild注入的方式來實現。組件中元數據ViewChild的作用是聲明對子組件元素的實例引用,它提供了一個參數來選擇將要引用的組件元素,這個參數可以是一個類的實例,也可以是一個字符串。

  • 參數為類實例,表示父組件將綁定一個指令或子組件實例
  • 參數為字符串類型,表示將起到選擇器的作用,即相當于在父組件中綁定一個模板局部變量,獲取到子組件的一份實例對象的引用

組件中元數據ViewChild的參數為字符串的實現方式和綁定模板局部變量是一樣的。

import { Component,AfterViewInit,ViewChild } from '@angular/core';
@Component({
    selector:'collection',
    template:`
        <contact-collect (click)="collectTheContact()"></contact-collect>
    `
})
export class CollectionComponent{
    @ViewChild(ContactCollectComponent) contactCollect:CotactCollectComponent;
    ngAfterViewInit(){
        //...
    }
    collectTheContact(){
        this.contactCollect.collectTheContact();
    }
}

組件內容嵌入

內容嵌入通常用來創建可復用組件。

import { Component } from '@angular/core';
@Component({
    selector:'example-content',
    template:`
        <div>
            <h4>ng-content 示例</h4>
            <div style="background-color:gray;padding:5px;margin:2px">
                <ng-content select="header"></ng-content>
            </div>
        </div>
    `
})
class NgContentExampleComponent {}

接著再定義一個根組件來使用它。

import { Component } from 'angular/core';
@Component({
    selector:'app',
    template:`
        <example-content>
            <header>Header content</header>
            <!--將自定義的內容放到 example-content標簽之間-->
        </example-content>
    `
})
export class NgContentAppComponent {}

最后組件的DOM樹會被渲染成:

<app>
    <example-content>
        <div>
            <h4>ng-content示例</h4>
            <div style="background-color:gray;padding:5px;margin:2px">
                <ng-content select="header"></ng-content>
            </div>
        </div>
    <example-content>
</app>

<example-content>標簽之間的內容,也就是<header>Header content</header>,被填充到ng-content,而NgContentExampleComponent組件模板中的其他元素沒有受到影響。select="header"表示匹配<example-content>標簽之間的第一個header標簽,并將其填充到相應的ng-content中。
還可以同時使用多個嵌入內容。

import { Component } from '@angular/core';
@Component({
    selector:'example-content',
    template:`
        <div>
            <h4>component with ng-content</h4>
            <div style="background-color:green;padding:5px;margin:2px">
                <ng-content select="header"></ng-content>
            </div>
<div style="background-color:gray;padding:5px;margin:2px">
                <ng-content select=".class-select"></ng-content>
            </div>
<div style="background-color:blue;padding:5px;margin:2px">
                <ng-content select="[name=footer]"></ng-content>
            </div>
        </div>
    `
})
class NgContentExampleComponent {}

//NgContemtAppComponent.ts
import { Component } from 'angular/core';
@Component({
    selector:'app',
    template:`
        <example-content>
            <header>Header content</header>
            <div class="class-select">div with .class-content</div>
            <div name="footer">Footer content</div>
        </example-content>
    `
})
export class NgContentAppComponent {}

組件生命周期

組件的生命周期由angular內部管理,從組件的創建、創建,到數據變動事件的觸發,再到組件從DOM中移除,angular都提供了一系列鉤子。這些鉤子可以在這些事件觸發時,執行相應的回調函數。

生命周期鉤子

鉤子的接口包含@angular/core中。每個接口都對應一個名為ng+接口名的方法。

class ExampleInitHook implements OnInit {
    constructor() {}
    ngOnInit() {
        console.log('OnInit');
    }
}

以下是組件常用的生命周期鉤子方法,angular會按以下的順序依次調用鉤子方法:

  • ngOnChanges
  • ngOnInit
  • ngDoCheck
  • ngAfterContentInit
  • ngAfterContentChecked
  • ngAfterViewInit
  • ngAfterViewChecked
  • ngOnDestroy

除此之外,有的組件還提供了自己特有的生命周期鉤子,例如路由有routerOnActivate鉤子。

ngOnChanges

ngOnChanges鉤子用來響應組件輸入值發生變化時觸發的事件。該方法接收一個SimpleChanges對象,包含當前值和變化前的值。該方法在ngOnInit之前,或者當數據綁定輸入屬性的值發生變化時觸發。
只要在組件里定義了ngOnChanges方法,在輸入數據發生變化時該方法就會被自動調用。這里的“輸入數據”指的是通過@Input裝飾器顯示指定的變量。

ngOnInit

ngOnInit鉤子用于數據綁定輸入屬性之后初始化組件。該鉤子方法會在第一次ngOnChanges之后被調用。
使用ngOnInit有兩個重要原因:

  • 組件構造后不久就要進行復雜的初始化
  • 需要在輸入屬性設置完成之后才構建組件

在組件中,經常會使用ngOnInit獲取數據。

ngDoCheck

用于變化監測,該鉤子方法會在每次變化監測發生時被調用。
每一個變化監測周期內,不管數據值是否發生了變化,ngDoCheck都會被調用。但要慎用,例如鼠標移動時會觸發mousemove事件,此時變化監測會被頻繁觸發,隨之ngDoCheck也會被頻繁調用。因此,ngDoCheck方法中不能寫復雜的代碼,否則性能會受影響。
絕大多數情況下,ngDoCheckngOnChanges不應該一起使用。ngOnChanges能做的事,ngDoCheck也能做到,而ngDoCheck監測的力度更小,可完成更靈活的變化監測邏輯。

ngAfterContentInit

在組件中使用<ng-content>將外部內容嵌入到組件視圖后就會調用ngAfterContentInit,它在第一次ngDoCheck執行后調用,且只執行一次。

ngAfterContentChecked

在組件使用了<ng-content>自定義內容的情況下,angular在這些外部內容嵌入到組件視圖后,或每次變化監測的時候都會調用ngAfterContentChecked

ngAfterViewInit

ngAfterViewInit會在angular創建了組件的視圖及其子視圖之后被調用。

ngAfterViewChecked

ngAfterViewChecked在angular創建了組件的視圖及其子組件視圖之后被調用一次,并且在每次子組件變化監測時也會被調用。

ngOnDestroy

ngOnDestroy在銷毀指令/組件之前觸發。那些不會被垃圾回收器自動回收的資源(如已訂閱的觀察者事件、綁定過的DOM事件、通過setTimeoutsetInterval設置過的計時器等)都應當在ngOnDestroy中手動銷毀掉,從而避免發生內存泄漏等問題。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,993評論 19 139
  • 本文章是我最近在公司的一場內部分享的內容。我有個習慣就是每次分享都會先將要分享的內容寫成文章。所以這個文集也是用來...
    Awey閱讀 9,477評論 4 67
  • 今天我來談談學習的過程,學習是一個持續的過程,而且學習必須是一個不斷沖擊自己意識的一個過程,一個好的理論、理念,你...
    春曉育兒講堂閱讀 660評論 0 0
  • 今天經緯實驗小學一年級(二)班的微信群信息不斷更新!寶爸寶媽們都積極參與討論明天學前培訓!想來,如果跟你談...
    周國景閱讀 252評論 0 1
  • 今日陽光普照 我的心情不在云霄 也不曾藏匿幽邃深谷 上學的路上 我踩著自己的步伐 找尋自己的節奏和感覺 口袋里的耳...
    阿琴姑娘閱讀 769評論 42 67