TypeScript+vue使用与迁移经验总结

源宝导读:ERP平台的前端底层使用了Vue作为组件的基础架构,而使用了TypeScript语言进行组件的封装与开发。本文将简要介绍平台在使用TypeScript和Vue框架进行老功能重构时的经验总结。

一、背景

下面主要探讨是以下三个方面:

  • 目前项目中使用到的vue+ts的哪些特性,还有哪些特性值得去使用,不会涉及到太多的ts语法知识;

  • 老项目的迁移为ts,有哪些点需要改造;

  • 各抒己见,探讨下各位都有哪些心得和见解。

二、为什么要用typescript

TypeScript简单介绍:

  • 是 JavaScript 的强类型版本。然后在编译期去掉类型和特有语法,生成纯粹的 JavaScript 代码。由于最终在浏览器中运行的仍然是 JavaScript,所以 TypeScript 并不依赖于浏览器的支持,也并不会带来兼容性问题。

  • TypeScript 是 JavaScript 的超集,这意味着他支持所有的 JavaScript 语法。并在此之上对 JavaScript 添加了一些扩展,如 class / interface / module 等。这样会大大提升代码的可阅读性。

总结优势:

  • 静态类型检查: 类型校验,能够避免许多低级代码错误;

  • IDE 智能提示: 使用一个方法时,能清楚知道方法定义的参数和类型和返回值类型;使用一个对象时,只需要.就可以知道有哪些属性以及属性的类型;

  • 代码重构: 经过不停的需求迭代,代码重构避免不了,在重构时,如果前期有清晰和规范的接口定义、类定义等,对于重构帮助很大;

  • 规范性和可读性: 类似于强类型语言,有了合理的类型定义、接口定义等,对于代码实现的规范性和可读性都有很大提高,不然搜索整个项目这个方法在哪里调用、怎么定义等。

个人认为最有价值点:写代码前,会先构思功能需求的整体代码架构。

三、安装和起步

一般我们会面临两个情况:

  • 新项目创建;

  • 觉得ts不错,想将老项目切换为vue+ts。

3.1、新项目起步

  • 安装vue-cli3.0;

  • vue create vue-ts-hello-world;

  • 选择Manually select features,勾选typescript。其他配置根据项目情况勾选。

3.2、老项目切换为vue+ts

  • 安装ts依赖(或使用yarn);

    • yarn add vue-class-component vue-property-decorator;

    • yarn add ts-loader typescript tslint tslint-loader tslint-config-standard —dev。

  • 配置 webpack,添加ts-loader和tslint-loader;

  • 添加 tsconfig.json;

