Home Blog Page 169

Gắn video làm background toàn màn hình

Chia sẻ kỹ thuật làm video background để chạy full màn hình

Kết quả các bạn có thể xem trên trang mình mới làm http://chat-production.com/

Để có một cái video background chạy toàn màn hình, chúng ta sẽ nghĩ ngay đến việc dùng thẻ <video /> của HTML5, đặt nó trong một cái <div/> có kích thước width: 100%, buồn thay nó không đúng như chúng ta mong đợi.

<!-- HTML -->
<div id="videoContainer">
  <video loop autoplay muted playsinline>
    <source src="/show-reel.mp4" type="video/mp4" />
  </video>
</div>

Thêm playsinline để có thể chạy trên IOS như iPhone

#container {
  overflow: hidden;
  height: 60vw;
  position: relative;
}
 
video {
  width: 100%;
  height: 100%;   
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
}

Thứ nhất là nó sẽ không tự scale ra 100% của màn hình, thứ 2 khi chúng ta kéo to thu nhỏ cửa sổ trình duyệt, tỷ lệ khung hình (ngang và đứng) cũng sẽ không đạt đúng tỉ lệ thật của video.

  Đặt tên sao cho đẹp trong JavaScript

Để video có thể thay đổi kích thước theo từng giá trị màn hình khác nhau, chúng ta phải dùng đến javascript và css

#videoContainer {
  height: 100vh;
  overflow: hidden;
  position: relative;
  @media (max-width: 780px) {
    height: 60vw;
  }
  video {
    width: 100vw;
    position: absolute;
    /* canh video ngay giữa */
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
  }
}

Source này được lấy từ https://codetheory.in/html5-fullscreen-background-video/, mình sử dụng chung với React

componentDidMount() {
  // JS
  var video = document.querySelector("video"),
    container = document.querySelector("#videoContainer");

  var setVideoDimensions = function() {
    var w = video.videoWidth,
      h = video.videoHeight;

    // Intrinsic Ratio
    // Will be more than 1 if W > H and less if W < H
    var videoRatio = (w / h).toFixed(2);

    // Get the container's computed styles
    //
    // Also calculate the min dimensions required (this will be
    // the container dimensions)
    var containerStyles = window.getComputedStyle(container),
      minW = parseInt(containerStyles.getPropertyValue("width")),
      minH = parseInt(containerStyles.getPropertyValue("height"));

    // What's the min:intrinsic dimensions
    //
    // The idea is to get which of the container dimension
    // has a higher value when compared with the equivalents
    // of the video. Imagine a 1200x700 container and
    // 1000x500 video. Then in order to find the right balance
    // and do minimum scaling, we have to find the dimension
    // with higher ratio.
    //
    // Ex: 1200/1000 = 1.2 and 700/500 = 1.4 - So it is best to
    // scale 500 to 700 and then calculate what should be the
    // right width. If we scale 1000 to 1200 then the height
    // will become 600 proportionately.
    var widthRatio = minW / w,
      heightRatio = minH / h;

    // Whichever ratio is more, the scaling
    // has to be done over that dimension
    let newWidth = 0;
    let newHeight = 0;
    if (widthRatio > heightRatio) {
      newWidth = minW;
      newHeight = Math.ceil(newWidth / videoRatio);
    } else {
      newHeight = minH;
      newWidth = Math.ceil(newHeight * videoRatio);
    }

    video.style.width = newWidth + "px";
    video.style.height = newHeight + "px";
  };

  video.addEventListener("loadedmetadata", setVideoDimensions, false);
  window.addEventListener("resize", setVideoDimensions, false);
}

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

Xem thêm việc làm Web Developer tại TopDev

TopDev via Vuilaptrinh

Thực ra bạn không cần jQuery

Không cần jQuery, bạn có thể dùng javascript thuần để thực hiện những thao tác trước đây bạn nghĩ phải có jQuery cơ.

Hiện giờ chúng ta đã làm việc nhiều với các framework hại điện hơn nhiều so với jQuery, nên rất ít chi đụng tới DOM thật, không còn ngày tháng xào trộn HTML bằng jQuery, chúng ta chỉ định nghĩa khi nào render, render cái gì. Tuy nhiên, kỹ năng sờ mó vào DOM là cần thiết cho mọi lập trình viên FE. Bây giờ muốn sờ vào DOM chúng ta cần nắm cách dùng javascript thuần.

Chọn element

document.querySelector(‘.vuilaptrinh’)

Nó trả về một HTMLElement object đầu tiên thỏa điều kiện CSS Selector, cái .vuilaptrinh này gọi là CSS Selector.

Nói là đầu tiên vì nếu có dăm ba cái .vuilaptrinh nó cũng chỉ trả về thằng đầu tiên

<p class="vuilaptrinh" />
<div class="vuilaptrinh" />
<a class="vuilaptrinh" />

document.querySelectorAll(‘.vuilaptrinh’)

Nó sẽ trả về một danh sách các element thỏa điều kiện CSS selector, ví dụ 3 thằng .vuilaptrinh ở trên đều được chọn.

Chạy function trên các element đã chọn

“Sờ” được các element này rồi, thì chúng ta sẽ muốn làm tiếp cái gì đó, chứ không chỉ sờ cho vui, chúng ta phải loop qua toàn bộ element đã sờ được bằng vòng lặp .forEach

document.querySelectorAll('.vuilaptrinh').forEach(vui => { vui.style.display = "none" }

Tìm element con bên trong element cha đã chọn

Chúng ta gọi lại .querySelector trên element cha thôi

var cha = document.querySelector('.vuilaptrinh')
cha.querySelector('.luckyluu')

Di chuyển qua các element kề cận

Hồi nhỏ học tiếng anh, cô dạy “brother”, “sister” là anh chị em, lớn lên xem Youtube mới biết, tụi nước ngoài nó dùng từ sibling để nói anh chị em, chứ ít khi dùng brother, sister. Javascript bê luôn nguyên chữ này vào để xài

<div class="bochungno">
<div class="anhtrai" />
<div class="vuilaptrinh" />
<div class="emgai" />
</div>
var box = document.querySelector('.vuilaptrinh')
box.nextElementSibling; // emgai
box.previousElementSibling; // anhtrai
box.parentElement; // bochungno

Gắn sự kiện

addEventListener

document.querySelector(".button").addEventListener("click", (e) => { /* ... */ });
document.querySelector(".button").addEventListener("mouseenter", (e) => { /* ... */ });
document.addEventListener("keyup", (e) => { /* ... */ });

Dispatch event

Nếu cần bún ra một sự kiện bằng javascript một cách chủ động, ko đợi sự kiện tự xảy ra

dispatchEvent

document.dispatchEvent( new Event('myEvent') )
document.querySelector('.box').dispatchEvent(new Event('myEvent'))

Styling cho element

Cái này cần tra cứu và học một số thuộc tính hay xài, nguyên tắc chung là viết thuộc tính css theo kiểu camelCase

var box = document.querySelector(".box");
box.style.color = "#000";
box.style.backgroundColor = "red";
box.style.paddingLeft = "10px";

Ẩn hiện element

style.display

Trỏ đến thuộc tính style, thay đổi giá trị display thành none hoặc block

document.querySelector(".box").style.display = "none";
document.querySelector(".box").style.display = "block";

Document ready

Một trong những tình huống hay gặp là chúng ta cần chạy một đoạn javascript sau khi HTML đã render xong

DOMContentLoaded

// khai báo hàm ready để xài cho tiện
var ready = callback => {
	if (document.readyState != 'loading') callback()
	else document.addEventListener('DOMContentLoaded', callback)
}

ready(() => {
 // thực hiện xử lý gì đó
})

Làm việc với class

classList

Thông qua classList chúng ta có thể thêm-xóa-toggle một class

var box = document.querySelector('.box')
box.classList.add('focus')
box.classList.remove('focus')
box.classList.toggle('focus')
box.classList.replace('focus', 'blurred')

Để kiểm tra element có class nào đó không

classList.contains

Ví dụ, kiểm tra xem element là .box có chứa class là active không

document.querySelector('.box').classList.contains('focus')

Network request

fetch

Mình có bài chi tiết về API này rồi, các bạn đọc lại Giới thiệu fetch() của javascript

Tạo element

Tạo thẻ <div /> với nội dung là <div>text</div>, rồi chèn nó vào <div class='box' />

var el = document.createElement('div')
// thêm nội dung text
el.textContent = 'text'
// chèn element nào vào đâu đó
document.querySelector('.box').appendChild(el)

TopDev via Vuilaptrinh

Đặt tên sao cho đẹp trong JavaScript

dat-ten-sao-cho-dep-trong-javascript
Bài viết được sự cho phép của tác giả Lưu Bình An
Cách nguyên tắc chung khi đặt tên trong Javascript. 

Tên biến

Trong Javascript, tên biến phân biệt hoa thường

var name = 'Vui Lap Trinh';
var Name = 'Lap Trinh Vui';
var NAME = 'Trinh Lap Vui';
console.log(name);
// "Vui Lap Trinh"
console.log(Name);
// "Lap Trinh Vui"
console.log(NAME);
// "Trinh Lap Vui"

Tên biến cần phải rõ nghĩa, không cần phải ghi chú gì thêm, nhìn vào tên biến là có thể biết được nó chứa thông tin gì

❌ Không ngon
var value = 'Vui';
❌ Không ngon
var val = 'Vui';

✅ Chuẩn cơm mẹ nấu
var firstName = 'Vui';

Viết Javascript được khuyến khích sử dụng tên biến theo kiểu con lạc đà

❌ Không ngon
var firstname = 'Vui';
❌ Không ngon
var first_name = 'Vui';
❌ Không ngon
var FIRSTNAME = 'Vui';
❌ Không ngon
var FIRST_NAME = 'Vui';

✅ Chuẩn cơm mẹ nấu
var firstName = 'Vui';

Các trường hợp ngoài lệ, có luật riêng là hằng số, biến cục bộ, class, component

Biến Boolean

Với biến mang giá trị là Boolean (true/false, 0/1), thêm tiền tố ishasare

❌ Không ngon
var visible = true;

✅ Chuẩn cơm mẹ nấu
var isVisible = true;

❌ Không ngon
var equal = false;

✅ Chuẩn cơm mẹ nấu
var areEqual = false;

❌ Không ngon
var encryption = true;

✅ Chuẩn cơm mẹ nấu
var hasEncryption = true;

Đặt tên class

Tên class được đặt theo kiểu PascalCase

class FrontendDeveloper {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
}
var me = new FrontendDeveloper('Vui', 'Lap Trinh');

Đặt tên hàm, phương thức của một class

Hàm cũng đặt tên theo con lạc đà, tốt nhất nên diễn đạt hàm đó làm gì bằng cách thêm một tiền tố là một động từ

❌ Không ngon
function name(firstName, lastName) {
  return `${firstName} ${lastName}`;
}

✅ Chuẩn cơm mẹ nấu
function getName(firstName, lastName) {
  return `${firstName} ${lastName}`;
}

Một số tiền tố hay được sử dụng là: getfetchpushapplycalculatecomputepost

class FrontendDeveloper {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
  // ✅ Chuẩn cơm mẹ nấu
  getName() {
    return `${this.firstName} ${this.lastName}`;
  }
}
var me = new FrontendDeveloper('Vui', 'Lap Trinh');
console.log(me.getName());
// "Vui Lap Trinh"

Phương thức, biến cục bộ

Thêm tiền tố _ vào trước biến, phương thức cục bộ của một class

class FrontendDeveloper {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.name = _getName(firstName, lastName);
  }
  _getName(firstName, lastName) {
    return `${firstName} ${lastName}`;
  }
}
var me = new FrontendDeveloper('Vui', 'Lap Trinh');

✅ Chuẩn cơm mẹ nấu
var name = me.name;
console.log(name);
// "Vui Lap Trinh"

❌ Không ngon
name = me._getName(me.firstName, me.lastName);
console.log(name);
// "Vui Lap Trinh"

Hằng số

Viết hoa tất cả nếu nó là hằng số

const SECONDS = 60;
const MINUTES = 60;
const HOURS = 24;
const DAY = SECONDS * MINUTES * HOURS;

Dash

Javascript không ưa gì ký tự -, tránh sử dụng - khi khai báo

 Không ngon
var person = {
  'first-name': 'Vui',
  'last-name': 'Lap Trinh',
};
var firstName = person['first-name'];

 Chuẩn cơm mẹ nấu
var person = {
  firstName: 'Vui',
  lastName: 'Lap Trinh',
};
var firstName = person.firstName;

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

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

Xem thêm việc làm IT tại TopDev

Tôi đã tối ưu WordPress nhanh hơn 18 lần như thế nào?

TTFBThời gian chờ nhận byte dữ liệu phản hồi đầu tiên từ server, server xử lý càng nhanh thì thời gian chờ càng ít, chứng tỏ càng hiệu quả:

  • Trước khi tối ưu: 648.92ms
  • Sau khi tối ưu: 36.57ms

Tính ra, website đã load nhanh hơn 18 lần so với ban đầu. OK, giờ ghi chú lại quá trình làm của mình thôi nào!

Trước tiên, muốn giảm độ trễ cần xác định request của mình được xử lý và đi qua những “hop” như thế nào.

  • Khi người dùng truy cập vào trang chủ (GET / ), request này sẽ được gửi lên server qua Internet. Tùy thuộc vào tốc độ kết nối mạng giữa client và server mà độ trễ cao hay thấp. Đây là điểm delay thứ 1.
  • Khi request đến được server sẽ trải qua nhiều bước để lên đến được tầng application và gặp Web Server. Web server sẽ tiếp nhận và xử lý request, đây là điểm delay thứ 2.
  • Web server nhận thấy request cần được PHP xử lý nên đẩy vào trong cho PHP + MySQL, đây là điểm delay thứ 3.
  • Sau khi PHP xử lý xong sẽ ra kết quả là 1 cục HTML, ném cục này lại cho Web Server và Web server sẽ ném cục này lại cho Browser.
  • Browser nhận được cục HTML sẽ hiển thị lên cho người dùng và đồng thời load tiếp các resource liên quan khác (js, css, image v.v).

Tham khảo việc làm WordPress hấp dẫn cho SV chưa có kinh nghiệm

Túm lại, sơ bộ đã xác định được 3 điểm cần check, giờ tiến hành khoanh vùng và check từng điểm.

1. Check điểm delay 1: Tốc độ kết nối giữa mình và máy chủ

Đơn giản và hiệu quả chính là câu lệnh ping thần thánh:

Okie, ping đẹp, delay chỉ từ 4ms-5ms. Chỗ này ổn, loại ra khỏi danh sách, tập trung vào các khu vực khác.

2. Check điểm delay 2: Web Server

Ngó sơ tình hình trước cái đã, mở Developer Tools của Browser lên (F12), chọn phần Network và truy cập vào website:

Hmm, thời gian xử lý lâu nhất nguyên cây xanh lè đập vào mắt là chỗ xử lý code của trang chủ. Ngó kỹ vào nó chút:

Phần lớn thời gian là chờ byte dữ liệu phản hồi đầu tiên (TTFB): 649ms, thời gian download cục HTML về chỉ tốn 2.07ms. Vậy việc cần làm là triệt tiêu bớt thời gian chờ, giúp nhận được cục HTML sớm hơn. Như theo mô hình đã khoanh vùng và loại trừ đoạn delay số 1, lúc này cần tập trung vào đoạn delay số 2 và số 3. Theo kinh nghiệm thì chắc chắn đoạn cần tối ưu là đoạn 3 (PHP + MySQL) , tuy nhiên cứ phải check từng bước đã, đừng để kinh nghiệm nó lừa mình.

Để kết quả test không bị ảnh hưởng bởi đoạn delay số 1 (network) thì SSH thẳng lên server check. Lúc này cần đo lường xem request đi qua Web Server tốn bao nhiêu thời gian, request xử lý trực tiếp (không đi qua Web Server) thì tốn bao nhiêu:

  • Check request đi qua web server:
  • Tốn hết 0.663s
  • Check request xử lý trực tiếp không thông qua web server
  • Tốn hết 0.602s

Kết luận: đi qua web server cũng không làm tăng độ trễ nhiều, vấn đề chính là ở cục PHP + MySQL thôi, tới đây loại trừ thằng Web server ra được rồi.

3. Check điểm delay 3: PHP + MySQL

Không ngoài dự đoán, 0.602s là thời gian xử lý của code, tính ra là gần bằng thời gian TTFB, giờ strace thử xem những system call nào đang tốn nhiều thời gian nhất:

Hmm, lstat + fstat + stat tính ra chiếm đến hơn 50% thời gian … những system call này liên quan đến việc lấy thông tin file như permission, file size .. cơ mà, sao lại chiếm nhiều thời gian thế nhỉ? Phải chăng là do số lượng file nhiều? Hay do I/O kém?

Để hiểu rõ hơn buộc phải dump kết quả strace chi tiết ra file để nhìn rồi.

Lướt từ trên xuống thì thấy cái theme này nhiều file thiệt, phần lớn các system call trên liên quan đến file trong thư mục theme. Vậy giờ phải làm sao để hạn chế việc check file trên ổ cứng? Muốn bypass ổ cứng thì chỉ có phương án là đưa lên RAM … mà lại là PHP … ờ thì opcache chứ còn gì nữa. Triển luôn thôi

Opcache đã có, giờ test lại xem nào:

Sặc, chuyện gì vậy .. 0.832s ? Tại sao lại tốn nhiều thời gian hơn khi không dùng opcache (chỉ 0.602s)? Opcache cli đã chắc chắn được enable rồi, tại sao lại tốn nhiều thời gian hơn? Nếu chỉ là request đầu tiên (cache miss) thì còn chấp nhận được, tại sao các request tiếp theo lại vẫn chậm như vậy?

Google một hồi thì mới hiểu là ở version PHP < 7.0 thì opcache chỉ có tác dụng khi process đang chạy, do đó khi mình chạy bằng command line, lúc process kết thúc cũng là lúc vùng memory lưu opcache của process đó bị hủy, giữa các lần chạy là các process khác nhau và không liên quan gì nhau nữa. Tuy nhiên, nếu vậy thì check qua web server có thể kết quả sẽ tốt hơn, vì mỗi process httpd sẽ handle được nhiều request trước khi exit.

Có thể chứ, đúng như dự đoán, thời gian xử lý thông qua web server được giảm xuống đáng kể, chỉ còn ~0.270s. So với ban đầu (0.663s) thì cũng nhanh hơn được 3 lần, Opcache cũng được phết. Đây là còn chưa bật query cache hay memcache các kiểu lên nữa, mà để tính sau, site bé tí chưa cần thiết.

Hiện tại đang xài PHP 5.6 (do con server này build lâu rồi), sẵn tiện nghe thiên hạ đồn PHP 7 performance tốt hơn, đo lường thử phát. Hì hục nâng cấp PHP lên version 7, và kết quả:

Ồ, tốt hơn thật anh em ạ, so với case này thì PHP 7 nhanh gấp đôi PHP 5. Chưa kể, PHP 7 giải quyết được vụ dùng opcache ở cli nữa. strace lại phát

Tỉ lệ thời gian dành cho các system call fstat, lstat và stat giảm xuống đáng kể, chỉ còn 21%, tuy nhiên là 21% của total 0.07s (khoảng 0,015s), so với ban đầu là 55% của 0.17s (khoảng 0.093s). Phải vậy chứ!

Tới đây thì cũng tạm hài lòng rồi, mà thôi sẵn làm luôn cho tới, đo lường cho vui. Quất thêm lớp cache nữa để bỏ qua luôn phần xử lý của cục PHP + MySQL. Đập thằng W3total Cache lên

Lúc này check trực tiếp trên chính server, thời gian xử lý chỉ còn 0.019s. Tuy nhiên, w3total cache hoạt động dựa trên .htaccess, nghĩa là nginx vẫn còn bước proxy_pass về httpd. Tối ưu thêm chút, quất luôn nginx cache để bỏ qua đoạn proxy_pass luôn xem sao:

Rút ngắn chút đỉnh, còn 0.013s. Test thử từ browser xem kết quả cuối cùng so với ban đầu thế nào

TTFB lúc này chỉ còn 36.57ms, so với ban đầu 648.92s, đã nhanh hơn 18 lần. 

TopDev via Blog.vietnix

Xem thêm: 

Hướng dẫn xây dựng chức năng thay đổi avatar người dùng WordPress

10 trang web hàng đầu để tìm hiểu WordPress

Hướng Dẫn Xây Dựng Trang Đăng Nhập Trong WordPress

Viễn Trần – Tech Lead tại CYBOZU Việt Nam: “Automation chính là tương lai của Testing”

Viễn Trần - Tech Lead tại CYBOZU Việt Nam

Tốt nghiệp đại học Tự Nhiên về mảng Trí tuệ Nhân tạo, kinh qua nhiều vai trò khác nhau và phụ trách Automation tech lead dự án Garoon Automation, anh Viễn Trần hiện đang là full-stack developer tại Cybozu Việt Nam, đồng thời cũng đảm nhận vị trí Tech Lead dự án BICO Pass trên nền tảng .Net Core và Angular. Trong mục “Chuyên gia nói” tuần này, hãy cùng TopDev tìm hiểu để đạt được thành tựu như ngày hôm nay, hành trình học hỏi và cơ duyên giữa anh Viễn và ngành Automation Test nhé.

Full-Stack Developer – Trưởng thành từ chính gian nan 

Anh hãy chia sẻ một chút về bản thân và con đường sự nghiệp của mình?

Tôi tốt nghiệp đại học Khoa học Tự nhiên, ở phòng thí nghiệm nhân tạo và có 1 dự án về thương mại điện tử, rất tiếc dự án này chưa thành công. Nên tôi quyết định đầu quân cho Cybozu Việt Nam, đến nay đã được hơn 8 năm rồi. Trong suốt khoảng thời gian này, tôi đã trải qua nhiều vị trí và vai trò khác nhau và học hỏi rất nhiều từ đồng nghiệp của mình. Gần đây tôi cũng tích cực tham gia và chia sẻ một số kiến thức và hiểu biết với cộng đồng thông qua cách tech events, nếu các bạn có tham dự các sự kiện do TopDev tổ chức thì có thể sẽ biết đến tôi.

Lựa chọn trở thành Full-stack developer có ảnh hưởng tích cực như thế nào đến sự nghiệp của anh? 

Tôi có một ví dụ khá đời thường để mình có thể hiểu rõ hơn. Ví dụ như bạn đi từ nhà đến công ty bạn chỉ biết một con đường, dù trời nắng trời mưa hay kẹt xe, hoặc ở trạng thái cảm xúc như thế nào cũng chỉ đi con đường đó. Ví dụ này giúp ta liên tưởng đến Full-stack developer. Full-stack developer là cơ hội giúp cho mọi người có những hiểu biết khác nhau ở những vai trò khác nhau, từ đó các bạn có thể đưa ra những giải pháp, những công nghệ phù hợp với một bài toán cụ thể mà công ty hay project của mình đang gặp phải. Tôi nghĩ rằng Full-stack developer là cơ hội giúp cho developer hoàn thiện bản thân mình hơn. 

Về cơ bản, Full-stack developer khá là rộng nên về khó khăn nói chung thì mình có thể nói như sau: Khó khăn thứ nhất là mình phải làm việc với những task khác nhau như server-side, client-side và những task liên quan đến network, do đó chúng ta phải research và nâng cấp bản thân mình liên tục. Thứ hai, chúng ta phải làm việc ở nhiều bộ phận khác nhau. Chúng ta phải hiểu được văn hóa và phong cách của từng bộ phận để làm việc hiệu quả. Đó là 2 khó khăn lớn nhất đối với Full stack developer.

Chia sẻ kỹ hơn về khó khăn thứ hai, đó là kỹ năng cần có của một Full stack developer. Kỹ năng đầu tiên là research một cách độc lập, cái thứ hai là chịu được áp lực, và thứ 3 là kỹ năng giải quyết vấn đề và cuối cùng là kỹ năng trình bày. Full stack developer giúp cho họ làm việc với nhiều vai trò, nhiều bộ phận khác nhau để từ đó họ có thể học hỏi và trưởng thành hơn từ các đàn anh đi trước. Giúp họ nâng cao kỹ năng trình bày và thuyết phục, kỹ năng trao đổi và kỹ năng làm việc cộng tác. Khi đi theo hướng full-stack cũng giúp họ tìm ra lĩnh vực để nghiên cứu chuyên sâu về nó từ đó họ trở nên chuyên gia hay PM hay giám đốc kỹ thuật có tâm và có tầm hơn.

Viễn Trần - Tech Lead tại CYBOZU Việt Nam

Khi đã trở thành Full-stack developer thì mình sẽ có những thuận lợi nào so với các vị trí khác không anh?

Theo quan điểm của tôi, Full-stack có con đường tìm việc rất rộng mở, vì họ có thể làm nhiều vai trò khác nhau trong một team. Thời gian gần đây lập trình có sự thay đổi theo dạng DevOps, hay là mô hình Scrum. Từ đó vai trò của một người trong team không chỉ là viết code, cũng không chỉ là người đi giải quyết vấn đề cũng không chỉ là người đi fix bug. Cho nên anh nghĩ con đường sự nghiệp của Full stack developer rất rộng mở.

Với vai trò là người đi trước, anh có lời khuyên nào dành cho các bạn hiện tại đang là Junior Developer

Tôi sẽ chia sẻ một ý thế này, khi bác sĩ chữa trị cho bệnh nhân, một phác đồ chỉ điều trị cho một người hay một vài người nào đó thôi. Nhưng nếu cố gắng để nêu quan điểm cá nhân thì theo tôi các bạn nên 

  • Học một ngôn ngữ lập trình thật chuyên sâu để nắm nguyên lý căn cơ trước khi bay cao, sang frameworks hoặc một tech nào khác.
  • Sử dụng và khai thác một công nghệ, framework tối đa để học hỏi và trải nghiệm cách giải quyết vấn đề từ framwork mạng lại, theo tôi các frameworks tổ chức nghiệp vụ và source-code rất tốt, do đó chúng ta sẽ học được thật nhiều.
  • Chủ động không ngại khó làm các công việc trong team đôi khi nó vượt tầm của mình một tí (nhớ là nhờ các anh hỗ trợ thêm). Có trách nhiệm đặt câu hỏi trong đội nhóm hoặc cho mọi người (đôi khi thật khó ở một vài môi trường kém thân thiện). Khi gia nhập team họ phải chủ động để nhận task hay trao đổi để học hỏi kỹ năng từ nhiều người, từ đó rút ngắn con đường để trở thành một Full stack thực chất.

