日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

react避免子组件渲染_如何与React一起使用正确的方法来避免一些常见的陷阱

發布時間:2023/11/29 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 react避免子组件渲染_如何与React一起使用正确的方法来避免一些常见的陷阱 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

react避免子組件渲染

One thing I hear quite often is “Let’s go for Redux” in our new React app. It helps you scale, and the App data shouldn’t be in React local state because it is inefficient. Or when you call an API and while the promise is pending, the component get unmounted and you get the following beautiful error.

我經常聽到的一件事是我們新的React應用程序中的“ Letux去吧 ”。 它可以幫助您擴展,并且App數據不應該處于React本地狀態,因為它效率低下。 或者,當您調用API且未完成Promise時,該組件將被卸載,并出現以下錯誤。

Warning: Can’t call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.警告:無法在已卸載的組件上調用setState(或forceUpdate)。 這是空操作,但它表明應用程序中發生內存泄漏。 要解決此問題,請在componentWillUnmount方法中取消所有訂閱和異步任務。

So the solution people usually arrive at is using Redux. I love Redux and the work that Dan Abramov is doing is simply incredible! That dude rocks big time — I wish I was as half talented as he is.

因此,人們通常會想到的解決方案是使用Redux 我喜歡Redux, Dan Abramov所做的工作簡直令人難以置信! 那家伙花了很大的時間-我希望我像他一樣才華橫溢。

But I am sure that when Dan made Redux, he was just giving us a tool in our tool-belt as a helper. It’s not the Jack of all tools. You don’t use a hammer when you can screw the bolt with a screw driver.

但是我敢肯定,當Dan制作Redux時,他只是在給我們提供工具,作為我們的助手。 不是所有工具的杰克。 使用螺絲刀擰緊螺栓時,請勿使用錘子。

Dan even agrees.

丹甚至同意

I love React, and I have been working on it for almost two years now. So far, no regrets. Best decision ever. I like Vue and all the cool library/frameworks out there. But React holds a special place in my heart. It helps me focus on the work that I am suppose to do rather then taking up all my time in DOM manipulations. And it does this in the best and most efficient way possible. with its effective reconciliation.

我喜歡React,并且我已經從事了將近兩年的工作。 到目前為止,沒有任何遺憾。 有史以來最好的決定。 我喜歡Vue和所有不錯的庫/框架。 但是React在我心中占有特殊的位置。 它可以幫助我專注于我應該做的工作,而不是花所有時間從事DOM操作。 它以最佳和最有效的方式做到這一點。 有效的和解 。

I have learned a lot over these past few years, and I’ve noticed a common problem among new and experienced React developers alike: not using React the right way when dealing with subscription or asynchronous tasks. I feel that the documentation out there isn’t well put up in this case, and so I decided to write this article.

在過去的幾年中,我學到了很多東西,而且我注意到新的和經驗豐富的React開發人員之間都存在一個共同的問題:在處理訂閱或異步任務時,沒有以正確的方式使用React。 我覺得這種情況下的文檔不夠完善,所以我決定寫這篇文章。

I’ll talk about subscriptions first, and then we’ll move on to handling asynchronous task cancellation to avoid memory leaks in React (the main purpose of this article). If not handled, this slows our app down.

我將首先討論訂閱,然后我們將繼續處理異步任務取消以避免React中的內存泄漏(本文的主要目的)。 如果不處理,這會使我們的應用程序變慢。

Now let’s get back to that beautiful error message that we initially talked about:

現在,讓我們回到最初討論的漂亮錯誤消息:

Warning: Can’t call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.警告:無法在已卸載的組件上調用setState(或forceUpdate)。 這是空操作,但它表明應用程序中發生內存泄漏。 要解決此問題,請在componentWillUnmount方法中取消所有訂閱和異步任務。

My goal for this article is to make sure that no one ever has to face this error and not know what to do about it again.

我在本文中的目標是確保沒有人必須面對這個錯誤并且不知道該怎么辦。

