Vue2的tsx开发入门完全指南

本篇文章尽量不遗漏重要环节,本着真正分享的心态,不做标题党

下面进入正题:

由于现在vue的官方脚手架已经非常完善我们就不单独配置webpack了,节省大量的时间成本。

首先使用@vue/cli创建一个vue模版项目(记得是@vue/cli不是vue-cli还不知道的人可以点此传送门进入先导学习站)。

在自己觉得合适的目录下打开命令行输入如下代码,创建一个名为vue-tsx的项目

接下来的步骤vue的cli会给出相应的配置提示,着重配置已截图

第一步选择自定义配置
在这里插入图片描述
第二步选择如图的配置
在这里插入图片描述
剩下的按个人喜好自己选择就可以了

创建完成后的项目结构如图所示
在这里插入图片描述
从图上看出这是一个普通的vue模版项目,使用typescript语言开发

默认使用的仍然是vue的template进行渲染

正常在这种情况下就可以开发直接写代码了,模版项目所提供的示例代码已经很良心了

不过今天要介绍的是使用tsx语法进行开发vue项目

首先介绍一下什么事tsx

其实他就是typescript的jsx语法

那么什么是jsx呢?从这里介绍的话又变成无脑长文了,所以直接掠过,想了解jsx的人可以先去看一下react的开发文档5分钟上手

,但是必须要说的是为什么要使用tsx来写vue项目?vue提供的自带模版不香吗?网友对vue和react的争论喋喋不休到现在,我在这里给的答案其实很简单,vue和react之间没有好坏之分,论性能差距在使用上已经近乎55开,论生态各自都很完善了,这两个框架并存的原因很简单,vue的作者在自己的文章中曾经也提过,创造vue项目只不过是想有一个“自己用起来顺手的框架”。答案就在这句话上,所以我觉得没必要争论哪个好,其实没有可比性,只是喜欢的人各自会觉得对方好而已。

所以今天介绍tsx开发vue项目其实原因很简单,就是让适应了jsx语法的人能无缝从react过渡到vue上。就是给用起来舒服的人准备了一个方案而已。

所以继续我们的项目搭建

接下来先运行一下刚才的模版项目

在vue-tsx目录下打开命令行输入

npm run serve 

出现如下图片证明以上操作全部没问题
在这里插入图片描述
在这里插入图片描述
以上操作全部通过后可以关闭服务器了,我们下一步要做的是修改项目的目录结构

首先

删除views文件夹,

删空components文件夹的内容保留文件夹,

删除App.vue文件

项目结构与图片一样即可,其他地方暂时不要动
在这里插入图片描述
首先将router文件夹中的index.ts文件内容修改为如下代码

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes:any = []

const router:VueRouter = new VueRouter({
  routes
})

export default router 

然后修改main.ts中的代码为如下

import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app') 

并在src下新建名为App.tsx的文件内容为

import { Vue ,Component } from 'vue-property-decorator';
@Component
export default class App extends Vue{
	render(){
		return (
			<div>I am the first module of tsx for the Vue Project! </div>
		)
	}
} 

其他地方暂时不需要改造

然后重新使用npm run serve启动项目访问默认地址
在这里插入图片描述
当界面出现如上图情况的欢迎语,说明我们已经成功的在vue项目中使用tsx模版语法了

与react一样tsx在vue项目中也是使用render方法混合html模版来实现界面渲染,用法与react一样,他在vue项目中会被解析成vue的render:h => h()形式去渲染页面,所以使用tsx模版开发vue带来的负面影响是我们牺牲了vue自带的很多语法糖,如最基本的v-if,v-for,prop.sync等等,不过他带来的好处是我们可以使用tsx语法更自由的去处理这些问题,并且使用tsx可以进行更好的抽象以及工程化的去处理前端项目,各有千秋。

我们在下文会详细的介绍相关内容,首先还是继续进行下去

