Home Blog Page 159

Làm việc với Redux trong ứng dụng lớn

Làm việc với Redux trong ứng dụng lớn
Bài viết được sự cho phép của tác giả Lưu Bình An
Cùng thảo luận xung quanh vấn đề ứng dụng thiên về dữ liệu lớn, rất lớn

Đây là những chỉ dẫn của AppNexus để tối ưu redux với lượng dữ liệu khủng. Mức độ bài viết khá chuyên sâu, bạn cần nắm thật vững redux, hoặc xem lại redux để hiểu rõ và nhớ lâu nội dung bài này.

  Redux vận hành như thế nào
  Tớ đã ăn hành với Redux như thế nào?

Lưu dữ liệu với chỉ mục. Truy cập bằng selector

Cách cấu trúc dữ liệu sẽ ảnh hưởng nhiều đến performance và việc tổ chức ứng dụng. Lưu dữ liệu trả về từ API theo chỉ mục (index) mang lại nhiều lợi ích. Nói nôm na, lưu theo chỉ mục tức là theo dạng object, theo cặp key-value. Tác giả Redux ( Dan Abramov ) có trình bài vấn đề này ở đây

Tưởng tượng chúng ta có một mảng object, được fetch từ REST API. Giả dụ chúng ta quyết định lưu toàn bộ xuống store như nó trả về. Khi chúng ta muốn lấy một object cụ thể nào đó? Phải loop qua toàn bộ, rồi muốn lưu danh sách các user đang được chọn và chưa được chọn?

Để tránh tình trạng này, lưu nó dạng chỉ mục, viết lại reducer trước khi lưu xuống store, cục dữ liệu mong muốn, (bạn nào sử dụng FireStore, NoSQL database sẽ hiểu liền tại sao)

{
  "usersById": {
    123: {
      id: 123,
      name: "Jane Doe",
      email: "jdoe@example.com",
      phone: "555-555-5555",
      ...
    },
    ...
  }
}

Dữ liệu được cấu trúc như thế này thì giải quyết vấn đề bằng cách nào? Ví dụ, chúng ta muốn truy cập đến một user object cụ thể

const user = state.usersById[userId]

Không cần loop, sử dụng key để lấy trực tiếp đến object mong muốn

Câu hỏi tiếp theo, ủa vậy sao render được danh sách user nếu dữ liệu tổ chức như vậy. Để làm chuyện đó, chúng ta viết một hàm (hàm như vậy gọi là selector) đơn giản bằng Object.keys()

const getUsers = ({userById}) => {
  return Object.keys(usersById).map(id => usersById[id]);
}

Thêm một hàm nữa cho việc lấy ra danh sách user với tham số truyền vào là mảng user id

const getSelectedUsers = ({ selectedUserIds, usersById }) => {
  return selectedUserIds.map((id) => usersById[id]);
}

Đừng lo chuyện phải viết quá nhiều hàm, viết như vậy càng dễ cho sau này maintain. Trường hợp cái model user có bị thay đổi đi nữa, chúng ta không cần phải update cả trăm cái view đang sử dụng dữ liệu này, đơn giản là update những hàm selector này lại, re-format dữ liệu một tí là xong.

View và edit nên có 2 state khác nhau

Những dữ liệu từ REST API trả về được xem là state chuẩn, giống hệt với database. State của ứng dụng chúng ta sẽ lưu thêm một số meta data khác cho từng user, bình thường chúng ta sẽ xử lý hết những dữ liệu trong cùng một reducer, vì nó tiện.

Nên tách việc xử lý state chuẩn trên reducer khác, nếu tập trong tất cả xử lý trong một reducer sẽ khó maintain hơn là tách ra thành nhiều reducer riêng biệt. (dùng combineReducers đấy mà)

Tại sao? Ví dụ chúng ta có 1 danh sách user, lưu dạng chỉ mục như ở trên

{
 "usersById": {
    123: {
      id: 123,
      name: "Jane Doe",
      email: "jdoe@example.com",
      phone: "555-555-5555",
      ...
    },
    ...
  }
}

Chúng ta có màn hình để user chỉnh sửa, user click nút Edit, chúng ta phải update lại state để render màn hình edit, chúng ta thêm một field mới vào object như sau

{
 "usersById": {
    123: {
      id: 123,
      name: "Jane Doe",
      email: "jdoe@example.com",
      phone: "555-555-5555",
      ...
      isEditing: true,
    },
    ...
  }
}

Submit lên trên API sau khi sửa. API trả về một object mới. Nhưng làm sau chúng ta merge lại vào store? Nếu replace toàn bộ object thì chúng ta mất cái field isEditing, tất nhiên là nếu muốn thì vẫn check và chỉ update những field mình muốn, nhưng như vậy rất tốn sức người sức máy. Tốt nhất chúng ta lưu dữ liệu từ API vào một nơi khác trong store bằng một reducer khác, không đụng gì vào nó, action cũng sẽ đơn giản hơn và dễ xử hơn

Thêm nữa, nếu user có nữa chừng ấn cancel chúng ta dễ dàng reverse lại nếu đưa edit state vào chổ khác

"usersByIds": {
  123: {
    id: 123,
    name: "Jane Doe",
    email: "jdoe@example.com",
    phone: "555-555-5555",
    ...
  },
  ...
},
"editingUsersById": {
  123: {
    id: 123,
    name: "Jane Smith",
    email: "jsmith@example.com",
    phone: "555-555-5555",
  }
}

Như vậy chúng ta vẫn có state chuẩn, để reverse, edit state nếu user click edit nữa. Nói chung, tách ra, đừng gọp chung

Xài chung state một cách khôn ngoan

Một khi ứng dụng phình ra, nhiều tính năng hơn, nên có cái reducer cho từng page, ví dụ trang hiển thị list user, lưu lại trong users reducer, một trang khác bao gồm tất cả post của user hiện tại. Tổ chức redux store như sau

{
  "usersPage": {
    "usersById": {...},
    ...
  },
  "postsPage": {
    "postsById": {...},
    ...
  }
}

Mỗi trang đảm trách state của chính nó, các file reducer có thể để cùng với các file page luôn.

Sẽ đến lúc chúng ta cần chia sẻ một vài state giữa 2 view. Cân nhắc các câu hỏi sau

  • Có bao nhiêu view hoặc reducer sẽ phụ thuộc vào dữ liệu này?
  • Mỗi trang có cần một bản sao dữ liệu không?
  • Dữ liệu thay đổi có thường xuyên không?

Ví dụ, thông tin user đang đăng nhập sẽ được hiển thị trên tất cả các trang. Tất cả trang đều dùng, thì nó sẽ không hợp lý với cách làm mỗi page một reducer. Thông tin user sẽ không đổi trên tất cả các trang (trừ khi nó vô sửa profile), vậy nên mỗi trang không cần phải có một bản sao thông tin này.

Tất cả các trang nên dùng chung một thông tin user đang login, cho nó một reducer riêng.

Trường hợp nào chuyện xài chung như vậy là ko hợp lý? Thí dụ trong các bài viết của user, nó có thêm danh sách các bình luận. Một trang hiển thị tất cả bình luận. Trang trang list post có tùy chọn hiển thị bình luận cho post đang chọn. Chúng ta có 2 trang đều phụ thuộc vào dữ liệu của bình luận. Trang list post sẽ bị thay đổi khá thường xuyên: user update, edit, delete, add post, bình luận tè le ở đó. Ở trang bình luận chỉ cho tương tác với API GET, PUT bình luận, có thể phân trang. Trang post thì ngược lại, nó chỉ lấy danh sách bình luận của chính nó. Rõ ràng, việc dùng chung bình luận giữa các view là không hợp lý. Mỗi trang nên lưu riêng một bản sao của bình luận.

Tái sử dụng các hàm xử lý reducer

Sau một thời gian viết reducer, sẽ có lúc mình thấy mấy cái function này xử lý na ná nhau, như vậy thì nên tái sử dụng nó đừng viết mới. Ví dụ nếu logic của việc load dữ liệu bài viết và bình luận là như nhau, khác cái endpoint thôi và object schema, phân trang cũng giống.

Để dùng chung reducer, cách thứ nhất, truyền vào scope bên trong payload của action. Để dễ hình dung, lấy vị dụ một trang chứa nhiều section khác nhau, tất cả đều load bất tuần tự từ các API endpoint khác nhau, để theo dõi tình trang load này bằng state trong store

const initialLoadingState = {
  usersLoading: false,
  domainsLoading: false,
  subDomainsLoading: false,
  settingsLoading: false,
};

Chúng ta có thể viết 4 reducer cho 4 cái action, thay vì như vậy nếu truyền thêm scope, một action SET_LOADING

// reducer
const loadingReducer = (state = initialLoadingState, action) => {
  const { type, payload } = action;
  if (type === SET_LOADING) {
    return Object.assign({}, state, {
      // tùy theo scope mà gán cho key tương ứng
      [`${payload.scope}Loading`]: payload.loading,
    });
  } else {
    return state;
  }
}

// Action
const setLoading = (scope, loading) => {
  return {
    type: SET_LOADING,
    payload: {
      scope,
      loading,
    },
  };
}

// ví dụ gọi dispatch
store.dispatch(setLoading('users', true));

Làm như vậy chúng ta khử được quá nhiều lần lập lại logic của reducer.

Còn về vấn đề phân trang, API có thể trả về gần giống như sau

{
  "users": ...,
  "count": 2500, // tổng số dòng
  "pageSize": 100, // số phần tử mỗi trang
  "startElement": 0, // giá trị index đầu tiên của phần từ đầu tiên
  ]
}

Để gọi dữ liệu trang tiếp theo, chúng ta có thể dùng tham số query startElement=100. Đây là cách chúng ta hiện thực reducer cho vấn đề phân trang

const initialPaginationState = {
  startElement: 0,
  pageSize: 100,
  count: 0,
};
const paginationReducerFor = (prefix) => {
  const paginationReducer = (state = initialPaginationState, action) => {
    const { type, payload } = action;
    switch (type) {
      case prefix + types.SET_PAGINATION:
        const {
          startElement,
          pageSize,
          count,
        } = payload;
        return Object.assign({}, state, {
          startElement,
          pageSize,
          count,
        });
      default:
        return state;
    }
  };
  return paginationReducer;
};

// Ví dụ
const postsReducer = combineReducers({
  postsData: postsDataReducer,
  paginationData: paginationReducerFor('POSTS_'),
});
const commentsReducer = combineReducers({
  commentsData: commentsDataReducer,
  paginationData: paginationReducerFor('COMMENTS_'),
});

// Action creator

const setPaginationFor = (prefix) => { 
  const setPagination = (response) => {
    const {
      startElement,
      pageSize,
      count,
    } = response;
    return {
      type: prefix + types.SET_PAGINATION,
      payload: {
        startElement,
        pageSize,
        count,
      },
    };
  };
  return setPagination;
};
// ví dụ sử dụng
const setPostsPagination = setPaginationFor('POSTS_');
const setCommentsPagination = setPaginationFor('COMMENTS_');

Nếu chúng ta dispatch ra 1 action là POSTS_SET_PAGINATION nó sẽ chỉ đụng đến postsReducer. Một kiểu viết hơi tricky nếu bạn nào chưa nắm được Closure function – mình có dịch trên MDN rồi, các bạn lên đó đọc lại.

Tích hợp với React

Ví dụ sử dụng selector và action creator

const ConnectedComponent = connect(
  (state) => {
    return {
      users: selectors.getCurrentUsers(state),
      editingUser: selectors.getEditingUser(state),
    };
  },
  (dispatch) => {
    const actions = {
      setPagination: actionCreatorFactories.setPaginationFor('USERS_'),
    };
    return bindActionCreators(actions, dispatch);
  })
)(UsersComponent);

Các component nó không cần quan tâm cái scope nào đang dùng với action và truy cập tới state bằng cách nào. Component giờ không cần quan tâm việc dữ liệu cụ thể bên trong state làm việc thế nào.

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

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

Xem thêm các việc làm it hồ chí minh, it hà nội, it đà nẵng hấp dẫn tại TopDev

Merge vs Rebase trong Git

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

Merge và Rebase là 2 công cụ để trộn 2 branch trong Git, mục đích sử dụng cho những tính huống khác nhau.

Một tình huống phổ biến khi sử dụng merge

  • Tạo nhánh my-new-feature từ nhánh master
  • Commit nhánh my-new-feature với một số thay đổi
  • Tạo Pull Request: my-new-feature vào master
  10 Vấn đề về Git thường gặp và Giải pháp
  Sự khác biệt giữa ‘git merge’ và ‘git rebase’ là gì?

Sau khi my-new-feature được merge vào master, chúng ta sẽ có

Đó là trường hợp lý tưởng rất ít khi xảy ra, 99.999999% là my-new-feature có vài điểm cần bổ sung sau khi review code, bug chẳng hạn, sai chính tả chẳng hạn.

  • Chúng ta bổ sung 2 commit C6 và C7 vào nhánh my-new-feature
  • Trong lúc đó, master cũng có thêm 2 commit C8C9 được merge vào bởi 2 bạn đồng nghiệp
  • Cuối cùng PR của chúng ta cũng được merge

Cái history lúc này (vẫn ok chứ không vấn đề gì)

Tuy nhiên, cũng là một tính huống rất hay gặp luôn, chúng ta ôm nhánh my-new-feature gần một tuần mà chưa xong, và chúng ta muốn có C8C9 đã được merge, và một cách chủ quan duy ý chí, dân dev chúng ta muốn có một cái history thật sạch đẹp, theo kiểu từng commit C4, C5, C6, C7,… là từng công việc rất cụ thể và độc lập, chúng ta không muốn gom hết một lượt đến cuối sprint rồi commit toàn bộ file là điều khiến người review code vô cùng mệt não.

Rebase giúp được gì ở tình huống này?

Năm 2016 Github giới thiệu một cách merge PR mới: Rebase and merge (Gitlab sẽ là Rebase front door). Nó cho phép chúng ta thực hiện một thao tác rebase trên commit PR rồi mới thực hiện việc merge. 2 thao tác này hoàn toàn độc lập và luôn đúng theo thứ tự rebase trước, merge sau, chứ ko có ngược lại.

Tương tự như nút Rebase and merge, nếu dùng command

git checkout my-new-feature
git rebase master
git checkout master
git merge my-new-feature --ff

Bằng cách đó, history lúc này là một đường thẳng tắp

Rebase không phải để thay thế merge, rebase dùng để thực hiện trên nhánh feature – private branch của chúng ta, merge thực hiện trên master – share branch với đồng nghiệp

Việc rebase -> merge như thế sẽ tránh mất đi những commit C4, C5, C6, C7 trên history của nhánh master, như khi chỉ dùng một lệnh merge. Chúng ta bê nguyên cái history của nhánh my-new-feature lên luôn.

Vấn đề thứ 2, làm sao để sync nhánh my-new-feature với master (có C8, C9)?

/* lấy những thay đổi mới */
git fetch
/* checkout nhánh chúng ta muốn sync với master */
git checkout my-new-feature
/* thực hiện sync với master */
git rebase origin/master

Giữ nhánh my-new-feature cập nhập với những thay đổi mới nhất ở nhánh master để tránh quá nhiều conflict xảy ra khi tạo PR

Nút Rebase and merge không được yêu thích lắm, vì nó tạo quá nhiều conflict, nên chúng ta vẫn thường ưu ái merge hơn.

Để có một history thẳng hàng, đầy đủ tốn khá nhiều mồ hôi chứ không dễ như ăn bánh.

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

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

Xem thêm các việc làm lập trình viên IT hấp dẫn tại TopDev

Chín thói quen xấu cần bỏ nếu muốn theo ngành CNTT

Chín thói quen xấu cần bỏ nếu muốn theo ngành CNTT.

1. Không chịu đọc tài liệu trước khi dùng:

Đây là một trong những thói quen tệ hại nhất nhưng lại thường gặp nhất. Có lẽ thói quen này nảy sinh từ tính thân thiện của “giao diện đồ hình” (GUI) khiến cho người dùng bồi đắp thói quen mò mẫm mà không cần đọc hướng dẫn nhưng cũng sử dụng được máy. Việc này không có gì đáng ngại đối với người dùng (rất) bình thường. Tuy nhiên, nếu bạn có ý định theo đuổi ngành CNTT một cách nghiêm túc thì hãy bỏ ngay thói quen tai hại này bởi vì đây là rào cản lớn nhất cho sự phát triển. Kiến thức vững chắc không phải… mò mà ra. Tài liệu hướng dẫn không phải vô cớ mà được viết ra.

2. Đọc lướt:

Đây cũng là một thói quen tệ hại và phổ biến không kém. Ngay trên những diễn đàn, với những ý kiến và chỉ dẫn bằng tiếng Việt rất cô đọng, rành mạch và dễ hiểu nhưng vẫn có quá nhiều người chỉ đọc lướt để rồi quay lại tiếp tục thắc mắc. Đây là thói quen cực kỳ nguy hiểm bởi vì nó rèn cho trí não thói quen đọc lướt. Việc này dẫn đến chỗ kiến thức thu thập một cách hời hợt, tạm bợ và chắp vá. Nếu những ý kiến bằng tiếng Việt rất cô đọng, rành mạch và dễ hiểu nhưng vẫn không chịu khó đọc kỹ và suy gẫm thì việc tham khảo, tổng hợp các sách tiếng nước ngoài gần như là vô khả thi.

3. Bắt chước mà không suy nghĩ:

Khi bắt đầu làm quen với những thứ trong ngành CNTT, cách dễ nhất là bắt chước làm theo từng bước. Nếu cứ nhắm mắt làm theo nhưng không hề suy nghĩ lý do tại sao mình làm như vậy, không thử đặt câu hỏi những gì xảy ra đằng sau những “bước” ấy thì không chóng thì chày sẽ tạo cho mình một thói quen tai hại: bắt chước không suy nghĩ không tư duy như một cỗ máy. Từ chỗ làm theo từng bước có sẵn mà không suy nghĩ đến chỗ biến thành thói quen thì khả năng nhận định và tư duy sẽ bị thui chột. Chẳng những vậy, thói quen này kiềm hãm sự thẩm thấu kiến thức xuyên qua hàng loạt những câu hỏi. Tự đặt câu hỏi chính là cách buộc trí não mình làm việc và là viên đá đầu tiên để dấn thân vào chỗ phát triển trí tụệ.

  10 thói quen của một lập trình viên thành công

4. Sợ khó:

Sợ khó tưởng chừng quá thông thường trên mọi lãnh vực nhưng trong lãnh vực CNTT thì thói quen “sợ khó” là thói quen giết chết ngay bước đầu làm quen và phát triển. Chẳng có ngành nghề thực thụ, đòi hỏi trí tuệ mà lại dễ dàng hết. Thói quen “sợ khó” biểu hiện từ chuyện đơn giản như học ngoại ngữ (để có thể tham khảo thêm tài liệu ngoại ngữ) cho đến chuyện tự mình đối diện với những khó khăn trong khi trau dồi kiến thức và kinh nghiệm. Thói quen này lâu dần ăn sâu và dẫn đến chỗ không muốn và không thể giải quyết được điều gì nếu chỉ cảm thấy có trở ngại. Nên tránh xa câu này: vạn sự khởi đầu nan, gian nan bắt đầu nản.

5. Viện cớ:

Quá trình tích lũy kiến thức luôn luôn có những khó khăn và trở ngại. Nếu chính bản thân mình không tự kỷ luật và tự nghiêm khắc thì chẳng còn ai trên đời này kỷ luật và nghiêm khắc giúp mình. Từ chỗ không kỷ luật và không nghiêm khắc, chỉ cần một thời gian rất ngắn có thể dẫn đến sự đổ vỡ, sợ hãi, chán nản và để bào chữa cho sự đổ vỡ thường là những viện cớ. Viện cớ chỉ để ẩn nấp sau cái cớ nhưng sự thật sụp đổ vẫn tồn tại. Tránh xa những câu như “nhà em nghèo”, “hoàn cảnh khó khăn”, “vì em là newbie” mà nên biết rằng vô số những người khác cũng như mình và thậm chí còn khó khăn hơn mình. Nên nhớ rằng, ngay khi dùng cái cớ để viện thì lúc ấy mình đã chính thức thất bại rồi.

6. “Đi tắt đón đầu”:

Trên đời này chẳng có loại tri thức đích thực nào hình thành từ “đi tắt” và “đón đầu” cả. “Mì ăn liền” có cái ngon của nó nhưng chính “mì ăn liền” không thể hình thành một bữa ăn thịnh soạn và đầy đủ. Tri thức đích thực cũng như thức ăn, nó cần điều độ, liều lượng và thời gian để… tiêu hoá. Tư duy và thói quen “đi tắt” luôn luôn dẫn đến những lổ hổng khủng khiếp trong kiến thức. Những lổ hổng ấy xem chừng không nhiều và không quan trọng khi kiến thức còn ít ỏi và nhu cầu công việc còn sơ khai. Tuy nhiên, một khi đối diện với những khó khăn và phức tạp trong công việc và trong đời sống thì những thứ “đi tắt đón đầu” là nguyên nhân sâu xa của những đổ vỡ và thất bại. Hãy nhớ: đừng đi tắt và đừng đón đầu bởi vì chẳng có cái đường tắt nào trong hành trình đi tìm tri thức.

