成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

如何優(yōu)雅的設(shè)計(jì) React 組件

Anchorer / 2634人閱讀

摘要:盡管現(xiàn)在的已不再那么流行,但的設(shè)計(jì)思想還是非常值得致敬和學(xué)習(xí)的,特別是的插件化。那么,如何解決我們回顧下的生命周期,父組件傳遞到子組件的的更新數(shù)據(jù)可以在中獲取。當(dāng)然,如何設(shè)計(jì)取決于你自己的項(xiàng)目,正所謂沒有最好的,

作者:曉冬
本文原創(chuàng),轉(zhuǎn)載請(qǐng)注明作者及出處

如今的 Web 前端已被 React、Vue 和 Angular 三分天下,一統(tǒng)江山十幾年的 jQuery 顯然已經(jīng)很難滿足現(xiàn)在的開發(fā)模式。那么,為什么大家會(huì)覺得 jQuery “過時(shí)了”呢?一來,文章《No JQuery! 原生 JavaScript 操作 DOM》就直截了當(dāng)?shù)母嬖V你,現(xiàn)在用原生 JavaScript 可以非常方便的操作 DOM 了。其次,jQuery 的便利性是建立在有一個(gè)基礎(chǔ) DOM 結(jié)構(gòu)的前提下的,看上去是符合了樣式、行為和結(jié)構(gòu)分離,但其實(shí) DOM 結(jié)構(gòu)和 JavaScript 的代碼邏輯是耦合的,你的開發(fā)思路會(huì)不斷的在 DOM 結(jié)構(gòu)和 JavaScript 之間來回切換。

盡管現(xiàn)在的 jQuery 已不再那么流行,但 jQuery 的設(shè)計(jì)思想還是非常值得致敬和學(xué)習(xí)的,特別是 jQuery 的插件化。如果大家開發(fā)過 jQuery 插件的話,想必都會(huì)知道,一個(gè)插件要足夠靈活,需要有細(xì)顆粒度的參數(shù)化設(shè)計(jì)。一個(gè)靈活好用的 React 組件跟 jQuery 插件一樣,都離不開合理的屬性化(props)設(shè)計(jì),但 React 組件的拆分和組合比起 jQuery 插件來說還是簡(jiǎn)單的令人發(fā)指。

So! 接下來我們就以萬能的 TODO LIST 為例,一起來設(shè)計(jì)一款 React 的 TodoList 組件吧!

實(shí)現(xiàn)基本功能

TODO LIST 的功能想必我們應(yīng)該都比較了解,也就是 TODO 的添加、刪除、修改等等。本身的功能也比較簡(jiǎn)單,為了避免示例的復(fù)雜度,顯示不同狀態(tài) TODO LIST 的導(dǎo)航(全部、已完成、未完成)的功能我們就不展開了。

約定目錄結(jié)構(gòu)

先假設(shè)我們已經(jīng)擁有一個(gè)可以運(yùn)行 React 項(xiàng)目的腳手架(ha~ 因?yàn)槲也皇莵斫棠闳绾未罱_手架的),然后項(xiàng)目的源碼目錄 src/ 下可能是這樣的:

.
├── components
├── containers
│   └── App
│       ├── app.scss
│       └── index.js
├── index.html
└── index.js

我們先來簡(jiǎn)單解釋下這個(gè)目錄設(shè)定。我們看到根目錄下的 index.js 文件是整個(gè)項(xiàng)目的入口模塊,入口模塊將會(huì)處理 DOM 的渲染和 React 組件的熱更新(react-hot-loader)等設(shè)置。然后,index.html 是頁(yè)面的 HTML 模版文件,這 2 個(gè)部分不是我們這次關(guān)心的重點(diǎn),我們不再展開討論。

入口模塊 index.js 的代碼大概是這樣子的:

// import reset css, base css...

import React from "react";
import ReactDom from "react-dom";
import { AppContainer } from "react-hot-loader";
import App from "containers/App";

const render = (Component) => {
  ReactDom.render(
    
      
    ,
    document.getElementById("app")
  );
};

render(App);

if (module.hot) {
  module.hot.accept("containers/App", () => {
    let nextApp = require("containers/App").default;
    
    render(nextApp);
  });
}

