Đã bao giờ bạn muốn tự spam với các thông báo phiền nhiễu và đầy xâm lấn chưa? Hoặc nhắc nhở bản thân rằng kế hoạch làm việc của chúng ta ngày hôm nay sắp đến mỗi khi lưới web chứ?
Chà chà, không đâu xa.
Chrome Extension có thể thực hiện tất cả những điều này, và phần hay nhất là bạn không thể thoát ứng dụng bằng cách reload lại trang. Thêm vào đó, không phải ai cũng biết cách tắt những thứ phiền phức này.
Trong bài viết này, chúng ta sẽ thử tạo một ứng dụng Todo và triển khai nó dưới Local như một ứng dụng Chrome extension với React.
Thực sự việc này là không cần thiết, nhưng hãy thử crazy lên một chút nó đánh thức sáng tạo chút nhé.
Điều kiện cần:
- Kiến thức nhập môn về React
- Một chút thông thạo về custom React Hooks
Clone the Repo:
Clone: https://gitlab.com/sk3pt1cc/react-starter
Run cmd npm install
& npm run start
Mở localhost:3000
để thử chạy thử ứng dụng.
Tạo Todo App
Bao gồm 3 phần là: App Component, Component form để tạo mới Todos, và useTodos
Hook. Chúng ta sẽ đi vào chi tiết từng phần
<CreateTodoForm />
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token keyword">import</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">CreateTodoForm</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> createNewTodo <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>label<span class="token punctuation">,</span> setLabel<span class="token punctuation">]</span> <span class="token operator">=</span> React<span class="token punctuation">.</span><span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">"create-todo-form"</span><span class="token operator">></span> <span class="token operator"><</span>input placeholder<span class="token operator">=</span><span class="token string">"Enter todo label"</span> onChange<span class="token operator">=</span><span class="token punctuation">{</span>e <span class="token operator">=></span> <span class="token function">setLabel</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">}</span> value<span class="token operator">=</span><span class="token punctuation">{</span>label<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>button onClick<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">createNewTodo</span><span class="token punctuation">(</span>label<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token operator">></span> Save <span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> CreateTodoForm<span class="token punctuation">;</span> |
Một form đơn giản để nhập Label và button để lưu thông tin. Khi nó nhận được một function createNewTodo
prop.
useTodos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <span class="token keyword">import</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> uuid <span class="token keyword">from</span> <span class="token string">"uuid"</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">getTodos</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> todos <span class="token operator">=</span> window<span class="token punctuation">.</span>localStorage<span class="token punctuation">.</span><span class="token function">getItem</span><span class="token punctuation">(</span><span class="token string">'todos'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>todos<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>todos<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> <span class="token function-variable function">useTodos</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>todos<span class="token punctuation">,</span> setTodos<span class="token punctuation">]</span> <span class="token operator">=</span> React<span class="token punctuation">.</span><span class="token function">useState</span><span class="token punctuation">(</span><span class="token function">getTodos</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">updateTodos</span> <span class="token operator">=</span> <span class="token punctuation">(</span>newTodos<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> stringifiedTodos <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>newTodos<span class="token punctuation">)</span><span class="token punctuation">;</span> window<span class="token punctuation">.</span>localStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'todos'</span><span class="token punctuation">,</span> stringifiedTodos<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setTodos</span><span class="token punctuation">(</span>newTodos<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">setTodoCompleted</span> <span class="token operator">=</span> <span class="token punctuation">(</span>id<span class="token punctuation">,</span> completed<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> todoIds <span class="token operator">=</span> todos<span class="token punctuation">.</span><span class="token function">findIndex</span><span class="token punctuation">(</span>todo <span class="token operator">=></span> todo<span class="token punctuation">.</span>id <span class="token operator">==</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> newTodos <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span>todos<span class="token punctuation">]</span><span class="token punctuation">;</span> newTodos<span class="token punctuation">[</span>todoIds<span class="token punctuation">]</span><span class="token punctuation">.</span>completed <span class="token operator">=</span> completed<span class="token punctuation">;</span> <span class="token function">updateTodos</span><span class="token punctuation">(</span>newTodos<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">addTodo</span> <span class="token operator">=</span> <span class="token punctuation">(</span>label<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> newTodos <span class="token operator">=</span> <span class="token punctuation">{</span> label<span class="token punctuation">,</span> completed<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> id<span class="token punctuation">:</span> <span class="token function">uuid</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token function">updateTodos</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token operator">...</span>todos<span class="token punctuation">,</span> newTodos<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">[</span>todos<span class="token punctuation">,</span> addTodo<span class="token punctuation">,</span> setTodoCompleted<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> useTodos<span class="token punctuation">;</span> |
Đây là phần quan trọng nhất nên chúng ta sẽ cùng nhau phân tích từng chút một nhé.
1 2 3 4 5 6 7 8 | <span class="token keyword">const</span> <span class="token function-variable function">getTodos</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> todos <span class="token operator">=</span> window<span class="token punctuation">.</span>localStorage<span class="token punctuation">.</span><span class="token function">getItem</span><span class="token punctuation">(</span><span class="token string">'todos'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>todos<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>todos<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Đầu tiên getTodos
, một function để lấy dữ liệu Todos từ dưới local storage. Nếu todos
không tồn tại chúng ta sẽ trả về một mảng rỗng. Còn không chúng ta sẽ trả về một javascript object dưới dạng chuỗi.
const [todos, setTodos] = React.useState(getTodos());
Chúng ta sẽ sử dụng State để re-render lại mỗi khi todos thay đổi giá trị. Giá trị ban đầu sẽ được gọi từ getTodos
.
1 2 3 4 5 6 | <span class="token keyword">const</span> <span class="token function-variable function">updateTodos</span> <span class="token operator">=</span> <span class="token punctuation">(</span>newTodos<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> stringifiedTodos <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>newTodos<span class="token punctuation">)</span><span class="token punctuation">;</span> window<span class="token punctuation">.</span>localStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'todos'</span><span class="token punctuation">,</span> stringifiedTodos<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setTodos</span><span class="token punctuation">(</span>newTodos<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
Một function cập nhật các todos mới dưới local storage và thiết lập chúng trong State. Chúng ta cần sử dụng JSON.stringify
trong newTodos vì local storage chỉ lưu dạng chuỗi.
1 2 3 4 5 6 7 | <span class="token keyword">const</span> <span class="token function-variable function">setTodoCompleted</span> <span class="token operator">=</span> <span class="token punctuation">(</span>id<span class="token punctuation">,</span> completed<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> todoIds <span class="token operator">=</span> todos<span class="token punctuation">.</span><span class="token function">findIndex</span><span class="token punctuation">(</span>todo <span class="token operator">=></span> todo<span class="token punctuation">.</span>id <span class="token operator">===</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> newTodos <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span>todos<span class="token punctuation">]</span><span class="token punctuation">;</span> newTodos<span class="token punctuation">[</span>todoIds<span class="token punctuation">]</span><span class="token punctuation">.</span>completed <span class="token operator">=</span> completed<span class="token punctuation">;</span> <span class="token function">updateTodos</span><span class="token punctuation">(</span>newTodos<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
Một function để đánh dấu Todos đã hoàn thành. Đầu tiên, chúng ta cần lấy dữ liệu từ mảng todos
trong State. Kê tiếp, chúng ta sẽ sử dụng array spread syntax để copy array thành 1 trường mới để không biến đổi giá trị của nó. Cuối cùng, chúng ta cập nhật completed
và gọi updateTodos
để cập nhật array mới.
1 2 3 4 5 | <span class="token keyword">const</span> <span class="token function-variable function">addTodo</span> <span class="token operator">=</span> <span class="token punctuation">(</span>label<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> newTodo <span class="token operator">=</span> <span class="token punctuation">{</span> label<span class="token punctuation">,</span> completed<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> id<span class="token punctuation">:</span> <span class="token function">uuid</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token function">updateTodos</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token operator">...</span>todos<span class="token punctuation">,</span> newTodo<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
Một function để thêm một todo vào danh sách việc cần làm. Chúng ta cần tạo mới một todo objec thông qua <CreateTodoForm />
. Chúng ta cũng thiết lập giá trị completed = false
và sử dụng thư viện uuid()
để tạo ra một unique id cho todo mới. và gọi updateTodos
để append newTodo
vào mạng todos
hiện tại.
Phần cuối cùng trả về mảng todos và refer đến 2 function: addTodos
và setTodoCompleted
.
<App />
Component
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <span class="token keyword">import</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> CreateTodoForm <span class="token keyword">from</span> <span class="token string">'./CreateTodoForm'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> useTodos <span class="token keyword">from</span> <span class="token string">'./useTodos'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token string">'./App.css'</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">App</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>todos<span class="token punctuation">,</span> addTodo<span class="token punctuation">,</span> setTodoCompleted<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useTodos</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">"App"</span><span class="token operator">></span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">"todo-container"</span><span class="token operator">></span> <span class="token operator"><</span>h2<span class="token operator">></span>Your Todos<span class="token operator"><</span><span class="token operator">/</span>h2<span class="token operator">></span> <span class="token operator"><</span>hr <span class="token operator">/</span><span class="token operator">></span> <span class="token punctuation">{</span> todos<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>todo <span class="token operator">=></span> <span class="token punctuation">(</span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">"todo"</span><span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>label<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span>input type<span class="token operator">=</span><span class="token string">"checkbox"</span> checked<span class="token operator">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>completed<span class="token punctuation">}</span> onChange<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">setTodoCompleted</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<span class="token punctuation">,</span> <span class="token operator">!</span>todo<span class="token punctuation">.</span>completed<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span>CreateTodoForm createNewTodo<span class="token operator">=</span><span class="token punctuation">{</span>addTodo<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> App<span class="token punctuation">;</span> |
App
Component sẽ gắn kết tất cả lại với nhau. Chúng ta sẽ nối các items trong todos
và render ra mỗi items một label và một checkbox. Chúng sẽ thực hiện xác nhận đánh dấu xử lý todos hoàn thành với sự kiện onChange
gọi tới function setTodoCompleted
trong setTodoCompleted
.
Cùng làm đẹp chút form ứng dụng với các style cơ bản sau:
App.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <span class="token selector">.App</span> <span class="token punctuation">{</span> <span class="token property">padding</span><span class="token punctuation">:</span> 32px<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.todo</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span> <span class="token property">padding</span><span class="token punctuation">:</span> 8px<span class="token punctuation">;</span> <span class="token property">margin</span><span class="token punctuation">:</span> 8px<span class="token punctuation">;</span> <span class="token property">background-color</span><span class="token punctuation">:</span> whitesmoke<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.todo p</span> <span class="token punctuation">{</span> <span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">margin-right</span><span class="token punctuation">:</span> 16px<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.create-todo-form</span> <span class="token punctuation">{</span> <span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.create-todo-form button</span> <span class="token punctuation">{</span> <span class="token property">margin</span><span class="token punctuation">:</span> 5px<span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Mọi thứ đã sẵn sàng, chúng ta sẽ chạy ứng dụng để kiểm tra xem nhé.
Deploying as a Chrome Extension
Mở public/manifest.json
và cấu hình thông tin cho ứng dụng như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token punctuation">{</span> <span class="token property">"short_name"</span><span class="token operator">:</span> <span class="token string">"TodoApp"</span><span class="token punctuation">,</span> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"TodoApp Sample"</span><span class="token punctuation">,</span> <span class="token property">"manifest_version"</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token property">"browser_action"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"default_popup"</span><span class="token operator">:</span> <span class="token string">"index.html"</span><span class="token punctuation">,</span> <span class="token property">"default_title"</span><span class="token operator">:</span> <span class="token string">"TodoApp"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"permissions"</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">"storage"</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"1.0"</span> <span class="token punctuation">}</span> |
Thuộc tính browser_action.default_popop
sẽ đánh dấu chỉ mục main page, trường hợp phổ biến sẽ là index.html
.
Tiếp theo chúng ta sẽ build ứng dụng với npm run build
từ thư mục root của app.
Mở Chrome và nhập địa chỉ chrome://extensions
. Bật chế độ Developer Mode
ở trên cùng bên phải cửa sổ trình duyệt và click vào Load Unpacked
ở bên trái.
Tìm đến thư mục build/
và chọn select. Ngay sau đó Extension của bạn sẽ xuất hiện trong danh sách.
Lưu ý: Ứng dụng có thể không hoạt động. Chính sách của Google Chrome ngăn chặn các extension thực thi nội tuyến. Để giải quyết vấn đề này, chúng ta cần thêm dòng này vào manifest.json
:
1 2 | "content_security_policy": "script-src 'self' '<your-sha>'; object-src 'self'" |
Nếu extension của bạn gặp lỗi, quay lại chrome://extensions
và click vào Errors
sẽ nhìn thấy dòng chữ Refused to execute inline script...
. Rebuild lại ứng dụng và load lại bên trong thư viện tiện ích chrome Load unpacked
.
Chúng ta cùng xem thành quả nhé:
Và bây giờ chúng ta đã có thể tự tạo một Google Chrome extension. Thế giới đã trong tay bạn hãy cùng phát triển những ứng dụng công nghệ hay nhé. Cảm ơn đã theo dõi bài viết này. Mọi đóng góp sẽ được ghi nhận và lưu ý cho những bài viết sau.
Thanks and Best Regards
Tài liệu tham khảo