Home Blog Page 168

Làm sao để fetch dữ liệu bằng React Hook

Bài viết được sự cho phép của tác giả Lưu Bình An

Trong bài này chúng ta sẽ sử dụng React.useState, React.useEffect, React.useReducer để fetch dữ liệu từ API, đồng thời cũng viết một custom hook để có thể sử dụng ở bất kỳ đâu

Chúng ta có một component, dữ liệu của component này sẽ được lấy từ API

import React, { useState } from 'react';

function App() {
  const [data, setData] = useState({ hits: [] });
  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}
export default App;

Chúng ta sẽ sử dụng axios để fetch dữ liệu, bạn thích xài cái khác thì cứ vô tư

import React, { useState, useEffect } from 'react';
import axios from 'axios';

function App() {
  const [data, setData] = useState({ hits: [] });
  
  useEffect(async () => {
    const result = await axios(
      'https://hn.algolia.com/api/v1/search?query=redux',
    );
    setData(result.data);
  });

  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}
export default App;

Bên trong React.useEffect chúng ta sẽ thực hiện việc fetch data từ API, sau khi nhận được dữ liệu gán giá trị nhận được cho giá trị của state data

Nếu dừng ở đây, khi chạy bạn sẽ thấy một vòng lặp vô tận của việc gọi fetch data. Effect sẽ chạy không chỉ ở lúc component mount mà còn ở các lần update tiếp theo. Bởi vì chúng ta gán giá trị state trên mỗi lần fetch, component lại được update và effect lại được gọi lại để chạy. Chúng ta chỉ muốn fetch data khi component mount lần đầu tiên. Đó là lý do chúng ta phải thêm một mảng rỗng vào tham số thứ hai của effect, như vậy các lần update tiếp theo nó sẽ không được gọi.

import React, { useState, useEffect } from 'react';
import axios from 'axios';

function App() {
  const [data, setData] = useState({ hits: [] });
  
  useEffect(async () => {
    const result = await axios(
      'https://hn.algolia.com/api/v1/search?query=redux',
    );
    setData(result.data);
  }, []);
  
  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}
export default App;

 

Tham số thứ 2 truyền vào cho effect này là danh sách những giá trị nào mà hook phụ thuộc, tức nếu các giá trị này thay đổi thì effect được gọi lại. Bỏ array trống sẽ không còn chuyện chạy ở lần update.

Trong đoạn code trên vẫn còn một chỗ phải chỉnh sửa, chúng ta sử dụng async/awaittheo như định nghĩa, tất cả những hàm nào là async sẽ được ngầm hiểu là trả về một Promise. Tuy nhiên, cũng theo như định nghĩa effect hook không được trả về gì cả, hoặc một function để clean up (xem lại bài nói về Hook Effect, có giải thích 2 loại Effect Hook).

Nên bạn mà copy đoạn trên mà chạy thì sẽ nhận thông báo bên dưới console. Không thể sử dụng async function bên trong React.useEffect, chúng ta sửa lại

useEffect(() => {
  const fetchData = async () => {
    const result = await axios(
      'https://hn.algolia.com/api/v1/search?query=redux',
    );
    setData(result.data);
  };
  fetchData();
}, []);

Để fetch dữ liệu bằng React.useEffect có thể tóm gọn như ở trên. Chúng ta sẽ tiếp tục xem cách handle error, loading indicator, gọi fetch từ form và làm thế nào tái sử dụng hook để fetch

Gọi hook thủ công/bằng code

Chúng ta đã xong phần fetch dữ liệu một lần lúc component mount. Nhưng làm thế nào để fetch dữ liệu khi có sự kiện từ user, ví dụ ô search, khi user nhập lấy danh sách kết quả tìm kiếm. Ví dụ bên dưới mặc định sẽ hiển thị kết quả cho từ khóa redux, nếu user nhập vào một giá trị khác, chúng ta cần làm sao để chạy useEffect một lần nữa

import React, { Fragment, useState, useEffect } from 'react';
import axios from 'axios';

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');
  
  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        'https://hn.algolia.com/api/v1/search?query=redux',
      );
      setData(result.data);
    };
    fetchData();
  }, []);
  
  return (
    <Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <ul>
        {data.hits.map(item => (
          <li key={item.objectID}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </Fragment>
  );
}
export default App;

Với nhu cầu như trên, chúng ta cần cập nhập lại useEffect

useEffect(() => {
  const fetchData = async () => {
    const result = await axios(
      `http://hn.algolia.com/api/v1/search?query=${query}`,
    );
    setData(result.data);
  };
  fetchData();
}, []);

Tuy nhiên, nếu chỉ như vậy, hàm fetchData sẽ không được gọi khi user input một giá trị mới vào ô tìm kiếm. Vì chúng ta đã truyền vào một mảng rỗng vào cho giá trị depend của effect, nên nó chỉ chạy lần đầu mount

useEffect(() => {
  const fetchData = async () => {
    const result = await axios(
      `http://hn.algolia.com/api/v1/search?query=${query}`,
    );
    setData(result.data);
  };
  fetchData();
}, [query]);

Có một vấn đề khác, user cứ nhập một ký tự, câu fetchData lại được gọi, gọi liên tục như vậy không hay, thêm vào một nút để user click vào mới thực hiện search thì sao

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');
  const [search, setSearch] = useState('');
  
  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        `http://hn.algolia.com/api/v1/search?query=${query}`,
      );
      setData(result.data);
    };
    fetchData();
  }, [query]);
  
  return (
    <Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <button type="button" onClick={() => setSearch(query)}>
        Search
      </button>
      <ul>
        {data.hits.map(item => (
          <li key={item.objectID}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </Fragment>
  );
}

Giờ effect phải phụ thuộc vào search, không chạy khi user nhập vào input

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');
  const [search, setSearch] = useState('redux');
  
  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        `http://hn.algolia.com/api/v1/search?query=${search}`,
      );
      setData(result.data);
    };
    fetchData();
  }, [search]);
  
  return (
    ...
  );
}

Nhưng nếu sửa như vậy, trường hợp component được mount lần đầu, nó sẽ không có hiển thị kết quả cho từ khóa redux nữa. Nếu dùng thềm một useEffect khác cho trường hợp chạy lúc đầu sẽ gây nhầm lẫn, không rõ ràng, thay vào đó nếu chúng ta xem search state là nguyên cái url sẽ đơn giản hơn

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');
  const [url, setUrl] = useState(
    'https://hn.algolia.com/api/v1/search?query=redux',
  );
  
  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(url);
      
      setData(result.data);
    };
    
    fetchData();
  }, [url]);
  
  return (
    <Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <button
        type="button"
        onClick={() =>
          setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
        }
      >
        Search
      </button>
      <ul>
        {data.hits.map(item => (
          <li key={item.objectID}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </Fragment>
  );
}

Loading indicator

Một nhu cầu khác cũng hay gặp là trong lúc fetch data từ API, chúng ta cần biết trạng thái loading tới đâu rồi, chúng ta sẽ bổ sung thêm state isLoading

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');
  const [url, setUrl] = useState(
    'https://hn.algolia.com/api/v1/search?query=redux',
  );
  const [isLoading, setIsLoading] = useState(false);
  
  useEffect(() => {
    const fetchData = async () => {
      setIsLoading(true);
      
      const result = await axios(url);
      
      setData(result.data);
      setIsLoading(false);
    };
    fetchData();
  }, [url]);
  
  return (
    <Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <button
        type="button"
        onClick={() =>
          setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
        }
      >
        Search
      </button>
      {isLoading ? (
        <div>Loading ...</div>
      ) : (
        <ul>
          {data.hits.map(item => (
            <li key={item.objectID}>
              <a href={item.url}>{item.title}</a>
            </li>
          ))}
        </ul>
      )}
    </Fragment>
  );
}
export default App;

Handle Error

Cũng tương tự như loading, chúng ta sẽ bổ sung thêm state isError để xác định việc fetch dữ liệu có bị lỗi không

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');
  const [url, setUrl] = useState(
    'https://hn.algolia.com/api/v1/search?query=redux',
  );
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);
  
  useEffect(() => {
    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);
      
      try {
        const result = await axios(url);
        
        setData(result.data);
      } catch (error) {
        setIsError(true);
      }
      
      setIsLoading(false);
    };
    fetchData();
  }, [url]);
  return (
    <Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <button
        type="button"
        onClick={() =>
          setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
        }
      >
        Search
      </button>
      {isError && <div>Something went wrong ...</div>}
      {isLoading ? (
        <div>Loading ...</div>
      ) : (
        <ul>
          {data.hits.map(item => (
            <li key={item.objectID}>
              <a href={item.url}>{item.title}</a>
            </li>
          ))}
        </ul>
      )}
    </Fragment>
  );
}

Fetch data với Form

Nãy giờ chúng ta chỉ fetch data với bằng input và button. Khi có nhiều element hơn, chúng ta sẽ đưa nó vào form để có thể trigger form submit bằng cách nhấn Enter

function App() {
  ...
  return (
    <Fragment>
      <form
        onSubmit={event =>
          setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
          event.preventDefault();
        }
      >
        <input
          type="text"
          value={query}
          onChange={event => setQuery(event.target.value)}
        />
        <button type="submit">Search</button>
      </form>
      {isError && <div>Something went wrong ...</div>}
      ...
    </Fragment>
  );
}

Tìm việc làm React lương cao cho SV mới ra trường tại đây

Custom hook để Fetch data

Để tái sử dụng được các đoạn code liên quan đến việc fetch data, chúng ta sẽ đưa nó ra thành một custom hook, các giá trị liên quan trực tiếp đến việc fetch data, cụ thể là loading, error chúng ta cũng đưa vào trong custom hook

const useHackerNewsApi = () => {
  const [data, setData] = useState({ hits: [] });
  const [url, setUrl] = useState(
    'https://hn.algolia.com/api/v1/search?query=redux',
  );
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);
  
  useEffect(() => {
    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);
      try {
        const result = await axios(url);
        setData(result.data);
      } catch (error) {
        setIsError(true);
      }
      setIsLoading(false);
    };
    
    fetchData();
  }, [url]);
  
  return [{ data, isLoading, isError }, setUrl];
}

Sử dụng bên trong App Component

function App() {
  const [query, setQuery] = useState('redux');
  const [{ data, isLoading, isError }, doFetch] = useHackerNewsApi();
  
  return (
    <Fragment>
      <form onSubmit={event => {
        doFetch(`http://hn.algolia.com/api/v1/search?query=${query}`);
        event.preventDefault();
      }}>
        <input
          type="text"
          value={query}
          onChange={event => setQuery(event.target.value)}
        />
        <button type="submit">Search</button>
      </form>
      ...
    </Fragment>
  );
}

Giá trị state lúc khởi tạo của thể đưa vào như một tham số truyền vào cho custom hook luôn

import React, { Fragment, useState, useEffect } from 'react';
import axios from 'axios';

const useDataApi = (initialUrl, initialData) => {
  const [data, setData] = useState(initialData);
  const [url, setUrl] = useState(initialUrl);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);
  
  useEffect(() => {
    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);
      try {
        const result = await axios(url);
        setData(result.data);
      } catch (error) {
        setIsError(true);
      }
      setIsLoading(false);
    };
    
    fetchData();
  }, [url]);
  
  return [{ data, isLoading, isError }, setUrl];
};

function App() {
  const [query, setQuery] = useState('redux');
  const [{ data, isLoading, isError }, doFetch] = useDataApi(
    'https://hn.algolia.com/api/v1/search?query=redux',
    { hits: [] },
  );
  
  return (
    <Fragment>
      <form
        onSubmit={event => {
          doFetch(
            `http://hn.algolia.com/api/v1/search?query=${query}`,
          );
          event.preventDefault();
        }}
      >
        <input
          type="text"
          value={query}
          onChange={event => setQuery(event.target.value)}
        />
        <button type="submit">Search</button>
      </form>
      {isError && <div>Something went wrong ...</div>}
      {isLoading ? (
        <div>Loading ...</div>
      ) : (
        <ul>
          {data.hits.map(item => (
            <li key={item.objectID}>
              <a href={item.url}>{item.title}</a>
            </li>
          ))}
        </ul>
      )}
    </Fragment>
  );
}
export default App;

Reducer hook

Với cái custom hook để fetch data như ở trên, chúng ta thấy có 2 state isLoadingisError quan hệ khá mật thiết với nhau, có thể hợp nhất 2 đứa nó lại bằng React.useReducer

import React, {
  Fragment,
  useState,
  useEffect,
  useReducer,
} from 'react';
import axios from 'axios';

const dataFetchReducer = (state, action) => {
  ...
};

const useDataApi = (initialUrl, initialData) => {
  const [url, setUrl] = useState(initialUrl);
  
  const [state, dispatch] = useReducer(dataFetchReducer, {
    isLoading: false,
    isError: false,
    data: initialData,
  });
  ...
};

React.useReducer sẽ nhận vào một hàm reducer (công dụng tương tự như hàm reducer của redux ấy) và các giá trị khởi tạo của state, trong trường hợp của chúng ta là isLoading và isError. Việc này chẳng qua là gom tất cả state liên quan vào một object cho nó tinh tế thôi, thay vì từng state riêng biệt như sử dụng useState

const dataFetchReducer = (state, action) => {
  ...
};

const useDataApi = (initialUrl, initialData) => {
  const [url, setUrl] = useState(initialUrl);
  
  const [state, dispatch] = useReducer(dataFetchReducer, {
    isLoading: false,
    isError: false,
    data: initialData,
  });
  
  useEffect(() => {
    const fetchData = async () => {
      dispatch({ type: 'FETCH_INIT' });
      try {
        const result = await axios(url);
        dispatch({ type: 'FETCH_SUCCESS', payload: result.data });
      } catch (error) {
        dispatch({ type: 'FETCH_FAILURE' });
      }
    };
    fetchData();
  }, [url]);
  
  ...
};

 

Mình đã bảo rồi, nó sẽ giống như cái reducer trong redux thôi, chúng ta dispatch một object gồm type và payload, căn cứ vào payload mà chúng ta xử lý, cập nhập state

Cuối cùng chúng ta cập nhập lại giá trị trả về của custom hook nữa

const useDataApi = (initialUrl, initialData) => {
  const [url, setUrl] = useState(initialUrl);
  
  const [state, dispatch] = useReducer(dataFetchReducer, {
    isLoading: false,
    isError: false,
    data: initialData,
  });
  
  ...
  
  return [state, setUrl];
};

Cuối cùng, không kém phần quan trọng, phần code thực hiện bên trong dataFetchReducer

const dataFetchReducer = (state, action) => {
  switch (action.type) {
    case 'FETCH_INIT':
      return {
        ...state,
        isLoading: true,
        isError: false
      };
    case 'FETCH_SUCCESS':
      return {
        ...state,
        isLoading: false,
        isError: false,
        data: action.payload,
      };
    case 'FETCH_FAILURE':
      return {
        ...state,
        isLoading: false,
        isError: true,
      };
    default:
      throw new Error();
  }
};

Bỏ qua việc fetch data

Tình huống là khi user chuyển qua một route khác, khi đang fetch data, việc gọi fetch ko cần thiết và có thể bỏ qua

const useDataApi = (initialUrl, initialData) => {
  const [url, setUrl] = useState(initialUrl);
  
  const [state, dispatch] = useReducer(dataFetchReducer, {
    isLoading: false,
    isError: false,
    data: initialData,
  });
  
  useEffect(() => {
    let didCancel = false;
    
    const fetchData = async () => {
      dispatch({ type: 'FETCH_INIT' });
      try {
        const result = await axios(url);
        if (!didCancel) {
          dispatch({ type: 'FETCH_SUCCESS', payload: result.data });
        }
      } catch (error) {
        if (!didCancel) {
          dispatch({ type: 'FETCH_FAILURE' });
        }
      }
    };
    
    fetchData();
    
    return () => {
      didCancel = true;
    };
    
  }, [url]);
  
  return [state, setUrl];
};

Với việc return một function ở cuối của React.useEffect, tên gọi các bạn React đặt là clean up function, nằm trong kiểu effect cần clean up – nói thật mình phát mệt với việc các bạn trong team React cứ thích chế thêm liên tục như vậy.

Tham khảo thêm các vị trí tuyển dụng React hấp dẫn tại Topdev

Bài viết gốc được đăng tải tại Vuilaptrinh

Bí mật giúp tạo động lực và tăng năng suất cho nhân viên!

Không đơn thuần chỉ là một cái vỗ nhẹ từ phía sau, hầu hết các nhân viên đều mong muốn nhận được sự quan tâm nhiều hơn của sếp để duy trì động lực làm việc. Đó là lý do tại sao nhiều nhà tuyển dụng lựa chọn việc thực hiện các chương trình khuyến khích để tạo sự gắn kết, thúc đẩy nhân viên làm việc chăm chỉ hơn. 

Thế nào là tạo động lực cho nhân viên?

Cung cấp phần thưởng như thẻ quà tặng, thêm thời gian nghỉ và ăn trưa miễn phí không chỉ là một điều có giá trị dành cho nhân viên của bạn; điều này cũng tốt cho các doanh nghiệp về lâu dài.

Một nghiên cứu năm 2018 của Genesis Associates – một công ty tuyển dụng có trụ sở tại Anh với các ngành kỹ thuật, bán hàng và phân ngành sáng tạo – khảo sát cho thấy 85% công nhân viên cảm thấy có động lực hơn để làm tốt nhất công việc khi nhận được sự khích lệ. Ngoài ra, 73% thể hiện sự hài lòng với những đánh giá là “tốt” hoặc “rất tốt” thông qua bầu không khí trong môi trường được tạo động lực. Các chương trình thưởng cho nhân viên cũng làm gia tăng lợi nhuận trung bình của một công ty lên tới 80.000 bảng Anh (khoảng 104.000 đô la) mỗi tuần, nghiên cứu cho thấy.

Bí mật giúp tạo động lực và tăng năng suất cho nhân viên!

Việc tạo động lực có thể thúc đẩy bạn, cung cấp những phần thưởng cho nhân viên của bạn, đồng thời khuyến khích họ làm việc năng suất hơn. Mọi người ai cũng đều thích được đánh giá cao về những nỗ lực và việc khuyến khích là cách để cho nhân viên thấy rằng phần thưởng sẽ thật sự xứng đáng với những người làm việc hiệu quả.

Những ví dụ nào về việc tạo động lực cho nhân viên?

Vậy, những sự khuyến khích nào thực sự thúc đẩy nhân viên làm việc chăm chỉ nhất? Không phải bàn cãi, phần thưởng về tiền tệ đã giành được vị trí hàng đầu; theo khảo sát, 40% người lựa chọn tiền là động lực thúc đẩy họ nhất, 29% chọn một kỳ nghỉ miễn phí và 23% mong muốn có thêm thời gian nghỉ trong công việc. Số phần trăm còn lại gồm những phần thưởng như các bữa ăn, đồ uống và tùy chọn để kết thúc sớm một ngày làm việc.

Mặc dù sự khuyến khích dựa trên hiệu suất cá nhân của nhân viên có thể mang lại những phần thưởng có giá trị hơn, cuộc khảo sát của Genesis cho thấy rằng 71% người lao động lại ưa thích sự khuyến khích dựa trên nỗ lực của nhóm. Patrick Bell, giám đốc điều hành của Genesis Associates, cho biết điều này có khả năng vì nhân viên làm việc nhằm hướng đến mục tiêu nhóm và sẽ có nhiều người giúp duy trì và phát triển động lực cao hơn trong thời gian khuyến khích. 

Tại sao nhà tuyển dụng nên tạo động lực cho nhân viên?

Nhân viên muốn được công nhận và khen thưởng cho những cống hiến của họ.

“Làm việc dựa trên sự khích lệ của nhóm, có tính trách nhiệm với tập thể; bạn không muốn để các thành viên khác trong nhóm thất vọng”, Bell nói với Business News Daily. “Do vậy, mọi người cố gắng nhiều hơn và thường đạt được kết quả tốt hơn. Sự phân chia về lợi ích một cách phù hợp là một tiêu chí rất quan trọng. Nếu quá nhiều, mọi người có thể che giấu trách nhiệm của mình; quá ít, bạn có thể mất đi lợi ích của động lực cao hơn.”

Nếu lo ngại về ngân sách của việc cung cấp phần thưởng, bạn không nhất thiết phải cung cấp các ưu đãi tiền tệ, Bell nói. Chẳng hạn, Genesis linh hoạt sử dụng các lợi ích khác, chẳng hạn như hoàn thiện công việc sớm hơn trong một ngày, kéo dài thời gian nghỉ trưa, trao tặng cúp hay đơn giản là một bức tranh trên tường kèm tên của nhân viên để tuyên dương cho sự phấn đấu của họ.

Bí mật giúp tạo động lực và tăng năng suất cho nhân viên!

Bell cũng lưu ý rằng, đối với nhiều nhân viên, sự công nhận thường quan trọng hơn số tiền mặt hoặc giải thưởng. Một số người yêu thích sự công nhận trong khi những người khác có thể ngại vì điều đó. Như vậy, điều quan trọng là phải xem xét những gì sẽ thực sự phù hợp cho nhân viên của bạn. “Chúng tôi có những giải thưởng hàng tháng để tạo động lực cho thực tập sinh tốt nhất, nhà tuyển dụng tốt nhất và đội ngũ ấn tượng của tháng”, Bell nói. 