接下來看 containers/ 目錄,它將放置我們的頁(yè)面容器組件,業(yè)務(wù)邏輯、數(shù)據(jù)處理等會(huì)在這一層做處理,containers/App 將作為我們的頁(yè)面主容器組件。作為通用組件,我們將它們放置于 components/ 目錄下。

基本的目錄結(jié)構(gòu)看起來已經(jīng)完成,接下來我們實(shí)現(xiàn)下主容器組件 containers/App。

實(shí)現(xiàn)主容器

我們先來看下主容器組件 containers/App/index.js 最初的代碼實(shí)現(xiàn):

import React, { Component } from "react";
import styles from "./app.scss";

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      todos: []
    };
  }

  render() {
    return (
      

Todo List Demo

this.input = input} />
    {this.state.todos.map((todo, i) => (
  • this.handleStateChange(i)} > {todo.text}
  • ))}
); } handleAdd() { ... } handleRemove(index) { ... } handleStateChange(index) { ... } } export default App;

我們可以像上面這樣把所有的業(yè)務(wù)邏輯一股腦的塞進(jìn)主容器中,但我們要考慮到主容器隨時(shí)會(huì)組裝其他的組件進(jìn)來,將各種邏輯堆放在一起,到時(shí)候這個(gè)組件就會(huì)變得無比龐大,直到“無法收拾”。所以,我們得分離出一個(gè)獨(dú)立的 TodoList 組件。

分離組件 TodoList 組件

components/ 目錄下,我們新建一個(gè) TodoList 文件夾以及相關(guān)文件:

.
├── components
+│   └── TodoList
+│       ├── index.js
+│       └── todo-list.scss
├── containers
│   └── App
│       ├── app.scss
│       └── index.js
...

然后我們將 containers/App/index.js 下跟 TodoList 組件相關(guān)的功能抽離到 components/TodoList/index.js 中:

...
import styles from "./todo-list.scss";

export default class TodoList extends Component {
  ...
  
  render() {
    return (
      
-
+
this.input = input} />
-
+
-
    +
      {this.state.todos.map((todo, i) => (
    • this.handleStateChange(i)} > {todo.text}
    • ))}
); } ... }

有沒有注意到上面 render 方法中的 className,我們省去了 todo-list* 前綴,由于我們用的是 CSS MODULES,所以當(dāng)我們分離組件后,原先在主容器中定義的 todo-list* 前綴的 className ,可以很容易通過 webpack 的配置來實(shí)現(xiàn):

...
module.exports = {
  ...
  module: {
    rules: [
      {
        test: /.s?css/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              modules: true,
              localIdentName: "[name]--[local]-[hash:base64:5]"
            }
          },
          ...
        ]
      }
    ]  
  }
  ...
};

我們?cè)賮砜聪略摻M件的代碼輸出后的結(jié)果:

...
...
    ...

從上面 webpack 的配置和輸出的 HTML 中可以看到,className 的命名空間問題可以通過語(yǔ)義化 *.scss 文件名的方式來實(shí)現(xiàn),比如 TodoList 的樣式文件 todo-list.scss。這樣一來,省去了我們定義組件 className 的命名空間帶來的煩惱,從而只需要從組件內(nèi)部的結(jié)構(gòu)下手。

回到正題,我們?cè)賮砜聪路蛛x TodoList 組件后的 containers/App/index.js

import TodoList from "components/TodoList";
...

class App extends Component {
  render() {
    return (
      

Todo List Demo

); } } export default App;
抽離通用組件

作為一個(gè)項(xiàng)目,當(dāng)前的 TodoList 組件包含了太多的子元素,如:input、button 等。為了讓組件“一次編寫,隨處使用”的原則,我們可以進(jìn)一步拆分 TodoList 組件以滿足其他組件的使用。

但是,如何拆分組件才是最合理的呢?我覺得這個(gè)問題沒有最好的答案,但我們可以從幾個(gè)方面進(jìn)行思考:可封裝性、可重用性和靈活性。比如拿 h1 元素來講,你可以封裝成一個(gè) Title 組件,然后這樣 </b> 使用,又或者可以這樣 <b><Title>{title} 來使用。但你有沒有發(fā)現(xiàn),這樣實(shí)現(xiàn)的 Title 組件并沒有起到簡(jiǎn)化和封裝的作用,反而增加了使用的復(fù)雜度,對(duì)于 HTML 來講,h1 本身也是一個(gè)組件,所以我們拆分組件也是需要掌握一個(gè)度的。