7. “Nghe nói là…”

Cụm “nghe nói là…” là một cụm phổ biến đến độ chóng mặt. Bất cứ một ngành khoa học hay có liên quan đến khoa học không thể dựa trên “nghe nói” mà luôn luôn cần dựa trên các bằng chứng khoa học và những bằng chứng ấy cần chính xác và cụ thể. Chính vì có thói quen “nghe nói” mà đánh rớt những cơ hội tìm tòi và kiểm chứng; những cơ hội quý báu để trau dồi kiến thức và kinh nghiệm. Cái gì không rõ thì nên tìm tòi và đừng “nghe nói” mà phải được thấy, được phân tích và được kiểm chứng. Không bỏ được thói quen này thì cách tốt nhất đừng bén mảng gần bất cứ ngành khoa học nào vì chỉ chuốc lấy sự thất bại và lãng phí.

8. Niềm tin và hy vọng:

Trong khoa học, khi nói đến kết quả và sự kiến tạo hoặc thậm chí con đường đi đến sự kiến tạo và kết quả thì hoàn toàn không có chỗ cho “niềm tin” và “hy vọng” một cách mù mờ. Thói quen “restart” lại máy hay “restart” lại chương trình với “hy vọng” nó sẽ khắc phục sự cố đã trở thành thói quen cố hữu. Nếu không có điều kiện thay đổi nào khác thì có “restart” một triệu lần và hy vọng một triệu lần thì kết quả vẫn y hệt nhau. Đừng “tin” và đừng “hy vọng” vào sự thay đổi của kết quả nếu như chính bạn không kiểm soát và thay đổi để tạo thay đổi trong kết quả. Tất cả mọi hoạt động từ lập trình cho đến quản lý hệ thống, quản lý mạng, bảo mật, reverse engineering…. thậm chí đối với người dùng bình thường, khi kết quả không như ý, sự điều chỉnh là điều cần thiết thay vì lặp lại y hệt hành động và chỉ… hy vọng.

9. Không vì trí tuệ mà vì… “đẳng cấp”:

Lắm bạn lao vào ngành này không phải là vì trí tuệ, vì kiến thức, vì đóng góp một cái gì đó ích lợi cho xã hội mà là vì… đẳng cấp mơ hồ nào đó. Nếu tiếp tục lao vào và chọn lấy một muc tiêu mơ hồ thì sẽ không bao giờ đi đến đích được. “Đẳng cấp” là một thứ mơ hồ, vô ích và đầy cá nhân tính nhưng khi nó biến thành thói quen và mục tiêu để nhắm tới thì nó chẳng mang lại được gì ngoài sự thất bại ngay từ đầu vì hoàn toàn không có một phương hướng nào cả. Trau dồi kiến thức hoàn toàn khác với việc xoa dịu mặc cảm (“đẳng cấp”).

Bài viết gốc được đăng tải tại Hoàng Ngọc Diêu

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

Xem thêm các vị trí tuyển dụng IT hấp dẫn tại TopDev

Làm thế nào để giải quyết bất đồng ý kiến nơi công sở?

bất đồng

Những bất đồng về quan điểm, hiệu quả làm việc là chuyện không quá xa lạ với chúng ta. Tuy chúng có thể khiến môi trường làm việc căng thẳng, mệt mỏi, và gây ra sự khó chịu cho tất cả các bên nhưng không thể phủ nhận chính những mâu thuẫn cũng góp phần mang lại hiệu suất làm việc cao hơn. 

Đôi khi một chút bất đồng có thể thúc đẩy sự đột phá, tăng động lực và ngược lại vẫn có nhiều trường hợp tạo ra rào cản lớn trong các mối quan hệ. Thông qua bài viết sau đây, TopDev sẽ chia sẻ với bạn những cách thức phù hợp để giải quyết bất đồng về ý kiến nơi công sở.

Cùng điểm qua 3 chiến lược giải quyết bất đồng nơi công sở nhé!

Nhìn nhận vấn đề trên cùng góc độ

Sự đồng điệu luôn luôn quan trọng

Chúng ta đều nhận ra được nguyên nhân dẫn đến những mâu thuẫn xuất phát từ chính cách nhìn nhận của mỗi người về vấn đề rất khác nhau. Đồng thời, cái tôi cá nhân cũng là yếu tố chi phối cách bạn thể hiện quan điểm, truyền tải thông điệp của mình đến mọi người. Nếu đủ lý trí để nhận ra, hãy thử ngẫm lại mọi thứ. Chúng ta là một đội, có cơ hội đồng hành trải nghiệm cùng nhau để thực hiện các mục tiêu chung. Bạn và các đồng nghiệp có thể bất đồng nhưng đó là tranh luận, không phải “sàn đấu ngôn ngữ” để thể hiện mình quá nhiều. Hãy nhìn nhận trên cùng một góc độ để nhận thấy sự tương đồng, cùng tìm ra sự thống nhất chung về các cách thức tổ chức để thực hiện công việc hiệu quả nhất.  

bất đồng

Một lưu ý nữa là, việc cộng tác cùng những người đồng đội cần được xây dựng trên cơ sở niềm tin. Thay vì chăm chăm phản bác ý kiến của đồng nghiệp, bạn hãy thử đặt niềm tin vào giải pháp của họ hoặc đơn giản, hãy trình bày những điểm mạnh – điểm hạn chế từ ý quan điểm của đồng nghiệp. 

  Kỹ năng giao tiếp? Làm thế nào để cải thiện giao tiếp hiệu quả?

Nếu ý kiến đó có thiếu sót, bạn có thể đóng góp một chút ít về chuyên môn để làm rõ tính khả thi của vấn đề. Đó là cách xử lý khôn khéo giúp hạn chế được những bất hòa phát sinh mà không gây nên bất kỳ sự xúc phạm nào cho đồng nghiệp của bạn. Ngoài ra, điểm cộng hoàn hảo trong việc tạo ra sự tương tác hài hòa giữa bạn và đồng nghiệp chính là sử dụng cách nói “cách nói” thay vì”tôi. Phát ngôn ấy thể hiện rằng bạn đang thật sự quan tâm đến giải pháp tích cực của họ đồng thời bộc lộ mình là một người nhìn nhận các vấn đề trên lợi ích chung của doanh nghiệp.

Bạn không cần lúc nào cũng đúng!

Khi đã vào cuộc giải quyết mâu thuẫn, cuộc khẩu chiến có thể diễn biến khá phức tạp khi bạn và đồng nghiệp đều cố tỏ ra là mình đúng và thể hiện quan điểm cá nhân thay vì lắng nghe, xác định mức độ và giải quyết vấn đề.

Khá dễ hiểu vì sao bạn lại muốn thắng trong cuộc tranh luận, nhưng nếu quá đề cao việc mình phải luôn đúng, bạn đã hoàn toàn quên mất ý nghĩa của những bất đồng này.

bất đồng

Những cuộc tranh luận là để tìm ra cách giải quyết tối ưu, không phải cuộc chiến của người thắng – kẻ thua. Bất đồng là cơ hội để hai bên bày tỏ quan điểm. Để được lắng nghe, và được học hỏi. Nếu cứ chăm chăm vào giành phần thắng, chúng ta đang ngăn cản mình gặt hái những giá trị tích cực từ cuộc thảo luận. Vì thế, trước cuộc thảo luận, bạn hãy đảm bảo những điều sau đây

  • Tính trung thực: Cam kết rằng bạn và đôi phương đều có chung mong muốn tìm ra được cái cốt lõi của vấn đề thông qua những sự thật đã diễn ra.
  • Loại bỏ những hiềm khích cá nhân: Chấp nhận thảo luận để giải quyết những bất đồng tức là bạn đã đặt lợi ích công việc lên hàng đầu. Vì thế, việc gạt bỏ đi những hiềm khích, thành kiến cá nhân về nhau là điều hết sức quan trọng.
  • Cởi mở và lắng nghe lẫn nhau: Đây là giai đoạn các nên mở lòng, lắng nghe và tiếp nhận ý kiến lẫn nhau,cùng thảo luận để đi đến sự thống nhất chung. Đừng quá thể hiện rằng mình luôn đúng trong mọi trường hợp

Hãy để mọi người giúp bạn

Việc tương tác trong các lĩnh vực xã hội, đặc biệt là trong ngành nhân sự, nhiều người có xu hướng không cho rằng việc tìm kiếm sự giúp đỡ là một ý kiến hay. Họ có phần đánh đồng đó là sự yếu đuối và chủ yếu họ đi theo hướng tự bản thân gầy dựng mọi thứ từ A đến Z. Và đôi khi, việc bạn nghĩ rằng mình đang tạo gánh nặng hay khiến người khác cảm thấy bực bội sẽ vô tình làm bạn trở nên thiếu tự tin, từ đó bạn chỉ biết thụ động tiếp thu các quan điểm, mất đi những chính kiến cá nhân về tư duy phán đoán, đánh giá các giải pháp vấn đề. Hãy nhớ rằng việc bạn để mọi người giúp bạn là bạn đang tạo cơ hội cho họ bày tỏ suy nghĩ. Đây được xem là một thủ thuật, khác với việc bạn đánh đi mất quyền lợi được nêu nhận xét về các giải pháp của đồng nghiệp.

  Cách thiết lập và duy trì mối quan hệ (networking) hiệu quả

bất đồng

Nhờ giúp đỡ cũng là một cách thể hiện bạn tin tưởng vào chuyên môn và khả năng của đồng nghiệp trong việc đưa ra những giải pháp thật sự hiệu quả. Một bí quyết dành cho bạn là thay vì mở đầu cuộc thảo luận bằng giọng điệu tranh cãi, bạn hãy thể hiện mong muốn được giúp đỡ từ đồng nghiệp. Đây là cách ứng xử thông minh và đồng thời cũng thể hiện bạn đang tôn trọng và đề cao thiện chí xây dựng mối quan hệ ý nghĩa với các đồng nghiệp của mình. 

Lời kết

Có nhiều cách thức để giải quyết bất đồng nơi công sở. Tuy nhiên, tùy vào đối tượng, hoàn cảnh và mức độ của mâu thuẫn đó mà bạn cần có cách thức xử lý phù hợp. TopDev mong rằng với bài viết này, các bạn đã có những chia sẻ bổ ích có thể vận dụng vào thực tiễn giúp bạn sớm giải quyết mọi nút thắt trong doanh nghiệp của mình.

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

Xem thêm IT Jobs for Developer trên TopDev

TopDev bị chặn

Chúng tôi bắt đầu nhận được phản hồi từ rất nhiều người dùng cũng như là khách hàng về việc không thể truy cập và sử dụng topdev.vn. Qua các bước loại bỏ nguyên nhân và xác minh, chúng tôi nhận thấy domain topdev bị chặn ở tất cả các nhà mạng. Bao gồm ba nhà mạng lớn nhất (xếp theo thứ tự alphabet) là FPT, Viettel và VNPT.

Bằng nhiều công văn với các cấp từ thấp đến cao nhất ở các nhà mạng này, các nhà mạng lần lượt bỏ chặn DNS của TopDev. Tuy nhiên còn một vài nhà mạng nhỏ khác vẫn chưa truy cập được.

Đại diện của cả 3 nhà mạng đều thông báo với chúng tôi cùng một thông điệp: Domain topdev.vn bị chặn do cơ quan bên “trên” gửi xuống. Chúng tôi thực sự không hiểu, không biết chuyện gì đã xảy ra.

Chúng tôi xin khẳng định, lỗi hoàn toàn không thuộc về các nhà mạng bởi danh sách từ “trên” đưa xuống bảo chặn thì họ phải chặn.

Lỗi cũng không thuộc về cơ quan quản lý hoặc lãnh đạo của cơ quan, vì không thể đi chặn vô cớ tên miền .VN, được khai báo pháp lý rõ ràng và đóng thuế đầy đủ. Điều đáng nói ở đây, topdev.vn là một website về tuyển dụng, nội dung hoàn toàn là các việc làm IT cho giới lập trình viên, được đăng lên bởi các doanh nghiệp công nghệ đang hoạt động hợp pháp. Chúng tôi kết nối hàng nghìn việc làm tốt cũng như là giúp các công ty công nghệ giải quyết được nhu cầu nhân lực của họ.

Chỉ cần vài giây lướt qua nội dung site đã có thể khẳng định nội dung hoàn toàn sạch sẽ của topdev.vn. Để rộng đường soi sét việc TopDev bị chặn là một điều vô cùng phi lý, chúng tôi xin chia sẻ, tuy hơi dài dòng, những chứng cứ xác thực được về chúng tôi như sau:

  • Chúng tôi đã nhận được sự công nhận của chính phủ Phần Lan và Việt Nam, bộ Khoa Học Công Nghệ thông qua chương trình IPP vì những đóng góp của mình. Để vào được chương trình này là một sự kiểm duyệt khắt khe minh bạch, vui lòng tham khảo reference letter ở cuối bài do đại diện bộ KHCN phát hành.

    Reference letter từ bộ KHCN
  • Bản thân topdev.vn đã hoàn thành tất cả các thủ tục để hoạt động hợp pháp bao gồm đăng ký Bộ Công Thương tại đây http://online.gov.vn/Home/WebDetails/24669, hiển thị đầy đủ giấy phép, mã số thuế, thông tin liên hệ và điều khoản sử dụng.
  • Chúng tôi là doanh nghiệp hoạt động hoàn toàn hợp pháp, đóng thuế đầy đủ, hoàn thành mọi nghĩa vụ với pháp luật, bằng chứng là nhận được giấy phép đầu tư nước ngoài sau quá trình kiểm duyệt gắt gao của cơ quan chính phủ, cũng như kiểm toán của nhà đầu tư, và quyết toán với cơ quan thuế https://vnexpress.net/mang-tuyen-dung-it-viet-nam-nhan-dau-tu-tu-han-quoc-4056530.html
  • Chúng tôi trực tiếp mang về nhiều triệu đô la ngoại tệ cho đất nước từ vốn đầu tư nước ngoài cũng như là doanh thu trong và ngoài nước, doanh thu sạch, nội dung sạch, không sex, không phim lậu, không lừa đảo.
  • TopDev.vn là đầu mối thông tin về nhân lực ngành IT uy tín của trong và ngoài nước trong những năm gần đây, được hầu hết các báo lớn nhỏ tại Việt Nam trích dẫn lại các số liệu nghiên cứu thị trường được public tại đây https://topdev.vn/page/bao-cao-it-viet-nam, thậm chí được các báo nước ngoài và đặc biệt là BIG4 hỏi xin số liệu, hình tham khảo cuối bài.

Sẽ còn quỹ đầu tư nào dám đầu tư vào các startup Việt Nam khi thấy rằng, một ngày nào đó, bỗng dưng khoản đầu tư của họ biến thành mây khói chỉ vì startup bị chặn bởi chẳng có một nguyên nhân hay lý do gì?

Nhà đầu tư nào sẽ dám đầu tư vào startup tại VN khi thấy rằng, kể cả bạn có trong sạch, hoàn thành nghĩa vụ thuế, tạo ra nhiều việc làm, thì vẫn có nguy cơ bị chặn không rõ lý do mặc dùng tên tuổi địa chỉ công ty rõ ràng mà không có một văn bản chính thức nào.

Một doanh nghiệp kinh doanh chân chính, vượt qua và tồn tại, chưa sa thải hay giảm lương của bất kỳ nhân viên nào sau mùa dịch Corona dù 30 triệu người mất việc.

Chúng tôi tin rằng, dù cho các nhà mạng đã gỡ chặn, thì gốc rễ của vấn đề vẫn nằm ở đó. Nếu không giải quyết tận gốc, nó vẫn sẽ sẵn sàng diễn ra với chúng tôi một lần nữa, bằng một công văn khác.

Chọn sếp tốt hay công ty tốt? – Đâu là nước đi đúng đắn?

chọn sếp

Những câu chuyện chốn văn phòng có nhiều điều thú vị, đặc biệt là câu chuyện xoay quanh vấn đề sếp và công ty. Nhiều người trẻ đã đi làm họ luôn băn khoăn rằng nên lựa chọn giữa việc làm việc cho một người sếp tệ ở một công ty tốt, hay ngược lại, đồng hành cùng một vị sếp tốt trong một công ty với nhiều tồn đọng?

Vậy theo bạn đâu là phương án tốt nhất? Sự cân đo đong đếm là điều cần thiết phải có vì nó ảnh hưởng lớn đến sự nghiệp phát triển của bạn. Với bài viết sau đây, TopDev sẽ chia sẻ những quan điểm cụ thể về vấn đề này.

Nếu ví diễn tiến hành trình tương lai của bạn là một thước phim thực tế thì cách bạn lựa chọn không khác gì việc bạn đang tìm kiếm một kịch bản phù hợp cho mình. Và theo quan điểm mà bài viết này thì chắc chắn rằng dù có bất cứ điều gì xảy ra, bạn cần phải lựa chọn một công ty tốt hơn là một người sếp tốt.

Kịch bản 1: Hãy quan tâm đến những trải nghiệm, đừng chỉ đánh giá một chiều từ sếp

Ở một công ty tốt, không sớm thì muộn, các nhà lãnh đạo nhân sự tài năng sẽ nhận ra được ai có đủ tố chất thật sự để tự hoàn thiện năng lực chuyên môn, quản lý, đồng thời có thúc đẩy sự phát triển của nhân viên mình. Từ đó, những vị sếp có uy quyền và sức nặng “tạm thời” sẽ bị loại bỏ. Tuy nhiên, việc này có thể mất thời gian đến vài tháng, một năm, một vài năm hoặc hơn thế nữa.

Trong cái rủi có may, việc bạn chấp nhận làm việc và không ngừng nỗ lực dưới sự phù phiếm (nếu có) của một người sếp không tốt sẽ giúp bạn được người khác đánh giá cao. Ở đây không đơn thuần là thể hiện sự chịu đựng, việc bạn cố gắng làm việc cho dù người sếp đó luôn áp đặt nhiều thứ chứng tỏ bạn đặt lợi ích chung là mối quan tâm hàng đầu và điều này rất quan trọng.

Dù thế nào, bạn chỉ cần nghĩ đơn giản, cuộc sống này vốn dĩ không mấy dễ dàng và việc “sống chung” với một người sếp tính khí thất thường, thiếu những phẩm chất về chuyên môn cũng không phải là điều tồi tệ nhất mà bạn gặp phải.

Có thể bạn không quá xuất sắc để được thăng chức khi sự thật về người cấp trên của bạn bị phơi bày nhưng với những nỗ lực, bạn xứng đáng có được một người sếp, người quản lý tốt hơn hoặc ít ra, bạn cũng được sắp xếp đến một phòng ban phù hợp khác tốt hơn.

Một điểm cực kỳ quan trọng mà các bạn đáng phải lưu tâm đó là hãy quan tâm đến những trải nghiệm cá nhân, đừng chỉ mãi lấy sếp ra làm thước đo mức độ đồng hành. 

chọn sếp

Đối với bạn hiện tại, thì bất kỳ một trải nghiệm nào trong công ty cũng thật sự  cần thiết. Từ những người đồng nghiệp, môi trường – văn hóa doanh nghiệp, mức độ/chỉ số danh tiếng tổ chức, các phúc lợi,..Tất cả là những giá trị bạn cần quan tâm nhiều hơn. Và nếu may mắn có được một tấm vé đặt chân vào một công ty tốt như thế thì bạn nên trân trọng và cố gắng nhiều hơn. Không có điều gì là thừa cả, công ty tốt sẽ tạo điều kiện tốt để bạn phát triển khả năng của mình.

Kịch bản 2: Sếp tốt nhưng liệu có đủ hay chưa?

Ai mà không mong muốn mình có một người sếp tốt. Những người sếp tốt sẽ biết cách hỗ trợ nhân viên phát triển một cách toàn diện hơn, giúp họ có những trải nghiệm thú vị nhất trong suốt quá trình làm việc. Có nhiều người sếp lại rất tâm lý, thấu hiểu và biết cách chia sẻ với nhân viên. Họ gần gũi và không khác gì những người anh chị lớn trong gia đình. Việc may mắn được theo dõi và đồng hành cùng một người sếp tốt, bạn sẽ có động lực nhiều hơn trong công việc và cả một phần của cuộc sống. 

