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


Posted in Javascript onJuly 25, 2019

前言

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 的介绍有限,所以以上代码肯定有不足的地方,希望大家指正。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript Event学习第八章 事件的顺序
Feb 07 Javascript
JavaScript调用堆栈及setTimeout使用方法深入剖析
Feb 16 Javascript
javaScript基础语法介绍
Feb 28 Javascript
基于Jquery实现表单验证
Jul 20 Javascript
为何JS操作的href都是javascript:void(0);呢
Nov 12 Javascript
javascript:void(0)是什么意思及href=#与href=javascriptvoid(0)的区别
Nov 13 Javascript
个人网站留言页面(前端jQuery编写、后台php读写MySQL)
May 03 Javascript
js实现百度登录框鼠标拖拽效果
Mar 07 Javascript
JavaScript实现旋转轮播图
Aug 18 Javascript
vue.js简单配置axios的方法详解
Dec 13 Javascript
微信小程序自定义tab实现多层tab嵌套功能
Jun 15 Javascript
通过实例解析jQ Ajax操作相关原理
Sep 23 Javascript
vue使用代理解决请求跨域问题详解
Jul 24 #Javascript
Vue父组件如何获取子组件中的变量
Jul 24 #Javascript
mock.js模拟数据实现前后端分离
Jul 24 #Javascript
vue+mock.js实现前后端分离
Jul 24 #Javascript
微信小程序如何修改radio和checkbox的默认样式和图标
Jul 24 #Javascript
Vue封装的组件全局注册并引用
Jul 24 #Javascript
vue子路由跳转实现tab选项卡
Jul 24 #Javascript
You might like
php操作memcache缓存方法分享
2015/06/03 PHP
浅析PHP中的闭包和匿名函数
2017/12/25 PHP
基于jQuery实现表格数据的动态添加与统计的代码
2011/01/31 Javascript
juqery 学习之五 文档处理 插入
2011/02/11 Javascript
JavaScript实现页面实时显示当前时间的简单实例
2013/07/20 Javascript
javascript实现div浮动在网页最顶上并带关闭按钮效果实例
2013/08/13 Javascript
checkbox使用示例
2013/08/23 Javascript
jQuery获取checkbox选中的值
2016/01/28 Javascript
BootstrapTable+KnockoutJS自定义T4模板快速生成增删改查页面
2016/08/01 Javascript
AngularJS中$http服务常用的应用及参数
2016/08/22 Javascript
AngularJS双向绑定和依赖反转实例详解
2017/04/15 Javascript
详解Angular 4.x Injector
2017/05/04 Javascript
JavaScript方法_动力节点Java学院整理
2017/06/28 Javascript
利用JavaScript如何查询某个值是否数组内
2017/07/30 Javascript
Vue + Vue-router 同名路由切换数据不更新的方法
2017/11/20 Javascript
快速解决处理后台返回json数据格式的问题
2018/08/07 Javascript
nuxt.js添加环境变量,区分项目打包环境操作
2020/11/06 Javascript
jQuery实现增删改查
2020/12/22 jQuery
win7 下搭建sublime的python开发环境的配置方法
2014/06/18 Python
深入理解python中的闭包和装饰器
2016/06/12 Python
Win7下Python与Tensorflow-CPU版开发环境的安装与配置过程
2018/01/04 Python
Python实现微信小程序支付功能
2019/07/25 Python
浅谈pytorch池化maxpool2D注意事项
2020/02/18 Python
Python3.6 + TensorFlow 安装配置图文教程(Windows 64 bit)
2020/02/24 Python
Python爬虫爬取、解析数据操作示例
2020/03/27 Python
如何在windows下安装Pycham2020软件(方法步骤详解)
2020/05/03 Python
使用SVG实现提示框功能的示例代码
2020/06/05 HTML / CSS
卡骆驰新加坡官网:Crocs新加坡
2018/06/12 全球购物
11月红领巾广播稿
2014/01/17 职场文书
演讲比赛的活动方案
2014/08/28 职场文书
化工见习报告范文
2014/10/31 职场文书
2014年村计划生育工作总结
2014/11/14 职场文书
给校长的建议书作文400字
2015/09/14 职场文书
毕业季聚会祝酒词!
2019/07/04 职场文书
python可视化之颜色映射详解
2021/09/15 Python
css3新特性的应用示例分析
2022/03/16 HTML / CSS