我們將介紹的內容 (What we’ll cover)

  • Clear subscriptions like setTimeout/setInterval

    清除訂閱,例如setTimeout / setInterval
  • Clear asynchronous actions when you call an XHR request using fetch or libraries like axios

    當您使用fetch或axios庫調用XHR請求時,清除異步操作

  • Alternate methods, some opinionated others deprecated.

    替代方法,有些人認為不贊成使用。

Before I start, a huge shout out to Kent C Dodds, the coolest person on the internet right now. Thank you for taking the time & giving back to the community. His Youtube podcasts and egghead course on Advanced React Component Patterns are amazing. Check these resources out if you want to take the next step in your React skills.

在我開始之前,向互聯網上最酷的人Kent C Dodds大喊大叫。 感謝您抽出寶貴時間并回饋社區。 他的YouTube 播客 高級React組件模式的 Egghead課程很棒。 如果您想進一步提高React技能,請查閱這些資源。

I asked Kent about a better approach to avoid setState on component unmount so I could better optimize React’s performance. He went above and beyond and made a video on it. If you are a video kind of person, check it out below. It’ll give you a step by step walk through with a detailed explanation.

我問肯特一個更好的方法來避免setState在組件卸載上,這樣我可以更好地優化React的性能。 他越過了上面,并制作了一個視頻。 如果您是視頻類人,請在下面查看。 它會逐步為您提供詳細的說明。

So now let’s jump in get started.

現在讓我們開始吧。

1:清除訂閱 (1: Clear Subscriptions)

Let’s start off with the example:

讓我們從示例開始:

Let’s talk what just happened here. What I want you to focus on is the counter.js file which basically increments the counter after 3 seconds.

讓我們說說這里發生了什么。 我想讓您關注的是counter.js文件,該文件基本上會在3秒后增加計數器的數量。

This gives an error in 5 seconds, because I unmounted a subscription without clearing it. If you want to see the error again, just hit the refresh button in the CodeSandbox editor to see the error in the console.

這會在5秒鐘內出現錯誤,因為我卸載了訂閱但未清除它。 如果您想再次看到該錯誤,只需在CodeSandbox編輯器中單擊“刷新”按鈕即可在控制臺中查看該錯誤。

I have my container file index.js which simply toggle’s the counter component after the first five seconds.

我有我的容器文件index.js ,它在前五秒后簡單地切換了計數器組件。

So

所以

— — — →Index.js— — —→Index.js — — — — → Counter.js— — — —→Counter.js

In my Index.js, I call Counter.js and simply do this in my render:

在我的Index.js中,我調用Counter.js并簡單地在渲染器中執行此操作:

{showCounter ? <Counter /> : null}

The showCounter is a state boolean which set’s itself to false after the first 5 seconds as soon as the component mounts (componentDidMount).

showCounter是一個狀態布爾值,一旦安裝了組件(componentDidMount),就會在最初的5秒內將自身設置為false。

The real thing which illustrates our problem here is the counter.js file which increments the count after every 3 seconds. So after the first 3 seconds, the counter updates. But as soon as it gets to the second update, which happens at the 6th second, the index.js file has already unmounted the counter component at the 5th second. By the time the counter component reaches it’s 6th second, it updates the counter for the second time.

在這里說明我們問題的真正問題是counter.js文件,該文件每3秒增加一次計數。 因此,在開始的3秒后,計數器會更新。 但是,一旦它到達第二次更新,即發生在第六次 其次, index.js文件已經在第5個卸載了計數器組件 第二。 當計數器部分達到第六位時 第二,它第二次更新計數器。

It updates its state, but then here is the problem. There is no DOM for the counter component to update the state to, and that is when React throws an error. That beautiful error we discussed above:

它更新其狀態,但是這就是問題所在。 計數器組件沒有將狀態更新為的DOM,也就是React引發錯誤時。 我們上面討論的那個美麗的錯誤:

Warning: Can’t call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.警告:無法在已卸載的組件上調用setState(或forceUpdate)。 這是空操作,但它表明應用程序中發生內存泄漏。 要解決此問題,請在componentWillUnmount方法中取消所有訂閱和異步任務。