光使用tsx实现了App.vue相当于没有解决任何问题。

我们下一步要改造的是VueRouter

所以

第一步在src下创建一个名为pages的文件夹

第二步在pages下分别创建Index.tsx,以及Login.tsx两个文件

成功后如下图
在这里插入图片描述
分别在两个文件中输入默认代码

Login.tsx

import { Vue , Component } from 'vue-property-decorator'
@Component
export default class Login extends Vue{
	render(){
		return (
			<div>login.tsx</div>
		)
	}
} 

Index.tsx

import { Vue , Component } from 'vue-property-decorator'
@Component
export default class Index extends Vue{
	render(){
		return (
			<div>index.tsx</div>
		)
	}
} 

然后我们把这两个路由的页面加入到router中

在router文件夹下的index.ts中加入如下代码

import Vue from 'vue'
import VueRouter from 'vue-router'
import Index from '@/pages/Index'
Vue.use(VueRouter)

const routes:Array<any> = [
	{
		path:'/',name:'index',component:Index
	},
	{
		path:'/login',name:'login',component:() => import('@/pages/Login')
	}
]

const router:VueRouter = new VueRouter({
  routes
})

export default router 

这里我们子啊router中使用了直接引用和懒加载两种方式去加载路由界面来测试vue对tsx的兼容性

最后一步修改App.tsx,在文件中加入路由的容器,代码如下

import { Vue ,Component } from 'vue-property-decorator';
@Component
export default class App extends Vue{
	render(){
		return (
			<div>
				<router-view></router-view>
			</div>
		)
	}
} 

以上步骤严格按照说明编写后,无需重启服务我们访问默认路径就可以看到变化(如中途遇见问题可重启服务)

在访问http://localhost:8080/#/,以及http://localhost:8080/#/login两个地址分别显示如下图就说明成功了,如有问题请自行检查在这里插入图片描述
在这里插入图片描述
到这里我们已经实现了所有页面使用tsx来替换vue模版

到目前渲染数据都没有问题,但是我们还没有设置页面的样式,下面就介绍一下如何在tsx中使用css样式

由于我在创建项目的时候使用的是node-sass来加载sass-loader所以这里我们使用的scss模版来编写css

App.tsx为例,介绍 一下如何使用scss在tsx中

第一步在src下创建一个名为App.module.scss的文件(中间一定要加.module,这个是vue脚手架的规范,如果想自由命名请熟读@vue/cli的官方文档,本文我们暂时采取默认方式)在其中设置如下样式

.app{
	background: lightblue;
}

第二步改造App.tsx的代码给根标签加一个class=“App”

import { Vue ,Component } from 'vue-property-decorator';
//以模块的形式引入当前的样式文件
import style from './app.module.scss';
@Component
export default class App extends Vue{
	render(){
		return (
		//这里代表将app.module.scss中的.app这个class注入到标签中
			<div class={style.app} >
				<router-view></router-view>
			</div>
		)
	}
} 

我们会发现当前的网页背景颜色会变成我们设置的light-blue如图所示
在这里插入图片描述
但是这里有一个问题,观察当前node命令行窗口会发现Cannot find module ‘./App.module.scss’.错误

这个错误是由于当前的项目默认是不认识scss语法的,我们需要在项目src下的shims-vue.d.ts文件中加入如下代码并重启服务

declare module '*.vue' {
	import Vue from 'vue'
	export default Vue
}
//解决scss文件报错问题
declare module '*.scss'{
	const sass:any
	export default sass
} 

执行此方法后就不会报错了

接下来我们来测试一下如何使用tsx中的scss

首先我们试试可不可以在样式中对html和body标签进行操作,App.module.scss修改为如下

html,body{
	width: 100%;
	height: 100%;
	margin: 0;
	color: red;
}
.app{
	background: lightblue;
	height: 100%;
} 