Thế nhưng, bạn có biết không? Nhiều vị sếp tốt cuối cùng đều ra đi. Lý do có thể là việc họ được thăng chức, nghỉ việc hoặc tương tự như bạn, vì họ giỏi và được tín nhiệm nên được chuyển đi và nhận những trách nhiệm khác cao. Có được một người sếp tốt là may mắn và biết đâu họ cũng hạnh phúc khi những nhân viên của mình có năng lực thật sự. Tuy vậy, khả năng đồng hành lâu dài chưa thể xác định vì những sự việc bất ngờ có thể xảy đến.

  Rời bỏ công việc tại các doanh nghiệp nhỏ - Nguyên nhân và giải pháp

chọn sếp

Có thể bạn không nhận ra được một sự thật là, những vị sếp tốt khi họ làm việc trong các công ty yếu kém, họ còn phải đối mặt với rất nhiều thách thức.

Một trong những thách thức lớn nhất của họ chính là “bảo vệ” những nhân viên của mình trước những vấn đề lớn hơn của công ty. Điều này đôi khi có thể trở nên nặng nề quá sức với họ, họ mệt mỏi và một khi tiếng nói trở nên yếu thế, họ nhận thấy mình chưa hoàn thành một phần trách nhiệm nên có thể sẽ quyết định dừng lại. Chúng ta khó có thể nói trước được bất kỳ điều gì vì mọi thứ xung quanh đều khó đoán. Chỉ biết rằng, nếu là một người sếp tốt, họ sẽ chịu nhiều áp lực và một mình họ không thể nào chấp vá được những “lỗ hỏng” – một đặc thù riêng mà bất cứ doanh nghiệp nào cũng luôn tồn tại.

Chung quy lại, cái cảm giác mà mỗi nhân viên nhận thấy khi được dẫn dắt bởi một người sếp tốt chỉ mang tính chất nhất thời. Những người giỏi cứ dần rời đi và bệ phóng phát triển của công ty thì vẫn vậy – vẫn tệ và yếu kém. Tất nhiên, bạn cũng chẳng thể làm gì để thay thế được điều đó cả. Lúc đó, bạn lại hoang mang, rơi vào khoảng lặng với nhiều nỗi lo khác nhau khiến bạn mắc kẹt và không biết mình phải giải quyết như thế nào. Liệu bạn sẽ tiếp tục làm việc tại một công ty với nhiều lời đồn thổi hay rời đi để thử sức chinh phục một công ty khác. Và liệu lúc đó, còn kịp không, tất cả đều bắt nguồn từ sự lựa chọn ban đầu của bạn.

Hãy là người thông minh trong việc lựa chọn những lợi ích

Xét về sự đồng hành ngắn hạn, việc bạn lựa chọn làm việc cho một vị sếp tệ, dù được làm việc ở một doanh nghiệp với quy mô phát triển tốt, thì bạn sẽ luôn rơi vào guồng quay của những áp lực. Nhưng khi xét về khả năng dài hạn, khi người sếp tốt ấy rời đi, thì ít nhất bạn vẫn có những cơ hội riêng để bản thân bước tiếp.

Tại sao “Hãy hết mình theo đuổi đam mê” là một lời khuyên tồi?

Dĩ nhiên, khi bạn làm việc cho một vị sếp tốt trong quảng thời gian ngắn, bạn sẽ rất vui, dù cho rằng mọi thứ trong công ty đang dần có xu hướng sụp đổ dần. Ngược lại, khi đồng hành dài hạn dài hạn, liệu sự vui vẻ và hạnh phúc ấy có còn tồn tại hay không? Sếp ra đi trong khi công ty đó bản chất là có nhiều “sạn” trong công tác tổ chức, quản lý đào tạo và phát triển. Sau đó, bạn lại rơi vào trạng thái phải chạy đua để tìm kiếm cơ hội hoặc phải chấp nhận từ bỏ vì không thể chịu đựng được sức ép quá lớn từ những ánh mắt soi mói, phản ánh từ dư luận về công ty của mình.

Điều đó cho thấy, sự lựa chọn của bạn quyết định những gì bạn sẽ đối mặt sau này. Hãy thật thông minh trong việc lựa chọn những lợi ích để không phải tiếc nuối bất cứ điều gì.

Lời kết

Mỗi cá nhân đều có cho những lối suy nghĩ riêng về vấn đề này. Vì vậy, hãy suy nghĩ thật thấu đáo để có những quyết định thật thông minh. Trước khi trở thành một nhân viên giỏi có thể tạo ra giá trị vật chất, tinh thần của cá nhân hay đóng góp cho sự phát triển chung cho doanh nghiệp, bạn cần có một sự quyết định sáng suốt. TopDev chúc bạn sẽ vững tin để sớm gặt hái được thành công trên con đường mà mình đã lựa chọn.

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

Xem thêm Jobs IT for Developers hàng đầu tại TopDev

Tìm hiểu về giải thuật: Một số phương pháp sắp xếp cơ bản

tim-hieu-ve-giai-thuat-mot-so-phuong-phap-sap-xep-co-ban

Bài viết được sự cho phép của BBT Tạp chí Lập trình

  Cấu trúc dữ liệu và giải thuật - Thuật toán tìm kiếm
  Kỹ Thuật Phân Tích Giải Thuật

1. Sắp xếp kiểu lựa chọn (Selection Sort)

Một trong những phương pháp đơn giản nhất để thực hiện sắp xếp một bảng khóa là dựa trên phép lựa chọn.

Nguyên tắc cơ bản của phương pháp sắp xếp này là “ở lượt thứ i(i=1,2,…,n) ta sẽ chọn trong dãy khoá Ki, Ki+1,…,Kn khoá nhỏ nhất và đổi chỗ nó với Ki ”.

Như vậy thì rõ ràng là sau j lượt, j khoá nhỏ hơn đã lần lượt ở các vị trí thứ nhất, thứ hai,…, thứ j theo đúng thứ tự sắp xếp. Ví dụ:

Sắp xếp dãy số sau theo thứ tự tăng dần: “42, 23, 74, 11, 65, 58, 94, 36, 99, 87”.

Sau đây là giải thuật:

Cho dãy khóa K gồm n phần tử. Giải thuật này thực hiện sắp xếp các phần tử của K theo thứ tự tăng dần dựa vào phép chọn phần tử nhỏ nhất trong mỗi lượt.

Procedure SELECT-SORT(K, n)
For i:=1 to n-1 do
     Begin
         M:=i;
         For j:=i+1 to n do
         If K[j]<K[m] then m:=j;
             If m!= j then
             Begin {đổi chỗ}
                 X:=K[i];
                 K[i]:=K[m];
                 K[m]:=X;
             End
     End
Return;

2. Sắp xếp chèn (Insertion Sort)

Nguyên tắc sắp xếp ở đây dựa theo kinh nghiệm của những người chơi bài. Khi có i-1 lá bài đã được sắp xếp ở trên tay, nay rút thêm lá bài thứ i nữa thì sắp xếp lại như thế nào ? Có thể so sánh lá bài mới lần lượt với lá bài thứ (i-1), thứ (i-2)… để tìm ra chỗ thích hợp và chèn nó vào chỗ đó.

Dựa trên nguyên tắc này, có thể triển khai một cách sắp xếp như sau:

Thoạt đầu K1 được coi như bảng chỉ gồm có một khoá đã sắp xếp. Xét thêm K2, so sánh nó với K1 để xác định chỗ chèn nó vào, sau đó ta sẽ có một bảng gồm 2 khoá đã được sắp xếp. Đối với K3 lại so sánh với K2, K1 và cứ tương tự như vậy với K4, K5, K6,… cuối cùng sau khi xét xong Kn thì bảng khoá đã được sắp xếp hoàn toàn.

Ta thấy ngay phương pháp này rất thuận lợi khi các khoá của dãy được đưa dần vào miền lưu trữ. Đó cũng chính là không gian nhớ dùng để sắp xếp. Có thể minh hoạ qua bảng sau:

Nhưng nếu các khóa đã có mặt ở bộ nhớ trong trước lúc sắp xếp rồi thì sao ?

Sắp xếp vẫn có thể thực hiện được ngay tại chỗ chứ không phải chuyển sang một miền sắp xếp khác. Lúc đó các khoá cũng lần lượt được xét tới và việc xác định chỗ cho khoá mới vẫn làm tương tự, chỉ có khác là: để dành chỗ cho khoá mới nghĩa là phải dịch chuyển một số khoá lùi lại sau, ta không có sẵn chỗ trống như trường hợp nói trên (vì khoá đang xét và các khoá sẽ được xét đã chiếm các vị trí đằng sau này rồi), do đó phải đưa khoá mới này ra một chỗ nhớ phụ và sẽ đưa vào vị trí thực của nó sau khi đã đẩy các khóa cần thiết lùi lại.

Sau đây là giải thuật ứng với trường hợp này:

Procedure SELECT-SORT(K, n)
For i:=1 to n-1 do
     Begin
         M:=i;
         For j:=i+1 to n do
         If K[j]<K[m] then m:=j;
             If m!= j then
             Begin {đổi chỗ}
                 X:=K[i];
                 K[i]:=K[m];
                 K[m]:=X;
             End
     End
Return;

{Trong thủ tục này người ta dùng X làm ô nhớ phụ để chứa khoá mới đang được xét. Để đảm bảo cho khoá mới trong mọi trường hợp, ngay cả khi vị trí thực của nó là vị trí đầu tiên, đều được chèn vào giữa khóa nhỏ hơn nó và khoá lớn hơn nó, ở đây đưa thêm vào một khoá giả K0, có giá trị nhỏ hơn mọi khoá của bảng, và đứng trước mọi khoá đó. Ta quy ước K0 = – }.

Procedure SELECT-SORT(K, n)
For i:=1 to n-1 do
     Begin
         M:=i;
         For j:=i+1 to n do
         If K[j]<K[m] then m:=j;
             If m!= j then
             Begin {đổi chỗ}
                 X:=K[i];
                 K[i]:=K[m];
                 K[m]:=X;
             End
     End
Return;

{xác định chỗ cho khoá mới được xét và dịch chuyển các khóa cần thiết }

Procedure SELECT-SORT(K, n)
For i:=1 to n-1 do
     Begin
         M:=i;
         For j:=i+1 to n do
         If K[j]<K[m] then m:=j;
             If m!= j then
             Begin {đổi chỗ}
                 X:=K[i];
                 K[i]:=K[m];
                 K[m]:=X;
             End
     End
Return;

{đưa X vào đúng chỗ}

Procedure SELECT-SORT(K, n)
For i:=1 to n-1 do
     Begin
         M:=i;
         For j:=i+1 to n do
         If K[j]<K[m] then m:=j;
             If m!= j then
             Begin {đổi chỗ}
                 X:=K[i];
                 K[i]:=K[m];
                 K[m]:=X;
             End
     End
Return;

Bảng ví dụ minh hoạ tương ứng với các lượt sắp xếp theo giải thuật này, tương tự như bảng đã nêu ở trên, chỉ có khác là không có chỗ nào trống trong miền sắp xếp cả, vì những chỗ đó đang chứa các khoá chưa được xét tới trong mỗi lượt (người đọc có thể tự lập ra bảng minh hoạ này).

3. Sắp xếp kiểu đổi chỗ (exchange sort)

Trong các phương pháp sắp xếp nêu trên, tuy kĩ thuật đổi chỗ đã được sử dụng, nhưng nó chưa trở thành một đặc điểm nổi bật. Bây giờ ta mới xét tới phương pháp mà việc đổi chỗ một cặp khoá kế cận, khi chúng ngược thứ tự, sẽ được thực hiện thường xuyên cho tới khi toàn bộ bảng các khóa đã được sắp xếp. Ý cơ bản có thể nêu như sau:

Bảng các khóa sẽ được duyệt từ đáy lên đỉnh. Dọc đường, nếu gặp hai khoá kế cận ngược thứ tự thì đổi chỗ chúng cho nhau. Như vậy trong lượt đầu khoá có giá trị nhỏ nhất sẽ chuyển dần lên đỉnh. Đến lượt thứ hai khoá có giá trị nhỏ thứ hai sẽ được chuyển lên vị trí thứ hai… Nếu hình dung dãy khoá được đặt thẳng đứng thì sau từng lượt sắp xếp, các giá trị khoá nhỏ sẽ nổi dần lên giống như các bọt nước nổi lên trong nồi nước đang sôi. Vì vậy phương pháp này thường được gọi bằng cái tên khá đặc trưng là: sắp xếp kiểu nổi bọt (bubble sort).

Ví dụ:

Sau đây là giải thuật:

Procedure SELECT-SORT(K, n)
For i:=1 to n-1 do
     Begin
         M:=i;
         For j:=i+1 to n do
         If K[j]<K[m] then m:=j;
             If m!= j then
             Begin {đổi chỗ}
                 X:=K[i];
                 K[i]:=K[m];
                 K[m]:=X;
             End
     End
Return;

Giải thuật này rõ ràng còn có thể cải tiến được nhiều. Chẳng hạn, xét qua ví dụ ở trên ta thấy: sau lượt thứ 3 không phải chỉ có ba khóa 11, 23, 36 vào đúng vị trí sắp xếp của nó mà là 5 khoá. Còn sau lượt thứ 4 thì tất cả các khóa đã nằm đúng vào vị trí của nó rồi. Như vậy nghĩa là năm lượt cuối không có tác dụng gì thêm cả. Từ đó có thể thấy: nếu nhớ được vị trí của khoá được đổi chỗ cuối cùng ở mỗi lượt thì có thể coi đó là giới hạn cho việc xem xét ở lượt sau. Chừng nào mà giới hạn này chính là vị trí thứ n, nghĩa là trong lượt ấy không có một phép đổi chỗ nào nữa thì sắp xếp có thể kết thúc được. Nhận xét này sẽ dẫn tới một giải thuật cải tiến hơn, chắc chắn có thể làm cho số lượt giảm đi và số lượng các phép so sánh trong mỗi lượt cũng giảm đi nữa. Người đọc hãy tự xây dựng giải thuật theo ý cải tiến này.

 (Nguồn: Cấu trúc dữ liệu và giải thuât –

Đỗ Xuân Lôi – NXB Đại học quốc gia Hà Nội).

Bài viết gốc được đăng tải tại Tạp Chí Lập Trình

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

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

SSH: Sơ lược, một số câu lệnh cơ bản (Phần 1)

SSH: Sơ lược, một số câu lệnh cơ bản (Phần 1)

Khi sử dụng một hệ điều hànhWindows, MacOS, đặc biệt là các hệ điều hành dựa trên nhân Linux như Ubuntu, CentOS,… bạn chắc hẳn đã từng sử dụng terminal để gõ các dòng lệnh cực kỳ ngầu lòi. Tới một ngày, bạn có một con server vừa mua nằm cách xa tận nửa vòng trái đất, làm thế nào bạn có thể truy cập tới và thực thi các câu lệnh, cài đặt các dịch vụ, cấu hình web server,…? Kèm theo đó là việc đảm bảo một kết nối thật an toàn ? SSH chính là thứ mà bạn đang tìm kiếm.

  Cấu hình SSH Key cho Github
  SSH: Sơ lược, một số câu lệnh cơ bản (Phần 1)
Tìm hiểu LDAP, cấu hình xác thực SSH với LDAP”]

1. Sơ lược

Secure Shell (SSH) là một giao thức mạng dùng để thiết lập kết nối mạng một cách bảo mật. SSH tạo ra một kênh kết nối được mã hóa an toàn từ một mạng không an toàn, dựa trên kiến trúc client-server, kết nối 1 SSH-client tới một SSH-server. Port mặc định đuợc sử dụng bởi SSH là 22.

Hay nói đơn giản, SSH giúp bạn có thể yên tâm truy cập tới một máy tính từ xa nhờ vào tính bảo mật của nó !

SSH ra đời như một sự thay thế cho Telnet và các giao thức điều khiển shell thiếu an toàn khác như Berkeley rsh, login và rexec . Các giao thức này trao đổi dữ liệu, mật khẩu dưới dạng plaintext, khiến chúng rất dễ bị phân tích và đánh cắp.

Điểm đặc biệt của SSH là giao thức này sử dụng các thuật toán mã hóa bất đối xứng, đối xứng và hashing để đảm bảo tính bảo mật và toàn vẹn của dữ liệu được trao đổi từ client tới server và ngược lại.

SSH có nhiều cách để xác thực một người dùng, nhưng hai cách thông dụng nhất vẫn là xác thực dựa trên mật khẩu và xác thực public-key.

Xác thực bằng mật khẩu ?

Xác thực dựa trên mật khẩu đơn giản là bạn chỉ việc sử dụng mật khẩu của user bạn tạo để truy cập, server sẽ lưu chúng, và đối chiếu với mật khẩu của bạn khi đăng nhập. Cách này thì không đủ an toàn do bạn có khả năng bị đánh cắp mật khẩu.

Còn xác thực bằng public-key?

Cách này sử dụng một cặp khóa – public-key và private-key – được tạo ra dựa trên thuật toán mã hóa public-key. Cặp khóa sau khi được tạo ra từ một máy tính, ta sẽ lấy public-key lưu vào server, khi truy cập ta sẽ dựa vào private-key lưu trên máy local và đặc tính liên quan mật thiết tới nhau của chúng để thiết lập kết nối. Kiểu xác thực này còn cho cho phép chúng ta thiết lập một kết nối an toàn một cách tự động hóa (automation).

Mô hình đơn giản cách hoạt động (Thực tế có thể phức tạp hơn)Mô hình đơn giản cách hoạt động (thực tế có thể phức tạp hơn).

Vậy thì nó hơn xác thực với mật khẩu chỗ nào ?

Quay lại khi bạn sử dụng mật khẩu, bản năng loài người của bạn trỗi dậy, trí nhớ của chúng ta là có hạn, và bạn sẽ nghĩ ra một mật khẩu sao cho dễ ghi nhớ. Và mật khẩu dễ nhớ thì khả năng cao là bạn sẽ bị đánh cắp bằng một cách nào đó (Brute-force attack, Dictionary attack,… ).

Không những vậy, mật khẩu này còn được bạn sử dụng xuyên suốt từ app này sang app khác ! Nó còn được lưu trên cả server bạn login vào, nên vẫn có thể bị đánh cắp (Man-in-the-middle attack,…). Và rất rất nhiều trường hợp cho thấy việc sử dụng mật khẩu là thiếu an toàn như thế nào.

Còn cặp khóa xác thực thì lại được tạo ra bởi máy tính, máy tính thì không như con người, máy tính sử dụng các thuật toán mã hóa cực kì phức tạp, vượt xa khả năng của con người, để sinh ra khóa. Bản thân các khóa này thì lại vừa dài (vài trăm, vài nghìn bits), vừa to… à không, vừa phức tạp và rất khó để brute-force attack. Hơn nữa, private-key mà bạn giữ không hề được gửi tới server mà bạn chỉ dùng nó để decrypt message được mã hóa từ server gửi về.

Mật khẩu có thể được sử dụng trên nhiều máy khác nhau và do bạn tự lưu trữ, còn private-key thì chỉ được lưu trên thiết bị mà bạn dùng để truy cập, dó đó bạn có thể sử dụng nhiều cặp khóa public-key private-key để truy cập vào các máy tính khác nhau, làm giảm khả năng bị đánh cắp khóa.

Ngoài ra, bạn còn có thể sử dụng thêm passphrase để tăng thêm độ bảo mật !

Các loại thuật toán mã hóa

SSH hỗ trợ nhiều loại thuật toán mã hóa public-key:

  • rsa – thuật toán được sử dụng nhiều nhất, ra đời từ năm 1977 dựa trên sự phức tạp của việc phân tích thừa số nguyên tố. Khi sử dụng nên kèm theo kích thước của khóa ít nhất là 2048 bits, tốt nhất nên là 4096 bits.
  • dsa -thuật toán dựa trên tính phức tạp của việc tính toán logarit rời rạc. Đã bị loại bỏ ở OpenSSH version 7 vì lý do bảo mật.
  • ecdsa – thuật toán dựa trên toạ độ của các điểm dựa trên đường cong Elliptic. Có thể thay thế cho RSA bởi mức an toàn và tốc độ xử lý cao hơn, kèm theo đó là việc sử dụng khoá có độ dài nhỏ hơn so với RSA. Từ đó làm tăng tốc độ xử lý một cách đáng kể. Chỉ hỗ trợ với 3 loại kích thước khóa: 256, 384, and 521 bits. Để an toàn nhất thì nên sử dụng 521 bits !
  • ed25519 – một thuật toán được thêm vào OpenSSH từ version 6.5. Là bản cải tiến của ECDSA, cung cấp bảo khả năng mật tốt hơn với hiệu suất nhanh hơn so với DSA hoặc ECDSA. Chưa thực sự phổ cập trên toàn thế giới.

2. Các câu lệnh cơ bản

Lưu ý: Các câu lệnh ta sẽ thực hiện máy tính cài hệ điều hành Linux (Ubuntu, CentOS,…).

Sau đây, chúng ta sẽ đi vào chi tiết những câu lệnh sẽ sử dụng để kết nối với một server từ xa.

