
store.Store sẽ chứa các state - dữ liệu trạng thái.store sẽ là reducer, xem như reducer là 1 callbacknew state sẽ được tạo từ xử lý reducer, nên ta khai báo như sau:function createStore(reducer){
let state = reducer()
...
}
VIEWVIEW ta sẽ xây dựng nên các Component - Thành phầnComponent : là các thành phần nhỏ lẻ từng chức năng cấu thành nên 1 giao diện VIEW.
ví dụ: Giao diện Todo App như sau:


(3) Component Filter: Lọc các trạng thái
index.html phần body sẽ tạo tưng ứng những Root thành phần gốc tương ứng với Component như sau:<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Todo App</title>
<link rel="stylesheet" href="./css/base.css" />
<link rel="stylesheet" href="./css/index.css" />
</head>
<body>
<!-- <div id="root"></div> -->
<section class="todoapp">
<header class="header">
<h1>todos</h1>
<input
class="new-todo"
placeholder="What needs to be done?"
autofocus
/>
</header>
<!-- This section should be hidden by default and shown when there are todos -->
<section class="main">
<input id="toggle-all" class="toggle-all" type="checkbox" />
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
<!-- These are here just to show the structure of the list items -->
<!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
<li class="completed">
<div class="view">
<input class="toggle" type="checkbox" checked />
<label>Taste JavaScript</label>
<button class="destroy"></button>
</div>
<input class="edit" value="Create a TodoMVC template" />
</li>
<li>
<div class="view">
<input class="toggle" type="checkbox" />
<label>Buy a unicorn</label>
<button class="destroy"></button>
</div>
<input class="edit" value="Rule the web" />
</li>
</ul>
</section>
<!-- This footer should be hidden by default and shown when there are todos -->
<footer class="footer">
<!-- This should be `0 items left` by default -->
<span class="todo-count"><strong>0</strong> item left</span>
<!-- Remove this if you don't implement routing -->
<ul class="filters">
<li>
<a class="selected" href="#/">All</a>
</li>
<li>
<a href="#/active">Active</a>
</li>
<li>
<a href="#/completed">Completed</a>
</li>
</ul>
<!-- Hidden if no completed items are left ↓ -->
<button class="clear-completed">Clear completed</button>
</footer>
</section>
<!-- Scripts here. Don't remove ↓ -->
<script type="module" src="./script.js"></script>
</body>
</html>
createStorecreateStoreStore sẽ chứa các state - dữ liệu trạng thái.store sẽ là reducer, xem như reducer là 1 callback sẽ viết sau này để truyền vào.new state sẽ được tạo từ xử lý reducer, nên ta khai báo như sau:function createStore(reducer){
let state = reducer()
...
}
VIEW.html để truyền vào element có id = "root".function html([first, ...values], ...strings) {
return values
.reduce(
(acc, cur) => {
return acc.concat(strings.shift(), cur);
},
[first]
)
.filter((x) => (x && x !== true) || x === 0)
.join('');
}
id = "root" được gọi là root (Node gốc), và sẽ được render thành chuỗi stringHtml để truyền vào DOM thông qua innerHtml.function createStore(reducer){
let state = reducer()
const roots = new Map(); // Ban đầu thì roots sẽ không có dữ liệu
/**
* roots : Map(){
* key : Value
* div#root : () => `<h1>Hello World!!!</h1>`
* root: div#root
* component : () => `<h1>Hello World!!!</h1>`
* }
*/
// attach(() => `<h1>Hello World!!!</h1>`, document.getElementById('root'));
/**
* roots : Map(1) {div#root => ƒ}
* {div#root => () => `<h1>Hello World!!!</h1>`}
* key: div#root
* value: () => `<h1>Hello World!!!</h1>`
* [[Prototype]]: Map
*/
...
}
key sẽ là element rootvalue sẽ là một hàm component để tạo ra được chuỗi html, ta sẽ gọi là thành phần component.roots : Map(){
key : Value
//Ví dụ: div#root : () => `<h1>Hello World!!!</h1>`
//Trong đó root: div#root
//component : () => `<h1>Hello World!!!</h1>`
}
Map(){
// key : Value
div#root : () => `<h1>Hello World!!!</h1>`
}
Thì:
key sẽ là div#rootcomponent sẽ là () => '<h1>Hello World!!!</h1>'Khi này hàm createStore sẽ là :
function createStore(reducer) {
let state = reducer();
const roots = new Map(); // Ban đầu thì roots sẽ không có dữ liệu
}
Render
roots lưu các gốc Element chuyển thành các component ở View thông qua DOM như sau: function render(){
// Vòng lặp qua roots để chuyển ra VIEW
for(const [root, component] of roots){
const output = component(); // Callback html() ra chuỗi
root.innerHTML = output; // Gán chuỗi html vào element #root để hiển thị
}
Khi này hàm createStore sẽ là :
function createStore(reducer) {
let state = reducer(); // Sử dụng closure
const roots = new Map();
function render(){
// Vòng lặp qua roots để chuyển ra VIEW
for(const [root, component] of roots){
const output = component();
root.innerHTML = output;
}
}
attachconst roots = new Map();
function attach(component, root) {
roots.set(root, component); // Sử dụng method set của đối tượng Map()
render(); // Sau khi gán xong sẽ render ra view luôn
}
createStore sẽ thành:function createStore(reducer) {
let state = reducer(); // Sử dụng closure
const roots = new Map();
// Xử lý Render từng hàm component vào root component thành phần tương ứng
function render(){
// Vòng lặp qua roots để chuyển ra VIEW
for(const [root, component] of roots){
const output = component();
root.innerHTML = output;
}
// Trả về Object gồm các phương thức để xử lý ra View
return {
// 1. Phương thức đẩy component và root element tương ứng vào Roots
attach(component, root){
roots.set(root,component); // Sử dụng method set của đối tượng Map()
render(); // Sau khi gán xong sẽ render ra view luôn
},
...
}
}
connect // let state = reducer();
function connect(selector = state => state){
return component => (props, ...args)=>
component(Object.assign({}, props, selector(state), ...args))
// Merge các đối tượng vào Object gán vào Object.assign
},
function createStore(reducer) {
let state = reducer(); // Sử dụng closure
const roots = new Map();
// Xử lý Render từng hàm component vào root component thành phần tương ứng
function render(){
// Vòng lặp qua roots để chuyển ra VIEW
for(const [root, component] of roots){
const output = component();
root.innerHTML = output;
}
// Trả về Object gồm các phương thức để xử lý ra View
return {
// 1. Phương thức đẩy component và root element tương ứng vào Roots
attach(component, root){
roots.set(root,component); // Sử dụng method set của đối tượng Map()
render(); // Sau khi gán xong sẽ render ra view luôn
},
// 2. Lọc các state thích hợp chuyển qua View
connect(selector = state => state){
return component => (props, ...args)=>
component(Object.assign({}, props, selector(state), ...args))
// Merge các đối tượng vào Object gán vào Object.assign
},
...
}
}
dispatchPhương thức này sẽ thực hiện xử lý khi thao tác người dùng tác động trên VIEW sẽ đẩy hành động action + state vào reducer.
Ở đây xem như thao tác sẽ tác động truyền vào reducer xử lý luôn.
reducer: hiểu tương tự như reduce sẽ truyền vào giá trị ban đầu, biến đổi và tạo thành giá trị tích trữ mới.
function dispatch(action, ...args) {
state = reducer(state, action, args); // reducer xem như callback
// Tạo state mới từ state cũ, action và các tham số khác.
// state được thay đổi mới -> store sẽ được update lại, và cần VIEW thay đổi lại.
render(); // Update lại VIEW
}
createStore sẽ thành:function createStore(reducer) {
let state = reducer(); // Sử dụng closure
const roots = new Map();
// Xử lý Render từng hàm component vào root component thành phần tương ứng
function render(){
// Vòng lặp qua roots để chuyển ra VIEW
for(const [root, component] of roots){
const output = component();
root.innerHTML = output;
}
// Trả về Object gồm các phương thức để xử lý ra View
return {
// 1. Phương thức đẩy component và root element tương ứng vào Roots
attach(component, root){
roots.set(root,component); // Sử dụng method set của đối tượng Map()
render(); // Sau khi gán xong sẽ render ra view luôn
},
// 2. Lọc các state thích hợp chuyển qua View
connect(selector = state => state){
return component => (props, ...args)=>
component(Object.assign({}, props, selector(state), ...args))
// Merge các đối tượng vào Object gán vào Object.assign
},
// 3. Dispatch : Thao tác người dùng tác động trên VIEW sẽ đẩy hành động
dispatch(action,...args){
state = reducer(state, action, args);
render(); // Update lại VIEW
}
}
}
