vue3+typescript入門到精通

vue3.0入門到精通

附B站講解視頻

vue3+typescript入門到精通

vue3.0 安裝

前安裝過vue的2.0版本,你需要把2.0相關的刪除

npm uni -g vue-cli

安裝vue/cli腳架

npm i -g @vue/cli

檢查版本號,目前安裝vuecli 4.5.4

vue -V

創建:在命令窗口輸入指令

選擇default vue 3

vue create 項目名稱

vue composition API


vue3.0 側重于解決代碼組織與邏輯復用問題

目前,我們使用的是“options”API 構建組件。 為了將邏輯添加到Vue組件中,我們填充(options)屬性,如data、methods、computed等。 這種方法最大的缺點是,它本身不是一個工作的JavaScript代碼。 您需要確切地知道模板中可以訪問哪些屬性以及this關鍵字的行為。在底層,Vue編譯器需要將此屬性轉換為工作代碼。正因為如此,我們無法從自動建議或類型檢查中獲益。

Composition API希望將通過當前組件屬性、可用的機制公開為JavaScript函數來解決這個問題。 Vue核心團隊將組件Composition API描述為“一套附加的、基于函數的api,允許靈活地組合組件邏輯”。 使用Composition API編寫的代碼更易讀,并且場景不復雜,這使得閱讀和學習變得更容易。

讓我們看到一個非常簡單的組件示例,它使用新的組件Composition API來理解它是如何工作的。

<template>

? <div id="app">

?? <img alt="Vue logo" src="./assets/logo.png">

?? <div>{{msg}}年齡為{{age}}</div>

?? <button @click="add"> + </button>

? </div>

</template>

?

<script>

?

?

export default {

? name: 'App',

? data() {

?? return {

? ?? msg:'王大合',

? ?? age:18

?? }

? },

? methods:{

?? add() {

? ?? this.age += 1

?? }

? }

?

}

</script>

setup

vue3.0將組件的邏輯都寫在了函數內部,setup()會取代vue2.x的data()函數,返回一個對象,暴露給模板,而且只在初始化的時候調用一次,因為值可以被跟蹤,所以我們通過vue3來改變編程習慣

首先引入ref

使用數據需要return

import {ref} from 'vue'

setup() {

?? const msg = ref('王大合')

?? const age = ref(18)

?? function add() {

? ?? age.value +=1

?? }

?? return {msg,age,add}

? }

computed

<template>

? <div id="app">

?? <img alt="Vue logo" src="./assets/logo.png">

?? <div>{{msg}}的年齡為{{age}}</div>

?? <div>{{double}}</div>

?? <button @click="add">+</button>

? </div>

</template>

?

<script>

import {computed, ref} from 'vue'

?

export default {

? name: 'App',

? setup() {

?? const msg = ref('王大合')

?? const age = ref(18)

?? const double = computed(() =>{

? ?? return age.value * 2

?? })

?? function add() {

? ?? age.value += 1

?? }

?? return {msg,age,add,double}


? }

}

</script>

?

?

?

reactive

在 setup 函數里面, 我們適應了 Vue3.0 的第一個新接口 reactive 它主要是處理你的對象讓它經過 Proxy 的加工變為一個響應式的對象,

toRefs

用于將響應式對象變成普通對象

<template>

? <div id="app">

?? <img alt="Vue logo" src="./assets/logo.png">

?? <div>{{msg}}的年齡為{{age}}</div>

?? <div>{{double}}</div>

?? <button @click="add">+</button>

? </div>

</template>

?

<script>

import {computed, reactive,toRefs} from 'vue'

?

export default {

? name: 'App',

? setup() {

?? const state = reactive({

? ? ?? msg:'王大合',

? ? ?? age:18,

? ? ?? double : computed(() =>{

? ?? return state.age * 2

?? })

?? })

?

?? function add() {

? ?? state.age += 1

?? }

?? return {...toRefs(state),add}


? }

}

</script>

?

?

?

props 和 context

在 Vue2.0 中我們可以使用 props 屬性值完成父子通信,在這里我們需要定義 props 屬性去定義接受值的類型,然后我們可以利用 setup 的第一個參數獲取 props 使用。

