在Vue项目中使用Typescript的实现
3.0迟迟没有发布release版本,现阶段在vue项目中使用typescript需要花不小的精力在工程的配置上面。主要的工作是webpack对ts,tsx的处理,以及2.x版本下面使用class的形式书写vue 组件的一些限制和注意事项。
webpack 配置
配置webpack对ts,tsx的支持,以便于我们在vue项目中使用typescript和tsx。
module.exports = { entry: './index.vue', output: { filename: 'bundle.js' }, resolve: { extensions: ['.ts', '.tsx', '.vue', '.vuex'] }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { ts: 'ts-loader', tsx: 'babel-loader!ts-loader', } } }, { test: /\.ts$/, loader: 'ts-loader', options: { appendtssuffixto: [/ts\.vue$/] } }, { test: /\.tsx$/, loader: 'babel-loader!ts-loader', options: { appendtsxsuffixto: [/tsx\.vue$/] } } ] } }
在上面的配置中,vue文件中的ts内容将会使用ts-loader处理,而tsx内容将会按照ts-loader-->babel-loader的顺序处理。
appendtssuffixto/appendtsxsuffixto 配置项的意思是说,从vue文件里面分离的script的ts,tsx(取决于<script lang="xxx"></script>)内容将会被加上ts或者tsx的后缀,然后交由ts-loader解析。
我在翻看了ts-loader上关于appendtsxsuffixto的讨论发现,ts-loader貌似对文件后缀名称有很严格的限定,必须得是ts/tsx后缀,所以得在vue-loader extract <script>中内容后,给其加上ts/tsx的后缀名,这样ts-loader才会去处理这部分的内容。
ts-loader只对tsx做语法类型检查,真正的jsx-->render函数应该交由babel处理。
所以我们还需要使用plugin-transform-vue-jsx来将vue jsx转换为真正的render函数。
// babel.config.json { "presets": ["env"], "plugins": ["transform-vue-jsx"] }
同时,配置ts对tsx的处理为preserve,让其只对tsx做type类型检查。
// tsconfig.json { "compileroptions": { "jsx": "preserve", }
使用vue cli 4.x
高版本的vue cli如4.x已经集成了vue + typescript的配置。选择use typescript + use class-style component syntax选项创建工程。
创建后的工程目录如下:
在src根目录下,有两个shims.xx.d.ts的类型声明文件。
// shims.vue.d.ts declare module "*.vue" { import vue from "vue"; export default vue; }
// shims.jsx.d.ts import vue, { vnode } from "vue"; declare global { namespace jsx { // tslint:disable no-empty-interface interface element extends vnode {} // tslint:disable no-empty-interface interface elementclass extends vue {} interface intrinsicelements { [elem: string]: any; } } }
它们是作什么用的呢?
shims.vue.d.ts给所有.vue文件导出的模块声明了类型为vue,它可以帮助ide判断.vue文件的类型。
shims.jsx.d.ts 为 jsx 语法的全局命名空间,这是因为基于值的元素会简单的在它所在的作用域里按标识符查找。当在 tsconfig 内开启了 jsx 语法支持后,其会自动识别对应的 .tsx 结尾的文件,(也就是vue 单文件组件中<script lang="tsx"></script>的部分)可参考
基本用法
在vue 2.x中使用class的方式书写vue组件需要依靠来对vue class做转换。
<script lang="ts"> import { component, prop, vue } from "vue-property-decorator"; export default class extends vue { @prop({ default: 'default msg'}) private msg!: string; name!: string; show() { console.log("this.name", this.name); } } </script>
导出的class是经过vue.extend之后的vuecomponent函数(理论上class就是一个function)。
其最后的结果就像我们使用vue.extend来扩展一个vue组件一样。
// 创建构造器 var profile = vue.extend({ template: '<p>{{firstname}} {{lastname}} aka {{alias}}</p>', data: function () { return { firstname: 'walter', lastname: 'white', alias: 'heisenberg' } } }) export default { components: { profile } }
注意上面的profile组件并不是和我们平时一样写的vue组件是一个plain object配置对象,它其实是一个vuecomponent函数。
父组件实例化子组件的时候,会对传入的vue object 进行扩展,使用vux.extend转换为组件函数。
如果components中的值本身是一个函数,就会省略这一步。这一点, 从vue 源码中可以看出。
if (isobject(ctor)) { ctor = basector.extend(ctor) }
上面的ctor就是在components中传入的组件,对应于上面导出的profile组件。
使用vuex
使用vuex-class中的装饰器来对类的属性做注解。
import vue from 'vue'import component from 'vue-class-component'import { state, getter, action, mutation, namespace } from 'vuex-class' const somemodule = namespace('path/to/module') @component export class mycomp extends vue { @state('foo') statefoo @state(state => state.bar) statebar @getter('foo') getterfoo @action('foo') actionfoo @mutation('foo') mutationfoo @somemodule.getter('foo') modulegetterfoo // if the argument is omitted, use the property name // for each state/getter/action/mutation type @state foo @getter bar @action baz @mutation qux created () { this.statefoo // -> store.state.foo this.statebar // -> store.state.bar this.getterfoo // -> store.getters.foo this.actionfoo({ value: true }) // -> store.dispatch('foo', { value: true }) this.mutationfoo({ value: true }) // -> store.commit('foo', { value: true }) this.modulegetterfoo // -> store.getters['path/to/module/foo'] } }
mixin
对于mixin,我们使用class的继承很容易实现类似功能。
import vue from 'vue' import { component } from 'vue-property-decorator' @component class deploymixin extends vue{ name: string; deploy(){ // do something } } @component class index extends deploymixin{ constructor(){ super() } sure(){ this.deploy() } }
vs code jsx快捷键
设置 vs code中对emmet的支持
"emmet.includelanguages": { "javascript": "html" }
或者是
"emmet.includelanguages": { "javascript": "javascriptreact" }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。