Now if you are new to React, you might say, “well Adeel … yeah but didn’t we just unmount the Counter component at the 5th second? If there is no component for counter, how can it’s state still update at the sixth second?”

現在,如果您是React的新手,您可能會說:“好吧Adeel…是的,但是我們不是只是在第5秒鐘卸下了Counter組件嗎? 如果沒有計數器的組件,它的狀態如何仍可以在第六秒更新?”

Yes, you are right. But when we do something like setTimeout or setInterval in our React components, it is not dependent on or linked with our React class like you think it may be. It will keep on running after its specified condition unless or until you cancel it’s subscription.

是的,你是對的。 但是,當我們在React組件中執行諸如setTimeout或setInterval時,它并不像您認為的那樣依賴或鏈接到我們的React類。 它會在指定條件后繼續運行,除非或直到您取消訂閱為止。

Now you might already be doing this when your condition is met. But what if your condition hasn’t been met yet and the user decides to change pages where this action is still happening?

現在,當滿足您的條件時,您可能已經在執行此操作。 但是,如果尚未滿足您的條件并且用戶決定更改仍在執行此操作的頁面該怎么辦?

The best way to clear these kinds of subscriptions is in your componentWillUnmount life cycle. Here is an example how you can do it. Check out the counter.js file’s componentWillUnmount method:

清除此類訂閱的最佳方法是在componentWillUnmount生命周期中。 這是一個示例,您可以如何做。 查看counter.js文件的componentWillUnmount方法:

And that is pretty much it for setTimout & setInterval.

而對于setTimout和setInterval來說, setTimout 。

2:API(XHR)中止 (2: API (XHR) Aborts)

  • The Ugly Old Approach (Deprecated)

    丑陋的舊方法(不建議使用)
  • The Good Newer Approach (The main purpose for this article)

    較新的方法(本文的主要目的)

So, we’ve discussed subscriptions. But what if you make an asynchronous request? How do you cancel it?

因此,我們已經討論了訂閱。 但是,如果您發出異步請求怎么辦? 您如何取消呢?

舊的方式 (The old way)

Before I talk about that, I want to talk about a deprecated method in React called isMounted()

在談論這個之前,我想談談React中一個不贊成使用的方法isMounted()

Before December 2015, there was a method called isMounted in React. You can read more about it in the React blog. What it did was something like this:

在2015年12月之前,React中有一種名為isMounted的方法。 您可以在React 博客中有關它的信息 它所做的是這樣的:

import React from 'react' import ReactDOM from 'react-dom' import axios from 'axios'class RandomUser extends React.Component {state = {user: null}_isMounted = falsehandleButtonClick = async () => {const response = await axios.get('https://randomuser.me/api/')if (this._isMounted) {this.setState({ user: response.data })}}componentDidMount() {this._isMounted = true}componentWillUnmount() {this._isMounted = false}render() {return (<div><button onClick={this.handleButtonClick}>Click Me</button><pre>{JSON.stringify(this.state.user, null, 2)}</pre></div>)} }

For the purpose of this example, I am using a library called axios for making an XHR request.

出于本示例的目的,我使用一個名為axios的庫來發出XHR請求。

Let’s go through it. I initially set this_isMounted to false right next to where I initialized my state. As soon as the life cycle componentDidMount gets called, I set this._isMounted to true. During that time, if an end user clicks the button, an XHR request is made. I am using randomuser.me. As soon as the promise gets resolved, I check if the component is still mounted with this_isMounted. If it’s true, I update my state, otherwise I ignore it.

讓我們經歷一下。 最初,我在初始化狀態的位置旁邊將this_isMounted設置為false 。 一旦生命周期componentDidMount被調用,我將this._isMounted設置為true。 在此期間,如果最終用戶單擊該按鈕,則會發出XHR請求。 我正在使用randomuser.me 。 諾言得到解決后,我將檢查組件是否仍通過this_isMounted安裝。 如果為真,則更新狀態,否則忽略它。

