饥人谷技术博客

使用Vue + TypeScript + TSX 实现 CNod

2020-05-12  本文已影响0人  EnochQin

前言: 众所周知,Vue很优秀,TypeScript也很优秀,但是Vue + TypeScript就会出现各种奇奇怪怪的问题。本文就将介绍我在「CNode 社区」这个项目开发的过程中遇到一些问题和解决办法。希望对你在Vue中使用TypeScript有所帮助。


项目源码及预览地址

效果预览

项目简介

仿CNode社区,使用Vue + TypeScript + TSX 等相关技术栈实现了原社区的看帖、访问用户信息、查看回复列表、查看用户信息、博客列表页分页查看等功能。
后端接口调用的是CNode 官方提供的api
本项目中的所有组件都使用了Vue的渲染函数render 以及 TSX

项目安装及启动

yarn install
yarn serve

技术栈


Vue + TypeScript 和 Vue的常规写法有什么不同

起手式

  1. 首先我们要把<script>标签的lang属性改为ts,即<script lang="ts">
  2. 要在Vue项目中引入 vue-property-decorator,后续很多操作都需要引用这个库里面的属性(包括VueComponent 等)。

shims-tsx.d.tsshims-vue.d.ts的作用

如果用vue-cli 直接生成一个「Vue + TS」的项目,我们会发现在 src 目录下出现了这两个文件,那么它们的作用是什么呢?

基于class的组件

引入组件 import component

Data 数据

Computed 计算属性

Methods 方法

在TS里面写methods,就像写class中的方法一样,有一个可选的修饰符。

生命周期钩子

生命周期钩子的写法和上一条写methods是一样的。Vue组件具有八个生命周期挂钩,包括createdmounted等,并且每个挂钩使用相同的TypeScript语法。这些被声明为普通类方法。由于生命周期挂钩是自动调用的,因此它们既不带参数也不返回任何数据。因此,我们不需要访问修饰符,键入参数或返回类型。

Props

我们可以在Vue的组件里面使用@Prop装饰器来替代 props,在Vue中,我们能给props提供额外的属性,比如required, default, type。如果用TypeScript,我们首先需要从vue-property-decorator引入Prop装饰器。我们甚至可以用TS提供的readonly来避免在代码中不小心修改了props
(备注:TypeScript中的赋值断言。!: 表示一定存在, ?:表示可能不存在。)

Ref

在Vue中我们经常会使用this.$refs.xxx 来调用某个组件中的方法,但是在使用TS的时候,有所不同:

<Loading ref="loading" />

export default class Article extends Mixins(LoadingMixin) {
  $refs!: {
    loading: Loading;
  };
}

$refs里面声明之后,TS就可以识别到 ref 属性了,调用方式和JS一样:this.$refs.loading.showLoading();

Watch

要想用watch侦听器的话,在TS中就要使用@Watch装饰器(同样从vue-property-decorator引入)。

Emit

这里同样要从vue-property-decorator引入装饰器@Emit

Mixin

想要在Vue+TypeScript中使用mixin,首先我们先创建一个mixin文件:

import { Component, Vue } from 'vue-property-decorator'
@Component
class ProjectMixin extends Vue {
  public projName: string = 'My project'
  public setProjectName(newVal: string): void {
    this.projName = newVal
  }
}
export default ProjectMixin

想要使用上面代码中的mixin,我们需要从vue-property-decorator 中引入 Mixins 以及 包含上述代码的mixins 文件,具体写法如下,主要不同就是组件不继承自Vue,而是继承自Mixins

<template>
  <div class="project-detail">
    {{ projectDetail }}
  </div>
</template>
<script lang="ts">
import { Component, Vue, Mixins } from 'vue-property-decorator'
import ProjectMixin from '@/mixins/ProjectMixin'
@Component
export default class Project extends Mixins(ProjectMixin) {
  get projectDetail(): string {
    return this.projName + ' ' + 'Preetish HS'
  }
}
</script>

Vuex

Vuex是大多数Vue.js应用程序中使用的官方状态管理库。最好将store分为 namespaced modules,即带命名空间的模块。我们将演示如何在TypeScript中编写Vuex。

Axios 封装

在Vue的项目中,我们使用 axios 来发送 AJAX 请求,我在项目里写了 axios 的统一拦截器,这里的拦截器写法和 JS 没有任何区别,但是在使用该拦截器发送请求的方法会有一些不同之处,具体代码可以参考项目中的api请求代码 。下面我贴一段代码简单介绍一下:

export function getTopicLists(
  params?: TopicListParams
): Promise<Array<TopicListEntity>> {
  return request.get("topics", {
    params
  });
}

使用TypeScript,最重要的就是类型,所以在上述代码中,传进来的参数规定类型为TopicListParams ,而函数返回的参数是Promise<Array<TopicListEntity>>,这样我们在调用getTopicLists的时候,就可以写成这样:

// 使用await
const response = await getTopicLists(); // response 即返回的Array<TopicListEntity>
// 或使用promise.then
await getTopicLists({
    limit: 40,
    page
  }).then(response => {
    // response 即返回的Array<TopicListEntity>
  })
});

另外:一般来说后端传给前端的响应体,我们应该添加一个interface类型来接收,就上面代码中的TopicListEntity,如果后端传过来的响应数据很多,手写interface就很麻烦,所以给大家推荐一个工具,可以根据 json 自动生成 TypeScript 实体类型:json to ts


在Vue中写TSX有哪些需要注意的地方

v-html

使用domPropsInnerHTML来替代v-html

<main
    domPropsInnerHTML={this.topicDetail.content}
    class="markdown-body"
>
    loading💤💤
</main>

v-if

使用三元操作符来替代v-if

 {this.preFlag ? <button class="pageBtn">......</button> : ""}

v-for

使用map遍历替代v-for

{this.pageBtnList.map(page => {
  return (
    <button
      onClick={this.changePageHandler.bind(this, page)}
      class={[{ currentPage: page === this.currentPage }, "pageBtn"]}
    >
      {page}
    </button>
  );
})}

render

注意:在render函数中的组件名一定用kebab-case命名

protected render() {
  return (
    <footer>
      <hello-word />
      <p>
        &copy; 2020 Designed By Enoch Qin
        <a href="https://github.com/dreamqyq/cnode-community" target="_blank">
          源码链接 GitHub >>
        </a>
      </p>
    </footer>   
  );
}

onClick事件传值(TSX)

使用template的时候,如果用v-on绑定事件,想要传参的话,可以直接这么写:

<button @click="clickHandle(params)">click me</button>

但是在TSX中,如果直接这么写,就相当于立即执行了clickHandle函数:

render(){
  // 这样写是不行的!!
  return <button onClick={this.clickHandler(params)}>click me</button>
}

因此,我们不得不使用bind()来绑定参数的形式传参:

render(){
  return <button onClick={this.clickHandler.bind(this, params)}>click me</button>
}

开发过程中遇到的问题及解决

Router history模式

原CNode社区的url是没有#的history模式,但是这需要后端支持,所以本项目中使用了hash模式。

publicPath 部署应用包时的基本URL

<base> 标签

在项目最开始开发的时候,出现了子页面无法刷新(刷新就会报错:Uncaught SyntaxError: Unexpected token '<‘),并且子页面用到的图片资源找不到的问题。通过stack overflow的这个问题的答案,使用<base>标签成功解决了这个问题。
<base>标签是用来指定一个HTML页中所有的相对路径的根路径,在/public/index.html中添加标签<base href="./" />,设置 href为相对路径,在本地调试和打包上线的时候,资源就都不会出问题啦。

Axios withCredentials

在本项目中,后端调用的是 cnode 提供的后端接口,所有接口的都设置了Access-Control-Allow-Origin: *,用来放置跨域。但是如果我们将axios 的 withCredentials(表示跨域请求时是否需要使用凭证)设置成true,会包CORS跨域错误:
原因是:Access-Control-Allow-Origin不可以为 *,因为 * 会和 Access-Control-Allow-Credentials:true 产生冲突,需配置指定的地址。
因此在项目中,withCredentials设置成false即可。

Github-markdown-css

在项目中使用到了github-markdown-css这个库用于展示markdown的样式。用法如下:

总结

Now you have all the basic information you need to create a Vue.js application completely in TypeScript using a few official and third-party libraries to fully leverage the typing and custom decorator features. Vue 3.0 will have better support for TypeScript out of the box, and the whole Vue.js code was rewritten in TypeScript to improve maintainability.
Using TypeScript might seem a bit overwhelming at first, but when you get used to it, you’ll have far fewer bugs in your code and smooth code collaboration between other developers who work on the same code base. (摘自How to write a Vue.js app completely in TypeScript

翻译:现在,您知道了在创建Vue.js + TypeScript应用程序的过程中,如何使用几个官方库和第三方库所需的所有基本信息,以充分利用类型自定义装饰器。已经发布了公测版本的Vue 3.0开箱即用将更好地支持TypeScript,并且整个Vue.js的项目代码都使用TypeScript进行了重写,以提高可维护性。
刚开始使用TypeScript似乎有点让人不知所措,但是当您习惯了它之后,您的代码中的错误将大大减少,并且,在同一个项目中可以和其他开发者更好的协同工作。


本文参考资料:
👉https://blog.logrocket.com/how-to-write-a-vue-js-app-completely-in-typescript/
👉https://zhuanlan.zhihu.com/p/99343202
👉TypeScript 支持 — Vue.js
👉TypeScript 官网
👉https://segmentfault.com/a/1190000016837020

(完)

上一篇 下一篇

猜你喜欢

热点阅读