// 这是平台目前用的tsconfig.json
{"compilerOptions": {"target": "esnext","module": "esnext","strict": true,"jsx": "preserve","importHelpers": true,"moduleResolution": "node","experimentalDecorators": true,"esModuleInterop": true,"allowSyntheticDefaultImports": true,"strictNullChecks": false,"sourceMap": true,"baseUrl": ".","types": ["webpack-env","jest"],"paths": {"@/*": ["src/*"],// 别名追加"components/*": ["src/components/*"],},"lib": [ // 编译过程中需要引入的库文件的列表"esnext","dom","dom.iterable","scripthost"]},"include": ["src/**/*.ts","src/**/*.tsx","src/**/*.vue","tests/**/*.ts","tests/**/*.tsx"],"exclude": ["node_modules","ui-tests"]
}

备注: ts 也可支持 es6 / es7,在 tsconfig.json中,添加对es6 / es7的支持。

 "lib": ["dom","es5","es6","es7","es2015.promise"
]
  • 添加 tslint.json 或者 prettierrc(可以视情况而定)。

// 目前平台使用的是.prettierrc.js
module.exports = {"$schema": "http://json.schemastore.org/prettierrc","singleQuote": true,"endOfLine": "auto","semi": false
}
  • 让 ts 识别 .vue。

declare module "*.vue" {import Vue from "vue";export default Vue;
}
    • 而在代码中导入 .vue 文件的时候,需要写上 .vue 后缀。原因还是因为 TypeScript 默认只识别 .ts 文件,不识别 *.vue 文件。

    • 添加vue-shim.d.ts,让vue文件给vue模块来编译。

  • 改造 .vue文件,将vue中script切换为<script lang="ts">;

  • 改造.js文件,修改为ts语法,定义类型等。

四、vue+ts常用的装饰器

    这里主要用到了vue-property-decorator,这个是在vue-class-component基础上做了一层增强,新增了一些装饰器,使用更加便捷。这里只分享一些常用的,对于老项目改写vue文件很有用:

4.1、@Component

    标识该vue文件是一个组件,并且可以引入其他组件。

非ts版本:

import MyComponent from '@/components/MyComponent'
export default {components: {MyComponent}
}

ts版本:

import { Vue, Component } from 'vue-property-decorator'
import MyComponent from '@/components/MyComponent'
@Component({components: {MyComponent}
})
export default class YourComponent extends Vue {
}

备注:这里不管有没有引入其他组件,都必须要使用@Component,目的是为了注册这个组件。否则在其他组件各种莫名其妙的问题。比如:路由找不到组件,而且不会报错。

4.2、@Prop

非ts版本:

export default {props: {propA: {type: Number},propB: {default: 'default value'},propC: {type: [String, Boolean]},propD: {type: Object,default: () => {},validator(val: object) {return val.prop = '1'}}}
}

ts版本:

import { Vue, Component, Prop } from 'vue-property-decorator'@Component
export default class YourComponent extends Vue {@Prop(Number)readonly propA: number | undefined@Prop({ default: 'default value' })readonly propB!: string@Prop([String, Boolean])readonly propC: string | boolean | undefined// 也可以一起@Prop({type: Object, default: () => {},validator(val: object) {return val.prop = '1'}})readonly propD!: object // 只是举例,一般会定义一个interface
}

4.3、@Watch

非ts版本:

export default {watch: {child: {handler: 'onChildChanged',immediate: false,deep: false},person: [{handler: 'onPersonChanged1',immediate: true,deep: true},{handler: 'onPersonChanged2',immediate: false,deep: false}]},methods: {onChildChanged(val, oldVal) {},onPersonChanged1(val, oldVal) {},onPersonChanged2(val, oldVal) {}}
}

ts版本:

import { Vue, Component, Watch } from 'vue-property-decorator'@Component
export default class YourComponent extends Vue {@Watch('child')onChildChanged(val: string, oldVal: string) {}@Watch('person', { immediate: true, deep: true })onPersonChanged1(val: Person, oldVal: Person) {}@Watch('person')onPersonChanged2(val: Person, oldVal: Person) {}
}

4.4、@Provide和@Inject

    场景:一般用于父级嵌套比较深的子孙vue组件,但是数据不是很方便传到深层级vue组件中,利用树型结构组件。

非ts版本:

// 父组件
provide () {return {OptionGroup: this}
}// 子孙组件
inject: ['OptionGroup']

ts版本:

父组件:

@Provide()getObj () {return this}

子孙组件:

@Inject() getObj!: anyget obj() {return this.getObj()
}

Privide的弊端:

  • 依赖注入它将你应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难;

  • 同时所提供的属性是非响应式的。这是出于设计的考虑,因为使用它们来创建一个中心化规模化的数据跟使用 $root做这件事都是不够好的。

建议:

一般不推荐过度使用。

  • provide 和 inject的绑定并不是可响应的,这是刻意为之的。但是,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的;

  • 如果你想要共享的这个属性是你的应用特有的,而不是通用化的,或者如果你想在祖先组件中更新所提供的数据,那么这意味着你可能需要换用一个像Vuex这样真正的状态管理方案了。

4.5、@Ref

非ts版本:

export default {computed() {anotherComponent () {return this.$refs.anotherComponent},button () {return this.$refs.aButton}}
}

ts版本:

import { Vue, Component, Ref } from 'vue-property-decorator'
import AnotherComponent from '@/Components/another-component.vue'@Component
export default class YourComponent extends Vue {@Ref() readonly anotherComponent!: AnotherComponent@Ref('aButton') readonly button!: HTMLButtonElement// 我们目前是这样使用的$refs!: {popover: anysearch: HcProjectSelectSearchtree: HcProjectTree}
}

4.6、@Emit

用的很少,参数和时机不是很好控制。

非ts版本:

export default {methods: {handleClick(e) {this.$emit('click', e)},loadData() {const promise = new Promise(resolve => {setTimeout(() => {resolve(20)}, 0)})promise.then(value => {this.$emit('load', value)})}}
}

ts版本:

import { Vue, Component, Emit } from 'vue-property-decorator'@Component
export default class YourComponent extends Vue {@Emit('click')handleClick(e) {// todo}@Emit()promise() {return new Promise(resolve => {setTimeout(() => {resolve(20)}, 0)})}
}

五、mixin改写

定义mixin:

export const cusMixin = {mounted() {this.$refs = {}// $0 instanceof HTMLElement// this.$refs = {}console.log('mixin mounted')},beforeUpdate() {this.$refs = {}// console.log('global mounted')},updated() {this.$refs = {}// console.log('global mounted')}
}

引入mixin:

import { Vue, Component } from 'vue-property-decorator'
import cusMixin from '@/mixin'@Component({components: {},mixins: [cusMixin]
})
export default class YourComponent extends Vue {}// 或者尝试使用
import { Component, Mixins, Vue } from 'vue-property-decorator';
import { MyOtherMixin } from './MyOtherMixin';@Component
export class MyMixin extends Vue {private created() {console.log('what?');}
}@Component
// 继承多个mixin,使用数组 [MyMixin, MyOtherMixin] 
export default class App extends Mixins(MyMixin) { private test = "test";private laowang = 'laowang';created() {console.log(this.test)console.log(this.Kitchen)console.log(this.Tv)}}

六、vue识别全局的方法和变量

  • vue-shim.d.ts文件中,增加如下代码:

import Vue from 'vue'
import VueRouter, { Route } from 'vue-router'
import { Store } from 'vuex'
// 声明全局方法
declare module 'vue/types/vue' {interface Vue {// 内部变量$router: VueRouter;$route: Route;$store: Store<any>;// element-ui等组件$Message: any$Modal: any// 自定义挂载到Vue.prototype上的变量$api: any$mock: any$configs: any}
}

七、vuex的改写

    关于store的改造,配置和结构和原来一样,具体编码设计没有特定套路,根据项目具体设计改写为ts的语法。

    主要是关于ts在vue如何使用,目前主流的方案是vue-class-component + vuex-class,一般常用的mapGetters和mapActions改写:

yarn add vuex-class

非ts版本:

import { mapGetters, mapActions } from 'vuex'
export default Vue.extend({computed: {...mapGetters({'name','age'})},methods: {...mapActions(['setNameAction'])}
})

ts版本:

import { Vue, Component } from 'vue-property-decorator'
import { Getter, Action } from 'vuex-class'
import { Test } from '@/store'export default class YourComponent extends Vue {@Getter('name') name: string@Getter('age') age: number@Action('setNameAction') setNameAction: Functionget innerName (): string {return this.name}get innerAge (): number {return this.age}setName (name: string) {this.setNameAction(products)}
}

备注:tsconfig.json需要调整下:

{"compilerOptions": {// 启用 vue-class-component 及 vuex-class 需要开启此选项"experimentalDecorators": true,// 启用 vuex-class 需要开启此选项"strictFunctionTypes": false}
}

八、vue render jsx语法改写

    改写的原理还是和上面类似,都是借助目前流行的两个库,除了使用vue-property-decorator以外,还需要借助vue-tsx-support,vue-tsx-support是在Vue外面包装了一层,将prop、event等以泛型的方式加了一层ts接口定义传了进去,目的是为了防止ts的类检查报错。

  • 步骤:

    • 引入 yarn add vue-tsx-support --dev;

    • 导入ts声明,在main,ts中import "vue-tsx-support/enable-check";

    • vue.config.jsextensions添加.tsx。

  • 使用:

import { Component, Prop } from "vue-property-decorator";
import * as tsx from "vue-tsx-support";interface YourComponentsProps {name?: stringage?: number
}@Component
export default class YourComponents extends tsx.Component<YourComponentsProps> {@Prop() public name!: string;@Prop() public age!: number;protected render() {return (<div><h1>姓名:{this.name}</h1><h1>年龄:{this.age}</h1></div>);}
}

    这里jsx改写为tsx大致简单了解下,如果大家有兴趣,以后可以一起学习探讨下。

九、思考

  • 关于老项目ts的改造,如何才能平滑过渡,不影响现有的功能。

  • 在vue中ts的实践,数据、视图、控制器分层设计的问题。

------ END ------

作者简介

罗同学: 研发工程师,目前负责ERP建模平台的设计与开发工作。

也许您还想看

从案例角度解析建模平台动态规则引擎

WEB页面前端性能诊断方法与实践

前端异步对象的原理与使用方法

Web页面适配移动端方案研究

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309371.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

结构型模式——适配器模式

一、 实验目的与要求 1.练习使用适配器模式。设计相关的模拟场景并进行实施&#xff0c;验证模式特性&#xff0c;掌握其优缺点。 2.实验结束后&#xff0c;对相关内容进行总结。 二、实验内容 1.模式应用场景说明 现在喜欢上网的年轻人越来越多&#xff0c;而家里面的电脑满足…

[号外] Blazor wasm 其实也挺快!

之前第一篇的时候&#xff0c;因为没有用任意配置&#xff0c;导致wasm加载很慢&#xff0c;我就感觉不会是这样的&#xff0c;为了不误导小盆友&#xff0c;所以还是趁着周末研究了一波&#xff0c;做了相关的调整&#xff0c;经过测试&#xff0c;速度基本可观了&#xff0c;…

WinUI 3 试玩报告

1. 什么是 WinUI 3#在微软 Build 2020 开发者大会上&#xff0c;WinUI 团队宣布可公开预览的 WinUI 3 Preview 1&#xff0c;它让开发人员可以在 Win32 中使用 WinUI。WinUI 3 Preview 1 包含新的 VisualStudio 项目模板&#xff0c;可以创建面向 .NET 5 的 C# 和 C/Win32 项目…

记一次EF Core连接MySql、Oracle

点击上方“Dotnet9”添加关注哦上上个月写的一篇文章&#xff0c;今天有同事问我使用EF Core连接MySql和Oracel的问题&#xff0c;我把这篇文章直接甩给了他。下面是正文&#xff1a;这几天研究了EF Core对MySql、Oracle的操作&#xff0c;包括连接、简单查询等&#xff0c;操作…

基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(五)

系列文章使用 abp cli 搭建项目给项目瘦身&#xff0c;让它跑起来完善与美化&#xff0c;Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API&#xff0c;包装返回模型再说Swagger&#xff0c;分组、描述、小绿锁接入GitHub&#xff0c;用JWT保护你的API异常处理和…

Codeforces Round #719 (Div. 3)/ Codeforces Round #720 (Div. 2)

A. Do Not Be Distracted! 题意&#xff1a; 一件事情一但开始&#xff0c;只能做完才能做别的事&#xff0c;当出现一件事不连续出现时&#xff0c;教师会怀疑 题目&#xff1a; Polycarp has 26 tasks. Each task is designated by a capital letter of the Latin alphab…

dotNET Core 3.X 使用 Autofac 来增强依赖注入

在上一篇《dotNET Core 3.X 依赖注入》中简单介绍了 dotNET Core 框架本身的依赖注入功能&#xff0c;大部分情况下使用框架的依赖注入功能就可以满足了&#xff0c;在一些特殊场景下&#xff0c;我们就需要引入第三方的注入框架。为什么要使用 Autofac&#xff1f;如果您在之前…

[JS-DOM]DOM概述

DOM&#xff1a; * 概念&#xff1a; Document Object Model 文档对象模型* 将标记语言文档的各个组成部分&#xff0c;封装为对象。可以使用这些对象&#xff0c;对标记语言文档进行CRUD的动态操作* W3C DOM 标准被分为 3 个不同的部分&#xff1a;* 核心 DOM - 针对任何结构…

基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(一)

系列文章使用 abp cli 搭建项目给项目瘦身&#xff0c;让它跑起来完善与美化&#xff0c;Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API&#xff0c;包装返回模型再说Swagger&#xff0c;分组、描述、小绿锁接入GitHub&#xff0c;用JWT保护你的API异常处理和…

2021年度训练联盟热身训练赛第一场 H题On Average They‘re Purple(BFS)

题意&#xff1a; 给你一些联通关系&#xff0c;问Bob先选择一些路径&#xff08;1~n&#xff09;联通&#xff0c;Alice在路径上染色&#xff0c;Bob的目的是选择一些路径使得染色变化最小&#xff0c;对于Alice来说&#xff0c;需要使得在Bob选择的&#xff08;1−n1-n1−n&…

使用请求头认证来测试需要授权的 API 接口

使用请求头认证来测试需要授权的 API 接口Intro有一些需要认证授权的接口在写测试用例的时候一般会先获取一个 token&#xff0c;然后再去调用接口&#xff0c;其实这样做的话很不灵活&#xff0c;一方面是存在着一定的安全性问题&#xff0c;获取 token 可能会有一些用户名密码…

双端队列 BFS + Chamber of Secrets CodeForces - 173B

题意&#xff1a; 一个 nmn\times mnm 的图&#xff0c;现在有一束激光从左上角往右边射出&#xff0c;每遇到 ‘#’&#xff0c;你可以选择光线往四个方向射出&#xff0c;或者什么都不做&#xff0c;问最少需要多少个 ‘#’ 往四个方向射出才能使光线在第 n 行往右边射出。 …

程序员过关斩将--作为一个架构师,我是不是应该有很多职责?

点击上方“蓝字”关注我们领取架构书籍每一个程序员都有一个架构梦。上面其实本质上是一句富有事实哲理的废话&#xff0c;要不然也不会有这么多人关注你的公众号。这些年随着“企业数字化”转型的口号&#xff0c;一大批企业奔跑在转型的路上&#xff0c;希望领先一步对手将企…

Excel使用技巧,补充中。。。

Excel表怎么把名字按字母排序 然后后面的数据也跟着变动 1、首先在excel表格的A列单元格中输入字母&#xff0c;选中需要排序的A列和B列单元格。 2、然后点击工具栏“数据”中的“排序”。 3、在弹出的对话框中的“次序”下拉框中选择“自定义序列”。 4、然后在弹出的对话…

递归函数中局部变量和全局变量

有时候会因为不注意递归函数中局部变量和全局变量&#xff0c;而导致结果和我们期望的不一致&#xff0c;递归中&#xff0c;在递归中的局部变量和全局变量&#xff0c;可以类似的看成函数调用时传递方式的按值传递&#xff08;局部变量&#xff09;和引用传递&#xff08;全局…

基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(二)

系列文章使用 abp cli 搭建项目给项目瘦身&#xff0c;让它跑起来完善与美化&#xff0c;Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API&#xff0c;包装返回模型再说Swagger&#xff0c;分组、描述、小绿锁接入GitHub&#xff0c;用JWT保护你的API异常处理和…

Azure App Service 如何在第一时间用上最新版 .NET Core

点击上方关注“汪宇杰博客” ^_^导语微软会经常对 .NET Core 发布更新&#xff0c;通常为安全补丁。这不&#xff0c;今天早上&#xff0c;.NET Core 3.1.5 更新发布了。然而 Azure App Service 自身的 .NET Core runtime 并不会在第一时间更新&#xff0c;每次都要等几周后微软…

我们是如何做DevOps的?

一、DevOps的理解DevOps的概念理解DevOps 的概念在软件开发行业中逐渐流行起来。越来越多的团队希望实现产品的敏捷开发&#xff0c;DevOps 使一切成为可能。有了 DevOps &#xff0c;团队可以定期发布代码、自动化部署、并将持续集成 / 持续交付作为发布过程的一部分。一句话概…

word文档相关使用

主要是为了记忆&#xff0c;有的时候&#xff0c;之前查阅过&#xff0c;后来使用又忘记了&#xff0c;以后碰了就陆续添加吧&#xff0c;先开一个博文 文章目录插入图片&#xff0c;显示不全的问题&#xff1a;方法一&#xff1a;方法二&#xff1a;方法三&#xff1a;在左侧显…

调试实战 —— dll 加载失败之 Debug Release争锋篇

缘起 最近&#xff0c;项目里遇到一个 dll 加载不上的问题。实际项目比较复杂&#xff0c;但是解决后&#xff0c;又是这么的简单&#xff0c;合情合理。本文是我使用示例工程模拟的&#xff0c;实际项目中另有玄机&#xff0c;但问题的本质是一样的。本文从行文上与 《调试实战…