原有vue项目接入typescript

为什么要接入typescript

javascript由于自身的弱类型,使用起来非常灵活。

这也就为大型项目、多人协作开发埋下了很多隐患。如果是自己的私有业务倒无所谓,主要是对外接口和公共方法,对接起来非常头疼。主要表现在几方面:

  1. 参数类型没有校验,怎么传都有,有时会出现一些由于类型转换带来的未知问题。
  2. 接口文档不规范,每次都要通过读代码才能知道传什么,怎么传
  3. 接口编写符合规范,但是公共库中有大量的处理类型校验的代码

这就非常不利于工程标准化。于是我们决定引入typescript进行代码层面的强校验。

概览

原有vue项目接入ts主要包含下面几大步骤:

  1. 安装typescript相关npm包
  2. 修改webpack和ts配置文件
  3. 项目公共库和vue文件改造

ok,我们开始

1. 安装typescript相关npm包

这块有个非常重要的点需要注意:

就是要根据你本地的环境,去升级对应版本的typescript

这块是很多初次使用的同学都会遇到的问题。

因为只是看到了官网的教程,一步一步安装完发现各种报错。主要问题就是webpack版本不匹配,或者其他一些npm包版本不匹配

以我本地为例:

我本地环境是webpack3,所以直接安装最新版本的typescript,控制台会报错webpack版本过低的问题。

所以你要不把自己的webpack升级到webapck4.要不就采用与之相匹配的typescript版本。

我选择的是后者,因为直接给自己的项目升级到webapck4,会花费更长的时间。我们用的脚手架是公司内部统一的。里面集成了很多底层通用的基础服务。冒然升级webpack4会带来更大的麻烦,更何况项目时间比较紧迫,你懂得。

下面是我安装的包和对应的版本:

  • "typescript": "^3.1.4" (这个是必须的,ts库)
  • "ts-loader": "^3.5.0" (识别ts的laoder)
  • "tslint": "^5.11.0" (tslint校验库)
  • "tslint-loader": "^3.5.4" (tslint的loader)
  • "tslint-config-standard": "^8.0.1" (用于tslint默认校验规则)
  • "vue-property-decorator": "^7.2.0" (用于在.vue文件中使用ts语法)

2. 修改配置文件

  • 修改webpack配置文件(加入ts的相关配)
base: {
entry: {
...
app: resolve('src/main.ts') // 把main.js改为main.ts
}
...
resolve: {
...
extensions: ['vue', '.js', '.ts']
}
module: {
rules: [
...,
{ // 加入对文件的ts识别
test: /\.ts$/,
exclude: /node_modules/,
enforce: 'pre',
loader: 'tslint-loader'
}, {
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
appendTsSuffixTo: [/\.vue$/],
}
}
]
}