Một lựa chọn khác cho việc tạo ra sự khuyến khích chính là sự thăng tiến, tức là cung cấp cho nhân viên một chức danh mới. Đây được xem là động lực lớn vì nó chính là minh chứng cho sự tin tưởng về tinh thần trách nhiệm đồng thời ghi nhận những nỗ lực của một cá nhân trong công việc.

Từng bước xây dựng plan tạo động lực cho nhân viên?

Việc tạo động lực không khó nếu bạn tiếp cận nó một cách chính xác. Một cách thức tốt để bắt đầu là hỏi nhân viên của bạn về những mong muốn nào họ có thể thích. 

Bạn cũng nên đặt ra những tiêu chuẩn và tính khả thi về hiệu quả cho mỗi phương án. Sau đây là những cách thức giúp bạn dễ dàng hơn trong việc tạo lực:

Tạo môi trường làm việc thân thiện và giao tiếp tích cực

Nhân viên của bạn dành một lượng lớn thời gian của cuộc sống của họ để làm việc trong văn phòng. 

Vì vậy, hãy cố gắng làm cho mọi trường làm việc nhân sự trở nên vui vẻ và hấp dẫn nhất. Bạn có thể khiến bầu không khí trở nên thú vị hơn với những câu chào hỏi thăm, kể những câu chuyện vui hay có những trò chơi mang tính kết nối.  

Bí mật giúp tạo động lực và tăng năng suất cho nhân viên!

Giao tiếp tích cực được xem là chìa khóa quan trọng để bạn có thể nắm bắt tốt những mong muốn của nhân viên, thấu hiểu họ để đưa ra những giải pháp tạo động lực tốt nhất. Hãy dành ra một khoảng thời gian ngắn mỗi ngày để chia sẻ, trao đổi với nhân viên; thảo luận để nhận thấy mối quan tâm và những vấn đề nào họ đang gặp phải. Mọi người giao tiếp tại nơi làm việc và đó có lẽ là điều dễ dàng nhất bạn có thể làm với nhân viên của mình. 

Công nhận thành tích, khen thưởng và đưa ra sự khuyến khích về động lực

Mọi người đều muốn được công nhận cho những gì họ đã làm; bất kể đó là cho một công việc hoặc thành tích cá nhân. Việc được thừa nhận năng lực từ người quản lý sẽ có ý nghĩa rất lớn đối với nhân viên. 

Bí mật giúp tạo động lực và tăng năng suất cho nhân viên!

Một số nhân viên có thể thích sự khuyến khích về hiện kim; trong những trường hợp này, cách tốt nhất là ghi chú những ưu đãi đó vào ngân sách hoặc lập quỹ cho họ. Bạn cũng có thể tạo ra một số biện pháp để theo dõi hiệu suất làm việc của nhân viên. Tuy vậy, không phải lúc nào phần thưởng tiền tệ cũng quan trọng vì đôi khi, một cái ôm, một cái bắt tay, một ánh nhìn hay một nụ cười đều là sự động viên, sự ghi nhận đặc biệt đối với những cố gắng của nhân viên trong công việc. 

Đưa ra sự khuyến khích là cách tạo động lực tuyệt vời nhất không những giúp nhân viên yêu công việc của mình, tự hoàn thiện mình trong kỹ năng, tác phong làm việc chuyên nghiệp, mà còn phát triển hóa mô hình hoạt động lâu dài của công ty.

Tạo mục tiêu phát triển cho nhân viên

Các nhà quản lý nhân sự nên đảm bảo rằng công ty có tầm nhìn và kế hoạch phát triển cho chính tổ chức/doanh nghiệp và mỗi nhân viên. Nhân viên họ cần một người lãnh đạo có thể đánh giá, nhận xét và định hướng phát triển lâu dài cho họ. Do đó, nhà quản lý nhân sự cần thiết lập những lộ trình nghề nghiệp vì đó là cơ sở giúp nhân viên có thể theo dõi, tự nhìn nhận khả năng và phát huy tối đa những nỗ lực của bản thân. 

Bí mật giúp tạo động lực và tăng năng suất cho nhân viên!

Ngoài ra, việc tạo mục tiêu phát triển còn là cơ hội giúp nhân viên bộc lộ sức sáng tạo để thực hiện các công việc. Họ biết được mình sẽ phải làm gì, mình sẽ trở thành ai từ đó có động lực mà phấn đấu. Đồng thời, điều này cũng tạo ra sự cam kết đồng hành trong hành trình tạo ra các giá trị cho một tổ chức/doanh nghiệp.

Có thể bạn quan tâm:

Xem thêm việc làm Developers hàng đầu tại TopDev

Trong thế giới lập trình game, sáng tạo là điều không giới hạn – CEO của WOLFFUN GAME, Nguyễn Đình Khánh

Lập trình game là một trong những lĩnh vực còn khá non trẻ tại Việt Nam. Nhưng không vì vậy mà thiếu đi nhiều công ty game làm nên tên tuổi của mình tại Việt Nam cũng như quốc tế, Có thể kể đến nhiều cái tên như Athena (chuyên sản xuất dòng game giải đố), Amanotes (game âm nhạc), TopeBox (game phổ thông), DivMob (game chiến đấu thời gian thực) hay WolfFun (game đấu trường trực tuyến)… đều được giới chuyên gia trong lĩnh vực game đánh giá là những công ty vẫn đang kiếm doanh thu khủng từ thị trường nước ngoài.

Trong chuyên mục “Chuyên gia nói” tuần này, hãy cùng TopDev trò chuyện cùng anh Nguyễn Đình Khánh – CEO và Founder của WOLFFUN Game. Có thể nói đây là lần đầu tiên trong lịch sử có công ty phát triển game độc lập và có quy mô nhỏ tại Việt Nam đã được Google mời và vinh danh giữa hàng loạt các nhà sáng lập Game khác tại sự kiện G star Busan năm 2018. Trước khi đến với thành công của WOLFFUN, ít ai biết anh Khánh đã từng khởi nghiệp 3 lần và đều không thành công. Đâu là nguyên do và bài học anh Khánh rút ra từ những lần khởi nghiệp trước đây là gì? 

Chào anh Khánh, trước tiên anh hãy chia sẻ cơ duyên giữa anh và mảng game, có phải do thích chơi game mà theo đuổi ngành này?

Có thể nói đây là một cái duyên. Cũng như những startup khác, VN mình nổi tiếng về ecommerce, app. Hồi xưa tôi cũng đã khởi nghiệp ở những công ty như vậy rồi. Cũng ecommerce, outsourcing, app, tôi cũng theo hết. Tôi đến với Game cũng vì có một người nhân viên trong công ty của tôi nghỉ. Tôi nói chuyện với bạn kỹ thuật đó thì bạn ấy nói là “em đi làm game, bởi vì em thích làm game”. Từ lúc đó, tôi mới chú ý và bắt đầu tìm hiểu về mảng này làm ra sao, có khó hay không. Thì một đặc tính của game là có thể tạo cái nguồn tiền nhanh, nghĩa là khi em ra game, em có thể thu tiền nhanh hơn ứng dụng web hoặc app. Thì đó là lý do tôi bắt đầu nghiên cứu và làm game thử.

Nếu em nói ví dụ từ việc mua skin thì nguồn tiền sẽ hơi chậm. Việc làm một game như Liên quân thì tốn khoảng 3 năm cho đến 5 năm tùy vào team. Nhưng đối với game giống như của Nguyễn Hà Đông – Flappy Bird em chỉ tốn khoảng 1 đến 2 tuần để làm, sau đó em bỏ lên store.

Một điểm thứ 2 nữa là tại thời điểm 2014, App Store bắt đầu marketing mạnh, thì đó là một trong những platform giúp cho mình tiếp cận với thế giới rất là nhanh chóng. Nghĩa là mình có thể bán hàng xuyên lục địa. Ví dụ như khi mình làm web thì việc mà mình phát triển một trang web hấp dẫn người khác vô web mình coi khó khăn hơn rất nhiều so với việc bỏ một game lên store thì công việc mang game đi khắp thế giới là công việc của Google. Điều này giúp các developer tiết kiệm được công sức để marketing rất là nhiều.

Tất nhiên bây giờ thì không phải vậy, có rất nhiều yếu tố mà nhà phát triển game phải để tâm đến. 

Tìm việc làm Game đãi ngộ tốt trên TopDev

Trong thế giới lập trình game, sáng tạo là điều không giới hạn - CEO của WOLFFUN GAME, Nguyễn Đình Khánh

Xu hướng sắp tới của ngành Game là gì? Theo anh một nhà làm game có còn được gọi là “nhà sáng tạo game” nữa không khi mãi chạy theo xu hướng và ép buộc bản thân không làm theo thế mạnh của mình?

Về xu hướng chơi game, bản thân nó từ trước đến nay có rất nhiều thể loại, rất nhiều kiểu. Tuy nhiên thế giới hiện nay là Thế giới kết nối. Việc mạng hay các thiết bị đã tốt hơn rất nhiều so với khi xưa. Thậm chí, Google còn có một số platform mà em không cần máy mạnh em vẫn có thể chơi được game rất mạnh, bởi vì nó xử lý trên cloud và chỉ trả về kết quả thôi. Và tôi nghĩ cách chơi game cũng sẽ thay đổi. Bởi vì mạng quá mạnh nên những game online sẽ xuất hiện nhiều hơn. Thì em biết là game online thì nó thiếu meta, có nghĩa là nội dung được sinh ra bởi người dùng sẽ gây hấp dẫn và hay hơn những game mình đã sắp đặt sẵn như game offline. Theo tôi, xu hướng chơi game online sẽ càng bùng nổ hơn nữa, nhất là thể loại esport. Bởi vì mình chơi online thì mình phải cạnh tranh với nhau, nhưng cạnh tranh mà không công bằng thì mình không vui, cho nên mình phải cạnh tranh công bằng. Thì đó gọi là esport.

Về vế thứ 2, thì thực ra cụm từ Nhà sáng tạo game cũng có một ý nghĩa tương đối, với tôi. Ví dụ như khi em làm một cái bàn tròn và người khác làm nó hình vuông thì em có gọi đó là sáng tạo hay không? Có thể đối với một số người, đó là sáng tạo nhưng đối với một số khác thì đó cũng chỉ là cái bàn thôi, thì đâu có gì sáng tạo, đúng không?

Trong việc sản xuất một phần mềm hay một game, chỉ cần khác nhau một vài mechanic (cơ chế) thì nó đã là một cái game hoàn toàn khác rồi. Tuy có thể em copy hình người ta lại, nhưng có thể là em sẽ không làm giống người ta được. Trong ngành tụi tôi có một quy định là như thế nào gọi là copy/clone. Đó là khi em làm lại một game hoàn toàn giống, xác suất, số lượng mechanic giống nhau khoảng 90% – 100%. Ví dụ một game làm 2 giây thì nhảy, 1 giây thì rớt xuống hố và mình cũng 2 giây nhảy, 1 giây rớt xuống thì đó gọi là clone. Clone tính cả khi làm giống y nhau về âm thanh, đồ họa,… thì ngoài những game clone như vậy ra thì tôi thấy đa số có thể gọi là sáng tạo.

Theo anh, đâu là điều thú vị giữa Game Developer – Lập trình game so với những lập trình viên khác khác?

Điểm khác giữa một game dev với một dev bình thường đó là em tạo ra một sản phẩm thấy nó nhúc nhích được. Rõ ràng khi mình làm game, ngay lập tức nhân vật sẽ chuyển động, có gameplay lên rồi nhìn nó thấy thú vị hơn rất nhiều. Còn app thì vẫn lên nhưng nó không động bằng trong game. Thì đó là cái khác biệt có bản làm cho những người làm game như tôi thấy cái công việc nó thú vị.

Cái thứ 2 nữa là, trong thế giới game là thế giới sáng tạo gần như không giới hạn. Em có thể nghĩ ra nhân vật nó như vậy, gameplay nó như vậy hoặc là những thứ rất kì cục em vẫn có thể đưa vô game được. Còn đối với app hay web developer khác thì khó thể làm được những chuyện này. Nó giới hạn hơn.

Và game thì nó cũng giống như điện ảnh vậy đó, em làm một cái game tương tự giống như nó mang cái nhiều rất nhiều yếu tố ở trong đó để tạo một cái game. Cho nên lần này tạo game, nó khác với lần sau tạo game nên gây sự hứng thú. Người của mình nó sẽ thấy chán nếu như mình làm cái gì đó lặp đi lặp lại. Nhưng trong sản xuất game thì cái game thứ 2 nó sẽ gần như là nó khác cái game thứ nhất. Kể cả là em đi làm lại cái game đầu tiên. Cho nên tôi nghĩ nó là một cái điểm mà giúp cho game developer cảm thấy motivate hơn, hứng thú hơn trong công việc.

*Tố chất gì cần phải có để đảm nhiệm được nhiều công việc như anh (CEO, Founder, Game developer) 

Thì tôi bốc ra từng cái title nha. Ví dụ em làm CEO, giống như mọi người, thì tôi nghĩ giống như các CEO khác, là cũng phải tìm cách cho công ty hoạt động, tìm cách công ty chạy, cách phát triển kinh doanh, chiến lược đi, lãnh đạo mọi thứ,… thì nó giống như những CEO khác. Tuy nhiên đối với những công ty như tụi tôi thì tôi đảm nhiệm một vai trò sản xuất nữa, là game design hoặc là project owner khác, trong đó công việc của tôi cũng giống như những project owner khác thì tôi nghĩ cái tố chất cần ở đây là cái đam mê công việc là chính. Khi có đam mê thì em sẽ tìm mọi cách để em làm cho kiến thức của em nhiều hơn, làm cho em quen việc hơn, còn nếu em không có đam mê thì em làm giống như bị sai khiến vậy, nó sẽ không hiệu quả. Thì đó là cái tố chất đầu tiên mà tôi nghĩ là nên cần.

Còn để làm CEO hay Founder thì nó cần có những cái tố chất nhất định như về leadership em phải biết được, em phải trao quyền tốt,.. nói chung rất nhiều thứ khác.

Được biết trước khi thành công với WOLFFUN, anh đã va vấp ở 3 lần khởi nghiệp trước. Anh có thể chia sẻ thêm về câu chuyện khởi nghiệp của mình?

Kinh nghiệm của tôi thì tôi nghĩ cũng sẽ giống như một số anh em đã va vấp khác. Thì tôi nghĩ thất bại là cái hay nhất, vì khi em thất bại em sẽ nghiệm ra một bài học thật là đau. Mà cái bài học đó dù em đọc trên sách có thể em hiểu, nhưng nó sẽ không đau được như khi em startup thất bại, nên là cứ làm thôi. Tôi nghĩ đó là cái thứ mà quan trọng nhất trong startup, đó là làm liều đi. Đó là cái quan trọng nhất.

Cái thứ 2 đó là khi mỗi cái startup nó thất bại thì phải xem vì sao nó thất bại để lần sau mình tránh. Nếu mà cứ thất bại xong rồi mình làm tiếp rồi cứ thất bại y chang như vậy thì nó không ổn. Nhưng tôi nghĩ con người của mình nó hay lắm, nó lên. Không có ai mà thất bại rồi nó làm y chang như vậy đâu. Tôi nghĩ là sau bao lần thất bại mình lên được, cái thất bại là điều quan trọng thứ 2.

Cái thứ 3 là sau những lần thất bại thì tôi nghiệm ra rằng là để mà hạn chế cái thất bại thì cái team của mình là cái thứ quan trọng thứ 3 mà để xây dựng một cái business bền vững hơn. Tại vì khi mà em có nhiều người tốt đi chung với em thì họ sẽ cản em bớt những cái tính liều lĩnh chẳng hạn hoặc những cái quyết định mà nó hơi mang tính cá nhân, nó hơi bất đồng, nó hơi không chính xác hoặc là kiểu kiểu như vậy.

Qua mỗi cái startup thất bại, tôi học mỗi bài học khác nhau. Bài học đầu tiên là do tôi chọn co-founder không đúng, cho nên sau này a chọn co-founder cẩn thận hơn. Bởi vì lúc mới khởi nghiệp thì tôi khởi nghiệp với sếp, nhưng mà 2 người lại không hợp nhau và cách biệt nhau về nhiều thứ cho nên chưa hòa hợp. Cái công ty thứ 2 lại chết về thiếu hiểu biết về ngành, đó là E-commerce. Khi tôi tham gia một ngành thì có 2 kiểu người mà tôi nghĩ rằng tôi là kiểu người thứ 2. Kiểu người thứ nhất là phải suy nghĩ đắn đo rất nhiều thứ rồi mới bắt tay vào làm, thường kiểu người như vậy sẽ rất sợ, sẽ nghĩ những tình huống xảy ra và họ tránh được.

Nó cũng có những điểm tốt. Còn kiểu người thứ 2 là kiểu người không suy nghĩ gì hết, cứ nhào vô làm. Cách đây khoảng 10 năm trước, tôi là ông thứ 2, tức là không suy nghĩ gì hết, cứ nhào vô làm thì mới biết là ở trong này nó không có như vậy, nó cần phải đòi hỏi là cái nguồn lực như vậy như vậy, bởi vì e-commerce thì em biết rồi, nó không có easy như người khác nhìn vào. Dù mình có biết trước tương lai người ta có thể mua hàng online rất là nhiều nhưng khi mà vô làm thực tế thì nó hoàn toàn khác. Bài học thứ 3 mà tôi rút ra được của cái startup tiếp theo thứ 3 đó là ảo tưởng.

Hồi xưa tôi cũng tốn 1 năm để làm một cái ứng dụng – cũng là một mxh nhưng khác biệt ở việc mix giữa google và facebook. Thì tôi ảo tưởng là cái ý tưởng của tôi nó hay như vậy, ai cũng nói cái ý tưởng đó khá là hay thì khi mà tôi launch cái ứng dụng đó ra thì sẽ có rất nhiều người dùng cái ứng dụng đó mà tôi không cần phải marketing gì hết. Thực tế sau khi tôi ra thì có rất ít người dùng, dẫn đến là tôi hết tiền, tôi fail. Thì đó là cái chuyện tôi không chuẩn bị kỹ càng kiến thức về marketing, về kinh doanh và cái chiến lược để ra mắt cái ứng dụng, sản phẩm nó ok, thì đó là bài tập thứ 3. 

Thì sau này, sau nhiều lần ngu, lỡ dại giống như vậy thì tôi bớt dại hơn.

Anh hãy chia sẻ những project sắp tới mà WOLFFUN đang ấp ủ

Sau 3 lần khởi nghiệp thất bại, tôi cảm thấy là mình làm nhiều thứ, nhiều mảng. Trong 3 lần đó thì tôi còn đi làm thêm ở công ty Nhật. Thì tôi cảm thấy cái kết quả dường như không tốt. Cho nên tôi nghiệm ra một điều là phải tập trung. Cho nên công ty của tôi cũng hoạt động theo nguyên lý tập trung là nếu tôi làm tôi chỉ làm một cái project thôi, duy nhất và làm cho nó tốt nhất có thể.

Hiện tại, bọn tôi chỉ tập trung làm một cái game mini mobile Heroes Strike. Tương lai tụi tôi sẽ research, tìm kiếm một số cơ hội mới trong mảng game online nhưng tương lai ở đây chắc khoảng 5 – 10 năm. Bởi vì vòng đời một game mà tụi tôi nhắm đến nó tới 5 năm đến 10 năm. Và cái game Heroes Strike sau khi mà tụi tôi launch ra tụi tôi còn rất là nhiều việc trong vòng 5 năm tới để làm.

Trong thế giới lập trình game, sáng tạo là điều không giới hạn - CEO của WOLFFUN GAME, Nguyễn Đình Khánh

Nếu được thay đổi 1 điều thì điều hối tiếc mà anh muốn thay đổi nhất là gì khi khởi nghiệp?

Thường trong mọi việc tôi có một câu là “tiên trách kỷ, hậu trách nhân”, có nghĩa đầu tiên mình phải trách mình trước. Mọi cái biến cố xảy ra trong cuộc đời của mình là do mình, cách làm của mình không ổn. Nếu được thay đổi thì tôi sẽ thay đổi chính bản thân của tôi. Tôi sẽ đọc nhiều hơn và suy nghĩ nhiều hơn trước khi tôi làm một cái việc gì đó. Nhưng mà cái việc đó nó chưa chắc là sẽ tạo nên cái con người là những gì giống như ngày hôm nay của tôi, giống như bây giờ. Có thể lúc đó tôi trở thành con người rất là sợ sệt, không dám làm và thậm chí là không startup luôn. Lúc đó sẽ nghĩ: startup nhiều quá, nhiều rủi ro quá, lại vừa tốn tiền, vừa mệt thôi mình đi làm công. Đấy, thì có thể không giống như bây giờ.

Tiêu chí chọn “bạn đồng hành” của anh và WOLFFUN là gì?

