// 期待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几项
选择好了过后回车,然后一路默认(具体安装每一步的含义可以复制下来翻译一下就能明白,这里先不赘述),到样式那里,我选择的是Less,语法检查需要选择TSLint,然年就静等安装完成。
3.初次运行默认项目
安装完成过后,需要对我们的产物进行验证,将工作区切换到刚才创建的项目之下,也就是 xxx/app,然后运行npm run serve,出现下面字样的话就代表你成功了,
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,这是保存后查看首页应该就成了如下所示的样子
好,证明tsx文件可以生效了,生效之后,就需要测试下使用tsx文件是否能够完整替代.vue文件了,我列了如下检查点:
第一步,测试下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;
}
}
保存后可以看到文字变成了蓝色,按钮变成了红色
需要注意的是,如果你导入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>
);
}
}
保存后查看页面,可以看到渲染出了组件的内容,证明组件传值是可以正常工作的
但是此时可能会遇到一个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的提示了
如果需要添加v-model或者是v-on之类的,可以添加为vModel驼峰的方式来声明,在JSX中,他们是兼容的,并且,如果是段斜杠的方式声明的话,vscode不会有代码提示。
以上就是本次踩坑之旅的全部内容,没写过文档,整理的有点乱.