Sử dụng SSH để login với password

Trên máy local của bạn:

ssh your-username@host

Ví dụ:

ssh thong@192.168.58.20

Host ở đây có thể là địa chỉ ip hoặc domain name của máy mà bạn truy cập tới

Sau đó nhập mật khẩu tương ứng với user của bạn ở host đó.

Tạo cặp khóa

Câu lệnh để tạo ra một cặp khóa xác thực SSH.

Câu lệnh đơn giản nhất, trên máy local:

ssh-keygen

Trong lúc generate, hệ thống sẽ yêu cầu bạn cung cấp passphrase. Mục đích sinh ra passphrase là để encrypt private key. Vậy khi một kẻ tấn công biết được private key của bạn cũng chưa chắc có thể sử dụng, vì nó đã bị mã hóa.

Trong thực tế, hầu hết khi tạo khóa SSH người ta thường không sử dụng thêm passphrase. Vì khi gặp vấn đề liên quan tới automation, passphrase này đâu thể đánh bằng tay mà ta phải lưu trong một kho lưu trữ hoặc là trong một đoạn script nào đó. Kết quả là bạn lại quay về xác thực bằng mật khẩu (lol !), kẻ tấn công vẫn có thể biết được passphrase của bạn !

Vậy nên để dễ dàng và thuận tiện bạn cứ nhấn Enter khi tới bước này là được !

Bạn có thể tùy chỉnh thêm các options:

ssh-keygen -f ~/key-name -t ecdsa -b 521

Trong đó:

-f là key name và nơi sẽ lưu trữ key

-t là thuật toán mã hóa để sinh khóa

-b là kích thước khóa

Thêm private-key vào SSH-agent

Trên máy local:

ssh-add /PATH/TO/YOUR/PRIVATE/KEY

ssh-add là câu lệnh để thêm SSH private-keys vào SSH authentication agent, gọi là ssh-agent để quản lý việc truy cập vào các máy tính sử dụng các khóa private. Khi bạn đã thêm khóa vào ssh-agent thì lúc truy cập bạn không cần phải khai báo thêm khóa này.

Phần public của khóa private được lưu vào ssh-agent phải được đặt trong

~/.ssh/authorized_keys (authorized_keys là một file)

ở server (Xem bước bên dưới).

Thêm public-key vào server

Bạn có thể sử dụng một trong hai câu lệnh sau:

Cách 1:

cat ~/.ssh/your-key.pub | ssh username@host "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys"

Cách 2:

ssh-copy-id -i ~/.ssh/your-key.pub username@host

Ngoài ra bạn có thể truy cập trực tiếp vào máy bằng password trước và sau đó thêm key bằng cơm (mkdir, touch, rồi sau đó cat, vim…).

Vậy là bạn đã có thể truy cập vào server thành công sử dụng xác thực khóa trên SSH !

Bạn có cũng thể thêm nhiều key khác nhau vào authorized_keys sử dụng các câu lệnh tương tự như trên.

Test nào:

ssh your-username@host

Loại bỏ xác thực bằng mật khẩu trên server (Chỉ sử dụng SSH keys)

Lưu ý: Chỉ làm bước này sau khi bạn đã cấu hình và thực hiện ssh bằng khóa.

Để tốt nhất và an toàn nhất thì sau khi thêm key vào server, bạn nên loại bỏ xác thực SSH bằng mật khẩu. Lý do thì tôi đã đề cập như trên !

Trên server:

sudo nano /etc/ssh/sshd_config

Tìm dòng

PermitRootLogin yes

Sửa lại thành

PermitRootLogin no

Lưu lại và exit. Sau đó trên terminal:

sudo systemctl restart sshd

3. Kết

Vậy là ta đã đi qua sơ lược về SSH và các câu lệnh cơ bản để thiết lập kết nối giữa hai máy tính. Ở phần sau tôi sẽ đưa ra một vài ví dụ thực tế để bạn có cái nhìn rõ hơn về nó. Cảm ơn các bạn đã theo dõi !

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

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

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

Giới thiệu Fetch API trong Javascript

Giới thiệu Fetch API trong Javascript và cú pháp sử dụng

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

fetch() cho phép tạo một network request tương tự như XMLHttpRequest(XHR). Sự khác nhau chủ yếu là Fetch hoạt động theo Promises, cho phép viết gọn ràng, dễ nhớ hơn là XHR. API Fetch có trong window.fetch() giờ đã được hỗ trợ phổ biến, bạn không cần polyfill gì đâu, vĩnh biệt IE. Tạm biệt XMLHttpRequest và cách viết dài dòng, giờ đây ta đã có fetch API.

Fetch API là gì?

Fetch API là một giao diện lập trình trong JavaScript, được sử dụng để thực hiện các yêu cầu HTTP đến các máy chủ web. Đây là một phần của tiêu chuẩn JavaScript ES6 và được tích hợp trực tiếp trong các trình duyệt hiện đại. Fetch JS cung cấp một cách thức đơn giản và mạnh mẽ để thực hiện các thao tác lấy dữ liệu (fetch) từ các nguồn bên ngoài, ví dụ như lấy dữ liệu từ máy chủ hoặc API, đồng thời hỗ trợ các thao tác như GET, POST, PUT, DELETE, v.v.

Điểm mạnh của Fetch fetch Javascript API là nó thay thế cho công nghệ cũ hơn là XMLHttpRequest và cung cấp cú pháp hứa hẹn (Promise-based), giúp lập trình viên quản lý các yêu cầu bất đồng bộ (asynchronous) dễ dàng hơn.

Fetch trong JS sử dụng cú pháp Promise, có nghĩa là nó không chặn (non-blocking), cho phép xử lý các yêu cầu và phản hồi mà không cần phải dừng các thao tác khác của chương trình. Khi thực hiện một yêu cầu HTTP thông qua Fetch API, nó trả về một Promise, và bạn có thể xử lý kết quả của yêu cầu bằng cách sử dụng .then().catch().

Một câu request network bằng fetch

fetch('/api/some-url')
  .then(
    function(response) {
      if (response.status !== 200) {
        console.log('Lỗi, mã lỗi ' + response.status);
        return;
      }
      // parse response data
      response.json().then(data => {
        console.log(data);
      })
    }
  )
  .catch(err => {
    console.log('Error :-S', err)
  });

Response của câu fetch() là một đối tượng Stream, nghĩa là khi chúng ta gọi phương thức json(), một Promise được trả về, vì quá trình đọc stream sẽ diễn ra bất đồng bộ.

  9+ cách để xóa một phần tử ra khỏi JavaScript Array

Response MetaData

Bên cạnh các dữ liệu chúng ta có thể truy cập như trong ví dụ trên, chúng ta có thể truy cập đến các meta data khác

fetch('/api/some-url')
  .then(response => {
    console.log(response.headers.get('Content-Type'));
    console.log(response.headers.get('Date'));

    console.log(response.status);
    console.log(response.statusText);
    console.log(response.type);
    console.log(response.url)
  })

response.type

Khi chúng ta tạo một fetch request, response trả về sẽ chứa response.type, với một trong 3 giá trị: basiccorsopaque.

Nó cho biết resource này đến từ đâu, cho chúng ta biết cách chúng ta nên đối xử với object trả về

  • Nếu request lên cùng một nhà (ứng dụng host trên server A gửi request lên API trên server A), response.type sẽ là basic, không có bất kỳ giới hạn việc xem các thông tin trên response.
  • Nếu request dạng CORS, nhà em ở Hồ Chí Mình, em quen bạn gái Hà Nội, type trả về sẽ là cors.cors, lúc đó bên trong header chúng ta chỉ được phép truy cập đến Cache-ControlContent-LanguageContent-TypeExpiresLast-Modified và Pragma
  • Type opaque cho các request tạo ra khác nhà, và thằng server nó không chấp nhận dạng request CORS, ba má cấm chú quen gái Hà Nội, nghĩa là không trả về dữ liệu, không xem được status của request, chia tay tình yêu.

Để khai báo 1 fetch request chỉ resolve khi thỏa điều kiện mode

  • same-origin: các request nhà kế bên sẽ trả về reject
  • cors: cho phép nhà khác nếu header trả về cũng là cors
  • cors-with-forced-preflight luôn thực hiện kiểm tra preflight. Là trước khi gửi đi, để đảm bảo an toàn, tạo một request dùng phương thức OPTIONS để kiểm tra độ an toàn, (nhà anh có điều kiện ko mà đòi quen bạn gái tận Hà Nội xa xôi)
  • no-cors tạo một request không cùng nhà, không trả về CORS

Để khai báo mode

fetch('http://some-site.com/cors-enabled/some.json', {mode, 'cors'})
  .then(function(response) {
    return response.text();
  })
  .then(function(text) {
    console.log('Request successful', text);
  })
  .catch(function(error) {
    log('Request failed', error)
  });
  Crawl dữ liệu bằng JavaScript ngay trên trình duyệt

Liên kết Promise

Một trong những tính năng hay (và sinh ra rắc rối) của Promise là cho phép mắc-xích-các-Promise lại với nhau.

Khi làm việc với JSON API, chúng ta quan tâm đến status và parse JSON trả về, để đơn giản hóa, đưa phần xử lý kiểm tra status và parse này ra hàm riêng. Chúng ta chỉ lo xử lý kết quả cuối cùng và trường hợp có lỗi

function status(response) {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
  } else {
    return Promise.reject(new Error(response.statusText))
  }
}
function json(response) {
  return response.json()
}

fetch('')
  .then(status)
  .then(json)
  .then(data => {
    console.log('Request succeeded with JSON response', data);
  })
  .catch(function(error) {
    console.log('Request failed', error);
  });

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

POST Request

Set giá trị method và body để tạo một POST request

fetch(url, {
  method: 'POST',
  headers: {
    "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
  },
  body: 'foo=bar&lorem=ipsum'
})
.then(json)
.then(data => {
  console.log('Request succeeded with JSON response', data);
})
.catch(error => {
  console.log('Request failed', error);
  });
})

Gửi lên dữ liệu dạng JSON

var data = {username: 'example'};

fetch(url, {
  method: 'POST', 
  body: JSON.stringify(data), 
  headers:{
    'Content-Type': 'application/json'
  }
})
.then(res => res.json())
.then(response => console.log('Success:', JSON.stringify(response)))
.catch(error => console.error('Error:', error))

Gửi thông tin xác thực với Fetch

Để gửi kèm thông tin xác thực cookie (user là ai), chúng ta truyền tham số credentials: include

fetch(url, {
  credentials: 'include'
})

Nếu muốn gửi credentials khi request URL là cùng nhà*, truyền giá trị same-origin

fetch(url, {
  crendentials: 'same-origin'
})Không cho

Không cho trình duyệt gửi thông tin xác thực, dùng omit

fetch(url, {
  crendentials: 'omit'
})

Upload file

Sử dụng cùng <input type='file' />FormData()

var formData = new FormData();
var fileField = document.querySelector("input[type='file']");

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/profile/avatar', {
  method: 'PUT',
  body: formData
})
.then(response => response.json())
.then(response => console.log('Success:', JSON.stringify(response)));
.catch(error => console.error('Error:', error))

Upload nhiều file

var formData = new FormData();
var photos = document.querySelector("input[type='file'][multiple]");

formData.append('title', 'My Vegas Vacation');
formData.append('photos', photos.files);

fetch('https://example.com/posts', {
  method: 'POST',
  body: formData
})
.then(response => response.json())
.then(response => console.log('Success:', JSON.stringify(response)))
.catch(error => console.error('Error:', error));

Kết luận

Fetch API là một công cụ mạnh mẽ và hiện đại giúp các lập trình viên thực hiện các yêu cầu HTTP một cách đơn giản và hiệu quả. Với cú pháp dựa trên Promise, Fetch API Javascript giúp mã nguồn trở nên dễ đọc hơn và phù hợp với các ứng dụng JavaScript hiện đại. Khi xây dựng các ứng dụng web, đặc biệt là khi làm việc với API hoặc các dịch vụ bên ngoài, Fetch API JS là lựa chọn tối ưu để lấy dữ liệu hoặc gửi dữ liệu từ/đến máy chủ.

Link bài viết gốc

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

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

Debug và khắc phục lỗi hiển thị ký tự Unicode của ứng dụng Web

Debug và khắc phục lỗi hiển thị ký tự Unicode của ứng dụng Web

Bài viết được sự cho phép của BBT Tạp chí Lập trình

Tác giả: Nguyễn Bình Sơn

Bài viết này ngầm định rằng bạn biết “bảng mã” nghĩa là gì, và bạn quen thuộc với các thành phần của mô hình ứng dụng web cũng như mô hình trình diễn MVC. Mặc dù mã ở đây được trình bày dưới dạng thức của ngôn ngữ Java, framework Spring MVC và database MySQL, các vấn đề được nhắc đến là chung, các khái niệm được nhắc đến là phổ biến, và các cách giải quyết là tổng quát. Hãy cùng tìm hiểu về Debug và khắc phục lỗi hiển thị ký tự Unicode của ứng dụng Web nhé!

  Debug và khắc phục lỗi hiển thị ký tự Unicode của ứng dụng Web
”]

  Một số tip debug trong Laravel

Model mang dữ liệu Unicode nhưng View thì không

Bạn có model như thế này.

Nhưng bạn nhận được kết quả như thế này:

Là vì View của bạn có nội dung như thế này:

CREATE DATABASE IF NOT EXISTS cms CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Bạn cần làm cho view engine render cho bạn một view mà trong đó các ký tự unicode của template và model được bảo toàn. Ví dụ sau đây là cấu hình cho view engine Thymeleaf.

@Bean
public ViewResolver viewResolver() {
    // ...
    viewResolver.setCharacterEncoding("UTF-8");
    return viewResolver;
}

Template mang ký tự Unicode nhưng View thì không

Bạn có view template như thế này:

<tr>
  <td>Tên</td>
  <td>
    <input type="text" name="name" th:value="${customer.name}">
  </td>
</tr>
<tr>
  <td>Email</td>
  <td>
    <input type="text" name="email" th:value="${customer.email}">
  </td>
</tr>
<tr>
  <td>Địa chỉ</td>
  <td>
    <input type="text" name="address" th:value="${customer.address}">
  </td>
</tr>

Nhưng bạn nhận được view như thế này:

Nếu bạn đã xử lý vấn đề encoding trong lúc render như ở trên rồi, thì có thể vấn đề là do template resorver đã dùng một encoding khác để đọc tài liệu template. Hãy cấu hình lại cho cả template resolver nữa.

@Bean
public ITemplateResolver templateResolver() {
    // ...
    templateResolver.setCharacterEncoding("UTF-8");
    return templateResolver;
}

Không thể chuyển tải ký tự Unicode qua tầng giao vận

Giả sử cần submit form sau:

Tầng giao vận TCP/IP không quan tâm với bảng mã, nó đơn giản và vận chuyển gói tin, từng byte một. Phần lớn web server, trừ khi là web server do bạn tự viết, decode các bytes này để có các parametter theo lối như thể rằng các bytes đó trước kia là ký tự của bảng mã ISO-8859–1. Nếu bạn nhìn các ký tự đã được decode ra dưới con mắt của bảng mã UTF-8 (trớ trêu rằng, phần lớn các ngôn ngữ lập trình làm như thế), bạn sẽ nhận được kết quả không mong muốn.

Cách xử lý luôn luôn là encode chuỗi ký tự ngược lại thành dòng bytes (theo bảng mã ISO-8859–1, tất nhiên), và sau đó decode lại theo bảng mã UTF-8. Cho dù là thủ công như sau:

public String createCustomer(Customer customer) {
    try {
        byte[] bytes = customer.getName().getBytes("ISO-8859-1");
        String decodedName = new String(bytes, "UTF-8");
        customer.setName(decodedName);
        customerService.save(customer);
        return "redirect:/customers";
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
        return "500";
    }
}

… hay tự động, bằng cách sử dụng filter, như sau chẳng hạn:

public class AppInit extends AbstractAnnotationConfigDispatcherServletInitializer {
    // ...    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        FilterRegistration.Dynamic filterRegistration =
            servletContext.addFilter("endcoding-filter", new CharacterEncodingFilter());
        filterRegistration.setInitParameter("encoding", "UTF-8");
        filterRegistration.setInitParameter("forceEncoding", "true");
        
        //make sure encodingFilter is matched most first, by "false" arg
        filterRegistration.addMappingForUrlPatterns(null, false, "/*");
        
        super.onStartup(servletContext);
    }
}

Entity mang thông tin Unicode, vào đến Database thì mất dấu

Bạn có form như thế này:

Entity ngon nghẻ như thế này:

Vào tới database thì loạn cào cào hết cả:

Hầu hết các client của các dbms, khi kết nối tới dbms server, đều chọn mặc định một bảng mã để làm việc với nhau. “Mặc định” này đôi khi là một giá trị cố định, đôi khi là lấy dynamic. Dù là trường hợp nào đi chăng nữa, đôi khi charset đó hoàn toàn khác với charset được dùng cho Schema/Table/Column. Nghĩa là “anh nói tiếng của anh, nhưng tôi hiểu theo tiếng của tôi”.

Cách xử lý luôn là cố định lại bảng mã dùng trong cuộc nói chuyện giữa client và dbms server:

CREATE DATABASE IF NOT EXISTS cms CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Và đồng thời (điều này rất quan trọng), sử dụng cùng một bảng mã đó cho Schema/Table/Column:

CREATE DATABASE IF NOT EXISTS cms CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Nếu database cùng với dữ liệu đã tồn tại, theo một bảng mã khác, cách xử lý luôn là sử dụng một kỹ thuật nào đó được dbms hỗ trợ để convert dữ liệu từ bảng mã cũ sang bảng mã mong muốn.

Bài viết gốc được đăng tải tại Tạp Chí Lập Trình

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

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

Tìm việc làm IT mùa dịch không hề khó như bạn nghĩ

tam viec lam

Tình hình doanh nghiệp

Tim viec lam mùa dịch là việc không hề dễ dàng vì dịch đã làm ảnh hưởng nặng nề rất nhiều đến việc kinh doanh của nhiều công ty, dù là thời điểm trong dịch hay hiện giờ thì cũng có khá nhiều công ty trong và ngoài nước cắt giảm nhân sự. Chính vì vậy, cơ hội tìm kiếm việc làm của người lao động thông thường khá khó khăn. Tuy nhiên đối với những ngành nghề đặc thù liên quan đến công nghệ như IT thì lại không bị ảnh hưởng quá nhiều, và các kỹ sư kỹ thuật có tỷ lệ % bị cắt giảm nhân sự rất thấp.

Doanh nghiệp về du lịch là bị ảnh hưởng nhiều nhất, rất nhiều công ty du lịch đóng cửa vì không có khách du lịch. Ngoài ra các ngày liên quan đến sản xuất như may mặc ở các xí nghiệp cũng bị ảnh hưởng nặng nề và buộc phải cắt giảm rất nhiều nhân viên

Dù là sau dịch như hiện tại thì nhu cầu tuyển dụng cũng không nhiều, rất nhiều công ty phải đóng băng hoặc phá sản, hàng quán thì trả mặt bằng, bán nhà trong khi đó nhu cầu tim viec lam thì lại tăng… Theo thống kê, gần 8 triệu người mất việc, nghỉ việc luân phiên do Covid-19, trong đó số người bị ảnh hưởng do giảm thu nhập lên tới hơn 17 triệu người. 

tim viec lam

Đó là tình hình không mấy lạc quan đối với những xí nghiệp sản xuất, tiêu thụ thông thường. Còn các ngành công nghiệp hiện đại có liên quan đến công nghệ thì vẫn có nhu cầu cấp thiết đối với nguồn lực công nghệ, từ duy trì cơ sở hạ tầng đám mây cho đến thiết kế các trang thương mại điện tử. Việc hạn chế những nơi đông người và tăng cường mua sắm trực tuyến khiến các doanh nghiệp chuyển đổi hành vi mua sắm tại điểm bán sang đẩy mạnh phát triển các nền tảng mua sắm online, dẫn đến nhu cầu tuyển dụng IT, lập trình viên hay các vị trí liên quan đến phát triển thương mại điện tử tăng cao. 

Lấy ví dụ như các vị trí lập trình viên như System Engineer, Developer hay Technical Product… đều đóng vai trò quan trọng trong các công ty Startup, công ty thiên về công nghệ, sàn thương mại điện tử, các vị trí đó được đăng tuyển khá nhiều để có thể hỗ trợ, điều chỉnh cho tình hình mua sắm tại nhà như hiện nay. 

Các kỹ sư công nghệ trở nên quan trọng hơn bao giờ hết khi Việt Nam và thế giới đang đối đầu với dịch bệnh. Ngành lập trình viên vẫn hoạt động và phát triển rất tốt như chưa hề có dịch Covid.

