使用VueCli3+TypeScript+Vuex一步步構(gòu)建todoList的方法
前言
Vue3.x 即將來襲,使用 TypeScirpt 重構(gòu),TypeScript 將成為 vue 社區(qū)的標(biāo)配,出于一名程序員的焦慮,決定現(xiàn)在 Vue2.6.x 踩一波坑。
vue 官方文檔已經(jīng)簡略地對 typescript 的支持進(jìn)行了介紹,我們使用 Vue Cli3 直接生成項(xiàng)目
創(chuàng)建項(xiàng)目
❓為什么使用 Vue Cli3 構(gòu)建項(xiàng)目
官方維護(hù),后續(xù)升級減少兼容性問題
使用以下配置進(jìn)行項(xiàng)目的生成:
- Babel 對 Ts 進(jìn)行轉(zhuǎn)譯
- TSLint 對 TS 代碼進(jìn)行規(guī)范,后續(xù)會使用 prettier 對項(xiàng)目進(jìn)行編碼的統(tǒng)一
- 默認(rèn)安裝 Vuex 和 Router , Router 使用 history 模式
- 使用 Jest 進(jìn)行單元測試
╭─~/otherEWokspace ╰─➤ vue create ts-vuex-demo Vue CLI v3.6.3 ┌───────────────────────────┐ │ Update available: 3.9.3 │ └───────────────────────────┘ ? Please pick a preset: Manually select features ? Check the features needed for your project: Babel, TS, Router, Vuex, CSS P re-processors, Linter, Unit ? Use class-style component syntax? Yes ? Use Babel alongside TypeScript for auto-detected polyfills? Yes ? Use history mode for router? (Requires proper server setup for index fallb ack in production) Yes ? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are suppor ted by default): Sass/SCSS (with node-sass) ? Pick a linter / formatter config: TSLint ? Pick additional lint features: (Press <space> to select, <a> to toggle all , <i> to invert selection)Lint on save ? Pick a unit testing solution: Jest ? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In de dicated config files ? Save this as a preset for future projects? Yes ? Save preset as: ts-vue-demo
看一下新項(xiàng)目的層級目錄
╭─~/otherEWokspace/ts-vuex-demo ‹master› ╰─➤ tree -L 2 -I node_modules . ├── README.md ├── babel.config.js ├── jest.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── App.vue │ ├── assets │ ├── components │ ├── main.ts │ ├── router.ts │ ├── shims-tsx.d.ts │ ├── shims-vue.d.ts │ ├── store.ts │ └── views ├── tests │ └── unit ├── tsconfig.json └── tslint.json
tsconfig.json
對 lib 、 target 、 module 進(jìn)行解釋
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve", // 開啟對 jsx 的支持
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env",
"jest"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}
- target --- 被 tsc 編譯后生成 js 文件代碼風(fēng)格
- module --- 被 tsc 編譯后生成 js 文件的模塊風(fēng)格
- lib --- 原 ts 文件支持的代碼庫
我們來看一下示例:
// index.ts
export const Greeter = (name: string) => `Hello ${name}`;
"module": "commonjs", "target": "es5"
// index.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Greeter = function (name) { return "Hello " + name; };
"module": "es2015", "target": "es5"
// index.js
export var Greeter = function (name) { return "Hello " + name; };
"module": "es2015", "target": "es6"
// index.js
export const Greeter = (name) => `Hello ${name}`;
"module": "commonjs", "target": "es6"
// index.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Greeter = (name) => `Hello ${name}`;
如果lib沒有指定默認(rèn)注入的庫的列表。默認(rèn)注入的庫為:
- 針對于 target:ES5:DOM,ES5,ScriptHost
- 針對于 target:ES6:DOM,ES6,DOM.Iterable,ScriptHost
tslint
類似于 eslint ,對 ts 代碼進(jìn)行檢測。
vscode 需要安裝tslint 插件 ,并在 vscode 的用戶配置中加入以下配置,用來在保存時(shí)自動解決 ts 的錯誤。
// settings.json
"editor.codeActionsOnSave": {
"source.fixAll.tsLint": true
}
❗️ vue cli3 已經(jīng)安裝了tslint依賴
使用prettier 插件,對項(xiàng)目進(jìn)行代碼風(fēng)格的統(tǒng)一和規(guī)范
npm i tslint-config-prettier -D
添加 tslint.json extends 字段如下:
"extends": ["tslint:recommended", "tslint-config-prettier"]
設(shè)置 vscode
- 勾選 tslintIntegration ,使 prittier 支持格式化 ts 文件
- "editor.formatOnSave": true 保存時(shí)自動格式化
也可以使用 shift + option + f 進(jìn)行格式化
在根目錄下添加 .prttierrc 文件 (應(yīng)對 prittier 格式化 vue 文件中的 ts 文件時(shí),沒辦法使用 tslint 規(guī)則進(jìn)行格式化,需要對它單獨(dú)處理,以免 tslint 報(bào)錯)
{ "singleQuote": true }
shims-vue.d.ts
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}
聲明所有以 .vue 結(jié)尾的文件,默認(rèn)導(dǎo)入 vue ,默認(rèn)導(dǎo)出 Vue,用以在項(xiàng)目中ts文件識別 .vue 結(jié)尾文件。
在 main.ts 中,引入一個(gè) vue 組件必須以 .vue 結(jié)尾。
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
Vue.config.productionTip = false;
new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app');
Vue class
寫一個(gè) todolist 組件順便來介紹 vue-property-decorator,為了方便頁面構(gòu)建,使用 element-ui
element-ui 使用 ts 開發(fā),默認(rèn)有 .d.ts 的聲明文件
npm i element-ui
// main.ts import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI);
在 /src/compenents/ 新建 todoList.vue , 代碼如下:
<template>
<div class="todo_list">
<el-card class="box-card">
<div slot="header">
<el-row :gutter="18">
<el-col :span="18">
<el-input
v-model="todo"
placeholder="請輸入內(nèi)容"
></el-input>
</el-col>
<el-col :span="2">
<el-button
type="primary"
icon="el-icon-circle-plus-outline"
@click="addItem"
>add</el-button>
</el-col>
</el-row>
</div>
<div
v-for="(item,index) in todoList"
:key="item"
class="text item"
@click="removeItem(index)"
>{{ item }}</div>
</el-card>
<label
class="text"
style="margin-top:20px"
>{{todoLength}} records</label>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue, Emit } from 'vue-property-decorator';
@Component
export default class HelloWorld extends Vue {
public todo: string = '';
@Prop({ default: [] }) private readonly todoList!: string[];
get todoLength(): number {
return this.todoList.length;
}
@Emit()
private addItem(): string | undefined {
if (this.todo) {
return this.todo;
}
}
@Emit('removeItem')
private removeItem(index: number): number {
return index;
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.todo_list {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
.box-card {
width: 480px;
}
.text {
font-size: 14px;
text-align: left;
}
.item {
margin-bottom: 18px;
}
}
</style>
對 ts 代碼的用法指出以下幾點(diǎn):
- prop 建議寫成 xxx!: type 的形式,不然要寫成 xxx : type | undefined
- @Emit 可以不傳參數(shù),emit 出去的事件名默認(rèn)是修飾的函數(shù)名,但是當(dāng)函數(shù)的命名規(guī)則為 camelCase 時(shí)需要注冊的函數(shù)名必須是 kebab-case
- @Emit 傳參是由修飾的函數(shù) return value
改造 Home.vue 如下:
<template>
<div class="home">
<todoList
:todoList="[]"
@add-item="addTodoList"
@removeItem="addTodoLisItem"
/>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import todoList from '@/components/todoList.vue'; // @ is an alias to /src
import { State, Getter, Action } from 'vuex-class';
@Component({
components: {
todoList
}
})
export default class Home extends Vue {
public addTodoList(val: string) {
console.log(val);
}
private created() {
console.log('i add life cycle funciton -- created');
}
private addTodoLisItem(index: number) {
console.log(index);
}
}
</script>
Vuex
有關(guān) ts 中的 vuex 的寫法要從vuex-class 說起,在 官方的 vue-property-decorator 中也推薦使用該庫。
npm i vuex-class
在 src 文件夾中新建 store 文件夾, 在 store 新建 index.ts,todoList.ts
// index.ts
import Vue from 'vue';
import Vuex from 'vuex';
import todolist from './todoList';
Vue.use(Vuex);
export default new Vuex.Store({
modules: { todolist }
});
// todoList.ts
import { Commit, Dispatch, GetterTree, ActionTree, MutationTree } from 'vuex';
const ADD_TODOLIST = 'ADD_TODOLIST';
const REMOVE_ITEM = 'REMOVE_ITEM';
export interface RootState {
version: string;
}
interface Payload {
[propName: string]: any;
}
interface TodoListType {
todoList: string[];
}
interface Context {
commit: Commit;
dispatch: Dispatch;
}
const dataSource: TodoListType = {
todoList: []
};
const getters: GetterTree<TodoListType, RootState> = {
getTodoList(state: TodoListType): string[] {
return state.todoList;
}
};
const mutations: MutationTree<TodoListType> = {
ADD_TODOLIST: (state: TodoListType, item: string) => {
console.log(item);
state.todoList.push(item);
},
REMOVE_ITEM: (state: TodoListType, removeIndex: number) => {
state.todoList = state.todoList.filter((item: string, index: number) => {
return removeIndex !== index;
});
}
};
const actions: ActionTree<TodoListType, RootState> = {
addList: async ({ commit }: Context, item: string) => {
await Promise.resolve(
setTimeout(() => {
commit(ADD_TODOLIST, item);
}, 100)
);
},
removeItem: async ({ commit }: Context, { index }: Payload) => {
await Promise.resolve(
setTimeout(() => {
commit(REMOVE_ITEM, index);
}, 100)
);
}
};
export default {
namespaced: true,
state: dataSource,
getters,
mutations,
actions
};
刪除原來與 main.ts 同級的 store.ts
對 todoList.ts 需要注意以下幾點(diǎn):
- 對于 getters 、mutations 、actions 響應(yīng)的 type 可以使用 command + 左鍵點(diǎn)擊 進(jìn)入聲明文件查看,也可以不指定 type ,但是建議寫上
- 對于 Payload 解構(gòu) tslint 報(bào)錯的,可以為 Payload 添加類型聲明
interface Payload {
[propName: string]: any;
}
代碼中的 dataSource 本意為 state ,但是不能用 state 命名,tslint 會和形參 state 沖突
改造 /views/Home.vue 如下:
<template>
<div class="home">
<todoList
:todoList="todoList"
@add-item="addTodoList"
@removeItem="addTodoLisItem"
/>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import todoList from '@/components/todoList.vue'; // @ is an alias to /src
import { State, Getter, Action } from 'vuex-class';
@Component({
components: {
todoList
}
})
export default class Home extends Vue {
@State(state => state.todolist.todoList) private todoList!: string[];
@Action('todolist/addList') private addList!: (val: string) => void;
@Action('todolist/removeItem') private removeItem!: (index: number) => void;
public addTodoList(val: string) {
console.log(val);
this.addList(val);
}
private created() {
console.log('i add life cycle funciton -- created');
}
private addTodoLisItem(index: number) {
this.removeItem(index);
}
}
</script>
有關(guān) vuex-class 的調(diào)用有以下幾點(diǎn)注意
- @State 如果有分模塊,必須使用 state => state.xxx.xxx 的形式獲取state
- @Action 中函數(shù)的聲明,形參必須和方法保持一致
所有的代碼到此為止,使用 npm run serve 即可查看應(yīng)用,保留原有 routes 文件,保持應(yīng)用的健壯性。
寫在最后
- 本文只是介紹了一個(gè)簡單構(gòu)建 ts-vue 應(yīng)用的例子,對于框架的健壯和可擴(kuò)展性有需要慢慢考慮,比如 webpack 的配置,適應(yīng)測試,生產(chǎn)等各種環(huán)境的區(qū)分,axois 的封裝,等等。
- 對于vue + ts 的配方,文章還有很多 vue 的特性沒有去兼容,比如 this.refs 的使用,比如 vue-property-decorator 其他特性的使用。
- 由于官方文檔對 ts 的介紹有限,所以以上代碼肯定有不足的地方,希望大家指正。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
vue2.0項(xiàng)目實(shí)現(xiàn)路由跳轉(zhuǎn)的方法詳解
這篇文章主要介紹了vue2.0項(xiàng)目實(shí)現(xiàn)路由跳轉(zhuǎn)的詳細(xì)方法,非常不錯,具有一定的參考借鑒借鑒價(jià)值,需要的朋友可以參考下2018-06-06
vue中使用echarts制作圓環(huán)圖的實(shí)例代碼
這篇文章主要介紹了vue中使用echarts制作圓環(huán)圖的實(shí)例代碼,代碼簡單易懂,非常不錯,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-07-07
Vue實(shí)現(xiàn)圓環(huán)進(jìn)度條的示例
這篇文章主要介紹了Vue實(shí)現(xiàn)圓環(huán)進(jìn)度條的示例,幫助大家更好的理解和使用前端框架進(jìn)行開發(fā),感興趣的朋友可以了解下2021-02-02
vue點(diǎn)擊導(dǎo)航頁面實(shí)現(xiàn)自動滾動到特定位置
這篇文章主要介紹了vue點(diǎn)擊導(dǎo)航頁面實(shí)現(xiàn)自動滾動到特定位置方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
Vue 2.0的數(shù)據(jù)依賴實(shí)現(xiàn)原理代碼簡析
本篇文章主要介紹了Vue 2.0的數(shù)據(jù)依賴實(shí)現(xiàn)原理代碼簡析,主要從初始化的數(shù)據(jù)層面上分析了Vue是如何管理依賴來到達(dá)數(shù)據(jù)的動態(tài)響應(yīng),有興趣的可以了解一下2017-07-07
關(guān)于nuxt?store中保存localstorage的問題
這篇文章主要介紹了關(guān)于nuxt?store中保存localstorage的問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10
vue項(xiàng)目element UI input框掃碼槍掃描過快出現(xiàn)數(shù)據(jù)丟失問題及解決方案
這篇文章主要介紹了vue項(xiàng)目element UI input框掃碼槍掃描過快出現(xiàn)數(shù)據(jù)丟失問題,輸入框要掉兩個(gè)接口,根據(jù)第一個(gè)驗(yàn)證接口返回的code,彈不同的框,點(diǎn)擊彈框確認(rèn)再掉第二個(gè)接口,需要的朋友可以參考下2022-12-12
vue如何使用mapbox對當(dāng)前行政區(qū)劃進(jìn)行反選遮罩
這篇文章主要介紹了vue如何使用mapbox對當(dāng)前行政區(qū)劃進(jìn)行反選遮罩問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10

