- vừa được xem lúc

Cài đặt và code theo kiểu TypeScript mang lại điều gì cho dự án Nuxt.js?

0 0 266

Người đăng: Nguyễn Văn Quy

Theo Viblo Asia

Typescript là gì ?

TypeScript là một ngôn ngữ mã nguồn mở miễn phí hiện đang được phát triển và bảo trì bởi Microsoft (theo wikipedia). Người ta thường biết đến typescript và sử dụng nó trong các ngôn ngữ như C sharp, Angular hay Node.js. Vì TypeScript là tập cha của JavaScript nên bất kì chương trình JavaScript nào đã có cũng đều là chương trình TypeScript hợp lệ. Chính vì vậy chúng ta hoàn toàn có thể áp dụng TypeScript vào project vuejs.

Vì sao lại sử dụng TypeScript?

  • Vấn đề về dự án: Việc phát triển các dự án có dễ dàng hay không cũng phụ thuộc khá nhiều vào bản thân người lập trình, nó phần lớn cũng phụ thuộc vào yếu tố khách quan nữa. Tuy nhiên, theo quan điểm của mình thì áp dụng được kỹ thuật mới và đặc biệt kỹ thuật lập trình hướng đối tượng như TypeScript làm việc phát triển các dự án lớn được dễ dàng, và việc code cũng hợp lý đúng không nào :<.
  • Việc áp dụng TypeSCript có hỗ trợ nhiều Framework dễ dàng cho việc lựa chọn: Ví dụ như AngularJS 2.0
  • Hỗ trợ các tính năng của Javascript phiên bản mới nhất: Hiện tại nó cũng hỗ trợ đến version mới của javascript là ECMAScript 2015 (ES6).
  • Bên cạnh việc không bị tính phí, TypeScript dần trở nên phổ biến và được khá nhiều người dùng sử dụng. Chính vì vậy cộng đồng phát triển TypeScript ngày càng lớn, đã được hỗ trợ dễ dàng hơn.
  • TypeScript là Javascript: Bản chất của TypeScript là biên dịch tạo ra các đoạn mã javascript nên bạn có thế chạy bất kì ở đâu miễn ở đó có hỗ trợ biên dịch Javascript. Ngoài ra bạn có thể sử dụng trộn lẫn cú pháp của Javascript vào bên trong TypeScript, điều này giúp các lập trình viên tiếp cận TypeScript dễ dàng hơn.

1. Cài đặt Typescript

Đầu tiên thì ta cần cài đặt project Nuxt.js như sau:

  • Cài đặt sử dụng npx: npx create-nuxt-app nuxt-ts-project

  • Cài đặt sử dụng npm: npm init _@.com nuxt-ts-project

  • Cài đặt sử dụng yarn: yarn create nuxt-app nuxt-ts-project

Sau khi cài đặt xong, hãy mở folder project lên thôi nào...

cd nuxt-ts-project
//Cài đặt nuxt typescript build
npm install --save-dev @nuxt/typescript-build

2. Cấu hình Typescript trong project

Thêm @nuxt/typescript-build vào buildModules trong nuxt.config.js:

export default { buildModules: ['@nuxt/typescript-build']
}

Tạo file tsconfig.json và thêm các tùy chọn như bên dưới:

{ "compilerOptions": { "target": "es2018", "module": "esnext", "moduleResolution": "node", "lib": [ "esnext", "esnext.asynciterable", "dom" ], "esModuleInterop": true, "allowJs": true, "sourceMap": true, "strict": true, "noEmit": true, "baseUrl": ".", "paths": { "~/*": [ "./*" ], "@/*": [ "./*" ] }, "types": [ "@types/node", "@nuxt/types" ] }, "exclude": [ "node_modules" ]
}

Rồi thì tạo file vue-shim.d.ts:

declare module "*.vue" { import Vue from 'vue' export default Vue
}

Sau đó bạn cần cài eslint cho TypeScript. Nếu trước đó đã cài eslint cho Javascript rồi thì xóa nó đi nhé rồi cài lại

npm remove @nuxtjs/eslint-config
npm i -D @nuxtjs/eslint-config-typescript

Và update lại script:

"lint": "eslint --ext .ts,.js,.vue ."

3. Code theo phong cách TypeScript

1. Options API (vanilla)

Chúng ta có thể viết Typescript theo các này để không phải thay đổi code Javascript quá nhiều. Cú pháp này sẽ trông giống cách viết code mà chúng ta thường dùng.