Không những thế, thậm chí trong tình hình dịch bệnh như thế này nhưng ngành lập trình vẫn còn đang thiếu hụt nhân sự vì nhu cầu nhân sự, nhu cầu tim viec lam của các lập trình viên thì vẫn có. Tuy nhiên, trong tình hình dịch ứng viên lại ngại các cuộc phỏng vấn trực tiếp sẽ là nguyên nhân gây lây nhiễm dịch bệnh. 

tim viec lam

Giải pháp tim viec lam mùa dịch

Vì để tránh tình trạng thiếu hụt nhân sự công nghệ, nhiều doanh nghiệp đưa ra giải pháp phỏng vấn online để có thể dễ dàng tiếp cận ứng viên hơn trong tình hình dịch bệnh như hiện nay. Ứng viên chỉ cần gửi so yeu ly lich kèm cv tiếng Việt hoặc cv tieng anh tùy vào yêu cầu của công ty ứng tuyển. Sau đó, nếu cv của ứng viên có thể lọt vào mắt xanh của nhà tuyển dụng nào đó thì sẽ nhận được mail mời phỏng vấn online.

Điểm thuận lợi của phỏng vấn online là có thể giúp cả đôi bên tiết kiệm thời gian, thời gian hẹn phỏng vấn cũng sẽ linh hoạt hơn vì không cần phải chuẩn bị quá nhiều thứ như khi gặp mặt nhau. Bên cạnh đó, ứng viên cũng có thể chủ động chọn lựa vị trí thuận tiện nhất để có cuộc phỏng vấn với nhà tuyển dụng mà không phải đi ra ngoài, chỉ cần nơi phỏng vấn của ứng viên chọn có wifi mạnh, backgound sáng sủa, gọn gàng.

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

  CEO TopDev ra mắt công nghệ AI/Computer Vision trợ giúp kết nối doanh nghiệp với người mất việc vì Covid

  Các công ty công nghệ Việt Nam và thách thức trong việc chuyển mình thời Covid - 19

  Tái tuyển dụng IT hiệu quả? Thách thức và cơ hội cho các công ty hậu Covid-19

 

Xem thêm Top IT Jobs lương cao trên TopDev

Mẫu CV xin việc Free cho các bạn IT

cv online

Với nhiều năm hoạt động trong ngành tuyển dụng lập trình viên TopDev tự tin rằng có thể cung cấp cho các bạn IT mẫu cv online chuẩn đẹp, free và đặc biệt là phù hợp nhu cầu ứng tuyển các vị trí khác nhau trong ngành lập trình.

Tại sao cần viết cv online?

Để có thể sở hữu được một bản cv online hoàn hảo, đầu tiên bạn cần hiểu được CV là gì và nó có ý nghĩa gì trong việc xin việc làm. Có thể hiểu nôm na CV là một bản tóm tắt sơ lược những công việc mà bạn đã làm qua, một bản CV luôn thể hiện được cho người đọc kinh nghiệm làm việc, học vấn, trình độ cá nhân…

Trước đây, khi công nghệ và nhu cầu tìm việc qua mạng còn ít thì những bản CV thường được viết tay hoặc đánh máy trên nền giấy A4. Sau này hiện đại hơn 1 tí thì có thể tạo CV bằng powerpoint, tuy nhiên khi bạn tự tạo CV thì sẽ mất khá nhiều thời gian vào nó, đặc biệt là những CV tự tạo đôi khi sẽ không được thẩm mỹ vì trình độ powerpoint của bạn có hạn, điều này sẽ gây ra ấn tượng xấu với nhà tuyển dụng.

Chính vì thấu hiểu những điều đó nên hiện nay có rất nhiều nền tảng cung cấp mẫu CV trên mạng với mẫu cv đa dạng, chỉnh chu hơn để hỗ trợ ứng viên tìm việc. Tuy nhiên điểm yếu của các mẫu CV này là sau khi hoàn thành xong phải tải về máy ngay, đôi khi muốn chỉnh sửa hay bổ sung một chi tiết nào đó thì phải làm lại từ đầu và tải lại thêm lần nữa, điều này rất phiền phức và làm rối tung folder của bạn.

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

  5 mẹo và mẫu CV IT để gây ấn tượng với nhà tuyển dụng!

Mẫu CV online ra đời để giảm thiểu những khó khăn đó. Làm cv online sẽ giảm thiểu được tình trạng thường xuyên phải tải file về và việc sửa chữa cũng đơn giản tiện dụng hơn vì bạn chỉ cần điền theo form mẫu có sẵn cho từng ngành nghề. Bên cạnh đó các mẫu template CV onl hiện nay cũng rất đẹp, được các trang web đầu tư chỉnh chu, đặc biệt là các mẫu CV này hoàn toàn free ở 1 số mẫu và web. Ví dụ ở topcv thì free những mẫu cơ bản, cao cấp, đa dạng nhóm ngành…còn những mẫu chuyên nghiệp thì chỉ có tài khoản VIP được nạp tiền vào thì mới sử dụng được. Ở TopDev thì được tạo CV online free hoàn toàn và điểm mạnh ở TopDev là những mẫu CV này được thiết dựa theo form đơn xin việc chuyên biệt dành cho dân lập trình.

  Tổng hợp một số mẫu CV đẹp mắt dành cho lập trình viên

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

Viết cv online thu hút nhà tuyển dụng

Khi bắt tay vào viết CV onl thì bạn nên chú ý điền đầy đủ thông tin cần điền bên phần thông tin cá nhân như hình ảnh, số điện thoại, mail… Tiếp theo mới bắt đầu điền các thông tin về kinh nghiệm làm việc có liên quan đến vị trí đang muốn ứng tuyển. Tất cả bạn chỉ cần điền theo form có sẵn, thêm bớt các thông tin sao cho CV của bạn trở nên thật hoàn hảo nhé! Ngoài ra nếu bạn muốn ver cv bằng tiếng anh thì hãy chọn từ đầu nhé.

Ngoài ra bạn cũng có thể điều chỉnh font chữ, size trên đó, vì vây hãy chắc chắn rằng font chữ của bạn đồng nhất. Bạn cũng nên kiểm tra lại lỗi chính tả trong CV online của bạn, đừng liệt kê quá nhiều sở thích cá nhân vì nhà tuyển dụng sẽ không quan tâm đến những điều đó và nếu liệt ra ra quá nhiều sở thích không cần thiết thì sẽ làm CV của bạn rối nùi.

Một mẹo nhỏ nữa để làm cv online của bạn được nhà tuyển dụng lựa chọn là bạn nên trung thực, đừng vì quá tham mà liệt kê những kỹ năng cũng như kiến thức bạn chưa bao giờ làm qua. Đúng là 1 nhân viên giỏi, biết nhiều thứ thì ai cũng thích nhưng nổ quá cái gì cũng biết từ sale đến design, marketing, lập trình cái nào cũng biết thì sẽ được đặt dấu chấm hỏi! Nguy hiểm hơn là nhà tuyển dụng còn nghĩ bạn viết dối chỉ để được phỏng vấn nên sẽ loại bạn ngay từ vòng lọc CV Online.

Tạo CV IT online, chuẩn ATS miễn phí trên TopDev

Thêm một điều nên nhớ nữa là bạn không nên dùng 1 CV cho tất cả ngành nghề, mỗi ngành khác nhau sẽ cần những kinh nghiệm công việc khác nhau. Ví dụ như mẫu CV dành cho dân lập trình viên rất đặc thù vì cần phải có thiết kế phù hợp để show ra được những đường link dự án sao cho thật bắt mắt, điều đó khác hẳn so với những CV của dân sale hay marketing….

Nhìn chung, việc viết cv không còn là nỗi ám ảnh khi đi xin việc nữa vì hiện nay ứng viên đã được hỗ trợ rất nhiều từ các template CV online của các trang web tìm việc. Việc duy nhất các ứng viên cần quan tâm đó là chọn cho mình 1 mẫu CV phù hợp với ngành và trang bị kiến thức thật vững vàng trước khi đi gặp nhà tuyển dụng.

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

Xem thêm Top Việc làm IT trên TopDev

6 ví dụ để bạn yêu luôn observable

6-vi-du-de-ban-yeu-luon-observable
Bài viết được sự cho phép của tác giả Lưu Bình An

Thêm những lý do để dụ dỗ bạn xài Observable

Observable mình dịch ra tiếng việt thế này cho bạn dễ hình dung. Một khi bạn bật chế độ observable với một đứa con gái nào đó, là bạn đang trong giai đoạn bị nó ám ảnh, nhất cử nhất động của nó bạn điều để ý, nó hắc xì bạn cũng biết, một tuần nó mặc mấy bộ đồ bạn cũng biết. Chỉ cần nghe tiếng bước chân là bạn biết được hôm nay nó mang đôi dép gì (mức độ này hơi kinh khủng lắm rồi) là bạn có những phản xạ vô điều kiện bộc phát nơi cửa miệng “Chiều nay trời mưa nhe em, mang dép lào đi cho chuẩn”. Phản xạ này là gọi là subscription

Rồi quay lại với vấn đề kỹ thuật, bài này không giải thích rõ Observable pattern, các khái niệm chính của nó, nếu muốn bạn đọc lại bài này trước đây có viết rồi, như cái tựa bài viết nó spoil hết cái nội dung rồi “Ví dụ để thấy tại sao chúng ta nên bật chế độ Observable với một em gái nào đó”

Thần chú mình muốn bạn thuộc lầu

Lập trình Reactive là làm việc với luồng dữ liệu bất đồng bộ

Lại phải giải thích câu này chút, Nếu những gì diễn ra trên ứng dụng đang xảy ra một cách bất đồng bộ, khả năng rất cao là Observable sẽ giúp ích cho cuộc sống của anh em chúng ta bớt khổ hơn.

Có nhiều cách làm và thư viện handle vụ luồng dữ liệu bất đồng bộ này, tuy nhiên, Observable có gì mà cool, sắp được chuẩn hóa và đưa vào ECMAScript. Thư viện RxJS đang được sử dụng rộng rãi và quá ngon rồi.

Rồi vô luôn ví dụ nhe

Handle các event bằng Observable

Chúng ta có 1 button, khi button này click tạo ra một chuỗi ngẫu nhiên. Viết bằng cả 2 cách javascript thuần, và sử dụng RxJS

const button = document.querySelector('button');
const output = document.querySelector('output');

button.addEventListener('click', e => {
    output.textContent = Math.random().toString(36).slice(2);
})

Bằng RxJS nè

const button = document.querySelector('button');
const output = document.querySelector('output');

Rx.Observable
    .fromEvent(button, 'click')
    .subscribe(() => {
        output.textContent = Math.random().toString(36).slice(3);
    })

Nó dài hơn khi viết javascript thuần mà man 😂. Chi mà phức tạp vậy? Đúng luôn, nhưng giờ thêm yêu cầu này vào thì sao: Ở mỗi lần click đến bội số của 3 ( 3,6,9,12,…) thì mới random một string mới

Rx.Observable
    .fromEvent(button, 'click')
    .bufferCount(3) // một dòng duy nhất
    .subscribe(() => {
        output.textContent = Math.random().toString(36).slice(3);
    })

Vậy bạn viết JS thôi thì sao, khỏi nói cũng biết nó sẽ dài dòng hơn.

Operator, operator

Trong ví dụ trên, .bufferCount *đã cho thấy sức mạnh vượt trội** so với cách thông thường. Có thể nói thế này, chúng ta xài Observable này là vì những gì chúng ta làm được bằng operator. Trong thư viện RxJS nó cả tá Operator tha hồ mà chơi.

Một ví dụ khác, cũng là vụ random string ở trên, mà giờ chỉ muốn random khi nó là một cú triple click (một phát 3 nháy, không phải double click nhoa)

const click$ = Rx.Observable.fromEvent(button, 'click');

click$.Observable
    .bufferWhen(() => {
        click$.delay(400);
        // kkhoảng thời gian của một cú 3 click
    })
    .filter(events => events.length >= 3)
    .subscribe(() => {
        output.textContent = Math.random().toString(36).slice(3);
    })

DỊch ra ngôn ngữ con người nó sẽ như thế này, trong khoảng thời gian là 400ms, trong đám event được emit (tụi này được đưa vào mảng events), nếu mảng này lớn hơn hoặc bằng 3, thực hiện đống việc đã đăng ký bên dưới subscribe

Bạn đã bắt đầu yêu Observable chưa? Mình đã khoái khoái rồi đó.

Ai có thể là Observable

Đơn giản, bất kể già trẻ lớn bé, trai gái, nếu RxJS có hàm ( khi nãy là .fromEvent) thì chúng ta có thể biến nó thành đối tượng bị theo dõi liên tục.

Observable cho các HTTP request

Một sức mạnh siêu nhiên khác của RxJS: xử lý mấy em HTTP request rất mượt mà

Ví dụ, fetching một danh sách album và render.

const albumsApiUrl = 'https://jsonplaceholder.typicode.com/albums';

Rx.Observable
    .ajax(albumsApiUrl)
    .subscribe(
        res => console.log(res),
        err => console.log(err)
    )

Trộn chung với ví dụ ở trên, chúng ta làm cái tính năng awsome sau, click là có danh sách album ngẫu nhiên

Rx.Observable
    .fromEvent(button, 'click')
    .flatMap(getAlbums)
    .subscribe(
        render,
        err => console.error(err)
    )

function getAlbums() {
    const userId = Math.round(Math.random() * 10);
    return Rx.Observable.ajax(
        `https://jsonplaceholder.typicode.com/albums?userId=${userId}`
        )
}

Ví dụ trên có sử dụng operator flatMap, 1 trong những operator siêu kinh điển của RxJS, cho phép merge 2 mảng kiểu Observable thành 1

Nếu chúng ta click liên tục trong thời gian ngắn, là có vấn đề, re-render nhiều lần, chúng ta cũng ko xác định được request nào được resolve cuối cùng. Cụ thể là thế này, có thể thằng xuất phát trước lại về đích sau cùng, chuyện của network ai mà biết được thời điểm đó nó download film gì làm chậm mạng, thằng request sau có khi lại về đích trước, như vậy thì dùng cục response lúc nào để render, mình muốn response của thằng request cuối cùng.

Bạn muốn, trong công cuộc tán gái, đứa nào ở lại đến giây phút cuối cùng là đứa chiến thắng, bạn sẽ dẹp luôn những đứa nào thả thính trước đó? Ví von như vậy cũng chưa chuẩn, phải là đứa nào đến sau cùng thì dữ lại, dẹp mẹ tụi tới trước (thế này thì bất công vl mấy bạn)

RxJS làm được chuyện đó không? Có chứ, mọi thứ đã có operator, chuyển qua dùng switchMap, sẽ chỉ có response cuối cùng được render, mấy request trước đó sẽ bị cancel hết

Rx.Observable
    .fromEvent(button, 'click')
    .switchMap(getAlbums)
    .subscribe(
        render,
        err => console.error(err)
    )

Kết hợp các Observable

Một use case khác mà chúng ta gặp hoài. Chức năng filter hoạt động như sau: cho tụi user nhập vào user id bằng <input />, và chọn thể loại âm nhạc nó muốn bằng <select />. Điều quan trọng là chỉ tạo request mới khi cả 2 giá trị trong đó điều có dữ liệu, và re-render khi một trong 2 giá trị này bị thay đổi.

Tạo Observable trước nhé

const id$ = Rx.Observable
    .fromEvent(input, 'input')
    .map(e => e.target.value)

const resource$ = Rx.Observable
    .fromEvent(select, 'change')
    .map(e => e.target.value)

Chúng ta phải hợp thể 2 thằng trên vào một, để khi một trong 2 thằng có thay đổi chúng ta lấy được giá trị sau cùng của cả 2. mọi thứ đã có operator, nhiều lắm, ở đây dùng combineLatest

Rx.Observable
    .combineLatest(id$, resource$)
    .switchMap(getResource)
    .subscribe(render)

Kết

Bạn đã thấy sử dụng Observable thú vị dường nào chưa? Nếu câu trả lời là “Có ❤️, trọn đời yêu em”, bạn hãy nhào vô document của nó để nghiên cứu chuyên sâu hơn.

Nếu câu trả lời là “No 💩, anh éo care mấy đứa ạ”. Thì bạn cũng nên bớt bớt đối xử tệ với nó đi, vì trong tương lai JS sẽ đưa nào vào như một object chính thức luôn, không chạy đằng trời được đâu các bạn ạ.

Hy vọng anh em hôm nay đã học thêm được cái gì đó thú vị, hẹn gặp lại anh em vào một viết thú vị khác.

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

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

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

Dùng Px, Em hay Rem để viết media query

Dùng Px, Em hay Rem để viết media query
Bài viết được sự cho phép của tác giả Lưu Bình An
Khi viết media query, bạn có bao giờ thắc mắc nên dùng đơn vị nào: px, em hay rem? Các bạn đọc bài này mình mặc định bạn đã phân biệt được sự khác nhau giữa em và rem

Setup để thử nghiệm

Chúng ta sẽ dùng 3 div tô 3 màu khác nhau để thấy được kết quả dễ dàng

.pixel { background: red; }
.em { background: green; }
.rem { background: blue; }

Chúng ta viết query min-width trên 3 element này, thay đổi opacity để thấy được khi nào css này được áp dụng

.pixel {
	background: red;
	@media (min-width: 400px) {
		opacity: .5
	}
}

Chúng ta sẽ đặt font-size cho html là 16px = 1em = 1rem. Như vậy 400px = 25em = 25rem

.em {
  background: green;  
  @media (min-width: 25em) {
    opacity: 0.5
  }
}

.rem {
  background: blue;  
  @media (min-width: 25rem) {
    opacity: 0.5
  }
}

Tất cả đều được trigger ở chính xác kích thước 400px

Dùng Px, Em hay Rem để viết media query

  Tối ưu hóa việc thực thi query trong MYSQL
  Những gợi ý sử dụng jQuery bạn nên biết

Thay đổi font size ở HTML

Trường hợp phổ biến nhất chúng ta hay gặp là thay đổi font-size trên HTML

html {
	font-size: 200%;
}

Khi thay đổi font-size lên 200%, nghĩa là 1em = 1rem = 32px. Nếu sự thay đổi font-size này tác động lên em và rem, chúng ta sẽ thấy 2 element bên dưới trigger ở 800px

Kết quả trên Chrome, Firefox và IE11, cả 3 thằng đều trigger ở 400px

Dùng Px, Em hay Rem để viết media query

Nếu chạy đúng, em và rem không nên bị ảnh hưởng bởi thay đổi font-size trên HTML, nó chỉ được phụ thuộc vào font-size mặc định của trình duyệt.

Tuy nhiên, trên Safari lại cho kết quả không như mong đợi, nó trigger ở 800px

Dùng Px, Em hay Rem để viết media query

Với kết quả này chúng ta có thể bỏ qua việc sử dụng rem với media query vì nó ko đảm bảo chạy đúng trên mọi trình duyệt.

Tuy nhiên, các thí nghiệm bên dưới chúng ta vẫn đưa rem vào cho vui!

User gọi Zoom In

Đây cũng là tình huống thường thấy, chữ quá nhỏ, user có xu hướng zoom to lên xem.

Nguyên nhân chính có đơn vị em là vì các trình duyệt cũ không thể update giá trị pixel khi user gọi zoom

Trên Chrome, Firefox và IE, pxemrem xảy ra cùng lúc

Dùng Px, Em hay Rem để viết media query

Và đương nhiên Safari tiếp tục không giống ai

Dùng Px, Em hay Rem để viết media query

Điều này có nghĩa là, đơn vị pixel không đúng trên mọi browser, bạn nên dừng sử dụng pixel trong câu media query, trừ khi bạn thuộc kiểu sống bất chấp sự tồn tại của safari

User thay đổi giá trị font mặc định của trình duyệt

Rất nhiều developer tin rằng user chả đứa nào thay đổi font size mặc định của trình duyệt, tìm cái thiết đặt này trong trình duyệt đã đủ khó khăn làm user nản chí

Tuy nhiên đó là niềm tin không có căn cứ, không có dữ liệu chứng minh được, user vẫn có thể google tìm cách thay đổi font size mặc định của trình duyệt, đặc biệt các thanh niên bị cận như mình.

Nếu chưa biết cách thay đổi font-size mặc định của trình duyệt, mình chỉ cho

Chrome: Settings > Show advanced settings > Web content Firefox: preferences > content > fonts and colors IE: page > text-size

Safari thì mình chưa biết thay đổi font-size của nó bằng cách nào.

Dùng Px, Em hay Rem để viết media query

Như có thể thấy, câu query dùng px trigger sớm hơn em và rem

Cái này không phải là bug, vì px là đơn vị chính xác đến từng pixel!!! Nó chỉ cần biết kích thước độ rộng màn hình, không liên quan họ hàng gì tới font-size

Ngược lại 2 đơn vị rem và em phụ thuộc hoàn toàn vào font-size của trình duyệt

