日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > vue >内容正文

vue

TypeScript+vue使用与迁移经验总结

發布時間:2023/12/4 vue 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 TypeScript+vue使用与迁移经验总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

源寶導讀:ERP平臺的前端底層使用了Vue作為組件的基礎架構,而使用了TypeScript語言進行組件的封裝與開發。本文將簡要介紹平臺在使用TypeScript和Vue框架進行老功能重構時的經驗總結。

一、背景

下面主要探討是以下三個方面:

  • 目前項目中使用到的vue+ts的哪些特性,還有哪些特性值得去使用,不會涉及到太多的ts語法知識;

  • 老項目的遷移為ts,有哪些點需要改造;

  • 各抒己見,探討下各位都有哪些心得和見解。

二、為什么要用typescript

TypeScript簡單介紹:

  • 是 JavaScript 的強類型版本。然后在編譯期去掉類型和特有語法,生成純粹的 JavaScript 代碼。由于最終在瀏覽器中運行的仍然是 JavaScript,所以 TypeScript 并不依賴于瀏覽器的支持,也并不會帶來兼容性問題。

  • TypeScript 是 JavaScript 的超集,這意味著他支持所有的 JavaScript 語法。并在此之上對 JavaScript 添加了一些擴展,如 class / interface / module 等。這樣會大大提升代碼的可閱讀性。

總結優勢:

  • 靜態類型檢查: 類型校驗,能夠避免許多低級代碼錯誤;

  • IDE 智能提示: 使用一個方法時,能清楚知道方法定義的參數和類型和返回值類型;使用一個對象時,只需要.就可以知道有哪些屬性以及屬性的類型;

  • 代碼重構: 經過不停的需求迭代,代碼重構避免不了,在重構時,如果前期有清晰和規范的接口定義、類定義等,對于重構幫助很大;

  • 規范性和可讀性: 類似于強類型語言,有了合理的類型定義、接口定義等,對于代碼實現的規范性和可讀性都有很大提高,不然搜索整個項目這個方法在哪里調用、怎么定義等。

個人認為最有價值點:寫代碼前,會先構思功能需求的整體代碼架構。

三、安裝和起步

一般我們會面臨兩個情況:

  • 新項目創建;

  • 覺得ts不錯,想將老項目切換為vue+ts。

3.1、新項目起步

  • 安裝vue-cli3.0;

  • vue create vue-ts-hello-world;

  • 選擇Manually select features,勾選typescript。其他配置根據項目情況勾選。

3.2、老項目切換為vue+ts

  • 安裝ts依賴(或使用yarn);

    • yarn add vue-class-component vue-property-decorator;

    • yarn add ts-loader typescript tslint tslint-loader tslint-config-standard —dev。

  • 配置 webpack,添加ts-loader和tslint-loader;

  • 添加 tsconfig.json;

// 這是平臺目前用的tsconfig.json {"compilerOptions": {"target": "esnext","module": "esnext","strict": true,"jsx": "preserve","importHelpers": true,"moduleResolution": "node","experimentalDecorators": true,"esModuleInterop": true,"allowSyntheticDefaultImports": true,"strictNullChecks": false,"sourceMap": true,"baseUrl": ".","types": ["webpack-env","jest"],"paths": {"@/*": ["src/*"],// 別名追加"components/*": ["src/components/*"],},"lib": [ // 編譯過程中需要引入的庫文件的列表"esnext","dom","dom.iterable","scripthost"]},"include": ["src/**/*.ts","src/**/*.tsx","src/**/*.vue","tests/**/*.ts","tests/**/*.tsx"],"exclude": ["node_modules","ui-tests"] }

備注: ts 也可支持 es6 / es7,在 tsconfig.json中,添加對es6 / es7的支持。

"lib": ["dom","es5","es6","es7","es2015.promise" ]
  • 添加 tslint.json 或者 prettierrc(可以視情況而定)。

// 目前平臺使用的是.prettierrc.js module.exports = {"$schema": "http://json.schemastore.org/prettierrc","singleQuote": true,"endOfLine": "auto","semi": false }
  • 讓 ts 識別 .vue。

declare module "*.vue" {import Vue from "vue";export default Vue; }
    • 而在代碼中導入 .vue 文件的時候,需要寫上 .vue 后綴。原因還是因為 TypeScript 默認只識別 .ts 文件,不識別 *.vue 文件。

    • 添加vue-shim.d.ts,讓vue文件給vue模塊來編譯。

  • 改造 .vue文件,將vue中script切換為<script lang="ts">;

  • 改造.js文件,修改為ts語法,定義類型等。