<template> <div class="container"> <p>Project: {{ project }}</p> <div>Calculate Age:</div> <input v-model="year" type="number" /> {{ text }} </div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({ data() { return { project: 'Viblo', year: null, text: 'May Fest' } }, computed: { project(): string { return this.project } }, watch: { year(newVal: number) { this.text = this.calculate(newVal) } }, methods: { calculate(newVal: number): string { return 'May Fest:' + newVal } }
})
</script> 

2. Vuex typing (vanilla)

import { GetterTree, ActionTree, MutationTree } from 'vuex'
export const state = () => ({ count: 0 as number
})
export type RootState = ReturnType<typeof state>
export const getters: GetterTree<RootState, RootState> = { count: state => state.count
}
export const mutations: MutationTree<RootState> = { CHANGE_COUNT: (state, newVal: number) => (state.count = newVal)
}
export const actions: ActionTree<RootState, RootState> = { updateCount({ commit }, newVal) { // Some async code commit('CHANGE_COUNT', newVal) }
}

3. Class-based API

Cài đặt thư viện:

npm install --save nuxt-property-decorator

Khởi tạo một class:

Với cách code Javascript thông thường:

<script> export default { name: 'MayFest' }
</script>

Ta có thể thay đổi chút xíu theo kiểu Typescript:

<script lang="ts"> import { Component, Vue } from 'nuxt-property-decorator' @Component export default class MayFest extends Vue { }
</script>

Thuộc tính lang="ts" là bắt buộc cho thẻ script để sử dụng TypeScript.

Import một component

Thông thường sẽ được viết với:

<script> import Header from '@/components/Header.vue' export default { name: 'MayFest', components: { Header } }
</script>

Để đăng ký component trong một component khác sử dụng @Component của nuxt-property-decorator:

<script lang="ts">
import Header from '@/components/Header.vue'
import { Vue, Component } from 'nuxt-property-decorator'
@Component({ components: { Header }
})
export default class MayFest extends Vue {}
</script>

Data

Thông thường sẽ được viết với:

export default { title: 'Post' list: [ { name: 'Có gì mới trong phiên bản NextJS 10', link: 'https://viblo.asia/p/co-gi-moi-trong-phien-ban-nextjs-10-gGJ59MRx5X2', }, { name: 'Tailwind CSS v2 có gì mới?', link: 'https://viblo.asia/p/tailwind-css-v2-co-gi-moi-6J3ZgNMMKmB', } ]
}

Viết theo kiểu Typescript

export default class MayFest extends Vue { title: string = 'Post' list: Array<object> = [ { name: 'Có gì mới trong phiên bản NextJS 10', link: 'https://viblo.asia/p/co-gi-moi-trong-phien-ban-nextjs-10-gGJ59MRx5X2', }, { name: 'Tailwind CSS v2 có gì mới?', link: 'https://viblo.asia/p/tailwind-css-v2-co-gi-moi-6J3ZgNMMKmB', } ]
}

Props

Thông thường sẽ được viết với:

export default { props: { item: { required: true }, quantity, brand: { default: 'Apple', }, type: { type: String }, stock: { required: false, type: string, default: 'Available' } }
}

Viết theo Typescript

import { Component, Prop, Vue } from 'nuxt-property-decorator'
@Component
export default class Tile extends Vue { @Prop({ required: true }) readonly item!: object @Prop() quantity!: number @Prop({ default: 'Apple' }) brand!: string @Prop(String) readonly type!: string @Prop({ required: false, type: String, default: 'Available' }) readonly stock!: string
}

Computed properties

Cách viết thông thường:

export default { buttonText() { if (this.quantity) { return 'Buy Now!' } else { return 'Coming Soon!' } }
}

Viết theo kiểu Typescript:

export default class Tile extends Vue { get buttonText(): string { if (this.quantity) { return 'Buy Now!' } else { return 'Coming Soon!' } }
}

Sử dụng gettersetter Viết code thông thường:

searchText: { get: function () { return this.searchTextValue }, set: function (val) { this.searchTextValue = val }
}

Viết code Typescript:

export default class MayFest extends Vue { get searchText() { return this.searchTextValue } set searchText(val) { this.searchTextValue = val }

Methods

Viết code thông thường:

export default { data() { return { laptopPrice: 1400 quantity: 0 } } methods: { calculateTotal() { return this.laptopPrice * this.quantity } }
}

Viết code Typescript:

import { Vue, Component } from 'nuxt-property-decorator'
@Component
export default class Laptop extends Vue { laptopPrice: number = 1400 quantity: number = 0 calculateTotal(): number { return this.laptopPrice * this.quantity }
}

Watcher

Watcher có nhiều cách viết, ví dụ chúng ta hay viết kiểu:

watch: { total: function(newval) { //do something }
}

và cách viết ít được sử dụng:

watch: { total: { handler: 'totalChanged' }
}
methods: { totalChanged(newVal) { // do something }
}

Tuy nhiên, viết theo kiểu Typescript sẽ giống với cách viết thứ 2 và sử dụng @watch để truyền tên biến mà bạn cần theo dõi (watch).

@Watch('name')
totalChanged(newVal: string) { if(newVal > 20000) { this.status = 'limit exceeded for user' }
}

Chúng ta cũng thiết lập immediatedeep cho watchers:

@Watch('itemList', { immediate: true, deep: true })
itemChanged(newVal: Product, oldVal: Product) { // do something
}

Có khác đôi chút so với cách viết JS thông thường:

watch: { itemList: { handler: 'itemChanged', immediate: true, deep: true }
}
methods: { itemChanged(newVal, oldVal) { // do something }
}

Emit

Viết code thông thường:

<some-component add-to-count="someMethod" />
<some-component reset-data="someMethod" /> //Javascript Equivalent methods: { addToCount(n) { this.count += n this.$emit('add-to-count', n) }, resetCount() { this.count = 0 this.$emit('resetData') }
}

và theo cách viết của Typescript

@Emit()
addToCount(n: number) { this.count += n
}
@Emit('resetData')
resetCount() { this.count = 0
}

Lifecycle hooks

Cách viết thông thường

export default { asyncData() { //do something } beforeUpdate() { // do something }
}

Do không có đối số nào nên cách viết không hề thay đổi

export default class MayFest extends Vue { asyncData() { //do something } beforeUpdate() { // do something }
}

Mixins

Cách viết thông thường là:

export default { data() { return { cartProducts: [] } }, methods: { addToCart(newItem) { this.cartProducts = { ...this.cartProducts, ...newItem } } }
}

Tạo một file CartMixin.ts trong folder mixín:

/mixins/CartMixin.ts
import { Component, Vue } from 'nuxt-property-decorator'
@Component
class CartMixin extends Vue { public cartProducts: Array<object> = [] public addToCart(newItem: object): void { this.cartProducts = { ...this.cartProducts, ...newItem } }
}
export default CartMixin

Sử dụng mixin với JS thông thường:

<template> <div class="phones"> <div class="item"> <img src="@/assets/images/iphone-11.png" /> <div>iphone 11</div> <button @click="add">Add to Cart</button> </div> <div class="cart"> <div v-for="(item, i) in cartProducts" :key="i" class="item"> <div>Item: {{ item.name}}</div> <div>Quantity: {{ item.quantity }}</div> </div> </div> </div>
</template>
<script>
import CartMixin from '@/mixins/CartMixin'
export default { mixins: [ CartMixin], methods: { public add() { this.addToCart({ name: 'phone', quantity: 1 }) } }
}
</script>

Sử dụng với Typescript:

<template> <div class="phones"> <div class="item"> <img src="@/assets/images/iphone-11.png" /> <div>iphone 11</div> <button @click="add">Add to Cart</button> </div> <div class="cart"> <div v-for="(item, i) in cartProducts" :key="i" class="item"> <div>Item: {{ item.name}}</div> <div>Quantity: {{ item.quantity }}</div> </div> </div> </div>
</template>
<script lang="ts">
import { Vue, Component, mixins } from 'nuxt-property-decorator'
import CartMixin from '@/mixins/CartMixin'
@Component
export default class Phones extends mixins(CartMixin) { public add() { this.addToCart({ name: 'phone', quantity: 1 }) }
}
</script>

Vuex

Viết thông thường:

export default { namespaced: true, state: { info: { first: 'Preetish', last: 'HS', address1: '', address2: '', state: '', country: '', phone: 9000000009 } }, getters: { fullName() { return this.info.first + ' ' + this.info.last } } mutations: { updateUserInfo(data) { this.info = { ...this.info, ...data } } }
}

Sử dụng Typescript: Cài đặt vuex-module-decorators

npm install -D vuex-module-decorators

Sau đó tạo file users.ts trong store

import { Module, VuexModule, Mutation } from 'vuex-module-decorators'
interface UserData { first: string last: string address1: string address2: string state: string country: string phone: number
}
@Module({ name: 'user', stateFactory: true, namespaced: true
})
export default class User extends VuexModule { public info: UserData = { first: 'Preetish', last: 'HS', address1: '', address2: '', state: '', country: '', phone: 9000000009 } get fullName(): string { return this.info.first + ' ' + this.info.last } @Mutation public updateUserInfo(data: UserData) { this.info = { ...this.info, ...data } }
}

Sử dụng vuex trong components

Viết JS thông thường:

<script>
import { mapState, mapGetters, mapMutations } from 'vuex'
export default { data() { return { localData: {} } }, computed: { ...mapState('user', ['info']), ...mapGetters('user', ['fullName']) }, mounted() { this.localData = { ...this.localData, ...this.info } }, methods: { ...mapMutations('user', ['updateUserInfo']), update() { this.updateUserInfo(this.localData) } }
}
</script>

Sử dụng Typescript:

<template> <div class="user"> <div class="title">Welcome {{ fullName }}</div> <div> First: <input type="text" v-model="localData.first" /> </div> <button @click="update">Update Info</button> </div>
</template>
<script lang="ts">
import { Vue, Component, namespace } from 'nuxt-property-decorator'
const user = namespace('user')
@Component
export default class User extends Vue { public localData: object = {} @user.State public info!: object @user.Getter public fullName!: string @user.Mutation public updateUserInfo!: (data: object) => void mounted() { this.localData = { ...this.localData, ...this.info } } public update(): void { this.updateUserInfo(this.localData) }
}
</script>

4. Tạm kết

Việc áp dụng TypeScript vào Vuejs cũng khá đơn giản đúng không nào. Ban đầu, các bạn sẽ thấy việc áp dụng này khiến chúng ta phải code phức tạp hơn, code nhiều file hơn, hay thậm chí là dài hơn chẳng hạn. Tuy nhiên, với những lợi ích mà nó mang lại thì không thể phủ nhận được. Nếu bạn là một người yêu thích tìm hiểu cái mới và không ngại thử thách, hãy thử áp dụng ngay nhé. Thú vị lắm đấy ?

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giới thiệu Typescript - Sự khác nhau giữa Typescript và Javascript

Typescript là gì. TypeScript là một ngôn ngữ giúp cung cấp quy mô lớn hơn so với JavaScript.

0 0 528

- vừa được xem lúc

Type annotation vs Type Inference - Typescript

Trong bài viết này, chúng ta sẽ tìm hiểu kỹ về TypeScript bằng cách tìm hiểu sự khác biệt giữa kiểu chú thích và kiểu suy luận. Tôi sẽ cho rằng bạn có một số kinh nghiệm về JavaScript và biết về các kiểu cơ bản, như chuỗi, số và boolean.

0 0 45

- vừa được xem lúc

Type Annotation và Type Inference trong TypeScript là gì?

Khi làm việc với javascript chắc hẳn các bạn đã quá quen với việc dùng biến mà không cần phải quan tâm đến kiểu dữ liệu của nó là gì phải không? Đúng là mới đầu tiếp cận với Typescript mình cũng cảm thấy nó khá là phiền vì cần phải khai báo đủ type để nó chặt chẽ hơn. Lúc đó mình còn nghĩ: " JavaScr

0 0 37

- vừa được xem lúc

Tìm hiểu TypeScript và kiến thức cơ bản

TypeScript là gì. TypeScript sử dụng tất cả các tính năng của của ECMAScript 2015 (ES6) như classes, modules.

0 0 53

- vừa được xem lúc

TypeScript - P1: Vì sao TypeScript được yêu thích đến vậy?

Trải nghiệm thực tế. Trước khi là một Web Developer, tôi là một Mobile Developer và Java là thứ mà tôi từng theo đuổi.

0 1 69

- vừa được xem lúc

4 Tính năng rất hay từ TypeScript

Xin chào các bạn hôm nay mình xin chia sẽ những tính năng rất hay của TypeScript (TS), các bạn cùng tìm hiểu nhé. Ngoài việc set Type cho biến, tham số hay function thì ví dụ khi bạn nhìn vào một tham

0 0 96