好,我們先拿 input 和 button 下手,在 components/ 目錄下新建 2 個(gè) ButtonInput 組件:

.
├── components
+│   ├── Button
+│   │   ├── button.scss
+│   │   └── index.js
+│   ├── Input
+│   │   ├── index.js
+│   │   └── input.scss
│   └── TodoList
│       ├── index.js
│       └── todo-list.scss
...

Button/index.js 的代碼:

...
export default class Button extends Component {
  render() {
    const { className, children, onClick } = this.props;

    return (
      
    );
  }
}

Input/index.js 的代碼:

...
export default class Input extends Component {
  render() {
    const { className, value, inputRef } = this.props;

    return (
      
    );
  }
}

由于這 2 個(gè)組件自身不涉及任何業(yè)務(wù)邏輯,應(yīng)該屬于純渲染組件(木偶組件),我們可以使用 React 輕量的無狀態(tài)組件的方式來聲明:

...
const Button = ({ className, children, onClick }) => (
  
);

是不是覺得酷炫很多!

另外,從 Input 組件的示例代碼中看到,我們使用了非受控組件,這里是為了降低示例代碼的復(fù)雜度而特意為之,大家可以根據(jù)自己的實(shí)際情況來決定是否需要設(shè)計(jì)成受控組件。一般情況下,如果不需要獲取實(shí)時(shí)輸入值的話,我覺得使用非受控組件應(yīng)該夠用了。

我們?cè)倩氐缴厦娴?TodoList 組件,將之前分離的子組件 Button,Input 組裝進(jìn)來。

...
import Button from "components/Button";
import Input from "components/Input";
...

export default class TodoList extends Component {
  render() {
    return (
      
this.input = input} />
...
); } } ...
拆分子組件

然后繼續(xù)接著看 TodoList 的 items 部分,我們注意到這部分包含了較多的渲染邏輯在 render 中,導(dǎo)致我們需要浪費(fèi)對(duì)這段代碼與上下文之間會(huì)有過多的思考,所以,我們何不把它抽離出去:

...

export default class TodoList extends Component {
  render() {
    return (
      
...
{this.renderItems()}
); } renderItems() { return (
    {this.state.todos.map((todo, i) => (
  • ...
  • ))}
); } ... }

上面的代碼看似降低了 render 的復(fù)雜度,但仍然沒有讓 TodoList 減少負(fù)擔(dān)。既然我們要把這部分邏輯分離出去,我們何不創(chuàng)建一個(gè) Todos 組件,把這部分邏輯拆分出去呢?so,我們以“就近聲明”的原則在 components/TodoList/ 目錄下創(chuàng)建一個(gè)子目錄 components/TodoList/components/ 來存放 TodoList 的子組件 。why?因?yàn)槲矣X得 組件 TodosTodoList 有緊密的父子關(guān)系,且跟其他組件間也不太會(huì)有任何交互,也可以認(rèn)為它是 TodoList 私有的。

然后我們預(yù)覽下現(xiàn)在的目錄結(jié)構(gòu):

.
├── components
│   ...
│   └── TodoList
+│       ├── components
+│       │   └── Todos
+│       │       ├── index.js
+│       │       └── todos.scss
│       ├── index.js
│       └── todo-list.scss

Todos/index.js 的代碼:

...
const Todos = ({ data: todos, onStateChange, onRemove }) => (
  
    {todos.map((todo, i) => (
  • onStateChange(i)} > {todo.text}
  • ))}
); ...

再看拆分后的 TodoList/index.js

render() {
  return (
    
...
this.handleStateChange(index)} onRemove={(index) => this.handleRemove(index)} />
); }
增強(qiáng)子組件

