最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 科技 - 知识百科 - 正文

使用VueCli3+TypeScript+Vuex一步步构建todoList的方法

来源:动视网 责编:小采 时间:2020-11-27 21:53:24
文档

使用VueCli3+TypeScript+Vuex一步步构建todoList的方法

使用VueCli3+TypeScript+Vuex一步步构建todoList的方法:前言 Vue3.x 即将来袭,使用 TypeScirpt 重构,TypeScript 将成为 vue 社区的标配,出于一名程序员的焦虑,决定现在 Vue2.6.x 踩一波坑。 vue 官方文档已经简略地对 typescript 的支持进行了介绍,我们使用 Vue Cli3 直接生成项目 创建项目
推荐度:
导读使用VueCli3+TypeScript+Vuex一步步构建todoList的方法:前言 Vue3.x 即将来袭,使用 TypeScirpt 重构,TypeScript 将成为 vue 社区的标配,出于一名程序员的焦虑,决定现在 Vue2.6.x 踩一波坑。 vue 官方文档已经简略地对 typescript 的支持进行了介绍,我们使用 Vue Cli3 直接生成项目 创建项目


前言

Vue3.x 即将来袭,使用 TypeScirpt 重构,TypeScript 将成为 vue 社区的标配,出于一名程序员的焦虑,决定现在 Vue2.6.x 踩一波坑。

vue 官方文档已经简略地对 typescript 的支持进行了介绍,我们使用 Vue Cli3 直接生成项目

创建项目

❓为什么使用 Vue Cli3 构建项目

官方维护,后续升级减少兼容性问题

使用以下配置进行项目的生成:

  • Babel 对 Ts 进行转译
  • TSLint 对 TS 代码进行规范,后续会使用 prettier 对项目进行编码的统一
  • 默认安装 Vuex 和 Router , Router 使用  history 模式
  • 使用 Jest 进行单元测试
  • ╭─~/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
    
    

    看一下新项目的层级目录

    ╭─~/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 进行解释

    {
     "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 文件代码风格
  • module --- 被 tsc 编译后生成 js 文件的模块风格
  • 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没有指定默认注入的库的列表。默认注入的库为:

  • 针对于 target:ES5:DOM,ES5,ScriptHost
  • 针对于 target:ES6:DOM,ES6,DOM.Iterable,ScriptHost
  • tslint

    类似于 eslint ,对 ts 代码进行检测。

    vscode 需要安装tslint 插件 ,并在 vscode 的用户配置中加入以下配置,用来在保存时自动解决 ts 的错误。

    // settings.json
     "editor.codeActionsOnSave": {
     "source.fixAll.tsLint": true
     }
    

    ❗️ vue cli3 已经安装了tslint依赖

    使用prettier 插件,对项目进行代码风格的统一和规范

    npm i tslint-config-prettier -D

    添加 tslint.json  extends 字段如下:

    "extends": ["tslint:recommended", "tslint-config-prettier"]

    设置 vscode

  • 勾选 tslintIntegration ,使 prittier 支持格式化 ts 文件
  • "editor.formatOnSave": true 保存时自动格式化
  • 也可以使用 shift + option + f 进行格式化

    在根目录下添加 .prttierrc 文件 (应对 prittier 格式化 vue 文件中的 ts 文件时,没办法使用 tslint 规则进行格式化,需要对它单独处理,以免 tslint 报错)

    { "singleQuote": true }

    shims-vue.d.ts

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

    声明所有以 .vue 结尾的文件,默认导入 vue ,默认导出 Vue,用以在项目中ts文件识别 .vue 结尾文件。

    在 main.ts 中,引入一个 vue 组件必须以 .vue 结尾。

    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

    vue-property-decorator

    写一个 todolist 组件顺便来介绍 vue-property-decorator,为了方便页面构建,使用 element-ui

    element-ui 使用 ts 开发,默认有 .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="请输入内容"
     ></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 代码的用法指出以下几点:

    1. prop 建议写成 xxx!: type 的形式,不然要写成 xxx : type | undefined
    2. @Emit 可以不传参数,emit 出去的事件名默认是修饰的函数名,但是当函数的命名规则为 camelCase 时需要注册的函数名必须是 kebab-case
    3. @Emit 传参是由修饰的函数 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

    有关 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 需要注意以下几点:

  • 对于 getters 、mutations 、actions 响应的 type 可以使用 command + 左键点击 进入声明文件查看,也可以不指定 type ,但是建议写上
  • 对于 Payload 解构  tslint 报错的,可以为 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>
    
    

    有关 vuex-class 的调用有以下几点注意

  • @State 如果有分模块,必须使用 state => state.xxx.xxx 的形式获取state
  • @Action 中函数的声明,形参必须和方法保持一致
  • 所有的代码到此为止,使用 npm run serve 即可查看应用,保留原有 routes 文件,保持应用的健壮性。

    写在最后

    1. 本文只是介绍了一个简单构建 ts-vue 应用的例子,对于框架的健壮和可扩展性有需要慢慢考虑,比如 webpack 的配置,适应测试,生产等各种环境的区分,axois 的封装,等等。
    2. 对于vue + ts 的配方,文章还有很多 vue 的特性没有去兼容,比如 this.refs 的使用,比如 vue-property-decorator 其他特性的使用。
    3. 由于官方文档对 ts 的介绍有限,所以以上代码肯定有不足的地方,希望大家指正。

    文档

    使用VueCli3+TypeScript+Vuex一步步构建todoList的方法

    使用VueCli3+TypeScript+Vuex一步步构建todoList的方法:前言 Vue3.x 即将来袭,使用 TypeScirpt 重构,TypeScript 将成为 vue 社区的标配,出于一名程序员的焦虑,决定现在 Vue2.6.x 踩一波坑。 vue 官方文档已经简略地对 typescript 的支持进行了介绍,我们使用 Vue Cli3 直接生成项目 创建项目
    推荐度:
    标签: VUE todo list
    • 热门焦点

    最新推荐

    猜你喜欢

    热门推荐

    专题
    Top