Chúng ta phải nói lời chia tay với pixel khi viết media query

Ví dụ bạn setup để màn hình dưới 600px có một cột, ngược lại có 2 cột. Mọi thứ đẹp nếu font size là 16px, tuy nhiên nếu user đổi font size thành 20px và xem ở màn hình 650px. Đơn vị rem và em sẽ cho ra giao diện 1 cột, trong khi pixel vẫn lì lợm 2 cột bất chấp cái font chữ giờ đã to đùng.

Kết luận

Đơn vị cho kết quả chấp nhận được ở mọi tình huống là em

Nếu từng thắc mắc khi đang dùng một thư viện nào đó, như bootstrap, tại sao nó lại dùng đơn vị em trong câu media query, thì giờ bạn đã có câu trả lời rồi đó.

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

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

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

Những ứng dụng tuyệt vời của Renderless component trong Vue

Những ứng dụng tuyệt vời của Renderless component trong Vue
Bài viết được sự cho phép của tác giả Lưu Bình An
Để tái sử dụng component trong Vue mà không biết tới slot thì quá thiếu sót. Một vài ví dụ để bạn sử dụng slot nhiều hơn.
  Tạo web siêu dễ với VuePress và Github Pages
  Viết Unit Test cho Vue component cho người mới bắt đầu

Slot

Slot trong vue là dạng “đặt gạch” trong component, sau này khi sử dụng ta có thể đưa nội dung khác vào những vị trí đã đặt gạch

Vue không chỉ có thể đặt một mà đặt nhiều gạch, số lượng tùy thích, viên gạch đó được Vue gọi tên là slot

<!-- mother.vue --> Mẹ đặt gạch 2 chỗ header và body cho con nha
<template>
  <div class="card">
    <div class="card-header">
      <slot name="header"></slot>
    </div>
    <div class="card-body">
      <slot name="body"></slot>
    </div>
  </div>
</template>
<!-- con.vue: Cho con dùng 2 chỗ header và body bằng nội dung mới nhé-->
<mother>
	<template #header>
	  <h1>Special Features</h1>
	</template>
	<template #body>
		<div>
		    <h5>Fish and Chips</h5>
		    <p>Super delicious tbh.</p>
		</div>
	</template>
</mother>

Đây là những viên gạch có đặt tên <slot name="header"/>, có một viên gạch không cần đặt tên, chỉ cần <slot />, khi đó component ném gạch sẽ được viết

<mother>
  Toàn bộ phần này nằm trong slot không đặt tên
</mother>

Slot scope

Đề truyền dữ liệu từ mẹ sang con, chúng ta bind dữ liệu muốn truyền qua slot <slot :ten-bien="du-lieu"/>

<!-- mother.vue -->
<template>
  <div class="card">
    <div class="card-header">
      <slot name="header" :close="close"></slot>
    </div>
    <div class="card-body">
      <slot name="body"></slot>
    </div>
  </div>
</template>

Component con sẽ nhận dữ liệu thông qua từ khóa slot-scope

<!-- con.vue-->
<mother>
	<template #header slot-scope="{close}">
		<h1>{{ close ? ‘Closed’ : ‘Open’ }}</h1>
	</template>
	<template #body>
		<div slot="body">
		  <h5>Fish and Chips</h5>
		  <p>Super delicious tbh.</p>
		</div>
	</template>
</mother>

Sử dụng làm modal

Lấy structure của bootstrap nhé, chúng ta sẽ cho Modal component có 3 chỗ có thể thay đổi là

  • <slot name="header" />
  • <slot name="body" />
  • <slot name="footer" />
<!-- my-modal.vue -->
<template>
<div class="modal" tabindex="-1" role="dialog">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <slot name="header"></slot>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">×</span>
        </button>
      </div>
      <div class="modal-body">
        <slot name="body"></slot>
      </div>
      <div class="modal-footer">
        <slot name="footer"></slot>
      </div>
    </div>
  </div>
</div>
</template>

Với 3 cục gạch đã đặt sẵn trong my-modal.vue,

<template>
  <my-modal>
    <!-- kiểu viết tắt của `v-slot` là # -->
    <template #header>
      <h5>Awesome Interruption!</h5>
    </template>
    <template #body>
      <p>Say something....</p>
    </template>
    <template #footer>
      <em>Ahihi</em>
    </template>
  </my-modal>
</template>

Bổ sung thêm một tính năng năng nữa cho component <my-modal/>, mặc định khi click nút close, nó sẽ gọi đến hàm close bên trong component <my-modal/>, chúng ta dùng scope slot để thằng con có thể truyền vào một function khác, đè lên function nhận được (tức không gọi hàm closeModal bên trong <my-modal/>)

<!-- my-modal.vue -->
<template>
<div class="modal" tabindex="-1" role="dialog">
	//...
	<div class="modal-footer">
	  <slot name="footer" :closeModal="closeModal"></slot>
	</div>
	//...
</div>
</template>

<script>
export default {
  //...
  methods: {
	closeModal () {	}
  }
}
</script>

Chúng ta có thể truyền hàm closeModal khác

<template #footer="{closeModal}">
	<button @click="closeModal">
		I'm here
	</button>
</template>

Composing Component (siêu nhân hợp thể)

Sự kết hợp của nhiều component thành một component mới, dữ dội hơn, như siêu nhân GAO, được gọi là hợp thể component. Từ khoa học của nó là Composing Components

Tại sao cần hợp thể?

Component được sinh ra là để chúng ta nhai đi nhai lại

<!-- BaseButton.vue -->
<template>
  <button class="nice-button" type="button">
    {{ text }}
  </button>
</template>

<script>
export default {
  props: ['text']
}
</script>

Nhu cầu thêm mắm, bớt muối cho một món phải nhai đi nhai lại là có. Giả dụ ta đã có sẵn một component BaseIcon để làm chuyện hiển thị icon, giờ cái Button cùng muốn thêm chút icon cho đời tươi mới, chúng ta xào chung hai món lại để nhai

<template>
  <button class="nice-button" type="button">
    <BaseIcon v-if="leftIcon" :icon="leftIcon"/>
    {{ text }}
    <BaseIcon v-if="rightIcon" :icon="rightIcon"/>
  </button>
</template>

<script>
export default {
  props: ['text', 'leftIcon', 'rightIcon']
}
</script>

Trong đó chúng ta đã thêm hai điều kiện để đặt icon nằm bên trái hay bên phải. Component Button bây giờ cũng được thêm 2 prop rightIconleftIcon.

Thí dụ như có thêm yêu cầu đưa cái spinner vào trong button, khi nào đang loading thì hiện cái spinner này

<template>
  <button class="nice-button" type="button">
    <BaseSpinner v-if="isLoading"/>
    <template v-else>
      <BaseIcon v-if="leftIcon" :icon="leftIcon"/>
      {{ text }}
      <BaseIcon v-if="rightIcon" :icon="rightIcon"/>
    </template>
  </button>
</template>

Chỉ mới thêm chút đường sữa thôi, mà món ăn sắp thành cháo heo thập cẩm khó nuốt. Với nhiều gia vị được yêu cầu bỏ vào của bọn khách hàng không biết gì về nấu nướng. Món ngon bây giờ thành đặc sản mà đứa nào đó muốn nấu tiếp, sửa đổi do quá mặn, thì cũng bất lực vì không biết đã thêm quá nhiều muối hay nhiều nước mắm.

Một cách nấu khác với slot

Trong cuốn bí kíp 100 cách nấu ngon của Vue.js, nó cho chúng ta cách làm khác gọi là slot

<template>
  <button class="nice-button" type="button">
    <slot/>
  </button>
</template>

Đây là kiểu món tao đã nấu xong, nếu mày muốn bỏ thêm bất cứ gì đó, dùng món này tao đã nấu xong như một nguyên liệu cho món mới, chứ tao ko sửa lại món của tao.

<BaseButton>
  Submit
  <BaseIcon icon="arrow-right"/>
</BaseButton>

Việc này tạo ra một tranh cãi trong giới đầu bếp, nếu tao phải phục vụ món ăn đó cho một trăm thực khách, tức là tao phải lặp lại việc order 100 gia vị Button về rồi tự nấu thêm 100 lần nữa, vi phạm nguyên tắc nghề nghiệp DRY (DON’T REPEAT YOURSELF) của tao. Đúng là vi phạm nguyên tắc nghề, nhưng nó lại đảm bảo KISS (Keep it simple stupid – NGU NHẤT CÓ THỂ)

<template>
  <BaseButton @click="sendForm">
    <BaseSpinner v-if="isLoading"/>
    <template v-else>
      Submit
      <BaseIcon icon="arrow-right"/>
    </template>
  </BaseButton>
</template>

Renderless Component (người vô hình chỉ mang logic)

Một component trong Vue có thể không render bất cứ gì cả, nếu chỉ đơn giản là chứa các function, thực hiện logic tính toán. Nó giống như cái ổ điện, nó chỉ biết làm một chuyện là cấp điện cho chui cắm, còn cái chui đó nối tới bóng đèn, máy tính, tủ lạnh, máy quạt là chuyện của người cắm điện.

<template>
	<transition name="fade" v-bind="$attrs" v-on="$listeners">
		<slot></slot>
	</transition>
</template>

<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
}
</style>

Một component để làm transition như trên không render html, không quan tâm cái nó hiển thị là gì. Nó chỉ bổ sung hiệu ứng fade cho component.

Có thể viết nó ở dạng này, truyền thêm ít dữ liệu cho đứa con

Vue.component('renderless-component-example', {
	render() {
		return this.$scopedSlots.default({
			exampleProp: 'vuilaptrinh.com'
		})
	}
})

Tại sao dùng Renderless component mà không dùng mixin hay directive?

Để tái sử dụng code trong Vue, ngoài renderless component ra còn có thể dùng Mixin hoặc 1 custom Directive. Cả 3 cách đều có thể dùng thay thế cho nhau được, vấn đề là mức độ tường minh của 3 thằng khác nhau, thằng directive là kém tường minh nhất, với mixin và renderless component chúng ta import độc lập trên từng component muốn xài, xem như bằng nhau. Mixin thì bị vấn đề, nếu khai báo một số state, hoặc hàm bên trong mixin, sau đó trộn chung với 1 component, không rõ ràng trực quan như là dùng một renderless component với prop

Ứng dụng renderless component làm ổ cắm mạng cấp dữ liệu internet

Chúng ta thường xuyên làm việc này, tạo một network request lúc component mounted() để lấy dữ liệu, chúng ta tạo ra một component chuyên làm nhiệm vụ này

// src/components/DataList.js
import axios from 'axios'

export default {
	props: {
		baseUrl: {
			type: String,
			default: 'https://jsonplaceholder.typicode.com'
		},
		endpoint: {
			type: String,
			required: true
		},
		// dùng để giới hạn kết quả query
		filter: {
			type: Object
		}
	},
	data() {
		return {
			api: axios.create({ baseURL: this.baseUrl }),
			data: null,
			error: null,
			loading: false,
		}
	},
	watch: {
		// load dữ liệu từ endpoint trong lần render đầu tiên
		// và khi giá trị filter thay đổi
		filter: {
			immediate: true,
			handler: 'load'
		}
	},
	methods: {
		// xử lý tất cả type: post, update, delete, get, put bằng một hàm duy nhất
		async query(type, ...params) {
			// tránh việc gọi load liên tục
			if (this.loading) return;

			this.loading = true;

			try	{
				const response = await this.api[type](...params);
				this.data = response.data;
				this.error = null;
				this.$emit('success', response);
			} catch (error) {
				this.data = null;
				this.error = error.response;
				this.$emit('error', error);
			}
			this.loading = false;
		},
		load() {
			return this.query('get', this.endpoint, { params: this.filter })
		}
	},
	render() {
		// đưa toàn bộ dữ liệu và phương thức xuống con thông qua slot scope
		return this.$scopedSlots.default({
			data: this.data,
			error: this.error,
			load: this.load,
			loading: this.loading
		})
	}
}

Renderless component <DataList/> sẽ làm nhiệm vụ fetch dữ liệu từ một API, endpoint được cung cấp qua prop, sau đó nó sẽ quăng kết quả, lỗi, trạng thái hiện tại về cho component con

<data-list endpoint="posts">
	<div slot-scope="{ data: posts, error, laoding }">
		<span v-if="loading">Loading...</span>
		<span v-else-if="error">Error while fetching data!</span>
		<ul v-else>
			<li v-for="post in posts" :key="post.id">
				<h3>{{ post.title }}</h3>
				<p>{{ post.body }}</p>
			</li>
		</ul>
	</div>
<data-list>

Thêm phần phân trang, chúng ta dùng giá trị filter

<data-list endpoint="posts" :filter="{ page }">
	<div slot-scope="{ data: posts, error, laoding }">
		<span v-if="loading">Loading...</span>
		<span v-else-if="error">Error while fetching data!</span>
		<ul v-else>
			<li v-for="post in posts" :key="post.id">
				<h3>{{ post.title }}</h3>
				<p>{{ post.body }}</p>
			</li>
		</ul>
		<button @click="page = 1">1</button>
    <button @click="page = 2">2</button>
    <button @click="page = 3">3</button>
	</div>
<data-list>

Bởi vì đã setup watch trên giá trị filter, nên khi thay đổi giá trị của page, nó sẽ gọi lại hàm load()

Xong phần Rờ trong CRUD, giờ đến Create-Update-Delete

Chúng ta cần tách phần code ở trên ra thành một mixin để sử dụng vào component detail. Chúng ta sẽ đưa nó vào một mixin là query.js

// src/components/mixins/query.js
import axios from 'axios';

export default {
  props: {
    baseUrl: {
      type: String,
      // The JSONPlaceholder API is a fake API
      // basically a Lorem Ipsum JSON API.
      default: `https://jsonplaceholder.typicode.com`,
    },
    endpoint: {
      type: String,
      required: true,
    },
  },
  data() {
    return {
      // Create a new axios instance.
      // See: https://github.com/axios/axios#creating-an-instance
      api: axios.create({ baseURL: this.baseUrl }),
      data: null,
      error: null,
      loading: false,
    };
  },
  methods: {
    // The `query` method will handle
    // different query types for us.
    async query(type, ...params) {
      // If we're currently loading content
      // we don't submit an additional request.
      if (this.loading) return;

      this.loading = true;
      try {
        const response = await this.api[type](...params);
        this.data = response.data;
        this.error = null;
        this.$emit(`success`, response);
      } catch (error) {
        this.data = null;
        this.error = error.response;
        this.$emit(`error`, error);
      }
      this.loading = false;
    },
  },
};

Với component để thêm-xóa-sửa

// src/components/DataModel.js
import queryMixin from './mixins/query'

export default {
	mixins: [queryMixin],
	props: {
		entity: {
			type: Object,
		},
		id: {
			type: [Number, String]
		}
	},
	data() {
		return {
			data: this.entity || null,
		}
	},
	create() {
		// nếu có id và chưa có dữ liệu, chúng ta gọi fetch dữ liệu
		if (this.id && !this.data) this.find();
	},
	methods: {
		create(data) {
			return this.query('post', this.endpoint, data)
		},
		destroy() {
			return this.query('delete', `${this.endpoint}/${this.id}`)
		},
		find() {
      return this.query('get', `${this.endpoint}/${this.id}`);
    },
    update(data) {
      return this.query('patch', `${this.endpoint}/${this.id}`, data);
    },
	},
	render() {
		return this.$scopedSlots.default({
			create: this.create,
			data: this.data,
			destroy: this.destroy,
			loading: this.loading,
			update: this.update
		})
	}
}

Create

<data-model endpoint="posts">
	<div slot-scope="{ data: post, loading, create }"></div>
	<span v-if="loading">Loading...</span>
  <template v-if="post">
    <h3>{{ post.title }}</h3>
    <p>{{ post.body }}</p>
  </template>
  <form @submit.prevent="create(newPost)">
  	<label>
      Title: <input v-model="newPost.title">
    </label>
    <label>
      Body: <input v-model="newPost.body">
    </label>
    <button :disabled="loading">
      <template v-if="loading">Loading...</template>
      <template v-else>Create</template>
    </button>
  </form>
</data-model>

Với component để Create, chúng ta không truyền dữ liệu và id, vì thế nó không gọi API để lấy dữ liệu, nếu sau khi tạo, chúng ta sẽ nhận giá trị post và id.

Cập nhập, giống hệt với tạo ở trên, có điều có thêm id

<data-model endpoint="posts" :id="1">
  <div slot-scope="{ data: post, loading, update }">
    <span v-if="loading">Loading...</span>
    <template v-if="post">
      <h3>{{ post.title }}</h3>
      <p>{{ post.body }}</p>
    </template>

    <form @submit.prevent="update(post);">
      <label>
        Title: <input v-model="post.title">
      </label>
      <label>
        Body: <input v-model="post.body">
      </label>
      <button :disabled="loading">
        <template v-if="loading">Loading...</template>
        <template v-else>Update</template>
      </button>
    </form>
  </div>
</data-model>

Delete

<data-model endpoint="posts" :id="1" @success="deleted = true">
  <div slot-scope="{ delete }">
    <p v-if="deleted">
      The post was successfully deleted.
    </p>
    <button :disabled="loading">
      <template v-if="loading">Loading...</template>
      <template v-else>Delete</template>
    </button>
  </div>
</data-model>

Tài liệu tham khảo

Building Renderless Components to Handle CRUD Operations in Vue.js

Vue.js Pattern for Async Requests: Using Renderless Components

Renderless Components in Vue.js

Composing Components in Vue.js

Using Slots In Vue.js

Creating Reusable Transitions in Vue

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

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

Xem thêm các việc làm VueJS lương cao hấp dẫn tại TopDev

“Sống sót” qua những áp lực trong tuyển dụng IT – lập trình, có dễ không?

ngành lập trình

Phải chăng bạn từng nghe luyên thuyên trong những cuộc trò chuyện của người lớn, những cuộc thoại của đám bạn rằng ngành lập trình là một ngành dễ thở, công việc thì thú vị, không những ngồi máy lạnh mà lại còn được hưởng mức lương cao. 

Liệu lối nghĩ mặc định ấy có đúng không và sự thật thế nào? Bạn có biết rằng ngành IT cũng giống như những ngành khác, đều có hai mặt của nó, việc phải thu nạp một lượng kiến thức khủng, gặp đủ thứ áp lực khác nhau là những mặt tối mà chỉ được chính dân lập trình giấu nhẹm đằng sau bề nổi hào nhoáng kia mà thôi.

Hôm nay, TopDev sẽ bật mí cho các bạn vô vàn những áp lực xoay quanh ngành lập trình IT khi đã quyết định dấn thân theo đuổi nó. Tất nhiên, bài viết sẽ chia sẻ thêm những cách thức giúp thích ứng và sống sót khi những áp lực bủa vây.

Áp lực từ các mối quan hệ: Sếp và đồng nghiệp

Không phải ai xa lạ, áp lực lớn nhất mà bạn phải đối mặt chính là từ sếp của bạn. Điều này khá dễ hiểu vì sếp là người trực tiếp phân bổ các đầu công việc mà bạn phải thực hiện, quản lý đồng thời hỗ trợ bạn tự đánh giá hiệu suất làm việc của bản thân.

Nếu không may mắn gặp phải một người sếp khó tính, những áp lực bạn phải đối mặt có thể là gì? 

  • Task quá khó nằm ngoài năng lực chuyên môn 
  • Buộc làm thêm ngoài giờ để đảm bảo tiến độ công việc, kết quả cho ra hoàn hảo nhất có thể
  • Làm khó làm dễ nhân viên, đánh giá thấp việc xem xét tăng lương làm giảm cơ hội cho việc phát triển sự nghiệp

Chính vì thế, điều bạn cần làm trường hợp này là cho sếp một khoảng riêng, giao tiếp trao đổi ý kiến và quan trọng là nắm bắt tâm lý và thấu hiểu được sếp của bạn. Sếp của bạn có rất nhiều việc cần giải quyết và đôi khi việc bộc phát những cơn giận chỉ là cách thúc đẩy bạn và team làm việc hiệu quả hơn. Vì thế đừng giận và đừng áp lực về công việc. 

Ngoài ra, khi giao tiếp, bạn nên cẩn trọng với từng phát ngôn của mình. Nếu có những phát sinh hay các tồn đọng tạo ra nguy cơ dẫn đến sự tiêu cực, bạn đừng vội đi sâu vào vấn đề chuyên môn hay kỹ thuật. Thay vào đó, hãy khai thác những khía cạnh mà sếp quan tâm đồng thời cũng là cách giúp tiếp cận cơn giận từ sếp.

  Kỹ năng giao tiếp? Làm thế nào để cải thiện giao tiếp hiệu quả?

ngành lập trình

  • Diễn tiến thực hiện dự án như thế nào?
  • Làm sao để cải thiện năng suất làm việc hiệu quả?
  • Sếp có lời khuyên nào cho em trong việc giải quyết vấn đề này không?