The user might clicked on the button while the asynchronous call was being resolved. This would result in the user switching pages. So to avoid an unnecessary state update, we can simply handle it in our life cycle method componentWillUnmount. I simply set this._isMounted to false. So whenever the asynchronous API call gets resolved, it will check if this_isMounted is false and then it will not update the state.

解決異步調用時,用戶可能單擊了按鈕。 這將導致用戶切換頁面。 因此,為避免不必要的狀態更新,我們可以在生命周期方法componentWillUnmount 。 我只是將this._isMounted設置為false。 因此,每當異步API調用得到解決時,它將檢查this_isMounted是否為false,然后將不更新狀態。

This approach does get the job done, but as the React docs say:

這種方法確實可以完成工作,但是正如React文檔所說:

The primary use case for isMounted() is to avoid calling setState() after a component has unmounted, because calling setState() after a component has unmounted will emit a warning. The “setState warning” exists to help you catch bugs, because calling setState() on an unmounted component is an indication that your app/component has somehow failed to clean up properly. Specifically, calling setState() in an unmounted component means that your app is still holding a reference to the component after the component has been unmounted - which often indicates a memory leak! Read More …

isMounted()的主要用例是避免在組件卸載后調用setState() ,因為在組件卸載后調用setState()會發出警告。 存在“ setState警告”可幫助您捕獲錯誤,因為在已卸載的組件上調用setState()表示您的應用程序/組件無法正確清理。 具體來說,在已卸載的組件中調用setState()意味著您的應用在卸載該組件后仍會保留對該組件的引用-這通常表示內存泄漏! …

This means that although we have avoided an unnecessary setState, the memory still hasn’t cleared up. There is still an asynchronous action happening which doesn’t know that the component life cycle has ended and it is not needed anymore.

這意味著盡管我們避免了不必要的setState,但內存仍未清除。 仍然有一個異步操作正在發生,它不知道組件的生命周期已經結束并且不再需要它。

讓我們談談正確的方法 (Let’s Talk About The Right Way)

Here to save the day are AbortControllers. As per the MDN documentation it states:

為了節省一天,這里是AbortControllers 。 根據MDN文檔,它指出:

The AbortController interface represents a controller object that allows you to abort one or more DOM requests as and when desired. Read more ..

AbortController接口表示一個控制器對象,允許您根據需要中止一個或多個DOM請求。 ..

Let’s look a bit more in depth here. With code, of course, because everyone ? code.

讓我們在這里更深入一些。 有了代碼,當然是因為每個人都有?代碼。

var myController = new AbortController(); var mySignal = myController.signal;var downloadBtn = document.querySelector('.download'); var abortBtn = document.querySelector('.abort');downloadBtn.addEventListener('click', fetchVideo);abortBtn.addEventListener('click', function() {myController.abort();console.log('Download aborted'); });function fetchVideo() {...fetch(url, { signal: mySignal }).then(function(response) {...}).catch(function(e) {reports.textContent = 'Download error: ' + e.message;}) }

First we create a new AbortController and assign it to a variable called myController. Then we make a signal for that AbortController. Think of the signal as an indicator to tell our XHR requests when it’s time to abort the request.

首先,我們創建一個新的AbortController并將其分配給名為myController的變量。 然后,我們為該AbortController發出信號 。 可以將信號視為指示符,以告知我們何時終止請求。

Assume that we have 2 buttons, Download and Abort . The download button downloads a video, but what if, while downloading, we want to cancel that download request? We simply need to call myController.abort(). Now this controller will abort all requests associated with it.

假設我們有兩個按鈕, Download和Abort 。 下載按鈕會下載視頻,但是如果我們在下載時要取消該下載請求怎么辦? 我們只需要調用myController.abort() 。 現在,此控制器將中止與其關聯的所有請求。

How, you might ask?

您可能會問如何?

After we did var myController = new AbortController() we did this var mySignal = myController.signal . Now in my fetch request, where I tell it the URL and the payload, I just need to pass in mySignal to link/signal that FETCh request with my awesome AbortController.

完成var myController = new AbortController()我們執行了var mySignal = myController.signal 。 現在在我的獲取請求中,在其中告訴我URL和有效負載的地方,我只需要傳入mySignal即可將FETCh請求與我的真棒AbortController鏈接/信號AbortController 。