Chính xác là sau này cái cách làm việc với một người đối với tôi nó cũng thay đổi rất nhiều. Hồi xưa tôi quan trọng một người về kỹ năng của họ, nhưng sau này là tôi quan trọng cái tâm của họ trong lúc làm việc, suy nghĩ của họ trong lúc làm việc nó như thế nào. Tất nhiên kỹ năng là cái chắc chắn phải có nhưng nó sẽ là yếu tố thứ 2.

Những bạn là Co-founder trong team của tôi đa số là tôi sẽ chọn và tuyển ra những người có đồng hành, có mong muốn, có cái vision, định hướng giống như tôi. Giống như tụi tôi tập leo núi, thì tôi sẽ đi cùng những người muốn chinh phục đỉnh cao, muốn chinh phục cái núi đó chứ tôi không đi với những người mà chỉ đi để xem thử coi có gì vui hay không, kiểu vậy. Lúc phỏng vấn tôi đặt ra rất nhiều câu hỏi để sàng lọc những người đó. Thì theo tôi cái tâm là thứ mà tôi quan trọng nhất.

Cái quan trọng tiếp theo tôi nghĩ đó là cái nỗ lực. Bởi thường làm game là mình đi sau thế giới. Mà mình đi sau mà mình đi với tốc độ giống như họ thì mình sẽ luôn luôn chậm hơn họ. Cho nên là mình bắt buộc phải đi nhanh hơn họ. Nhanh mà mình mới tập đi mà mình đi nhanh thì bắt buộc là mình phải tập và ngày nào cũng tập, tập nhiều hơn so với bình thường. Thì chăm chỉ là cái yếu tố tiếp theo.

Anh hãy chia sẻ các thói quen để duy trì và nâng cao kiến thức của mình

Tôi nghĩ tùy người mà họ chọn cái thói quen của mình để tốt nhất với họ. Đối với tôi thì thường tôi quan niệm là nếu mà em muốn khỏe đầu óc em phải khỏe cơ thể đã. Thể thao là việc phải làm hằng ngày. Thứ hai nữa là em tránh xa những mối quan hệ độc hại. Kể cả là trên Facebook đi chăng nữa. Thì tôi chỉ follow những người mà tôi nghĩ là tôi sẽ học được từ người đó.

Thì khi mà tôi mở Facebook thì tôi học được một cái kiến thức của cái tôi đó chứ k phải mỗi lần tôi mở Facebook thì tôi sẽ thấy một món ăn hay một bạn nào đó post hình đi du lịch. Thứ ba nữa là thường một tháng tôi sẽ dành 3 ngày và 4 ngày liên tục chỉ để đọc sách thôi. Và theo tôi quan trọng hơn nữa để rèn luyện cái não của mình là em có thể chơi một số thứ như âm nhạc, học ngoại ngữ chẳng hạn thì nó sẽ làm cho đầu óc của mình linh hoạt hơn, có nhiều lựa chọn hơn, nhiều mối quan hệ hơn khi mà nhìn một cái gì đó cái góc nhìn của mình nó cũng đa dạng hơn

Lời khuyên cho những bạn theo đuổi ngành game

Trong ngành game có rất nhiều vị trí. Tôi sẽ nói từng vị trí như sau. Ví dụ như em là developer thì em cũng học trong trường đại học bình thường thôi. Thì tất cả những kiến thức tôi học trong trường đại học nó dùng cho game nó đều hữu dụng cả. Tạo một cái game cũng giống như tạo một website hoặc một cái app về mặt công nghệ. Và trong game cũng có dùng AI, dùng data analytics, big data,… thì nó có rất nhiều cơ hội cho những bạn lập trình viên chuyên về data, chuyên về code, chuyên về hệ thống hoặc cao hơn ví dụ như là system architect, system design,… hoặc kể cả sau khi em tốt nghiệp ra em cũng làm bình thường, sẽ lên cái vị trí mà tôi nghĩ nó không quá khác biệt so với những ngành khác.

Tuy nhiên trong game có nhiều lựa chọn hơn cho một developer. Em có thể chuyển sang làm game designer nếu em cảm thấy việc code nó khá là nhàm chán. Thì trong một cái team game ngoài vị trí dev giống như mọi app hoặc web khác thì em có front-end, back-end, rồi làm về operations thì đó là vị trí dev. Thứ 2 nữa đối với artist, trong game thì có artist 2D, artist 3D rồi animation, về effect – những cái gọi là hiệu ứng, rồi sound design thì mình có những loại artist như vậy. Thì tùy vào cái mong muốn, em giỏi cái gì, em giỏi tưởng tượng em có thể phát triển kỹ năng để làm 2D artist.

Nếu em không giỏi tưởng tượng, em giỏi về khối, em giỏi vận khối và em thích 3D thì em có thể làm 3D artist hoặc em muốn làm chuyển động, làm nhân vật chuyển động thì em làm animation. Trong đó thì em có một số vai trò như làm cái sound (fx) là cái sound trong game. Thì cái đó thì tụi tôi thuê outsource nước ngoài tại do đa số người Việt phát âm tiếng tôi không chuẩn cho nên phải thuê người nước ngoài làm việc đó; hoặc làm effect. Thì đó là vị trí thứ 2.

Vị trí thứ 3 là game design – là cái thứ khó nhất trong game. Nó ảnh hưởng tới sự sống còn của một cái game. Game design ở Việt Nam thì không có thường dạy cho nên đa số các bạn vừa chơi game rồi tự học, có thể lên mạng học rồi tự làm, sai rồi sửa. Thì game design bình thường em phải mất khoảng 5 năm để làm được một cái game là ok và đó là vị trí khó nhất. Tại game design em phải học rất là nhiều về tâm lý, về marketing về dự án về cách phát triển phần mềm thậm chí em cũng phải biết sơ sơ code để em có thể nói chuyện được với code, em biết sơ về animation,… mọi thứ ở trong game. Thì đó là cái vị trí mà tôi cho là nó là khó nhất.

Các doanh nghiệp vẫn gặp khó khăn trong việc tuyển dụng các lập trình viên phù hợp, đặc biệt trong ngành Gaming thì theo anh đâu là vấn đề?

Chuyện “nhân tài như lá mùa thu” tôi nghĩ không chỉ ở Việt Nam mà còn ở những nước khác. Riêng ngành Game còn có đặc thù là lập trình viên phải yêu thích và muốn làm trong ngành Game nữa, đây là lọc thêm một bước filter nữa cho nên càng khó hơn. Theo tôi, thứ cản trở nhiều nhất là tư duy của thị trường Việt về Game không tốt, vì trẻ em hay lạm dụng Game và phụ huynh không thích chuyện này.

Nếu mình không kiểm soát tốt mà chơi game từ ngày này qua tháng nọ, thậm chí có người đi cướp tiền để chơi game thì sẽ tạo hiệu ứng xã hội không tốt cho game. Từ đó chỉ cần nói đến game là người ta sợ, mà không thấy được lợi ích của game, dẫn đến giới hạn những kỹ sư tiếp cận đến ngành game. Đối với artist/3D thì không như vậy. Ban đầu họ thích vẽ nên có thể vẽ trong phim, trong game và cả những chỗ khác. Cho nên cái việc chuyển qua game không khó bằng developer.

Anh nghĩ là thị hiếu chơi game của người già trẻ lớn bé khác nhau như thế nào?

Thật ra tôi cũng không nắm số liệu về chủ đề này, tôi chỉ đọc báo cáo từ những nguồn như Google, hay những trang chuyên về thống kê về game. Thì vấn đề khá khó nói vì còn tùy thuộc vào văn hóa, ví dụ ở Mỹ, văn hóa chơi game của người nhỏ, người lớn và người già khác nhau, thể loại game họ chơi cũng muôn hình vạn trạng. Theo thống kê thường người trẻ trung học chiếm số lượng đông đảo nhất, và tỷ lệ cao là game đối kháng như Fortnight, PUBG,… Sau đó là đến đối tượng trung niên (sau khi ra trường), thời gian chơi game ít lại thì họ có xu hướng chọn game giúp họ giải trí ngắn hạn, nhất là những game mobile.

Tiếp đến là những người thành công, cách chơi game cũng khác. Họ có thể mua một cái game chất lượng cao, hay mua rất nhiều vật dụng chơi game. Khi chơi game họ cảm thấy vui với việc mua hay còn gọi là “pay to win”. Sau đó là những người lớn tuổi, thường chọn những game giúp đầu óc tỉnh táo, như game xếp hình hay về giải đố, trang trí nhà cửa. Mình có thể thấy những game này khá chán vì làm đi làm lại 1 công việc nhưng đối với người lớn tuổi lại thích. Còn lớn hơn nữa thì bất kỳ game nào, miễn sao mang lại cho người chơi cảm giác vui vẻ ở tuổi già. Đó là những gì tôi tìm hiểu về các đối tượng chơi game, và đây chỉ là thống kê ở game mobile.

Các bước ASO (App Store optimization) cơ bản trên appstore cho game như thế nào?

Lúc trước tôi cũng làm SEO thì tôi thấy SEO và ASO khá tương đồng. Ví dụ khi em đi mua hàng, giả sử giá cả không phải là vấn đề thì tiệm nào được trang trí đẹp và bắt mắt, khách hàng sẽ vào tiệm đó. Icon là thứ đầu tiên mà user click vào. Icon phải làm sao đúng với thị hiếu, nhất là tập người dùng mình hướng tới (ví dụ người già, người trẻ, trẻ con đều khác nhau), mình phải biết đối tượng chơi game của mình thuộc lứa tuổi nào để thiết kế icon cho phù hợp, đặc biệt là phải A/B Testing. Tuy nhiên mỗi nước sẽ khác nhau, ví dụ như Mỹ, Hàn Quốc thích kiểu khác, mình có thể tạo ra nhiều icon. Sau khi vô được game app rồi, giống như em vào một cái tiệm thì cách bài trí trong tiệm sẽ hấp dẫn em. Cách bài trí tôi hay gọi là screenshot trong ứng dụng, nói về game của em là ứng dụng gì, càng rõ càng tốt, phải dự đoán user mong muốn điều gì ở một cái game.

Tùy vào mức độ hiểu user như thế nào mà mình đưa những đặc điểm này lên screenshot càng ngắn gọn, dễ hiểu và đẹp sẽ càng hấp dẫn user. Việt Nam thường thích kiểu hình, nhân vật bay bổng, người Nhật thì thích vừa chèn ảnh, emo icon, chèn hình, người Mỹ thì đơn giản, tập trung, rõ ràng. Tùy thị hiếu mà mình thiết kế bộ screenshot cho từng nước. Ngoài ra localized cũng rất quan trọng, không phải tất cả user đều biết tiếng Anh, một số nước mình phải localized ngôn ngữ của họ luôn. Sau khi localized, mình cũng phải A/B Testing như icon, mình tạo rất nhiều bộ và chọn bộ nào tốt nhất. 

Sau đó tới phần video, ở một số quốc gia có internet mạnh, người dùng sẽ xem video để xem game này, ứng dụng này nói về cái gì, chơi như thế nào. Một số bạn Marketing hay làm lố hơn, điều này vô tình làm sai tệp khách hàng. Họ tưởng tượng game thế này nhưng khi vào game nó lại khác thì người dùng sẽ xóa game đó.

Về cách phân phối, tạo traffic, mua traffic, user acquisition, retention, gamification, balancing trong game, anh nghĩ như thế nào?

Đây là câu hỏi rất rộng mà không thể trả lời hết trong một buổi talk nên tôi sẽ tập trung một số điểm. Mua user như thế nào? Bây giờ cách làm game không giống ngày xưa. Cách đây 10 năm một cái game bỏ lên App Store thì sẽ có user ngay, vì họ không có nhiều lựa chọn. Thị trường mobile lúc này cũng đi lên, nên marketing khá dễ dàng. Bây giờ ai cũng biết đây là nguồn lợi nhuận cao nên rất nhiều developer nhảy vào, cùng với các hãng lớn. Game bắt đầu nhiều lên, hiện giờ trên App Store và Google đã trên vài triệu game. Sự cạnh tranh rất lớn, lúc này người dùng có nhiều lựa chọn. Chất lượng là thứ quan trọng nhất. làm cho user ở lại lâu hơn và chơi nhiều hơn. Đây là khó khăn nhưng đồng thời là thách thức với ngành game hiện giờ.

Về traffic, đa số mọi người phải chạy số vì traffic không còn organic nữa, như là Google, Facebook, TikTok. Cách khác là sau khi có một game mới, mình promote trên một game khác sẵn có. Đặc biệt với những game đối kháng mình có KOL (blogger, người nổi tiếng hay streamer) cũng là nguồn marketing. Nhưng nguồn này không phải game nào cũng làm được. Với game mobile họ có thể stream những trận đánh của họ cũng là cách marketing mới, hiệu quả và phổ biến.

Vấn đề bản quyền và chi phí mua của các nguồn tài nguyên sử dụng trong game sẽ như thế nào?

Đối với các bạn indie hay các bạn mới làm game cần lưu ý, về hình ảnh google kiểm tra tự động bằng thuật toán. Nếu vi phạm họ sẽ ban ngay khi vừa up lên, nên phải cẩn thận. Lúc mới khởi nghiệp mà không có artist, mình có thể thuê freelancer hoặc chấp nhận game xấu hơn, điều này không sao miễn sao game hay. Chứ nếu đã vi phạm thì rất khó làm lại vì bị xóa mất account. Về âm thanh thì dễ hơn, tuy nhiên với bản nhạc ai cũng biết thì Google cũng có thuật toán có thể dò ra được. Tôi nghĩ mình nên “cây nhà lá vườn”, nó có thể dở hơn một chút nhưng nó vẫn an toàn hơn.

Sau khi đã có doanh thu các bạn có thể thuê freelancer để làm, có điều kiện hơn nữa thì thuê người vào team làm luôn.

Nhắc đến indie thì anh có giải pháp hay lời khuyên nào dành cho các Studio nhỏ để phát triển ngành game tại Việt Nam hay ngành Dev Game nói chung được không?

Với góc nhìn của mình, tôi nghĩ các bạn startup ngành game nếu xem đó là thú vui thì làm sao cũng được, còn nếu xem nó là business như tôi, thì bạn phải có chiến lược rõ ràng, và phải biết chiến lược mình đi như thế nào để khỏi gặp nhiều chướng ngại. Thứ hai các bạn nên kết nối với nhau để học từ nhau để giảm thiểu rủi ro khi mình đi một mình. Tôi thấy cộng đồng developer Việt Nam không mạnh như ở Ấn Độ hay là Mỹ.

Tôi cũng có tạo những group để mọi người trao đổi nhưng tôi thấy những bạn thành công lại không hay share các kinh nghiệm của mình. Nên tôi nghĩ để ngành game thành công thì nhiều người phải share bài học của họ. Có thể bài học đó còn sai nhưng những người khác có thể học hỏi thêm điều gì đó, hoặc những bạn chưa có kinh nghiệm thì có thể tìm và nói chuyện để học hỏi được nhiều hơn. Đặc biệt ngành game rất rộng, không phải một bài học có thể fix được hết, nên mình phải tìm đúng người làm thể loại giống mình thì họ mới có thể giúp mình được. Ví dụ như em định làm game Match-3 thì nên tìm người Match-3 như candy crush, chứ không phải là tôi vì tôi không có kinh nghiệm làm game Match-3.

Trong thế giới lập trình game, sáng tạo là điều không giới hạn - CEO của WOLFFUN GAME, Nguyễn Đình Khánh

Xin cảm ơn anh Khánh đã chia sẻ hết sức chi tiết về ngành Game cùng cơ hội và những thách thức. Hy vọng WOLFFUN sẽ sớm đạt được mục tiêu mà anh và cộng sự đặt ra. Ngoài ra, các bạn độc giả đừng quên đón đọc Chuyên giá nói vào kỳ tiếp theo để trò chuyện và  học hỏi kinh nghiệm từ các “cao thủ” trong ngành nhé!

Xem thêm các vị trí tuyển dụng từ WOLFFUN Game tại đây

Sử dụng localStorage trên website như thế nào

Kiến thức căn bản sử dụng localStorage để lưu thông tin cần thiết xuống trình duyệt
  Local Storage, Session Storage và Cookie
  Javascript: Sự khác nhau giữa Cookie, localStorage và sessionStorage

Vì sao chúng ta cần localStorage

Nói đến HTTP, nó là dạng kết nối stateless, nghĩa là khi đóng một ứng dụng web, lần truy cập sau mọi thứ bị reset lại như ban đầu.

localStorage đơn giản là nó giúp dev chúng ta lưu lại một vài thông tin ở phía trình duyệt của user, để lần sau truy cập ta có thể truy xuất các thông tin này.

Cookie là một dạng file text lưu trên máy tính của user, link đến từng domain. Một vài giới hạn của cookie

Tất cả request đến domain, đều sẽ nhét cái cookie này vào trên header

Tối đa có 4KB dung lượng

Sử dụng local Storage trên trình duyệt hỗ trợ HTML5

Cú pháp để set, get, delete giá trị của localStorage

// set
localStorage.setItem(‘tentui’,’luubinhan’);

// get
Var tentui = localStorage.setItem(‘tentui’);
// -> luubinhan

// delete
localStorage.removeItem(‘tentui’);

Làm việc trên object

Vì khi lưu chúng ta chỉ có thể đưa string vào trong localStorage, để đưa một object

Làm việc trên object

