一.引入TypeScript
// ts-loader typescript 必须安装,其他的相信你以后也会装上的
npm i ts-loader typescript tslint tslint-loader tslint-config-standard --save-dev
- ts-loader:TypeScript 为 Webpack 提供了 ts-loader,其实就是为了让webpack识别 .ts .tsx文件
- tslint-loader跟tslint:我想你也会在.ts .tsx文件 约束代码格式(作用等同于eslint)
- tslint-config-standard:tslint 配置 standard风格的约束
二.修改webpack配置文件(webpack.base.conf.js)
1.入口文件修改为main.ts
entry: {
app: './src/main.ts'
}
2.扩展名resolve.extensions新增.ts后缀
extensions: ['.js', '.vue', '.json', '.ts']
3.添加解析ts文件的规则
{
test: /\.ts$/,
exclude: /node_modules/,
enforce: 'pre',
loader: 'tslint-loader',
},
{
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
appendTsSuffixTo: [/\.vue$/]
}
}
三:添加 tsconfig.json
ts-loader 会检索当前目录下的 tsconfig.json 文件,根据里面定义的规则来解析.ts文件(就跟.babelrc的作用一样)
{
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
],
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"allowJs": true,
"module": "esnext",
"target": "es5",
"moduleResolution": "node",
"isolatedModules": true,
"lib": [
"dom",
"es5",
"es6",
"es7",
"es2015.promise",
"scripthost"
],
"sourceMap": true,
"pretty": true,
"strictFunctionTypes": false,
"importHelpers": true
}
}
四:添加 tslint.json
{
"extends": "tslint-config-standard",
"globals": {
"require": true
},
"rules": {
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"deprecation": {
"severity": "warn"
},
"eofline": true,
"forin": true,
"import-blacklist": [
true,
"rxjs",
"rxjs/Rx"
],
"import-spacing": true,
"indent": [
true,
"spaces"
],
"interface-over-type-literal": true,
"label-position": true,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"space-before-function-paren": ["error", "never"],
"ter-indent": 4,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"prefer-const": true,
"quotemark": [
true,
"single"
],
"radix": true,
"semicolon": [
true,
"always"
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"typeof-compare": true,
"unified-signatures": true,
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
]
}
}
五:让 ts 识别 .vue
由于 TypeScript 默认并不支持 *.vue 后缀的文件,所以在 vue 项目中引入的时候需要创建一个 vue-shim.d.ts 文件,放在项目项目对应使用目录下,例如 src/vue-shim.d.ts
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}
意思是告诉 TypeScript *.vue 后缀的文件可以交给 vue 模块来处理。
而在代码中导入 *.vue 文件的时候,需要写上 .vue 后缀。原因还是因为 TypeScript 默认只识别 *.ts 文件,不识别 *.vue 文件:
import Component from 'components/component.vue'
六:改造vue-router
因为 ts 无法识别 require,所以按需加载路由的时候需要安装声明文件npm i @types/webpack-env -save-dev;然后才可以用const rank = r => require.ensure([], () => r(require('../pages/rank/rank')), 'rank'); 这样的写法;
七:开始修改App.vue文件
- 在script 标签上加上 lang="ts", 意思是让webpack将这段代码识别为typescript 而非javascript
- 修改vue组件的构造方式( 跟react组件写法有点类似, 详见官方 ), 如下图
- 用vue-property-decorator语法改造之前代码
<template>
<div id="app">
<img src="./assets/logo.png">
<router-view/>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
@Component({})
export default class App extends Vue {
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
八:改造 .vue 文件
npm i vue-class-component vue-property-decorator --save
- vue-class-component:强化 Vue 组件,使用 TypeScript/装饰器 增强 Vue 组件
- vue-property-decorator:在 vue-class-component 上增强更多的结合 Vue 特性的装饰器
1.vue-class-component
vue-class-component 对 Vue 组件进行了一层封装,让 Vue 组件语法在结合了 TypeScript 语法之后更加扁平化:
<template>
<div>
<input v-model="msg">
<p>msg: {{ msg }}</p>
<p>computed msg: {{ computedMsg }}</p>
<button @click="greet">Greet</button>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
@Component
export default class App extends Vue {
// 初始化数据
msg = 123
// 声明周期钩子
mounted () {
this.greet()
}
// 计算属性
get computedMsg () {
return 'computed ' + this.msg
}
// 方法
greet () {
alert('greeting: ' + this.msg)
}
}
</script>
使用时需要注意的问题
问题1: ts 无法识别$ref
解决办法:
- 直接在 this.refs.lyricsLines as HTMLDivElement;
- 在export default class xxx extends Vue里面声明全部的$ref 的类型
$refs: {
audio: HTMLAudioElement,
lyricsLines: HTMLDivElement
}
问题2: 怎么把mixin引入
@Component({
/*components: {
Swipe
},*/
mixins: [mixin]
})
2.vue-property-decorator
vue-property-decorator 是在 vue-class-component 上增强了更多的结合 Vue 特性的装饰器,新增了这 7 个装饰器:
- @Emit
- @Inject
- @Model
- @Prop
- @Provide
- @Watch
- @Component (从 vue-class-component 继承)
在这里列举几个常用的@Prop/@Watch/@Component
import { Component, Emit, Inject, Model, Prop, Provide, Vue, Watch } from 'vue-property-decorator'
@Component
export class MyComponent extends Vue {
@Prop()
propA: number = 1
@Prop({ default: 'default value' })
propB: string
@Prop([String, Boolean])
propC: string | boolean
@Prop({ type: null })
propD: any
@Watch('child')
onChildChanged(val: string, oldVal: string) { }
}
上面的代码相当于:
export default {
props: {
checked: Boolean,
propA: Number,
propB: {
type: String,
default: 'default value'
},
propC: [String, Boolean],
propD: { type: null }
}
methods: {
onChildChanged(val, oldVal) { }
},
watch: {
'child': {
handler: 'onChildChanged',
immediate: false,
deep: false
}
}
}
九:注意点
1.如果你引用第三方无类型声明的库,那就需要自己编写x.d.ts文件
2.如果引用 ui 组件的时候,如果控制台出现Property '$xxx' does not exist on type 'App'的话,那么可以在vue-shim.d.ts增加
declare module 'vue/types/vue' {
interface Vue {
$xxx: any,
}
}
其他类似的报错:比如我写了一个自定义的权限指令 在vue文件中直接this.auth会报错Property 'auth' does not exist on type 'orderList'.
解决办法:(this as any).auth('TESTXMC_order-status-index');
3.如何全局使用window的变量??
declare global{
interface Window {
DEBUG: string;
API_HOST: string
}
}
(window as any).DEBUG = 'dev';
4.如何引入jquery?
npm install --save jquery
npm install --save-dev @types/jquery
在index.html里面引入
<script src="node_modules/jquery/dist/jquery.js"></script>