当我们重新访问页面时出现下图结果
在这里插入图片描述
接下来我们给Index.tsx中的代码修改一下,测试一下可不可以用App.module.scss影响他的子组件,加入index-page的className

import { Vue , Component } from 'vue-property-decorator'
@Component
export default class Index extends Vue{
	render(){
		return (
			<div class="index-page">index.tsx</div>
		)
	}
} 

然后在App.module.scss中设置

html,body{
	width: 100%;
	height: 100%;
	margin: 0;
	color: red;
}
.app{
	background: lightblue;
	height: 100%;
	.index-page{
		text-align: center;
		color: blue;
	}
} 

访问页面后,index.tsx的字体并没有居中也没有变色,查看控制台发现样式并没有注入进来,看来当前的模块引入影响的只是当前组件本身,不过在vue的模版中的style标签下可以通过/deep/的方式来实现样式的跨组件穿透,如果我一定要在App.module.scss中设置其他组件的公共样式,把它当成一个基础样式组件来用呢?当然有解决方案,我们只需要做一个简单的修改

html,body{
	width: 100%;
	height: 100%;
	margin: 0;
	color: red;
}
.app{
	background: lightblue;
	height: 100%;
/*穿透效果*/
	:global(.index-page){
		text-align: center;
		color: blue;
	}
} 

使用:global就可以实现/deep/的功能
在这里插入图片描述
到这里样式的基本使用介绍完毕。

接下来是核心环节,就是关于自定义组件以及在vue中如何使用ts开发

修改index.tsx的内容为

import { Vue , Component } from 'vue-property-decorator'
@Component
export default class Index extends Vue{
	//相当于js中的data中的其中一个属性
	private title?:string = '我是标题'

	private author?:string = 'LeoZhang'
	//相当于computed中的函数
	get authorComputed(){
		return `作者是:${this.author}`
	}
	render(){
		return (
			<div class="index-page">
				<h2>{this.title}<small>by {this.author}</small></h2>
				<p>{this.authorComputed}</p>
			</div>
		)
	}
} 

在这里插入图片描述
当前内容输入成功说明你已经习惯了ts与js的区别,这个结构体现出了ts更加清晰的结构化代码

接下来我们测试一下methods与v-model如何实现

Index.tsx修改为如下内容

import { Vue , Component } from 'vue-property-decorator'
import style from './index.module.scss';
@Component
export default class Index extends Vue{
	//相当于js中的data中的其中一个属性
	private title?:string = '我是标题'

	private author?:string = 'LeoZhang'
	//相当于computed中的函数
	get authorComputed(){
		return `作者是:${this.author}`
	}
	//生命周期函数
	created(){
		console.log('我是默认的生命周期')
	}
	//相当于methods
	handleClick(arg:string):void{
		this.title = arg;
	}
	render(){
		return (
			<div class="index-page">
				<h2>{this.title}<small>by {this.author}</small></h2>
				<p>{this.authorComputed}</p>
				<button class={style['p-btn']} onClick={this.handleClick.bind(this,'我是新标题')}>改变标题</button>
				<br/>
				测试v-model改变author
				<input v-model={this.author}/>
			</div>
		)
	}
} 

在这里插入图片描述
测试结果为上图内容

可以看出当前的tsx语法中v-model还是被继续支持的不过v-on和v-bind都有相应的变化这里首先看到的是v-on变成了on事件名的写法,而且给事件传参数使用的是.bind这里与vue自带的template是完全不一样的

接下来我们在login.tsx中引入一个自定义组件来看一下自定义组件的参数和一些内容是否有变化

首先在components中声明一个Test组件
在这里插入图片描述
组件代码如下

import {
	Vue,
	Prop,
	Watch,
	Emit,
	Model,
	Component
} from 'vue-property-decorator'
@Component
export default class Test extends Vue{
	//代表js的props属性可在注解中设置类型是否必填默认值等
	@Prop({required:false,type:String,default:'我是默认值'})
	private msg?:string;