Var user = {
	Name: ‘an’,
	Age: ‘18+’,
	Gender: ‘superman’
}
localStorage.setItem(‘user’, JSON.stringify(user));
Console.log(JSON.parse(localStorage.getItem(‘user’));

Thông tin lưu xuống localStorage

  • Để cache những dữ liệu lớn, tốn thời gian để load.
  • Lưu lại trạng thái của giao diện user đã custom, có thể lưu cả một đoạn HTML xuống localStorage

Có thể bạn quan tâm:

Xem thêm các việc làm Web Developer hấp dẫn tại TopDev

TopDev via Vuilaptrinh

Giải thích Flux Pattern theo phong cách John Wick

Tác giả: Lưu Bình An
Điểm lại các khái niệm trong Flux action, dispatch, store, điều kiện tiên quyết đề đọc bài này là nên xem lại John Wick

Vấn đề

Trước tiên chúng ta cần biết Flux giải quyết vấn đề gì. Flux là một pattern để xử lý luồng dữ liệu trong ứng dụng. Flux và React được sinh ra và lớn lên dưới ngôi nhà Facebook. 2 đứa chúng nó thường đi cùng nhau, chứ không phải dính vào nhau như hình với bóng

Một trong những ví dụ phổ biến khi nói đến Flux là vòng lặp của tính năng notification. Khi đăng nhập vào Facebook, bạn thấy một thông báo mới trên icon cái chuông huyền diệu, một khi click vào cái chuông này, toàn bộ thông báo sẽ ko còn nằm trong new message nữa. Một vài phút sau, khi nhận được thông báo mới, cái chuông lại rung lên, báo bạn biết có thông báo mới, và cứ thế, vòng lặp cứ tiếp tục.

Với kiểu thiết kế Model-View

Các model sẽ nắm giữ dữ liệu và truyền dữ liệu này xuống các cục view -> nơi sẽ render, hiển thị dữ liệu này.

User tương tác thông qua view, view đôi lúc sẽ cập nhập lại dữ liệu của model, và đôi khi model này cần thay đổi dữ liệu trên model khác. Hơn nữa, nhiều khi một thay đổi của user kéo theo một chuỗi các thay đổi khác, có khi nó là một async. Tưởng tượng như đánh trái banh bàn, bạn ko thể biết được trái banh nó sẽ đập vào đâu hết.

Khi ấy bạn sẽ không biết được dữ liệu bị rơi rớt ở đâu.

Việc làm React trong tháng mới nhất

Giải pháp của Facebook: luồng dữ liệu một chiều

Đội ngũ Facebook giải quyết bằng một kiến trúc khác, luồng dữ liệu sẽ đi một chiều duy nhất, một khi cần thêm dữ liệu mới, luồng lại đi từ điểm xuất phát. Và họ gọi kiến trúc đó là Flux

Nhìn vào cái hình trên, bạn sẽ không thể cảm được của kiến trúc này ngay và luôn, không đọc tài liệu về Flux, có thể bạn sẽ chẳng hứng thú vì với nó.

Cùng đi chi tiết từng khái niệm một. Hình dung tổ chức hội bàn đào trong John Wick, với những nhóm nhân vật khác nhau, nắm giữ những vai trò khác nhau.

Giới thiệu các vai chính

Bọn action creator

Giải thích Flux pattern

Nhiệm vụ của bọn này là tạo ra action, tất cả những thay đổi, tương tác phải tới gặp bọn này. Nó giống như bọn ngồi điều hành điện thoại trong phim John Wick, những đứa khác tới đây, phát đi 1 thông điệp, action creator sẽ “định dạng” lại thông điệp đó bằng một mật mã mà tất các những đứa khác nằm trong hệ thống hiểu được.

Giải thích Flux pattern

Thông điệp được gửi đi bao gồm: kiểu thông điệp (type) và nội dung chính của thông điệp (payload). Trong đó kiểu thông điệp là một hằng số đã được định nghĩa trước đó.

Tác dụng phụ của một hệ thống mà toàn bộ kiểu thông điệp đã được định nghĩa sẵn, lính mới vào chỉ cần mở file này ra là biết được ứng dụng đang làm, sẽ có những tình huống nào sẽ làm thay đổi trạng thái dữ liệu.

Bọn dispatcher

Giải thích Flux pattern

Trong John Wick, nói chung các bạn nên xem John Wick trước khi đọc bài này đó, bạn trực điện thoại nhận tin nhắn, format tin nhắn xong, bạn sẽ hét lên cho các bạn đứng trực chổ tổng đài điện thoại. Bạn trực điện thoại này biết danh sách các đầu cầu (store) cần gửi thông báo đến.

Quá trình này được thực hiện một cách tuần tự, không chen lấn, không xen ngang, nếu mỗi đầu cầu cần ràng buộc về thứ tự nhận thông tin, chúng ta có để anh dispatcher này quản lý.

Anh Dispatcher trong Flux sẽ khác với dispatcher trong các kiến trúc khác. Thông tin luôn được gửi đến hết các đầu cầu bất kể nó là thông tin gì. Nghĩa là mỗi đầu cầu không chỉ đăng ký một kênh thông tin nhất định, nó lắng nghe toàn bộ thông tin được gửi đi, chuyện nó quan tâm và xử lý trên từng thông tin nào là nó tự quyết định, giống như chú Bowery King nhận được yêu cầu truy sát John Wick, nhưng anh nhận tin rồi ko làm gì cả.

Bọn đầu cầu Store

Gọi là đầu cầu thì cũng chưa đầy đủ, ngoài là nơi tiếp nhận và thực thông tin, nó còn là nơi chứa toàn bộ dữ liệu của ứng dụng, nguồn tiền của 1 tổ chức, mọi luật lệ, logic của dữ liệu sẽ nằm ở đây.

Giải thích Flux pattern

Anh Store này như chú Bowery King, khi muốn anh ấy làm gì đó, chuyển tiền, nhận tiền, đóng tiền thì bạn buộc phải làm đúng quy trình từ trên xuống dưới action creator -> dispatcher

Bọn View

Giải thích Flux pattern

Nhận dữ liệu, thay đổi thông tin hiển thị, tiếp nhận dữ liệu từ user, đưa ngược lên lại tổ chức là công dụng toàn bộ của bọn này.

Tổng hợp lại chúng ta có sơ đồ vận hành của tổ chức này như sau

Trong hình minh họa trên, còn một đứa nữa đứng giữa View và Store, được gọi là controller view, một dạng của người đưa tin, nó sẽ nhận thông báo từ đầu cầu store khi có dữ liệu thay đổi, rồi mới đưa xuống view

Khi user gửi đi một thông báo đến View

Thông tin được gửi lại action creator

Bài viết gốc được đăng tải tại Vuilaptrinh

Gặp gỡ Ying Zhang – Engineering Manager và Research Scientist tại Facebook

Xem thêm các việc làm software engineer tại TopDev

5 điều cần nhớ khi làm việc với service worker

Năm điều nhỏ nhỏ, nhưng rất hay ho cần thiết, cần biết

Nếu chưa biết Service worker API là gì, bạn hãy vào đọc lại link mình đã gắn sẵn

Đặt file service worker trong thư mục root

5 điều cần nhớ khi làm việc với service worker

Đừng thấy file service worker là js mà bạn đi bỏ nào trong thư mục js hay scripts, bởi vì file service worker bỏ vào thư mục nó sẽ bị giới hạn hoạt động ở trong thư mục js đó thôi. Nghĩa là nó chỉ can thiệp được khi user truy cập www.yoursite.com/js/, tất cả request từ www.yoursite.com hay www.yoursite.com/news nó sẽ cho qua.

Tuy nhiên, chúng ta có thể thay đổi scope này

navigator.serviceWorker.register('/sw.js', {
 scope: '/'
});

Nhưng thật lòng mà nói, bỏ luôn trong thư mục root có phải dễ chịu không, nó tự động handle toàn bộ request ở cả site luôn cho khỏe

Sử dụng Panel Application trên Chrome Dev Tools

5 điều cần nhớ khi làm việc với service worker

Trên tab này chúng ta sẽ biết được mình đã đăng ký file service worker thành công chưa, giả lập offline, bypass cái service worker hoặc gỡ bỏ luôn.

Không sử dụng Hard Reload

5 điều cần nhớ khi làm việc với service worker

Một trong những thói quen của chúng ta là dùng “Hard Reload” hay “Empty Cache and Hard Reload” trên trình duyệt để xem những thay đổi mới nhất. Tuy nhiên là khi có service worker rồi, nó sẽ tự động bypass vụ “Hard Reload” này. Tip tiếp theo sẽ chỉ bạn cách làm ngay thôi

Bật “Update on Reload”

5 điều cần nhớ khi làm việc với service worker

Để đảm bảo luôn luôn lấy file mới nhất, trên tab Application check vào ô Update on Reload là xong. Như vậy thì khi thực hiện reload trang (reload bình thường luôn ấy) trình duyệt tự động update cái service worker luôn.

Còn muốn thực hiện manual, click vào link Update bên dưới màn hình này.

Inspect và manual delete cache

Cuối cùng, cũng hay, là trên tab Application cho phép chúng ta xóa chỉ định cụ thể file cache nào muốn xóa. Cột bên trái, mục Cache Storage, click nút expand, bạn sẽ thấy danh sách cache object đang được lưu trên trang này

5 điều cần nhớ khi làm việc với service worker

Muốn xóa? Đơn giản click phải chọn Delete

5 điều cần nhớ khi làm việc với service worker

Làm quen khái niệm CORS của Web

Bài này khá căn bản và cần thiết cho bạn nào chưa biết gì về CORS, nghe ai đó nói về từ khóa ghê gớm này mà ko biết nó là gì, không để cập đến vấn đề setup làm sao để chạy CORS trên server – vì mình ko biết code phía server đâu

 

Cross-Origin Resource Sharing (CORS) là một cơ chế sử dụng thông tin trên HTTP header để báo với trình duyệt, cho phép ứng dụng web chạy từ nhà này, có quyền truy xuất resource từ 1 nhà khác (2 thằng gọi là khác nhà khi khác tên miền, khác port, khác giao thức http và https)

Ví dụ một request cross-origin: nhà bạn ở http://domain-a.com dùng javascript gửi request bên nhà http://api.domain-b.com/data.json

Vì lý do bảo mật, trình duyệt sẽ không cho thực hiện các request cross-origin như vậy. Nghĩa là các ứng dụng web gọi API chỉ có thể sử dụng resource từ cùng nhà (same-origin policy là từ chuẩn, nếu bạn cần research thêm), trừ khi response từ nhà khác đó cho phép gọi CORS (bằng cách thêm một số thông tin trên header)

Các request có thể dùng CORS

  • Gửi một network request bằng fetch
  • Web font, hoặc load @font-face trong CSS
  • WebGL texture
  • Image, video

Khi config thành công trên server, server sẽ trả thêm một số thông tin trên header để trình duyệt biết và cấp phép chạy

Access-Control-Allow-Origin

Chỉ định các tên miền nào được phép truy cập, ví dụ để cho phép tất cả tên miền có thể gọi tới

Access-Control-Allow-Origin: *

Cho phép 1 tên miền cụ thể

Access-Control-Allow-Origin: https://example.com

Các kiểu request CORS

Có 2 kiểu CORS request: các request đơn thuần, và các request preflight, 2 cái này sẽ do trình duyệt xác định sử dụng cái nào, là một developer chúng ta cũng thật sự không cần quan tâm.

Request đơn thuần như GETPOSTHEAD

Các request được trình duyệt xếp loại đơn thuần là GET, POST, HEAD Sử dụng CORS safe -listed header Khi sử dụng Content-Type, chỉ các giá trị sau là được cho phép application/x-www-form-urlencodedmultipart/form-datatext/plain Không có các listener nào được đăng ký trên XMLHttpRequestUpload Không sử dụng ReadableStream

Preflight request

Đơn giản là ngược lại các trường hợp ở trên thì sẽ là dạng preflight, trình duyệt sẽ gửi đi một request ở phương thức options để xác định server có hỗ trợ ko trước khi thực sự gửi đi request chính.

Đối với loại preflight request, ngoài việc chuyển phương thức sang options, nó sẽ set thêm một số thuộc tính trên header

Access-Control-Request-Method: phương thức GET hay POST nên được sử dụng Access-Control-Request-Headers: kiểu header muốn sử dụng Origin: nơi gửi request

Ví dụ

# Request
curl -i -X OPTIONS localhost:3001/api/ping \
-H 'Access-Control-Request-Method: GET' \
-H 'Access-Control-Request-Headers: Content-Type, Accept' \
-H 'Origin: http://localhost:3000'

Chúng ta có thể tạm dịch nó ra ngôn ngữ tự nhiên là “Tao muốn thực hiện một request dạng GET với content-type và Accept header từ địa chỉ localhost:3000 có được ko?”

Kết quả trả về từ server sẽ cho phép trình duyệt tiến hành tiếp, hay dừng lại ở đó. Response từ server sẽ như thế này

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
Vary: Access-Control-Request-Headers
Access-Control-Allow-Headers: Content-Type, Accept
Content-Length: 0
Date: Fri, 05 Apr 2019 11:41:08 GMT
Connection: keep-alive

Các phương thức trên array cần nhớ

Khi cần loop qua một array, tìm phần tử, sắp xếp, hoặc làm gì đó trên array, khả năng rất cao là trong array đã có một phương thức sẵn để bạn xài, không cần dùng tới vòng lặp for. Chúng ta sẽ cùng điểm qua những phương thức như vậy trong bài viết này.

Các phương thức phải biết

map

Hàm được sử dụng nhiều nhất trong đám, mỗi khi cần thay đổi giá trị phần tử trong array, không thay đổi số lượng phần tử, nghĩ tới map

const numbers = [1, 2, 3, 4]

// cộng thêm một vào tất cả các phần tử
const numbersPlusOne = numbers.map(n => n + 1)
console.log(numbersPlusOne) // [2, 3, 4, 5]

Tạo một array mới, chỉ giữ lại một kiểu property mong muốn trong object

const allActivities = [
  { title: 'My activity', coordinates: [50.123, 3.291] },
  { title: 'Another activity', coordinates: [1.238, 4.292] },
  // etc.
]

const allCoordinates = allActivities.map(activity => activity.coordinates)
console.log(allCoordinates) // [[50.123, 3.291], [1.238, 4.292]]

filter

Hàm này sẽ trả về array mới, khi phần tử thỏa điều kiện đặt ra

const numbers = [1, 2, 3, 4, 5, 6]
const oddNumbers = numbers.filter(n => n % 2 !== 0)
console.log(oddNumbers) // [1, 3, 5]
const participants = [
  { id: 'a3f47', username: 'john' },
  { id: 'fek28', username: 'mary' },
  { id: 'n3j44', username: 'sam' },
]

function removeParticipant(participants, id) {
  return participants.filter(participant => participant.id !== id)
}

console.log(removeParticipant(participants, 'a3f47')) 
//  [{ id: 'fek28', username: 'mary' }, { id: 'n3j44', username: 'sam' }];

reduce

Một trong những phương thức khó hiểu nhất, theo quan điểm cá nhân, nhưng một khi đã master rồi thì bạn sẽ làm được khá nhiều thứ hay ho với nó

Về căn bản, reduce sẽ lấy các giá trị trong array, tính toán các kiểu rồi trả về 1 giá trị. Nó nhận vào các tham số

  • Kết quả trả về từ lần tính toán trước, lần chạy đầu tiên, giá trị này là phần tử đầu tiên trong array
  • Giá trị phần tử hiện tại trong array
  • Giá trị index của phần tử
  • Mảng đã gọi trước đó

Hầu như chúng ta chỉ sử dụng 2 tham số đầu

Lấy một ví dụ kinh điển về reduce, cộng tất cả giá trị trong mảng

const numbers = [37, 12, 28, 4, 9]
const total = numbers.reduce((total, n) => total + n)
console.log(total) // 90

Chúng ta có thể dựng hàm map và filter bằng hàm reduce luôn

const map = (arr, fn) => {
  return arr.reduce((mappedArr, element) => {
    return [...mappedArr, fn(element)]
  }, [])
}

console.log(map([1, 2, 3, 4], n => n + 1)) // [2, 3, 4, 5]

const filter = (arr, fn) => {
  return arr.reduce((filteredArr, element) => {
    return fn(element) ? [...filteredArr] : [...filteredArr, element]
  }, [])
}

console.log(filter([1, 2, 3, 4, 5, 6], n => n % 2 === 0)) // [1, 3, 5]

Giờ xét tới một ví dụ tương đối phức tạp hơn, giảm số chiều trong mảng xuống 1, cụ thể là [1, 2, 3, [4, [[[5, [6, 7]]]], 8]] thành [1, 2, 3, 4, 5, 6, 7, 8]

function flatDeep(arr) {
  return arr.reduce((flattenArray, element) => {
    return Array.isArray(element)
      ? [...flattenArray, ...flatDeep(element)]
      : [...flattenArray, element]
  }, [])
}

console.log(flatDeep([1, 2, 3, [4, [[[5, [6, 7]]]], 8]])) 
// [1, 2, 3, 4, 5, 6, 7, 8]

spread operator

Đồng ý đây không phải là một phương thức. Nhưng vì nó quá hữu dụng nên cũng đưa vào luôn

Merge nhiều mảng lại thành 1

const numbers = [1, 2, 3]
const numbersCopy = [...numbers]
console.log(numbersCopy) 
// [1, 2, 3]

const otherNumbers = [4, 5, 6]
const numbersConcatenated = [...numbers, ...otherNumbers]
console.log(numbersConcatenated)
// [1, 2, 3, 4, 5, 6]

Lưu ý quan trọng, khi sử dụng spread operator luôn khắc ghi trong lòng là nó sẽ thực hiện một shallow copy, mà shallow copy nghĩa là gì, là nó sẽ copy dùng cách đỡ tốn công nhất có thể, nếu các giá trị trong mảng kiểu số, chữ (primitive types) thì không vấn đề, khi trong mảng đó chứa mảng khác, object thì nó chỉ trỏ tới cùng đối tượng gốc thôi, chứ không phải là sao i

const arr = ['foo', 42, { name: 'Thomas' }]
let copy = [...arr]

copy[0] = 'bar'

console.log(arr)
// No mutations: ["foo", 42, { name: "Thomas" }]
console.log(copy)
// ["bar", 42, { name: "Thomas" }]

copy[2].name = 'Hello'

console.log(arr)
// /!\ MUTATION ["foo", 42, { name: "Hello" }]
console.log(copy)
// ["bar", 42, { name: "Hello" }]

Phải nhắc lại, vì đây là trường hợp hay bị bug nhất. Nên khi cần giải quyết vấn đề copy này triệt để, nhớ đến cloneDeep của Lodash

Biết thì tốt

includes

Nếu đã từng sử dụng phương thức indexOf để kiểm tra xem phần tử đó có tồn tại trong array không, thì bạn có thể thay thế bằng việc dùng includes

const sports = ['football', 'archery', 'judo']
const hasFootball = sports.includes('football')
console.log(hasFootball) // true

concat

Phương thức merge nhiều mảng thành 1

const numbers = [1, 2, 3]
const otherNumbers = [4, 5, 6]

const numbersConcatenated = numbers.concat(otherNumbers)
console.log(numbersConcatenated) 
// [1, 2, 3, 4, 5, 6]

// You can merge as many arrays as you want
function concatAll(arr, ...arrays) {
  return arr.concat(...arrays)
}

console.log(concatAll([1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12])) 
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

Loop qua mảng, chậm hơn for nhưng xài tiện hơn

const numbers = [1, 2, 3, 4, 5]
numbers.forEach(console.log)
// 1 0 [ 1, 2, 3 ]
// 2 1 [ 1, 2, 3 ]
// 3 2 [ 1, 2, 3 ]

indexOf

Được sử dụng thường xuyên để kiểm tra phần tử có tồn tại trong mảng không

const sports = ['football', 'archery', 'judo']

const judoIndex = sports.indexOf('judo')
console.log(judoIndex) 
// 2

find

Khá tương đồng với hàm filter, chúng ta cung cấp cho nó một hàm để kiểm tra tất cả các phần tử của mảng. Tuy nhiên nó sẽ trả về phần tử đầu tiên thỏa điều kiện chứ không chạy hết toàn bộ mảng.

const users = [
  { id: 'af35', name: 'john' },
  { id: '6gbe', name: 'mary' },
  { id: '932j', name: 'gary' },
]

const user = users.find(user => user.id === '6gbe')
console.log(user)
// { id: '6gbe', name: 'mary' }

findIndex

Giống như hàm find, nhưng thay vì trả về phần tử, nó trả về index của phần tử

const users = [
  { id: 'af35', name: 'john' },
  { id: '6gbe', name: 'mary' },
  { id: '932j', name: 'gary' },
]

const user = users.findIndex(user => user.id === '6gbe')
console.log(user) // 1

slice

Khi chúng ta cần lấy một đoạn trong mảng, hoặc copy một đoạn, chúng ta nhớ tới slice. Nó cũng thực hiện một shallow copy

const numbers = [1, 2, 3, 4, 5]
const copy = numbers.slice()

Ví dụ, chúng ta muốn lấy một số đoạn chat messages từ API, 2 cách làm với vòng lặp for và slice

// The "traditional way" to do it:
// xác định số lượng muốn lấy, sử dụng vòng lặp for
const nbMessages = messages.length < 5 ? messages.length : 5
let messagesToShow = []
for (let i = 0; i < nbMessages; i++) {
  messagesToShow.push(posts[i])
}

// Nếu "arr" ít hơn 5 phần tử,
// nó vẫn chạy bình thường
const messagesToShow = messages.slice(0, 5)

some

Để kiểm tra có ít nhất một phần tử trong mảng thỏa điều kiện

const users = [
  {
    id: 'fe34',
    permissions: ['read', 'write'],
  },
  {
    id: 'a198',
    permissions: [],
  },
  {
    id: '18aa',
    permissions: ['delete', 'read', 'write'],
  },
]

const hasDeletePermission = users.some(user =>
  user.permissions.includes('delete')
)
console.log(hasDeletePermission) // true

every

Tất cả các phần tử trong mảng điều thỏa điều kiện

const users = [
  {
    id: 'fe34',
    permissions: ['read', 'write'],
  },
  {
    id: 'a198',
    permissions: [],
  },
  {
    id: '18aa',
    permissions: ['delete', 'read', 'write'],
  },
]

const hasAllReadPermission = users.every(user =>
  user.permissions.includes('read')
)
console.log(hasAllReadPermission) // false

from

Tạo một array mới từ một object hoặc một đối tượng bất kỳ có thể tạo được

const nodes = document.querySelectorAll('.todo-item')
 //lấy danh sách NodeList
const todoItems = Array.from(nodes)
 // có thể sử dụng các phương thức của array trên todoItems này

todoItems.forEach(item => {
  item.addEventListener('click', function() {
    alert(`You clicked on ${item.innerHTML}`)
  })
})

 TopDev via Vuilaptrinh

Thuật toán Rate Limiting là gì?

Hiện tại hầu hết những hệ thống lớn trên thế giới đều cung cấp Rate Liming cả. Nhưng mà ít ai để ý đến thuật toán đằng sau công nghệ đó như thế nào.

  • Kĩ thuật này được 69.96% các hệ thống từ lớn đến nhỏ như Google, Facebook, LinkedIn, Youtube áp dụng
  • Kĩ thuật này giúp chúng ta ngăn chặn DDOS, chống spam, giữ cho hệ thống hoạt động trơn tru.

Thế nhưng, chúng ta ít người biết đến “người hùng”  thầm lặng này. Kĩ thuật này có tên là Rate Limiting.

Rate Limiting là cái gì cơ?

Giả sử như hệ thống của chúng ta nhận được hàng nghìn request nhưng mà trong số đó chỉ xử lí được trăm request/s chẳng hạn, và số request còn lại thì bị lỗi (do CPU hệ thống đang quá tải không thể xử lí được).

Để giải quyết được vấn đề này thì cơ chế Rate Limiting đã ra đời. Mục đích của nó chỉ cho phép nhận 1 số lượng request nhất định trong 1 đơn vị thời gian. Nếu quá sẽ trả về response lỗi.

1 số ví dụ hay gặp trong Rate Limiting như:

  • Mỗi người dùng chúng ta chỉ cho phép gửi 100 request/s. Nếu vượt quá thì sẽ trả về response lỗi.
  • Mỗi người dùng chỉ cho phép nhập sai thẻ credit 3 lần trong 1 ngày
  • Mỗi địa chỉ IP chỉ có thể tạo được 2 account trong 1 ngày.

Để dễ hiểu mình sẽ lấy ví dụ như khi bạn uống bia nhé 😉 Giả sử 1 chai bia là một request tới server thì tửu lượng 6 chai Tiger Nâu độ cồn 5% của bạn là thuật toán Rate Limit giúp bạn không sập quá nặng 😄.

Về cơ bản thì rate limiter sẽ cho phép gửi bao nhiêu request trong 1 khoảng thời gian nào đó. Nếu vượt quá sẽ bị block lại.

  Tất cả những thủ thuật Google Search mà bạn cần biết

Rate limit

Khuyến cáo: Hình ảnh minh họa thôi nha. Mùa dịch nhìn có thèm cũng không được ra quán nhậu nha :)))