到目前為止,大體上的功能已經(jīng)搞定,子組件看上去拆分的也算合理,這樣就可以很容易的增強(qiáng)某個(gè)子組件的功能了。就拿 Todos 來說,在新增了一個(gè) TODO 后,假如我們并沒有完成這個(gè) TODO,而我們又希望可以修改它的內(nèi)容了。ha~不要著急,要不我們?cè)俨鸱窒逻@個(gè) Todos,比如增加一個(gè) Todo 組件:

.
├── components
│   ...
│   └── TodoList
│       ├── components
+│       │   ├── Todo
+│       │   │   ├── index.js
+│       │   │   └── todo.scss
│       │   └── Todos
│       │       ├── index.js
│       │       └── todos.scss
│       ├── index.js
│       └── todo-list.scss

先看下 Todos 組件在抽離了 Todo 后的樣子:

...
import Todo from "../Todo";
...

const Todos = ({ data: todos, onStateChange, onRemove }) => (
  
    {todos.map((todo, i) => (
  • onStateChange(i)} />
  • ))}
); export default Todos;

我們先不關(guān)心 Todo 內(nèi)是何如實(shí)現(xiàn)的,就如我們上面說到的那樣,我們需要對(duì)這個(gè) Todo 增加一個(gè)可編輯的功能,從單純的屬性配置入手,我們只需要給它增加一個(gè) editable 的屬性:

 onStateChange(i)}
/>

然后,我們?cè)偎伎枷?,?Todo 組件的內(nèi)部,我們需要重新組織一些功能邏輯:

根據(jù)傳入的 editable 屬性來判斷是否需要顯示編輯按鈕

根據(jù)組件內(nèi)部的編輯狀態(tài),是顯示文本輸入框還是文本內(nèi)容

點(diǎn)擊“更新”按鈕后,需要通知父組件更新數(shù)據(jù)列表

我們先來實(shí)現(xiàn)下 Todo 的第一個(gè)功能點(diǎn):

render() {
  const { completed, text, editable, onClick } = this.props;

  return (
    
      
        {text}
      
      {editable && 
        
      }
    
  );
}

顯然實(shí)現(xiàn)這一步似乎沒什么 luan 用,我們還需要點(diǎn)擊 Edit 按鈕后能顯示 Input 組件,使內(nèi)容可修改。所以,簡(jiǎn)單的傳遞屬性似乎無法滿足該組件的功能,我們還需要一個(gè)內(nèi)部狀態(tài)來管理組件是否處于編輯中:

render() {
  const { completed, text, editable, onStateChange } = this.props,
    { editing } = this.state;

  return (
    
      {editing ? 
         this.input = input}
        /> :
        
          {text}
        
      }
      {editable && 
        
      }
    
  );
}

最后,Todo 組件在點(diǎn)擊 Update 按鈕后需要通知父組件更新數(shù)據(jù):

handleEdit() {
  const { text, onUpdate } = this.props;
  let { editing } = this.state;

  editing = !editing;

  this.setState({ editing });

  if (!editing && this.input.value !== text) {
    onUpdate(this.input.value);
  }
}

需要注意的是,我們傳遞的是更新后的內(nèi)容,在數(shù)據(jù)沒有任何變化的情況下通知父組件是毫無意義的。

我們?cè)倩剡^頭來修改下 Todos 組件對(duì) Todo 的調(diào)用。先增加一個(gè)由 TodoList 組件傳遞下來的回調(diào)屬性 onUpdate,同時(shí)修改 onClickonStateChange,因?yàn)檫@時(shí)的 Todo 已不僅僅只有單個(gè)點(diǎn)擊事件了,需要定義不同狀態(tài)變更時(shí)的事件回調(diào):

 onStateChange(i)}
+ onStateChange={() => onStateChange(i)}
+ onUpdate={(value) => onUpdate(i, value)}
/>

而最終我們又在 TodoList 組件中,增加 Todo 在數(shù)據(jù)更新后的業(yè)務(wù)邏輯。

TodoList 組件的 render 方法內(nèi)的部分示例代碼:

 this.handleUpdate(index, value)}
  onStateChange={(index) => this.handleStateChange(index)}
  onRemove={(index) => this.handleRemove(index)}
/>

TodoList 組件的 handleUpdate 方法的示例代碼:

handleUpdate(index, value) {
  let todos = [...this.state.todos];
  const target = todos[index];

  todos = [
    ...todos.slice(0, index),
    {
      text: value,
      completed: target.completed
    },
    ...todos.slice(index + 1)
  ];

  this.setState({ todos });
}
組件數(shù)據(jù)管理

既然 TodoList 是一個(gè)組件,初始狀態(tài) this.state.todos 就有可能從外部傳入。對(duì)于組件內(nèi)部,我們不應(yīng)該過多的關(guān)心這些數(shù)據(jù)從何而來(可能通過父容器直接 Ajax 調(diào)用后返回的數(shù)據(jù),或者 Redux、MobX 等狀態(tài)管理器獲取的數(shù)據(jù)),我覺得組件的數(shù)據(jù)屬性的設(shè)計(jì)可以從以下 3 個(gè)方面來考慮:

在沒有初始數(shù)據(jù)傳入時(shí)應(yīng)該提供一個(gè)默認(rèn)值

一旦數(shù)據(jù)在組件內(nèi)部被更新后應(yīng)該及時(shí)的通知父組件

當(dāng)有新的數(shù)據(jù)(從后端 API 請(qǐng)求的)傳入組件后,應(yīng)該重新更新組件內(nèi)部狀態(tài)

根據(jù)這幾點(diǎn),我們可以對(duì) TodoList 再做一番改造。

首先,對(duì) TodoList 增加一個(gè) todos 的默認(rèn)數(shù)據(jù)屬性,使父組件在沒有傳入有效屬性值時(shí)也不會(huì)影響該組件的使用:

export default class TodoList extends Component {
  constructor(props) {
    super(props);

    this.state = {
      todos: props.todos
    };
  }
  ...
}

TodoList.defaultProps = {
  todos: []
};

然后,再新增一個(gè)內(nèi)部方法 this.update 和一個(gè)組件的更新事件回調(diào)屬性 onUpdate,當(dāng)數(shù)據(jù)狀態(tài)更新時(shí)可以及時(shí)的通知父組件:

export default class TodoList extends Component {
  ...
  handleAdd() {
    ...
    this.update(todos);
  }

  handleUpdate(index, value) {
    ...
    this.update(todos);
  }

  handleRemove(index) {
    ...
    this.update(todos);
  }

  handleStateChange(index) {
    ...
    this.update(todos);
  }

  update(todos) {
    const { onUpdate } = this.props;

    this.setState({ todos });
    onUpdate && onUpdate(todos);
  }
}

這就完事兒了?No! No! No! 因?yàn)?this.state.todos 的初始狀態(tài)是由外部 this.props 傳入的,假如父組件重新更新了數(shù)據(jù),會(huì)導(dǎo)致子組件的數(shù)據(jù)和父組件不同步。那么,如何解決?

我們回顧下 React 的生命周期,父組件傳遞到子組件的 props 的更新數(shù)據(jù)可以在 componentWillReceiveProps 中獲取。所以我們有必要在這里重新更新下 TodoList 的數(shù)據(jù),哦!千萬別忘了判斷傳入的 todos 和當(dāng)前的數(shù)據(jù)是否一致,因?yàn)?,?dāng)任何傳入的 props 更新時(shí)都會(huì)導(dǎo)致 componentWillReceiveProps 的觸發(fā)。

componentWillReceiveProps(nextProps) {
  const nextTodos = nextProps.todos;

  if (Array.isArray(nextTodos) && !_.isEqual(this.state.todos, nextTodos)) {
    this.setState({ todos: nextTodos });
  }
}

注意代碼中的 _.isEqual,該方法是 Lodash 中非常實(shí)用的一個(gè)函數(shù),我經(jīng)常拿來在這種場(chǎng)景下使用。

結(jié)尾

由于本人對(duì) React 的了解有限,以上示例中的方案可能不一定最合適,但你也看到了 TodoList 組件,既可以是包含多個(gè)不同功能邏輯的大組件,也可以拆分為獨(dú)立、靈巧的小組件,我覺得我們只需要掌握一個(gè)度。當(dāng)然,如何設(shè)計(jì)取決于你自己的項(xiàng)目,正所謂:沒有最好的,只有更合適的。還是希望本篇文章能給你帶來些許的小收獲。