	render(){
		return (
			<div class="test">
				{this.msg}
			</div>
		)
	}
} 

然后在Login.tsx中做如下修改

import { Vue , Component } from 'vue-property-decorator'
import Test from '@/components/Test';
@Component
export default class Login extends Vue{

	private title?:string = '我是Login页面'

	render(){
		return (
			<div>
				{this.title}<br/>
				<Test></Test>
			</div>
		)
	}
}

此时访问http://localhost:8080/#/login

如果显示为如下图就说明已经配置成功一个基础组件了
在这里插入图片描述
我们测试给定义的组件msg传入一个参数

import { Vue , Component } from 'vue-property-decorator'
import Test from '@/components/Test';
@Component
export default class Login extends Vue{

	private title?:string = '我是Login页面'

	private msg:string = '我是login传入的msg'

	render(){
		return (
			<div>
				{this.title}<br/>
				<Test msg={this.msg}></Test>
			</div>
		)
	}
}

之后页面的值如果变为如图,说明成功
在这里插入图片描述
不过此处会在node控制台报错,错误说明是检测不到有msg这个参数类型因为这个参数是我们后创建的vue在默认的组件中是检测不到的所以为了让框架能不管我们自己创建的参数我们需要在shims-tsx.d.ts文件中加入如下代码

declare module "vue/types/options" {

  interface ComponentOptions<V extends Vue> {

	[propName: string]: any;

  }
}

之后重启服务,这样我们自定义组件的参数就不会出现报错了

之后我们再测试一下给组件绑定v-model如何实现双向绑定并监听参数

将Test.tsx的代码修改为如下,代码注释已经添加到代码中了

import {
	Vue,
	Prop,
	Watch,
	Emit,
	Model,
	Component
} from 'vue-property-decorator'
@Component
export default class Test extends Vue{
	//代表js的props属性可在注解中设置类型是否必填默认值等
	@Prop({required:false,type:String,default:'我是默认值'})
	private msg?:string;
	//Model装饰器相当于model属性参数相当于给event赋值,装饰器设置的属性相当于设置prop属性
	@Model('cc')
	@Prop({required:false,type:String,default:'我是双向绑定的默认值'})
	private value?:string;
	//相当于调用this.$emit('cc',val)
	@Emit('cc')
	sendValue(val:string){}
	//相当于watch下监听value属性的变化
	@Watch('value')
	handleWatchValue(newVal:string,oldVal:string){
		console.log(newVal,oldVal);
	}

	handleInput(event:InputEvent){
		//相当于调用this.$emit('cc',val)
		this.sendValue(event.target.value);
	}

	get getValue(){
		return `组件内部的value:${this.value}`
	}
	render(){
		return (
			<div class="test">

				{this.msg}<br/>

				<input value={this.value}
					onInput={this.handleInput}/>
				<br/>
				{this.getValue}
			</div>
		)
	}
}

然后在Login.tsx中修改为如下代码

import { Vue , Component } from 'vue-property-decorator'
import Test from '@/components/Test';
@Component
export default class Login extends Vue{

	private title?:string = '我是Login页面'

	private msg:string = '我是login传入的msg'

	private value:string = '我是外部传入的value'

	get getValue(){
		return `组件外部的value:${this.value}`
	}

	render(){
		return (
			<div>
				{this.title}<br/>
				{this.getValue}
				<Test msg={this.msg} v-model={this.value}></Test>
			</div>
		)
	}
}

成功后会得到如下结果

在这里插入图片描述

可以看到在Test组件中创建的input标签可以触发value属性的变化,并同时通知了外部传入的value属性进行变更,大量使用ts的装饰器(与java中的注解原理类似)这样可以更加直观的进行逻辑归纳,适合结构化开发。

到这里vue+tsx的基本入门关已经过了,掌握本文的技巧之后便开启了vue的tsx之旅。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值