Như anh chia sẻ mình cần chọn một ngôn ngữ để phát triển lên, anh có thể chia sẻ nguồn tài liệu mà anh thấy bổ ích?

Trong công ty tôi cũng có một số bạn hay hỏi, Em nên học ngôn ngữ gì hay Nguồn tài liệu ở đâu? Quan điểm của tôi thì để học một server-side language (ví dụ như PHP), thì mình nên lên chính trang gốc của nó, hay mình học selenium, về automation testing thì mình cũng follow chính trang gốc, documentation từ chính những trang này, chọn ra những best practice thì sẽ hay hơn đọc những trang trung gian. Sau đó mình có thể chọn những trang khác như W3School, … 

Tôi thường xem tạp chí online để cập nhật công nghệ và xu thế chuyên ngành công nghệ thông tin.

Còn hai cuốn sách liên quan tới chuyên ngành mà tôi thường hay sử dụng hoặc đọc lại mỗi trước khi tôi bắt đầu thiết kế project mới hay là module quan trọng hay là tái cấu trúc một dự án cũ là: The Art of Readable CodeCode Complete của McConnell

Viễn Trần - Tech Lead tại CYBOZU Việt Nam

End-to-end testing system và những kinh nghiệm xương máu

Được biết hiện nay anh đang triển khai hệ thống E2E system. Anh có thể chia sẻ thêm về hệ thống này không?

Tôi cũng đã từng chia sẻ về hệ thống End-to-End đang được triển khai tại Cybozu Việt nam tại một số sự kiện kỹ thuật. Trước đây nó được xây dựng trên nền tảng PHP Selenium, điều này dẫn đến khá nhiều khó khăn đối với những bạn là QA hay những bạn vừa gia nhập Automation Testing. Tôi nghĩ hệ thống hiện tại được triển khai giải quyết được 2 vấn đề chính mà người build hệ thống tự động hóa cần lưu tâm: build hệ thống tự động hóa cho tất cả mọi người có thể sử dụng được (không phân biệt QA hay developer), thứ 2 là có thể mở rộng liên tục được, mình có thể phát triển thêm loại test. Anh nghĩ hiện tại hệ thống hiện tại giải quyết rất tốt điều này.

Còn về cơ duyên đưa tôi đến với Testing, thực ra anh từng làm việc ở nhiều nơi khác nhau và nhiều vai trò khác nhau tôi nhận thấy developer full stack cần có kỹ năng test tự động hóa và hơn nữa phải build một hệ thống tự động. Từ đó tôi bắt tay vào hoàn thiện kỹ năng này cũng có thể coi là cơ duyên đi nhỉ.

  • Một sản phẩm thành công, lớn thường được xây dựng qua nhiều năm, nhiều tính năng, và nhiều thành viên vào nhóm và ra đi. Do đó cần phải test và phải đảm bảo luôn đúng ở mọi thời điểm. Và đặt biệt là sản phẩm đó không được suy yếu trong quá trình maintain và thêm mới tính năng. Do đó lập trình viên dù giỏi cỡ nào cũng dễ mắc phải do đó anh muốn mình làm chủ về công nghệ testing để mình có thể yên tâm vươn cao bay xa và tha hồ thử nghiệm công nghệ mới trên project hiện tại.
  • Việc build hệ thống tự động hóa rất đơn giản nhưng để trở nên scable và bất kỳ ai cũng có thể làm việc, thì không hề đơn giản do đó tôi thấy hơn ai hết fullstack developer nên build hệ thống trước rồi QA, và developer cùng mở rộng bộ test-case tự động dễ dàng nhất.
  • Việc build hệ thống tự động hóa cũng đòi hỏi rất nhiều kỹ năng bao gồm: tầng hệ thống, giao thức mạng và kiến trúc phần mềm, CI/CD…

Nên tôi thường hay có một câu nói với các bạn khi đề cập tới testing mindset là: Testing code is real code.

Anh có thể chia sẻ thêm khi nào mình nên dùng End-to-End testing và khi nào cần dùng Functional testing? Đặc điểm và ưu nhược điểm của từng loại.

Tôi sẽ trả lời qua ba ý như sau

  • Manual Test thì dùng sức người. Automation testing dùng máy tính: đây là sự khác nhau rất cơ bản. Do đó hệ thống test tự động sẽ không bị chi phối bởi cảm tính con người. Tôi mệt, thì tôi có thể test sai nhưng automation test không bị chi phối bởi yếu tố này, về cơ bản tự động hóa dùng sức máy và tránh lỗi của con người.
  • Tự động hóa có thể chạy song song. Tại một thời điểm có thể chạy nhiều test case để giảm thời gian test xuống. Còn nếu về manual thì hầu như không làm được việc này. Tôi lấy ví dụ, giả sử chúng ta có 1000 test case thuộc bộ regression test, nếu dùng manual thì chúng ta tốn 10 ngày, nếu dùng tự động chạy dưới chế độ song song thì chúng ta mất 1 giờ. Ví dụ này cũng đưa ra sự khác nhau khá rõ ràng.
  • Đây là điều thú vị mà QA làm manual rất muốn biết: tự động hóa sẽ không phát hiện được bug mà manual phát hiện được. Ví dụ như một ngày đẹp trời trang login có UI bị vỡ, nhưng vẫn còn username và password để nhập. Về cơ bản manual sẽ phát hiện UI đang bị lỗi nhưng tự động hóa thì không. Đây là yếu tố khá là quan trọng mà người làm manual tự tin mình vẫn còn giá trị trong công việc. 

Tự động hóa cho dù đạt được mức độ nào thì vẫn không thể thay thế được manual test.

Trong quá trình làm việc ở mảng System Testing thì anh có kỷ niệm vui nào có thể chia sẻ?

Gần đây nhất thì tôi có giải quyết được 1 cái bug làm cho chương trình test không ổn định. Bên Nhật có nhờ tìm hiểu nguyên nhân và tôi đã giải quyết được cái bug này trong vòng 1 buổi sáng, mà anh dự định trong 3 ngày. Tôi thấy khá vui khi giải quyết được bug từ hệ thống được xây dựng bên Tokyo. Đó là kỷ niệm vui của tôi trong tự động hóa.

Còn kỷ niệm buồn mà tôi nhớ nhất là, sau khi đội tự động hóa ở Việt Nam đã chọn kỹ thuật PHP Selenium, Facebook Webdriver để build hệ thống tự động, Mọi người rất vất vả, sau một năm thì build hệ thống thành công, Nhưng vào một ngày không xa sau đó, đội automation testing bên Nhật đã họp bàn và chốt lại là kỹ thuật đó pending và không làm nữa. Từ đó chuyển toàn bộ sang kỹ thuật mới, kỹ thuật mới này được ứng dụng tới bây giờ. Như hồi nãy tôi chia sẻ, mình phải giải quyết được 2 yêu cầu: một là mọi người đều viết được và maintain được hệ thống đó, thứ 2 nữa là có thể mở rộng liên tục. Ngoài ra, một lý do cho câu chuyện chuyển sang kỹ thuật mới đó là: mình phải cập nhật xu hướng công nghệ, công nghệ PHP Selenium đã ra đời cách đây hơn chục năm, còn Web Driver Node thì nó khoảng 4 năm, và anh nghĩ nó sẽ bùng nổ trong những năm tiếp theo, đặc biệt ở thị trường automation testing thì dù muốn hay không, đã là xu thế thì mình phải cập nhật thôi.

Quan điểm của anh về xu hướng phát triển của ngành Testing trong hiện tại và tương lai

Thực ra để có câu trả lời chính xác thì chúng ta nên dựa trên số liệu hay những công bố, tuy nhiên để trả lời câu hỏi này thì tôi nghĩ là đối với tương lai, một bạn làm Tester hay lĩnh vực QA dù muốn hay không, cũng nên trang bị những kỹ năng automation testing. Dù không đam mê đến mức build một hệ thống automation testing hoàn chỉnh, bạn cũng có thể maintain mở rộng hệ thống của công ty, tổ chức mà bạn đang hoặc sẽ tham gia trong tương lai. Thứ 2, đối với người tuyển dụng, yêu cầu về automation testing bây giờ là optional, nhưng tương lai nó sẽ là một requirement (bắt buộc). Từ 2 nhận định này, trong tương lai thị trường về automation testing hầu như chiếm xu thế, và những doanh nghiệp đang có sản phẩm CNTT là chủ lực, họ sẽ chuyển dần từ manual sang automation.

Viễn Trần - Tech Lead tại CYBOZU Việt Nam

Anh hãy chia sẻ về công việc hàng ngày của anh cũng như team tại Cybozu nhé

Công ty tôi, đặc biệt là team tôi thì chủ động phần lớn các công việc, từ lý tưởng chung của công ty đến lý tưởng của team thì mọi người tự động nhận task team đã tạo ra trước đó. Công việc của tôi thường ngày xoay quanh những task như: training bạn mới đội Cybozu dev ở VN, và team Nhật nếu có yêu cầu, thứ hai là support team khác viết những tự động hóa, thứ 3 là cùng mọi người trong team viết những test case mà trước đây dùng manual, điều quan trọng nhất đối với tôi là improve hệ thống hiện tại.

Nếu bạn nào làm ở lĩnh vực CNTT thì cũng biết là, khi xây dựng hệ thống, sẽ không có gì đúng mãi mãi, nên mình sẽ improve dần dần, bằng cách viết test case mới cho nó phù hợp hơn. Điều cuối cùng, cũng như điều tôi luôn làm hàng ngày, đó là research công nghệ mới. Nếu các bạn có quan điểm tự động hóa là lĩnh vực khá nhỏ, nhưng khi bước chân vào tự động hóa được 4 năm, anh lại thấy rất rộng, và nhiều thứ để học hỏi.

Về công việc của tôi thì có thể kể đến như sau:

  1. Training automation testing cho đội Dev (hiện tại cũng đã vừa xong)
  2. Build Automation system cùng với đội Automation
  3. Viết tự động hóa cho những test-case đang test bằng manual, viết lại tự động hóa cho những test code đã viết bằng công nghệ cũ là Java sang nền tảng mới.
  4. Viết tài liệu của hệ thống tự động
  5. Và tech solutions cho các bạn trong team(hoặc ai đó cần).
  6. Research những kỹ thuật công nghệ để có thể nâng cao hiệu suất của tự động hóa(docker, circleci, scable, headless, isolation design pattern,…)
  7. Và công việc không kém phần quan trọng là review source-code của các bạn develop viết để đảm bảo source code có tính đồng nhất và chặt chẽ nhất.
  8. Speaker về tự động hóa cho những nơi cần nó.

Bài toán cung – cầu nhân lực IT và lời khuyên từ chính người trong nghề 

Hiện tại có vài doanh nghiệp gặp khó khăn trong việc tuyển dụng. Team của anh có gặp khó khăn trong vấn đề này không?

Tôi cũng thuộc bộ phận tuyển dụng 5 năm, nên có thể chia sẻ trải nghiệm của mình như sau. Về cơ bản tôi thấy công ty anh cũng gặp khó khăn trong tuyển dụng, và có thể nêu 3 góc độ nhìn. Góc độ đầu tiên, người tuyển dụng, tôi thấy một số bạn thường trang bị những kỹ năng khá khô cứng. Họ chỉ biết những gì họ làm, chứ không research thêm tổng thể để biết được những thứ cốt lõi, khi đưa ra tình huống và bài toán khác, thì họ không trả lời hay giải quyết được. Tôi cũng có lời khuyên với các bạn, trước khi làm được ứng dụng hoàn hảo thì mình hãy trang bị cho mình những kiến thức mang tính chất buộc bạn phải biết khi bước chân vào ngành lập trình.

Khó khăn thứ 2 đứng dưới góc độ một doanh nghiệp, tôi thấy hiện giờ ở Việt Nam có rất nhiều doanh nghiệp ngoại đổ bộ vào nên việc cạnh tranh về nguồn nhân lực chất lượng cao là rất khó. Điều cuối cùng, các doanh nghiệp nên có chính sách hay văn hóa phù hợp hơn với xu thế của các bạn trẻ. Các bạn trẻ cần sự năng động, chính sách nên phù hợp để làm sao các bạn thấy thoải mái để làm việc và giới thiệu cho bạn bè khác, cũng như có chính sách lương và benefits sao cho phù hợp. Thì điều này cũng phần nào cải thiện tình hình tuyển dụng của doanh nghiệp.

CYBOZU VIETNAM Tuyển dụng AUTOMATION TEST ENGINEER

CYBOZU VIETNAM Tuyển dụng Software QA Engineer

Anh hãy chia sẻ nhiều hơn về văn hóa của công ty cũng như ở team anh

Viễn Trần - Tech Lead tại CYBOZU Việt Nam

Về benefit của công ty thì tôi sẽ để bộ phận HR trả lời sẽ phù hợp hơn. Về văn hóa thì tôi thấy có sự thú vị với tiêu chí đầu tiên là Tôn trọng sự khác biệt. Khi bước chân vào Cybozu thì các bạn sẽ không có cảm giác là, sao tôi khác biệt với mọi người quá, từ đó trở nên ngại và tách biệt với tập thể. Ở đây, bạn có quyền nói lên tiếng nói của bạn, bạn có quyền mặc bộ đồ khác với xu hướng mọi người. Thứ 2, mọi người đều có cơ hội như nhau, ở đây mọi người có quyền tranh luận, có quyền đưa ra ý kiến cá nhân và đề xuất những giải pháp mà giải pháp đó không chịu sự chi phối của tuổi tác. Ở đây không có sự cả nể, không phải như “Ở đây tôi lớn tuổi là tôi nói các bạn phải nghe”. Mọi thứ được quyết định dựa trên các lập luận thuyết phục. Theo tôi đây là 2 yếu tố rất quan trọng đối với các doanh nghiệp.

Anh có thể chia sẻ về công cụ mà team anh dùng để quản lý công việc và có thể đo lường hiệu suất của mọi người trong team không?

Với câu hỏi này thì tôi xin phép không trả lời vì đặc tính công việc. Trong team tôi cũng như trong công ty, thì trên tinh thần của sự khích lệ. Tức là nếu bạn không nhận task thì bạn sẽ không có cơ hội để làm việc, nếu không chủ động thì bạn sẽ khó trưởng thành và phát triển tốt hơn.

Viễn Trần - Tech Lead tại CYBOZU Việt Nam

Quá trình tuyển dụng của Cybozu hiện nay như thế nào?

Tôi xin chia sẻ về quan điểm của mình, cơ bản thì là lọc CV, không phải để tìm ra người giỏi nhất, mà để tìm ra người phù hợp, những người mà có thể đáp ứng sự kỳ vọng của các bạn. Tiếp đến sẽ có những buổi nói chuyện face-to-face về chuyên môn, không hẳn là phỏng vấn mà chỉ để xem 2 bên có cùng lý tưởng hay không, nếu các bạn phù hợp và có kỹ năng thì các bạn sẽ được công ty mời vào vòng nói chuyện với manager. Còn nếu chưa phù hợp thì có thể hẹn bạn vào thời điểm khác. Đó là quy trình chung ở Cybozu.

Ứng tuyển các vị trí việc làm hấp dẫn tại CYBOZU

Hiện nay có một số doanh nghiệp thường đặt nặng phần thuật toán. Suy nghĩ của anh như thế nào?

Về cơ bản mỗi công ty có một cách và chiến lược khác nhau, để chọn ra ứng viên cho phù hợp. Cá nhân tôi khi phỏng vấn, tôi cũng không quá đặt nặng về thuật toán mà thường hay tìm những ứng viên có thể cộng tác với nhau, có tinh thần trách nhiệm, có khả năng research, làm việc độc lập hay chịu được áp lực. Tôi nghĩ đây là những ứng viên anh hoặc công ty mong đợi chứ không phải ứng viên số 1 hay xuất chúng.

Xin phép được cám ơn anh đã đến với buổi phỏng vấn của TopDev, và nếu các bạn cảm thấy câu trả lời của anh Việt, những kiến thức mà anh mang lại trong buổi phỏng vấn bổ ích với các bạn thì các bạn đừng quên nhấn ‘Like & Subscribe’ ở Channel nhé, hẹn gặp lại các bạn ở các series tiếp theo.

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

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

Ứng dụng JSON.parse để cải thiện tốc độ?

Với các ứng dụng web ngày nay, không khó bắt gặp việc sử dụng object như một nơi lưu trữ state và các dạng dữ liệu cần thiết khác cho ứng dụng. Cụ thể nhất chúng ta thường thấy trong store của Redux.

const data = { foo: 42, bar: 1337 }; // 🐌

Trong thực tế nó sẽ không đơn giản như ví dụ ở trên, mà cấu trúc nếu không chồng chéo thì cũng rất lớn vì phải gánh vác toàn bộ state của ứng dụng. Nếu nó lại dữ liệu cần thiết trong lúc khởi tạo ứng dụng lần đầu tiên. Việc này sẽ nằm trong render critical path của trình duyệt, đồng nghĩa với việc user không thấy gì hết cho đến khi dữ liệu này được load, parse, compile, execute bởi Javascript engine bên dưới.

Để khắc phục việc này một trong những cách làm là dùng server side rendering, chúng ta chỉ quăng cái HTML đã chứa toàn bộ kết quả của quá trình xuống user. Trình duyệt user không cần đảm đương công việc đó nữa.

Nhưng nếu chúng ta không thể dùng server side rendering thì sao?

Nếu object chúng ta cần không chứa những gì mà JSON không hỗ trợ, như BigInt, Maps, Sets,… Chúng ta có thể sử dụng JSON.parse

Vì cú pháp của JSON đơn giản hơn nhiều so với Javascript, nên quá trình parse sẽ ít tốn kém hơn so với Javascript rất nhiều.

Nội dung bên trong JSON đối với các engine rất dễ đoán, và ngược lại với các object. Thí dụ nếu bạn là cái engine rồi nhìn vào đây