四、vue+ts常用的裝飾器

? ? 這里主要用到了vue-property-decorator,這個是在vue-class-component基礎上做了一層增強,新增了一些裝飾器,使用更加便捷。這里只分享一些常用的,對于老項目改寫vue文件很有用:

4.1、@Component

? ? 標識該vue文件是一個組件,并且可以引入其他組件。

非ts版本:

import MyComponent from '@/components/MyComponent' export default {components: {MyComponent} }

ts版本:

import { Vue, Component } from 'vue-property-decorator' import MyComponent from '@/components/MyComponent' @Component({components: {MyComponent} }) export default class YourComponent extends Vue { }

備注:這里不管有沒有引入其他組件,都必須要使用@Component,目的是為了注冊這個組件。否則在其他組件各種莫名其妙的問題。比如:路由找不到組件,而且不會報錯。

4.2、@Prop

非ts版本:

export default {props: {propA: {type: Number},propB: {default: 'default value'},propC: {type: [String, Boolean]},propD: {type: Object,default: () => {},validator(val: object) {return val.prop = '1'}}} }

ts版本:

import { Vue, Component, Prop } from 'vue-property-decorator'@Component export default class YourComponent extends Vue {@Prop(Number)readonly propA: number | undefined@Prop({ default: 'default value' })readonly propB!: string@Prop([String, Boolean])readonly propC: string | boolean | undefined// 也可以一起@Prop({type: Object, default: () => {},validator(val: object) {return val.prop = '1'}})readonly propD!: object // 只是舉例,一般會定義一個interface }

4.3、@Watch

非ts版本:

export default {watch: {child: {handler: 'onChildChanged',immediate: false,deep: false},person: [{handler: 'onPersonChanged1',immediate: true,deep: true},{handler: 'onPersonChanged2',immediate: false,deep: false}]},methods: {onChildChanged(val, oldVal) {},onPersonChanged1(val, oldVal) {},onPersonChanged2(val, oldVal) {}} }

ts版本:

import { Vue, Component, Watch } from 'vue-property-decorator'@Component export default class YourComponent extends Vue {@Watch('child')onChildChanged(val: string, oldVal: string) {}@Watch('person', { immediate: true, deep: true })onPersonChanged1(val: Person, oldVal: Person) {}@Watch('person')onPersonChanged2(val: Person, oldVal: Person) {} }

4.4、@Provide和@Inject

? ? 場景:一般用于父級嵌套比較深的子孫vue組件,但是數據不是很方便傳到深層級vue組件中,利用樹型結構組件。

非ts版本:

// 父組件 provide () {return {OptionGroup: this} }// 子孫組件 inject: ['OptionGroup']

ts版本:

父組件:

@Provide()getObj () {return this}

子孫組件:

@Inject() getObj!: anyget obj() {return this.getObj() }

Privide的弊端:

  • 依賴注入它將你應用程序中的組件與它們當前的組織方式耦合起來,使重構變得更加困難;

  • 同時所提供的屬性是非響應式的。這是出于設計的考慮,因為使用它們來創建一個中心化規模化的數據跟使用 $root做這件事都是不夠好的。

建議:

一般不推薦過度使用。

  • provide 和 inject的綁定并不是可響應的,這是刻意為之的。但是,如果你傳入了一個可監聽的對象,那么其對象的屬性還是可響應的;

  • 如果你想要共享的這個屬性是你的應用特有的,而不是通用化的,或者如果你想在祖先組件中更新所提供的數據,那么這意味著你可能需要換用一個像Vuex這樣真正的狀態管理方案了。

4.5、@Ref

非ts版本:

export default {computed() {anotherComponent () {return this.$refs.anotherComponent},button () {return this.$refs.aButton}} }

ts版本:

import { Vue, Component, Ref } from 'vue-property-decorator' import AnotherComponent from '@/Components/another-component.vue'@Component export default class YourComponent extends Vue {@Ref() readonly anotherComponent!: AnotherComponent@Ref('aButton') readonly button!: HTMLButtonElement// 我們目前是這樣使用的$refs!: {popover: anysearch: HcProjectSelectSearchtree: HcProjectTree} }

4.6、@Emit

用的很少,參數和時機不是很好控制。

非ts版本:

export default {methods: {handleClick(e) {this.$emit('click', e)},loadData() {const promise = new Promise(resolve => {setTimeout(() => {resolve(20)}, 0)})promise.then(value => {this.$emit('load', value)})}} }

ts版本:

import { Vue, Component, Emit } from 'vue-property-decorator'@Component export default class YourComponent extends Vue {@Emit('click')handleClick(e) {// todo}@Emit()promise() {return new Promise(resolve => {setTimeout(() => {resolve(20)}, 0)})} }

五、mixin改寫

定義mixin:

export const cusMixin = {mounted() {this.$refs = {}// $0 instanceof HTMLElement// this.$refs = {}console.log('mixin mounted')},beforeUpdate() {this.$refs = {}// console.log('global mounted')},updated() {this.$refs = {}// console.log('global mounted')} }

引入mixin:

import { Vue, Component } from 'vue-property-decorator' import cusMixin from '@/mixin'@Component({components: {},mixins: [cusMixin] }) export default class YourComponent extends Vue {}// 或者嘗試使用 import { Component, Mixins, Vue } from 'vue-property-decorator'; import { MyOtherMixin } from './MyOtherMixin';@Component export class MyMixin extends Vue {private created() {console.log('what?');} }@Component // 繼承多個mixin,使用數組 [MyMixin, MyOtherMixin] export default class App extends Mixins(MyMixin) { private test = "test";private laowang = 'laowang';created() {console.log(this.test)console.log(this.Kitchen)console.log(this.Tv)}}

六、vue識別全局的方法和變量

  • vue-shim.d.ts文件中,增加如下代碼:

import Vue from 'vue' import VueRouter, { Route } from 'vue-router' import { Store } from 'vuex' // 聲明全局方法 declare module 'vue/types/vue' {interface Vue {// 內部變量$router: VueRouter;$route: Route;$store: Store<any>;// element-ui等組件$Message: any$Modal: any// 自定義掛載到Vue.prototype上的變量$api: any$mock: any$configs: any} }

七、vuex的改寫

? ? 關于store的改造,配置和結構和原來一樣,具體編碼設計沒有特定套路,根據項目具體設計改寫為ts的語法。

? ? 主要是關于ts在vue如何使用,目前主流的方案是vue-class-component + vuex-class,一般常用的mapGetters和mapActions改寫:

yarn add vuex-class

非ts版本:

import { mapGetters, mapActions } from 'vuex' export default Vue.extend({computed: {...mapGetters({'name','age'})},methods: {...mapActions(['setNameAction'])} })

ts版本:

import { Vue, Component } from 'vue-property-decorator' import { Getter, Action } from 'vuex-class' import { Test } from '@/store'export default class YourComponent extends Vue {@Getter('name') name: string@Getter('age') age: number@Action('setNameAction') setNameAction: Functionget innerName (): string {return this.name}get innerAge (): number {return this.age}setName (name: string) {this.setNameAction(products)} }

備注:tsconfig.json需要調整下:

{"compilerOptions": {// 啟用 vue-class-component 及 vuex-class 需要開啟此選項"experimentalDecorators": true,// 啟用 vuex-class 需要開啟此選項"strictFunctionTypes": false} }

八、vue render jsx語法改寫

? ? 改寫的原理還是和上面類似,都是借助目前流行的兩個庫,除了使用vue-property-decorator以外,還需要借助vue-tsx-support,vue-tsx-support是在Vue外面包裝了一層,將prop、event等以泛型的方式加了一層ts接口定義傳了進去,目的是為了防止ts的類檢查報錯。

  • 步驟:

    • 引入 yarn add vue-tsx-support --dev;

    • 導入ts聲明,在main,ts中import "vue-tsx-support/enable-check";

    • 在vue.config.js中extensions添加.tsx。

  • 使用:

import { Component, Prop } from "vue-property-decorator"; import * as tsx from "vue-tsx-support";interface YourComponentsProps {name?: stringage?: number }@Component export default class YourComponents extends tsx.Component<YourComponentsProps> {@Prop() public name!: string;@Prop() public age!: number;protected render() {return (<div><h1>姓名:{this.name}</h1><h1>年齡:{this.age}</h1></div>);} }

? ? 這里jsx改寫為tsx大致簡單了解下,如果大家有興趣,以后可以一起學習探討下。

九、思考

  • 關于老項目ts的改造,如何才能平滑過渡,不影響現有的功能。

  • 在vue中ts的實踐,數據、視圖、控制器分層設計的問題。

------ END ------

作者簡介

羅同學:?研發工程師,目前負責ERP建模平臺的設計與開發工作。

也許您還想看

從案例角度解析建模平臺動態規則引擎

WEB頁面前端性能診斷方法與實踐

前端異步對象的原理與使用方法

Web頁面適配移動端方案研究

總結

以上是生活随笔為你收集整理的TypeScript+vue使用与迁移经验总结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。