Việc thấu hiểu sếp còn phụ thuộc vào nhiều yếu tố, vì nếu đối với một người sếp không tốt, lúc nào cũng kiếm chuyện với nhân viên thậm chí không tuân thủ các quy định thì việc bạn nhảy việc, bắt đầu sự nghiệp  tại một công ty khác là điều sớm muộn.

Đồng nghiệp là người sẽ tiếp xúc, làm việc với bạn nhiều hơn là sếp. Vì thế, những áp lục xảy ra có liên quan đến đồng nghiệp cũng khiến bạn đau đầu đó.

Bạn có thể sẽ gặp phải những người đồng nghiệp khoác lác về năng lực hoặc quá tự kiêu về bản thân có thể khiến bạn cảm thấy phiền phức. Khi làm việc nhóm đòi hỏi sự bàn luận về dự án và trình bày những ý kiến đóng góp, đồng nghiệp của bạn tỏ thái độ thiếu hợp tác; đùn đẩy trách nhiệm cho người khác. Hoặc khi chung team với những đồng nghiệp giỏi khiến bạn cảm thấy tự ti và thua kém về nhiều mặt.

“Mỗi người một vẻ” nên có đồng nghiệp này, đồng nghiệp khác. Ai cũng có điểm mạnh điểm yếu riêng. Ví dụ một số người chưa thật sự giỏi về lập trình IT, nhưng bù lại họ có chí cầu tiến và khả năng tiếp nhận kiến thức một cách nhanh chóng. Điều bạn cần nhớ và thực hiện là hãy học cái hay của những người đồng nghiệp giỏi.

Bộc lộ sự ngờ vực về chính bản thân

Có lẽ nỗi ám ảnh về chuyên đề nhập môn lập trình hay các bài giảng về thuật toán và cấu trúc dữ liệu có thể bạn sẽ không thể nào quên. Khoảng thời gian đầu đã trôi qua nhưng khi nhìn nhận lại, những rào cản ấy đã phần nào khiến bạn nhận ra sự không phù hợp với đặc thù ngành hoặc đơn giản khiến bạn nghi ngờ về khả năng và chính sự lựa chọn của bản thân mình. Sai ngành thật à? Tồn thời gian học làm chi vậy? Bao nhiêu câu hỏi sẽ được đặt ra và chúng dường như quay cuồng xung quanh bạn.

Thời học C++, nhiều bạn phải điên cuồng làm những bài tập, đôi khi không tha thiết mấy những vẫn cố mà cày nó. Vấn đề nằm ở chỗ việc bạn có nhìn nhận được hiện thực chung hay không thôi. 

ngành lập trình

Một lập trình viên không tự sinh ra được, cái gì cũng phải có quá trình khổ luyện. Tất nhiên trong quá trình ấy phải có sự cộng hưởng giữa nhiều yếu tố về năng lực, tiềm năng, sự cố gắng và cả sự may mắn. Vì vậy, khi gặp những khó khăn trong ngành học hay công việc, dù nó là gì thì nó cũng chỉ là một phần, một khía cạnh nhỏ trong cuộc sống này mà thôi. Điều bạn cần nhớ là đừng bao giờ nghĩ mình không có khả năng, mà đơn là mình cố gắng chưa đủ nhé.

Liệu bạn đã bắt kịp xu thế công nghệ? – Áp lực đặc thù của ngành IT

Công nghệ có nghĩa quan trọng vì nó không thể tách rời khỏi công việc của ngành lập trình. Chính mối quan hệ chặt chẽ ấy đã vô tình tạo ra áp lực đặc thù của ngành IT

Bạn thử nghĩ xem liệu những giải pháp xưa cũ có còn ứng dụng được vào bộ máy vận hành hoạt động của các tổ chức vừa áp dụng các mô hình công nghệ hiện đại? Nhiều yếu tố trong ngành IT như các công cụ công nghệ, thuật ngữ ngôn ngữ lập trình, framework,.. dường như mọi thứ đều được thay đổi.

Chính áp lực đó đã tạo nên cho dân IT một nỗi lo lớn. Họ rất sợ kiến thức của mình bị lạc hậu và khó khăn trong vấn đề tìm kiếm việc làm do thị trường đầy cạnh tranh và rủi ro bị đào thải nếu không đáp ứng được cầu. Những developer lâu năm chắc chắn thấm thía những áp lực này rồi.

  6 giải pháp công nghệ hữu ích cho phòng nhân sự của bạn

ngành lập trình

Đúc kết từ kinh nghiệm thực tế, dưới đây là những cách mình dùng để tránh những áp lực này. 

  • Tập trung nắm vững những kiến thức căn bản trước vì nó là cơ sở giúp bạn dễ dàng hơn trong việc tiếp cận công nghệ
  • Trải nghiệm thực tế thông qua việc tham gia và thực hiện những dự án lớn nhỏ để áp dụng những kiến thức IT đã tích lũy
  • Đừng lo lắng quá nhiều về công nghệ. Hãy rèn luyện về tính tư duy độc lập và tư duy hệ thống để nắm bắt cơ chế đồng thời liên tục cập nhật những thông tin mới về ngành lập trình IT

Áp lực nảy sinh từ quá trình thực hiện công việc/dự án

Cuộc sống này không quá dễ dàng đối với chúng ta. Khi bạn có một người sếp hoàn hảo và những đồng nghiệp tuyệt vời thì bạn nghĩ chắc mình không cần phải lo nghĩ nhiều về những áp lực xoay các mối quan hệ. Tuy nhiên, đừng vội vui mừng vì vẫn còn tổn tại một loại áp lực mà dù bạn có muốn trốn tránh cũng không thể tránh được. Đó là áp lực nảy sinh từ quá trình thực hiện công việc hoặc dự án.

ngành lập trình

Nếu đối với dự án nhỏ, khối lượng công việc không quá nhiều nên bạn khá thảnh thơi. Tuy nhiên, với các dự án lớn đòi hỏi độ phức tạp cao, bạn sẽ khó thể chối từ lời mời “đồng hành” của người bạn mang tên áp lực. 

Dự án quá phức tạp, quá khó khăn khi code; team chịu trách nhiệm thực hiện dự án lại không đủ về nguồn lực và chuyên môn. Trong khi thời gian hoàn thành dự án quá gấp rút và vượt tầm kiểm soát khiến nhân lực phải thực hiện trong điều kiện chạy hết tố lực. Cảm giác khó chịu và tất nhiên việc phải OT (OverTime) “điên cuồng” là điều khó tránh khỏi.

Nếu cố gắng, bạn có thể thoát khỏi nó được không? Tất nhiên là được chứ, nhưng bạn phải buông bỏ suy nghĩ của việc trốn chạy, phải đối diện với áp lực vì đó là cách tốt nhất để “lật bài ngửa” giúp giảm thiểu những áp lực có thể diễn ra. Bạn có thể tham khảo một số cách thức làm bạn với những áp lực như sau:

  • Hãy thảo luận với các anh chị Leader hoặc quản lý dự án (Project Manager) xem có cách nào đàm phán với khách hàng (Clients) để kéo dài được deadline hay không?
  • Ngoài ra, việc chăm sóc sức khỏe là điều quan trọng để ứng phó với các áp lực. Ăn uống điều độ, ngủ không quá khuya, không cố gắng làm việc khi cơ thể có dấu hiệu mệt mỏi.

Lời kết

Áp lực trong ngành IT dễ hay khó vượt qua là do bản thân bạn quyết định vì đơn giản không riêng gì ngành lập trình mà rất nhiều ngành/lĩnh vực đều tồn tại những áp lực. Điều quan trọng là bạn có biết nhận ra được áp lực, đối mặt và tìm cách vượt qua nó. Mong rằng với bài viết vừa rồi, các bạn sẽ có những hình dung cụ thể về những áp lực diễn ra trong thực tế của những người làm về IT.

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

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

8 thủ thuật khi làm việc với Object sử dụng resting và spreading

8 thủ thuật khi làm việc với Object sử dụng resting và spreading
Bài viết được sự cho phép của tác giả Lưu Bình An
Những đoạn code bỏ túi hay xài nhất khi đụng tới object
  JavaScript đã tạo ra Object từ Function như thế nào?
  Object Relational Mapping

Merge object

part1 và part2 sẽ được merge vào user1

const part1 = { id: 100, name: 'An Luu' }
const part2 = { id: 100, password: 'Password!' }

const user1 = { ...part1, ...part2 }
//=> { id: 100, name: 'An Luu', password: 'Password!' }

Thêm property

Clone một object đồng thời thêm một số property mới vào object mới clone

const user = { id: 100, name: 'An Luu'}
const userWithPass = { ...user, password: 'Password!' }

user //=> { id: 100, name: 'An Luu' }
userWithPass //=> { id: 100, name: 'An Luu', password: 'Password!' }

Thêm property khi thỏa điều kiện

Trường hợp này hay dùng nhất là lúc chúng ta truyền lên API một object, nếu thõa điều kiện, sẽ thêm một số property vào trong object

const user = { id: 100, name: 'An Luu' }
const password = 'Password!'
const userWithPassword = {
  ...user,
  id: 100,
  ...(password && { password })
}

userWithPassword //=> { id: 100, name: 'An Luu', password: 'Password!' }

Xóa property khỏi object

// hàm này sẽ trả về object mới ko bao gồm password
const noPassword = ({ password, ...rest }) => rest

const user = {
  id: 100,
  name: 'An Luu',
  password: 'Password!'
}

noPassword(user) //=> { id: 100, name: 'An Luu' }

Xóa property với key chỉ định

const user1 = {
  id: 100,
  name: 'An Luu',
  password: 'Password!'
}

const removeProperty = prop => ({ [prop]: _, ...rest }) => rest
//                     ----       ------
//                          \   /
//                dynamic destructuring

const removePassword = removeProperty('password')
const removeId = removeProperty('id')

removePassword(user1) //=> { id: 100, name: 'An Luu' }
removeId(user1) //=> { name: 'An Luu', password: 'Password!' }

Sắp xếp property

Đôi khi chúng ta sẽ muốn thay đổi các property theo một thứ tự nào đó, nếu sắp xếp toàn bộ luôn thì chắc dùng Object.keys rồi thay xếp cái mảng key này lại.

Để di chuyển id lên đầu, trước hết gán giá trị undefined cho nó trước, sau đó, override lại giá trị này bằng cách resting

const user3 = {
  password: 'Password!',
  name: 'An Luu',
  id: 300
}

const organize = object => ({ id: undefined, ...object })
//                            -------------
//                          /
//  dời id lên đầu

organize(user3)
//=> { id: 300, password: 'Password!', name: 'An Luu' }

Còn di chuyển xuống dưới cùng

const user3 = {
  password: 'Password!',
  name: 'An Luu',
  id: 300
}

const organize = ({ password, ...object }) =>
  ({ ...object, password })
//              --------
//             /
// dời password xuống cuối

organize(user3)
//=> { name: 'An Luu', id: 300, password: 'Password!' }

Property mặc định

Ví dụ, user2 không có chứa quotes, hàm setDefaults đảm bảo tất cả object đều chứa property là quotes, nếu ko nó thêm vào []

const user2 = {
  id: 200,
  name: 'An Luu'
}

const user4 = {
  id: 400,
  name: 'You',
  quotes: ["I've got a good feeling about this..."]
}

const setDefaults = ({ quotes = [], ...object}) =>
  ({ ...object, quotes })

// hoặc nếu muốn dời thằng quotes lên đầu
// const setDefaults = ({ ...object}) => ({ quotes: [], ...object })

setDefaults(user2)
//=> { id: 200, name: 'An Luu', quotes: [] }

setDefaults(user4)
//=> {
//=>   id: 400,
//=>   name: 'You',
//=>   quotes: ["I've got a good feeling about this..."]
//=> }

Đổi tên property

Thí dụ bạn ko muốn trong object chứa property ID, nó phải viết thường id, đầu tiên chúng ta remove ID ra khỏi object, sau đó add lại bằng tên là id

const renamed = ({ ID, ...object }) => ({ id: ID, ...object })

const user = {
  ID: 500,
  name: "An Luu"
}

renamed(user) //=> { id: 500, name: 'An Luu' }

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

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

Xem thêm các vị trí tuyển dụng IT hấp dẫn tại TopDev

for vs forEach vs for/in vs for/of trong javascript

for-vs-foreach-vs-for-in-vs-for-of-trong-javascript
Bài viết được sự cho phép của tác giả Lưu Bình An
Trong javascript có rất nhiều cách để loop qua một array, chúng ta cùng bàn qua 4 cách chính hay sử dụng nhất
  • for (let i = 0; i < arr.length; ++i)
  • arr.forEach((v, i) => { /* ….. */})
  • for (let i in arr)
  • for (const v of arr)

2 phương thức là for và for/in cho phép chúng ta truy cập đến giá trị index trong array, ko phải giá trị của element trong array

const arr = ['a', 'b', 'c'];
// sau đó chúng ta dùng truy cập element bằng giá trị index
for (let i = 0; i < arr.length; ++i) {
  console.log(arr[i]);
}

for (let i in arr) {
  console.log(arr[i]);
}

Trong khi đó hai phương thức for/of và forEach sẽ truy xuất đến phần tử trong element, cũng có thể truy xuất vào index, nếu thích.

arr.forEach((v, i) => console.log(v));
for (const v of arr) {
  console.log(v);
}

Có thể bạn chưa biết, array trong javascript cũng là một dạng đặc biệt của object, chúng ta có thể gán một property cho nó

const arr = ['a', 'b', 'c'];
console.log(typeof arr); // 'object'

arr.test = 'bad';
console.log(arr.test); // bad

arr[1] === arr['1']; // true,

Nếu loop qua bằng 4 phương thức trên, chỉ duy nhất thằng for/in sẽ chạy qua

const arr = ['a', 'b', 'c'];
arr.test = 'bad';

// "a, b, c, bad"
for (let i in arr) {
  console.log(arr[i]);
}

Đó là lý do tại sao chúng ta ko nên dùng for/in để loop qua array

Đối với một element trống như thế này

const arr = ['a',,'b']
console.log(arr.length); // 3

Xem thêm các tin đăng tuyển dụng lập trình viên javascript trên TopDev