JSON.parse('{

Khi bạn thấy dấu {, bạn sẽ biết được chỉ có 2 khả năng có thể xảy ra: một là bắt đầu một object, hai là một JSON không hợp lệ.

Trong khi đó sau dấu { của object, có rất nhiều khả năng xảy ra

const x = 42;
const y = ({x}

Đây có phải là object không? Giá trị x đang trỏ tới đâu? Không thể nào có đáp án nếu không xem hết toàn bộ code

// khởi tạo object, x trở tới thằng khai báo trước đó
const y = ({x})
// object destructuring, x không trỏ tới thằng đầu
const y = ({x} = { x: 21});
// một arrow function
const y = ({x}) => x;

Như vậy, nếu gặp dấu {, các engine phải vô cùng thận trọng vì phải biết ngữ cảnh hiện tại mới biết nó là gì.

Lợi dụng đặc tính này chúng ta có thể cải thiện tốc độ của các ứng dụng web có sử dụng một object có cấu trúc tương tự như JSON (ví dụ như Redux Store). Thay vì sử dụng một khai báo object như thế này

const data = { foo: 42, bar: 1337 }; // 🐌

Có thể tăng tốc bằng cách viết

const data = JSON.parse('{"foo":42,"bar":1337}'); // 🚀

Miễn là việc tính toán này chỉ cần parse một lần bằng JSON.parse, nó sẽ nhanh hơn nhiều so với cách khai bao object bình thường, và chỉ nên suy nghĩ áp dụng khi object đã vượt quá kích thước 10kB.

Thực hiện kiểm tra tốc độ của một object khoảng 8Mb dung lượng trên các engine khác nhau. Kết quả nhận được ít nhất cũng tăng tốc gấp 1.5 lần trên các phiên bản khác nhau của V8

TopDev via Vuilaptrinh

Tôi đã viết API lấy thời khóa biểu trường như thế nào?

Helu các bạn! Mình học theo tín chỉ cho nên lịch học không phải tuần nào cùng giống nhau nên hôm nào cũng phải vào trang trường xem lịch (hơi mất chút thời gian và giao diện không trực quan). Một cách nữa là viết lại thời khóa biểu (Mất nhiều thời gian mà có thể sai sót). Các cách này không phù hợp với một thằng lười như mình và mình muốn dùng nhiều lần… Dựng API lấy thời khóa biểu sau đó có thể viết chatbot nhắc lịch hay web/mobile xem lịch tùy ý nữa… Hừm .. Bắt đầu thôi.

Bước 0: Ý tưởng thực hiện

  • Input: tài khoản, mật khẩu sử dụng trên trang trường.
  • Lấy file thời khóa biểu.
  • Phân tích file thời khóa biểu => dữ liệu (JSON).

Bước 1: Phân tích request

Đăng nhập vào trang trường

Password encrypted trước khi gửi lên!!

Bắt request login

Password truyền lên đã mã hóa md5, có một số data khác

Chọn request login > Copy as cURL (bash). Mở Postman Chọn Import > Paste raw text > Ấn nút import Ta được:

Gửi POST thử xem sao !

Vậy là lấy được cookie rồi ^^ Đến với phần lấy file, mình sẽ phân tích request lấy file như request trên và đây là kết quả:

Headers

Phần Headers

Response

Phần response – file excel là đây chứ đâu ^^

Bước 2: Tiến hành code

Mình sử dụng express của NodeJS

Cấu trúc project

File login

Nhiệm vụ là lấy cookie

let optionsGetCookies = (username,password,form) =>{
    form['__EVENTTARGET'] = '';
    form['txtUserName'] = username;
    form['txtPassword'] = password;
    form['btnSubmit'] = 'Đăng nhập';
    return {
        method : 'POST',
        uri : urlLogin,
        simple: false,
        timeout: 20000,
        followRedirect: true,
        resolveWithFullResponse: true,
        form:form,
        headers: {
            'Connection' : 'keep-alive',
            'Cache-Control' : 'max-age=0',
            'Origin' : urlOrigin,
            'Upgrade-Insecure-Requests':'1',
            'Content-Type' : 'application/x-www-form-urlencoded',
            'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.18 Safari/537.36 Edg/75.0.139.4',
            'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
            'Referer' : urlLogin,
            'Accept-Encoding' : 'gzip, deflate',
            'Accept-Language' : 'en-US,en;q=0.9',
        }
    }    
}
module.exports = (username,password) =>{
    return new Promise((resolve,reject)=>{
        getElements(urlLogin,null).then((result)=>{
            let elements = result.elements;
            requestPromise(optionsGetCookies(username,password,elements)).then((_result)=>{
               return resolve({task:'getCookies',success:true, cookies:_result.headers['set-cookie']});
            }).catch((_error)=>{
                return reject({task:'getCookies',success: false, error:_error});
            })
        }).catch((error)=>{
            return reject({task:'getElements',success: false, error:error});
        })
    });
}

File get elements

Mục đích là lấy các element trong body. Sử dụng cheerio

let optionsGetElement = (uri,cookies) =>{
    return {
        method : 'GET',
        uri : uri,
        followRedirect: true,
        headers: {
            'Connection' : 'keep-alive',
            'Cache-Control' : 'max-age=0',
            'Origin' : urlOrigin,
            'Upgrade-Insecure-Requests':'1',
            'Content-Type' : 'application/x-www-form-urlencoded',
            'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.18 Safari/537.36 Edg/75.0.139.4',
            'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
            'Referer' : urlLogin,
            'Accept-Encoding' : 'gzip, deflate',
            'Accept-Language' : 'en-US,en;q=0.9',
            'cookie' : formatCookieListToString(cookies),
        }
    }    
}
module.exports = (uri,cookies) =>{
    return new Promise((resolve,reject)=>{
        requestPromise(optionsGetElement(uri,cookies)).then((response)=>{
            let $ = cheerio.load(response);
            let hiddenInputList = $('input[type="hidden"]');

            
            let elements = {};
            if (cookies != null){
                let options = $('option[selected="selected"]');
                elements['PageHeader1$drpNgonNgu'] = options[0].attribs.value;
                elements['drpSemester'] = options[1].attribs.value;
                elements['drpTerm'] = options[2].attribs.value;
                elements['drpType'] = options[3].attribs.value;
                elements['btnView'] = 'Xuất file Excel';
            }
            
            for (let i=0;i<hiddenInputList.length;i++){
                if (hiddenInputList[i].attribs.value == undefined){
                    elements[hiddenInputList[i].attribs.name] = '';
                }else{
                    elements[hiddenInputList[i].attribs.name] = hiddenInputList[i].attribs.value;
                }
            }
            return resolve({task:'getElements',success:true,elements:elements})
        }).catch((error)=>{
            return reject({task:'getElements',success:false,error:error})
        })
    }); 
}

Lấy file excel thời khóa biểu

let optionsGetFile = (cookies,form) =>{
    return {
        resolveWithFullResponse: true,
        method : 'POST',
        encoding: null,
        uri : urlStudentTimeTable,
        headers: {
            'Connection' : 'keep-alive',
            'Cache-Control' : 'max-age=0',
            'Origin' : urlOrigin,
            'Upgrade-Insecure-Requests' : '1',
            'Content-Type' : 'application/x-www-form-urlencoded',
            'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
            'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
            'Referer' : urlStudentTimeTable,
            'Accept-Encoding' : 'gzip, deflate',
            'Accept-Language' : 'en-US,en;q=0.9,vi;q=0.8',
            'cookie' : formatCookieListToString(cookies),
        },
        form:form
    }
}

module.exports = async(username,password) =>{
    return new Promise(async(resolve,reject)=>{
        await getCookies(username,password).then(async(result)=>{
            let cookies = result.cookies;
            await getElements(urlStudentTimeTable,cookies).then(async(_result)=>{
                await requestPromise(optionsGetFile(cookies,_result.elements)).then(async(__result)=>{
                    let file =  await fs.createWriteStream(path.resolve(`${__dirname}/files`,`${username}.xls`));
                    await file.write(__result.body);
                    await file.end();
                    return resolve({task:'getFile',success:true,filename:`${username}.xls`});
                }).catch((__error)=>{
                    console.log(__error);
                })
            }).catch((_error)=>{
                console.log(_error);
            })
        }).catch((error)=>{
            console.log(error);
        })
        return reject('error');
    });   
}

Trích xuất dữ liệu từ file

Phân tích file và tiến hành code.

Sử dụng xlsx.

module.exports = (file) =>{
    return new Promise((resolve,reject)=>{
        try{
            let schedule = [];
            let getAddressCell = (str) =>{
                let r = /\d+/;
                let row = parseInt(str.match(r)[0]);
                let col = str.replace(row,"");
                return {row:row,col:col}
            }
            let formatDateYMD= (str)=>{
                str = str.split("/");
                let day = str[0];
                let month = str[1];
                let year = str[2];
                return new Date(`${year}/${month}/${day}`)
            }
            let formatDay = (day) =>{
                return day -1;
            }
            let findDatewithDay = (start_date,end_date,day) =>{
                let dateList =[];
                for (let i = start_date; i < end_date; i+=60*60*24*1000){
                    let date_check = new Date(i);
                    if(formatDay(day) == date_check.getDay()){
                        dateList.push(date_check.getTime()); 
                    }
                }
                return dateList;
            }
            let findIndexDate = (date) =>{
                let index = -1;
                for (let i = 0; i < schedule.length;i++){
                    if (schedule[i].date==date){
                        return i;
                    }
                }
                return index;
            }
            let addToSchedule = (start_date,end_date,day,lesson,subject_name,address) =>{
                let dateList = [];
                // console.log(start_date,end_date,day,lesson,subject_name,address);
                dateList = findDatewithDay(start_date,end_date,day);
                for (let i = 0; i < dateList.length; i++){
                    var found = findIndexDate(dateList[i]);
                    if (found == -1 ){
                        let lessons = new Array();
                        lessons.push({lesson:lesson,subject_name:subject_name,address:address});
                        schedule.push({date:dateList[i],lessons:lessons});
                    }else{
                        schedule[found].lessons.push({lesson:lesson,subject_name:subject_name,address:address});
                    }
                }
            }
            let lessonArray = (lesson) =>{
                let lesson_array_new = [];
                let lesson_array = ['1,2,3','4,5,6','7,8,9','10,11,12','13,14,15,16'];
                for (let i = 0 ; i < lesson_array.length ; i++){
                    if (lesson.indexOf(lesson_array[i]) != -1){
                        lesson_array_new.push(lesson_array[i]);
                    }
                }
                return lesson_array_new;
            }
            let getInfoDetail = (start_date,end_date,subject_name,str) =>{
                str = str.split("\n");
                for (let i=0;i < str.length; i++){
                    if (str[i] != '' && str[i] != null && str[i] != undefined){
                        //console.log("===>",str_start_date,str_end_date,str[i]);
                        let str_info = str[i].split("tại");
                        let address = str_info[1];
                        let day_and_lesson = str_info[0].split("tiết");
                        let lesson = day_and_lesson[1].replace(" ","").replace(" ","");
                        let day = day_and_lesson[0].replace(" ","").replace("Thứ","");
                        let lesson_array = lessonArray(lesson);
                        for (let i = 0; i < lesson_array.length;i++){
                            // console.log(start_date,end_date,day,lesson_array[i],subject_name,address);
                            addToSchedule(start_date,end_date,day,lesson_array[i],subject_name,address);
                        }
                    } 
                }
            }     
            var workbook = XLSX.readFile(path.resolve(`${__dirname}/files`,file));
            var worksheet = workbook.Sheets[workbook.SheetNames[0]];

            let  name = worksheet["C6"]["v"];
            let  student_id = worksheet["F6"]["v"];
            let  class_name = worksheet["C7"]["v"];
            let  major = worksheet["F8"]["v"];
            for (z in worksheet) {
                if(z[0] === '!') continue;
                    //console.log(getAddressCell(z),z,worksheet[z]["v"]);
                if (worksheet[z]["v"].toString().indexOf("Từ")>=0 && worksheet[z]["v"].toString().indexOf("đến")>=0){
                    let address_cell = getAddressCell(z);
                    let subject_name = worksheet[`F${address_cell.row}`]["v"].split("-")[0];
                    let sessionList = worksheet[z]["v"].toString().split("Từ");
                    for (let i =0;i<sessionList.length;i++){
                        let str = sessionList[i].replace("\n","").split(":");
                        let str_datetime = str[0].replace(" ","").split("đến");
                        let str_start_date = str_datetime[0];
                        let str_end_date = str_datetime[1];
                        if (str_start_date != undefined && str_end_date != undefined){
                            getInfoDetail(formatDateYMD(str_start_date).getTime(),formatDateYMD(str_end_date).getTime(),subject_name,str[1]);
                        }
                    }          
                } 
            }
            fs.unlink(path.resolve(`${__dirname}/files`,file), function (err) {
                if (err) throw err;
                // if no error, file has been deleted successfully
                console.log({task:'readFile',success:true,message:`File ${file} deleted!`});
            }); 
            return resolve({task:'readFile',success:true,student_id :student_id,name:name,class_name:class_name,major:major,schedule:schedule});
        }catch(e){
            fs.unlink(path.resolve(`${__dirname}/files`,file), function (err) {
                if (err) throw err;
                // if no error, file has been deleted successfully
                console.log({task:'readFile',success:true,message:`File ${file} deleted!`});
            }); 
            return reject({task:'readFile',success:false,error:e});
        }
    });
}

Kết quả

{"task":"readFile","success":true,"student_id":"xxxxx","name":"xxxxx","class_name":"xxx","major":"Công nghệ thông tin","schedule":[{"date":1578355200000,"lessons":[{"lesson":"10,11,12","subject_name":"Hệ thống thông tin di động","address":" 301_TA2 TA2"}]},{"date":1578960000000,"lessons":[{"lesson":"10,11,12","subject_name":"Hệ thống thông tin di động","address":" 301_TA2 TA2"},{"lesson":"7,8,9","subject_name":"Kiến trúc máy tính","address":" 301_TA2 TA2"}]},{"date":1580688000000,"lessons":[{"lesson":"10,11,12","subject_name":"Hệ thống thông tin di động","address":" 301_TA2 TA2"},{"lesson":"7,8,9","subject_name":"Nguyên lý hệ điều hành","address":" 301_TA2 TA2"}]},{"date":1581292800000,"lessons":[{"lesson":"10,11,12","subject_name":"Hệ thống thông tin di động","address":" 301_TA2 TA2"},{"lesson":"7,8,9","subject_name":"Nguyên lý hệ điều hành","address":" 301_TA2 TA2"}]},{"date":1581897600000,"lessons":[{"lesson":"10,11,12","subject_name":"Hệ thống thông tin di động","address":" 301_TA2 TA2"},{"lesson":"7,8,9","subject_name":"Nguyên lý hệ điều hành","address":" 301_TA2 TA2"}]},{"date":1582502400000,"lessons":[{"lesson":"10,11,12","subject_name":"Hệ thống thông tin di động","address":" 301_TA2 TA2"},{"lesson":"7,8,9","subject_name":"Nguyên lý hệ điều hành","address":" 301_TA2 TA2"}]},{"date":1583107200000,"lessons":[{"lesson":"10,11,12","subject_name":"Hệ thống thông tin di động","address":" 301_TA2 TA2"},{"lesson":"7,8,9","subject_name":"Nguyên lý hệ điều hành","address":" 301_TA2 TA2"}]},{"date":1583712000000,"lessons":[{"lesson":"10,11,12","subject_name":"Hệ thống thông tin di động","address":" 301_TA2 TA2"},{"lesson":"7,8,9","subject_name":"Nguyên lý hệ điều hành","address":" 301_TA2 TA2"}]},{"date":1584316800000,"lessons":[{"lesson":"10,11,12","subject_name":"Hệ thống thông tin di động","address":" 301_TA2 TA2"},{"lesson":"7,8,9","subject_name":"Nguyên lý hệ điều hành","address":" 301_TA2 TA2"}]},{"date":1584057600000,"lessons":[{"lesson":"10,11,12","subject_name":"Hệ thống thông tin di động","address":" 301_TA2 TA2"}]}]}

Sản phẩm

API được sử dụng trong app viết bởi anh trong CLB của mình. Ứng dụng xem lịch học Ảnh

TopDev via Traiit.github

KPI là gì? 5 bước xây dựng KPI hiệu quả

KPI là gì? Kinh nghiệm triển khai KPI hiệu quả

KPI được xem là một nhân tố quan trọng trong việc định hướng những chiến lược phát triển lâu dài của bất kỳ tổ chức, doanh nghiệp nào. Tuy nhiên, không phải ai cũng có thể vạch ra những kế hoạch cụ thể và đúng đắn. Chỉ số đánh giá thực hiện công việc – KPI có ý nghĩa lớn trong việc tối ưu hiệu quả việc thiết lập, theo dõi và đánh giá trong công tác Quản trị Nhân sự.

KPI là gì?

KPI (viết tắt của từ Key Performance Indicators) là chỉ số đo lường hiệu quả công việc. KPI giúp đo lường hiệu suất, hiệu quả, chất lượng thực hiện công việc của mỗi cá nhân hoặc của toàn tổ chức, doanh nghiệp, từ đó đưa ra các quyết định quản lý và cải tiến kịp thời.

Mục đích của việc sử dụng chỉ số KPI trong đánh giá thực hiện công việc là nhằm đảm bảo cho nhân viên thực hiện đúng trách nhiệm trong bảng mô tả công việc theo từng vị trí của tổ chức doanh nghiệp. Điều này góp phần cho việc đánh giá thực hiện công việc trở nên minh bạch, rõ ràng, cụ thể, công bằng và đạt hiệu quả cao hơn. 

Tùy theo từng quy mô tổ chức, cơ cấu hoạt động của từng doanh nghiệp mà KPI sẽ khác nhau và ngay cả mỗi bộ phận cũng sẽ có một KPI khác nhau (các phòng ban Sales, Marketing, Product) và ngay cả mỗi người trong một bộ phận cũng có KPI khác nhau (SEO KPIs, Email KPIs, Social KPIs). 

Phân loại các chỉ số KPI

Việc hiểu nhầm và áp dụng máy móc hệ thống KPI sẽ tạo ra những bất cập trong hệ thống đánh giá trong công ty. KPI có thể thuộc ba loại như sau:

KPI chiến lược (Strategic KPIs)

KPI chiến lược là các chỉ số ở mức độ cao nhất, thường được sử dụng bởi các nhà lãnh đạo và quản lý cấp cao để đánh giá tổng quan hiệu suất của toàn bộ công ty. Các KPI chiến lược cung cấp một cái nhìn tổng thể về tình hình hoạt động của công ty nhưng không đi sâu vào chi tiết cụ thể. Ví dụ về KPI chiến lược bao gồm tỷ lệ lợi nhuận trên đầu tư (ROI), biên lợi nhuận và tổng doanh thu của công ty.

KPI hoạt động (Operational KPIs)

KPI hoạt động tập trung vào khung thời gian ngắn hơn, thường đo lường hiệu suất hàng tháng hoặc hàng ngày của các quy trình, phân khúc hoặc khu vực địa lý cụ thể. Các KPI này được sử dụng bởi các quản lý để phân tích các vấn đề phát sinh từ KPI chiến lược. Ví dụ, nếu doanh thu toàn công ty giảm, họ sẽ điều tra xem sản phẩm nào đang gặp khó khăn. Ví dụ về KPI hoạt động bao gồm tăng trưởng doanh số hàng tháng, điểm hài lòng của khách hàng theo tháng và tỷ lệ luân chuyển hàng tồn kho.

KPI chức năng (Functional KPIs)

KPI chức năng tập trung vào các bộ phận hoặc chức năng cụ thể trong công ty. Chúng có thể mang tính chiến lược hoặc hoạt động nhưng cung cấp giá trị lớn nhất cho một nhóm người dùng cụ thể. Mỗi bộ phận sẽ có các KPI riêng để đo lường hiệu suất của họ. Ví dụ về KPI chức năng bao gồm số lượng nhà cung cấp mới được đăng ký trong hệ thống thông tin kế toán mỗi tháng cho bộ phận tài chính, số lượt nhấp chuột vào mỗi email phân phối cho bộ phận marketing và tỷ lệ nhân viên nghỉ việc hàng tháng cho bộ phận nhân sự.

Quy trình xây dựng chỉ số KPI

Dưới đây là một số yêu cầu cơ bản khi xây dựng và áp dụng KPIs, đây không phải là những yêu cầu bắt buộc nhưng lại là những yêu cầu có tính quyết định đến việc thành công của hệ thống.

Xác định các chỉ số KPI bằng công cụ SMART

Công cụ SMART là một phương pháp phổ biến để thiết lập và quản lý các chỉ số KPI hiệu quả. SMART là từ viết tắt của các tiêu chí: Specific (Cụ thể), Measurable (Đo lường được), Achievable (Có thể đạt được), Relevant (Liên quan), và Time-bound (Có thời hạn). Dưới đây là cách áp dụng SMART để xác định các chỉ số KPI.

Xác định các chỉ số KPI bằng công cụ SMART

Specific (Cụ thể)

Chỉ số KPI phải rõ ràng và cụ thể, không mơ hồ. Điều này giúp tất cả các bên liên quan hiểu rõ mục tiêu và hướng tới.

Ví dụ:

  • Không cụ thể: “Tăng doanh số bán hàng.”
  • Cụ thể: “Tăng doanh số bán hàng sản phẩm A lên 20% trong quý 3.”

Measurable (Đo lường được)

Chỉ số KPI phải có thể đo lường được để theo dõi tiến độ và kết quả.

Ví dụ:

  • Không đo lường được: “Cải thiện sự hài lòng của khách hàng.”
  • Đo lường được: “Tăng điểm hài lòng của khách hàng lên 90% trong khảo sát cuối quý.”

Achievable (Có thể đạt được)

Chỉ số KPI phải thực tế và có thể đạt được dựa trên các nguồn lực và khả năng hiện có.

Ví dụ:

  • Không thể đạt được: “Tăng doanh số bán hàng lên 1000% trong một tháng.”
  • Có thể đạt được: “Tăng doanh số bán hàng lên 15% trong 6 tháng tới.”

Relevant (Liên quan)

Chỉ số KPI phải liên quan trực tiếp đến mục tiêu chiến lược của tổ chức.

Ví dụ:

  • Không liên quan: “Tăng số lượng cây xanh trong văn phòng.”
  • Liên quan: “Tăng tỷ lệ giữ chân khách hàng lên 80% trong năm tới.”

Time-bound (Có thời hạn)

Chỉ số KPI phải có thời hạn cụ thể để đảm bảo mục tiêu được theo dõi và đạt được trong một khoảng thời gian nhất định.

Ví dụ:

  • Không có thời hạn: “Tăng lượt truy cập trang web.”
  • Có thời hạn: “Tăng lượt truy cập trang web lên 30% trong vòng 3 tháng.”

Các bước xây dựng hệ thống KPIs

Không phải tất cả các tổ chức/ doanh nghiệp/ dự án đều áp dụng KPI giống nhau. Quy trình áp dụng tùy thuộc vào mục tiêu của từng đơn vị. Tuy nhiên, khung chung về quy trình để xây dựng hệ thống KPIs như sau:

Bước 1: Xác định chủ thể xây dựng KPIs

  • Người xây dựng KPIs thường là nhà quản lý, trưởng bộ phận/ phòng/ ban. Nhưng dù là ai thì cũng phải là người có chuyên môn cao và hiểu rõ về mục tiêu, nhiệm vụ của tổ chức/dự án. Đồng thời hiểu rõ về KPI.
  • Sau khi xây dựng khung cá chỉ số đánh giá hiệu suất cốt yếu, để đảm bảo tính thống nhất, khả thi, cần phải có sự góp ý, thẩm định của các bộ phận/cá nhân liên quan

Bước 2: Xác định chức năng, nhiệm vụ của các bộ phận

Tiếp theo, cần phải xác định rõ chức năng và nhiệm vụ của từng bộ phận trong tổ chức. Điều này giúp đảm bảo rằng các KPIs được thiết lập phù hợp với mục tiêu và nhiệm vụ cụ thể của từng bộ phận, đồng thời hỗ trợ mục tiêu chiến lược tổng thể của tổ chức.

Bước 3: Xác định các chỉ số hiệu suất cốt yếu KPIs

  • Chỉ số của nhóm/ bộ phận: được xây dựng trên cơ sở chức năng, nhiệm vụ của từng nhóm/ bộ phận
  • Chỉ số cá nhân: xây dựng các KPIs cá nhân theo đúng yêu cầu về tiêu chí SMART nêu trên
  • Xây dựng kỳ đánh giá từng chỉ số cụ thể

Bước 4: Xác định khung điểm số cho các kết quả thu được

Để đánh giá và theo dõi tiến độ đạt được các KPIs, cần xác định một khung điểm số hoặc hệ thống đánh giá để định lượng kết quả. Khung điểm số này sẽ giúp phân loại kết quả đạt được theo các mức độ khác nhau, từ đó có thể đưa ra các quyết định quản lý phù hợp.

Bước 5: Đo lường, tổng kết và điều chỉnh nếu có

Cuối cùng, các KPIs cần được đo lường và theo dõi liên tục để đảm bảo rằng các mục tiêu đề ra đang được tiến hành đúng hướng. Dựa trên các kết quả đo lường, cần tổng kết và đánh giá hiệu quả của các KPIs. Nếu cần thiết, điều chỉnh các KPIs để phù hợp hơn với tình hình thực tế và đảm bảo các mục tiêu chiến lược được thực hiện một cách hiệu quả.

Ưu và nhược điểm của KPI

Ưu điểm của KPI

Một công ty có thể muốn phân tích KPI vì nhiều lý do. KPI giúp thông báo cho ban quản lý về các vấn đề cụ thể; phương pháp tiếp cận dựa trên dữ liệu cung cấp thông tin định lượng hữu ích trong việc lập kế hoạch chiến lược và đảm bảo sự xuất sắc trong hoạt động.

KPI giúp giữ cho nhân viên có trách nhiệm. Thay vì dựa vào cảm giác hoặc cảm xúc, KPI được hỗ trợ bởi thống kê và không thể phân biệt đối xử giữa các nhân viên. Khi được sử dụng một cách thích hợp, KPI có thể khuyến khích nhân viên vì họ nhận ra rằng các số liệu của họ đang được giám sát chặt chẽ.

KPI cũng là cầu nối giữa hoạt động kinh doanh thực tế và mục tiêu. Một công ty có thể đặt ra các mục tiêu, nhưng nếu không có khả năng theo dõi tiến độ hướng tới những mục tiêu đó, thì kế hoạch đó hầu như không có mục đích. Thay vào đó, KPI cho phép các công ty đặt ra các mục tiêu và sau đó theo dõi tiến độ hướng tới các mục tiêu đó.

Nhược điểm của KPI

Có một số hạn chế cần xem xét khi làm việc với KPI. Có thể cần một khung thời gian dài để KPI cung cấp dữ liệu có ý nghĩa. Ví dụ, một công ty có thể cần thu thập dữ liệu hàng năm từ nhân viên trong nhiều năm để hiểu rõ hơn về xu hướng tỷ lệ hài lòng trong thời gian dài.

KPI đòi hỏi phải giám sát liên tục và theo dõi chặt chẽ để có ích. Một báo cáo KPI được chuẩn bị nhưng không bao giờ được phân tích thì không có mục đích. Ngoài ra, KPI không được giám sát liên tục về độ chính xác và hợp lý thì không khuyến khích việc ra quyết định có lợi.

KPI mở ra khả năng cho các nhà quản lý “đánh lừa” KPI. Thay vì tập trung vào việc cải thiện quy trình hoặc kết quả thực tế, các nhà quản lý có thể cảm thấy được khuyến khích tập trung vào việc cải thiện KPI gắn liền với tiền thưởng hiệu suất. Ngoài ra, chất lượng có thể giảm nếu các nhà quản lý quá tập trung vào KPI năng suất, và nhân viên có thể cảm thấy bị đẩy quá mức để đáp ứng các đo lường KPI cụ thể mà có thể không hợp lý.

Ví dụ về một số KPI của các phòng ban

Marketing KPIs

Các KPI trong marketing nhằm hiểu rõ hơn về hiệu quả của các chiến dịch tiếp thị và quảng cáo. Các chỉ số này thường đo lường tỷ lệ chuyển đổi dựa trên việc khách hàng tiềm năng thực hiện các hành động cụ thể đối với một phương tiện tiếp thị nhất định.

  1. Website traffic: Số lượng người truy cập vào các trang cụ thể trên website của công ty. Quản lý có thể sử dụng KPI này để hiểu rõ hơn liệu lưu lượng truy cập trực tuyến có được hướng dẫn đến các kênh bán hàng tiềm năng hay không.
  2. Social media traffic: Số lượt xem, theo dõi, thích, chia sẻ, tương tác và các tương tác khác giữa khách hàng và các trang mạng xã hội của công ty.
  3. Conversion rate on call-to-action content: Tỷ lệ chuyển đổi của các chương trình khuyến mãi cụ thể yêu cầu khách hàng thực hiện hành động. Ví dụ, một chiến dịch có thể khuyến khích khách hàng hành động trước khi ngày khuyến mãi kết thúc.
  4. Blog articles published per month: Số lượng bài viết trên blog được đăng mỗi tháng.
  5. Click-through rates: Số lượt nhấp chuột vào các email phân phối. Ví dụ, đo lường số khách hàng mở email, nhấp vào liên kết và hoàn tất mua hàng.

IT KPIs

Các KPI trong IT nhằm theo dõi hoạt động của phòng công nghệ thông tin nội bộ, giúp hiểu rõ hơn về sự hài lòng của nhân viên hoặc liệu bộ phận IT có đủ nhân lực hay không.

  1. Total system downtime: Thời gian hệ thống phải dừng hoạt động để cập nhật hoặc sửa chữa. Khi hệ thống bị dừng, khách hàng có thể không thể đặt hàng hoặc nhân viên không thể thực hiện các nhiệm vụ cụ thể.
  2. Number of tickets/resolutions: Số lượng yêu cầu hỗ trợ và số lượng vấn đề được giải quyết. Các yêu cầu này liên quan đến các vấn đề công nghệ nội bộ như nhu cầu về phần cứng hoặc phần mềm, sự cố mạng.
  3. Number of developed features: Số lượng tính năng mới được phát triển nội bộ.
  4. Count of critical bugs: Số lượng lỗi nghiêm trọng trong hệ thống hoặc chương trình. Công ty cần có tiêu chuẩn riêng về lỗi nhỏ và lỗi lớn.
  5. Back-up frequency: Tần suất sao lưu dữ liệu quan trọng và lưu trữ ở một vị trí an toàn. Quản lý có thể đặt mục tiêu khác nhau cho từng loại thông tin dựa trên yêu cầu lưu trữ hồ sơ.

Sales KPIs

Mục tiêu cuối cùng của công ty là tạo ra doanh thu thông qua bán hàng. Mặc dù doanh thu thường được đo lường qua các KPI tài chính, KPI bán hàng cung cấp cái nhìn chi tiết hơn bằng cách sử dụng dữ liệu phi tài chính để hiểu rõ hơn về quy trình bán hàng.

  1. Customer lifetime value (CLV): Tổng số tiền mà một khách hàng dự kiến sẽ chi tiêu cho sản phẩm của bạn trong suốt mối quan hệ kinh doanh.
  2. Customer acquisition cost (CAC): Tổng chi phí bán hàng và tiếp thị cần thiết để thu hút một khách hàng mới. Bằng cách so sánh CAC với CLV, doanh nghiệp có thể đo lường hiệu quả của nỗ lực thu hút khách hàng.
  3. Average dollar value for new contracts: Giá trị trung bình của các hợp đồng mới. Công ty có thể đặt ngưỡng mong muốn cho việc thu hút các khách hàng lớn hoặc nhỏ hơn.
  4. Average conversion time: Thời gian từ khi tiếp xúc lần đầu với khách hàng tiềm năng đến khi ký kết hợp đồng.
  5. Number of engaged leads: Số lượng khách hàng tiềm năng đã được liên hệ hoặc gặp mặt. Chỉ số này có thể chia nhỏ theo các phương tiện như thăm viếng, email, cuộc gọi điện thoại hoặc các phương thức liên hệ khác với khách hàng.

KPI là gì? Kinh nghiệm triển khai KPI hiệu quả

Quy trình áp dụng KPI trong quản trị mục tiêu và đánh giá nhân viên

Sau khi đã xác định được KPIs cho phòng ban và từng vị trí công việc trong doanh nghiệp, đã đến lúc áp dụng nó vào trong việc quản trị, cả nhân sự và năng suất.

Bước 1: Đánh giá mức độ hoàn thành KPI

Bởi các KPIs đã được xác định dựa trên tiêu chí có thể đo lường, nên chắc chắn đã có phương pháp đánh giá cụ thể cho từng mục KPI. Nếu các KPI có liên quan tới nhau (ví dụ như lượng traffic website và % tăng traffic website trong tháng) thì có thể dùng chung phương pháp đo lường (dùng công cụ phân tích).

Thông thường, mức độ hoàn thành KPI được chia thành 2-5 thang điểm.

Càng nhiều mức độ điểm số thì việc đánh giá càng khách quan. Tuy nhiên, nếu chia quá nhỏ thì việc đánh giá mức độ hoàn thành KPI có thể gặp khó khăn.

Bước 2: Liên hệ giữa đánh giá KPIs và lương thưởng

Với mỗi mức độ hoàn thành KPIs, người xây dựng hệ thống KPIs sẽ xác định một mức lương thưởng nhất định.

Chính sách này có thể được quy định từ trước bởi các cấp lãnh đạo trong doanh nghiệp, của quản lý cấp cao nhất trong phòng ban, người xây dựng hệ thống KPIs hoặc do chính các nhân viên tự thống nhất với nhau.

Thông thường, sẽ có một buổi nghiệm thu đánh giá kết quả công việc định kỳ cuối mỗi kỳ đánh giá. Việc đánh giá nên được khách quan và toàn diện bằng cách kết hợp ý kiến của cả sếp, đồng nghiệp, khách hàng và bản thân nhân viên.

>>> Xem thêm: Công cụ tính lương Gross – Net

Bước 3: Điều chỉnh và tối ưu KPI

KPIs có thể được theo dõi và điều chỉnh theo thời gian.

Ban đầu, hãy xem xét các KPIs vừa được lập để đảm bảo rằng các số liệu là phù hợp. Có thể mất vài tháng đầu để mọi thứ đạt đến mức tối ưu nhưng một khi đã có được KPI cuối cùng, hãy duy trì nó trong ít nhất một năm.

Một điểm quan trọng bạn cần nhớ là đừng ứng xử sai lầm với kết quả đánh giá KPI. Theo tâm lý chung, khi nhân viên không đạt đủ chỉ tiêu đề ra thì bị khiển trách, cắt lương, cắt thưởng. Khi nhân viên đạt chỉ tiêu thì lại lập tức tăng chỉ tiêu. Đó được xem là cách điều hành chưa khoa học và về lâu về dài, công ty không thể phát triển. Động lực và sự quan tâm đối với nhân viên rất quan trọng.

Do vậy, bạn cần cân nhắc kỹ lưỡng trước khi quyết định một việc gì đó vì đều có ảnh hưởng đến khả năng phát triển của cá nhân nhân viên và sự thăng tiến lâu dài của công ty. Điều này chứng tỏ động lực và sự quan tâm rất quan trọng đối với nhân viên.

Câu hỏi thường gặp về KPI 

Đối với công ty chưa có hệ thống quy trình đúng chuẩn, cần triển KPI như thế nào? 

Có thể nói đây là quan trọng mà sếp cần quan tâm. Đi từ chức năng, nhiệm vụ để bắt đầu xây dựng chu trình. Bí quyết là hãy giao việc theo con người. Người được phân công sẽ được trao cơ hội thực hiện công việc và xác lập mục tiêu phát triển lâu dài. Theo từng tháng, từng năm, những kế hoạch của họ dần bám sát mục tiêu chung của tổ chức và doanh nghiệp. Đó là cơ sở để tiếp cận việc xây dựng hệ thống quy trình một cách đúng chuẩn hơn. tiêu đề tuyển dụng

Tồn đọng của chỉ số KPI các quý trước làm ảnh hưởng đến tính chính xác về kết quả KPI các quý sau?

Đây là tình huống thường gặp, tuy nhiên vẫn tùy thuộc vào quy trình tổ chức của từng công ty. Có công ty quan tâm chặt chẽ việc kiểm soát về mục tiêu, có công ty chọn cách không cộng dồn. Nhưng dù thế nào thì việc quan trọng vẫn là phân tích rõ nguyên nhân.

Nguyên nhân có thể giải thích là khi rất nhiều nhân viên đạt nhưng chỉ có 1 vài người không đạt thì đó là do chủ quan. Còn nếu tất cả nhân viên hoặc số lượng lớn không đạt thì có thể là do khách quan. Nên tránh việc tạo thêm áp lực cho nhân viên nếu họ đã nỗ lực hết mình.

Khi áp KPI thường dễ bị gãy, cảm thấy quy trình khá rườm rà, có cách nào giúp giải quyết vấn đề này?

KPI cần được quan tâm từ lý luận đến việc vận dụng vào thực tiễn. nguyên tắc không nên bỏ bước nhưng có thể tối giản lại các bước. Ví dụ: Để xây dựng chiến lược cần làm đủ các bước mới ra chiến lược. Tuy nhiên để đơn giản hóa thì chỉ cần trả lời được những câu hỏi về chiến lược:

– Công ty là ai?

– Cạnh tranh với đối thủ nào?

– Công ty muốn mục tiêu đạt được như thế nào?

Làm thế nào để tìm ra các KPI “key” cho các phòng ban?

Để tìm ra thì con đường an toàn nhất chính là thử để trải nghiệm nhiều thứ. Đặt những câu hỏi xoay quanh các yếu tố chi phối đến KPI. Phản ứng của nhân viên đối với KPI phụ thuộc vào nhiều yếu tố như thói quen, quan hệ,.. như thế nào từ đó xác định được tham số chính. Tham số chính phải là mục tiêu của công ty. Và phản ứng của nhân viên chính là thước đo xem KPI có thật sự phù hợp hay không. Đặc biệt, việc điều chỉnh KPI nên được điều chỉnh theo quý, tránh tình trạng thay đổi liên tục.

KPI là gì? Kinh nghiệm triển khai KPI hiệu quả

Lời kết

Hệ thống KPI tốt là hệ thống KPI thực tế với hoàn cảnh doanh nghiệp của mình, khả thi, thu thập được thông tin chính xác với chi phí và thời gian hợp lý. Nếu muốn thiết lập các KPIs cho từng phòng ban, nhân viên nhân sự phải tham vấn với trưởng phòng ban đó và cấp cao hơn (họ cần những thông tin gì để đưa ra các quyết định quản lý) ngoài ra, cũng cần căn cứ vào mục tiêu và chức năng của phòng ban đó…

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

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

Các vị trí tuyển dụng ngành IT khiến nhà tuyển dụng đau đầu

Các vị trí tuyển dụng ngành IT khiến nhà tuyển dụng đau đầu

Tuyển dụng nhân viên IT (hay công nghệ) được xem là ngành có tỷ lệ nhảy việc rất cao, chưa kể đến một số vị trí  công việc trong ngành công nghệ rất khó tuyển dụng được nhân tài. Nhân lực không thiếu nhưng rất nhiều công ty trong tình trạng thiếu hụt nhân sự trầm trọng. Trước tình trạng này, việc tuyển dụng cần phải được thay đổi theo chiến lược bài bản và hoạch định lâu dài.

  7 bước cấp thiết của chiến lược tuyển dụng IT trên social media hiệu quả

Bước vào một thập kỷ mới, các CIO tiếp tục gánh vác trách nhiệm lớn hơn đối với nhiều nhiệm vụ kinh doanh chiến lược, bao gồm cả những nhiệm vụ liên quan đến an ninh mạng, phân tích và khoa học dữ liệu, AI/machine learning.

Với trách nhiệm to lớn đó, cần tìm được nhân tài với các kỹ năng cần thiết để thúc đẩy những sáng kiến ​​chiến lược đó. Trong thời buổi này, tìm ứng viên phù hợp khó như mò kim đáy biển. Mỗi ngành lại có đặc thù riêng, nhất là trong ngành IT, ngành có tỷ lệ nhảy việc cao nhất thì việc tuyển dụng với những vị trí quan trọng thì càng lại khó nhằn hơn. Sau đây là 12 vị trí IT khó tuyển dụng nhất khiến nhà tuyển dụng đau đầu.

12 vị trí IT khiến các nhà tuyển dụng đau đầu

Theo báo cáo IT của TopDev, năm 2020 Việt Nam sẽ cần hơn 400,000 nhân lực ngành IT, và con số này có thể sẽ tăng lên đến 500,000 vào năm 2021, trong đó có khoảng 39% các nhà lãnh đạo IT dự đoán khó tìm thấy người có kỹ năng bảo mật tốt. Những yêu cầu đáng chú ý khác như tư duy thiết kế, DevOps và các kỹ năng Agile (phương pháp phát triển phần mềm linh hoạt để chúng có thể đến tay người dùng càng sớm càng tốt) đều được coi là những thách thức tuyển dụng trong năm tới.

Dưới đây là 12 lĩnh vực mà các nhà lãnh đạo IT dự đoán sẽ khó tìm được người có kỹ năng phù hợp nhất trong năm nay:

  • An ninh mạng: 39%
  • Khoa học dữ liệu/phân tích: 35%
  • AI/machine learning/RPA: 31%
  • Tích hợp/dịch vụ đám mây: 18%
  • Công nghệ kế thừa: 18%
  • Các quy trình DevOps/DevSecOps/Agile: 17%
  • Internet of things: 17%
  • Kiến trúc đám mây: 16%
  • Tư duy thiết kế/UX: 16%
  • Kỹ sư phần mềm: 15%
  • Phát triển ứng dụng: 15%
  • Quản lý đa đám mây: 15%

Lý do từ đâu mà số lượng ứng viên nhiều nhưng nhà tuyển dụng luôn phải đau đầu vì không tuyển được ứng viên phù hợp. Nguyên nhân có thể đến từ ứng viên hoặc đến từ nhà tuyển dụng? Để phân định “lỗi” tại ai thì sẽ chẳng ai chịu nhận cả, vậy nên bài viết này tập trung nhiều hơn vào nhà tuyển dụng. 

Sự linh hoạt và kinh nghiệm của nhân viên có tầm quan trọng ra sao?

Các vị trí tuyển dụng ngành IT khiến nhà tuyển dụng đau đầu

Bảo mật vẫn là ưu tiên quan trọng đối với các CIO. Trong thế giới của Hybrid Cloud, làm việc từ xa, BYOD (Bring Your Own Device) và nhiều công nghệ khác mở rộng ranh giới của mạng doanh nghiệp, cần nhiều tài nguyên hơn để đảm bảo rằng các mạng, dữ liệu và tài sản được bảo vệ.

Nhà tuyển dụng nên xem xét kinh nghiệm của nhân viên, như mọi tương tác mà nhân viên đó có với công ty trong toàn bộ thời gian làm việc đến khi nghỉ hưu và cố gắng tạo ra một môi trường tích cực, nơi mọi người có thể làm việc hết sức mình.

Điểm mấu chốt của việc này là cung cấp các cơ hội học tập và phát triển, thúc đẩy sự đa dạng và hòa nhập. Quan trọng nhất, cần loại bỏ những yếu tố gây ức chế cho nhân viên, bằng cách cho phép họ truy cập các công cụ họ cần và thích sử dụng để thực hiện công việc tốt nhất, dù ở bất cứ đâu, vào bất cứ lúc nào. Sự linh hoạt này sẽ khiến nhân viên cảm thấy thoải mái hơn từ đó nâng cao năng suất làm việc hơn.  

Tận dụng nguồn lực từ bên trong

Các vị trí tuyển dụng ngành IT khiến nhà tuyển dụng đau đầu

Bảo mật là thách thức lớn đối với các công ty toàn cầu, do sự phức tạp và có rất nhiều các quy tắc, quy định khác nhau. Nguồn trải nghiệm của khách hàng cũng rất khó tìm được.

NTT Data Services đã giải quyết vấn đề này bằng cách thí điểm một ứng dụng cho phép nhân viên khám phá các cơ hội trên phạm vi toàn công ty và tự đề cử cho những vai trò đó.

Họ sẽ có thể thấy các kỹ năng và kinh nghiệm của mình phù hợp với yêu cầu của từng vị trí ra sao, nhận được các khóa học và đọc các đề xuất để thu hẹp khoảng cách kỹ năng, v.v… Từ đó, họ có cơ hội được trao dồi kĩ năng và chuyên môn hơn, được giao trọng trách tại những vị trí quan trọng hơn.

Sức mạnh của việc nâng cao kỹ năng

Các vị trí tuyển dụng ngành IT khiến nhà tuyển dụng đau đầu

Các nhà khoa học dữ liệu, kỹ sư dữ liệu, chuyên gia bảo mật và full-stack developer (người phụ trách cả front-end và back-end) là những vị trí mang tính thách thức đối với nhà tuyển dụng.

Với mỗi công ty công nghệ, người chịu trách nhiệm về kỹ thuật có vai trò quan trọng hơn bao giờ hết. Với sự cạnh tranh khốc liệt đối với các nguồn lực hạn chế, việc cung cấp các cơ hội nâng cao và học những kỹ năng mới là điều rất cần thiết. Đó là điều giữ chân được nhân viên, chúng ta cũng khó có thể lường trước được, liệu rằng một ngày đẹp trời nào đó, các kỹ thuật, công nghệ của công ty mình lại “tá túc” bên đối thủ. 

Giảm áp lực với công nghệ

 

Các vị trí tuyển dụng ngành IT khiến nhà tuyển dụng đau đầu

Tại BMC Software, CIO Scott Crowder đang tận dụng việc tự động hóa để giúp giảm áp lực phải hoàn thành một số nhiệm vụ IT nhất định, đặc biệt là trong lĩnh vực phân tích Big Data và khoa học dữ liệu.

Ngoài ra việc áp dụng công nghệ trong quản trị nguồn lực sẽ giúp ích rất nhiều  và có tác động không hề nhỏ cho nhân sự. Khi công nghệ thông tin thiết lập được chỗ đứng của nó trong bộ phận nguồn nhân sự, các chuyên viên nhân sự sẽ nắm bắt được các thông tin rõ ràng, minh bạch, và toàn diện hơn. Họ sẽ nhận biết được các khuynh hướng mới nhất trong hoạch định chính sách, các hoạt động tuyển dụng trong ngành. Ngoài ra, họ cần phải có các thông tin hiện hành về pháp luật và các quy định có liên quan bởi vì điều này giúp các chuyên viên nhân sự thể hiện được sự linh hoạt trong trường hợp có những thay đổi không  dự kiến trước. Hơn nữa, những đặc tính này sẽ làm tăng giá trị và sự đóng góp của bộ phận nhân sự cho tổ chức.

Vì vậy, để bắt kịp với sự thay đổi luật liên tục, thông tin phải luôn được cập nhật. Công nghệ đã cho phép các các công ty kết nối với internet để tương tác với các chuyên gia trong ngành. Nó đã giúp nhiều chuyên viên nhân sự trong việc thu thập thông tin mà họ cần phải duy trì để chứng minh các đặc tính mong muốn và khả năng. Những bằng chứng này cũng cho thấy công nghệ đã không chỉ cho phép các chuyên gia nguồn nhân lực để truy cập và phân phối thông tin mà còn có ảnh hưởng đến mong ước của họ.

Công nghệ thì không ngừng phát triển mà AI thì hiện được ứng dụng rộng rãi trong việc xây dựng, phân tích, cũng như trực quan hóa các mô hình dữ liệu. Tất nhiên không thể phủ nhận vai trò của con người trong việc giải thích và tổng hợp những điều quan trọng nhất từ dữ liệu, nhưng việc áp dụng tự động hóa vào một số giai đoạn sẽ giúp ích rất nhiều.

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

Xem thêm các vị trí công việc khác tại TopDev

Lựa chọn framework frontend nào trong thời điểm hiện tại

lua-chon-framework-frontend-nao-trong-thoi-diem-hien-tai
Bài viết được sự cho phép của tác giả Lưu Bình An

Thời điểm hiện tại nếu bạn đang làm Frontend thì chắc hẳn đang sử dụng một framework nào đó trong 3 thằng này, Vue, React, và Angular. Nếu trước đây trên cả tá framework, và cả tá ví dụ về làm một ứng dụng web ToDoMVC trên github, thì cuộc chơi giờ đây đã đỡ hơn rất nhiều, khi chúng ta chỉ còn 3 lựa chọn sáng giá.

Để viết một ứng dụng phức tạp, chúng ta bắt buộc phải sử dụng framework, vì nếu không có những framework như vậy, chúng ta sẽ tốn không biết bao nhiêu thời gian để đạt được kết quả cuối cùng.

Chắc các bạn cũng như mình đã quá mệt mỏi với những bài viết so sánh 3 framework trên, ai ngon hơn ai, các bạn cũng nên dừng tìm kiếm câu trả lời cho câu hỏi “Top 10 framework nên xài trong năm 2019”. Tại sao? Vì những bài viết này đa phần sẽ tập trung vào đếm số lượng sao trên Github, số lượng tải về từ NPM, số câu hỏi liên quan trên Stack Overflow. Những con số thống kê vô hồn này chỉ có tác dụng trong những trường hợp cụ thể, như đi quảng bá về mức độ phủ rộng của những framework này. Nếu bạn là dân kỹ thuật và nhìn nhận ở góc độ kỹ thuật, phán xét những framework này ở góc độ kỹ thuật chứ không thể căn cứ trên số lượt view và download

Kỹ thuật quyết định bên trong của từng framework là gì, đâu là sự khác nhau thực sự của từng framework

Mục tiêu cuối cùng của các framework đều là để giúp chúng ta viết ứng dụng web hiệu quả nhất có thể, việc cạnh tranh giữa các framework với nhau là ý tưởng tốt hay không? Mỗi framework sẽ có một số lượng người sử dụng nhất định, như React-Angular-Vue hiện tại có khoản hơn nửa triệu developer đang ăn nằm với nó hằng ngày.

Không có khái niệm “điểm tốt” và “điểm chưa tốt” cho các framework. Người ta thường hay hỏi mấy câu, framework nào xài ngon nhất. Một dạng câu hỏi bạn nên ngừng làm khó nhau vì không thể nào so sánh như toán học 3 > 2 > 1

Việc thiết kế phần mềm luôn đòi hỏi một sự đánh đổi, đặc biệt là với web, chắc có lẽ vì có quá nhiều thứ người ta muốn làm thông qua web, từ một trang web đơn giản chỉ là HTML tĩnh đến cả một hệ thống phức tạp nhất bạn có thể nghĩ ra, để đáp ứng toàn bộ những nhu cầu khác nhau đó, các framework phải chấp nhận đánh đổi một số thứ, chứ ko thể đáp ứng toàn bộ với một giải pháp toàn diện được

Front-end tuyển dụng lương cao trên TopDev

Scope

Framework cung cấp bao nhiêu đồ chơi cho bạn

Một trong những ví dụ kinh điển giữa thư viện và framework là React và Angular. React được xem là thư viện trong khi Angular sẽ là framework

Là một thư viện, React chỉ muốn tập trung cung cấp mô hình để phát triển UI. Để hình dung dễ hơn, liên tưởng tới các nhà máy sản xuất bún, scope rất cụ thể, tôi sẽ tập trung vào việc sản xuất ra bún, việc các bạn đem bún này về nấu thành món gì là do bạn, lý do tại sao ecosystem của React luôn luôn sôi động, rất nhiều dev đã chế biến thành các món khác nhau, như với món bún chúng ta có bún riêu, canh bún, bún đậu mắm tôm, bún cá châu đốc, bún mắm, vâng vâng.

Trong khi đó, Angular với tư cách là một framework thực thụ, nó sẽ tiếp cận vấn đề theo hướng từ trên xuống. Hình dung như mì gói nuôi nhân tài ở Việt Nam, với mọi thứ đóng gói đầy đủ để bạn có một món cứu đói tạm thời, bột nêm, dầu, hành. Angular cung cấp hệ thống form validation, animation,… rất nhiều tính năng khác mà chúng ta rất cần thiết để dựng nên một ứng dụng hoàn chỉnh. Với scope lớn như vậy, mọi tính năng khi thiết kế đã được nghĩ đến làm thế nào để chúng sống chung với nhau một cách mượt mà

Lợi ích khi có scope nhỏ và cụ thể

  • Ít khái niệm, dễ tiếp cận ngay từ đầu. Trong React bạn sẽ có mô hình làm component, prop, state, virtual DOM, hook, bao nhiêu kiến thức thôi là bạn đã có thể bắt đầu làm quen React.
  • Linh động, món bún có thể kết hợp với rất nhiều thứ gia vị, cách nấu khác nhau để cho ra các món ăn khác nhau.
  • Team duy trì React rảnh hơn, những chuyện khác đã có cộng đồng gánh vác, như đi làm React Router, React Redux, React Form, họ sẽ có thời gian nhiều hơn để tập trung vào các ý tưởng mới cho bản thân React

Hạn chế khi scope nhỏ và cụ thể

  • Khi bạn phải xây dựng một ứng dụng phức tạp, một vài concept cơ bản là không đủ xài, bạn phải tự viết khá nhiều. Giống như bắt bạn diễn tả tất cả những câu nói hằng ngày bằng cách chỉ sử dụng 10 từ, thì bạn diễn đạt ý như thế nào?
  • Cách làm (pattern) mới ngày càng nhiều. Nói học React rất dễ là bạn thực sự chưa biết đến ngoài React ra, bạn có phải học vô số các cách làm (pattern) khác, như Redux, mặc dù không có trong tài liệu chính thức trên React, nhưng lại là thứ bạn không thể bỏ qua nếu muốn dùng React như một React Developer chân chính. Nào là Higher Other Component, Render Props, React hook, rồi quá trời cách để sử dụng CSS trong JS, tài liệu chính thức của React sẽ không nói bạn nên dùng gì, bạn phải tự tìm hiểu và chọn cái nào mình thích. Những kiến thức này bạn phải nạp từ từ như là một kiến thức chính quy để bạn có thể vỗ ngực xưng tên tao là React Developer.
  • Ecosystem phát triển quá nhanh không đồng nghĩa với chất lượng các thư viện dành cho React cái nào cũng ngon, rất nhiều thư viện nổi lên một thời rồi ra đi mãi mãi, như Flux, rồi bao nhiêu là cách viết CSS trong JS. Trong ta luôn trong cảm giác lo sợ lỡ mất không xài thằng ngon nhất rồi

Lợi ích khi có scope bao la bát ngát

  • Các vấn đề thường gặp đã được giải quyết hết, bạn có ngay tô mì để ăn trong 5 phút mà không cần suy nghĩ nhiều, cứ lên trang chủ, đọc tài liệu, học cái framework xong là xài, để nghiên cứu giây mơ rễ má có thể để sau. Đâu ai cấm bạn ăn mì gói bỏ thêm thịt bò, trứng hay tôm càng.
  • Các tính năng bên trong được thiết kế để làm việc mượt mà đảm bảo tính thống nhất của toàn bộ hệ thống, không cần chạy đi đâu để kiếm giải pháp cho một vấn đề quá căn bản, bạn cứ lên trang chính thức của nó xem người ta giải quyết vấn đề đó như thế nào, không còn phải suy nghĩ lựa chọn đâu là cách tốt nhất trong hơn chục cái giải pháp được đưa ra.

Hạn chế của scope bao quát bát ngát hết vườn hoa

  • Học là một quá trình dài, nhiều khi để tới được bước có một cái gì đó hiển thị trên màn hình, bạn phải trải qua cả khóa học bài bảng. Những người không có kiến thức về một ngôn ngữ Backend nào cả, chỉ biết HTML, CSS, javascript mà đọc tài liệu của Angular thì phải nói là một trãi nghiệm vô cùng đau thương
  • Đôi khi một giải pháp được cung cấp sẵn lại không phù hợp với tính huống gặp phải, chúng ta ước gì có thể làm cách khác, nhưng điều đó là không thể.
  • Hệ thống lớn đòi hỏi chi phí rất lớn để duy trì cũng như đưa ra các ý tưởng mới, rất nhiều thành phần phải tích hợp để cả hệ thống có thể kết nối hoạt động trơn tru

Cơ chế render

Cách structure, cách quản lý code của framework

Để đơn giản chúng ta so sánh JSX và Templates

Điểm cộng của JSX/Virtual DOM

  • Tất cả điều là javascript, ai cũng thích, bạn không cần biết những syntax mới được định nghĩa bởi framework, những kiến thức bạn nạp vào là kiến thức nền tảng của javascript, một khi bạn đã cứng tay, bạn có thể thiên biến vạn hóa theo sở thích.
  • Xem view như một dạng *data *, một component sẽ return một thứ gì đó dựa vào những giá trị input khác nhau, bạn có thể làm những thứ như chụp một cái snapshot dựa trên virtual DOM, render nó trên những target khác như terminal, PDF, Canvas, WebGL

Điểm trừ của JSX/Virtual DOM

  • Vốn dĩ sẽ tiêu tốn tài nguyên. Khi React mới ra đời, họ cũng đã trả lời cho câu hỏi làm như vậy có chậm không?vâng nó chậm nhưng vẫn nhanh đủ để dùng. Nếu nhìn nhận về mặc kỹ thuật, phải làm rất nhiều thao tác xử lý trên virtual DOM. Kích thước của một VDom chuẩn sẽ liên quan tới kích thước của view chứ không phải số lượng node sẽ thay đổi.
  • Hàm render cơ bản rất linh động, vì linh động nên nó cũng rất khó optimize, linh động ở đây muốn nói đến một hàm render như thế này
function render() {
    const children = []
    for (let i = 0; i < 5; i++) {
        children.push(h('p', {
            class: 'text'
        }, i === 2 ? this.message: "vuilaptrinh.com" ))
    }
    return h('div', { id: 'content'}, children)
}

Chúng ta có thể tạo parent node trước, rồi sau đó nhét thêm các node con, hoặc bất cứ thứ gì bạn có thể nghĩ ra được, javascript rất linh động, có nhiều tình huống đặc biệt chúng ta khó có thể đảm bảo optimize được cho tất cả.

  • Giải pháp của React cho tình huống này không tập trung vào việc làm cho virtual DOM nhanh hơn, mà làm cho chúng ta cảm giác performance tốt hơn (giống như việc gửi tin nhắn trên facebook luôn cảm giác như gửi được liền chứ không cần đợi), việc đó được thực hiện bằng các kỹ thuật runtime scheduling, concurrent mode, time slicing. Những giải pháp này buộc họ phải tự tạo và quản lý một stack riêng, một công việc rất tốn kém

Điểm cộng của Template

  • Với cách tiếp cận trực tiếp hơn cho việc render, performance đương nhiên sẽ gần hơn render performance của trình duyệt, với cách viết template như thế này sẽ không thể nào thay đổi được thứ tứ của những element đã khai báo
<template>
    <div id="content">
        <p class="text">Lorem ipsum</p>
        <p class="text">Lorem ipsum</p>
        <p class="text">{{ message }}</p>
        <p class="text">Lorem ipsum</p>
    </div>
</template>

Việc đoán trước được những gì có thể thay đổi, giúp việc cải thiện hiệu năng cũng sẽ dễ tiếp cận hơn

  • Tùy vào tình huống, có thể giá trị baseline runtime sẽ thấp hơn

Điểm trừ của Template

  • Dính chặt vào cú pháp của Template, bạn sẽ mất đi một ít tự do bay nhảy bằng javascript thông thường. Sự sáng tạo của chúng ta bị giới hạn trong những thứ mà framework cung cấp. Lỡ đâu bạn đã là một master javascript và bạn thấy cách làm của framework này chuối cả nải và muốn làm khác hơn.
  • Cái giá phải trả cho giá trị baseline runtime thấp sẽ là kết quả trả về của mỗi template sẽ dài dòng hơn. Đôi khi để code chạy nhanh nhất có thể, chúng ta phải nhét cứng một số thông tin bên trong output

Cơ chế State

mutable vs immutable, dirty checking vs dependency tracking, reactivity vs simulated reactivity

Rất tiếc, Evan You không có thời gian trình bài phần này trong bài thuyết trình của mình.

Tổng kết

Nếu bạn đang muốn chọn một framework một cách hợp lý, bạn phải hiểu được những gì mà framework đó đang đánh đổi, biết hướng đi của framework đó có khớp với những gì bạn ưu tiên hàng đầu cho dự án mình làm.

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

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

Tìm việc IT lương cao, đãi ngộ tốt trên TopDev ngay!

4 ứng dụng của Array.from

Bài viết này sẽ liệt kê 4 ứng dụng của Array.from: khởi tạo một dãy số, tạo một mảng gồm n phần tử giống nhau, sao chép một array và tạo một array không trùng lặp.

Array.from được dùng để convert giá trị về kiểu Array, cú pháp như sau

Array.from(arrayLikeOrIterable [, mapFunction [, thisArg]])

Có 3 tham số có thể truyền vào, 2 thằng sau không bắt buộc

  • arrayLikeOrIterable giá trị muốn chuyển
  • mapFunction(item, index) function sẽ chạy qua toàn bộ các phần tử
  • thisArg được sử dụng như giá trị this bên trong mapFunction
const someNumbers = { "0": 10, "1": 15, length: 2 }
Array.from(someNumbers, value => value * 2)
// => [20, 30]

Ứng dụng 1: khởi tạo một dãy số

Khởi tạo dãy số từ 0 đến giá trị truyền vào

function range(end) {
    return Array.from({ length: end }, (_, index) => index)
}

range(4)
// => [0,1,2,3]

Ứng dụng 2: Tạo một mảng gồm n phần tử giống nhau

Input: số lượng phần tử, giá trị. Output: toàn một mảng n phần tử giống với giá trị đã cho.

const length = 3
const init = 0
const result = Array.from({ length }, () => init )

// => [0,0,0]

Thật ra chúng ta cũng có thể viết bằng Array.fill() dễ nhìn hơn

const length = 3;
const init   = 0;
const result = Array(length).fill(init);

Ứng dụng 3: sao chép một array

Nếu chỉ đơn giản là sao chép array thì ta có thể viết

const cloneArray = [...array1, array2]

Nhưng nếu array1 lại chứa một array bên trong đó thì sao? Hàm đệ quy để chép tuốt

function recursiveClone(val) {
  return Array.isArray(val) ? Array.from(val, recursiveClone) : val;
}

const numbers = [[0, 1, 2], ['one', 'two', 'three']];
const numbersClone = recursiveClone(numbers);

numbersClone; // => [[0, 1, 2], ['one', 'two', 'three']]
numbers[0] === numbersClone[0] // => false

Ứng dụng 4: Tạo một array không trùng lặp

Thật ra thì bản thân Array.from không làm được, chúng ta dùng new Set() rồi mới sau đchuyển ngược lại về array

function unique(arr) {
    return Array.from(new Set(arr))
}
unique([1, 1, 2, 3, 3]); // => [1, 2, 3]

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

Xem thêm việc làm IT job trên TopDev

TopDev via Vuilaptrinh

Hướng dẫn lấy Date và Time trong C++

Date và Time trong C++

Thư viện chuẩn C++ (C++ Standard Library) không cung cấp một kiểu Date thích đáng. C++ kế thừa cấu trúc và hàm để thao tác Date và Time từ C. Để truy cập các hàm và cấu trúc liên quan tới Date và Time, bạn sẽ cần khai báo trong chương trình của bạn.

Có 4 kiểu liên quan tới thời gian: clock_t, time_t, size_t, và tm. Trong đó các kiểu clock_t, size_t và time_t có thể biểu diễn System date và time dạng int.

Kiểu cấu trúc tm giữ Date và Time trong mẫu một cấu trúc C có các phần tử sau:

struct tm {
 int tm_sec; // seconds of minutes from 0 to 61
 int tm_min; // minutes of hour from 0 to 59
 int tm_hour; // hours of day from 0 to 24
 int tm_mday; // day of month from 1 to 31
 int tm_mon; // month of year from 0 to 11
 int tm_year; // year since 1900
 int tm_wday; // days since sunday
 int tm_yday; // days since January 1st
 int tm_isdst; // hours of daylight savings time
}

Bảng dưới liệt kê các hàm quan trọng trong khi bạn làm việc với Date và Time trong C hoặc C++. Tất cả hàm này là một phần của thư viện chuẩn và bạn có thể kiểm tra chi tiết bởi sử dụng tham chiếu tới Thư viện chuẩn C++ được cung cấp dưới đây:

STT Hàm & Mục đích
1 time_t time(time_t *time);

Trả về thời gian theo lịch của hệ thống, là số giây đã trôi qua từ 1/1/1970. Nếu hệ thống không có thời gian, nó trả về -1

2 char *ctime(const time_t *time);

Hàm này trả về một con trỏ tới một chuỗi của mẫu: day month year hours:minutes:seconds year\n\0.

3 struct tm *localtime(const time_t *time);

struct tm *localtime(const time_t *time); Hàm này trả về một con trỏ tới cấu trúctm biểu diễn local time

4 clock_t clock(void);

Hàm này trả về một giá trị mà xấp xỉ với lượng thời gian của chương trình đang gọi đã đang chạy. Trả về -1 nếu thời gian là không có sẵn

5 char * asctime ( const struct tm * time );

Trả về một con trỏ tới một chuỗi mà chứa thông tin được lưu giữ trong cấu trúc được trỏ tới bởi time được biến đổi thành mẫu: day month date hours:minutes:seconds year\n\0

6 struct tm *gmtime(const time_t *time);

Trả về một con trỏ tới time trong mẫu cấu trúc tm. Thời gian được biểu diễn dạng Coordinated Universal Time (UTC), về bản chất là Greenwich Mean Time (GMT).

7 time_t mktime(struct tm *time);

Trả về thời gian theo lịch tương đương với thời gian được tìm thấy trong cấu trúc được trỏ tới bởi time

8 double difftime ( time_t time2, time_t time1 );

Hàm này tính toán sự khác nhau về số giây của time1 và time2

9 size_t strftime();

Hàm này có thể được sử dụng để định dạng Date và Time trong một định dạng cụ thể

  1001 Tips: Con trỏ và hàm (Pointer & Function) trong C++

Date và Time hiện tại trong C++

Giả sử bạn muốn lấy date và time hiện tại của hệ thống: hoặc local time hoặc dạng UTC. Ví dụ sau thực hiện công việc trên:

#include <iostream>
#include <ctime>
using namespace std;
int main( )
{
 // current date/time based on current system
 time_t now = time(0);

 // convert now to string form
 char* dt = ctime(&now);
 cout << "The local date and time is: " << dt << endl;
 // convert now to tm struct for UTC
 tm *gmtm = gmtime(&now);
 dt = asctime(gmtm);
 cout << "The UTC date and time is:"<< dt << endl;
}

Khi code trên được biên dịch và thực thi, nó cho kết quả sau:

The local date and time is: Sat Jan 8 20:07:41 2011

The UTC date and time is:Sun Jan 9 03:07:41 2011

Định dạng Time bởi sử dụng cấu trúc tm trong C++

Cấu trúc tm là rất quan trọng trong khi làm việc với Date và Time trong C và C++. Cấu trúc này giữ Date và Time trong mẫu của một cấu trúc C đã được đề cập ở trên. Hầu hết các hàm liên quan tới thời gian đều sử dụng cấu trúc tm. Ví dụ sau sử dụng các hàm đa dạng liên quan tới Date và Time và cấu trúc tm:

Trong khi sử dụng cấu trúc trong chương này, mình giả sử bạn đã hiểu cơ bản về cấu trúc trong C và cách truy cập các thành viên của cấu trúc bởi sử dụng toán tử ->.

#include <iostream>
#include <ctime>
using namespace std;
int main( )
{
 // current date/time based on current system
 time_t now = time(0);
 cout << "Number of sec since January 1,1970:" << now << endl;
 tm *ltm = localtime(&now);
 // print various components of tm structure.
 cout << "Year: "<< 1900 + ltm->tm_year << endl;
 cout << "Month: "<< 1 + ltm->tm_mon<< endl;
 cout << "Day: "<< ltm->tm_mday << endl;
 cout << "Time: "<< 1 + ltm->tm_hour << ":";
 cout << 1 + ltm->tm_min << ":";
 cout << 1 + ltm->tm_sec << endl;
}

Khi code trên được biên dịch và thực thi, nó cho kết quả sau:

Number of sec since January 1, 1970:1294548238
Year: 2011
Month: 1
Day: 8
Time: 22: 44:59

Đừng quên xem thêm các bài viết:

Cơ hội việc làm C++ hấp dẫn tại TopDev đang chờ bạn!

Sử dụng React Context như thế nào cho hiệu quả

Để có thể quản lý được state của ứng dụng một cách tốt nhất, chúng ta cần sự phân chia phù hợp giữa local state (internal state của component) và state cửa ứng dụng đặt trong React Context. Một vài điều muốn chia sẽ để nâng cao khả năng bảo trì và trải nghiệm nếu sử dụng đến context trong React.
Chúng ta có một module (một nhánh trên cây react component cho dể hình dung) muốn sử dụng Context là count, chúng ta tạo file count-context.js
// src/count-context.js
import React from 'react'

const CountStateContext = React.createContext()
const CountDispatchContext = React.createContext()

Điều muốn nói đầu tiên là chúng ta cố tình không khai báo giá trị khởi tạo cho CountStateContext, nếu muốn bạn có thể gọi React.createContext({count: 0}). Khai báo một defaultValue chỉ hữu dụng trong trường hợp như bên dưới

function CountDisplay() {
    const { count } = React.useContext(CountStateContext)
    return <div>{count}</div>
}

Vì không khai báo giá trị khởi tạo, chúng ta sẽ nhận lỗi khi viết destructure giá trị trả về từ useContext. Vì giá trị mặc định là undefined và chúng ta không thể destructure undefined

Không ai trong chúng ta muốn nhận lỗi do không có giá trị khởi tạo. Tuy nhiên, nếu chỉ sử dụng giá trị mặc định được cung cấp, lợi ích sẽ không nhiều bằng việc linh động giá trị này. Kinh nghiệm thực tế cho thấy khi khởi tạo và sử dụng context trong ứng dụng, chúng ta muốn các consumer context (component sử dụng useContext) có thể cung cấp thêm các giá trị mới.

Trong tài liệu chính thức của React có đề cập, “cung cấp giá trị mặc định có thế giúp việc test component độc lập mà không cần bộc chúng lại (component lồng vào nhau)”. Không cùng quan điểm với ý kiến này, cá nhân tác giả (Kent C. Dodds) cho rằng tốt hơn nên bộc component với các context cần thiết khi test. Hãy nhớ là mỗi lần chúng ta làm gì đó bên trong test, chúng ta không làm những việc như vậy trong ứng dụng, chúng ta giảm bớt sự tự tin mà unit test mang lại.

Lưu ý: nếu đang sử dụng Flow hay TypeScript, không cung cấp giá trị mặc định sẽ bị nhận ngay thẻ “cảnh cáo”, rất phiền toái nếu sử dụng React.useContext. Đọc tiếp phần dưới sẽ chỉ cách khắc phục

Mục đích của CountDispatchContext là để làm gì. Mình đã sử dụng context trong một thời gian, và nói chuyện với một số người đang làm việc với nó, có thể chia sẻ với bạn là đây là cách đơn giản nhất để tránh các rắc rối với context (đặc biệt khi bạn bắt đầu sử dụng dispatch trong effect) khi bạn bắt đầu tách state và dispatch trong context. Hãy tin mình!

Tìm việc làm React lương cao không yêu cầu KN

Provider Component

Để các component bên dưới có thể sử dụng context module, chúng ta phải bộc các component lại về trong cùng một context, sử dụng với Provider Component, cái này thì ai cũng biết

function App() {
  return (
    <CountProvider>
      <CountDisplay />
      <Counter />
    </CountProvider>
  )
}

CountProvider được đưa ra để xài như thế nào cho hiệu quả thì lại ích ai quan tâm, đây là cách mà CountProvider được đưa ra cho thế giới

// src/count-context.js
import React from 'react'
const CountStateContext = React.createContext()
const CountDispatchContext = React.createContext()

function countReducer(state, action) {
  switch (action.type) {
    case 'increment': {
      return {count: state.count + 1}
    }
    case 'decrement': {
      return {count: state.count - 1}
    }
    default: {
      throw new Error(`Unhandled action type: ${action.type}`)
    }
  }
}

function CountProvider({ children }) {
  const [state, dispatch] = React.useReducer(countReducer, {count: 0})
  return (
    <CountStateContext.Provider value={state}>
      <CountDispatchContext.Provider value={dispatch}>
        {children}
      </CountDispatchContext.Provider>
    </CountStateContext.Provider>
  )
}
export {CountProvider}

Consumer Hook

Các thư viện sử dụng context tìm thấy trên mạng đa phần sẽ dùng cách này

import React from 'react'
import { SomethingContext } from 'some-context-package'

function YourComponent() {
  const something = React.useContext(SomethingContext)
}

Để nâng cao trải nghiệm khi chúng ta sử dụng, câu lệnh này React.useContext(SomethingContext) cần phải thay thế. Nếu có thể viết như thế này, sẽ tuyệt vời hơn rất nhiều

import React from 'react'
import { useSomething } from 'some-context-package'

function YourComponent() {
  const something = useSomething()
}

Để có thể dùng useSomething() như bên trên, chúng ta sẽ cần viết lại context như sau

// src/count-context.js
import React from 'react'

const CountStateContext = React.createContext()
const CountDispatchContext = React.createContext()

function countReducer(state, action) {
  switch (action.type) {
    case 'increment': {
      return {count: state.count + 1}
    }
    case 'decrement': {
      return {count: state.count - 1}
    }
    default: {
      throw new Error(`Unhandled action type: ${action.type}`)
    }
  }
}

function CountProvider({children}) {
  const [state, dispatch] = React.useReducer(countReducer, {count: 0})
  return (
    <CountStateContext.Provider value={state}>
      <CountDispatchContext.Provider value={dispatch}>
        {children}
      </CountDispatchContext.Provider>
    </CountStateContext.Provider>
  )
}

function useCountState() {
  const context = React.useContext(CountStateContext)
  if (context === undefined) {
    throw new Error('useCountState must be used within a CountProvider')
  }
  return context
}

function useCountDispatch() {
  const context = React.useContext(CountDispatchContext)
  if (context === undefined) {
    throw new Error('useCountDispatch must be used within a CountProvider')
  }
  return context
}

export {CountProvider, useCountState, useCountDispatch}

Chúng ta tạo useCountState và useCountDispatch hook sử dụng React.useContext để lấy được giá trị context cung cấp từ CountProvider. Tuy nhiên, nếu không có giá trị, chúng ta hiển thị thông báo lỗi để báo hook chỉ được sử dụng trong function component được render bên trong CountProvider. Lỗi này rất hay quên, nên nhắc trước cho tốt.

Nếu cần sử dụng với React < 16.8.0, chúng ta dùng render-prop với Consumer Component như sau

function CountConsumer({children}) {
  return (
    <CountStateContext.Consumer>
      {context => {
        if (context === undefined) {
          throw new Error('CountConsumer must be used within a CountProvider')
        }
        return children(context)
      }}
    </CountStateContext.Consumer>
  )
}

Nếu bạn đã dùng React cũ, hoặc là nâng cấp mới nhất, hoặc giữ nguyên tình trạng hiện tại cho an toàn?

TypeScript/Flow

Như đã hứa ở trên, với vấn đề defaultValue khi sử dụng TypeScript và Flow. Giải quyết như sau

// src/count-context.tsx
import * as React from 'react'

type Action = {type: 'increment'} | {type: 'decrement'}
type Dispatch = (action: Action) => void
type State = {count: number}
type CountProviderProps = {children: React.ReactNode}

const CountStateContext = React.createContext<State| undefined>(undefined)
const CountDispatchContext = React.createContext(<Dispatch | undefined>(
undefined,
)

function countReducer(state: State, action: Action) {
  switch (action.type) {
    case 'increment': {
      return {count: state.count + 1}
    }
    case 'decrement': {
      return {count: state.count - 1}
    }
    default: {
      throw new Error(`Unhandled action type: ${action.type}`)
    }
  }
}

function CountProvider({children}: CountProviderProps) {
  const [state, dispatch] = React.useReducer(countReducer, {count: 0})
  
  return (
    <CountStateContext.Provider value={state}>
      <CountDispatchContext.Provider value={dispatch}>
        {children}
      </CountDispatchContext.Provider>
    </CountStateContext.Provider>
  )
}

function useCountState() {
  const context = React.useContext(CountStateContext)
  if (context === undefined) {
    throw new Error('useCountState must be used within a CountProvider')
  }
  return context
}

function useCountDispatch() {
  const context = React.useContext(CountDispatchContext)
  if (context === undefined) {
    throw new Error('useCountDispatch must be used within a CountProvider')
  }
  return context
}

export {CountProvider, useCountState, useCountDispatch}

 

Với cách viết này, ai cũng có thể sử dụng useCountState hoặc useCountDispatch mà không cần kiểm tra undefined

Vậy còn dispatch type?

Nếu bạn từng viết Redux, sẽ thắc mắc “vậy action creator đâu?”. Nếu thích bạn có thể tự viết nếu muốn. Nhưng mình không phải là fan của action creator (cô thư ký xinh đẹp trong bài giải thích về Flux Pattern của mình. Mình luôn cảm thấy em này khá dư thừa, không cần thiết phải phức tạp thêm một tổ chức đã quá phức tạp như Flux (hoàn hảo không phải là không còn gì để thêm nữa mà là ko còn gì có thể bỏ đi mà). Nếu sử dụng TypeScript hoặc Flow, nó sẽ giúp chúng ta tự điền action type có thể điền

Mình thích kiểu gọi dispatch như thế này, nếu để ý bạn sẽ biết dispatch sẽ không thay đổi trong suốt quá trình tồn tại của component, nghĩa là bạn có thể truyền nó vào mảng phụ thuộc của useEffect vô tư.

Vậy còn các async action thì sao ?

Một câu hỏi hay, bạn gặp tình huống cần xử lý là một async (thao tác bất đồng bộ như network request) và bạn cần dispatch nhiều action cùng lúc, dispatch một action nào đó phụ thuộc vào kết quả từ dispatch trước đó? Bạn có thể làm điều đó trong component, tuy nhiên xử lý thủ công như thế trên từng component rất là phiền.

Mình đề nghị tạo một helper function trong context module, nhận dispatch và tất cả những dữ liệu bạn cần, helper function (AKA middleware) này sẽ chịu trách nhiệm xử lý tất cả những việc đã nêu trên (theo kiểu state machine)

// user-context.js
async function updateUser(dispatch, user, updates) {
  dispatch({type: 'start update', updates})
  try {
    const updatedUser = await userClient.updateUser(user, updates)
    dispatch({type: 'finish update', updatedUser})
  } catch (error) {
    dispatch({type: 'fail update', error})
  }
}

export {UserProvider, useUserDispatch, useUserState, updateUser}

Sử dụng nó sẽ như thế này

// user-profile.js
import {useUserState, useUserDispatch, updateUser} from './user-context'

function UserSettings() {
  const {user, status, error} = useUserState()
  const userDispatch = useUserDispatch()
  
  function handleSubmit(event) {
    event.preventDefault()
    updateUser(userDispatch, user, formState)
  }
  //...
}

Cảm thấy tách state và dispatch rất khó chịu

Nhiều người phàn nàn tách state và dispatch riêng rất khó chịu

const state = useCountState()
const dispatch = useCountDispatch()

Tại sao không đơn giản là làm thế này

const [state, dispatch] = useCount()

Tất nhiên bạn có thể

function useCount() {
  return [useCountState(), useCountDispatch()]
}

Cái này tùy cách nhìn nhận của từng người viết, không có chuyện ai đúng ai sai, chỉ là bạn cảm thấy thoải máivui hơn với cách viết nào thôi.

Toàn bộ source code

// src/count-context.js
import React from 'react'
const CountStateContext = React.createContext()
const CountDispatchContext = React.createContext()

function countReducer(state, action) {
  switch (action.type) {
    case 'increment': {
      return {count: state.count + 1}
    }
    case 'decrement': {
      return {count: state.count - 1}
    }
    default: {
      throw new Error(`Unhandled action type: ${action.type}`)
    }
  }
}

function CountProvider({children}) {
  const [state, dispatch] = React.useReducer(countReducer, {count: 0})
  return (
    <CountStateContext.Provider value={state}>
      <CountDispatchContext.Provider value={dispatch}>
        {children}
      </CountDispatchContext.Provider>
    </CountStateContext.Provider>
  )
}

function useCountState() {
  const context = React.useContext(CountStateContext)
  if (context === undefined) {
    throw new Error('useCountState must be used within a CountProvider')
  }
  return context
}

function useCountDispatch() {
  const context = React.useContext(CountDispatchContext)
  if (context === undefined) {
    throw new Error('useCountDispatch must be used within a CountProvider')
  }
  return context
}

export {CountProvider, useCountState, useCountDispatch}

Ở đây, mình đang cố tính không export CountContext, chúng ta chỉ cung cấp một cách để cung cấp giá trị trong context và một cách để lấy về giá trị này. Việc này đảm bảo người sử dụng giá trị context đúng theo cái cách chúng ta muốn, cho phép chúng ta hạn chế một số code không cần thiết khi lúc nào cũng phải khai báo useContext nào.

Hy vọng bài viết mang đến nhiều điều hữu ích cho bạn.

Tâm niệm rằng:

  • Không nên đặt trọn niềm tin 100% vào context có thể giải quyết tất cả vấn đề liên quan đến chia sẻ state
  • Context không nhất thiết là một global state cho toàn bộ ứng dụng, nó có thể được áp dụng trên một phần của cây component cụ thể nào đó.
  • Bạn có thể (và bạn nên) chia các logic khác nhau trên các context khác nhau.

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

Tham khảo thêm vị trí tìm việc lương cao ngành cntt tại Topdev

Công Bố Top Thí Sinh Thắng Giải Và Đáp Án Của Quiz 02 – Kambria Code Challenge

Quiz 02 – Kambria Code Challenge đã diễn ra thành công, mời bạn cùng TopDev tổng kết những điểm đáng chú ý của cuộc thi.

  • Quiz 02 thu hút 255 bạn đăng ký tham gia
  • Cuộc thi được tổ chức vào thứ 7, ngày 29/2/2020
  • Các thí sinh đã trả lời 15 câu trắc nghiệm và câu hỏi ngắn
  • Chủ đề tập trung vào mô hình Convolutional Neural Network
  • Không có thí sinh nào đạt điểm tuyệt đối trong Quiz 02, nhưng chất lượng dự thi của các bạn thí sinh ấn tượng hơn so với Quiz 01, trong đó bạn cao nhất đạt 9.75 điểm.
  • Các thí sinh lọt vào top 10 đã được đưa lên Leaderboard của cuộc thi
  • Top 10 bạn này sẽ có cơ hội được kết nối với các đối tác doanh nghiệp của Kambria, cung cấp các cơ hội nghề nghiệp trong thời gian tới.
  • Tổng giải thưởng của cuộc thi là $350 đã được trao cho 4 thí sinh có điểm số cao nhất!

Sau đây là danh sách những bạn chiến thắng Quiz 02:

🏆Quán quân: Le Dung

🥇Á quân 1: Luu Quan

🥈Á quân 2: Doan Truc

🥈Á quân 2: Nguyen Phu

Nếu bạn bỏ lỡ cơ hội tham gia Quiz 02 vừa rồi, Kambria có tin vui dành cho các bạn. Bạn có thể giải lại các câu hỏi của Quiz 02. Hãy đọc và trả lời thật kĩ các câu hỏi, chúng sẽ là những gợi ý rất tốt cho bạn để chuẩn bị cho các cuộc thi sắp tới của Kambria Code Challenge.🔥

Để bắt đầu, bạn chỉ cần nhấn vào đường link bên dưới, tạo tài khoản Kambria sau đó chọn “Practice Quiz 02“. Sau khi hoàn thành Quiz, bạn hãy xem lại các câu trả lời của mình và so sánh chúng với đáp án từ Kambria. Chúng sẽ giúp ích cho bạn rất nhiều để nâng cao kiến thức về Convolutional Neural Networks đấy!

Truy cập và giải lại Quiz 02 tại đây: http://bit.ly/KambriaQuiz02

Một lần nữa cám ơn các bạn đã tham gia #KambriaCodeChallenge

Kambria rất muốn nhận ý kiến đóng góp của bạn cho chương trình, bạn vui lòng giúp chúng tôi thực hiện khảo sát sau, khảo sát này sẽ giúp team cải thiện chất lượng chương trình trong các cuộc thi kế tiếp.

Bản khảo sát: http://bit.ly/KambriaQuiz02-Survey

Kambria đang chuẩn bị cho 1 thử thách Bounty mới vào tháng 3/2020, do đó Quiz 03 sẽ được diễn ra vào tháng 4/2020.

Hẹn gặp lại các bạn trong những sự kiện tiếp theo của Kambria!

Hiểu về setTimeout và setInterval trong Javascript

Nếu bạn cần gọi một hàm lặp lại theo một khoản thời gian nhất định trong javascript bạn sẽ dùng gì? Một là dùng setInterval hay là đệ quy setTimeout

Vì sao bạn nên cân nhắc trước khi sử dụng setInterval, nó đã gây ra tội tình gì? Vì sao sẽ tốt hơn nếu chúng ta lắng nghe và đợi một tín hiệu nào đó rồi chạy

window.onload = () => {
  // đợi tính hiệu nào đó rồi thực thi một số việc 
  // sẽ luôn là lựa chọn tốt nhất
};

Nếu ông bà có dạy đợi mua bò mới đi làm chuồng thì đã muộn không đúng trong trường hợp này.  trước hẳn làm gì thì làm.

setInterval

Với setInterval nó sẽ tiếp tục chạy cho tới khi chúng ta ra lệnh xóa nó hoặc đóng luôn trình duyệt.

setInterval cam kết đoạn code của chúng ta nó sẽ được đưa vào STACK theo đúng một chu kỳ thời gian. Tuy nhiên, đoạn code này của bạn không được cam kết sẽ chạy theo đúng chu kỳ thời gian, phụ thuộc vào các yếu tố khác nữa, và đã phần là có độ trễ, theo một cách dân gian ta gọi nó là HÊN XUI

Thời gian chạy của hàm dummyMethod1 tốn nhiều thời gian hơn dự tính, lý do thì không rõ.

Nếu bạn chưa biết, javascript được thiết kế để chạy single thread, nghĩa là nó không thực hiện hai công việc cùng một lúc.

Điều đó có nghĩa, các phương thức khác phía trên stack phải đợi cho đến khi dummyMethod1 làm xong công chuyện của nó.

Tuyển dụng Javascript lương cao, xem ngay!

Thêm một ví dụ khác, nếu hàm khai báo bên trong setInterval có thời gian chạy lớn hơn giá trị delay của setInterval (ví dụ như hàm gọi ajax), chúng ta sẽ có vấn đề như thế này

var fakeCallToServer = function() {
  setTimeout(function() {
    console.log('returning from server', new Date().toLocaleTimeString());
  }, 4000);
}

setInterval(function(){
 let insideSetInterval = new Date().toLocaleTimeString();
 console.log('insideSetInterval', insideSetInterval);
 fakeCallToServer();
}, 2000);
//insideSetInterval 14:13:47
//insideSetInterval 14:13:49
//insideSetInterval 14:13:51
//returning from server 14:13:51
//insideSetInterval 14:13:53
//returning from server 14:13:53 
//insideSetInterval 14:13:55
//returning from server 14:13:55

Như kết quả ở trên cho thấy, câu console.log("insideSetInterval") sẽ liên tục gọi ajax bất kể trước đó đã gọi thành công chưa. Đáng lẽ chúng ta phải kết thúc việc gọi liên tục này, đa phần chúng ta quên clearInterval. Nó sẽ tạo ra một hàng đợi dài ngoằn trong stack.

Giờ thử một xử lý tuần tự trong setInterval

var counter = 0;

var fakeTimeIntensiveOperation = function() {

    for (var i =0; i< 50000000; i++) {
        document.getElementById('random');
    }

    let insideTimeTakingFunction  = new Date().toLocaleTimeString();

    console.log('insideTimeTakingFunction', insideTimeTakingFunction);
}

var timer = setInterval(function(){ 

    let insideSetInterval = new Date().toLocaleTimeString();

    console.log('insideSetInterval', insideSetInterval);

    counter++;
    if(counter == 1){
        fakeTimeIntensiveOperation();
    }

    if (counter >= 5) {
       clearInterval(timer);
    }
}, 1000);

//insideSetInterval 13:50:53
//insideTimeTakingFunction 13:50:55
//insideSetInterval 13:50:55 <---- mất tiêu câu gọi lúc 54 giây
//insideSetInterval 13:50:56
//insideSetInterval 13:50:57
//insideSetInterval 13:50:58

Những gì đang diễn ra ở trên, với một thao tác tốn kha khá thời gian xử lý, nó mất hẳn đoạn code console.log("insideSetInterval"), nôm na là nó bị đứt một nhịp, tình huống này xảy ra với Chrome, nó tạo ra một nhịp mới.

Thay vì dùng setInterval, chúng ta có thể dùng đệ quy setTimeout

>>> Xem thêm: 15 ví dụ sử dụng map, reduce và filter

setTimeout

Mặc dù cũng chưa hẳn cam kết 100% đoạn code của chúng chạy đúng theo một chu kỳ đã định với đệ quy setTimeout. Chí ít nó cũng không gây ra chuyện đưa hàng đống lệnh chờ chạy vào trong stack

Khi thực hiện bằng setTimeout, bên trong vòng đệ quy chúng ta đã có bước kiểm tra có nên gọi thêm lần nữa không.

Lưu ý khi bạn dùng setTimeout, chớ có thực thi hàm đó luôn (kèm dấu ()), chúng ta chỉ truyền hàm đó thôi

Kiểu enum trong TypeScript: làm việc như thế nào, sử dụng ra sao

Kiểu enum trong TypeScript: làm việc như thế nào, sử dụng ra sao
Chúng ta sẽ trả lời 2 câu hỏi sau: enum của TypeScript làm việc như thế nào? Nó được sử dụng để làm gì. Vỡ lòng cho người mới viết TypeScript

Các khái niệm căn bản của enum

Javascript chỉ có đúng một kiểu mà giá trị bị ràng buộc rất cụ thểboolean, giá trị chỉ được phép là true hoặc false, không chấp nhận một giá trị nào khác. Enum là phiên bản mở rộng với công dụng tương tự như boolean

enum NoYes {
    No,
    Yes
}

2 giá trị No Yes được gọi là thành viên của hội enum NoYes. Dùng nó như một object, chúng ta có thể chấm đến giá trị đó

function toGerman(value: NoYes) {
  switch (value) {
    case NoYes.No:
      return 'Nein';
    case NoYes.Yes:
      return 'Ja';
  }
}

Tất cả thành viên của hội enum đều được cấp một thẻ thành viên gồm name và value. Ở trên chúng ta chỉ đang khai báo phần name cho enum, value lúc đó sẽ lấy mặc định là số từ 0 đi lên

enum NoYes {
  No,
  Yes,
}
// nếu khai báo một cách tường minh hơn
enum NoYes {
  No = 0,
  Yes = 1,
}

assert.equal(NoYes.No, 0);
assert.equal(NoYes.Yes, 1);

Nếu cà khịa, khai báo như thế này

enum Enum {
  A,
  B,
  C = 4,
  D,
  E = 8,
  F,
}

Đồng nghĩa là các giá trị kế tiếp sẽ tự động tăng lên một, so với giá trị khai báo trước đó

assert.deepEqual(
  [Enum.A, Enum.B, Enum.C, Enum.D, Enum.E, Enum.F],
  [0, 1, 4, 5, 8, 9]
);

Về cách đặt tên (name) trong enum, cũng có vài ba lựa chọn

  • Đặt theo kiểu JavaScript, viết hoa hết, Number,MAX_VALUE
  • Đặt theo kiểu symbol, con lạc đà, chữ đầu viết thường, Symbol.asyncIterator
  • Kiểu TypeScript, con lạc đà, chữ đầu viết hoa, Number.MaxValue

Tương tự như object, chúng ta có thể truy xuất đến một thành viên của hội bằng cách viết sau

enum HttpRequestField {
  'Accept',
  'Accept-Charset',
  'Accept-Datetime',
  'Accept-Encoding',
  'Accept-Language',
}
assert.equal(HttpRequestField['Accept-Charset'], 1);

Giá trị value của enum, không bắt buộc là một number, có thể là một string

enum NoYes {
  No = 'No',
  Yes = 'Yes',
}
assert.equal(NoYes.No, 'No');
assert.equal(NoYes.Yes, 'Yes');

Còn một cách khai báo, ít được sử dụng, là kiểu xăng pha nhớt, các giá trị trong enum có thể là số cũng có thể là chữ

enum Enum {
  A,
  B,
  C = 'C',
  D = 'D',
  E = 8,
  F,
}
assert.deepEqual(
  [Enum.A, Enum.B, Enum.C, Enum.D, Enum.E, Enum.F],
  [0, 1, 'C', 'D', 8, 9]
);

Theo như kinh nghiệm từ các bật tiền bối, sử dụng enum thì nên dùng kiểu giá trị string

enum NoYes { No = 'NO', Yes = 'YES' }

Nếu có log ra chúng ta cũng biết được giá trị chính xác là gì, đỡ hack não ngồi đếm số thứ tự

console.log(NoYes.No);
console.log(NoYes.Yes);

Thêm nữa, ràng buộc được luôn kiểu giá trị

function func(noYes: NoYes) {}

func('abc');
func('Yes');

Trường hợp sử dụng enum

hằng số nhiều giá trị

Nếu chúng ta có một mớ các hằng số, có quan hệ họ hàng gần với nhau

const fatal = Symbol('fatal');
const error = Symbol('error');
const warn = Symbol('warn');
const info = Symbol('info');
const debug = Symbol('debug');
const trace = Symbol('trace');

Có thể dùng enum

enum LogLevel {
  fatal = 'fatal',
  error = 'error',
  warn = 'warn',
  info = 'info',
  debug = 'debug',
  trace = 'trace',
}

Lợi ích mang lại: nhóm lại với nhau một cục, TypeScript dễ đàng kiểm tra giúp chúng ta

Tường minh hơn boolean

Chúng ta hay dùng boolean để làm cờ bật tắt hoặc đảo ngược giá trị

class List1 { isOrdered: boolean; }

Nếu dùng enum, nó sẽ rõ nghĩa hơn, nhiều lựa chọn hơn

enum ListKind { ordered, unordered }
class List2 {
  listKind: ListKind;
  
}

String là một hằng số đúng nghĩa, an toàn hơn vì không thể thay đổi được giá trị

Ví dụ hàm bên dưới dùng const

const GLOBAL = 'g';
const NOT_GLOBAL = '';
type Globalness = typeof GLOBAL | typeof NOT_GLOBAL;

function createRegExp(source: string,
  globalness: Globalness = NOT_GLOBAL) {
    return new RegExp(source, 'u' + globalness);
  }

assert.deepEqual(
  createRegExp('abc', GLOBAL),
  /abc/ug);

dùng enum tiện hơn

enum Globalness {
  GLOBAL = 'g',
  NOT_GLOBAL = '',
}

function createRegExp(source: string, globalness = Globalness.NOT_GLOBAL) {
  return new RegExp(source, 'u' + globalness);
}

assert.deepEqual(
  createRegExp('abc', Globalness.GLOBAL),
  /abc/ug);

Enum lúc chạy thì sẽ trở thành gì?

Sau khi TypeScript đã compile, enum sẽ được được chuyển thành javascript object

enum NoYes {
  No,
  Yes,
}
var NoYes;
(function (NoYes) {
  NoYes[NoYes["No"] = 0] = "No";
  NoYes[NoYes["Yes"] = 1] = "Yes";
})(NoYes || (NoYes = {}));

THam khảo các vị trí tuyển lập trình Typescript cho bạn.

TopDev via Vuilaptrinh

Gặp gỡ Sakshi Jawa — Tổng giám đốc nhân sự Tiki

Bài viết được đăng tải lần đầu trên Vietcetera

Là một trong những quốc gia có tốc độ tăng trưởng nhanh nhất châu Á, Việt Nam hiện là vùng đất hứa cho các doanh nghiệp trong và ngoài nước, đặc biệt là các doanh nghiệp trong lĩnh vực thương mại điện tử. Với dân số trẻ và tỉ lệ sử dụng điện thoại thông minh cao, thị trường thương mại điện tử tại Việt Nam là một trong những thị trường phát triển nhanh nhất nhì khu vực.

Thành lập từ tháng 3/2010, Tiki là hệ sinh thái thương mại tất cả trong một tại Việt Nam. Với đội ngũ gồm hơn 4.500 nhân viên, không chỉ là môi trường làm việc vui vẻ, sáng tạo và năng động, Tiki còn mang đến cho nhân viên của mình những cơ hội để phát triển bản thân lẫn thăng tiến trong sự nghiệp.

Và người đứng đằng sau sự phát triển này là Sakshi Jawa, hiện là Tổng giám đốc nhân sự tại Tiki. Gia nhập Tiki cách đây hơn một năm trước, Sakshi Jawa đã có rất nhiều đóng góp trong việc thay đổi các chính sách về nhân sự và con người.

Với nhiều năm kinh nghiệm làm việc cho các thương hiệu toàn cầu như Citibank, Prudential, Amazon và Coupang, động lực nào đã khiến Sakshi chuyển đến Việt Nam và gia nhập Tiki? Thế mạnh nào trong phong cách quản lý của chị đã giúp góp phần giúp Tiki phát triển thành một doanh nghiệp lớn mạnh và năng động như hiện nay?

Hãy ngồi xuống cùng Sakshi và nghe những câu chuyện của chị về phong cách quản lý cũng như văn hoá và bản sắc của Tiki.

tiki 4Sakshi Jawa đã có rất nhiều đóng góp trong việc thay đổi các chính sách về nhân sự và con người.

Điều gì khiến chị quyết định gia nhập Tiki?

Cách đây 10 năm tôi từng sống và làm việc tại Việt Nam, và cảm thấy vô cùng thích trải nghiệm đó. Việt Nam là một nước đang phát triển và thị trường công nghệ ở đây có rất nhiều tiềm năng để lớn mạnh mỗi ngày. Với tốc độ phát triển nhanh chóng trong lĩnh vực công nghệ, Việt Nam đang ngày càng vươn lên chứng tỏ tiềm năng trở thành trung tâm công nghệ và cải tiến. Tôi cực kỳ ấn tượng với sức bật mạnh mẽ này.

Chuyên môn của tôi là quản lý nhân sự, đặc biệt là nhân sự trong ngành công nghệ/thương mại điện tử. Với tôi, để trở thành một người quản lý nhân sự giỏi, chúng ta cũng cần trang bị cho mình kiến thức chuyên sâu về lĩnh vực mà mình đang hoạt động. Lời mời từ Tiki là một lựa chọn lý tưởng, cùng với việc được làm trong một thị trường bùng nổ là một yếu tố quan trọng dẫn đến quyết định của tôi. Tiki đã cho tôi cả hai, nên tôi quyết định tham gia vào đội ngũ ở đây.

Phong cách quản lý của chị là gì? Chị điều hành đội ngũ của mình như thế nào?

Tôi không nghĩ quản lý vi mô là một cách hiệu quả. Tôi không kiểm tra nhân viên của mình mỗi ngày. Phong cách quản lý nhân sự của tôi chú trọng về mặt chất lượng. Mỗi thành viên trong đội ngũ đều có mục tiêu riêng, được đặt ra vào đầu năm. Ở một môi trường làm việc năng động và thay đổi nhanh chóng như Tiki, những mục tiêu này có thể thay đổi tùy thuộc vào sự phát triển của doanh nghiệp trong năm đó. Những lúc như vậy, tôi yêu cầu họ báo cáo tiến độ của họ và cùng thảo luận về hướng đi sắp tới. Mỗi tháng một lần, tôi làm thế này với chính mình cũng như các cá nhân báo cáo trực tiếp cho mình. Khi tuyển dụng đội ngũ làm công tác nhân sự, tôi tin tưởng họ có khả năng và kinh nghiệm để hoàn thành những mục tiêu họ được giao, vì thế, tôi không trực tiếp tham gia vào công việc hằng ngày của họ, trừ khi họ tìm đến tôi để hỏi ý kiến.

Mặc dù tôi đánh giá cao các kinh nghiệm làm việc trước đây của nhân viên, điều tôi quan tâm là liệu họ hơn cả là liệu họ có khả năng để đạt được mục tiêu chung hay không. Tôi đang cố gắng xây dựng một đội ngũ có thể hoàn thành công viên một cách chủ động, chứ không phải là giám sát giờ vào làm, giờ tan ca. Chúng tôi vẫn đang từng bước một xây dựng một văn hoá làm việc như thế.

Hơn nữa, tôi là một người ra quyết định dựa trên những con số, những cơ sở dữ liệu thích hợp chứ không theo cảm tính hay những lý do chủ quan. Vì thế, tôi hy vọng đội ngũ của mình cũng đưa ra quyết định theo cách tương tự.

Chị có thể miêu tả công việc của mình ở Tiki được không?

Tiki hiện sở hữu một trong những nền tảng thương mại đáng tin cậy nhất Việt Nam. Với tốc độ mở rộng nhanh chóng của doanh nghiệp, trách nhiệm của người làm nhân sự là kiểm soát quy mô nhân sự, cả trong quá trình tuyển dụng, sự gắn kết của nhân viên cho đến quy trình tổ chức nhân sự.

Thêm một nhân viên là thêm một công quản lý, thế nên với tốc độ phát triển quy mô doanh nghiệp như tại Tiki, nhân viên nhân sự cần phải tìm những phương pháp mới để quản lý theo quy mô lớn sao cho hiệu quả. Tiki từng quản lý dữ liệu trên một chuỗi bảng tính rườm rà được nhập tay, nên thường dễ xảy ra lỗi thủ công. Sau này, các giải pháp công nghệ lên ngôi, giúp công việc hàng ngày diễn ra hiệu quả hơn, giảm được các sai sót thủ công, đơn giản hoá các tính toán và thiết lập các dự đoán.

Chị sẽ mô tả môi trường làm việc tại Tiki như thế nào?

Đội ngũ Tiki nhìn chung khá trẻ, độ tuổi trung bình vào tầm 27-28 tuổi. Chúng tôi muốn tạo ra một môi trường lành mạnh, nơi mọi người tìm thấy cảm hứng và động lực để làm việc, góp phần vào sự ổn định và thành công lâu dài của cá nhân cũng như tập thể công ty.

Một trong những thế mạnh lớn nhất của tổ chức chúng tôi là khả năng phối hợp trong công việc. Những người làm việc nhóm tốt có xu hướng phát triển trong một mô hình văn phòng mở, nơi mà mọi người cùng cống hiến cho một mục tiêu chung.

Tiki khuyến khích cách làm việc cộng tác, tạo ra một môi trường hoà đồng và thân thuộc giữa các nhân viên.

Chị tìm kiếm những phẩm chất gì ở một người lãnh đạo giỏi?

Không chỉ riêng Tiki mà khi tuyển dụng hoặc tư vấn tuyển dụng cấp lãnh đạo cho các công ty đang phát triển nhanh nói chung, câu hỏi đặt ra là: “Thử thách của chúng ta trong việc tìm kiếm một người lãnh đạo là gì?” và “Nếu tuyển không đúng người, bài học của chúng ta là gì?”

Người lãnh đạo là một người biết “chỉ đạo” và dẫn dắt các cá nhân, đội nhóm hay thậm chí là toàn thể tổ chức. Chính vì họ được xem như một hình mẫu, chúng tôi muốn đảm bảo rằng mình chọn đúng người để đảm nhận vai trò này. Việc này quan trọng cho cả bản thân họ cả sự thành công của công ty.

Phẩm chất đầu tiên mà tôi tìm kiếm ở một người lãnh đạo giỏi là khả năng thích nghi. Trong giai đoạn tăng trưởng liên tục, người đứng đầu phải đối mặt với những thay đổi không ngừng và ngày một phức tạp hơn. Việc này đòi hỏi họ phải có khả năng thích ứng và điều phối tuỳ theo tình huống. Thay đổi, đối với họ, không phải là chướng ngại vật mà là cơ hội tiềm năng để phát triển. Đồng thời, việc lãnh đạo còn yêu cầu kỹ năng đưa ra các quyết định quan trọng. Chúng tôi không muốn thấy một người lãnh đạo tỏ ra mơ hồ, thiếu quyết đoán, ngay cả trong các tình huống vô định. Ngược lại, họ cần làm chủ mọi quyết định của mình, và dẫn dắt đồng đội của mình theo lựa chọn đó.

Thứ hai, họ ý thức và chấp nhận những thất bại của mình. Người lãnh đạo không phải lúc nào cũng đúng. Họ cũng mắc sai lầm như bao người — nhưng thường những sai lầm đó để lại hậu quả lớn hơn. Bất kỳ người lãnh đạo tuyệt vời nào cũng sẽ nói rằng họ đã từng mắc sai lầm trong quá khứ. Những người đứng đầu giỏi sẽ không ngần ngại kể về những thất bại của mình, cũng như những bài học họ rút ra. Hơn nữa, họ cần hiểu rằng mình không thể giỏi toàn diện, có những việc họ cần phải trông cậy vào người khác. Biết thừa nhận những hạn chế của mình và tìm những người có nhiều chuyên môn hơn có lẽ là bí kíp hàng đầu của những người lãnh đạo giỏi.

Cuối cùng, sự khiêm nhường và khả năng tiếp thu là những phẩm chất tôi muốn thấy ở một người đứng đầu. Họ cần liên tục trau dồi kiến thức và kỹ năng của bản thân. Học hỏi là một việc thú vị và nó giúp ta luôn khiêm tốn. Mọi người đánh giá cao việc tôi thích học hỏi từ họ và từ môi trường xung quanh. Nó giúp tôi nhận ra rằng, nhờ việc bày tỏ nguyện vọng được học của mình với người khác, tôi cũng khiến họ cảm thấy quan trọng hơn.

Với số lượng nhân viên tại Tiki hiện giờ, làm thế nào để chị đảm bảo mỗi cá nhân đều đang thấy hạnh phúc và hài lòng với công việc của mình?

Khi đề cập đến sự gắn bó của nhân viên, chúng ta thường nghĩ đến những hoạt động như tiệc tùng hoặc liên hoan. Nhưng xét cho cùng, khi nhân viên rời đi, họ không rời đi vì công ty bạn ít tiệc tùng hơn. Không ai chọn công tác ở một nơi chỉ vì ở đó tiệc tùng nhiều hơn những nơi khác. Đó không phải là yếu tố đo lường mức độ gắn kết với nhân viên.

Thay vào đó, nên đánh giá độ gắn bó của nhân viên qua những lần họ được công nhận, những lần họ đóng góp vào các dự án và những lần họ được giao mục tiêu. Tại Tiki, chúng tôi có một hệ thống quản lý nhân viên chặt chẽ, giúp cả quản lý và nhân viên có thể thảo luận và đánh giá hiệu suất công việc. Chúng tôi còn có chính sách luân chuyển công việc để giúp nhân viên trau dồi các kỹ năng và kinh nghiệm mới ở những khía cạnh khác nhau. Năm nay, chúng tôi tập trung vào các buổi học nghiệp vụ cũng như các kế hoạch đào tạo và phát triển cho nhân viên, giúp họ nâng cao chuyên môn của mình.

Chúng tôi cũng đang nỗ lực hơn trong việc giao tiếp với nhân viên của mình. Với tốc độ phát triển và cải tiến của công ty, chúng tôi muốn đảm bảo sự đồng thuận của mọi người. Tôi nghĩ giao tiếp chính là chìa khóa dẫn tới thành công cho bất kỳ tổ chức nào.

Tiki - SakshiTiki là một trong những nền tảng thương mại điện tử có tốc độ nhanh và uy tín nhất Việt Nam.

Khó khăn lớn nhất trong công việc của chị là gì?

Thử thách xuất hiện mỗi ngày. Điều bạn làm tốt hôm qua chưa chắc vẫn tốt hôm nay. Một chuyên viên nhân sự cần có những đường hướng phù hợp để bổ trợ chiến lược phát triển của công ty. Đôi lúc, sẽ rất khó để vận hành ở cùng một tốc độ. Đôi lúc trở ngại tiếp theo sẽ ập đến bất ngờ. Việc liên tục chuyển mình và đồng thời dẫn dắt cả đội, đôi khi là cả một thách thức.

Lời khuyên của chị dành cho những người chuẩn bị nắm vai trò quản lý hoặc chủ chốt trong công ty là gì?

Dành cho bất kỳ ai đang làm việc tại công ty công nghệ, không chỉ riêng Tiki mà ở bất kỳ đâu trong khu vực Đông Nam Á, hãy nhớ lấy ba điều: hãy sẵn sàng với những điều mơ hồ phía trước, luôn luôn tìm đến những người có thể bù trừ cho điểm yếu của bạn, những người giỏi hơn bạn, và hãy luôn tin rằng vẫn còn rất nhiều kiến thức mà ta cần trau dồi.

Tham khảo thêm các vị trí:

Chị nghĩ xu hướng tuyển dụng của các công ty công nghệ vào năm 2020 là gì?

Có thể ví Việt Nam như “Thung lũng Silicon” của Đông Nam Á với tốc độ tăng trưởng từng ngày của thị trường công nghệ tại đây. Tuy có rất nhiều tài năng tìm đến thị trường đang lớn mạnh này, tôi vẫn thấy rất nhiều người chọn Canada, Nhật Bản hay Đức. Vấn nạn “chảy máu chất xám” đang trở nên nghiêm trọng hơn. Tuy nhiên, tôi chú ý thấy nhiều doanh nghiệp điện tử nước ngoài đang dần dà công nhận nguồn lực tài năng tại Việt Nam và bắt đầu mở văn phòng tại đây. Bản thân Việt Nam cũng đang mở nhiều cơ hội về công nghệ để thúc đẩy các doanh nghiệp cũng như khuyến khích khởi nghiệp. Điều này đồng nghĩa với việc tăng cơ hội việc làm và tăng sự cạnh tranh giữa các tài năng.

Hơn nữa, hiện tại chúng tôi đã có thêm các công cụ tuyển dụng hiệu quả hơn. Ví dụ, nếu muốn chiêu mộ 100-200 người cùng lúc, chúng tôi sẽ không thể chỉ dùng sức người. Chúng tôi cần những công cụ tuyển lọc tự động, giúp đẩy nhanh quá trình như trí tuệ thông minh nhân tạo (AI) và các phần mềm tuyển dụng khác.

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

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

TopDev via Vietcetera

Docker là gì? Kiến thức cơ bản về Docker

Docker là gì? Kiến thức cơ bản về Docker

Tác giả: Quân Phạm

Tại sao phải dùng Docker?

Việc setup và deploy application lên một hoặc nhiều server rất vất vả từ việc phải cài đặt các công cụ, môi trường cần cho application đến việc chạy được ứng dụng chưa kể việc không đồng nhất giữa các môi trường trên nhiều server khác nhau. Chính vì lý do đó Docker được ra đời để giải quyết vấn đề này.

Docker là gì?

Docker là một nền tảng cho developers và system admin để develop, deploy và run application với container. Nó cho phép tạo các môi trường độc lập và tách biệt để khởi chạy và phát triển ứng dụng và môi trường này được gọi là container. Khi cần deploy lên bất kỳ server nào chỉ cần run container của Docker thì application của bạn sẽ được khởi chạy ngay lập tức.

Lợi ích của Docker

  • Không như máy ảo Docker start và stop chỉ trong vài giây.
  • Bạn có thể khởi chạy container trên mỗi hệ thống mà bạn muốn.
  • Container có thể build và loại bỏ nhanh hơn máy ảo.
  • Dễ dàng thiết lập môi trường làm việc. Chỉ cần config 1 lần duy nhất và không bao giờ phải cài đặt lại các dependencies. Nếu bạn thay đổi máy hoặc có người mới tham gia vào project thì bạn chỉ cần lấy config đó và đưa cho họ.
  • Nó giữ cho word-space của bạn sạch sẽ hơn khi bạn xóa môi trường mà ảnh hưởng đến các phần khác.

Cài đặt

Link download: tại đây

Chọn bản cài đặt tương ứng với hệ điều hành của bạn và tiến hành cài đặt theo hướng dẫn đối với Linux còn Windows và MacOS thì bạn chỉ cần tải bản cài về và cài đặt như mọi application khác.

Sau khi cài đặt xong để kiểm tra xem cài đặt thành công hay không ?

  • Mở command line:
$ docker version$ docker info$ docker run hello-world

Một số khái niệm

Docker là gì? Kiến thức cơ bản về Docker

  • Docker Client: là cách mà bạn tương tác với docker thông qua command trong terminal. Docker Client sẽ sử dụng API gửi lệnh tới Docker Daemon.
  • Docker Daemon: là server Docker cho yêu cầu từ Docker API. Nó quản lý images, containers, networks và volume.
  • Docker Volumes: là cách tốt nhất để lưu trữ dữ liệu liên tục cho việc sử dụng và tạo apps.
  • Docker Registry: là nơi lưu trữ riêng của Docker Images. Images được push vào registry và client sẽ pull images từ registry. Có thể sử dụng registry của riêng bạn hoặc registry của nhà cung cấp như : AWS, Google Cloud, Microsoft Azure.
  • Docker Hub: là Registry lớn nhất của Docker Images ( mặc định). Có thể tìm thấy images và lưu trữ images của riêng bạn trên Docker Hub ( miễn phí).
  • Docker Repository: là tập hợp các Docker Images cùng tên nhưng khác tags. VD: golang:1.11-alpine.
  • Docker Networking: cho phép kết nối các container lại với nhau. Kết nối này có thể trên 1 host hoặc nhiều host.
  • Docker Compose: là công cụ cho phép run app với nhiều Docker containers 1 cách dễ dàng hơn. Docker Compose cho phép bạn config các command trong file docker-compose.yml để sử dụng lại. Có sẵn khi cài Docker.
  • Docker Swarm: để phối hợp triển khai container.
  • Docker Services: là các containers trong production. 1 service chỉ run 1 image nhưng nó mã hoá cách thức để run image — sử dụng port nào, bao nhiêu bản sao container run để service có hiệu năng cần thiết và ngay lập tức.

Dockerfile

– Dockerfile là file config cho Docker để build ra image. Nó dùng một image cơ bản để xây dựng lớp image ban đầu. Một số image cơ bản: python, unbutu and alpine. Sau đó nếu có các lớp bổ sung thì nó được xếp chồng lên lớp cơ bản. Cuối cùng một lớp mỏng có thể được xếp chồng lên nhau trên các lớp khác trước đó.

– Các config :

  • FROM — chỉ định image gốc: python, unbutu, alpine…
  • LABEL — cung cấp metadata cho image. Có thể sử dụng để add thông tin maintainer. Để xem các label của images, dùng lệnh docker inspect.
  • ENV — thiết lập một biến môi trường.
  • RUN — Có thể tạo một lệnh khi build image. Được sử dụng để cài đặt các package vào container.
  • COPY — Sao chép các file và thư mục vào container.
  • ADD — Sao chép các file và thư mục vào container.
  • CMD — Cung cấp một lệnh và đối số cho container thực thi. Các tham số có thể được ghi đè và chỉ có một CMD.
  • WORKDIR — Thiết lập thư mục đang làm việc cho các chỉ thị khác như: RUN, CMD, ENTRYPOINT, COPY, ADD,…
  • ARG — Định nghĩa giá trị biến được dùng trong lúc build image.
  • ENTRYPOINT — cung cấp lệnh và đối số cho một container thực thi.
  • EXPOSE — khai báo port lắng nghe của image.
  • VOLUME — tạo một điểm gắn thư mục để truy cập và lưu trữ data.

Tạo Demo

Tạo file Dockerfile

FROM golang:1.11 AS builderWORKDIR /go/src/docker-demo/COPY . .RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o docker-demo .FROM alpine:latestWORKDIR /root/COPY — from=builder /go/src/docker-demo .CMD [“./docker-demo”]

Tạo file main.go

package mainimport (   “fmt”)func main() {   fmt.Println(“Learning Docker”)}

Tiến hành build file Dockerfile

$ docker build .

Docker là gì? Kiến thức cơ bản về Docker

$ docker run 4cc010d9d657

Docker là gì? Kiến thức cơ bản về Docker

Kết quả in ra dòng chữ Learning Docker đã được code trong file main.go

Các lênh cơ bản trong docker

  • List image/container:
$ docker image/container ls
  • Delete image/container:
$ docker image/container rm <tên image/container >
  • Delete all image hiện có:
$ docker image rm $(docker images –a –q)
  • List all container hiện có:
$ docker ps –a
  • Stop a container cụ thể:
$ docker stop <tên container>
  • Run container từ image và thay đổi tên container:
$ docker run –name <tên container> <tên image>
  • Stop all container:
$ docker stop $(docker ps –a –q)
  • Delete all container hiện có:
$ docker rm $(docker ps –a –q)
  • Show log a container:
$ docker logs <tên container>
  • Build một image từ container:
$ docker build -t <tên container> .
  • Tạo một container chạy ngầm:
$ docker run -d <tên image>
  • Tải một image trên docker hub:
$ docker pull <tên image>
  • Start một container:
$ docker start <tên container>

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

Gặp gỡ Nguyễn Sơn Tùng CTO Viec.co – Quán quân StartupViet 2019

Gặp gỡ Nguyễn Sơn Tùng CTO Viec.co

Đôi nét về anh Tung NS – Nguyễn Sơn Tùng – CTO/Founder | Việc Có

Anh Sơn Tùng từng có 5 năm kinh nghiệm giữ vị trí Head of Technology (Giám đốc kỹ thuật) tại Tiki.vn – sàn giao dịch TMĐT lớn nhất hiện nay. Hiện tại đang nắm giữ vị trí Co-founder & CTO của Viec.co – ứng dụng cùng tên để kết nối người lao động làm việc ngắn với các doanh nghiệp một cách nhanh chóng và đơn giản.

Từ vị trí là một Manager của một trong những trang TMĐT hàng đầu Việt Nam cho đến việc trở thành Founder/CTO của một Startup đoạt giải quán quân Startup Việt và gọi vốn thành công 300.000 USD tại Thương vụ bạc tỷ – Shark Tank Việt Nam. Trong cuộc nói chuyện hôm nay, anh Sơn Tùng sẽ chia sẻ 1 số kinh nghiệm quý giá  trong lĩnh vực công nghệ cũng như phác họa chân dung của 1 người làm CTO ở nhiều môi trường khác nhau. Hãy cùng TopDev gặp gỡ anh Sơn Tùng trong chương trình Chuyên Gia Nói ngày hôm nay nhé!

Chào anh, anh có thể chia sẻ với mọi người về công việc hằng ngày của mình cho mọi người được hiểu thêm về công việc của anh được không?

Mỗi ngày việc đầu tiên khi tới công ty, tôi sẽ lập 1 cái danh sách (blue-list) để xem mình dùng thời gian cho việc gì trong ngày cho nó chất lượng. Cụ thể hơn như là tôi sẽ xem công việc tiến độ ra sao, mọi người có cần tôi hỗ trợ gì không, rồi review công việc của mọi người, sau đó tôi sẽ trao đổi với các team về kinh doanh khác, coi có gì cần cập nhật hoặc chuẩn bị cho planning kế tiếp của team không, rồi thỉnh thoảng tôi sẽ “phân thân” vào phụ mọi người ở các phần việc khác nhau.

Ngoài ra thì tôi cũng dành khá nhiều thời gian để đi kết nối gặp các bạn có cùng chí hướng để xây dựng team và thỉnh thoảng cũng đi gặp khách hàng nữa. 

Hiện tại VIEC.CO đã có những chỉ số hoạt động đáng kể nào, anh có thể chia sẻ cho mọi người được biết không?

Sau 1 năm hoạt động thì bên tôi cũng may mắn là có khoản 50.000 cộng tác viên đăng ký đi làm, cũng giúp kết nối việc làm cho khoảng hơn 100 công ty, và nếu tính trên việc làm thực sự đã diễn ra thì bọn tôi có tạo ra được 500.000 giờ làm giữa 2 bên. 

Gặp gỡ Nguyễn Sơn Tùng CTO Viec.co

Đó là một con số rất ấn tượng, vậy theo anh, để đạt được những thành quả đó, với vai trò là một CTO thì điều quan trọng nhất là gì?

Về vai trò này thì tôi có 4 đặc điểm tối quan trọng như sau 

Thứ 1, CTO thì phải giỏi về công nghệ, vì bạn phải chịu trách nhiệm trả lời mọi thứ về công nghệ, nó có triển khai được không và mọi thứ thì bạn phải có 1 độ hiểu sâu nhất định, không chỉ cho những thứ đang diễn ra mà cho mọi thứ sắp tới nữa, đó là điều quan trọng nhất. 

Điều thứ 2 là bạn phải biết cách xây dựng 1 team hoàn chỉnh, bởi nếu làm 1 mình thì bạn sẽ không làm được những việc lớn, việc khó khăn nhất là tìm ra những người cùng chí hướng, từ đó giúp họ, cùng họ phát triển và trụ lại ở lại công ty. Nhìn chung thì chiến lược của công ty có thể thay đổi lên xuống, nhưng tôi có 1 team mạnh thì mình làm gì cũng được.

Đặc điểm thứ 3 là 1 CTO thì phải biết cách để giúp cho team của mình triển khai được sản phẩm hoặc ý tưởng. Dù bạn có bao nhiêu người giỏi cạnh bên nhưng không có triển khai được ý tưởng hay sản phẩm thì cũng chả giải quyết được vấn đề nào hết. Để làm được như vậy, bạn CTO đó phải xây dựng cho được 1 thứ mà tôi hay gọi là engineering, bao gồm có văn hóa, quy trình, những thứ cần thiết để mọi người biết là sẽ làm việc theo cách như thế nào. Có được điều đó, công ty sau này phát triển lớn lên sẽ có những vị trí chuyên trách như là head director hoặc là VP hoặc là engineering riêng, còn thời gian đầu thì CTO phải làm tất tần tật những việc đó.

Cái thứ 4 mà tôi nghĩ rất là cần là CTO thì phải là người hiểu rất là rõ về business và khách hàng, thì khi đó mới nói chuyện cùng ngôn ngữ được với những team khác. Từ đó tôi mới có thể dùng công nghệ của mình để giải quyết vấn đề và chuẩn bị tốt nhất cho sản phẩm của mình, để nó có thể cũng đáp ứng được cái nhu cầu của tất cả mọi người. 

Tôi nghĩ đó là 4 yếu tố cơ bản nhất, còn 1 cái cuối cùng thì tôi nghĩ mọi người chắc cũng biết đó là sẽ phải học, bởi vì mọi thứ sẽ thay đổi rất nhanh. Tôi học để biết mình áp dụng những công nghệ gì, và chuẩn bị cho việc đón đầu những xu hướng công nghệ tiếp theo.

Với tư cách là một CTO, anh có trực tiếp tham gia code hay trực tiếp đảm nhiệm công việc nào? Theo anh thì phần việc nào CTO không nên trực tiếp làm?

Từ lúc triển khai ban đầu, khi chưa thực sự có gì thì CTO phải làm tất cả mọi thứ. Làm từ code như 1 developer, test như 1 QC, làm sản phẩm để có UI prototype, hay DevOps rồi system deploy tất cả mọi thứ. Tôi cũng code VIEC.CO từ những dòng đầu tiên từ cho mobile cho backend mà từ từ rồi sẽ có người khác tiếp sức. Từ đó, công việc, vai trò của tôi, về những chuyện mà tôi sẽ nhúng tay sâu vào sẽ giảm bớt lại.

Tôi tránh dẫm chân lên mọi người ở những chỗ tôi cảm thấy mọi người có thể làm được hoặc có tiềm năng sẽ làm được, cần tạo cho họ cơ hội để họ có thể làm chuyện đó. Nhưng bên cạnh đó, tôi vẫn phải làm chi tiết như là 1 số technical *debt. Khi thấy mọi người đang gặp khó khăn ở đâu thì tôi cũng phải xắn tay áo lên. Hay đôi lúc có một ý tưởng về business muốn thử nghiệm và triển khai nhanh chẳng hạn, thì CTO cũng có vai trò là sẽ nghiên cứu và triển khai. Ví dụ có 1 đợt mọi người muốn thử nghiệm chấm công bằng công nghệ bluetooth, tôi cũng phải tự nghiên cứu để làm 1 cái MVP (Minimum Viable Product) thử. Đó là chi tiết những gì tôi phải làm. 

Được biết trước đây anh từng làm manager tại Tiki, anh có thể chia sẻ về sự khác nhau khi làm ở 1 tổ chức lớn và ở Startup như VIEC.CO không?

Trước đây thì tôi từng đảm nhận vai trò Head of Technology 5 năm ở Tiki. Vị trí này đối với 1 công ty startup nhỏ như tôi khá tương đồng, nhưng cũng tồn tại sự khác biệt. Khi phụ trách về công nghệ ở Tiki cũng có thách thức cũng tương đối lớn. 

Ví dụ đầu tiên là thách thức để tăng trưởng. Lấy ví dụ bạn có thể đóng 1 chiếc ghế trong vòng một ngày hết sức dễ dàng. Nhưng nếu mà bạn phải đóng chiếc ghế trong vòng 1 giờ và càng ngày càng rút ngắn thời gian, chiếc ghế còn phải đẹp hơn nữa thì việc này sẽ đẩy bạn đến giới hạn cuối cùng của bản thân. Bạn sẽ phải học hỏi và rèn luyện thêm hàng ngày.

Thách thức thứ 2 khi làm với 1 công ty lớn như Tiki đó là bạn phải giải quyết nhiều việc cùng 1 lúc. Thứ nhất, bạn phải duy trì cải tiến những hệ thống cũ, sản phẩm của lúc không có người giỏi tham gia (và họ có xu hướng thích làm cái mới) và bên cạnh cái cũ thì mình phải build những cái mới. Sau đó, phải đi làm chuyện khác như là làm sao migrate những cái cũ sang cái mới. Đó là những việc phức tạp, rủi ro mà không phải ai cũng dũng cảm để làm. Tôi sẽ phải làm sao để mọi thứ nó đều chạy ổn định. Nó không giống startup khi bạn build mọi thứ mới ngay từ đầu, đó là công việc tương đối dễ dàng.

Đấy là những thách thức cơ bản nhất khi bạn làm ở cái startup lớn như Tiki, còn khi làm ở startup nhỏ thì có những điểm thú vị. Thứ nhất, bạn có nhiều cơ hội học hỏi hơn. Ở 1 tổ chức lớn, không phải việc gì bạn cũng có thể tham gia vào ngay được, vì bạn sẽ phải cần có độ sâu sắc nhất định trong từng lĩnh vực. Sự sâu sắc đó không phải tự nhiên bạn tích lũy được mà đến từ một quá trình trước đó. Khi tham gia những startup nhỏ, bạn có cơ hội tích lũy dễ dàng hơn, tích tiểu thành đại. 

Trước đây tôi cũng đảm nhận một vài vị trí mà tôi cảm thấy từng trải qua trong quá trình trước đó. Lúc làm ở vị trí product manager, tôi thực sự chưa hiểu rõ về business khi mình làm. Tôi tìm hiểu qua bằng việc đọc sách rồi thử nghiệm. Nhưng sau khi hoàn thành thì kết quả chưa được tốt. Nhưng dựa trên nền tảng đó, khi làm vị trí hiện tại, khi làm business tôi đã có góc nhìn về business tốt hơn. Đó là cách để tôi học hỏi từ từ, tích lũy để tôi làm startup quy mô lớn hơn. Đó là sự khác biệt khi mà làm cho cái lớn và cái nhỏ.

Anh có thể chia sẻ ví dụ cụ thể hơn khi merge từ công nghệ cũ sang công nghệ mới ở Tiki không?

Tôi có một trải nghiệm về chuyện merge rất đáng nhớ. Khi tôi vô Tiki, họ có hệ thống tương đối phức tạp, quy mô năm 2012 có thể hình dung: khoảng 1000 table và 300 extension chạy trên 1 nguồn mở. Khi có sự cố, bug, vấn đề xảy ra, mọi người cảm thấy bức xúc, cần phải giải quyết. 

Có vấn đề có thể giải quyết được bằng cách fix trực tiếp, có những cái rất là vô vọng. Tôi buộc phải build 1 hệ thống mới nhỏ gọn hơn, đáp ứng đúng cái nhu cầu của Tiki. Nhưng với nguồn lực có hạn, tôi không thể tập trung build một thứ được mà tôi phải tách nguồn lực ra để làm nhiều thứ cùng lúc. Sau khi mất thời gian để build được cái hệ thống core platform của Tiki đó rồi, bài toán tiếp theo là làm sao đưa hệ thống này vào sử dụng. Việc đẩy hệ thống rất lớn qua 1 hệ thống mới hoàn toàn khá là thử thách. Không thể 1 ngày là chuyển giao được mà vẫn phải duy trì fix bug ở bên này, build hệ thống mới ở bên kia. Rồi bạn dành thời gian process để merge cái cũ sang cái mới.

Tôi nhớ mình làm 1 cái script chạy 5 tiếng đồng hồ, đẩy toàn bộ dữ liệu qua và gặp rất nhiều lỗi bởi vì cũ mới thì có những cái không tương thích. Đây cũng là ví dụ về công việc tương đối phức tạp, yêu cầu bạn phải vừa phải rất hiểu cái cũ cũng như rất hiểu cái mới. Không phải ai cũng dũng cảm và sẵn sàng để làm chuyện như vậy và CTO chắc chắn sẽ là người nhận trách nhiệm đó về mình. 

Lý do nào anh chọn khởi nghiệp với ý tưởng sàn giao dịch việc làm thời vụ VIEC.CO?

VIEC.CO tôi start cùng với 1 bạn Co-founder nữa. Bọn tôi mỗi người đến với mục đích khác nhau nhưng đều chung muốn giúp những người lao động phổ thông, những người thu nhập chưa cao, nhiều rủi ro ở trong xã hội. Mọi người có thể hình dung, mỗi tháng mọi người nhận lương thì nghe ‘ting ting’ có khi mấy chục triệu là chuyện bình thường. Nhưng với người lao động phổ thông, mỗi kỳ hay mỗi tuần bọn tôi chuyển cho họ vài trăm nghìn thì họ cũng reo lên sung sướng. Hoặc với 1 triệu mình mua sắm được vài thứ, nhưng số tiền đó đưa cho họ có khi cải thiện bữa ăn được vài tuần. Hoặc là giúp đứa bé đóng học phí không bị cô nhắc nữa chẳng hạn. Đó là động lực để cho bọn mình làm hệ thống này và tiếp tục sẽ làm hệ thống này. 

Hiện tại VIEC.CO cũng được offer từ chương trình Shark Tank và đồng thời cũng là quán quân của Startup Việt 2019 thì – Sau những sự kiện như vậy thì chỉ số hoạt động của VIEC.CO thay đổi tích cực như thế nào? 

Rất là may mắn là bọn tôi đã có 1 cái deal với Shark Tank và quán quân Startup Việt, ngay sau đó bọn tôi cũng rất là bất ngờ với những cái kết quả đi kèm. Có thể mô tả như sau: có một vài ngày, số khách hàng tăng bằng cả năm cộng lại vậy. Có những khách hàng mang lại cho bọn tôi cơ hội mới,  không chỉ là khách hàng mới mà còn nhu cầu mới và từ đó đến nay bọn tôi đã làm việc rất chăm chỉ để đáp ứng nhu cầu của những khách hàng đó. 

Ngoài những thành công, anh có thể chia sẻ một kỷ niệm sai lầm đáng nhớ mà anh từng mắc phải? 

Có một sai lầm mà tôi nghĩ sẽ nhớ rất là lâu. Cách đây cũng 8 năm, khi tôi bắt đầu tham gia Tiki, tôi tham gia với 1 cái tâm thế là mình đã làm 1 cái hệ thống rất lớn, 1 website có thứ hạng thứ 18 trên Alexa. Tôi tin là với Tiki cũng nhỏ thôi, mình tin là sẽ làm ok. Nhưng thực ra thời điểm đó tôi chỉ là tay mơ trong lĩnh vực thương mại điện tử thôi. Hệ thống Tiki rất phức tạp, 1 hệ thống e-commerce pha rất là nhiều (mà chuyên ngành gọi là) write xuống chứ không phải là read như các trang web khác. Cho nên khi đó Tiki làm event là hệ thống bị quá tải, kinh nghiệm rút ra là tôi cần phải rất là ‘ở dưới mặt đất’, nghiên cứu rất là kĩ những cái gì tôi sắp làm và có sự chuẩn bị phù hợp thì tôi mới làm. 

Anh có lời khuyên nào dành cho các bạn developer trong công việc nâng cao kỹ năng cũng như career path.

Để trả lời câu hỏi đó thì tôi xin chia sẻ câu chuyện mình đã từng giới thiệu 1 bạn trong team cho Facebook. Bên Facebook hỏi tôi là theo bạn “người nhân viên này nằm trong top bao nhiêu những người bạn đã làm việc cùng?”. Từ chuyện đó, tôi có liên hệ tới 1 số chuyện khác, khi tôi đã làm việc với rất là nhiều bạn rồi, phỏng vấn khá nhiều thì phải để trở thành 1 cái gì đó rất giỏi, làm được những công ty lớn thì các bạn phải định vị mình là ai trong số tất cả những người còn lại. Ở trong lớp, bạn top bao nhiêu, trong 1 nhóm, hội nhóm, bạn định vị mình là như thế nào, bất kể là bạn đang là bao nhiêu, quan trọng là bạn phải định vị mình, đặt mục tiêu cho mình. Trong sự lựa chọn 100 người tại sao người ta tuyển bạn? Đấy là câu hỏi phỏng vấn thường hay hỏi nhất. Khi mà bạn định hình cho mình một mục tiêu rất cao và chăm chỉ đạt được mục tiêu đó, bạn sẽ có cơ hội làm khác biệt, để làm những chuyện lớn hơn trong tương lai.

Anh có thể chia sẻ một vài lý do để các bạn Developer join vào team VIEC.CO?

Thứ nhất, hiện nay bọn tôi đang có nhiều thách thức lớn, như là xây dựng một nền tảng về lao động để mọi người làm việc, và bọn tôi tin là nó sẽ có nhiều tác động đến việc mọi người đi làm trong tương lai. Đó là một bài toán khó và phức tạp, và nó có quy mô tăng trưởng rất là nhanh nên nếu các bạn tham gia thì các bạn có cơ hội để giải quyết các thử thách đó.

Thứ 2, bọn tôi có nhiều kinh nghiệm giải quyết bài toán ở quy mô lớn như là thương mại điện tử như Tiki, từ đó cũng có thể tư vấn để giúp bạn. Với những bạn còn trẻ nhận biết đây là giai đoạn các bạn phát triển, rút ngắn khoảng thời gian để các bạn có thể phát triển nhanh hơn, và nhìn chung là team của tôi khá là ‘geeky’ thể hiện qua cái cách mà tôi tuyển dụng.

Quy trình tuyển dụng ở VIEC.CO không giống như các công ty khác. Bọn tôi không tuyển theo kiểu chỉ đăng cái job lên rồi nhận CV. Bọn tôi code hẳn 1 cái trang là tuyển dụng gọi là VIEC.CO HIRING API, cũng là cách để tôi giao tiếp với developer bằng cái ngôn ngữ của họ để đăng tuyển. Để tìm hiểu thông tin về công việc và công ty thì bạn phải gửi API, bạn sẽ nhìn thấy mô tả công việc, nhìn thấy cái bài test trên đó các bạn tự sắp list bài test và bạn thấy kết quả luôn. Khi các bạn pass bài test, tôi sẽ liên hệ lại các bạn. Đầu tiên thì cũng sẽ gọi điện hỏi thăm mấy cái vấn đề quan trọng, sau đó là phỏng vấn. 

Phỏng vấn là có cả team bọn tôi cũng phỏng vấn chung, và cuối cùng là bọn tôi sẽ quyết định là sẽ làm việc với bạn đó hay không.

Theo quan điểm cá nhân của anh, xu hướng công nghệ nào trong tương lai mà một Developer cần nắm bắt?

Xu hướng công nghệ cũng phụ thuộc vào tùy lĩnh vực. Còn đối với cá nhân tôi, tôi cũng rất là quan tâm tới AI, bởi những trải nghiệm của tôi với AI rất là tuyệt. Tôi nhớ là khi tôi tham gia một event của GOOGLE IO , tôi nghe rất nhiều SUNDAR PICHAI, anh đó chia sẻ về những thứ mà Google Assistant có thể làm được. Tôi thấy cái này thay đổi cuộc sống rất là khủng khiếp, và tôi nhìn lại thì những cái gì đang diễn ra ở hiện tại thì nó vẫn còn cách đích đến 1 đoạn, để làm sao mà tới cái đích đó thì cần rất nhiều công sức của mọi người nữa. Mọi người học nhiều hơn để chuẩn bị dần để đến cái tương lai đó.

Cũng cách mới đây mấy hôm thôi, tôi cũng thấy Elon Musk đăng tuyển dụng về AI. Những bạn nào có thể làm được AI có thể là nộp đơn cho anh ấy. Thậm chí là không cần phải là qua Đại học, thậm chí phổ thông anh ấy cũng nhận luôn. Miễn là mọi người đam mê và thuyết phục được là mình làm được thì đó là cơ hội, nó không quá xa nếu như các bạn bắt đầu sớm và tạo ra nhiều thay đổi trong tương lai.

Theo quan điểm của anh thì bằng cấp trong công việc IT có quan trọng hay không?

Chúng ta hãy hình dung bằng cấp là gì. Bằng cấp là mục tiêu đặt ra trong cái sự nghiệp của mọi người hay là cái mà ai đó bảo bạn là phải lấy? Như mọi người biết, Bill Gates cũng không có bằng đại học, nhưng bạn có thể không biết là ông là người học giỏi nhất cả mấy tiểu bang, và ông ấy cũng đang ở trong trường tốt nhất mà ra.

Câu chuyện bằng cấp nó không quan trọng nếu như bạn có mục tiêu rõ ràng. Nếu mục tiêu của bạn là lấy bằng thì bạn phải lấy bằng vì đó là commitment giữa cá nhân bạn với chuyện đó. Còn nếu mục tiêu của bạn là đi làm cái chuyện khác chẳng hạn, bạn có đủ động lực, bạn có đủ kiến thức, đủ đam mê thì bạn cứ làm. Và cá nhân tôi cũng không có quan trọng là các bạn tham gia VIEC.CO là phải có bằng cấp.

Anh có nhắc đến việc AI thay đổi cuộc sống của con người theo hướng tích cực, không biết trong tương lai anh có dự định áp dụng AI vào VIEC.CO không?

Tôi quan tâm và tôi đang chuẩn bị dần, thì cũng đi học mọi người thôi. Bởi lẽ tôi tin rằng mình đang giải quyết bài toán rất lớn. Làm sao để ai đó biết họ cần bao nhiêu nguồn lực, ai đó biết rằng họ cần những người như thế nào, rồi người họ cần có những đặc tính ra làm sao.

Như tôi tìm hiểu thì 1 startup giống VIEC.CO ở bên Mỹ, họ đang phân tích chỉ số liên quan tới người lao động, có khoảng 40 chỉ số và họ sẽ dùng những chỉ số này để xây dựng dần những hệ thống, giúp người cần lao động biết được ai phù hợp với họ và ngược lại bạn đi làm chỗ nào thì phù hợp với bạn, và đấy cũng là cái VIEC.CO sẽ chuẩn bị và đầu tư cho tương lai.

Đâu là những khó khăn và thuận lợi nào đối với một Founder, Startup có nền tảng là Tech như anh?

Thuận lợi thì mọi người biết rồi, CTO khá rành về công nghệ cho nên nếu tôi làm Startup công nghệ thì tương đối phù hợp, lên MVP và mọi thứ tương đối nhanh và cũng sẵn sàng để scale lên mức độ cao hơn nữa. Còn khó khăn thì từ đúc kết bản thân tôi, ban đầu cũng hơi khô cứng nên business sense của mình chưa có nhạy, chưa thể dễ dàng làm việc với team business. Tôi nên áp dụng những lĩnh vực này, lĩnh vực kia, những cái đó thì phải học thôi, và tôi sẽ phải tự học thêm hoặc là đi gặp gỡ khách hàng nhiều để lấy insight của họ.

Anh có thể chia sẻ với khán giả về một số cách để học Business không ạ?

Đầu tiên là đọc sách, tôi quan tâm đến lĩnh vực nào thì sẽ kiếm sách về lĩnh vực đó, rồi học chăm chỉ đi trong việc gặp khách hàng, tôi sẽ ghi nhận lại những cái insight của họ, về những cái họ cần là gì. Về sau đó là tôi sẽ so sánh lại cái suy nghĩ hồi trước mà gọi gọi là khô cứng để coi là mình suy nghĩ vậy đã đúng chưa rồi điều chỉnh lại.

Anh hãy chia sẻ về Technical Stack mà anh đã chọn và vì sao anh lại chọn Technical Stack đó?

Technical Stack của VIEC.CO hiện nay sử dụng nhiều. Về cơ bản, bọn tôi sử dụng React Native và Reactjs cho frontend của phần mobile app, core platform thì mình sử dụng Laravel cho backend, còn về hạ tầng thì dùng Google Cloud Platform, Database thì có MySQL.

Lý do mà tôi đang dùng Stack, khi bạn muốn làm việc gì, bạn sẽ bắt đầu với thứ mà bạn mạnh nhất và có đồng đội của bạn mạnh nhất nữa. Rồi bạn phải biết được là cái bạn cần làm thì cần dùng cái công nghệ gì cho phù hợp.

Tôi có thể lựa chọn theo trend thí dụ như là Golang, để làm việc đó nhưng mà bài toán của tôi nó có cần những cái như vậy và nó có phù hợp với những cái như vậy hay không, và đây một quyết định hoặc câu hỏi đặt ra thường xuyên của các bạn developer hay là technical leader thường sẽ giải quyết, sao không áp dụng cái này cái kia đi chẳng hạn. Mình thấy chuyện tranh luận đó không có hồi kết, sẽ phụ thuộc vào chuyện là bạn đang vướng cái gì.

Theo tôi để ý ở các engineer công ty khác, tại sao Airbnb phải lập cái usability hoặc là lập ra rất là nhiều công nghệ riêng của họ, như diaflow chẳng hạn. Bản chất là họ đang gặp phải 1 cái giới hạn và họ phải tìm cách phá vỡ giới hạn đó, nếu như PHP đối với mình giải quyết cái bài toán dưới 100 millisecond, dưới 50-100 millisecond ở server side cũng ok, nếu mà tôi không cần tới mức giới hạn đó thì tôi giải quyết được rồi, tùy theo cái scale của bạn đang làm là gì thì bạn phải chọn công nghệ phù hợp và người tech leader phải dành rất nhiều thời gian để lựa chọn cái stack công nghệ phù hợp thay vì là cứ thấy thị trường đang có cái gì mới thì chạy theo, rồi hệ thống của bạn phải đổi cái nọ cái kia, phải viết đi viết lại nhiều lần thì cũng mất thời gian ra, không để làm gì cả.

Các bạn có quyền là thử nghiệm và áp dụng những cái công nghệ mới nhưng là nên là 1 cái test project nào đó, đến khi bạn thực sự am hiểu nó rồi thì mình hãy dùng, đừng là cái người đi thử hết công nghệ này đến công nghệ kia trong business, mà hãy tìm hiểu rất là sâu để mình biết chắc chắn về công nghệ rồi mới áp dụng cái nào phù hợp. 

Gặp gỡ Nguyễn Sơn Tùng CTO Viec.co

Anh hay dùng công nghệ và cách thức gì để quản lý công việc của team? Cũng như là đo đạc hiệu quả làm việc của họ? 

Thời gian đầu thực sự tôi không dùng công cụ gì hết, startup mọi người làm việc rất là chăm chỉ, 200% sức lực, cái đo hiệu quả nhất là những trải nghiệm của khách hàng, rep của tôi ra sao, tôi chia sẻ lại bạn đó để bạn đó biết là à, cái này khách hàng đang chê này.

Và có những lúc mà bọn tôi deploy 1 cái chức năng nào đó hay ho, cụ thể như là làm chức năng mà đặt cho mọi người 1 cái badge để thể hiện bạn có 500 giờ để làm việc cho cái hệ thống này, deploy xong 1 ít phút  sau thì có bạn khoe “ôi tôi làm được việc nè” với mọi người, thì đó là những cách ghi nhận thành quả ghi nhận là bạn làm việc hiệu quả, rất là tốt.

Còn về cách thức bọn tôi tổ chức trong tương lai khi mà nhiều người hơn thì sẽ cần những quy trình. Thực ra bọn mình đã trải nghiệm chuyện đó rồi, ở quy mô lớn hơn thì tôi cũng đang chuẩn bị quy trình đó cho team tiếp theo, do đông hơn rồi mà. Bọn tôi sẽ có những cái gọi là Story Point để cho task mọi người làm, định nghĩa bởi vì là cái task này là effort của nó bao nhiêu kết hợp với lại cái giá trị business, sau mỗi screen rồi tự nhìn nhận tôi deliver được bao nhiêu point, đấy là 1 cách. Tuy nhiên cái ghi nhận lớn nhất cho tôi là từ khách hàng.

Làm thế nào để anh xác định những tính năng hoặc sản phẩm mà team anh cần làm tiếp? Và làm thế nào để quản lý những request đó?

Tôi sắp xếp việc đó từ 2 phía. Phía thứ nhất là Topdown, từ business xuống và tôi có cái process để phân tích những business infinitive, bọn tôi sẽ có mục tiêu đi xuống cụ thể hóa, cần phải làm gì để giải quyết cái mục tiêu đó. Rồi từ từ sẽ chuyển xuống bên dưới thành những cái ‘to do’ của developer như là IPic, Story Point hay Task.

Còn đi từ hướng thứ 2 là phía khách hàng thì team sales và mình phải đi gặp trực tiếp khách hàng, ghi nhận lại những cái nhu cầu của khách hàng, xong rồi tôi sẽ set độ ưu tiên và bỏ chung vào đánh giá lại cái gì sẽ làm.

Công cụ thì mình đang xài Jira.

Anh quan tâm đến những khía cạnh phi kỹ thuật nào khi có ứng viên muốn apply vào team?

Khi hỏi ứng viên thì tôi sẽ thường hay hỏi là thứ nhất bạn tự hào về cái gì trong quá khứ bạn làm nhất, bạn có một project, thậm chí là code hay không phải code, bởi vì khi mà bạn bỏ tâm đến cái mà bạn làm cái gì đó, tạo ra 1 kết quả thì tôi hiểu là bạn rất nghiêm túc với công việc và có thể là bạn sẽ tạo ra những kết quả awesome trong tương lai.

Cái thứ 2, tôi sẽ hỏi là bạn thường đam mê cái gì nhất. Có nhiều bạn sẽ trả lời là đam mê code, cái đó thì tùy bạn. Nếu bạn yêu code như thế, mình cũng sẽ biết thôi, nhưng cũng có nhiều bạn trả lời đam mê những cái khác nữa. Có bạn thì đam mê xem phim, hay là đi du lịch, đam mê làm cái mà đôi lúc mình cũng không biết nó là cái gì nhưng mà mình cũng đánh giá rất cao chuyện đó vì mình biết rằng là khi bạn đam mê thì bạn cũng có cái sự cam kết nhất định của mình với đam mê đó. Và khi 1 người cam kết với mục đích của mình thì các bạn sẽ đẩy cái khả năng của bạn đến 1 mức cao hơn. Như chơi game, đâu phải ai cũng chơi giỏi, cũng phải có những kỹ năng nào đó.
Và ngược lại có những bạn mà tôi cũng cảm thấy là tôi nên kết thúc buổi gặp này sớm vì bạn không thể hiện được bạn là ai cả, bạn không thực sự thích cái gì cả thì tất nhiên là bạn phải quay lại cái chuyện định hướng 1 chút.

Xin phép được cám ơn anh đã đến với buổi phỏng vấn của TopDev, và nếu các bạn cảm thấy câu trả lời của anh Tùng, những kiến thức mà anh mang lại trong buổi phỏng vấn bổ ích với các bạn thì các bạn đừng quên nhấn ‘Like & Subscribe’ ở Channel nhé, hẹn gặp lại các bạn ở các series tiếp theo.

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

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

Phương pháp test React Component

Trang này mặc định bạn đang dùng Jest làm test runner. Nếu dùng một test runner khác, bạn cần thay đổi API cho phù hợp, giải pháp sẽ gần như nhau. Đọc thêm chi tiết cách cài đặt môi trường test ở Môi trường Test.

Một vài cách viết test phổ biến cho component React. Trên trang này, chúng tôi sẽ tập chung vào function component. Tuy nhiên, cách để tiếp cận test không phụ thuộc vào phần hiện thực cụ thể, nó cũng sẽ làm việc tốt với class c

Tuyển react không cần kinh nghiệm


Cài đặt cụ thể {#setup–teardown}

Trên mỗi test, chúng ta thường muốn render React tree của chúng ta thành DOM element và chèn nó vào document. Chỉ như thế chúng ta mới nhận được các sự kiện trên DOM. Khi kết thúc một test, chúng ta muốn “dọn dẹp” và gỡ bỏ cây này khỏi DOM.

Một cách phổ biến để làm nó là sử dụng bộ đôi beforeEach và afterEach, để chúng luôn chạy một cách độc lập và không ảnh hưởng đến test khác:

import { unmountComponentAtNode } from "react-dom";

let container = null;
beforeEach(() => {
  // cài đặt một DOM element như là target cho render
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  // dọn dẹp lúc thoát
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

Bạn có thể sử dụng một cách khác, nhưng hãy nhớ chúng ta muốn chạy việc dọn dẹp ngay cả khi test fail. Nếu không, test có thể trở nên “bất ổn”, và một test có thể ảnh hưởng đến hoạt động của test khác. Như vậy sẽ rất khó để debug.


act() {#act}

Khi viết UI test, công việc như render, sự kiện từ user, hoặc fetch dữ liệu có thể được xem như một “đơn vị” tương tác với giao diện người dùng. React cung cấp một hàm trợ giúp act() để đảm bảo tất cả mọi cập nhập liên quan đến “đơn vị” đã được thực thi và áp dụng đến DOM trước khi chúng ta xác nhận kết quả:

act(() => {
  // render component
});
// xác nhận kết quả

Nó giúp test chạy giống nhất với những gì user nhận được khi sử dụng ứng dụng. Tất cả những ví dụ bên dưới sử dụng act() để đảm bảo điều này.

Bạn có thể thấy sử dụng act() trực tiếp rất rườm rà. Để tránh rườm rà, bạn có thể sử dụng một thư viện như React Testing Library, các hàm hỗ trợ đã được wrap lại sẵn trong act().

Lưu ý:

Tên act có nguồn gốc từ cách làm Arrange-Act-Assert.


Rendering {#rendering}

Thường thì, chúng ta muốn test xem một component render đúng hay không với các prop nhận được. Xem xét một component đơn giản sẽ render một thông tin dựa vào prop:

// hello.js

import React from "react";

export default function Hello(props) {
  if (props.name) {
    return <h1>Hello, {props.name}!</h1>;
  } else {
    return <span>Hey, stranger</span>;
  }
}

Chúng ta có thể viết test cho component:

// hello.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";

import Hello from "./hello";

let container = null;
beforeEach(() => {
  // cài đặt một DOM element như là target cho render
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  // dọn dẹp lúc thoát
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("renders with or without a name", () => {
  act(() => {
    render(<Hello />, container);
  });
  expect(container.textContent).toBe("Hey, stranger");

  act(() => {
    render(<Hello name="Jenny" />, container);
  });
  expect(container.textContent).toBe("Hello, Jenny!");

  act(() => {
    render(<Hello name="Margaret" />, container);
  });
  expect(container.textContent).toBe("Hello, Margaret!");
});

Fetch dữ liệu {#data-fetching}

Thay vì gọi APIs thật trong test, chúng ta có thể giả lập các request này bằng dữ liệu giả. Giả lập dữ liệu với dữ liệu “fake” để tránh ảnh hưởng đến test khi backend không sử dụng được, và để nó chạy nhanh hơn. Lưu ý: bạn có thể muốn nó chạy danh sách các test con sử dụng framework “end-to-end” để xem toàn bộ ứng dụng có làm việc với nhau không.

// user.js

import React, { useState, useEffect } from "react";

export default function User(props) {
  const [user, setUser] = useState(null);

  async function fetchUserData(id) {
    const response = await fetch("/" + id);
    setUser(await response.json());
  }

  useEffect(() => {
    fetchUserData(props.id);
  }, [props.id]);

  if (!user) {
    return "loading...";
  }

  return (
    <details>
      <summary>{user.name}</summary>
      <strong>{user.age}</strong> years old
      <br />
      lives in {user.address}
    </details>
  );
}

Bạn có thể viết test cho nó:

// user.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import User from "./user";

let container = null;
beforeEach(() => {
  // cài đặt một DOM element như là target cho render
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  // dọn dẹp lúc thoát
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("renders user data", async () => {
  const fakeUser = {
    name: "Joni Baez",
    age: "32",
    address: "123, Charming Avenue"
  };

  jest.spyOn(global, "fetch").mockImplementation(() =>
    Promise.resolve({
      json: () => Promise.resolve(fakeUser)
    })
  );

  // sử dụng một phiên bản async để áp dụng resolved promise
  await act(async () => {
    render(<User id="123" />, container);
  });

  expect(container.querySelector("summary").textContent).toBe(fakeUser.name);
  expect(container.querySelector("strong").textContent).toBe(fakeUser.age);
  expect(container.textContent).toContain(fakeUser.address);

  // xóa giả lập để đảm bảo test chạy tách biệt
  global.fetch.mockRestore();
});

Giả lập các module {#mocking-modules}

Một vài module không làm việc tốt trong môi trường test, hoặc không cần thiết cho test đó. Giả lập các module này bằng dummy để dễ dàng test hơn phần code của chúng ta.

Component Contact có nhúng một component third-party GoogleMap:

// map.js

import React from "react";

import { LoadScript, GoogleMap } from "react-google-maps";
export default function Map(props) {
  return (
    <LoadScript id="script-loader" googleMapsApiKey="YOUR_API_KEY">
      <GoogleMap id="example-map" center={props.center} />
    </LoadScript>
  );
}

// contact.js

import React from "react";
import Map from "./map";

function Contact(props) {
  return (
    <div>
      <address>
        Contact {props.name} via{" "}
        <a data-testid="email" href={"mailto:" + props.email}>
          email
        </a>
        or on their <a data-testid="site" href={props.site}>
          website
        </a>.
      </address>
      <Map center={props.center} />
    </div>
  );
}

Nếu không muốn load component GoogleMap trong test của chúng ta, giả lập bằng một dummy component và chạy test:

// contact.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";

import Contact from "./contact";
import MockedMap from "./map";

jest.mock("./map", () => {
  return function DummyMap(props) {
    return (
      <div data-testid="map">
        {props.center.lat}:{props.center.long}
      </div>
    );
  };
});

let container = null;
beforeEach(() => {
  // cài đặt một DOM element như là target cho render
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  // dọn dẹp lúc thoát
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("should render contact information", () => {
  const center = { lat: 0, long: 0 };
  act(() => {
    render(
      <Contact
        name="Joni Baez"
        email="test@example.com"
        site="http://test.com"
        center={center}
      />,
      container
    );
  });

  expect(
    container.querySelector("[data-testid='email']").getAttribute("href")
  ).toEqual("mailto:test@example.com");

  expect(
    container.querySelector('[data-testid="site"]').getAttribute("href")
  ).toEqual("http://test.com");

  expect(container.querySelector('[data-testid="map"]').textContent).toEqual(
    "0:0"
  );
});

Event {#events}

Chúng tôi khuyến nghị dispatch một event DOM thật trên DOM element, và đặt phần xác nhận kết quả. Xem một component Toggle:

// toggle.js

import React, { useState } from "react";

export default function Toggle(props) {
  const [state, setState] = useState(false);
  return (
    <button
      onClick={() => {
        setState(previousState => !previousState);
        props.onChange(!state);
      }}
      data-testid="toggle"
    >
      {state === true ? "Turn off" : "Turn on"}
    </button>
  );
}

Chúng ta có thể viết test cho nó:

// toggle.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";

import Toggle from "./toggle";

let container = null;
beforeEach(() => {
  // cài đặt một DOM element như là target cho render
  container = document.createElement("div");
  // container *phải* được chèn vào document để event chạy đúng.
  document.body.appendChild(container);
});

afterEach(() => {
  // dọn dẹp lúc thoát
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("changes value when clicked", () => {
  const onChange = jest.fn();
  act(() => {
    render(<Toggle onChange={onChange} />, container);
  });

  // lấy toàn bộ các element, và trigger một vài sự kiện click
  const button = document.querySelector("[data-testid=toggle]");
  expect(button.innerHTML).toBe("Turn on");

  act(() => {
    button.dispatchEvent(new MouseEvent("click", { bubbles: true }));
  });

  expect(onChange).toHaveBeenCalledTimes(1);
  expect(button.innerHTML).toBe("Turn off");

  act(() => {
    for (let i = 0; i < 5; i++) {
      button.dispatchEvent(new MouseEvent("click", { bubbles: true }));
    }
  });

  expect(onChange).toHaveBeenCalledTimes(6);
  expect(button.innerHTML).toBe("Turn on");
});

Các event DOM và thuộc tính được mô tả trong MDN. Lưu ý bạn phải truyền vào { bubbles: true } trên từng event bạn tạo cho nó để đến React listener vì React tự động truyền các event này đến document.

Lưu ý:

React Testing Library cung cấp một số hàm hỗ trợ cho việc bắn sự kiện.


Timer {#timers}

Code có thể sử dụng hàm liên quan thời gian như setTimeout để lên lịch các công việc sẽ thực hiện trong tương lai. Trong ví dụ, một cửa sổ nhiều lựa chọn đợi cho đến khi có lựa chọn, nếu sau 5 giây sẽ không thể chọn:

// card.js

import React, { useEffect } from "react";

export default function Card(props) {
  useEffect(() => {
    const timeoutID = setTimeout(() => {
      props.onSelect(null);
    }, 5000);
    return () => {
      clearTimeout(timeoutID);
    };
  }, [props.onSelect]);

  return [1, 2, 3, 4].map(choice => (
    <button
      key={choice}
      data-testid={choice}
      onClick={() => props.onSelect(choice)}
    >
      {choice}
    </button>
  ));
}

Chúng ta có thể viết test cho component bằng cách dùng Jest’s timer mocks và test sự khác nhau của state.

// card.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";

jest.useFakeTimers();

let container = null;
beforeEach(() => {
  // cài đặt một DOM element như là target cho render
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  // dọn dẹp lúc thoát
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("should select null after timing out", () => {
  const onSelect = jest.fn();
  act(() => {
    render(<Card onSelect={onSelect} />, container);
  });

  // chạy đến lúc 100ms
  act(() => {
    jest.advanceTimersByTime(100);
  });
  expect(onSelect).not.toHaveBeenCalled();

  // và chạy đến lúc 5 giây
  act(() => {
    jest.advanceTimersByTime(5000);
  });
  expect(onSelect).toHaveBeenCalledWith(null);
});

it("should cleanup on being removed", () => {
  const onSelect = jest.fn();
  act(() => {
    render(<Card onSelect={onSelect} />, container);
  });

  act(() => {
    jest.advanceTimersByTime(100);
  });
  expect(onSelect).not.toHaveBeenCalled();

  // unmount app
  act(() => {
    render(null, container);
  });

  act(() => {
    jest.advanceTimersByTime(5000);
  });
  expect(onSelect).not.toHaveBeenCalled();
});

it("should accept selections", () => {
  const onSelect = jest.fn();
  act(() => {
    render(<Card onSelect={onSelect} />, container);
  });

  act(() => {
    container
      .querySelector("[data-testid='2']")
      .dispatchEvent(new MouseEvent("click", { bubbles: true }));
  });

  expect(onSelect).toHaveBeenCalledWith(2);
});

Bạn có thể giả lập thời gian trong một test. Ở trên, chúng ta bật lên bằng cách gọi jest.useFakeTimers(). Ưu điểm chính của chúng cho ta là test không cần thực sự đợi đến 5 giây để chạy, và bạn cũng không cần thay đổi component để phục vụ việc test.


Snapshot Test {#snapshot-testing}

Framework như Jest cho chúng ta lưu “ảnh” với toMatchSnapshot / toMatchInlineSnapshot. Với chúng, bạn có thể “lưu” một kết quả render và đảm bảo một thay đổi có thể làm thay đổi của kết quả snapshot.

Trong ví dụ, chúng ta render một component và định dạng HTML đã render với thư viện pretty, trước khi lưu nó như một snapshot inline:

// hello.test.js, again

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import pretty from "pretty";

import Hello from "./hello";

let container = null;
beforeEach(() => {
  // cài đặt một DOM element như là target cho render
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  // dọn dẹp lúc thoát
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("should render a greeting", () => {
  act(() => {
    render(<Hello />, container);
  });

  expect(
    pretty(container.innerHTML)
  ).toMatchInlineSnapshot(); /* ... được tự động điền bởi jest ... */

  act(() => {
    render(<Hello name="Jenny" />, container);
  });

  expect(
    pretty(container.innerHTML)
  ).toMatchInlineSnapshot(); /* ... được tự động điền bởi jest ... */

  act(() => {
    render(<Hello name="Margaret" />, container);
  });

  expect(
    pretty(container.innerHTML)
  ).toMatchInlineSnapshot(); /* ... được tự động điền bởi jest ... */
});

Thường sẽ tốt hơn nếu chỉ rõ kết quả muốn nhận được thay vì snapshot. Những kiểu test này bao gồm phần hiện thực chi tiết để chúng dễ dàng bị fail. Chọn giả lập một vài component con có thể giúp giảm kích thước snapshot và giữ chúng dễ độc lúc review code.


Multiple Renderer {#multiple-renderers}

Trong những tình huống hiếm, bạn có thể chạy một test trên một component sử dụng multiple renderer. Lấy ví dụ, bạn có thể chạy snapshot test trên một component với react-test-renderer, bên trong đó nó dùng ReactDOM.rendertrong một child component để render một vài nội dung. Tình huống đó, bạn có thể wrap phần cập nhập với act() ứng với từng renderer

import { act as domAct } from "react-dom/test-utils";
import { act as testAct, create } from "react-test-renderer";
// ...
let root;
domAct(() => {
  testAct(() => {
    root = create(<App />);
  });
});
expect(root).toMatchSnapshot();

 

Còn thiếu gì đó? {#something-missing}

Nếu các tình huống hay gặp không được đề cập ở đây, có thể liên hệ với chúng tôi qua issue tracker cho toàn bộ tài liệu của website

TopDev via Vuilaptrinh

Xem thêm các việc làm it hấp dẫn tại đây