Vue-cli 3.5 结合TSX使用踩坑旅

// 期待Vue3.0版本对TS以及TSX的大力支持
其实挺想期待的,但是vue3目前为止核心重点还是捞住老用户,对ts的优化是一点一点的减少。。心累
// 后续再出对TS + Webpack + Vue3.0 的结合使用
后面有空想办法给vue3加层壳让其更能与ts良好的结合吧

最近项目重构,准备将所有固件升级,顺便引进TS来对项目的规范和代码提示的优化进行提升。之前写React项目的时候对JSX新式语法糖和TypeScript类型声明以及代码提示这块的喜爱还是比较高的,所以就想着在Vue项目中也能尝试以同样的方式进行开发,之前由于项目较老,很多坑陷入了死循环,所以干脆重新搭建新的环境进行测试。

环境准备:

1.安装vue-cli 3.5

npm install -g @vue/cli

2.使用脚手架创建新项目(可以查看官网教程

vue create app

运行后会进入项目首次初始化,第一段会提示你选择所需要的插件(上下移动,空格选择),我选择的是Babel、TypeScript、Router、Vuex、Css Pre-processors、Linter几项

image

选择好了过后回车,然后一路默认(具体安装每一步的含义可以复制下来翻译一下就能明白,这里先不赘述),到样式那里,我选择的是Less,语法检查需要选择TSLint,然年就静等安装完成。

3.初次运行默认项目

安装完成过后,需要对我们的产物进行验证,将工作区切换到刚才创建的项目之下,也就是 xxx/app,然后运行npm run serve,出现下面字样的话就代表你成功了,

image

4.创建一个tsx文件测试一下(如果想在*.vue文件中进行,可以将vue文件中的script改为<script lang="tsx"></script>)

在src某目录下创建一个demo.tsx文件,里面使用类组件的导出一个vue组件,代码如下:


import { Component, Vue } from 'vue-property-decorator';

@Component

export default class App extends Vue {

  protected render() {

    return <div>helloword</div>;

  }

}

将main.ts导入的./App.vue改为./demo,这是保存后查看首页应该就成了如下所示的样子

image

好,证明tsx文件可以生效了,生效之后,就需要测试下使用tsx文件是否能够完整替代.vue文件了,我列了如下检查点:

image

第一步,测试下v-model是否能被支持,将demo.tsx改造后代码如下:


import { Component, Vue } from 'vue-property-decorator';

@Component

export default class App extends Vue {

  public value = '';

  protected render() {

    return (

      <div>

        <input type='text' v-model={this.value} />

        {this.value}

      </div>

    );

  }

}

然后到页面上在输入框输入内容,右边就会跟着发生响应,这证明v-model是被支持的,v-on同理,这里赘述。

第二步,验证watch、computed、dataObserve、methods等特性

watch需要使用到vue-property-decorator中的Watch方法,代码如下:


import { Component, Vue, Watch } from 'vue-property-decorator';

@Component

export default class App extends Vue {

  public value = '';

  public msg = '';

  @Watch('value')

  protected valueWatch(newV: any, oldV: any) {

    this.msg = `监听到属性value发生变化,新的值为:${newV}`;

  }

  protected render() {

    return (

      <div>

        <input type='text' v-model={this.value} />

        {this.msg}

      </div>

    );

  }

}

保存后到页面上看,输入值后右侧文字会跟随输入而改变,证明watch是OK的。

computed的使用方法是get name(){},代码如下:


import { Component, Vue, Watch } from 'vue-property-decorator';

@Component

export default class App extends Vue {

  public value = '';

  public msg = '';

  public get valueLength() {

    return this.value.length;

  }

  @Watch('value')

  protected valueWatch(newV: any, oldV: any) {

    this.msg = `监听到属性value发生变化,新的值为:${newV}`;

  }

  protected render() {

    return (

      <div>

        <input type='text' v-model={this.value} />

        {this.valueLength}

      </div>

    );

  }

}

保存后在输入框输入时右侧则会打印出相应的长度,证明computed特性是被支持的。

methods的用法是直接在class中声明一个函数,声明后等同于放在methods中的方法,但要特别注意的是,methods的方法要与vue自带的特性方法名要区分开,如果你声明一个方法为created(){}或者是mounted之类的,其函数实际上是在组件的生命周期中被调用的函数,例如:


import { Component, Vue, Watch } from 'vue-property-decorator';

@Component

export default class App extends Vue {

  public value = '';

  public msg = '';

  public get valueLength() {

    return this.value.length;

  }

  public created() {

    console.log('我在组件创建时被调用');

  }

  public handleClick() {

    console.log('我被点了');

  }

  @Watch('value')

  protected valueWatch(newV: any, oldV: any) {

    this.msg = `监听到属性value发生变化,新的值为:${newV}`;

  }

  protected render() {

    return (

      <div>

        <input type='text' v-model={this.value} />

        {this.valueLength}

        <button onClick={this.handleClick}>点我</button>

      </div>

    );

  }

}

使用外部引入样式文件,在项目中,如果需要使用css module的话,需要将文件名声明为 .module.less /.module.css的格式,表示使用css module,如果不想写的话可以在vue.config.js中配置cssmodules为true来解决(参见:传送门),这里先不修改配置文件,创建一个文件名为test.module.less,在demo.tsx中引入,并测试其css module和普通css穿透的效果,需要注意的是,在*.vue文件中,css穿透一般是使用>>>或者/deep/来实现的,但是在样式文件中,应该遵循loader的处理方式,例如less,需要使用:global(.className)的方式来实现,代码如下

demo.tsx


import { Component, Vue, Watch } from 'vue-property-decorator';

import style from './test.module.less';

@Component

export default class App extends Vue {

  public value = '';

  public msg = '';

  public get valueLength() {

    return this.value.length;

  }

  public created() {

    console.log('我在组件创建时被调用');

  }

  public handleClick() {

    console.log('我被点了');

  }

  @Watch('value')

  protected valueWatch(newV: any, oldV: any) {

    this.msg = `监听到属性value发生变化,新的值为:${newV}`;

  }

  protected render() {

    return (

      <div class={style.class1}>

        <input type='text' v-model={this.value} />

        {this.valueLength}

        <button class='class2' onClick={this.handleClick}>

          点我

        </button>

      </div>

    );

  }

}

test.module.less


.class1 {

    color: blue;

    :global(.class2) {

        background: red;

    }

}

保存后可以看到文字变成了蓝色,按钮变成了红色

image

需要注意的是,如果你导入less的时候被提示找不到模块xxx.less,的话,你需要在项目中声明下对less模块的支持,在项目中找到或创建shims-vue.d.ts文件,添加内容:


declare module "*.less" {

  const less: any;

  export default less;

}

再切换回去的话就没有错误提示了。这个错误提示是tslint检查的,虽然不影响使用,但是看着报错也挺不舒服的是吧。

再验证下props传值和ref的功能,在同级目录新建一个组件,xxx.tsx,内容如下:


import { Component, Vue, Prop } from 'vue-property-decorator';

@Component

export default class App extends Vue {

  @Prop({ required: false })

  public msg?: string;

  protected render() {

    return <span>{this.msg}</span>;

  }

}

再在demo.tsx中引入组件,内容修改后如下:


import { Component, Vue, Watch } from 'vue-property-decorator';

import style from './test.module.less';

import Xxx from './xxx';

@Component

export default class App extends Vue {

  public value = '';

  public msg = '';

  public get valueLength() {

    return this.value.length;

  }

  public created() {

    console.log('我在组件创建时被调用');

  }

  public handleClick() {

    console.log('我被点了');

  }

  @Watch('value')

  protected valueWatch(newV: any, oldV: any) {

    this.msg = `监听到属性value发生变化,新的值为:${newV}`;

  }

  protected render() {

    return (

      <div class={style.class1}>

        <input type='text' v-model={this.value} />

        {this.valueLength}

        <button class='class2' onClick={this.handleClick}>

          点我

        </button>

        <div>

          <Xxx msg='渲染组件成功' />

        </div>

      </div>

    );

  }

}

保存后查看页面,可以看到渲染出了组件的内容,证明组件传值是可以正常工作的

image

但是此时可能会遇到一个tslint的错误提示,Type '{ msg: string; }' is not assignable to type 'ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<string, any>>',说你的key在组件上根本找不到,这个就很蛋疼了,如果把传值的方式改为 <Xxx props={{msg:'传值'}}/>的话,报错就会消失,这是因为vue官方设定的属性中就有props这个属性值,具体文件位置在 node_modules/vue/types/options 的ComponentOptions中可以看到,如果直接修改源码的话可以添加支持任意属性,但是这种方式显然不够完美,那么如何做呢?实际上应该是在项目的声明文件中,将对应的interface接口扩展一下就OK了,修改或创建shims-tsx.d.ts文件,在其中添加如下内容:


declare module "vue/types/options" {

  interface ComponentOptions<V extends Vue> {

    [propName: string]: any;

    ref?: string;

  }

}

ref我也添加进去了,原因是为了方便代码提示,这样的话,我在JSX代码中,输入r就会出现ref的提示了

image

如果需要添加v-model或者是v-on之类的,可以添加为vModel驼峰的方式来声明,在JSX中,他们是兼容的,并且,如果是段斜杠的方式声明的话,vscode不会有代码提示。

以上就是本次踩坑之旅的全部内容,没写过文档,整理的有点乱.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,165评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,503评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,295评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,589评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,439评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,342评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,749评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,397评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,700评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,740评论 2 313
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,523评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,364评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,755评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,024评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,297评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,721评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,918评论 2 336