Một số thuật toán hay dùng?

Vì tên mấy thuật toán dịch ra mình thấy hơi chuối nên mình để nguyên, anh em nào biết dịch sao cho hay thì comment ở dưới nha.

Những thuật toán thường được áp dụng để rate limit bao gồm:

Bucket Algorithm

Leaky bucket: Vào một ngày bạn có bồ (giả định) và cô ấy không muốn bạn uống nhiều, nên đổ hết 8, 9 chai vào 1 cái xô rồi đục lỗ cho nó lủng. Dù bạn có mua một két để luyện thành tiên tửu nhưng đều phải đổ vào xô, mỗi ngày chỉ chảy ra tầm 1 chai. Và bạn chỉ được uống bia chảy ra từ xô.

Cách này giúp cho hệ thống không sợ bị quá tải khi có nhiều người truy cập, vì cái xô sẽ chặn bớt.

rate-limit

Token Bucket

Thay vì đổ bia vào xô, bồ bạn sẽ bỏ vào xô mỗi ngày 40 Token. Nếu bạn uống Tiger bạn sẽ mua được 2 chai, còn uống Corona thì chỉ được 1 chai.

Hết ngày, dù còn hay hết token, bồ bạn sẽ bỏ vào cho đủ khẩu phần 50 Token mỗi ngày. Cách này đảm bảo 1 ngày bạn chỉ mua bia tối đa 40 Token.

Trong thực tế, có nhiều request sẽ tốn nhiều tài nguyên hơn, chạy lâu hơn (tạo report, lưu nhiều dữ liệu). Nếu dùng Leaky Bucket, ta chỉ đảm bảo được số lượng request tới server. Với thuật toán này, cái gì tốn nhiều tài nguyên hơn sẽ cần nhiều token hơn, ta có thể dễ dàng kiểm soát lượng tải tới server.

rate limitng

Window Algorithm

Thay vì dùng xô để hạn chế lưu lượng, những thuật toán này phân chia khoảng thời gian (timing window) để hạn chế số lượng request.

Fixed window

Thấy xô dùng lâu ngày thấy nhàm chán, gấu bạn quyết định giờ 1 tuần bạn chỉ được uống tối đa 3 chai mỗi chai 20 token. Một tuần (tính từ 0h sáng thứ 2 tới 24h đêm CN) này chính là fixed window, tối đa là 3 chai.

rate-limit

Cách này dễ hiểu, dễ code, các hệ thống lớn khi cung cấp API cũng hay ra giới hạn như thế này. Tuy nhiên, nó có 1 điểm yếu là… có thể bị lợi dụng để burst số lượng request vượt giới hạn.

Ví dụ, gấu bạn ra mức giới hạn này vì muốn bạn uống 2 ngày 1 chai. Tuy nhiên đêm đó bạn lên cơn thèm nên đợi 11h30 tối CN uống 3 chai, sau đó 1h sáng thứ 2 bơm thêm 3 chai nữa (tiên tửu)!

Vậy là trong canh Tý bạn uống 6 chai, bạn sập như chưa từng luôn!

Do vậy, để tránh bọn ranh ma người ta đã dùng Sliding Window Log / Sliding Window để hạn chế trường hợp trên.

Sliding Window Log

Thay vì dùng fixed window, gấu bạn quyết định thay đổi thuật toán. Giờ đây, khi bạn bơm alcohol, gấu sẽ ghi log lại. Sau đó, gấu bắt đầu tính từ lần đó đến đúng một tuần sau.

Ví dụ vào một buổi trưa nóng nực 11h thứ 2 bạn khui một chai bia mát lạnh để giải nhiệt cuộc sống, gấu sẽ tra log từ 11h trưa thứ 2 tuần trước đến 11h trưa thứ 2 tuần này. Đây chính là Sliding Window.

Tuần 1         - Tuần 2

2 3 4 5 6 7 CN - 2 3 4 5 6 7 CN

Nếu gấu đếm trong khoản thời gian này, bạn uống ít hơn 3 chai thì sẽ vui vẻ khui cho bạn uống, còn không … gấu vẫn sẽ vui cười và khui nhưng là cho nó uống (còn bạn thì nhìn thèm vãi chưởng).

Cách này đảm bảo lượng cồn bạn nạp vào người trong 1 tuần không vượt quá 3 chai. Nếu bạn nốc liền 3 chai cùng lúc, bạn sẽ phải nhịn … đúng 7 ngày sau mới được liếm nhẹ được miếng sinh tố lúa mạch :))

rate-limit

Cách này rất chính xác, tuy nhiên, nó vẫn có 1 số khuyết điểm:

  • Tốn khá nhiều bộ nhớ vì phải log toàn bộ request,
  • Tốn tài nguyên vì phải log ở mỗi lần request
  • Chạy có thể sẽ chậm, vì mỗi lần có request, ta phải query đếm lượng request trong 1 khoản thời gian nhất định để xem có tiếp nhận request hay không
  • Hãy tưởng tượng hệ thống tầm 1 triệu người dùng, mỗi ngày họ gửi 1000 request là ta phải log gần 1 tỷ event mỗi ngày. Toang rồi ông giáo ạ!

Sliding Window (Rolling Window)

Sliding Window là sự cải tiến từ Sliding Window Log. Ta sẽ đánh đổi độ chính xác lấy tốc độ và bộ nhớ (lưu ít hơn ,query ít hơn). Ta không log trên mỗi request, mà chỉ lưu lại số lượng trên mỗi khoản thời gian.

Quay lại với cô người yêu ví dụ nào. Do gấu thấy ghi lại mỗi lần bạn uống mệt qué nên bây giờ, gấu chỉ log 1 lần vào Chủ Nhật là tuần đó bạn uống bao nhiêu chai bia.

Ví dụ ở tuần 1 bạn uống 3 chai. Vào 11h trưa thứ 2 tuần thứ 2, bạn định uống thêm 1 chai. Sliding Window sẽ là từ 11h trưa tuần thứ 2 tuần trước đến 11h thứ 2 tuần này.

Tuần 1           Tuần 2

2 3 4 5 6 7 CN - 2 3 4 5 6 7 CN

Sliding window này có 6 ngày rưỡi trong tuần thứ 1 (trưa thứ 2 đến tối CN), nửa ngày trong tuần 2 (sáng thứ 2).

Do không có log cụ thể, ta sẽ ước đoán lượng bia bạn uống trong khoảng thời gian này bằng công thức sau.

Số uống tuần 1 * (Ngày trong tuần 1 / 7) + Số uống tuần 2 * (Ngày trong tuần 2 / 7)

3 * (4.5/7) + 1 * (2.5/7) = 2.285

Tức là trong khoảng 7 ngày này bạn đã uống tầm 2.285 chai bia, chưa uống thêm được.

Có thể là không chính xác lắm, vì số lượng bia bạn uống sẽ không trải dài đều theo tuần. Tuy vậy, nó không bị khai thác lỗ hổng như fixed window, lại tốn ít tài nguyên và bộ nhớ hơn, nên được sử dụng tương đối nhiều.

rate-limit

 

Có thể bạn quan tâm:

Xem thêm IT Jobs Developer hấp dẫn tại TopDev

Nâng skill lập trình PHP như thế nào?

Là một lập trình viên thì ta luôn chú ý đến năng suất của sản phẩm như phải an toàn, không bị lỗi và đương nhiên sẽ chạy nhanh. Và bài này mình sẽ đề cập đến vấn đề tối ưu code của PHP và làm thế nào để website chạy nhanh hơn. Vậy nâng skill lập trình PHP như thế nào?

1. Nắm được nguyên tắc hoạt động của trình biên dịch

Có lẽ phần này thì ai cũng biết nhưng tôi nghĩ cũng nên đưa ra vì nó rất là quan trọng, và hy vọng những bạn chưa hiểu nguyên tắc hoạt động sẽ nắm bắt được.

Đối với trình biên dịch một ngôn ngữ lập trình bất kỳ thì trình biên dịch luôn luôn dịch một file từ trên xuống dưới và từ trái qua phải. Giả sử bạn tạo 2 fiel a.php và b.php thì nếu bạn require b.php vào a.php thì lúc này trình biên dịch sẽ thực thi hết file b.php rồi mới dịch xuống dòng kế tiếp sau lệnh require ở file a.php.

Ví dụ:

File a.php:

echo 'Begin file a.php<br/>';
 
require 'b.php';
 
echo 'end file a.php';

File b.php:

echo 'File b<br/>';

Test nhanh kết quả sẽ là:

Begin file a.php
File b
end file a.php

Trường hợp bạn gọi tới một hàm nào đó. Nếu trong chương trình chạy đến một hàm nào đó thì nó sẽ thực thi hết nội dung bên trong hàm đó rồi mới chạy xuống dòng lệnh kế tiếp, và hàm đó bạn có thể đặc bất kỳ vị trí nào trong file PHP, không giống như JavaScript hay C++ là bạn phải khai báo phía trên nó mới hiểu.

2. Thuần thục các toán tử trong lập trình

Các ngôn ngữ lập trình sẽ có những cách thể hiện toán tử khác nhau nhưng về bản chất thì toán tử thuộc về toán học nên nó không thể thay đổi, tức là cho dù bạn làm việc trên ngôn ngữ nào đi nữa thì nó vẫn là nó. Và đây cũng là tiền đề về kỹ năng lập trình cho các bạn.

Tôi có một ví dụ nho nhỏ:

$a = 12;
$b = '12';
var_dump($a == $b); // kết quả true
var_dump($a === $b);// kết quả false

Trong ví dụ này  kết quả thú nhất trả về true, kết quả thứ 2 trả về false, lý do là vì khi ta sử dụng toán tử so sánh ba dấu === thì nó sẽ so sánh cả giá trị và kiểu dữ liệu, còn hai dấu bằng == thì nó chỉ so sánh giá trị. Điều này thể hiện đúng trong ngôn ngữ PHP và JavaScript.

Còn rất nhiều các toán tử khác. Các bạn có thể tham khảo bài này toán tử và biểu thức hoặc lên trang php.net

Xem tin tuyển lập trình viên PHP đãi ngộ tốt trên TopDev

3. Có tư duy lập trình tốt

Là một lập trình viên thì ai cũng muốn mình có một cái đầu logic. Nhưng vấn đề này có thành hiện thực hay không thì nó phụ thuộc vào thiên phú trời cho và sự chăm chỉ của bạn. Tôi có một ví dụ sau:
$b = 1;
$a = '';
if ($b = 1){
    $a = 'B = mot';
}
else{
    $a = 'B khac mot';
}
echo $a;

Các bạn  thấy kết quả của biến $a luôn luôn là một trong hai giá trị của mệnh đề if hoặc else. Như vậy cái đoạn khai báo biến $a = ''; liệu có cần thiết không? Rõ ràng là không cần thiết vì biến $a luôn luôn tồn tại sau khi mệnh đề if else thực thi nên câu lệnh echo $a; luôn luôn không lỗi và có kết quả đúng như mong muốn. Tức là ta code lại như sau:

$b = 1;
if ($b = 1){
    $a = 'B = mot';
}
else{
    $a = 'B khac mot';
}
echo $a;

Kết quả tương đương nhỉ :D. Tuy nhiên điều này lại gây ra sự khó nhìn đó là không nhìn rõ biến $a bắt nguồn từ đâu.

Xét ví dụ 2 như sau:

if ($a == 12 && isset($a)){
    echo 'A = 12';
}

Rõ ràng bài toán này sẽ không logic vì trường hợp biến $a không tồn tại sẽ sinh lỗi. Lỗi là vì ta đi kiểm tra $a == 12 trước rồi mới kiểm tra isset sau, nếu ta đổi ngược lại thì sẽ logic.

4. Rút ngắn code lại

Việc rút ngắn code lại không hề đơn giản với những bạn mới vào nghề lập trình (giống như mình ), và ai cũng muốn code mình ngắn gọn, dễ hiểu và chạy nhanh (tối ưu). Thông thường ta code xong một chương trình thì xem lại việc tối ưu code xem có thể giảm được phần nào hay không.

Xét ví dụ sau:

$a = 12;
$b = 20;
if ($b == 20){
    $a = 20;
}
else{
    $a = 12;
}

Với ví dụ này nó hơi dài dòng, vì biến $a sẽ có giá trị là 20 hoặc là 12 sau khi chương trình chạy xong. Như vậy ta có cách này code gọn hơn đó là bỏ cái else đi vì bến $a ta đã khai báo $a = 12; , tức là:

$a = 12;
$b = 20;
if ($b == 20){
    $a = 20;
}

5. Kiểm tra dữ liệu trước khi đưa xử lý truy vấn database

Database

Thông thường những bạn mới vào nghề ít khi chú ý vấn đề này, đó là khi ta lấy dữ liệu từ phía người dùng. Chẳng hạn bạn làm một form đăng ký với giới tính là nam hoặc nữ tương đương với 0 hoặc 1. Như vậy cái thẻ select của bạn sẽ có 2 options có giá trị là 0 và 1, và người dùng sẽ chọn rồi submit. Nhưng giả sử người dùng biết sử dụng firebug thì họ dễ dàng chỉnh sửa số 1 hoặc số 0 thành một số khác bất kỳ, điều này dẫn đến khi bạn update vào data không đúng quy tắc. Bởi vậy với những trường hợp như vậy nên kiểm tra cho chính xác.

6. Thiết kế database chuẩn theo ứng dụng

Database

Đang nói về code mà lại đưa vấn đề database vào thì hơi kỳ nhưng mình nghĩ cũng nên đưa vào vì nó rất quan trọng cho một chương trình ứng dụng có sử dụng cơ sở dữ liệu. Dưới đây là một số chú ý khi sử dụng với Sql.

  • Sử dụng indexes cho các field nằm trong điều kiện where của các câu truy vấn.
  • Khi truy vấn với kiểu int sẽ nhanh hơn kiểu varchar, vì vậy những field có như cầu tìm kiếm nếu có thể nên chuyển sang hết kiểu INT và index chúng. Ví dụ như tìm kiếm theo ngày tháng.
  • Không sử dụng toán tử like trong các câu truy vấn vì nó rất chậm, nếu có thể hãy dùng fulltext search.
  • Trong các câu truy vấn càng ít điều kiện lọc càng tốt. Chẳng hạn như bạn check thông tin đăng nhập, thông thường bạn thiết lập 2 điều kiện là tên đăng nhập và mật khẩu. Nhưng có cách khác hay hơn đó là bạn chỉ lọc với điều kiện tên đăng nhập thôi, sau đó bạn lấy kết quả và so sánh với mật khẩu để đưa ra kết quả thành công hay thất bại.
  • Hiểu được sự logic của các toán tử và dữ liệu thực tế để đưa ra vị trí các biểu thức trong các điều kiện AND và OR. Ví dụ trong bảng tin tức bạn có status và is_featured. Trong đó status = 1 thì hiển thị và status = 0 thì ẩn. Còn is_featured = 1 thì là tin đặc đặc trưng và is_featured = 0 là không phải. bây giờ tôi muốn lấy danh sách các tin đặc trưng thì đương nhiên ta sẽ dùng 2 điều kiện đó là where status = 1 AND is_featured = 1. Nhưng giả sử ta đặc ngược lại where is_featured = 1 AND status = 1 thì câu truy vấn sẽ nhanh hơn. Tại vì đối với toán tử AND thì nếu biểu thức đầu tiên sai thì nó không cần kiểm tra biểu thức thứ 2, trong database thì is_featured chiếm số lượng ít nên ta có thể áp dụng được.
  • Có thể trùng lặp dữ liệu, ở trường các bạn được dạy là càng ít trùng lặp càng tốt, nhưng khi đi làm thực tế thì việc tốn dung lượng lưu trữ ko quan trọng nữa mà chỉ đòi hỏi tốc độ.
  • Áp dụng quy tắc “cần gì thì lấy nấy”, tức là cần field nào thì ta select field đó chứ ko nên sử dụng select *
  • Có thể áp dụng kỹ thuật Partition trong Mysql  để phân khối ra. Vấn đề này tôi chỉ nêu ra chứ ko bàn luận về nó.

7. Sử dụng cache cho website

cache

Kỹ thuật cache quyết định tốc độ của một website thực thụ. Hiện nay có rất nhiều kỹ thuật cache như cache file, xml, memcache, redis cache.

Kết luận

Những vấn đề trên mình chỉ đưa ra thảo luận nên nếu có gì sai sót các bạn góp ý giúp mình để mình có thể hoàn thiện bài này hơn nhé. Hy vọng anh em sẽ tạo được những website ưng ý hơn

Xem ngay những tin đăng tuyển dụng IT mới nhất trên TopDev

Kiểm tra element có nằm trong viewport không bằng javascrip

Học cách viết một helper function để kiểm tra element nằm trong viewport

“Nằm trong viewport” nghĩa là nó đang hiển thị bên trong phần nhìn thấy được của trình duyệt, function này cần thiết khi chúng ta cần tới lazy loading, hiệu ứng này kia.

Phần quan trọng nhất của function này là dùng Element.getBoundingClientRect(), nó cho chúng ta giá trị position của element so với viewport. Nó trả về một object chứa heightwidth, khoảng cách đến toprightbottomleft với viewport

// chọn element
var h1 = document.querySelector('h1');

// lấy position của element trên
var bounding = h1.getBoundingClientRect();

console.log(bounding)
// {
//  height: 118,
//  width: 591.359375,
//  top: 137,
//  bottom: 255,
//  left: 40.3125,
//  right: 631.671875
// }

Tìm việc làm javascript trong tháng mới nhất

Nếu một element nằm trong viewport được xác định như sau

  • topleft >= 0
  • right <= độ rộng của viewport
  • bottom <= độ cao của viewport

Để check độ rộng của viewport, chúng ta có 2 cách, một số trình duyệt hỗ trợ window.innerWidth, một số khác hỗ trợ document.documentElement.clientWidth, số còn lại thì support cả 2. Rắc rối nhở!

(window.innerWidth || document.documentElement.clientWidth)

 

Cũng tương tự với giá trị viewport height

(window.innerHeight || document.documentElement.clientHeight)

 

Chúng ta kiểm tra xem element có nằm trong viewport không

if (
    bounding.top >= 0 &&
    bounding.left >= 0 &&
    bounding.right <= (window.innerWidth || document.documentElement.clientWidth) &&
    bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight)
) {
    console.log('Trong viewport!');
} else {
    console.log('Không nằm trong viewport... whoa la la');
}

 

Chúng ta gom lại vào một function helper để dành xài