Không chỉ vậy thôi đâu, nếu loop qua mảng ['a',,'b'] nó cũng sẽ khác với ['a', undefined, 'c']. 2 thằngfor/infor/eachsẽ bỏ qua phần tử trống như vậy, nhưngforfor/of` vẫn tính

const arr = ['a',, 'c']
// Prints "a, undefined, c"
for (let i = 0; i < arr.length; ++i) {
  console.log(arr[i]);
}

// Prints "a, c"
arr.forEach(v => console.log(v));

// Prints "a, c"
for (let i in arr) {
  console.log(arr[i]);
}

// Prints "a, undefined, c"
for (const v of arr) {
  console.log(v);
}

Tuy nhiên, nếu là ['a', undefined, 'c'], cả 4 phương thức trên đề print hết giá trị trong array.

  10 câu hỏi JavaScript để tăng cường kỹ năng của bạn
  Giới thiệu về Reactive Programing trong javascript

Một cách để chèn phần tử trống vào array

const arr = ['a', 'b', 'c'];
arr[5] = 'e';

Tuy nhiên là trường hợp [a, , c] này sẽ rất rất ít khi xảy ra, vì căn bản là file JSON như thế là không hợp lệ. Chúng ta cũng không cần lo lắng lắm

> JSON.parse('{"arr":["a","b","c"]}')
{ arr: [ 'a', 'b', 'c' ] }
> JSON.parse('{"arr":["a",null,"c"]}')
{ arr: [ 'a', null, 'c' ] }
> JSON.parse('{"arr":["a",,"c"]}')
SyntaxError: Unexpected token , in JSON at position 12

Với từ khóa thisforfor/infor/of sẽ dùng chung scope với thằng cha, trong khi forEach thì nó là scope của nó.

forEach cũng xảy ra nhiều tình huống ko đúng khi dùng với async/await hoặc generator. Code bên dưới là không chạy, không dùng await cho callback của forEach cũng như yield

async function run() {
  const arr = ['a', 'b', 'c'];
  arr.forEach(el => {
    // SyntaxError
    await new Promise(resolve => setTimeout(resolve, 1000));
    console.log(el);
  });
}

function* run() {
  const arr = ['a', 'b', 'c'];
  arr.forEach(el => {
    // SyntaxError
    yield new Promise(resolve => setTimeout(resolve, 1000));
    console.log(el);
  });
}

Dùng với for/of thì ok

async function asyncFn() {
  const arr = ['a', 'b', 'c'];
  for (const el of arr) {
    await new Promise(resolve => setTimeout(resolve, 1000));
    console.log(el);
  }
}

function* generatorFn() {
  const arr = ['a', 'b', 'c'];
  for (const el of arr) {
    yield new Promise(resolve => setTimeout(resolve, 1000));
    console.log(el);
  }
}

Túm lại, for/of có thể dùng gần như mọi lúc. Mặc dù performance ko bằng for, nhưng dễ xài hơn, cũng ko dính nhiều trường hợp đặc biệt như for/in và forEach. Nếu ko cần dùng đến giá trị index, thì for/of sẽ được dùng. Còn nếu muốn truy xuất tới giá trị index với for/of

for (const [i, v] of arr.entries()) {
  console.log(i, v); // Prints "0 a", "1 b", "2 c"
}

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

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

Xem thêm các việc làm IT tại Hồ Chí Minh, Hà Nội hấp dẫn tại TopDev

Khi nào thì sử dụng Generics trong TypeScript

Khi nào thì sử dụng Generics trong TypeScript

Bài viết được sự cho phép của tác giả Vũ Công Tấn Tài

Trong TypeScript, có một kiểu dữ liệu khá đặc biệt, đó là kiểu generics. Với generic, chúng ta có thể viết ra các hàm / components / modules mà không nhất thiết phải chỉ định rõ kiểu dữ liệu lúc định nghĩa. Khai báo kiểu dữ liệu sẽ được đẩy về lúc sử dụng components đó. Như vậy, lúc sử dụng, nếu chúng ta chỉ định kiểu dữ liệu là number thì component sẽ chỉ chấp nhận dữ liệu kiểu number, tương tự như vậy, chúng ta có thể chỉ định kiểu dữ liệu cụ thể bất kì lúc sử dụng.

  Học kiến thức căn bản TypeScript chỉ trong 30 phút
  Frontend là gì? Làm thế nào để trở thành một Frontend developer

Với sự tiện dụng đó, bạn có thể thắc mắc, tại sao chúng ta không luôn sử dụng kiểu generics? Khi nào chúng ta nên dùng generics, khi nào nên chỉ định rõ kiểu dữ liệu lúc định nghĩa components? …

Giả định một tình huống cụ thể

Chương trình của chúng ta có 1 hàm trả về đối tượng “già nhất” trong 1 tập dữ liệu:

1
2
3
function getOldest(items: Array<{ age: number }>) {
    return items.sort((a, b) => b.age - a.age)[0];
}

Có thể thấy, hàm này chấp nhận kiểu dữ liệu đầu vào là 1 object bất kì, với điều kiện là nó phải có thuộc tính age. Để rõ ràng hơn, ta sẽ định nghĩa kiểu dữ liệu cho ràng buộc này như sau:

1
type HasAge = { age: number };

Như vậy, hàm của chúng ta sẽ được viết lại rõ ràng hơn như sau:

1
2
3
function getOldest(items: HasAge[]): HasAge {
    return items.sort((a, b) => b.age - a.age)[0];
}

Với ngữ cảnh này, chúng ta chạy một đoạn mã kiểm tra có sử dụng hàm trên như sau:

1
2
3
4
const things = [{ age: 10 }, { age: 20 }, { age: 15 }];
const oldestThing = getOldest(things);
console.log(oldestThing.age); // 20

Có thể thấy, vì dữ liệu trả về từ hàm getOldest() là một đối tượng có “hình dạng” tuân theo kiểu HasAge, do đó nó sẽ có thuộc tính age, vì thế ta có thể in ra độ tuổi của nó bằng cách gọi oldestThing.age. Cho tới lúc này đoạn mã của chúng ta viết vẫn đáp ứng tốt yêu cầu.

Vấn đề xảy ra khi chúng ta sử dụng kiểu dữ liệu phức tạp hơn

Giả sử, chúng ta có kiểu dữ liệu là Person, và tất nhiên, người thì có thuộc tính age, vì dữ liệu kiểu Person bao hàm luôn kiểu dữ liệu HasAge, nên ta có thể sử dụng được hàm getOldest() đối với dữ liệu kiểu Person.

1
2
3
4
5
6
7
8
9
type Person = { name: string, age: number};
const people: Person[] = [
    { name: 'Amir', age: 10 },
    { name: 'Betty', age: 20 },
    { name: 'Cecile', age: 15 }
];
const oldestPerson = getOldest(people); // This is OK

Vậy vấn đề ở đâu? Vấn đề là: kể cả khi ta sử dụng hàm getOldest với một kiểu dữ liệu khác (lớn hơn), dữ liệu trả về chỉ có kiểu dữ liệu là HasAge.

Chính vì kiểu dữ liệu trả về có kiểu là HasAge (chứ không phải kiểu Person), nên đoạn code sau sẽ báo lỗi khi ta truy cập vào thuộc tính name:

1
console.log(oldestPerson.name); // ❌ type error: Property 'name' does not exist on type 'HasAge'.

Rõ ràng, khi ta truyền vào mảng people có kiểu dữ liệu là Person, đối tượng trả về sẽ có thuộc tính name. Tuy nhiên, vì ta đã “hard-code” kiểu dữ liệu trả về của ta là HasAge, vậy nên trình thông dịch của TypeScript sẽ không chấp nhận cho ta truy cập bất kì thuộc tính nào khác ngoài ageChương trình của ta đã mất đi tính linh động.

Kể cả khi ta cố tình ép kiểu, TypeScript vẫn sẽ báo lỗi:

1
2
const oldestPerson: Person = getOldest(people); // ❌ type error
// Property 'name' is missing in type 'HasAge' but required in type 'Person'.

Kể cả khi ta có thể “lách luật” trình biên dịch với type assertions, thì đó cũng không phải là 1 cách làm tốt.

1
2
const oldestPerson = getOldest(people) as Person; // Lách luật
console.log(oldestPerson.name); // no type error

(Thử đoạn mã trên với TypeScript Playground)

Khi Generics phát huy tác dụng

Vì chương trình của ta đã bị mất đi tính linh động, ta có thể dùng generics để cải tiến như sau:

1
2
3
4
5
function getOldest<T extends HasAge>(items: T[]): T {
    return items.sort((a, b) => b.age - a.age)[0];
}
const oldestPerson = getOldest(people); // ✅ type Person

Done! Với việc sử dụng kiểu dữ liệu linh động, giờ ta biến oldestPerson của chúng ta đã kiểu dữ liệu là Person, và kiểu dữ liệu này linh động phù hợp đúng theo kiểu dữ liệu đã truyền vào.

Như vậy, ta đã có thể truy cập được thuộc tính name của dữ liệu trả về mà không bị TypeScript báo lỗi nữa:

1
2
const oldestPerson = getOldest(people); // ✅ type Person
console.log(oldestPerson.name);  // OK

Ta có thể kiểm tra với nhiều kiểu dữ liệu khác ở đây:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type Person = {name: string, age: number};
const people: Person[] = [
    { name: 'Amir', age: 10 },
    { name: 'Betty', age: 20 },
    { name: 'Cecile', age: 15 }
 ];
type Bridge = {name: string, length: number, age: number};
const bridges = [
    { name: 'London Bridge', length: 269, age: 48 },
    { name: 'Tower Bridge', length: 244, age: 125 },
    { name: 'Westminster Bridge', length: 250, age: 269 }
]
const oldestPerson = getOldest(people); // type Person
const oldestBridge = getOldest(bridges); // type Bridge
console.log(oldestPerson.name); // 'Betty' ✅
console.log(oldestBridge.length); // '250' ✅

(Thử đoạn mã trên với TypeScript Playground ở đây)

Khi nào không nên dùng Generics?

Kể cả khi bạn sử dụng dữ liệu đầu vào có kiểu là HasAge hay là một dữ liệu phủ lớn hơn của nó, nếu dữ liệu trả về của bạn không cần linh hoạt, hoặc không liên quan gì tới kiểu dữ liệu truyền vào, lúc đó bạn không cần sử dụng Generics làm gì cả. Ví dụ: trường hợp này sử dụng generics là không cần thiết.

1
2
3
function isFirstOlder<T extends HasAge>(a: T, b: T) {
    return a.age > b.age;
}

Có thể thấy, generics không phát huy được ưu điểm của nó trong tình huống này. Với xử lí ở trên, đơn giản ta chỉ cần làm như sau:

1
2
3
function isFirstOlder(a: HasAge, b: HasAge) {
return a.age > b.age;
}

 

Bài viết gốc được đăng tải tại Những dòng code vui

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

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

Khó khăn của một CTO – Chief Technology Officer

cto

Để có thể hiểu rõ những khó khăn của CTO hay gặp phải thì việc đầu tiên chúng ta cần định hình được định nghĩa CTO và vai trò của CTO trong công ty, sự khác biệt của CTO và CIO là gì? Từ đó đúc kết được những khó khăn mà CTO sẽ gặp phải.

Nếu sau khi đọc xong bài tổng hợp này từ TopDev mà bạn vẫn cảm thấy hứng thú với vai trò CTO và cảm thấy đây mà một thách thức đáng để trải nghiệm thì chúc mừng bạn, cơ hội trải nghiệm vị trí CTO tại TopDev chào mừng bạn! 

CTO là gì?

Theo nghĩa tiếng anh CTO – Chief Technology Officer, được hiểu là Giám đốc Công nghệ. Tuy nhiên khái niệm này khá rộng và mơ hồ, vậy công việc thực sự của CTO là gì?

Có thể hiểu nôm na CTO là người đưa ra những quyết định mang tính mấu chốt liên quan đến những vấn đề về công nghệ trong công ty. Họ sẽ là người nhìn thấu và có thể đo lường mức độ thành công của bất cứ dự án nào liên quan đến công nghệ. Thật ra thì, không có một từ ngữ hay một định nghĩa cụ thể nào để diễn đạt được vai trò của CTO. Một CTO ở công ty lớn sẽ có công việc và trách nhiệm khác với một CTO ở một Startup, ví dụ, ngoài việc phải xử lý những vấn đề liên quan đến kỹ thuật thì CTO của công ty Startup thường phải đảm nhận thêm những vấn đề liên quan đến cả sản phẩm (nếu công ty đó là công ty công nghệ), marketing, kinh doanh….

Nói chung, CTO là vị trí của một giám đốc đa năng, họ không cần phải là một người cực kì giỏi hay có chuyên môn quá sâu nhưng nhất thiết phải có những kiến thức nền tảng cũng như kỹ năng giải quyết vấn đề trong nhiều lĩnh vực, tầm nhìn xa, khả năng thấu hiểu và dẫn dắt nhân viên thay vì chỉ tập trung phát triển bản thân để có thể đảm bảo mọi thứ trong công ty đều vận hành trơn tru. Đặc biệt, trong một công ty công nghệ, vai trò của CTO là cực kì to lớn, hầu như những vấn đề nào xảy ra đều có mặt của CTO đứng ra giải quyết.

  CTO là gì - Những sự thật cần biết về Chief Technology Officer 

Thông thường các Giám đốc Công nghệ có 2 chức năng chính, đó là quan sát và chăm lo cho quy trình hoạt động kinh doanh nội bộ như phát triển tính năng sản phẩm. Chức năng thứ hai của CTO là họ sẽ là gương mặt đại diện cho công ty của họ để đi tham gia các sự kiện, hội nghị thương mại nhằm củng cố và nâng cao vị thế của doanh nghiệp trong giới.

Những năm trở lại đây, công nghệ ngày càng được ứng dụng nhiều hơn, từ những việc đơn giản nhất như thanh toán, đọc báo… cũng đều được công nghệ hóa tối đa. Công nghệ được nâng cấp và sử dụng để giảm các chi phí không cần thiết khi vận hành công ty, bên cạnh đó, công nghệ cũng được áp dụng để phân tích xu hướng thị trường và hoạch định được lợi nhuận trong hầu hết các ngành nghề hiện nay.

CTO chịu trách nhiệm cho tất cả các khía cạnh liên quan đến công nghệ trong 1 tổ chức, mỗi khi nền công nghệ có một bước tiến mới thì CTO sẽ là người chịu trách nhiệm cho việc cập nhật xu hướng mới đó. Họ cần phải có khả năng cân bằng giữa chiến lược kinh doanh và chiến lược công nghệ thay vì chỉ mãi tập trung nâng cao lợi nhuận giống như vị trí CEO trong công ty.

Sự nhầm lẫn giữa CTO và CIO – Chief Information Officer

Khái niệm giữa CTO và CIO rất dễ bị mọi người hiểu lầm chung là 1 hoặc hiểu sai vai trò của nhau. Thật ra, Giám đốc Thông tin (CIO) có nhiệm vụ chịu trách nhiệm và mảng công nghệ thông tin, ở một vài công ty nhỏ, vai trò của 2 vị trí này thường được gộp lại làm 1 vì sự tương đồng là ‘Công nghệ’. Có thể hiểu, CIO là người chịu trách nhiệm về việc phát triển kỹ thuật công nghệ cho công ty, duy trì và đảm bảo cơ sở hạ tầng Công nghệ Thông tin cho công ty.

Tuy nhiên, vì sự phát triển ‘khủng bố’ của nền công nghệ hiện nay, đồng nghĩa với việc khối lượng công việc của một CIO cũng ngày càng tăng, chính vì thế từ đó mới có sự xuất hiện của CTO nhằm chia sẻ bớt phần nào gánh nặng công việc của CIO và vai trò CTO cũng đã phát triển thêm nhiều chức năng quản lý khác để vận hành doanh nghiệp tốt hơn.

Tùy vào quy mô, phạm vi ngành nghề hoạt động của công ty mà trách nhiệm của CTO sẽ thay đổi theo sao cho phù hợp với công ty đó. Ví dụ, tại một Startup nhỏ, chưa có khả năng để tự thành lập team kỹ thuật riêng của công thì Giám đốc Công nghệ sẽ là người chịu trách nhiệm quản lý và làm việc với nguồn outsource ở bên ngoài sao cho có thể hoàn thành tốt việc vận hành phát triển cũng như giúp công ty đạt được các mục tiêu tài chính và tiếp thị bằng công nghệ tiên tiến.

Thông thường, CTO không có mức lương quy chuẩn nào cả vì như ở trên, CTO thường không có trách nhiệm nhất định, nên mức lương cũng sẽ lên xuống theo đó. Theo số liệu mới nhất vào năm 2020 thì mức lương trung bình cho vị trí CTO giao động trong khoản 159,920$/năm, trong trường hợp đối với các công ty công nghệ thì mức lương trung bình mỗi năm của CTO công ty công nghệ là vào khoảng 200,000$/năm.

cto

Khó khăn của một CTO

Phải đưa ra quyết định có tầm nhìn về nền tảng, thiết kế kỹ thuật

Vị trí CTO sẽ là người lao đầu vào tất tần tật những kế hoạch, dự án có liên quan đến công nghệ, kỹ thuật của công ty. Bên cạnh đó họ còn phải duy trì cân bằng cho các chiến lược phát triển của một dự án, đồng thời phải triển khai ý tưởng để thực hiện dự án đó thành công. Ở một vài công ty Startup nhỏ trẻ, một trong những thành viên sáng lập thường là người chịu trách nhiệm cho vị trí CTO, còn nếu ở một công ty lớn thì CTO sẽ là người phân phối công việc, vận hành team kỹ thuật, lập trình viên hoặc những bộ phận có trách nhiệm về mảng công nghệ của công ty.

Trong một số trường hợp đặc biệt, thường là ở các công ty công nghệ thì Giám đốc Công nghệ có thể vừa là người chịu trách nhiệm về công nghệ vừa là người kiêm luôn trách nhiệm đưa ra quyết định cho các kế hoạch phát triển, kinh doanh, thiết kế, sản phẩm có liên quan đến công nghệ.

Không những thế, nếu nguồn nhân lực về công nghệ trong một công ty không đủ kiến thức cũng như kinh nghiệm, tài nguyên để đáp ứng nhu cầu công việc thì chắc chắn rằng CTO sẽ là người có trách nhiệm tìm ra giải pháp hợp lý để giải quyết vấn đề này. Chính vì thế, nói CTO là một giám đốc đa năng cũng không ngoa vì để đảm nhiệm vị trí này ngoài có sự hiểu biết về lĩnh vực công nghệ thì họ cũng cần phải nắm được các kỹ năng đa dạng khác để có thể hoàn thành công việc.

  Lời khuyên từ Việt Trần - CTO DOF Hunt “Cách quản trị tốt nhất chính là không quản trị”
  Gặp gỡ Nguyễn Sơn Tùng CTO Viec.co - Quán quân StartupViet 2019

Đảm bảo chất lượng nhân sự team công nghệ

Ngoài những trách nhiệm công việc đã nêu trên, Giám đốc Công nghệ có thể còn kiêm luôn cả vị trí HR, chịu trách nhiệm phỏng vấn và tuyển dụng các chuyên viên kỹ thuật, chuyên viên khoa học dữ liệu và kỹ sư công nghệ. Trong những năm gần đây, việc tuyển dụng ngày càng trở nên khó khăn cho các Startup, thị trường càng ngày càng đa dạng, ứng viên ngày càng có nhiều sự lựa chọn hơn. Nguồn cầu lớn hơn nguồn cung nên các ứng viên sáng giá cho vị trí công nghệ có xu hướng lựa chọn cho bản thân một môi trường hoàn hảo nhất có thể, nhưng hầu hết những startup trẻ lại chưa đủ lực để đáp ứng được nhu cầu đó. Chính vì thế, để đảm bảo chất lượng nhân sự team công nghệ chưa bao giờ là một nhiệm vụ dễ dàng cả.

cto

Dù thích hay không, dù HR có hỗ trợ hay không thì Giám đốc Công nghệ vẫn luôn là người chịu trách nhiệm tuyển dụng, phỏng vấn, giám sát và đào tạo một đội ngũ hoàn hảo có thể hỗ trợ vận hành kỹ thuật công nghệ của công ty một cách trơn tru. Thậm chí các CTO ngày nay còn rất quan tâm đến các kỹ năng tuyển dụng.

Đảm bảo hệ thống an toàn

Bên cạnh việc quản lý bộ phận kỹ thuật của công ty thì việc đảm bảo tính an toàn của hệ thống cũng là công việc trong list trách nhiệm của một CTO. Cơ sở dữ liệu của công ty là một nơi cần được bảo mật tuyệt đối, nhiệm vụ của CTO là điều động team kỹ thuật lắp kín các lỗ hổng bảo mật và phát triển, xây dựng lớp tường thành bảo vệ nó. CTO phải đảm bảo các thông tin của công ty cũng như khách hàng của công ty được thuật toán bảo mật tuyệt đối, giữ được sự riêng tư. Đồng thời các kỹ sư kỹ thuật cũng phải làm việc theo quy tắc bảo mật do CTO đặt ra.

Đảm bảo chất lượng các sản phẩm công nghệ

Vai trò này được thể hiện mạnh mẽ khi Giám đốc Công nghệ làm trong các công ty phát triển sản phẩm công nghệ. Các bộ phận đảm bảo chất lượng của các công ty Startup công nghệ trẻ thường rất non nớt và yếu. Nhiệm vụ của các CTO ở đây là kiểm tra sản phẩm và đưa ra những thay đổi giúp cải thiện các chức năng sản phẩm, ngoài ra Giám đốc Công nghệ cũng sẽ chỉ định bộ phận nào hoặc ai là người chịu trách nhiệm chính trong quá trình debug.

Đóng góp ý kiến phát triển sản phẩm

Bên cạnh những nghĩa vụ trên CTO còn chịu trách nhiệm về việc phát triển sản phẩm mới và áp dụng công nghệ mới lên sản phẩm tiếp theo. Từ việc nghiên cứu, phân tích phản hồi từ khách hàng và hợp tác với các phòng ban khác, chắc chắn vai trò đóng góp để phát triển sản phẩm mới của CTO là không thể thiếu trong công ty công nghệ.

Học hỏi các kỹ năng mềm

Ngoài sự nổi trội trong lĩnh vực chính của mình thì điểm mạnh của CTO còn được thể hiện qua kinh nghiệm vận hành, quản lý và phát triển công ty thông qua các kỹ năng mềm. Quy mô công ty càng lớn thì càng có những yêu cầu kỹ năng cao hơn để thích ứng được với nhu cầu phát triển của công ty.

cto

  • Kỹ năng đàm phán là một trong những kỹ năng được các Giám đốc Công nghệ chú trọng hàng đầu, vì vai trò của CTO là quản lý đội ngũ kỹ thuật, tuyển dụng các ứng viên tiềm năng, làm việc với đối tác dự án kỹ thuật, thảo luận với giám đốc cấp cao và thậm chí là gặp khách hàng lớn.
  • Kỹ năng giải quyết vấn đề cũng được Giám đốc Công nghệ chú trọng vì khi có vấn đề kỹ thuật xảy ra thì CTO sẽ là người đầu tiên đưa ra kế hoạch giải pháp để khắc phục được điều đó.
  • Kỹ năng lãnh đạo, đây là một kỹ năng cần phải có ở dù ở bất cứ vị trí quản lý nào. Một CTO giỏi sẽ có thể lãnh đạo được team kỹ thuật của mình đạt được những thành tựu nhất định trong ngành.
  • Kỹ năng truyền cảm hứng cũng là một kỹ năng mà CTO cần bồi dưỡng để có thể nạp lại tinh thần cho nhân viên của mình nâng cao hiệu quả công việc. 
  • Kỹ năng tech tốt để hỗ trợ xây dựng kiến trúc sản phẩm kỹ thuật số, MVP và lập trình API và test, quản trị hệ thống công nghệ cao của công ty, các kỹ năng DevOps – tất cả kỹ năng này bạn có thể tìm thấy khi đọc bảng mô tả công việc CTO trong nhiều công ty khác nhau. Bộ kỹ năng này sẽ được yêu cầu tùy vào quy mô và đặc thù công nghệ của mỗi công ty.
  • Kỹ năng hoạch định chiến lược, CTO là người lên kế hoạch cho dự án kỹ thuật, họ cũng là người đưa ra quyết định cách tiếp cận phát triển quy trình làm việc, lập kế hoạch và kiểm tra ngân sách cùng với những bộ phận khác. Chính vì vậy CTO là người cần phải có kỹ năng lên kế hoạch chiến lược. Có thể nói CTO là người có cái nhìn tổng quan về toàn bộ công ty từ dự án đến các bộ phận khác nhau trong công ty miễn là liên quan đến công nghệ.
  Top 5 kỹ năng mềm mà các kỹ sư phần mềm cần phải biết
  Bài học về kỹ năng giải quyết vấn đề - Hãy tư duy như một Lập trình viên!
  Muốn vượt trội hơn người khác, không thể bỏ qua những kỹ năng cần thiết sau

Đút kết từ những thông tin trên mạng thì thật khó để có thể phân biệt được vị trí và vai trò chính xác của CTO và CIO cũng như những khó khăn khi làm Giám đốc Công nghệ. Nhưng bạn có thể hiểu nôm na là CTO là người sẽ sở hữu nhiều kỹ năng mềm và kỹ năng cứng nổi bật. Họ đóng vai trò quan trọng trong khía cạnh kỹ thuật của một doanh nghiệp, thấu hiểu và cân bằng nhu cầu của nhân viên, khách hàng. Tùy vào từng công ty, vai trò của CTO sẽ có những thay đổi nhất định để phù hợp với quy mô cũng như lĩnh vực của công ty đó.

Vai trò Giám đốc Công nghệ “khó chơi” ở chỗ họ phải thật sự thấu hiểu nhiều khía cạnh khác nhau của công việc. Ngoài ra họ còn đảm nhiệm rất nhiều nhiệm vụ công việc khác nhau, vị trí này khó có thể định nghĩa được là cần phải làm những gì. Có thể ở công ty A, bạn đảm nhiệm vị trí CTO rất tốt nhưng nó không đồng nghĩa với việc bạn cũng sẽ làm tốt vị trí CTO ở một công ty B. 

Quả là một công việc phức tạp nhỉ? Nhìn chung nếu bạn có kỹ năng hoạch định chiến lược, đảm bảo được vấn đề an ninh, chất lượng công nghệ cho công ty, xây dựng thành công team kỹ thuật giỏi, cập nhật xu hướng công nghệ mới và có thể vận hành tốt công nghệ mới, tìm ra tìm ra các giải pháp hiệu quả cho các khó khăn về phần kỹ thuật thì có thể bạn khá là phù hợp với vai trò của một Giám đốc Công nghệ. Năm 2020 là một năm được các chuyên gia dự đoán sẽ là một năm công nghệ bùng nổ, chính vì thế nhu cầu săn lùng CTO và tuyển dụng các vị trí kỹ sư kỹ thuật tại các công ty là cực kỳ cao. Ghé thăm trang tuyển dụng của TopDev ngay để tìm thêm những vị trí phù hợp với năng lực của mình nhé!

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

Việt Nguyễn – Từ cơ duyên đồng hành cùng Tiki đến vị trí CTO tại Ticketbox

CTO là gì – Những sự thật cần biết về Chief Technology Officer

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

TopDev tổng hợp.

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