Part 1
ref – https://www.youtube.com/watch?v=JfI5PISLr9w
Why Use Typescript
– type errors are caught at compile time
– easier to read the code
– better debugging
vue create hyrule-jobs
Use space bar to select manually select features.
Want to use TypeScript
use 3.x
DO NOT want class style component syntax
DO want to use Babel
Pick linter: Basic
Lint on Save
In dedicated config files
cd hyrule-jobs
Code . to open it with Visual Studio Code
npm run serve
File layout
Open the src folder.
notice main.ts
1 2 3 4 |
import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app') |
It is a typescript file.
shims-vue.d.ts – tells typescript all about vue files so it can better understand single file components
don’t change any of it.
1 2 3 4 5 |
declare module '*.vue' { import type { DefineComponent } from 'vue' const component: DefineComponent<{}, {}, any> export default component } |
tsconfig.json – typescript compiler
Look at src/App.vue
script tag has lang attribute for ts. That’s because we’re using typescript inside of it.
Go into components, delete HelloWorld.vue
Go into assets, delete logo.png
In App.vue, remove all code in template tag. Leave a div with text ‘Link’ in it.
remove HelloWorld import
remove HelloWorld from components
remove styles from style tags.
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<template> <div>Link</div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ name: 'App', components: {} }); </script> <style> </style> |
create assets > global.css
1 2 3 4 5 |
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;700&display=swap'); body { font-family: 'Open Sans'; background: #ece6d9; } |
Register css in main.ts
1 2 3 4 5 |
import { createApp } from 'vue' import App from './App.vue' import './assets/global.css' createApp(App).mount('#app') |
npm run serve
You will see text.
Part 2
ref – https://www.youtube.com/watch?v=UDAVj_vlAr8
Declare own data ‘name’ and display it in our template:
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<template> <div class="app"> <p>{{ name }} </p> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ name: 'App', components: {}, data() { return { name: 'Link' } } }); </script> <style></style> |
create methods to set name:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<template> <div>{{ name }}</div> <button @click="changeName('zelda')">change name</button> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ name: 'App', components: {}, data() { return { name: 'Link', age: 25 } }, methods: { changeName(name: string) { this.name = name } } }); </script> <style></style> |
Test by running browser again.
Using Union Types
1 |
let age: string | number = '25' // 25 is also fine |
For data property, we must use type casting. If it was a single type, it gets inferred. But what if we want to use multiple type such as number OR string?
1 2 3 4 5 6 7 8 9 |
<p>{{ name }} - {{ age }} </p> ... ... data() { return { name: 'Link', age: 25 as number | string } } |
create changeAge method:
1 2 3 4 5 |
methods: { changeAge(age: string | number) { this.age = age } } |
duplicate button, and change it for age. Use number 30 for the parameter. Can also use string “30” for the parameter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<template> <div>{{ name }} - {{ age }} </div> <button @click="changeName('zelda')">change name</button> <button @click="changeAge('8')">change age</button> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ name: 'App', components: {}, data() { return { name: 'Link', age: 25 as number | string } }, methods: { changeName(name: string) { this.name = name }, changeAge(age: string | number) { this.age = age } } }); </script> <style> </style> |
Part 3
ref – https://www.youtube.com/watch?v=H-hXNym2CK8
https://stackoverflow.com/questions/61452458/ref-vs-reactive-in-vue-3
- reactive() only takes objects, NOT JS primitives (String, Boolean, Number, BigInt, Symbol, null, undefined)
- ref() is calling reactive() behind the scenes
- Since reactive() works for objects and ref() calls reactive(), objects work for both BUT, ref() has a .value property for reassigning, reactive() does not have this and therefore CANNOT be reassigned
therefore…
Use ref() when..
- it’s a primitive (for example ‘string’, true, 23, etc)
- it’s an object you need to later reassign (like an array)
ref() is good for objects that need to be reassigned, like an array.
1 2 3 4 5 6 7 |
setup() { const blogPosts = ref([]); return { blogPosts }; } getBlogPosts() { this.blogPosts.value = await fetchBlogPosts(); } |
If we were to do the same with reactive, we’d have to reassign property posts
1 2 3 4 5 6 7 |
setup() { const blog = reactive({ posts: [] }); return { blog }; } getBlogPosts() { this.blog.posts = await fetchBlogPosts(); } |
reactive() Use-Case
A good use-case for reactive() is a group of primitives that belong together:
1 2 3 4 5 |
const person = reactive({ name: 'Albert', age: 30, isNinja: true, }); |
the code above feels more logical than
1 2 3 |
const name = ref('Albert'); const age = ref(30); const isNinja = ref(true); |
Let’s try grouping name and age in an object and passing it to reactive. Notice that its more natural when we use the variable state? Even better if we change it to personState, because properties name and age group together better.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<script lang="ts"> import { defineComponent, reactive, toRefs } from 'vue'; export default defineComponent({ name: 'Ap', components: {}, setup () { const state = reactive({ name: 'Link', age: 25 as string | number }) return { ...toRefs(state) } } }) </script> |
We then set properties like so:
1 2 |
state.name = 'hehe' state.age = 23 |
using refs
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import { ... ref ....} from 'vue'; setup() { const name = ref('Link') // ref with automatically conf inference // name.value = 'yoshi' // allowed const age = ref<number | string>(25) // pass in the type to over-ride the default infered type age.value = '30' // works age.value = 24 // works return { name, age } } |
Create custom type
create folder called types in src
in types folder, create jobs.ts
job.ts
1 2 3 4 5 6 7 8 |
interface Job { title: string, location: string, salary: number, id: string } export default Job |
use it inside App component
src/App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<template> {{ this.jobs }} </template> <script lang="ts"> import { defineComponent, ref } from 'vue'; import Job from './types/Job' export default defineComponent({ name: 'App', components: {}, setup() { const jobs = ref<Job[]>([ { title: 'farm worker', location: 'lon lon ranch', salary: 3000000, id: '1'}, // conforms to Job interface { title: 'quarryman', location: 'death mountain', salary: 6000000, id: '2'}, // conforms to Job interface { title: 'flute player', location: 'lost woods', salary: 2000000, id: '3'}, // conforms to Job interface { title: 'fisherman', location: 'lake hylia', salary: 1000000, id: '4'}, // conforms to Job interface { title: 'prison guard', location: 'gerudo valley', salary: 400000, id: '5'} // conforms to Job interface ]) return { jobs }; } }); </script> <style> </style> |