export default {

? name: 'App',

? components:{

?? Content

? },

? setup() {

?? const state = reactive({

? ? ?? msg:'王大合',

? ? ?? age:18,

? ? ?? double : computed(() =>{

? ?? return state.age * 2

?? })

?? })

?

?? function add() {

? ?? state.age += 1

?? }

?? return {...toRefs(state),add}


? }

}

我們在 App.vue 里面就可以使用該頭部組件,有了上面的 props 我們可以根據傳進來的值,讓這個頭部組件呈現不同的狀態。

<template>

? <div id="app">

?? <img alt="Vue logo" src="./assets/logo.png">

?? <div>{{msg}}的年齡為{{age}}</div>

?? <div>{{double}}</div>

?? <Content :msg='msg' />

?? <button @click="add">+</button>

? </div>

</template>

這里我新建一個新的組件content,在app.vue中引入

<!--? -->

<template>

? <div>{{data}}</div>

</template>

?

<script>

import {ref} from 'vue'

export default {

name:'content',

props:{

? ? msg:String

},

setup(props) {

? ? const data = ref(props.msg)

? ? return {data}

}

}

?

</script>

setup 函數的第二個參數是一個上下文對象,這個上下文對象中包含了一些有用的屬性,這些屬性在 Vue2.0 中需要通過 this 才能訪問到,在 vue3.0 中,訪問他們變成以下形式:

setup(props, ctx) {

? console.log(ctx) // 在 setup() 函數中無法訪問到 this

? console.log(this) // undefined

}

具體能訪問到以下有用的屬性:

- slot

- attrs

- emit

父組件

<template>

? <div id="app">

?? <img alt="Vue logo" src="./assets/logo.png">

?? <div>{{msg}}的年齡為{{age}}</div>

?? <div>{{double}}</div>

?? <Content :msg='msg' @change='showName' />

?? <button @click="add">+</button>

? </div>

</template>

?

<script>

import {computed, reactive,toRefs} from 'vue'

import Content from './components/content.vue'

export default {

? name: 'App',

? components:{

?? Content

? },


? setup() {

?? const state = reactive({

? ? ?? msg:'王大合',

? ? ?? age:18,

? ? ?? double : computed(() =>{

? ? ? ?? return state.age * 2

? ? ?? })

?? })

?? function showName(params) {

? ? ?? alert(params)

?? }

?

?? function add() {

? ?? state.age += 1

?? }

?? return {...toRefs(state),add,showName}


? }

}

</script>

?

?

?

子組件

<!--? -->

<template>

? <div>{{data}}</div>

? <button @click="changeName">打個招呼!</button>

</template>

?

<script>

import {ref} from 'vue'

export default {

name:'content',

props:{

? ? msg:String

},

setup(props,context) {

? ? const data = ref(props.msg)

?? function changeName() {

? ? ?? context.emit('change','Hello,王大合!')

?? }

? ? return {data,changeName}

}

}

?

</script>

watch

監聽ref

不指定數據源

const a = ref(18)

?

watch(()=>{

? ? console.log(a.value)

})

指定數據源

const a = ref(18)

?

? watch(a,()=> {

?

? console.log(a.value)

?

? })

監聽reactive

const state = reactive({

? ? ?? msg:'王大合',

? ? ?? age:18,

? ? ?? double : computed(() =>{

? ? ? ?? return state.age * 2

? ? ?? })

?? })

不指定數據源

watch(()=>{

? ? console.log(state.age)

})

指定數據源

watch(()=>state.age,()=>{

? ? console.log(state.age)

})

回調函數參數以及watche clean,使用clean時候是處理重復性的watch監聽事件

watch(() => state.age,(newVal,oldVal,clean)=> {

? ? console.log(state.msg + "去年年紀:"+oldVal +"今年年紀:" + newVal)

? ? clean(

? ? ? ()=>{

? ? ? ? console.log('clean')

? ? ? }

? ? )

? })

vue3.X+vite+typescript

放棄webpack,使用vite安裝vue3.0