注意: main.js改成main.ts后,还要做一些改造,这个比较简单,按照tslint的错误提示改就可以了

  • 在根目录下创建tslint.json(类似eslint,这里设定一个校验标准)
{
"extends": "tslint-config-standard",
"globals": {
"require": true
}
}
  • 在根目录创建tsconfig.json(typescript配置文件)
{
"compilerOptions": {
// 编译目标平台
"target": "es5",
// 输出目录
"outDir": "./dist/",
// 添加需要的解析的语法,否则TS会检测出错。
"lib": ["es2015", "es2016", "dom"],
// 模块的解析
"moduleResolution": "node",
// 指定生成哪个模块系统代码
"module": "esnext",
// 在表达式和声明上有隐含的any类型时报错
"noImplicitAny": false,
// 把 ts 文件编译成 js 文件的时候,同时生成对应的 map 文件
"sourceMap": true,
// 允许编译javascript文件
"allowJs": true,
// 指定基础目录
"baseUrl": "./",
// 启用装饰器
"experimentalDecorators": true,
// 移除注释
"removeComments": true,
"pretty": true,
// 是相对于"baseUrl"进行解析
"paths": {
"vue": ["node_modules/vue/types"],
"@/*": ["src/*"]
}
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
}
  • 在src目录下创建sfc.d.ts(用来声明全局变量、class、module、function、命名空间)

我们在这里主要是让ts识别.vue文件、window对象和一些module

具体declare的使用方式请看这里

/**
* 告诉 TypeScript *.vue 后缀的文件可以交给 vue 模块来处理
* 而在代码中导入 *.vue 文件的时候,需要写上 .vue 后缀。
* 原因还是因为 TypeScript 默认只识别 *.ts 文件,不识别 *.vue 文件
*/
declare module "*.vue" {
import Vue from 'vue'
export default Vue
}
/**
* 告诉 TypeScript window是个全局对象,直接可用,这样就不会在window.xx = 123时报错
*/
declare var window: any
/**
* 引入部分第三方库/自己编写的模块的时候需要额外声明文件
* 引入的时候,需要使用类似 import VueLazyLaod from 'vue-lazyload' 的写法
*/
declare module 'vue-lazyload'
declare module '@zz/perf/vue'
declare module 'raven-js'
declare module 'raven-js/plugins/vue'

将src/main.js改为main.ts

项目改造

这个部分是最麻烦的,主要有几大块

  • 基础库改造

    如果你的基础库引用了大量的npm包,那么恭喜你,这部分你的改造成本会低很多。

    如果你的lib库有相当一部分都是自己手写的,那么,我也得恭喜你。。。

    我们自己的lib库里,有大量的自己维护的js文件。那么如果你要进行ts改造的话,通通都要改。

    举个例子:

    lib/url.js 中的getParam (算法并不高级,就是易读、兼容性好)

export default class URL{
/**
* @memberOf URL
* @summary 获取当前页面连接中指定参数
* @type {function}
* @param {string} param1 - 如果param2为undefined,param1是指从当前页面url中获取指定参数的key, 如果param2不为空,param1为指定的url
* @param {string} param2 - 可选参数,如果param2存在,则从指定的param1连接中获取对应参数的key
* @return {string|null}
*/
static getParam (param1, param2) {
let url = ''
let param = null;
// 如果只有一个参数,默认从当前页面链接获取参数
if (typeof param2 === 'undefined') {
url = window && window.location.href || ''
param = param1
// 从指定url中获取参数
} else {
url = param1
param = param2
}
// 排除hash的影响
url = url.split('#')[0]
if (url.indexOf('?') > -1) {
url = url.split('?')[1]
}
const reg = new RegExp('(^|&)' + param + '=([^&]*)[&#$]*', 'i')
const rstArr = url.match(reg)
if (rstArr !== null) {
return decodeURIComponent(rstArr[2])
}
return null
}
...
}

改造后的文件为:lib/url.ts

export default class URL {
/**
* @memberOf URL
* @summary 获取url中指定参数
* @type {function}
* @param {string} param1 - 如果param2为undefined,param1是指从当前页面url中获取指定参数的key, 如果param2不为空,param1为指定的url
* @param {string} param2 - 可选参数,如果param2存在,则从指定的param1连接中获取对应参数的key
* @return {string|null}
*/
static getParam (param1: string, param2?: string): string {
let url: string = ''
let param = null
// 如果只有一个参数,默认从当前页面链接获取参数
if (typeof param2 === 'undefined') {
url = window && window.location.href || ''
param = param1
// 从指定url中获取参数
} else {
url = param1
param = param2
}
url = url.split('#')[0]
if (url.indexOf('?') > -1) {
url = url.split('?')[1]
}
const reg = new RegExp('(^|&)' + param + '=([^&]*)[&#$]*', 'i')
const rstArr = url.match(reg)
if (rstArr !== null) {
return decodeURIComponent(rstArr[2])
}
return null
}
...
}

对于一个方法多种调用方式,如果你想完全改成typescript推荐的方式,你可以用到方法重载

我没有用是因为我不希望改变原有页面的使用方式。

注:对于一个大型项目来讲,我们并不建议上来就对全部的文件进行ts改造。

我们更建议采用渐进式改造方案,在不影响原有页面的情况下,逐一改造。具体方案后面会介绍

  • vue文件改造

src/components/helper/newUser/index.vue

<template>...</template>
<script>
import { LEGO_ATTR, initLegoData, legic } from '@/lib/legic'
import { getMyProfile } from '@/api/helper'
import { toast } from '@/lib/ZZSDK'
import myComponent from './myComponent.vue'
let flag = false // 是否发送视频点击埋点
export default {
components: {
// 自定义组件
myComponent
},
data () {
return {
// 用户头像
portrait: '',
// 用户名称
nickName: '',
// 是否点击播放
isPlay: false
}
},
mounted () {
this.initData()
initLegoData({
type: 'newUserGuide'
});
legic(LEGO_ATTR.newUserGuide.SHOW);
},
methods: {
initData () {
getMyProfile().then(data => {
console.log('data', data)
const { respData } = data
this.portrait = respData.portrait || ''
this.nickName = respData.nickname || ''
}).catch(err => {
toast({ msg: err })
})
},
goPageClick (type) {
switch (type) {
case 'SUN':
legic(LEGO_ATTR.newUserGuide.SUNVILLAGECLICK)
break
case 'FOOTBALL':
legic(LEGO_ATTR.newUserGuide.FOOTBALLCLICK)
break
case 'SIGN':
legic(LEGO_ATTR.newUserGuide.SIGNCLICK)
break
default:
return
}
},
videoClick () {
if (flag) {
return
} else {
flag = true
legic(LEGO_ATTR.newUserGuide.SIGNCLICK)
this.isPlay = true
this.$refs.video.play()
}
}
}
}
</script>
<style lang="scss" scoped>...</style>

改造后

<template>...</template>
<script lang="ts">
import { LEGO_ATTR, initLegoData, legic } from '@/lib/legic'
import { getMyProfile } from '@/api/helper.ts'
import { toast } from '@/lib/ZZSDK'
import { Component, Vue } from 'vue-property-decorator'
import test from './test.vue' let flag: boolean = false // 是否发送视频点击埋点
@Component({
components: {
test
}
})
export default class NewUser extends Vue {
// 用户头像
portrait = ''
// 用户名称
nickName = ''
// 是否点击播放
isPlay = false mounted (): void {
this.initData()
initLegoData({
type: 'newUserGuide'
});
legic(LEGO_ATTR.newUserGuide.SHOW)
} initData () {
// 获取profile信息
getMyProfile().then((data: any) => {
console.log('data', data)
const { respData } = data
this.portrait = respData.portrait || ''
this.nickName = respData.nickname || ''
}).catch((err: string) => {
toast({ msg: err })
})
} goPageClick (type: string) {
switch (type) {
case 'SUN':
legic(LEGO_ATTR.newUserGuide.SUNVILLAGECLICK)
break
case 'FOOTBALL':
legic(LEGO_ATTR.newUserGuide.FOOTBALLCLICK)
break
case 'SIGN':
legic(LEGO_ATTR.newUserGuide.SIGNCLICK)
break
default:
return
}
} videoClick () {
if (flag) {
return
} else {
flag = true
legic(LEGO_ATTR.newUserGuide.SIGNCLICK)
this.isPlay = true
this.$refs.video['play']()
}
}
}
</script>
<style lang="scss" scoped>...</style>

myComponent.vue改造前略,这里只展示改造后的组件

<template>
<div class="main">{{title}}{{name}}</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator' @Component
export default class MyComponent extends Vue { @Prop({ type: String, default: '' })
name: string title: string = '您好'
}
</script>
<style lang="scss" scoped>
.main{
display: none;
}
</style>

这里需要注意的是:

  • ts默认不会识别.vue文件,所以需要在sfc.d.ts文件中声明,同时在引入vue组件时,要加.vue后缀
  • 引入vue-property-decorator插件。采用修饰符的方式进行组件注册,这样里面的data、prop和function都通过扁平化方式调用(这也是官方推荐的方式)
  • ts中import引入文件,如果不写后缀,默认是js文件。如果js文件没有,则才识别ts文件

现在说下前面提到的改造方案:

这里其实主要涉及.vue文件和lib库的改造,vue文件没啥可说的,一个个改就可以了。主要说lib里面的文件,这里我建议:

  • 一开始保留原来的js文件,并不删除。这样目前尚未改造的文件可以继续使用
  • 新建对应的ts文件,比如lib中有util.js,新创建util.ts
  • 新改造的vue文件通通引入lib库中xx.ts(要加.ts后缀),如import Util from '@/lib/util.ts'

    这样可以一点点改造整个项目,同时未改造的页面照样可以运行。

ok以上就是我们改造的全部过程。

有什么问题可以指正,大家互相学习

原有vue项目接入typescript的更多相关文章

  1. Vue项目接入MQTT

    Vue项目接入MQTT 安装mqtt库 npm install mqtt --save Vue代码实现 <template> <div id="app"> ...

  2. vue项目接入markdown

    vue 项目接入 markdown 最近做一个项目,需要在vue项目中接入 markdown 编辑器,其实这个好接,他没有什么特别的样式,男的就是图片的上传. 今天给大家推荐一个插件 :mavonEd ...

  3. 初次在Vue项目使用TypeScript,需要做什么

    前言 总所周知,Vue新版本3.0 使用 TypeScript 开发,让本来就很火的 TypeScript 受到更多人的关注.虽然 TypeScript 在近几年才火,但其实它诞生于2012年10月, ...

  4. vue项目接入api接口

    我们在做项目时,一切基础在于数据上面,所以接入api接口是关键. 访问接口是我们会遇到跨域,而,vue-cli给我们提供了反向代理,所以我们只需要配置一下就可以了. 在config文件中找到index ...

  5. vue项目接入百度地图

    方法一 :使用第三方工具 vue-baidu-map 安装命令: yarn add vue-baidu-map --save 文档地址:https://dafrok.github.io/vue-bai ...

  6. vue项目中使用ts(typescript)入门教程

    最近项目需要将原vue项目结合ts的使用进行改造,这个后面应该是中大型项目的发展趋势,看到一篇不错的入门教程,结合它并进行了一点拓展记录之.本文从安装到vue组件编写进行了说明,适合入门. 1.引入T ...

  7. 在typeScript+vue项目中使用ref

    因为vue项目是无法直接操作dom的,但是有时候开发需求迫使我们去操作dom. 两个办法,一个是很low的再引入jq,然后通过jq来操作,但是这样就失去了我们使用vue的意义, 可惜的是我曾经这样干过 ...

  8. 如何在Vue项目中使用Typescript

    0.前言 本快速入门指南将会教你如何在Vue项目中使用TypeScript进行开发.本指南非常灵活,它可以将TypeScript集成到现有的Vue项目中任何一个阶段. 1.初始化项目 首先,创建一个新 ...

  9. TypeScript编写Vue项目结构解析

    使用TypeScript编写Vue项目也已经有了一段时间,笔者在刚刚使用TypeScript时候也是很茫然,不知道从何下手,感觉使用TypeScript写项目感觉很累赘并不像JavaScript那么灵 ...

随机推荐

  1. Jmeter(1)介绍

    JMeter是什么东西 Jmeter(Apache JMeter)是一个100%基于JAVA的应用程序,它的功能是 分析和衡量 web应用程序和各种服务的性能和负载能力 Jmeter不是一个浏览器,它 ...

  2. net读取文件字节流要注意的小细节

    ]; var numBytesToRead = (int)fileLenght; ; using (fs) { do { ); numBytesRead += n; numBytesToRead -= ...

  3. 使用Nginx反向代理和内容替换模块实现网页内容动态替换功能

    2016年11月21日 10:30:00 xian_02 阅读数:10943   Nginx是一款轻量级高性能服务器软件,虽然轻量,但功能非常强大,可用于提供WEB服务.反向代理.负载均衡.缓存服务. ...

  4. phpmail发送phpexcel生成的附件,php导出的Excel 作为邮件附件发送

    PHP发送邮件戳这里https://www.cnblogs.com/wangzhaobo/p/8931328.html PHP导出excel戳这里https://www.cnblogs.com/wan ...

  5. 2018-2019-2 网络对抗技术 20165220 Exp2 后门原理与实践

    实验内容 1.使用netcat获取主机操作Shell,cron启动2.使用socat获取主机操作Shell, 任务计划启动3.使用MSF meterpreter(或其他软件)生成可执行文件,利用nca ...

  6. SQL Server 获取 ActiveDirectory 用户信息

    SQL Server 是可以访问活动目录中的用户信息的. 1. 先用domain administrator建立一个linked server 2. 然后用OpenQuery去查询活动目录   大家可 ...

  7. sublime2 nodejs 执行编译无反应

    这个问题困扰了我得一周了,好不容易解决了, 一.问题描述: 安装网上的一些教程在sublime text 2 里面安装Nodejs 的编译环境,但是安装完之后执行编译没有任何输出信息,编译没有反应,只 ...

  8. 练习html,css,js仿制百度首页

    1.练习目的 练习使用html,scc,js 完成界面样式,用ul标签实现文本框下拉,通过js完成添加列表内容等功能 2.效果 3.程序代码 <!DOCTYPE html> <htm ...

  9. oracle主键和索引

    主键:能够唯一标识一条记录的字段为主键(亦或主码),不能重复的,不允许为空.作用:用来保证数据完整性个数:主键只能有一个 索引:作用:是提高查询排序的速度个数:一个表可以有多个索引 常用索引类型:No ...

  10. Python面向对象编程指南

    Python面向对象编程指南(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1SbD4gum4yGcUruH9icTPCQ 提取码:fzk5 复制这段内容后打开百度网 ...