@manh11235 Wow, lần vào Container thì đúng như bạn nói, nó tạo RenderObject nhờ tạo thằng ColoredBox là một RenderObjectElement trong hàm build. Thảo nào, doc nó nói ComponentElement tạo gián tiếp RenderObject thông qua thằng Element khác, thằng ColoredBox là thằng gọi hàm createRenderObject, ko phải thằng Container Element gọi.
@nguyen.thanh.minhb mình cũng từng đọc qua https://medium.com/flutter-community/flutter-what-are-widgets-renderobjects-and-elements-630a57d05208 , nhưng vì k hiểu được rõ ràng có lẽ do trình tiếng anh toàn phải google nên mình chủ yếu lần theo các class đi theo nó (commander + B ).
ví dụ : Container -> StatelessWidget -> tạo ra StatelessElement , kế thừa từ ComponentElement -> Vào ComponentElement thì hàm mount không gọi widget.createRenderObject.
Ngược lại: Column -> Flex, kế thừa từ MultiChildRenderObjectWidget -> taọ ra MultiChildRenderObjectElement , kế thừa từ RenderObjectElement. hàm mount gọi widget.createRenderObject.
Nhưng ComponentElement không gọi widget.createRenderObject thì để tạo nên RenderObject tree thì làm thế nào. Cái này lần theo hàm build của Container. Nó trả về ví dụ như ColoredBox -> kế thừa từ RenderObjectWidget-> tạo ra RenderObjectElement . Suy ra ComponentElement cũng được tạo ra từ RenderObjectElement
Mấy chỗ như DecoratedBox, Padding, Column bị lộn thì phải. 3 widget này kế thừa từ RenderObjectWidget, RenderObjectWidget tạo ra RenderObjectElement. Còn StatelessElement , StatefulElement hay InheritedElement là ComponentElement.
Có thể chia thành 2 loại element chính là
1.ComponentElement ( StatelessElement , StatefulElement hay InheritedElement ). Loại này không tạo RenderObject
2.RenderObjectElement (Center, Padding,...).Loại này mới tạo RenderObject bằng cách gọi widget.createRenderObject trong hàm mount
voidmain(){final userA =User(1,'Minh');final userB =User(1,'Minh');// Toán tử == sẽ so sánh giá trịprint(userA == userB);// true vì cả userA và userB đều có id là 1 và name là Minh// hàm identical sẽ kiểm tra xem userA với userB có cùng trỏ đến 1 object khôngprint(identical(userA, userB));// false vì userA và userB không cùng trỏ đến 1 objectfinal userC = userA;// userC trỏ đến cùng object với userAprint(identical(userA, userC));// nên hàm identical print ra true}classUser{User(this.id,this.name);final int id;finalString name;// ta sẽ override lại toán tử ==// tức là ta định nghĩa lại "Khi nào thì 2 User gọi là == nhau)@override
bool operator==(Object other){// Khi 2 user có cùng id và cùng name thì chúng bằng nhaureturn id ==(other asUser).id && name ==(other asUser).name;}}
Câu 2: tại sao ngay từ đầu trong cái source này https://dartpad.dev/cbcf93cfd5655c514af90e87010be7c3 , mình sử dụng UniqueKey nhưng State của widget Tile vẫn được giữ như cũ. Bằng chứng là màu sắc của Tile nó nó không bị reset thành 1 màu mới. Còn trong ví dụ lần này thì một State mới của TextField đã được tạo ra.
Trả lời: Đầu tiên first build, thì object _MyHomePageState được khởi tạo lần đầu và biến listTile trong _MyHomePageState cũng được khởi tạo lần đầu. Giả sử lúc khởi tạo lần đầu này object Tile thứ nhất trong listTile có màu đen và key là 1, object Tile thứ 2 có màu đỏ và key là 2. Khi gọi hàm setState, nó chỉ đơn giản là swap 2 object Tile này chứ không tạo ra một object Tile mới thay thế. Hơn nữa _MyHomePageState cũng không có bị destroy nên biến listTile cũng không được tạo mới. Tóm lại, 2 object Tile được tạo từ đầu, không bao giờ bị destroy nên tất nhiên Key nó sẽ không thay đổi. Trước key là 1, sau khi rebuild vẫn là 1, trước là 2 thì sau vẫn là 2, vì vậy Element so sánh thấy Key trước và sau đều không đổi nên State được bảo toàn. Còn trong ví dụ 2 TextField thì khi rebuild 1 object TextField mới được tạo ra, nên trước đó key có thể là 1, sau khi rebuild key có thể là 3. Vì vậy, trước và sau khi rebuild, Element so sánh thấy key khác nhau nên destroy chính nó, tạo ra Element mới và State mới.
Mount: cái từ này là cái từ a thấy là 1 trong những từ rất khó dịch ra tiếng Việt 1 cách chính xác, nghĩa của nó theo a thường hiểu là "gắn kết". Thời điểm component được mount vào DOM tức là thời điểm đó nó bắt đầu được "gắn" vào DOM - vào màn hình và ta bắt đầu nhìn thấy nội dung. Và mount chỉ xảy ra 1 lần trong life cycle của component
Render là 1 lần "tô, vẽ": cứ 1 lần e thấy nội dung ở trên màn hình thì coi như 1 lần render, mỗi lần e bấm button làm thay đổi data của Vue -> thấy nội dung trên màn hình thay đổi -> gọi là 1 lần Re-render (vẽ lại)
Sau khi mount hoàn tất thì nội dung được render lần đầu tiên, sau đó data thay đổi thì là re-render
các hook trong phần mounting sẽ chạy trước và sau khi "vue instance được gán (mounted) vào DOM"
"Vue instance được gán vào DOM" nghe hơi mập mờ . Dựa theo ngữ nghiã tên của các hook và chức năng của nó thì mounting theo a là trước hook mounted
Câu 1: nếu trong ví dụ lần này, mình không sử dụng ValueKey, cũng không sử dụng UniqueKey, nói chung là không sử dụng Key thì kết quả sẽ như thế nào, Element TextField có bị deactivate hay bị dispose không?
Trả lời: không bị deactivate cũng không bị dispose. Vì ko có key thì Element chỉ so sánh Widget Type và nó thấy cùng type là TextField nên nó chỉ cập nhật cho biến widget trỏ đến Widget mới, Element và State vẫn được bảo toàn.
Đoạn lý giải của bạn khi chạy docker-compose bằng user james và khi ls-l trong container lại ra user node, thì mình nghĩ là user trong container và môi trường ngoài (tạm gọi là host) có liên hệ với nhau thông qua uid rồi. Do vậy mình nghĩ vấn đề permission ở đây là làm sao cho uid của container = uid host.
Vậy cách 1 của bạn có thể làm đơn giản hơn. Vẫn chạy docker-compose = user james, ko cần add user mytestuser, set group root, rồi switch qua để chạy docker-compose, vì như vậy là quá nhiều quyền cho 1 user mới. Bằng cách chạy lệnh sau:
james trong host có uid=1000
node trong container có uid=1000
Nếu james có uid=1001 và node vẫn là default 1000 thì không thể chạy app bằng node được mà phải tạo appuser với uid=1001.
Để an toàn thì mỗi 1 service độc lập ta tạo ở host 1 user mới, uid mới unique và tạo tương tự trong Dockerfile cho container, set uid này cho các service khác như redis, db ở docker-compose.yml.
Hello anh, lại là một bài viết quá hay về Vue. Nhưng anh cho em thắc mắc một chút là khi em đọc một số bài viết cùng chủ đề thì họ lại giải thích rằng các hook trong phần mounting sẽ chạy trước và sau khi "vue instance được gán (mounted) vào DOM". Trong bài viết thì e lại thấy anh dùng từ "render" vậy cho em hỏi 2 khái niệm trên có liên quan không ạ, hay chúng đều như nhau? Em cảm ơn.
volumes: theo mình hiểu thì dùng thông số này để mount thư mục code trên máy tính với thư mục chỉ định trên container. Khi mình sửa code thì nó cũng sẽ được phản ánh lên container và nếu reload lại giao diện thì mình có thể thấy những thay đổi đó.
THẢO LUẬN
@thienhavodinh Hình đó mình lấy từ nguồn này, tác giả thuộc Flutter team nên cũng là một nguồn đáng tin chứ nhỉ. Hy vọng bác cho ý kiến để mọi người cùng học hỏi. https://medium.com/flutter/how-to-create-stateless-widgets-6f33931d859
@manh11235 Wow, lần vào Container thì đúng như bạn nói, nó tạo RenderObject nhờ tạo thằng ColoredBox là một RenderObjectElement trong hàm build. Thảo nào, doc nó nói ComponentElement tạo gián tiếp RenderObject thông qua thằng Element khác, thằng ColoredBox là thằng gọi hàm createRenderObject, ko phải thằng Container Element gọi.
@nguyen.thanh.minhb mình cũng từng đọc qua https://medium.com/flutter-community/flutter-what-are-widgets-renderobjects-and-elements-630a57d05208 , nhưng vì k hiểu được rõ ràng có lẽ do trình tiếng anh toàn phải google nên mình chủ yếu lần theo các class đi theo nó (commander + B ). ví dụ : Container -> StatelessWidget -> tạo ra StatelessElement , kế thừa từ ComponentElement -> Vào ComponentElement thì hàm mount không gọi widget.createRenderObject. Ngược lại: Column -> Flex, kế thừa từ MultiChildRenderObjectWidget -> taọ ra MultiChildRenderObjectElement , kế thừa từ RenderObjectElement. hàm mount gọi widget.createRenderObject.
Nhưng ComponentElement không gọi widget.createRenderObject thì để tạo nên RenderObject tree thì làm thế nào. Cái này lần theo hàm build của Container. Nó trả về ví dụ như ColoredBox -> kế thừa từ RenderObjectWidget-> tạo ra RenderObjectElement . Suy ra ComponentElement cũng được tạo ra từ RenderObjectElement
Hình đó mình lấy từ nguồn này, tác giả thuộc Flutter team. https://medium.com/flutter/how-to-create-stateless-widgets-6f33931d859
Còn 2 loại Element thì đúng như bạn nói, một thằng tạo trực tiếp, một thằng tạo gián tiếp. Để mình update. Cảm ơn bạn nhé
Bạn có nguồn nào nói về cái này không. Mình tìm hiểu từ sách, nó không nói kĩ đến mức này. Cảm ơn bạn
Mấy chỗ như DecoratedBox, Padding, Column bị lộn thì phải. 3 widget này kế thừa từ RenderObjectWidget, RenderObjectWidget tạo ra RenderObjectElement. Còn StatelessElement , StatefulElement hay InheritedElement là ComponentElement. Có thể chia thành 2 loại element chính là 1.ComponentElement ( StatelessElement , StatefulElement hay InheritedElement ). Loại này không tạo RenderObject 2.RenderObjectElement (Center, Padding,...).Loại này mới tạo RenderObject bằng cách gọi widget.createRenderObject trong hàm mount
Sự khác nhau giữa so sánh == và identical
Câu này có bị ngược k tác giả ơi "Để tạo chữ ký, người gửi sẽ dùng private key và người nhận sẽ dùng public key để xác thực chữ ký đó" ?
good
@maitrungduc1410 đã thông não thành công, cảm ơn chia sẻ của a.
Bài viết rất cụ thể. Bạn Anh cho hỏi code viết labelText như thế nào? Trong bài ko thấy. Tks
Câu 2: tại sao ngay từ đầu trong cái source này https://dartpad.dev/cbcf93cfd5655c514af90e87010be7c3 , mình sử dụng UniqueKey nhưng State của widget Tile vẫn được giữ như cũ. Bằng chứng là màu sắc của Tile nó nó không bị reset thành 1 màu mới. Còn trong ví dụ lần này thì một State mới của TextField đã được tạo ra.
Trả lời: Đầu tiên first build, thì object
_MyHomePageStateđược khởi tạo lần đầu và biếnlistTiletrong_MyHomePageStatecũng được khởi tạo lần đầu. Giả sử lúc khởi tạo lần đầu này objectTilethứ nhất tronglistTilecó màu đen và key là 1, objectTilethứ 2 có màu đỏ và key là 2. Khi gọi hàmsetState, nó chỉ đơn giản là swap 2 objectTilenày chứ không tạo ra một objectTilemới thay thế. Hơn nữa_MyHomePageStatecũng không có bị destroy nên biếnlistTilecũng không được tạo mới. Tóm lại, 2 objectTileđược tạo từ đầu, không bao giờ bị destroy nên tất nhiên Key nó sẽ không thay đổi. Trước key là 1, sau khi rebuild vẫn là 1, trước là 2 thì sau vẫn là 2, vì vậy Element so sánh thấy Key trước và sau đều không đổi nên State được bảo toàn. Còn trong ví dụ 2 TextField thì khi rebuild 1 object TextField mới được tạo ra, nên trước đó key có thể là 1, sau khi rebuild key có thể là 3. Vì vậy, trước và sau khi rebuild, Element so sánh thấy key khác nhau nên destroy chính nó, tạo ra Element mới và State mới.mountchỉ xảy ra 1 lần trong life cycle của componentdatacủa Vue -> thấy nội dung trên màn hình thay đổi -> gọi là 1 lần Re-render (vẽ lại)mounthoàn tất thì nội dung đượcrenderlần đầu tiên, sau đódatathay đổi thì làre-render"Vue instance được gán vào DOM" nghe hơi mập mờ
. Dựa theo ngữ nghiã tên của các hook và chức năng của nó thì
mountingtheo a là trước hookmountedCâu 1: nếu trong ví dụ lần này, mình không sử dụng ValueKey, cũng không sử dụng UniqueKey, nói chung là không sử dụng Key thì kết quả sẽ như thế nào, Element TextField có bị deactivate hay bị dispose không?
Trả lời: không bị deactivate cũng không bị dispose. Vì ko có key thì Element chỉ so sánh Widget Type và nó thấy cùng type là TextField nên nó chỉ cập nhật cho biến widget trỏ đến Widget mới, Element và State vẫn được bảo toàn.
@maitrungduc1410 sau khi đọc bài non-root thì mình đã làm được và có comment cách hiểu của mình ở đây: https://viblo.asia/c/3m5WweA4w5O
Đoạn lý giải của bạn khi chạy docker-compose bằng user james và khi ls-l trong container lại ra user node, thì mình nghĩ là user trong container và môi trường ngoài (tạm gọi là host) có liên hệ với nhau thông qua uid rồi. Do vậy mình nghĩ vấn đề permission ở đây là làm sao cho uid của container = uid host.
Vậy cách 1 của bạn có thể làm đơn giản hơn. Vẫn chạy docker-compose = user james, ko cần add user mytestuser, set group root, rồi switch qua để chạy docker-compose, vì như vậy là quá nhiều quyền cho 1 user mới. Bằng cách chạy lệnh sau:
sudo adduser -u 1410 mytestuser sudo chown -R mytestuser:mytestuser .
Cách 2 của bạn làm được là do:
james trong host có uid=1000 node trong container có uid=1000
Nếu james có uid=1001 và node vẫn là default 1000 thì không thể chạy app bằng node được mà phải tạo appuser với uid=1001.
Để an toàn thì mỗi 1 service độc lập ta tạo ở host 1 user mới, uid mới unique và tạo tương tự trong Dockerfile cho container, set uid này cho các service khác như redis, db ở docker-compose.yml.
Hello anh, lại là một bài viết quá hay về Vue. Nhưng anh cho em thắc mắc một chút là khi em đọc một số bài viết cùng chủ đề thì họ lại giải thích rằng các hook trong phần mounting sẽ chạy trước và sau khi "vue instance được gán (mounted) vào DOM". Trong bài viết thì e lại thấy anh dùng từ "render" vậy cho em hỏi 2 khái niệm trên có liên quan không ạ, hay chúng đều như nhau? Em cảm ơn.
volumes: theo mình hiểu thì dùng thông số này để mount thư mục code trên máy tính với thư mục chỉ định trên container. Khi mình sửa code thì nó cũng sẽ được phản ánh lên container và nếu reload lại giao diện thì mình có thể thấy những thay đổi đó.
Bài viết rất hữu ích! Cảm ơn nhiều!
Python đã có toán tử 3 ngôi rồi à !?