iKcamp官網(wǎng):http://www.ikcamp.com

訪問官網(wǎng)更快閱讀全部免費(fèi)分享課程:《iKcamp出品|全網(wǎng)最新|微信小程序|基于最新版1.0開發(fā)者工具之初中級(jí)培訓(xùn)教程分享》。
包含:文章、視頻、源代碼

iKcamp原創(chuàng)新書《移動(dòng)Web前端高效開發(fā)實(shí)戰(zhàn)》已在亞馬遜、京東、當(dāng)當(dāng)開售。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/89657.html

相關(guān)文章

  • React 應(yīng)用設(shè)計(jì)之道 - curry 化妙用

    摘要:右側(cè)展現(xiàn)對(duì)應(yīng)產(chǎn)品。我們使用命名為的對(duì)象表示過濾條件信息,如下此數(shù)據(jù)需要在組件中進(jìn)行維護(hù)。因?yàn)榻M件的子組件和都將依賴這項(xiàng)數(shù)據(jù)狀態(tài)。化應(yīng)用再回到之前的場(chǎng)景,我們?cè)O(shè)計(jì)化函數(shù),進(jìn)一步可以簡(jiǎn)化為對(duì)于的偏應(yīng)用即上面提到的相信大家已經(jīng)理解了這么做的好處。 showImg(https://segmentfault.com/img/remote/1460000014458612?w=1240&h=663...

    sewerganger 評(píng)論0 收藏0
  • React 應(yīng)用設(shè)計(jì)之道 - curry 化妙用

    摘要:右側(cè)展現(xiàn)對(duì)應(yīng)產(chǎn)品。我們使用命名為的對(duì)象表示過濾條件信息,如下此數(shù)據(jù)需要在組件中進(jìn)行維護(hù)。因?yàn)榻M件的子組件和都將依賴這項(xiàng)數(shù)據(jù)狀態(tài)。化應(yīng)用再回到之前的場(chǎng)景,我們?cè)O(shè)計(jì)化函數(shù),進(jìn)一步可以簡(jiǎn)化為對(duì)于的偏應(yīng)用即上面提到的相信大家已經(jīng)理解了這么做的好處。 showImg(https://segmentfault.com/img/remote/1460000014458612?w=1240&h=663...

    LinkedME2016 評(píng)論0 收藏0
  • 玩轉(zhuǎn) React(三)- JavaScript代碼里寫HTML一樣可以很優(yōu)雅

    摘要:但是隨著程序邏輯越來越復(fù)雜,業(yè)務(wù)邏輯代碼跟代碼混到一起就變得越來越難以維護(hù),所以就有了開發(fā)模式。其實(shí)只是給加了點(diǎn)糖上面這種在中寫類似代碼的語(yǔ)法被稱為。你可以理解為擴(kuò)展版的。尤其是對(duì)一些相對(duì)還比較流行的框架或技術(shù),更是如此。 這是《玩轉(zhuǎn) React》系列的第三篇,看到本篇的標(biāo)題,了解過 React 的同學(xué)可能已經(jīng)大致猜到我要講什么了,本篇中要講的內(nèi)容對(duì)于剛接觸 React 的同學(xué)來說,可...

    android_c 評(píng)論0 收藏0
  • 玩轉(zhuǎn) React(三)- JavaScript代碼里寫HTML一樣可以很優(yōu)雅

    摘要:但是隨著程序邏輯越來越復(fù)雜,業(yè)務(wù)邏輯代碼跟代碼混到一起就變得越來越難以維護(hù),所以就有了開發(fā)模式。其實(shí)只是給加了點(diǎn)糖上面這種在中寫類似代碼的語(yǔ)法被稱為。你可以理解為擴(kuò)展版的。尤其是對(duì)一些相對(duì)還比較流行的框架或技術(shù),更是如此。 這是《玩轉(zhuǎn) React》系列的第三篇,看到本篇的標(biāo)題,了解過 React 的同學(xué)可能已經(jīng)大致猜到我要講什么了,本篇中要講的內(nèi)容對(duì)于剛接觸 React 的同學(xué)來說,可...

    elarity 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

Anchorer

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<