var isInViewport = function (elem) {
    var bounding = elem.getBoundingClientRect();
    return (
        bounding.top >= 0 &&
        bounding.left >= 0 &&
        bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
};

 

Sử dụng function này, chúng ta có thể làm lazy load image

<figure data-image="url/to/my/image.jpg">Hình sẽ được load khi scroll tới đây..</figure>

 

var image = document.querySelector('[data-image]');
window.addEventListener('scroll', function (event) {
    if (isInViewport(image)) {
        image.innerHTML = '<img src="' + image.getAttribute('data-image') + '">';
    }
}, false);

TopDev via Vuilaptrinh

Một vài ứng dụng hay ho của reduce

Hãy học sử dụng reduce, vượt qua những ví vụ căn bản bằng cộng, trừ, nhân, chia

Khi đọc tài liệu trên MDN về Array.prototype.reduce() chúng ta sẽ có cái nhìn khá tổng quát về .reduce(), những ví dụ cộng, nhân số cơ bản để chúng ta dễ nắm cách hàm .reduce() chạy. Tuy nhiên vì nó quá căn bản, nên bạn sẽ không thể thấy hết được sự lợi hại của .reduce()

function add(a, b) {
    return a + b;
}

function multiply(a, b) {
    return a * b;
}

const sampleArray = [1, 2, 3, 4];

const sum = sampleArray.reduce(add, 0);
console.log('Tổng:', sum);
// ⦘ Tổng: 10

const product = sampleArray.reduce(multiply, 1);
console.log('Nhân lại bằng:', product);
// ⦘ Nhân lại bằng: 24

 

Có thể nhiều người không để ý, biến tích lũy (accumulator = tham số đầu tiên) và giá trị hiện tại (tham số thứ 2) không nhất thiết phải giống nhau.

Chúng ta có thể khai báo một hàm reducer như sau là hoàn toàn hợp lệ

function fizzBuzzReducer(acc, element) {
    if (element % 15 === 0) return `${acc}Fizz Buzz\n`;
    if (element % 5 === 0) return `${acc}Fizz\n`;
    if (element % 3 === 0) return `${acc}Buzz\n`;
    return `${acc}${element}\n`;
}

const nums = [
    1, 2, 3, 4, 5, 6, 7, 8, 9,
    10, 11, 12, 13, 14, 15
];

console.log(nums.reduce(fizzBuzzReducer, ''));

 

Với bài viết này chúng ta cũng xem xét những ứng dụng khác, cao cấp hơn của .reduce(), sử dụng việc thay đổi kiểu của biến tích lũy như ví dụ trên, mở ra nhiều ứng dụng bảo đảm là hay

Chuyển một array sang object

Chúng ta có một array thế này

const peopleArr  = [
    {
        username:    'glestrade',
        displayname: 'Inspector Lestrade',
        email:       'glestrade@met.police.uk',
        authHash:    'bdbf9920f42242defd9a7f76451f4f1d',
        lastSeen:    '2019-05-13T11:07:22+00:00',
    },
    {
        username:    'mholmes',
        displayname: 'Mycroft Holmes',
        email:       'mholmes@gov.uk',
        authHash:    'b4d04ad5c4c6483cfea030ff4e7c70bc',
        lastSeen:    '2019-05-10T11:21:36+00:00',
    },
    {
        username:    'iadler',
        displayname: 'Irene Adler',
        email:       null,
        authHash:    '319d55944f13760af0a07bf24bd1de28',
        lastSeen:    '2019-05-17T11:12:12+00:00',
    },
];

 

Nếu cần chuyển nó qua dạng object, lấy giá trị username làm key

function keyByUsernameReducer(acc, person) {
    return { ...acc, [person.username]: person }
};

const peopleObj = peopleArr.reduce(keyByUsernameReducer, {} );
// ⦘ {
//     "glestrade": {
//         "username":    "glestrade",
//         "displayname": "Inspector Lestrade",
//         "email":       "glestrade@met.police.uk",
//         "authHash":    "bdbf9920f42242defd9a7f76451f4f1d",
//          "lastSeen":    "2019-05-13T11:07:22+00:00"
//     },
//     "mholmes": {
//         "username":    "mholmes",
//         "displayname": "Mycroft Holmes",
//         "email":       "mholmes@gov.uk",
//         "authHash":    "b4d04ad5c4c6483cfea030ff4e7c70bc",
//          "lastSeen":    "2019-05-10T11:21:36+00:00"
//     },
//     "iadler":{
//         "username":    "iadler",
//         "displayname": "Irene Adler",
//         "email":       null,
//         "authHash":    "319d55944f13760af0a07bf24bd1de28",
//          "lastSeen":    "2019-05-17T11:12:12+00:00"
//     }
// }

 

Chuyển một array thành một array khác

Bình thường khi nghĩ tới .reduce() chúng ta nghĩ tới viết lấy một mảng sau đó đưa nó về 1 giá trị bằng biến tích lũy, giá trị này hoàn toàn có thể là một array khác.

const fileLines = [
    'Inspector Algar,Inspector Bardle,Mr. Barker,Inspector Barton',
    'Inspector Baynes,Inspector Bradstreet,Inspector Sam Brown',
    'Monsieur Dubugue,Birdy Edwards,Inspector Forbes,Inspector Forrester',
    'Inspector Gregory,Inspector Tobias Gregson,Inspector Hill',
    'Inspector Stanley Hopkins,Inspector Athelney Jones'
];

function splitLineReducer(acc, line) {
    return acc.concat(line.split(/,/g));
}

const investigators = fileLines.reduce(splitLineReducer, []);
// [
//   "Inspector Algar",
//   "Inspector Bardle",
//   "Mr. Barker",
//   "Inspector Barton",
//   "Inspector Baynes",
//   "Inspector Bradstreet",
//   "Inspector Sam Brown",
//   "Monsieur Dubugue",
//   "Birdy Edwards",
//   "Inspector Forbes",
//   "Inspector Forrester",
//   "Inspector Gregory",
//   "Inspector Tobias Gregson",
//   "Inspector Hill",
//   "Inspector Stanley Hopkins",
//   "Inspector Athelney Jones"
// ]

 

Tất nhiên trong điều kiện có thể sử dụng .flatMap() ( không hỗ trợ trên edge và IE) thì nên dùng .flatMap() thay cho reduce

Thực hiện 2 tính toán cùng lúc

Nếu cần thực hiện 2 tính toán dựa trên 1 array, ví dụ lấy giá trị nhỏ nhất và lớn nhất trong dãy số

const readings = [0.3, 1.2, 3.4, 0.2, 3.2, 5.5, 0.4];
const maxReading = readings.reduce((x, y) => Math.max(x, y), Number.MIN_VALUE);
const minReading = readings.reduce((x, y) => Math.min(x, y), Number.MAX_VALUE);

 

.reduce không chỉ có thể trả về number, nó có thể trả về bất cứ kiểu gì, chúng ta có thể đưa 2 giá trị này vào một object

function minMaxReducer(acc, reading) {
    return {
        minReading: Match.min(acc.minReading, reading),
        maxReading: Match.max(acc.maxReading, reading)
    }
}

const initMinMax = {
    minReading: Number.MAX_VALUE,
    maxReading: Number.MIN_VALUE,
}

const minMax = readings.reduce(minMaxReducer, initMinMax);

 

Vấn đề duy nhất của cách thứ 2 này là performance không tốt.

Kết hợp map và filter cùng lúc

Lấy lại ví dụ với mảng peopleArr ở trên, chúng ta lấy người vừa đăng nhập sau cùng, không tính các user không có email. Cách thứ nhất để làm là tách ra làm 3 bước

  • Lọc hết các user không có email
  • Lấy giá trị lastSeen
  • Tìm giá trị lớn nhất
function notEmptyEmail(x) {
   return (x.email !== null) && (x.email !== undefined);
}
function getLastSeen(x) {
    return x.lastSeen;
}
function greater(a, b) {
    return (a > b) ? a : b;
}
const peopleWithEmail = peopleArr.filter(notEmptyEmail);
const lastSeenDates   = peopleWithEmail.map(getLastSeen);
const mostRecent      = lastSeenDates.reduce(greater, '');

 

Một cách khác để làm với reduce

function notEmptyEmail(x) {
    return (x.email !== null) && (x.email !== undefined);
}

function greater(a, b) {
    return (a > b) ? a : b;
}

function notEmptyMostRecent(currentRecent, person) {
    return (notEmptyEmail(person)) ? greater(currentRecent, person.lastSeen) : currentRecent;
}

const mostRecent = peopleArr.reduce(notEmptyMostRecent, '');

 

Chạy các phương thức async theo hàng đợi

Rất hữu ích khi cần giới hạn số lượng request API, hoặc lấy kết quả của một Promise truyền cho thằng kế tiếp. Ví dụ lấy message cho tất cả user trong mảng peopleArr

function fetchMessages(username) {
    return fetch(`https://example.com/api/messages/${username}`).then(response => response.json());
}

function getUsername(person) {
    return person.username;
}

async function chainedFetchMessages(p, username) {
    // p là một promise
    const obj = await p;
    const data = await fetchMessages(username);
    return { ...obj, [username]: data };
}
const msgObj = peopleArr
    .map(getUsername)
    .reduce(chainedFetchMessages, Promise.resolve({}))
    .then(console.log)

 

TopDev via Vuilaptrinh

Xem thêm các vị trí tuyển dev lương cao tại Topdev

Fluent Design – Ngôn Ngữ Thiết Kế Mới Của Microsoft

Fluent Design sẽ là ngôn ngữ thiết kế được Microsoft áp dụng rộng rãi trong tương lai, những thay đổi đã dần xuất hiện trên Windows và hãng muốn mở rộng ngôn ngữ này trên nhiều nền tảng khác như iOS, Android cũng như thiết kế web.

Fluent Design sẽ là ngôn ngữ thiết kế được Microsoft áp dụng rộng rãi trong tương laiFluent Design sẽ là ngôn ngữ thiết kế được Microsoft áp dụng rộng rãi trong tương lai

Tham vọng của Microsoft về Fluent Design

Microsoft cho ra mắt hẳn một trang web để nói về Fluent Design – link. Khi nói về ngôn ngữ thiết kế này, Microsoft đang tham vọng đưa ngôn ngữ Fluent Design đến với mọi sản phẩm và dịch vụ của hãng, từ các ứng dụng trên Windows đến các ứng dụng điện toán đám mây. Không những vậy, tham vọng của Microsoft còn muốn vươn ra ngoài hệ sinh thái của mình, lan tỏa ra các nền tảng di động như Android và IOS, website.

Fluent Design - Ngôn ngữ thiết kế mới của Microsoft trên các nền tảngFluent Design – Ngôn ngữ thiết kế mới của Microsoft trên các nền tảng

Video giới thiệu Fluent Design từ Microsoft

Từ khi Windows 8 ra đời và sau đó là Windows 10 thì Flat Design với phong cách tối giản, phẳng hóa mọi thứ là ngôn ngữ thiết kế chính của các sản phẩm hệ sinh thái Microsoft. Nhưng hiện nay, sự phát triển của các thiết bị ngoại vi như kính thực tế ảo, cũng như sự nhàm chán dần dần của người dùng với thiết kế phẳng đặt ra yêu cầu về một ngôn ngữ thiết kế đổi mới. Câu trả lời của Microsoft chính là Fluent Design.

Fluent Design được áp dụng dần trên các ứng dụng WindowsFluent Design được áp dụng dần trên các ứng dụng Windows

Có thể nói, Fluent Design chính là sự kế thừa những điểm tốt của Material Design – Google, phát triển thêm những tối ưu cho thực tế ảo và trải nghiệm người dùng mới. Vì sao mình lại nói như vậy, chúng ta hãy đi qua một số nguyên lý cơ bản của Fluent Design nhé.

Những yếu tố chính tạo nên Fluent Design

1. Light

Ánh sáng (Light) là công cụ quan trọng để thu hút sự chú ý của người dùng. Một nút bấm phát sáng có thể hướng dẫn người dùng cách sử dụng một ứng dụng, hoặc nhấn mạnh một tính năng của chương trình mà có thể họ chưa từng để ý đến nó.

2. Depth

Chiều sâu (Depth) Hai thứ Light và Depth giúp app đẹp hơn, mượt mà hơn, cảm giác thân thiệt hơn. Cũng như cách mà Material Design đem tới cho người dùng. Ngôn ngữ thiết kế Metro phẳng, ngắn ngủi, Microsoft hầu như chỉ dựa vào các khung hình vuông vức buồn tẻ, để cung cấp cho người dùng thông tin và công cụ. Còn với Fluent Design, Microsoft đang thử thách các nhà phát triển khi loại bỏ mô hình này và thay thế nó bằng cách đưa thông tin và các đối tượng thoát khỏi khung hình 2D truyền thống.

Những yếu tố chính tạo nên Fluent Design: Ánh sáng, Chiều sâu, Chuyển động, Vật thể, Mở rộngNhững yếu tố chính tạo nên Fluent Design: Ánh sáng, Chiều sâu, Chuyển động, Vật thể, Mở rộng

3. Motion

Chuyển động (Motion) – Một giao diện hình ảnh động đẹp có thể là một yếu tố hấp dẫn, nhưng các hiệu ứng Chuyển động mới là thứ giúp thu hút sự chú ý của người dùng, bằng cách cho họ thứ gì đó hoạt động để nhìn vào và tương tác với nó. Belfiore so sánh việc sử dụng chuyển động trong Fluent Design giống như việc một đạo diễn sử dụng các cử động để dẫn dắt người dùng vào câu chuyện mà họ muốn kể.

4. Material

Chất liệu (Material) – Giống như các ý tưởng cốt lõi của Fluent Design, Chất liệu dường như là một sự thay đổi đột phá cho phong cách đồ họa của Windows trong quá khứ. Các hình vuông chức năng trong giao diện truyền thống của Microsoft có thể vẫn hoạt động được, nhưng nó thiếu tính kết nối với thế giới thực. Belfiore gợi ý rằng điều quan trọng làm người dùng yêu thích thiết kế ứng dụng của Windows là nó có thể mô phỏng cảm giác “nhạy cảm và đầy sinh lực” của những chất liệu làm nên thế giới thực.

Material trong Fluent DesignMaterial trong Fluent Design

Đọc tới đây, chúng ta có đã có cảm giác rất giống những gì Material Design đang làm. Nhưng khi nhìn vào concept Fluent Design bên trên, chúng ta có thể thấy sự khác biệt, Material Design tập trung vào nội dung, làm nổi khối nội dung chính trên nền phẳng, thu hút người dùng. Fluent Design không chỉ làm nổi khối nội dung mà toàn bộ giao diện đều được làm dạng vật thể, và làm hiệu ứng ánh sáng cho cả nền giao diện.

5. Scale

Khả năng mở rộng – thu hẹp (Scale) – Kích thước tương đối của các vật thể kỹ thuật số là điều mà các nhà phát triển VR đã nỗ lực làm việc trong vài năm nay – một vật thể có kích thước đúng trên màn hình nhưng có thể cực lớn hoặc cực nhỏ khi nhìn qua kính VR hoặc AR. Vì vậy, tạo ra một kích thước đúng cho các đối tượng ảo là điều tối quan trọng để có được một giao diện người dùng thứ nhất tốt.

Một trong những nhược điểm của Windows 10 so với MacOS mà người dùng thường nhắc tới là khả năng hỗ trợ độ phân giải cao. Màn hình 4K khi dùng Win 10 các icon, chữ rất nhỏ, khi scale lên lại bị mờ.

Hiện tại Fluent Design đã được triển khai trên một số ứng dụng Windows như bộ icon Office 365, ứng dụng Notepads mới. Bạn có thể tải và trải nghiệm Notepads trên Microsoft Store nhé.

TopDev via Goclamweb

 

3 bước tối ưu hiệu năng React App bằng các API mới của React

Bài viết hướng dẫn tối ưu hiệu năng bằng memo, useMemo, useCallback

Khi sử dụng function component, React cung cấp 3 phương thức để tối ưu: React.memouseMemo, và useCallback, chúng ta cùng điểm qua 3 thằng này

Xét ví dụ

const ListPage = ({ data, title }) => (
    <>
        <Header title={title} />
        <List listItems={data} />
    </>
)

Component như trên (<ListPage />), khi nhận một data mới, tất cả component con bên trong là Header và List sẽ re-render, mặc dù cái title không hề thay đổi. Nếu Header không tốn quá nhiều thời gian để render thì ko có vấn đề. Ngược lại dĩ nhiên là nếu render Header tốn rất nhiều thời gian, chúng ta phải xây lại để tối ưu hơn.

Tuyển nhân viên it react lương cao up to 25M

React.memo

const MyComponent = React.memo(function MyComponent(props) {
  /* render using props */
});

React.memo là một HOC, đọc lại bài này, nó sẽ nhớ kết quả render của component. Nếu component trả về một output giống hệt cho cùng một prop, đưa nó vào React.memo sẽ tiết kiệm tí thời gian.

const Header = ({ title }) => <h1>{title}</h1>

export default Header;

Wrap lại trong React.memo

const Header = ({ title }) => <h1>{title}</h1>

export default React.memo(Header);

useMemo

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

Cũng tương tự nó sẽ nhớ kết quả trả về, tuy nhiên nó sẽ có thêm phần gọi là array dependencies, là một danh sách các thằng mà nó phụ thuộc, nếu giá trị phụ thuộc thay đổi nó mới rọi render lại, không thể trả thẳng kết quả lần trước

const widgetList = useMemo(
    () => 
        widgets.map(w => ({
        ...w,
        totalPrice: someComplexFunction(w.price),
        estimatedDeliveryDate: someOtherComplexFunction(w.warehouse)
    })),
    [widgets]
);

Trong ví dụ trên, 1 component nhận một danh sách các widget, các widget này trước khi truyền vào sẽ được thêm vào 2 giá trị là total price và delivery date. Nếu giá trị các widget không thay đổi khi render lại component, thì không cần thiết phải chạy qua các hàm someComplexFunctionsomeOtherComplexFunction. Sử dụng useMemo để ghi nhớ kết quả và bỏ qua trong trường hợp đó.

useCallback

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

Mục đích để chặn các lần render không cần thiết giữa component cha và con

const Parent = () => {
    const [showExtraDetails, setShowExtraDetails] = useState(false);
    return (
        <>
        <Child onClick={() => { showData(showExtraDetails); }/>
        </>
    )
}

1 component như vậy sẽ re-render cả cha và con cùng lúc, thậm chí component con có là PureComponent được wrap bên trong React.memo đi nữa, bởi vì onClick sẽ khác nhau trên mỗi lần render. Sử dụng useCallback chúng ta viết lại như sau

const Parent = () => {
    const [showExtraDetails, setShowExtraDetails] = useState(false);
    const handleClick = useCallback(
      () => {
        showData(showExtraDetails);
      },
      [showExtraDetails],
    );
    return (
        <>
        <Child onClick={handleClick} />
        </>
    )
}

Như vậy hàm handleClick sẽ giống nhau cho các lần render khác nhau, nó chỉ khác khi showExtraDetails thay đổi.

TopDev via Vuilaptrinh

Xem thêm các vị trí tuyển gấp it dev tại đây

Phân tích con người – Chiến lược quan trọng trong ngành Nhân sự năm 2023

Khi định hướng phát triển nguồn nhân lực ngày càng được cụ thể hóa, các chuyên gia nhân sự cần có cái nhìn nghiêm túc về nguồn nhân lực (tức con người nhân sự) của họ. Cho dù xã hội đang từng bước thay đổi, những sản phẩm của công nghệ ra đời để thay thế cho con người nhưng có một quy luật không bao giờ thay đổi được, đó là con người vẫn là trung tâm của mọi yếu tố.

Với lý do đó, ta nhận thấy việc tập trung vào khai thác, phân tích con người, tận dụng nguồn dữ liệu của con người để hình thành nên những chiến lược quan trọng về nhân sự từ thu hút ứng viên, giữ chân và đồng hành cùng tài năng nhân sự.

Tại sao phân tích con người là chiến lược quan trọng?

Theo báo cáo về Xu hướng vốn nguồn nhân lực của Deloitte, có đến 71% các công ty xem việc phân tích yếu tố con người là ưu tiên hàng đầu, đồng thời đánh giá cao những dữ liệu mà yếu tố này mang lại. Ngược lại, chỉ vỏn vẹn 9% trong số họ tin rằng họ thật sự hiểu rõ về khía cạnh phát triển tài năng nhân sự có thể tạo ra sự thúc đẩy hiệu suất làm việc trong công ty họ. 

  Trí tuệ cảm xúc là gì và áp dụng như thế nào trong ngành Nhân sự

Jaclyn Lee, Giám đốc Nhân sự trường Đại học Công nghệ và Thiết kế chia sẻ:

Các tổ chức hiện nay nhận ra rằng đang tồn tại một thực tiễn quan trọng. Đó là việc quản trị nhân sự cần một cách tiếp cận tốt hơn. Và giải pháp hữu hiệu chính là nguồn dữ liệu từ việc phân tích và phát triển con người.

Cách tiếp cận này sẽ làm giảm thiểu sự thiên vị và chủ quan của con người vì đã nắm bắt được nguồn dữ liệu này thông qua phân tích. Và khi một người làm nhân sự có thể tạo ra sự ảnh hưởng thông qua những phân tích (tức vận dụng những dữ liệu thông minh từ phân tích con người), họ sẽ đủ tự tin để đảm nhận vai trò xây dựng và phát triển chiến lược nhân sự; tạo ra lợi thế cạnh tranh lớn.

Các khía cạnh để xây dựng chiến lược phân tích con người nhân sự và những case study thực tế

Thu hút nhân tài và phát hiện những tồn đọng

Việc phân tích con người tạo ra những dữ liệu thực có giá trị. Các chuyên gia nhân sự có thể dựa trên nguồn dữ liệu này (là những đặc tính, sự phù hợp về các yếu tố kỹ năng, phẩm chất; mức độ đồng hành lâu dài,…) để tạo ra các giải pháp nhân sự thích hợp nhằm thu hút các ứng viên tiềm năng. 

Ví dụ, LinkedIn cung cấp cho các chuyên gia nhân sự một nền tảng truyền thông xã hội, trên đó là những mô tả cụ thể, chi tiết về nguồn nhân lực mà họ mong muốn. Lúc này, chính nguồn tài nguyên mà các nhà tuyển dụng, quản trị nhân sự có được từ việc phân tích yếu tố con người sẽ hợp thức hóa để lựa chọn nguồn nhân lực phù hợp.  tiêu đề tuyển dụng

Một minh chứng khác chính là việc thay đổi chiến lược nhân sự của ISS để tạo ra sự khác biệt lớn. ISS có hơn 500.000 nhân viên trên toàn cầu thế nhưng có các công ty cơ sở của họ đang phát sinh nhiều vấn đề lớn. Và để giải quyết mọi vấn đề, các nhà lãnh đạo nhân sự đã tiến hành phân tích yếu tố con người, cụ thể như sau:

1. Phân tích sức mạnh liên kết giữa sự gắn kết của các nhân viên, sự hài lòng của khách hàng thông qua sự chuyển biến về cả hai mặt tích cực/tiêu cực trong lợi nhuận.

2. Phân tích dữ liệu để xác định các quy trình nhân sự có tác động lớn nhất trong việc cải thiện sự gắn kết và trải nghiệm của ứng viên; tập trung vào việc phát triển động lực, chất lượng phục vụ nhân sự, định hướng đào tạo đối với các nhân viên.

Từ những phân tích trên, các nhà quản trị nhân sự đã tìm thấy mối liên hệ chặt chẽ giữa ứng viên, nhân viên với các vấn đề phát sinh không mong muốn. Có thể thấy, việc phân tích về nguồn lực con người có thể giúp nhận ra những tồn đọng trong công tác nhân sự đồng thời đề ra những phương án giúp khắc phục tình trạng đó.

Giữ chân nhân viên

Một trong những vấn đề lớn được giải quyết triệt để thông qua việc phân tích con người chính là sự lãng phí lao động.

Một dẫn chứng từ IBM có thể giúp chúng ta có cái nhìn sâu sắc hơn. Công ty này đã áp dụng một chương trình mang tên “Chủ động duy trì”. Và họ đã thừa nhận rằng nhờ vào việc phân tích yếu tố con người, họ có thể dự đoán được nhân viên nào có khả năng sẽ rời đi. Đó là cơ sở để họ tự nhìn nhận, đánh giá lại cả quá trình phát triển nhân sự và xác định kế hoạch tốt nhất để giữ chân nhân viên của mình. 

  Lời khuyên từ Việt Trần - CTO DOF Hunt “Cách quản trị tốt nhất chính là không quản trị”

Nielsen cũng nhờ vào việc phân tích yếu tố con người để tìm ra hàng loạt mong muốn của nhân viên mình đối với tổ chức/doanh nghiệp. Nielsen cho phép nhân viên tự bày tỏ những phản hồi của bản thân về mọi khía cạnh của công ty đồng thời trao quyền cho nhân viên có thể tự quyết định về việc ra đi hay ở lại tiếp tục đồng hành cùng công ty của mình. Sau khi thực hiện những cách thức, Nielsen đã có những kê cụ thể cho thấy mức độ hài lòng của nhân viên đã có sự cải thiện lên đến 70% và hầu như chưa có trường hợp nào phải thay đổi môi trường làm việc nhân sự.

Đo lường và quản lý hiệu suất làm việc

Đây được đánh giá là lợi ích thiết thực nhất từ việc phân tích con người. Các nhà tuyển dụng nhân sự có thể nắm bắt được xu hướng chung của nhân viên mình từ đó, khuyến khích các vấn đề về năng suất làm việc ở cấp độ nhóm và cá nhân. Hiệu suất làm việc càng cao cho thấy con người và vấn đề phát triển nguồn nhân lực càng quan trọng.

Minh họa thực tế từ công ty công nghệ Cisco. Công ty đã thực hiện các cuộc khảo sát về sự tham gia của nhân viên hàng năm và số lượng phản hồi cho nghiên cứu này là hơn 73.000 nhân viên. Điều này đồng nghĩa phải mất rất nhiều thời gian để phân tích tất cả dữ liệu đó, chia sẻ nó và đề ra những giải kế hoạch hành động tương ứng. 

Thông qua việc phân tích con người, HR đo lường được hiệu suất của nhóm và mức độ tương tác của nhà lãnh đạo với các nhóm của họ. Đánh giá phân tích này cho thấy tình trạng hoạt động hiện tại của từng nhóm thậm chí từng cá nhân. Vì vậy, nhà quản trị nhân sự có thể dễ dàng quan sát, theo dõi các chỉ số hiệu suất, làm cơ sở cho việc thiết lập các lộ trình cải thiện hiệu suất sau này.

Lời kết

Phân tích con người (hay quản trị và phát triển nguồn nhân lực) không phải là một giải pháp thay thế cho chiến lược hoặc tư duy mới trong lĩnh vực nhân sự. Mà thật sự nó đủ tư cách là một chiến lược quan trọng, một công cụ chuyên dụng của HR cho phép tối ưu hóa nguồn dữ liệu nhằm cải thiện và phát triển tổ chức/doanh nghiệp ở mọi cấp độ từ thu nhận tài năng đến hiệu suất.

Tuy nhiên, đây là một công cụ khác trong hộp công cụ của HR chuyên nghiệp. Với các phép đo phù hợp, các công ty có thể sử dụng dữ liệu để cải thiện và phát triển doanh nghiệp ở mọi cấp độ từ thu nhận nhân tài đến quản trị hiệu suất làm việc.

Có thể bạn quan tâm:

Xem thêm Top Việc làm ngành cntt trên TopDev

Tìm hiểu sâu hơn về useEffect từ a tới z

Bài viết được sự cho phép của tác giả Lưu Bình An

Đây là một bài viết tương đối dài dòng về useEffect, bạn cần biết và đã đọc qua tài liệu về useEffect trên trang chính thức của React trước, và nếu chỉ thực sự cần biết sử dụng useEffect ra sao, bạn không cần đọc bài viết phân tách mổ xẻ sâu kiểu này.

Mỗi lần render là một giá trị Prop và State độc lập

Trước khi bắt đầu nói về useEffect chúng ta cần nhắc lại quá trình render

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p> ...
      <button onClick={() => setCount(count + 1)}></button>
    </div>
  );
}