If you want to read an even more extensive example about AbortController, the cool folks at MDN have this really nice and elegant example on their Github. You can check it out here.

如果您想閱讀有關AbortController更廣泛的示例,則MDN上的好人在其Github上都有這個非常漂亮而優雅的示例。 您可以在這里查看 。

I wanted to talk about these abort requests was because not many people are aware of them. The request for an abort in fetch started in 2015. Here’s the Original GitHub Issue On Abort — it finally got support around October 2017. That is a gap of two years. Wow! There are a few libraries like axios that give support for AbortController. I will discuss how you can use it with axios, but I first wanted to show the in-depth under-the-hood version of how AbortController works.

我想談談這些中止請求是因為沒有多少人知道它們。 要求中止獲取的請求始于2015 年 。這是Abort上的原始GitHub問題 -它終于在2017年10月左右獲得了支持。這是兩年的時間。 哇! 有一些像axios這樣的庫提供對AbortController的支持。 我將討論如何在axios上使用它,但是我首先想展示AbortController的工作原理的深入的幕后版本。

在Axios中終止XHR請求 (Aborting An XHR Request In Axios)

“Do, or do not. There is no try.” — Yoda“做還是不做。 沒有嘗試。” - 尤達

The implementation I talked about above isn’t specific to React, but that’s what we’ll discuss here. The main purpose of this article is to show you how to clear unnecessary DOM manipulations in React when an XHR request is made and the component is unmounted while the request is in pending state. Whew!

我上面討論的實現并非特定于React,但這就是我們將在此處討論的實現。 本文的主要目的是向您展示如何在發出XHR請求并在請求處于掛起狀態時卸載組件時在React中清除不必要的DOM操作。 ew!

So without further ado, here we go.

因此,事不宜遲,我們開始吧。

