Vite2 Vue3 Typescript 入门级教程
新建项目
创建项目目录 todoList
我们来到 Vite2
的官网,网址如下:
https://vitejs.dev/guide/#scaffolding-your-first-vite-project
Vite2
内置了很多中模板:
vanilla
vue
vue-ts
react
react-ts
preact
preact-ts
lit-element
lit-element-ts
今天我们选择 vue
这个模板来创建项目:
# npm 6.x
npm init @vitejs/app my-vue-app --template vue
# npm 7 , extra double-dash is needed:
npm init @vitejs/app my-vue-app -- --template vue
# yarn
yarn create @vitejs/app my-vue-app --template vue
安装 typescript
代码语言:javascript复制yarn add typescript
接着使用以下命令来初始化 ts
配置:
npx tsc --init
接着我们将 main.js
修改为 main.ts
接着我们将 index.html
中的:
<script type="module" src="/src/main.js"></script>
改成如下:
代码语言:javascript复制<script type="module" src="/src/main.ts"></script>
这个时候我们发现 App.vue
会有警告:
这是怎么回事呢?
代码语言:javascript复制/**
* shim.d.ts的作用
* 为了 typescript 做的适配定义文件,因为.vue 文件不是一个常规的文件类型,ts 是不能理解 vue 文件是干嘛的,
* 加这一段是是告诉 ts,vue 文件是这种类型的。
* 可以把这一段删除,会发现 import 的所有 vue 类型的文件都会报错。
*/
所以我们需要在项目根目录创建 shim.d.ts
来定义 .vue
模块:
declare module "*.vue" {
import { Component } from "vue";
const component: Component;
export default component;
}
这个时候 App.vue
就不会报错了。
安装 vue-router
vuex
代码语言:javascript复制yarn add vue-router@4.x vuex@4.x
在 src
目录新建 router/index.ts
:
import {createRouter, createWebHashHistory} from 'vue-router'
// 在 Vue-router新版本中,需要使用createRouter来创建路由
export default createRouter({
// 指定路由的模式,此处使用的是hash模式
history: createWebHashHistory(),
// 路由地址
routes: []
})
接着在 src
中新建 store/index.ts
:
import { createStore } from 'vuex'
// ts 中接口数据
interface State {
userName: string,
taskList: any[]
}
let state: State = {
userName: "小仙女",
taskList: []
}
export default createStore({
state,
});
安装 sass sass-loader
代码语言:javascript复制yarn add sass sass-loader -D
然后在根目录下新建 index.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;
}
}
}
然后在 main.ts
引入代码:
import { createApp } from 'vue'
import App from './App.vue'
import './index.scss'
import router from './router/index'
import vuex from './store/index'
const app = createApp(App)
app.use(router)
app.use(vuex)
app.mount('#app')
配置路由
首先我们需要在 src
下新建 views/home.vue
:
<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>
<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>
在 src
新建 views/about.vue
:
<template>
<div id="about" class="about-wrap">
about
</div>
</template>
<script>
export default {
name: 'about'
}
</script>
然后在 router/index.ts
配置路由:
import {createRouter, createWebHashHistory} from 'vue-router'
// 在 Vue-router新版本中,需要使用createRouter来创建路由
export default createRouter({
// 指定路由的模式,此处使用的是hash模式
history: createWebHashHistory(),
// 路由地址
routes: [
{
path: '/',
name: 'home',
component: () => import('../views/home.vue')
},
{
path: '/about',
name: 'about',
component: () => import('../views/about.vue')
}
]
})
在配置路由的这个过程你可能会碰到这个问题:
解决的方式如下:
在 tsconfig.json
下来配置:
"lib": ["es2015"]
这个时候,报错就解除了。
然后在 App.vue
:
<template>
<div id="app-wrap">
<div class="nav" id="nav">
<router-link to='/'>todoList</router-link>
<router-link to='/about'>about</router-link>
</div>
<router-view></router-view>
</div>
</template>
<script lang="ts">
export default {
name: 'App',
components: {}
}
// This starter template is using Vue 3 experimental <script setup> SFCs
// Check out https://github.com/vuejs/rfcs/blob/script-setup-2/active-rfcs/0000-script-setup.md
</script>
添加任务
我们需要一个 store 仓库来保存任务列表,每个列表项有一个状态标识。
在 store/index.ts
:
import { createStore } from 'vuex'
// 定义一个接口数据,用来限定 state 里面有两个属性
// 一个是 userName
// 一个是 taskList
interface State {
userName: string,
taskList: any[]
}
let state: State = {
userName: "小仙女",
taskList: []
}
export default createStore({
state
})
页面上我们需要输入框输入内容之后,回车触发向仓库添加数据,在 views/home.vue
添加如下代码:
<script lang="ts">
import { ref, computed, defineComponent } from 'vue'
import { useStore } from 'vuex'
export default defineComponent({
name: "home",
setup() {
// 使用 hook 的方式,拿到 store 仓库
const store = useStore()
// 通过 get 的方式链接 store.state.taskList
const taskList = computed(() => store.state.taskList)
// 绑定输入框
let inputValue = ref("")
// 向数据仓库提交一个 createTask 方法修改 taskList 数组
// 修改之后将 inputValue 制空
const addTask = () => {
store.commit('createTask', {
lable: inputValue.value,
isfinished: false
})
inputValue.value = ""
}
return {
taskList,
inputValue,
addTask,
}
}
});
</script>
接下来我们需要在 store/index.ts
给数据仓库定义 mutations
,用来接收外部提交的执行的方法:
export default createStore({
state,
mutations: {
createTask(state: any, newTask: any) {
state.taskList.push(newTask)
},
}
});
更新任务
当我们点击任务列表项时,需要重新设置列表项的状态值,所以我们需要在 views/home.vue
定义一个更新状态的方法,这个方法只需要传递列表项的下标以及状态值:
<script lang="ts">
import { ref, computed, defineComponent } from 'vue'
import { useStore } from 'vuex'
export default defineComponent({
name: "home",
setup() {
const store = useStore()
const taskList = computed(() => store.state.taskList)
let inputValue = ref("")
const addTask = () => {
store.commit('createTask', {
lable: inputValue.value,
isfinished: false
})
inputValue.value = ""
}
const updateStatus = (index, status) => {
store.commit('updateStatus', {
index,
status
})
}
return {
taskList,
inputValue,
addTask,
updateStatus,
}
}
});
</script>
然后我们需要在 store/index.ts
添加更新任务的方法:
export default createStore({
state,
mutations: {
createTask(state: any, newTask: any) {
state.taskList.push(newTask)
},
// 接收 state, payload
updateStatus(state: any, payload: any) {
// 解构 index, status
const {index, status} = payload
// 修改列表项的状态
state.taskList[index].isfinished = status
},
}
});
删除任务
当我们点击列表项的删除按钮的时候,需要向仓库提交方法删除列表项,所以我们需要在 views/home.vue
下添加删除任务的方法,只需要将下标传递过去就行:
<script lang="ts">
import { ref, computed, defineComponent } from 'vue'
import { useStore } from 'vuex'
export default defineComponent({
name: "home",
setup() {
const store = useStore()
const taskList = computed(() => store.state.taskList)
let 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 {
taskList,
inputValue,
addTask,
updateStatus,
deleteTask
}
}
});
</script>
然后在 store/index.ts
新增删除方法:
export default createStore({
state,
mutations: {
createTask(state: any, newTask: any) {
state.taskList.push(newTask)
},
updateStatus(state: any, payload: any) {
const {index, status} = payload
state.taskList[index].isfinished = status
},
// 删除任务方法
deleteTask(state, payload: any) {
// 只需要解构 index
const {index} = payload
// 将对应的数据删除即可
state.taskList.splice(index, 1)
}
}
});
到此为止,我们的 todoList 已经圆满结束了,我们来运行下,看看效果吧。