這個是尤大開發的新工具,目的是以后替代webpack,原理是利用瀏覽器現在已經支持es6的import了,遇到import會發送一個http請求去加載文件,vite攔截這些請求,做一些預編譯,省去了webpack冗長打包的時間,提升開發體驗

npm install -g create-vite-app

create-vite-app vue3-vite

cd vue3-vite

npm install

npm run dev

# 或者使用yarn

yarn add -g create-vite-app

yarn create vite-app <project-name>

安裝依賴

yarn

使用yarn啟動項目

yarn dev

引入typescript

# 安裝 typescript

?

yarn add typescript -D

初始化tsconfig.json

# 然后在控制臺執行下面命令

npx tsc --init

將main.js修改為main.ts,同時將index.html里面的引用也修改為main.ts,

然后在script 里添加 lang="ts"

<template>

? <img alt="Vue logo" src="./assets/logo.png" />

? <HelloWorld msg="Hello Vue 3.0 + Vite" />

</template>

?

<script lang="ts">

import HelloWorld from './components/HelloWorld.vue'

?

export default {

? name: 'App',

? components: {

?? HelloWorld

? }

}

</script>

?

修改完之后,重啟就可以訪問項目了。雖然這樣配置是可以了,但是打開main.ts會發現import App from App.vue會報錯:Cannot find module './App.vue' or its corresponding type declarations.,這是因為現在ts還沒有識別vue文件,需要進行下面的配置:

在項目根目錄添加shim.d.ts文件

# powerShell終端,也可以手動創建

New-Item shim.d.ts

添加以下內容

declare module "*.vue" {

? import { Component } from "vue";

? const component: Component;

? export default component;

}

安裝vue-router

yarn add vue-router@4.0

這樣可以選擇最新的vue-router 4.0.0的測試版本,這里更新到beta.13

配置vue-router

在項目src目錄下面新建router目錄,然后添加index.ts文件,在文件中添加以下內容

import {createRouter, createWebHashHistory} from 'vue-router'

?

// 在 Vue-router新版本中,需要使用createRouter來創建路由

export default createRouter({

? // 指定路由的模式,此處使用的是hash模式

? history: createWebHashHistory(),

? // 路由地址

? routes: []

})

安裝vuex

同上

yarn add vuex@4.0

目前只能選擇最新測試版

在項目src目錄下面新建store目錄,并添加index.ts文件,文件中添加以下內容

import { createStore } from 'vuex'

?

interface State {

? userName: string

}

?

export default createStore({

? state:{

? userName:'王大合'

? }

});

main.ts中引入vuex和vue-router

import { createApp } from 'vue'

import App from './App.vue'

import './index.css'

import router from './router/index'

import vuex from './store/index'

?

const? app = createApp(App)

?

app.use(router)

app.use(vuex)

app.mount('#app')

?

上線小項目todoList

app.vue

<template>

? <div id="app">

?? <div id="nav">

? ?? <router-link to="/">todoList</router-link> |

? ?? <router-link to="/about">About</router-link>

?? </div>

?? <router-view/>

? </div>

</template>

?

<script lang="ts">

?

export default {

? name: 'App'

}

</script>

?

<style lang="scss">

? #app {

? font-family: Avenir, Helvetica, Arial, sans-serif;

? -webkit-font-smoothing: antialiased;

? -moz-osx-font-smoothing: grayscale;

? text-align: center;

? color: #2c3e50;

}

?

#nav {

? padding: 30px;

?

? a {

?? font-weight: bold;

?? color: #2c3e50;

?

?? &.router-link-exact-active {

? ?? color: #42b983;

?? }

? }

}

</style>

配置路由 router/index.ts

import {createRouter, createWebHashHistory} from 'vue-router'

?

// 在 Vue-router新版本中,需要使用createRouter來創建路由

export default createRouter({

? // 指定路由的模式,此處使用的是hash模式

? history: createWebHashHistory(),

? // 路由地址

? routes: [

?? {

?? path: '/',

?? // 必須添加.vue后綴

?? component: () => import('../views/todo-list.vue')

? ?? },

?? {

? ? ?? path: '/about',

? ? ?? name: 'About',


? ? ?? component: () => import('../views/About.vue')

? ?? }

]

})