import React, { Component } from 'react'; import axios from 'axios';class Example extends Component {signal = axios.CancelToken.source();state = {isLoading: false,user: {},}componentDidMount() {this.onLoadUser();}componentWillUnmount() {this.signal.cancel('Api is being canceled');}onLoadUser = async () => {try {this.setState({ isLoading: true });const response = await axios.get('https://randomuser.me/api/', {cancelToken: this.signal.token,})this.setState({ user: response.data, isLoading: true });} catch (err) {if (axios.isCancel(err)) {console.log('Error: ', err.message); // => prints: Api is being canceled} else {this.setState({ isLoading: false });}}} render() {return (<div><pre>{JSON.stringify(this.state.user, null, 2)}</pre></div>)}}

Let’s walk through this code

讓我們看一下這段代碼

I set this.signal to axios.CancelToken.source()which basically instantiates a new AbortController and assigns the signal of that AbortController to this.signal. Next I call a method in componentDidMount called this.onLoadUser() which calls a random user information from a third party API randomuser.me. When I call that API, I also pass the signal to a property in axios called cancelToken

我設置this.signal到axios.CancelToken.source()基本上實例化新的AbortController和受讓人的,該信號AbortController到this.signal 。 接下來,我在componentDidMount調用一個名為this.onLoadUser()的方法,該方法從第三方API randomuser.me調用隨機用戶信息。 當我調用該API時,我還將信號傳遞給axios中名為cancelToken的屬性

The next thing I do is in my componentWillUnmount where I call the abort method which is linked to that signal. Now let’s assume that as soon as the component was loaded, the API was called and the XHR request went in a pending state.

接下來,我要做的是在我的componentWillUnmount中調用與該signal鏈接的中止方法。 現在,我們假設一旦加載了組件,就會調用API,并且XHR request went in a pending state 。

Now, the request was pending (that is, it wasn’t resolved or rejected but the user decided to go to another page. As soon as the life cycle method componentWillUnmount gets called up, we will abort our API request. As soon as the API get’s aborted/cancelled, the promise will get rejected and it will land in the catch block of that try/catch statement, particularly in the if (axios.isCancel(err) {} block.

現在,該請求處于待處理狀態(也就是說,該請求尚未解決或拒絕,但用戶決定轉到另一個頁面。生命周期方法componentWillUnmount被調用時,我們將中止我們的API請求。 API get被中止/取消,承諾將被拒絕,它將if (axios.isCancel(err) {}該try/catch語句的catch塊中,特別是在if (axios.isCancel(err) {}塊中。

Now we know explicitly that the API was aborted, because the component was unmounted and therefore logs an error. But we know that we no longer need to update that state since it is no longer required.

現在,我們明確知道該API已中止,因為該組件已卸載,因此記錄了一個錯誤。 但是我們知道我們不再需要更新該狀態,因為它不再需要。

P.S: You can use the same signal and pass it as many XHR requests in your component as you like. When the component gets un mounted, all those XHR requests that are in a pending state will get cancelled when componentWillUnmount is called.

PS:您可以使用相同的信號,并根據需要在組件中傳遞盡可能多的XHR請求。 卸載組件時,調用componentWillUnmount時,所有處于掛起狀態的XHR請求都將被取消。

最終細節 (Final details)

Congratulations! :) If you have read this far, you’ve just learned how to abort an XHR request on your own terms.

恭喜你! :)如果您已經閱讀了本文,那么您已經學會了如何根據自己的條件中止XHR請求。

Let’s carry on just a little bit more. Normally, your XHR requests are in one file, and your main container component is in another (from which you call that API method). How do you pass that signal to another file and still get that XHR request cancelled?

讓我們繼續多一點。 通常,您的XHR請求位于一個文件中,而主容器組件位于另一個文件中(從中調用該API方法)。 您如何將該信號傳遞到另一個文件并仍然取消該XHR請求?

Here is how you do it:

這是您的操作方式:

import React, { Component } from 'react'; import axios from 'axios';// API import { onLoadUser } from './UserAPI';class Example extends Component {signal = axios.CancelToken.source();state = {isLoading: false,user: {},}componentDidMount() {this.onLoadUser();}componentWillUnmount() {this.signal.cancel('Api is being canceled');}onLoadUser = async () => {try {this.setState({ isLoading: true });const data = await onLoadUser(this.signal.token);this.setState({ user: data, isLoading: true });} catch (error) {if (axios.isCancel(err)) {console.log('Error: ', err.message); // => prints: Api is being canceled} else {this.setState({ isLoading: false });}}}render() {return (<div><pre>{JSON.stringify(this.state.user, null, 2)}</pre></div>)}};} export const onLoadUser = async myCancelToken => {try {const { data } = await axios.get('https://randomuser.me/api/', {cancelToken: myCancelToken,})return data;} catch (error) {throw error;} };

I hope this has helped you and I hope you’ve learned something. If you liked it, please give it some claps.

希望這對您有所幫助,也希望您學到了一些東西。 如果您喜歡它,請給它鼓掌。

Thank you for taking the time out to read. Shout out to my very talented colleague Kinan for helping me proof read this article. Thanks to Kent C Dodds for being an inspiration in the JavaScript OSS community.

感謝您抽出寶貴的時間閱讀。 向我很有才華的同事Kinan大喊大叫,以幫助我證明閱讀本文的內容。 感謝Kent C Dodds在JavaScript OSS社區中的啟發。

Again, I’d love to hear your feedback on it. You can always reach me out on Twitter.

同樣,我很想聽聽您對此的反饋。 您可以隨時在Twitter上與我聯系

Also there is another amazing read on Abort Controller that I found through the MDN documentation by Jake Archibald. I suggest you read it, if you have a curios nature like mine.

另外,我在Jake ArchibaldMDN文檔中找到了有關Abort Controller的 另一個驚人的讀物 。 如果您像我一樣有好奇心,我建議您閱讀。

翻譯自: https://www.freecodecamp.org/news/how-to-work-with-react-the-right-way-to-avoid-some-common-pitfalls-fc9eb5e34d9e/

react避免子組件渲染

總結

以上是生活随笔為你收集整理的react避免子组件渲染_如何与React一起使用正确的方法来避免一些常见的陷阱的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。