Khác với Vue, nó không phải là một dạng data bindingwatcherproxy, nó chỉ là một giá trị thông thường.

const count = 42;

<p> {count} </p>;

Đầu tiên giá trị khởi tạo của count sẽ =0. Khi chúng ta gọi setCount(1), React sẽ gọi lại component một lần nữa, với giá trị count lúc này là 1. Cứ vậy

// Lần đầu render
function Counter() {
  const count = 0; // trả về bởi useState()  // ...
  <p>You clicked {count} times</p>;
  // ...
}

// sau khi click, function này được gọi lại lần nữa
function Counter() {
  const count = 1; // trả về bởi `useState()  // ...
  <p>You clicked {count} times</p>;
  // ...
}

// sau khi click, function được gọi lại lần nữa
function Counter() {
  const count = 2; // trả về bởi useState()  // ...
  <p>You clicked {count} times</p>;
  // ...
}

Khi update một state, React gọi lại component, mỗi lần render như vậy, nó sẽ thấy một giá trị count mới. Sau đó React sẽ update lại DOM tương ứng.

Vấn đề mấu chốt cần nắm là giá trị count trong các lần render khác nhau là khác nhau.

function Counter() {
  const [count, setCount] = useState(0);

  function handleAlertClick() {
    setTimeout(() => {
      alert(`You clicked on: ${count}`);
    }, 3000);
  }

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
      <button onClick={handleAlertClick}>Show alert</button>
      ...
    </div>
  );
}

Chúng ta thực hiện các bước sau

  • Bấm counter lên 3
  • Bấm “Show alert”
  • Bấm tiếp Click me cho counter lên 5 trước khi bị gọi timeout

Tìm hiểu sâu hơn về useEffect từ a tới z

Câu hỏi ở đây là nó sẽ alert ra 5 – giá trị cuối cùng, hay là 3 giá trị lúc chúng ta click

Chạy thử

Bạn có thấy kết quả quá vô lý?

Như đã nói ở trên, giá trị count là hằng số trên mỗi lần render. Function của chúng ta được gọi nhiều lần, mỗi lần gọi như vậy giá trị count bên trong là một số độc lập hoàn toàn với giá trị trước đó

Không phải đặc sản của React, viết dạng function như thế này bạn sẽ dễ hình dung hơn.

function sayHi(person) {
  const name = person.name;
  setTimeout(() => {
    alert(`Hello, ${name}`);
  }, 3000);
}

let someone = { name: "Dan" };
sayHi(someone);

someone = { name: "Yuzhi" };
sayHi(someone);

someone = { name: "Dominic" };
sayHi(someone);

Thế còn hàm xử lý event thì sao? cụ thể là hàm handleAlertClick? Cũng như trên, hàm này là có các version khác nhau ở các lần render khác nhau.

Bài viết được quảng cáo là nói về useEffect mà nãy giờ chưa đá động gì!

Quay lại với ví dụ từ trang chính thức của React

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

Câu hỏi là useEffect đã làm cách nào để lấy được giá trị cuối cùng của count?

Lẽ nào đó có một dạng “data binding” hay “watching” ở đây để update giá trị count bên trong hàm effect? Hoặc giả React chơi chiêu dùng biến mutable bên trong component để luôn có được giá trị cuối?

Không hề!

Chúng ta đã biết: giá trị count là hằng số cho các lần render, event handle cũng độc lập trên các lần render khác nhau, effect cũng vậy luôn.

Không phải giá trị count thay đổi bên trong useEffect bất biến, mà là useEffect cũng bị thay đổi trên từng lần render.

// lần render đầu tiên
function Counter() {
  // ...
  useEffect(() => {
    document.title = `You clicked ${0} times`;
  });
  // ...
}

// sau khi click
function Counter() {
  // ...
  useEffect(() => {
    document.title = `You clicked ${1} times`;
  });
  // ...
}

// click thêm lần nữa
function Counter() {
  // ...
  useEffect(() => {
    document.title = `You clicked ${2} times`;
  });
  // ..
}

Có thể mường tượng effect là một phần của kết quả lúc render

Giờ thử với setTimeout

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    ...
    setTimeout(() => {
          console.log(`You clicked ${count} times`);
        }, 3000);
    ...
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Nếu mà click vài lần với một khoảng thời gian bỏ nhỏ thì kết quả log ra là gì?

Thử ở đây

Bạn không chỉ nhận được 1 mà là một chuỗi các đoạn log ứng với số lần click.

Tìm hiểu sâu hơn về useEffect từ a tới z

Đương nhiên phải chạy như vậy mới đúng chứ, đâu có gì phải thắc mắc?

Bạn đã thử với this.state trong class component chưa?

componentDidUpdate() {
    setTimeout(() => {
        console.log(`You clicked ${this.state.count} times`);
    }, 3000)
}

Tìm hiểu sâu hơn về useEffect từ a tới z

Lý do? Giá trị this.state bên trong class component là một mutation (có thể thay đổi).

Nếu luôn muốn lấy giá trị sau cùng bên trong effect, cách dễ nhất là dùng refs

function Example() {
    const [count, setCount] = useState(0);
    ...
    const latestCount = useRef(count);
    ...

    useEffect(() => {
        ...
        latestCount.current = count;
        ...
        setTimeout(() => {
            ...
                console.log(`You clicked ${latestCount.current} times`);
            ...
        }, 3000);
    });
}
function Greeting({ name }) {
  return <h1 className="Greeting">Hello, {name}</h1>;
}

Nếu chúng ta render <Greeting name="Dan" />, sau đó render <Greeting name="Luu" />. Cuối cùng chúng ta luôn nhận được Hello, Luu

React luôn đồng bộ cục DOM với giá trị hiện tại của prop và state. Không cần phân biệt giữa mount và update khi render. Có thể hình dung effect cũng tương tự như vậy, useEffect cho phép đồng bộ những phần không nằm trong React tree với giá trị của prop và state

function Greeting({ name }) {
  useEffect(() => {
    document.title = "Hello, " + name;
  });

  return <h1 className="Greeting">Hello, {name}</h1>;
}

Câu thần chú cho việc này là: Quan trọng là đích đến, không phải quá trình

Chạy effect trên tất cả lúc chạy render sẽ không hay lắm, đôi khi có trường hợp lặp vô tận.

Trong quá trình re-render, React chỉ cập nhập đúng phần DOM đã thay đổi.

Ví dụ như

<h1 className="Greeting">Hello, Dan</h1>

Sang

<h1 className="Greeting">Hello, Luu</h1>

React sẽ thấy 2 object

const oldProps = { className: "Greeting", children: "Hello, Dan" };
const newProps = { className: "Greeting", children: "Hello, Yuzhi" };

Nó sẽ xác định được children bị thay đổi và cần update, còn className thì không, nó sẽ làm như sau

domNode.innerText = "Hello, Luu";

Chúng ta cũng muốn effect làm điều tương tự, khi re-render chỉ apply những update cần thiết

Ví dụ với component này

function Greeting({ name }) {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    document.title = "Hello, " + name;
  });

  return (
    <h1 className="Greeting">
      Hello, {name}
      <button onClick={() => setCounter(count + 1)}></button>
    </h1>
  );
}

useEffect không hề liên quan tới giá trị state counter, gọi document.title khi giá trị counter thay đổi không phải là ý hay.

Đó là lý do tại sao chúng ta có thêm tham số dependency (một mảng) khi dùng useEffect

useEffect(() => {
  document.title = "Hello, " + name;
}, [name]); // deps

Dịch ra ngôn ngữ con người là thế này: “Tao biết React mày không phân biệt được sự khác nhau bên trong function, nên tao hứa là tao chỉ dùng đến name bên trong function này thôi, và chỉ giá trị name này update thì mày hả gọi nó”

Một là không nói dối, 2 là không nói dối nhiều lần

Đừng bao giờ lừa gạt React bằng cách đưa dependency không đúng cho nó, hậu quả nhãn tiền. Hợp lý, nhưng nhiều lập trình viên quen sử dụng class sẽ cố tình qua mặt

function SearchResults() {
  async function fetchData() {
    // ...
  }

  useEffect(() => {
    fetchData();
  }, []);
  // việc ntn được hôn? không phải lúc nào cũng đúng
  // có cách viết tốt hơn
}

Bạn sẽ nghĩ là “Tao chỉ muốn chạy nó lúc mount thôi”. Nếu chúng ta chỉ định một dependency, tất cả giá trị bên trong component sử dụng bởi effect phải được khai báo cụ thể. Bao gồm prop, state, function

Đôi khi mà làm như vậy nó phát sinh lỗi. Thí dụ như gọi fetch data liên tục hoặc socket được tạo không cần thiết. Cách giải quyets là không xóa chúng khỏi dependency

Trước khi nói về cách giải quyết, chúng ta xem vấn đề ở đây là gì khi so sánh Dependency

Hậu quả của việc dối trá

Nếu mảng dependency chứa tất cả giá trị sử dụng trong useEffect, React biết được khi nào thì re-run nó

useEffect(() => {
  document.title = "Hello, " + name;
}, [name]);

Tìm hiểu sâu hơn về useEffect từ a tới z

Nhưng nếu chúng ta chỉ định [], nó không re-run sau lần đầu tiên

useEffect(() => {
  document.title = "Hello, " + name;
}, []); // thiếu name

Tìm hiểu sâu hơn về useEffect từ a tới z

useEffect(() => {
  document.title = "Hello, " + name;
}, []); // Sai: không được phép bỏ qua thằng name

Rõ ràng là 2 thằng dependency không khác nhau, nên nó sẽ không chạy effect

Trong tình huống này, vấn đề khá là hiển nhiên, nhưng trực giác có thể đánh lừa bạn trong các tình huống khác, lấy ví dụ, chúng ta muốn giá trị counter tăng đều sau mỗi giây. Với một class, trực giác sẽ mách bảo: “Set up cái interval một lần, rồi dứt tình vứt áo một lần”, kiểu như thế này, khi chuyển qua dùng useEffect bạn sẽ nghĩ đến dùng [] cho mảng phụ thuộc “Tao chỉ muốn tình một đêm”, đúng không?

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);

  return <h1>{count}</h1>;
}

Theo như lập luận rất hay gặp “danh sách phụ thuộc cho phép chúng ta chỉ định việc re-render effect khi nào”, và ở đây ta chỉ muốn trigger nó một lần vì nó là interval, nhưng tại sao lại có vấn đề ở đây?

Chúng ta đang muốn effect này chỉ chạy lần đầu tiên mà thôi, đưa vào dependencies là [] có vẻ hợp lý, React sẽ bỏ qua hết những lần sau, nhưng chúng ta đang lừa dối React, vì bên trong chúng ta có sử dụng giá trị count, chúng ta có giá trị phụ thuộc mà không khai báo. Thực tế setCount() sẽ gọi liên tục sau 1 giây, chứ không dừng lại sau lần gọi đầu tiên.

Ở lần render đầu tiên, count = 0, vì thế setCount(count + 1) ở lần render đầu tiên nghĩa là setCount(0+1), nhưng vì không re-run effect thêm lần nào nữa, chúng ta cứ gọi mãi setCount(0+1) ở những lần tiếp theo