store的index.ts新建vuex

import { createStore } from 'vuex'

?

interface State {

? userName: string

? taskList: any[]


}

?

export default createStore({

? state: {


? ?? userName: "王大合",

? ?? taskList: []


? },

? mutations:{


?? createTask (state:any, newTask:string) {

? ? ?? state.taskList.push(newTask)

? ?? },

? ?? deleteTask (state:any, index:number) {

? ? ?? state.taskList.splice(index, 1)

? ?? },

? ?? updateStatus (state:any, payload:any) {

? ? ?? const { index, status } = payload


? ? ?? state.taskList[index].isfinished = status

? ?? }

? }

});

在src目錄新建view文件夾,創建todoList和about

todoList

?

<template>

? <div class="home">

?? <!-- input輸入list內容 -->

?? <div>

? ? ? <input

?? @keyup.enter="addTask"

? ?? class="input"

? ?? type="text"

? ?? v-model="inputValue"

? ?? placeholder="請輸入" />

?? </div>

?? <!-- todoList內容展示和刪除 -->

? ? <ul class="ul">

? ?? <li class="item" v-for="(item, index) in taskList" :key="index">

? ? ?? <p

? ? ?? @click="updateStatus(index, !item.isfinished)"

? ? ?? class="content"

? ? ?? :class="item.isfinished ? 'active' : ''"

? ? ?? >{{item.lable}}</p>

? ? ?? <div class="item-delete" @click="deleteTask(index)">X</div>

? ?? </li>

? ?? <li v-if="taskList.length === 0" class="item-none">暫無數據</li>

?? </ul>

? </div>

</template>

?

?

<script lang="ts">

import { ref, computed } from 'vue';

import { useStore } from "vuex";

export default {


? setup() {

?? const store = useStore()

?? const taskList = computed(() => store.state.taskList);

?? const inputValue = ref('');

?? const addTask = () => {

? ?? store.commit('createTask', {

? ? ?? lable: inputValue.value,

? ? ?? isfinished: false

? ?? })

?

? ?? inputValue.value = ''

?? }

?

?? const updateStatus = (index, status) => {

? ?? store.commit('updateStatus', {

? ? ?? index,

? ? ?? status

? ?? })

?? }

?

?? const deleteTask = (index) => {

? ?? store.commit('deleteTask', index)

?? }

?

?? return {

? ?? inputValue,

? ?? taskList,

? ?? addTask,

? ?? updateStatus,

? ?? deleteTask

?? };

? }

}

</script>

?

<style scoped lang='scss'>

* {

? box-sizing: border-box;

? margin: 0;

? padding: 0;

}

ul,

li {

? list-style: none;

?

? text-align: left;

}

.home {

? max-width: 400px;

? margin: 0 auto;

? .input {

?? width: 100%;

?? height: 40px;

?? border-radius: 5px;

?? outline-style: none;

?? border: 2px solid #999;

?? padding: 5px 10px;

? }

? .ul {

?? margin-top: 10px;

? }

?

? .item {

?? height: 40px;

?? line-height: 40px;

?? padding-bottom: 5px;

?? border-bottom: 1px solid #dcdfe6;

?? color: #333333;

? }

? .item-none {

?? height: 40px;

?? line-height: 40px;

?? padding-bottom: 5px;

?? color: #333333;

?? text-align: center;

? }

? .content {

?? float: left;

?? height: 40px;

?? line-height: 40px;

?? cursor: pointer;

? }

? p.active {

?? text-decoration:line-through;

?? color: #999999;

? }

? .item-delete {

?? float: right;

?? width: 25px;

?? text-align: center;

?? cursor: pointer;

? }

}

</style>

about

<template>

? <div class="about">

?? <h1>{{name}}</h1>


?


? </div>

</template>

?

<script lang='ts'>

import { ref, watch } from 'vue';

?

export default {

? name: 'about',

? setup() {

?? const name = ref('王大合出品');

?


?

?

?


?? return {

? ?? name,


?? };

? }

};

</script>

?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372