// state = 0
function Counter() {
  // ...
  useEffect(
    // lần đầu
    () => {
      const id = setInterval(() => {
        setCount(0 + 1); // luôn là setCount(1)      }, 1000);
      return () => clearInterval(id);
    },
    [] // không re-run  );
  // ...
}

// state = 1
function Counter() {
  // ...
  useEffect(
    // không bao giờ chạy    () => {
      const id = setInterval(() => {
        setCount(1 + 1);
      }, 1000);
      return () => clearInterval(id);
    },
    []
  );
  // ...
}

Những con bug như thế này sẽ rất rất khó để mò ra được, vì thế hãy luôn thành thật với React, khai báo hết dependency đang có.

Tìm hiểu sâu hơn về useEffect từ a tới z

2 cách để thú thật với React về dependency

Nên chọn cách một, cách 2 chỉ áp dụng khi cần thiết

Cách 1: luôn là người trung thực, chính trực đạo đức hết mực, luôn khai báo đầy đủ thông tin bạn trai, bạn gái, ba má, chú bác nào bạn đang phụ thuộc cho cơ quan thuế

useEffect(() => {
  const id = setInterval(() => {
    etCount(count + 1);
  }, 1000);
  return () => clearInterval(id);
}, [count]);

Tuy nhiên thế này, khi giá trị count thay đổi, cái interval của chúng ta sẽ bị xóa và đặt lại lần nữa sau những lần render, nó không phải là cái chúng ta mong muốn nó hoạt động như vậy

Tìm hiểu sâu hơn về useEffect từ a tới z

Cách 2 là thay đổi tư duy, giảm bớt anh trai nuôi, em gái nuôi không cần thiết

Chúng ta không nói xạo, chúng ta giảm bớt số lượng những thứ phụ thuộc cho việc re-run effect

Để làm được việc này, chúng ta phải hỏi bản thân: chúng ta dùng count để làm gì? Có vẻ như chúng ta chỉ dùng nó cho việc gọi hàm setCount, chúng ta không thực sự cần giá trị count nếu chúng ta biết được giá trị trước đó, trường hợp trên, chúng ta có thể không cần dùng đến giá trị count mà dùng previous state

useEffect(() => {
  const id = setInterval(() => {
    setCount(c => c + 1);
  }, 1000);
  return () => clearInterval(id);
}, []);

Tìm hiểu sâu hơn về useEffect từ a tới z

Chạy thử

Tính năng update của Google Docs

Khi nói về effect, định hướng lập trình chúng ta là đồng bộ hóa, có một khái niệm khá thú vị khi thực hiện đồng bộ hóa là chúng ta thường không đồng bộ toàn bộ nội dung. Lấy ví dụ như Google Docs, nó không thực sự truyền tải cả trang lên phía server, làm như vậy hiệu năng sẽ rất tệ, cái nó làm là gửi đi một thông tin chứa cái mà user đang muốn thực hiện.

Tốt nhất truyền đi thật ít thông tin từ effect (chỉ những thông tin cần thiết nhất) vào trong component. Hàm setCount(c => c + 1) sẽ gửi đi ít thông tin hơn so với hàm setCount(count + 1) đứng trên một khía cạnh nào đó vì nó không phụ thuộc giá trị hiện tại, sử dụng ít state nhất có thể để đạt được kết quả là một trong các nguyên lý chính của đợt cập nhập React với effect

Tuy nhiên không phải lúc nào cuộc sống cũng đơn giản với bạn như vậy, nếu chúng ta muốn tính toán giá trị của state mới dựa trên một prop, 2 giá trị state phụ thuộc lẫn nhau, setState là không đủ. Chúng ta có người chị em hàng xóm tên useReducer

function Counter({ step }) {
  const [count, dispatch] = useReducer(reducer, 0);

  function reducer(state, action) {
    if (action.type === "tick") {
      return state + step;
    } else {
      throw new Error();
    }
  }

  useEffect(() => {
    const id = setInterval(() => {
      dispatch({ type: "tick" });
    }, 1000);
    return () => clearInterval(id);
  }, [dispatch]);

  return <h1>{count}</h1>;
}

Cách dùng useReducer như vậy là một dạng cheat mode của hook, cho phép chúng ta bỏ qua các dependency ngầm khỏi effect, và chặn re-run không không cần thiết

Chạy thử

Bài viết này vẫn còn, và nếu bạn vẫn còn muốn đào sâu hơn nữa, có thể tìm đọc bài viết gốc của Dan A Complete Guide to useEffect

Bài viết gốc được đăng tải tại Vuilaptrinh

Những kỹ năng cần thiết cho freelancer

Tác giả: Thi Trần

Trước khi qua Singapore làm việc, mình đã có 1 khoảng thời gian khá dài là 1 freelancer chuyên nhận những dự án phần mềm trực tiếp từ khách hàng và ngoài ra mình cũng làm đầu mối đưa cho bạn bè thực hiện nhiều dự án khác nhau. Nhờ đó mình tích lũy được 1 số kinh nghiệm cũng như rút ra được những kỹ năng cần có để trở thành 1 freelancer tốt.

Kỹ năng chuyên môn

Mình nghĩ đây là yếu tố tiên quyết để bạn có thể nhận dự án. Mình thường thấy các bạn freelancer thường bắt đầu làm ở 1 cty rồi mới tách ra làm riêng khi đã tích lũy đủ kiến thức, kinh nghiệm. Cũng có bạn có khả năng kết nối công việc cho những freelancer khác thì có thể không cần chuyên môn nhưng bài viết này mình sẽ tập trung cho bạn nào làm trực tiếp với khách hàng nhé.

Kỹ năng “deal” cho dự án

1. Làm thế nào để ước tính giá trị cho 1 dự án freelancer?

Nhiều bạn mới bắt đầu làm 1 dự án riêng thường phân vân không biết định giá thế nào hay hỏi mình câu này lắm. Đa số những dự án mình đã thực hiện được ước tính dựa trên “hour rate”, nghĩa là giá tiền/giờ. Sau đó mình sẽ ước lượng tổng số giờ thực hiện xong dự án và cộng thêm khoảng 15% dành cho việc phản hồi, chỉnh sửa từ khách hàng.

Ví dụ hour rate trước đây của mình là 20USD/giờ, giả sử dự án mình ước tính phải thực hiện trong 50 giờ thì tổng chi phí của mình được tính ra như sau:

Tổng chi phí = 20 * (50 + 50 * 0.15) = 1150 USD

Ngoài ra, bạn sẽ phải gặp 1 số dạng dự án mà khách hàng đưa ra mức tiền tối đa thì lúc đó phải tính theo phương pháp khác. Đó là tính ngược lại để ra tổng số giờ, dựa trên số giờ đó để tính ra số lượng giờ thực hiện cho các chức năng, dùng thư viện, framework có sẵn hay không để giảm bớt công sức bạn phải bỏ ra.

2. Làm thế nào để ước tính thời gian cho 1 dự án freelancer?

Cái này phải dựa trên kinh nghiệm thực chiến, càng làm nhiều thì bạn sẽ dễ dàng đánh giá được từng chức năng tốn thời gian bao nhiêu, có rủi ro gì không,… hoặc nếu bạn quen ai đó có kinh nghiệm thì có thể trao đổi thử rồi đưa ra ước lượng. Kinh nghiệm của mình là bạn có thể tăng 1 số giờ cho các chức năng bạn đã xác định được lên 1 chút để có thể bù lại cho chức năng bạn vẫn chưa chắc chắn thì sẽ giảm rủi ro hơn.

3. Chốt deal với khách những chức năng cần có

Đây là 1 thao tác CỰC KỲ QUAN TRỌNG nếu như bạn không muốn ôm hận về sau. Trước khi chốt hạ với khách, bạn cần lên được 1 danh sách những chức năng sẽ làm, tốt nhất là 1 bảng báo giá cụ thể để khách xem rồi sau đó yêu cầu họ làm 1 bảng hợp đồng thỏa thuận 2 bên rõ ràng để nếu họ có yêu cầu thêm những chức năng không nằm trong đó cũng có cái để làm bằng chứng. Hầu như dự án nào cũng sẽ có phần phát sinh, bạn cũng nên cân nhắc nếu phần đó không tốn quá nhiều thời gian thì cũng nên hỗ trợ miễn phí để họ vui và biết đâu dự án tới họ sẽ tìm đến bạn.

Mình nhắc lại là PHẢI CÓ HỢP ĐỒNG, đặc biệt là lần đầu tiên làm việc với họ bởi giấy trắng mực đen là cái đem ra trao đổi dễ nhất và ngay cả khách hàng của bạn cũng sẽ cần cái đó để đảm bảo bạn có thực hiện theo cam kết hay không. Mình có 1 khách hàng làm khá nhiều dự án, đến lần thứ 4, thứ 5 mình bỏ qua bước làm hợp đồng này và cuối cùng phải chịu thiệt làm thêm 1 số cái cũng như thời gian chuyển khoản bị delay.

4. Deal với những thứ phát sinh bằng đàm phán

Mình chắc chắn dự án kiểu này luôn có phần phát sinh và bạn phải là người linh hoạt xử lý sao cho mềm mại, thuyết phục khách hàng bằng lý lẽ vì chất lượng của dự án để không làm họ khó chịu. Nhiều bạn gặp phần này thường rất khó xử, hoặc đôi khi mất bình tỉnh nhưng nên giữ cái đầu lạnh, nếu cần thiết thì lấy bản hợp đồng ra nói chuyện nhé. Có 1 số cái trao đổi miệng thì sau buổi họp bạn cần gửi 1 tin nhắn hoặc 1 email để chốt lại để có bằng chứng nhé.

Kỹ năng sắp xếp độ ưu tiên

Mình nghĩ phần này quan trọng hơn cả sắp xếp thời gian, bởi khi làm việc với khách hàng, bạn phải biết họ cần xem gì theo từng giai đoạn. Cách đây mình giao 1 dự án cho đứa em làm và bị khách phàn nàn vì sau cả tuần vẫn không thấy có gì thay đổi. Mình trao đổi với em nó thì mới biết em nó tập trung làm phần backend nhiều quá. Nên cố gắng tập trung cho khách hàng thấy sự thay đổi theo tiến độ, đừng để tư duy “tech” ảnh hưởng bởi có những khách hàng họ không cần quan tâm bên dưới thế nào đâu.

Kỹ năng giải quyết vấn đề

Do tính chất mỗi dự án mỗi khác, có nhiều vấn đề khác nhau nên đòi hỏi bạn phải có khả năng nghiên cứu, giải quyết vấn đề bằng “mọi thủ đoạn” để “get shit done”. Cần phải luôn dựa trên số thời gian đã xác định từ đầu để bạn đưa ra giải pháp hợp lý, tránh bị sa đà quá cỡ dẫn tới chịu thiệt thòi. Nhưng nếu bạn chủ động chịu thiệt thòi để học hỏi thì thoải mái nhé.

Kỹ năng giao tiếp

Phải có 1 kênh giao tiếp để giữ liên lạc thường xuyên với khách hàng để họ thấy bạn làm việc chuyên nghiệp. Có những bạn mình biết khi dự án bị trễ tiến độ hoặc có vấn đề thì thường tỏ ra sợ hãi hoặc có dấu hiệu “mất tích” không xem tin nhắn khách hàng. Đừng như vậy, lớn hết rồi, cứ thẳng thắn trao đổi với khách hàng vấn đề bạn đang gặp phải và đưa ra hướng giải quyết. Có nhiều bạn lấy lý do này nọ kiểu nhà em bị mất internet, điện thoại em hỏng, máy em hư các kiểu mà không có phương án tự giải quyết thì chỉ làm khách hàng cảm thấy khó chịu hơn thôi. Họ thuê bạn để giải quyết vấn đề cho họ chứ không phải họ giải quyết vấn đề của bạn.

Kỹ năng xây dựng thương hiệu cá nhân

Dù hiện giờ mình ở Singapore nhưng vẫn thường xuyên có bạn bè nhờ làm các dự án freelancer, ít nhất đã có 5 dự án mình đã giới thiệu lại cho mấy đứa em ở Việt Nam từ khi mình qua đây.

Và điều cuối cùng mình lưu ý đó chính là sức khỏe. Nên chia đều thời gian ra để làm, đừng để đến giai đoạn cuối mới bắt đầu bức tốc thì rất dễ cạn kiệt sức lực. Chúng ta kiếm tiền để tận hưởng cuộc sống chứ không phải kiếm tiền để mua thuốc trị bệnh. Nến hãy cố gắng cân bằng thời gian, ăn uống điều độ thì mới có đủ sức khỏe cày cuốc được nhiều dự án khác nữa nhé.

Bài viết gốc được đăng tải tại Medium

Top 5 website giúp thiết kế CV chuẩn format, đủ nội dung

CV hay còn gọi là Curriculum Vitae, một bản sơ yếu lý lịch tóm tắt những thông tin cơ bản về mỗi ứng viên.

Theo một cuộc khảo sát về những người làm nghề nhân sự của Eric Hilden,những yếu tố được nhà tuyển tìm kiếm và đánh giá cao trong CV của ứng viên tương ứng như sau:

1. Kinh nghiệm trong những công viên liên quan: 45%

2. Kỹ năng và trình độ chuyên môn: 44%

3. Yếu tố thẩm mỹ: 25%

4. Thành tích cá nhân: 16%

5. Chính tả và ngữ pháp: 14%

6. Có mục tiêu nghề nghiệp và khao khát thành công: 11%

7. Những “từ khóa”, trải nghiệm cá nhân, sở thích,…: 5%

Với thời buổi công nghệ phát triển như hiện nay thì việc bạn tự thiết kế một CV đúng chuẩn là điều không quá khó khăn. Một bản CV ấn tượng là bước đầu tiên giúp bạn chứng tỏ năng lực để chinh phục các nhà tuyển dụng. Ngay sau đây, cùng tham khảo top 5 website giúp bạn tạo cho mình những CV thật hoàn hảo.

1. TopDev CV

Được đánh giá là một trong những chuyên trang trực tuyến miễn phí nổi bật, CV TopDev là giải pháp hiệu quả nhất giúp các ứng viên kết nối nguồn nhân lực với những tập đoàn lớn trong và ngoài nước.

TopDev CV IT

Với giao diện tiện ích, TopDev cho phép bạn tự thiết kế những CV IT chuẩn Developer và đúng nội dung đồng thời phù hợp với sở thích, năng lực của mình.

Tạo CV Online miễn phí

2. Resumedone

Nếu bạn yêu thích những Curriculum Vitae theo phong cách chuyên nghiệp nước ngoài thì Resumedone là sự lựa chọn hoàn hảo. Website tập trung tạo những khung thiết kế nhằm đáp ứng nhu cầu tuyển dụng chất lượng cao. Resumedone cũng là nơi thích hợp để tìm kiếm các CV tiếng anh chất lượng nhất.

  Cách viết CV giúp lập trình viên ghi điểm với nhà tuyển dụng

Đây được xem là một trong những công cụ trực tuyến tốt nhất nếu bạn muốn tạo ra một bản CV vừa chuyên nghiệp vừa ấn tượng. Tuy nhiên, điều chắc chắn là thời gian bạn đầu tư cho nó cũng sẽ tốn nhiều hơn so với các công cụ đơn giản khác.

3. EnhanCV

EnhanCV là công cụ tạo CV online với nhiều kiểu thiết kế đa dạng, độc đáo.

EnhanCV từng gây sốt trong giới tuyển dụng khi trở thành trang web được yêu thích nhất vì sự đánh giá tích cực từ các ứng viên. Tuy nhiên đối với trang web này, bạn có thể sẽ phải trả phí khi tải và tạo một CV song, vẫn có nhiều bản miễn phí để các bạn lựa chọn.

4. Resume

Resume được đánh giá là một trong những công cụ trực tuyến tốt nhất nếu bạn muốn tạo ra một bản CV vừa chuyên nghiệp vừa ấn tượng. 

Khả năng về sự điều chỉnh với những giao diện thiết kế tinh tế là một trong những ưu điểm của phần mềm này. Đồng thời, một điểm cộng lớn cho Resume là bạn có thể dễ dàng định dạng CV theo cả hai dạng PDF hoặc DOC tùy vào yêu cầu của nhà tuyển dụng.

5. Resume Coach

Đây cũng là một trong những công cụ được sử dụng phổ biến vì mức độ đơn giản của nó.

 

Resume Coach giúp tạo ra mẫu CV theo nền tảng có sẵn và các bước để thiết kế một CV trên phần mềm này cũng không mất quá nhiều thời gian. Tuy vậy, với khả năng tùy chỉnh cao và dễ sử dụng, bạn có thể thỏa sức sáng tạo trên bản mẫu theo cá tính riêng của bạn.

Trên đây là top 5 website giúp bạn thiết kế những bộ CV chuẩn nhất. Hãy lựa chọn và tự thiết kế ngay cho mình những CV chất lượng để có thể gây ấn tượng đối với nhà tuyển dụng nhé! 

Có thể bạn quan tâm:

Xem thêm Tạo CV Online cho IT đẹp nhất tại TopDev

SASS/SCSS là gì?

Bất cứ một lập trình viên nào đều phải từng làm việc liên quan đến CSS. Tuy nhiên làm việc với CSS thuần một thời gian dài bạn sẽ thấy nó rất nhàm chán. Bạn có thể viết CSS một cách chuyên nghiệp hơn, nhanh và rõ ràng mạch lạc hơn bằng SASS/SCSS.

CSS Preprocessor là gì?

CSS Preprocessors là ngôn ngữ tiền xử lý CSS. Là một ngôn ngữ kịch bản mở rộng của CSS và được biên dịch thành cú pháp CSS giúp bạn viết CSS nhanh hơn và có cấu trúc rõ ràng hơn. CSS Preprocessor có thể giúp bạn tiết kiệm thời gian viết CSS, dễ dàng bảo trì và phát triển CSS.

SASS/SCSS là gì?

SASS/SCSS là một chương trình tiền xử lý CSS (CSS preprocessor). Nó giúp bạn viết CSS theo cách của một ngôn ngữ lập trình, có cấu trúc rõ ràng, rành mạch, dễ phát triển và bảo trì code hơn. Ngoài ra nó có rất nhiều các thư viện hỗ trợ kèm theo giúp bạn viết code CSS một cách dễ dàng vào đơn giản hơn. Có rất nhiều loại CSS Preprocessor trong đó bao gồm SASS, Stylus hay LESS.

SASS và SCSS về bản chất vấn đề là giống nhau, chỉ khác nhau ở cách viết 

Sass là chữ viết tắt của Syntactically Awesome Style Sheets, chương trình tiền xử lý bằng ngôn ngữ kịch bản (Preprocessor Scripting Language ), sẽ được biên dịch thành CSS. Nghĩa là, mình sẽ làm style bằng SASS, rồi SASS sẽ render việc mình làm thành file CSS.

SASS bản thân có hai kiểu viết khác nhau, một kiểu như là HAML, Pug – Sử dụng indent (cách thụt đầu dòng) để phân tách các khối code , sử dụng xuống dòng để phân biệt rules , có phần mở rộng là .sass. 

.header
  color: #f3f3f3;

.header__inner
  border: 1px solid #f3f3f3

SCSS sử dụng cú pháp giống với Ruby (vì đơn giản nó được thiết kế bởi các lập trình viên Ruby)Có phần mở rộng là .scss , SCSS ra đời sau SASS và có cú pháp viết tương tự như cách viết CSS. Cú pháp này được tạo ra nhằm thu hẹp khoảng cách giữa SASS và CSS bằng cách mang lại một thứ gì đó thân thiện với CSS. Trong hình phía dưới:

– Bên trái: Được viết bằng SCSS

– Bên phải: là code CSS được biên dịch từ SCSS

  Xây dựng một bộ source SASS thế nào cho đẹp
  11 công cụ hữu ích để kiểm tra và tối ưu hóa các file CSS

Các tính năng cơ bản của SCSS

Xếp chồng – Nested Rules

Đây là một tính năng rất hay của SCSS và được sử dụng rất thường xuyên.

Hãy thử nhìn vào một đoạn HTML như sau:

 <div class="container">
    <div class="row">
        <div class="navbar col-14">
            <a class="brand">TopDev</a>
            <ul class="menu">
                <li><a href="#">Menu 1</a></li>
                <li><a href="#">Menu 2</a></li>
            </ul>
        </div>
    </div>
</div>

Ví dụ nếu bạn chỉ muốn CSS cho thẻ ul với class menu, với CSS thuần bạn sẽ viết

.navbar ul.menu {
    list-style: none;
}

Nếu bạn tiếp tục muốn CSS cho thẻ li trong thẻ ul (có class là menu) thì

.navbar ul.menu li {
    padding: 3px;
}

Sau đó bạn muốn tiếp tục CSS cho thẻ a trong thẻ li… bạn sẽ phải lặp đi lặp lại tên tag (hoặc class, hoặc id) cha của thẻ muốn css thì sẽ rất mệt và nhàm chán. Thay vào đó bạn có thể dùng Nested Ruled của SASS để giúp mọi thứ trở nên đơn giản hơn một cách rõ rệt. Ví dụ:

.navbar {
    ul.menu {
        list-style: none;

        li {
            padding: 3px;

            a {
                text-decoration: none;
            }
        }
    }
}

Và sau khi được đoạn SASS trên được compile ra CSS thuần sẽ như sau:

.navbar ul.menu {
    list-style: none;
}
.navbar ul.menu li {
    padding: 3px;
}
.navbar ul.menu li a {
    text-decoration: none;
}

Thực tế mình nhận thấy rằng quy tắc xếp chồng này cũng được sử dụng rất nhiều khi vào 1 project có viết css.

Biến – variable

Sử dụng biến với SCSS vô cùng cơ bản, bạn chỉ cần đặt tên cho biến – bắt đầu bằng $. Biến chứa đựng các giá trị mà chúng ta dùng nhiều lần ví dụ như mã màu, font hay kiểu chữ.

$RedColor = #fff;

.navbar {
    ul.menu {
        list-style: none;

        li {
            padding: 3px;

            a {
                text-decoration: none;
                color: $Redcolor
            }
        }
    }
}

Quy tắc Mixin

Mixin giúp bạn tạo các hàm được sử dụng trong SCSS, bạn hoàn toàn có thể truyền các tham số vào bên trong nó để sử dụng.

Mixin là một cơ chế khá phổ biến trong SASS. Công dụng của nó là mang nhiều thuộc tính mà bạn đã quy ước trong một mix nào đó rồi @include vào một thành phần bất kỳ mà không cần phải viết lại các thuộc tính đó (Ví dụ ở trên là color vs font-style)

@mixin colorVsStyle {
    color: #f06;
    font-style: italic;
}

.navbar {
    ul.menu {
        list-style: none;

        li {
            padding: 5px;

            a {
                text-decoration: none;
                @include colorVsStyle;
            }
        }
    }
}

Hoặc nếu bạn không muốn color lúc nào cũng là #f06, thì bạn có thể truyền thuộc tính vào mix như 1 tham số bằng cách viết như sau:

@mixin colorVsStyle($color, $fontStyle) {
    color: $color;
    font-style: $fontStyle;
}

.navbar {
    ul.menu {
        list-style: none;

        li {
            padding: 5px;

            a {
                text-decoration: none;
                @include colorVsStyle(#000, italic);
            }
        }
    }
}

Nó kiểu như truyền params vào method ấy

Kế thừa – Extends

Khi nghe đến extends hay còn gọi là kế thừa, thì có thể bạn sẽ nghĩ ngay đến OOP (lập trình hướng đối tượng) đúng không? Cách làm sẽ là bạn định nghĩa ra 1 class, rồi những tag nào cần thì @extend nó vào là xong:

.title-box {
    color: ##2EFEC8;
    text-shadow: 0px 0px 10px #6E6E6E;
    display: inline-block;
    text-transform: uppercase;
}

.navbar {
    ul.menu {
        list-style: none;

        li {
            padding: 4px;

            a {
                text-decoration: none;
                @extend .title-box;
            }
        }
    }
}

Import

Cú pháp import của SASS rất hữu dụng và thường xuyên được sử dụng trong các project. Nó tương tự cách bạn require hay include file này vào file khác trong PHP.

Đặt trường hợp bạn có 1 trang index, bao gồm header, body, footer. Thay vì sử dụng CSS cho những cái trên vào một style.css thì với SASS bạn sẽ thực hiện như sau, nhớ có dấu _ trước tên file được import:

  1. Tạo 1 file _header.scss để CSS riêng cho header.
  2. _body.scss để CSS riêng cho body.
  3. _footer.scss để CSS riêng cho footer.
  4. Rồi ở file style.css ta chỉ cần @import 3 file trên là mượt mà ngay

_header.scss

#header {
    // viết code sass ở đây
}

_body.scss

#body {
    // viết code sass ở đây
}

_footer.scss

#footer {
    // viết code sass ở đây
}

style.scss

@import 'header';
@import 'body';
@import 'footer';

// viết code sass ở đây

Vòng lặp

scss

scss

scss

Mệnh đề điều kiện IF

scss

Trình compile SASS

Hiện nay tồn tại tương đối nhiều trình biên dịch SASS sang CSS thuần. Trong đó có hai trình biên dịch mình xài thường xuyên, mình sẽ giới thiệu ở dưới. Ngoài ra bạn có thể tự search thêm những trình duyệt khác nhé.

topdev

  1. Koala
  • Đây là một phần mềm dùng để compile CSS Preprocessor như SASS, LESS… mình hay dùng nó khi viết SASS.
  • Nó hoàn toàn free nên bạn có thể tải và cài đặt nó tại: http://koala-app.com/

2. Laravel Mix

  • Nếu bạn đang làm việc bằng Laravel thì bạn không cần đến phần mềm thứ 3 đâu vì bản thân Laravel đã tích hợp một công cụ tên là Laravel Mix rất đa năng, compile các CSS Preprocessor sang CSS thuần là một trong những tính năng xịn xò của nó.
  • Bạn có thể tìm hiểu thêm về nó tại: https://laravel.com/docs/5.7/mix

Kết luận

Như những gì mình vừa trình bày ở phía trên, các bạn cũng đã có thể thấy những sức mạnh mà SASS/SCSS mang lại trong việc viết CSS, nó biến việc làm việc với SCSS như làm việc với một ngôn ngữ lập trình thực sự. Ngoài ra, với việc phải biên dịch từ SCSS ra CSS cũng cho phép chúng ta có thể sử dụng 1 số các tính năng như: tự động thêm prefix vào các thuộc tính CSS3, định dạng lại các tệp tin CSS (nén hoặc ko nén).

Có thể bạn muốn xem thêm:

Xem thêm tuyển dụng it hấp dẫn lương cao tại TopDev!