自主搭建vue 2 + typescript 3.7 + eslint 6.0, 及起飛姿勢(shì). 附腳手架源碼typescript-vue-eslint-starter

目錄

  • 腳手架搭建

  • d.ts文件聲明

  • vue-property-decorator 裝飾器使用

  • vuex-class 裝飾器使用

  • typescript按需轉(zhuǎn)換ES6 Api

webpack 完整配置可以參考 typescript-vue-eslint-starter [ https://github.com/vok123/typescript-vue-eslint-starter ]

歡迎提建議,如果覺(jué)得有用的給個(gè)star哈~

↓↓↓↓↓↓高能時(shí)刻↓↓↓↓↓↓

開(kāi)局一只蔡徐坤, 漲薪全靠vue + ts [https://www.bilibili.com/video/av50374517/?spm_id_from=333.788.videocard.4]

腳手架搭建

1. 安裝依賴(lài)

  • 開(kāi)發(fā)依賴(lài)

npm i -D @typescript-eslint/eslint-plugin @typescript-eslint/experimental-utils @typescript-eslint/parser @typescript-eslint/typescript-estree eslint eslint-config-standard eslint-plugin-standard eslint-plugin-import eslint-plugin-promise eslint-loader eslint-plugin-node eslint-plugin-vue typescript ts-loader

  • 項(xiàng)目依賴(lài)

npm i -S vue-class-component vue-property-decorator vuex-class

2. Webpack loader配置 (ts-loader/eslint-loader)

完整webpack配置 [https://github.com/vok123/typescript-vue-eslint-starter/blob/master/build/webpack.config.js]

module: {
  rules: [
    {
      test: /\.ts(x)?$/,
      loader: 'ts-loader',
      exclude: /node_modules/,
      options: {
        appendTsSuffixTo: [/\.vue$/],
        transpileOnly: true,
        happyPackMode: false
      }
    },
    {
      test: /\.(js|vue|ts|tsx|jsx)$/,
      enforce: 'pre',
      exclude: /node_modules/,
      loader: 'eslint-loader',
      options: {
        fix: false,
        extensions: ['.js', '.jsx', '.vue', '.ts', '.tsx'],
        cache: false,
        emitWarning: true,
        emitError: false
      }
    }
  ];
}

3. 根目錄添加eslint配置文件 .eslintrc.js



module.exports = {
  plugins: ['vue', '@typescript-eslint'],
  parserOptions: {
    parser: '@typescript-eslint/parser',
    env: { es6: true },
    sourceType: 'module'
  },
  root: true,
  env: {
    browser: true,
    node: true,
    serviceworker: true
  },
  extends: ['plugin:vue/base', 'plugin:@typescript-eslint/recommended', 'plugin:vue/essential', 'standard'],
  rules: {
    // 設(shè)置默認(rèn)eslint規(guī)則
    'one-var': 0,
    'arrow-parens': 0,
    'generator-star-spacing': 0,
    'no-debugger': 0,
    'no-console': 0,
    semi: [2, 'always'],
    'no-extra-semi': 2,
    'space-before-function-paren': 0,
    eqeqeq: 0,
    'spaced-comment': 0,
    'no-useless-escape': 0,
    'no-tabs': 0,
    'no-mixed-spaces-and-tabs': 0,
    'new-cap': 0,
    camelcase: 0,
    'no-new': 0,
    indent: 'off',
    semi: 'off',
    // 設(shè)置typescript-eslint規(guī)則
    // https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin/docs/rules
    '@typescript-eslint/semi': ['error'],
    '@typescript-eslint/indent': ['error', 2],
    '@typescript-eslint/explicit-function-return-type': 0
  }
};

4. 根目錄添加 tsconfig.json


{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "allowJs": true,
    "baseUrl": ".",
    "types": ["webpack-env", "node"],
    "paths": {
      "@/*": ["src/*"]
    },
    "lib": ["esnext", "dom", "dom.iterable", "scripthost"]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": ["node_modules"]
}

d.ts文件聲明

src/@types/shims-vue.d.ts

  • 聲明.vue文件

declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}

declare module "vue/types/vue" {
  interface Vue {
    $message: (msg: string): void;
    $balala: string;
  }
}
// 在組件中使用
this.$balala
this.$message('Hello world');

declare module "vue/types/options" {
  interface ComponentOptions<V extends Vue> {
    i18n: any
  }
}

// 使用
new Vue({
  i18n, ...
});

  • 聲明第三方npm包
// 聲明 element-ui
declare module 'element-ui'

// 聲明 axios
declare module 'axios' {
  import Axios from 'axios/index';
  export default Axios;
}

vue-property-decorator 裝飾器使用

  • 常用裝飾器及方法
裝飾器 用途 描述
Component 聲明class組件 只要是個(gè)組件都必須加該裝飾器
Prop 聲明props 對(duì)應(yīng)普通組件聲明中的props屬性
Watch 聲明監(jiān)聽(tīng)器 對(duì)應(yīng)普通組件聲明中的watch屬性
Mixins 混入繼承 對(duì)應(yīng)普通組件聲明中的mixins屬性
Emit 子組件向父組件值傳遞 對(duì)應(yīng)普通this.$emit()
Inject 接收祖先組件傳遞的值 對(duì)應(yīng)普通組件聲明中的inject屬性
Provide 祖先組件向其所有子孫后代注入一個(gè)依賴(lài) 對(duì)應(yīng)普通組件聲明中的provide屬性
  • 用法

    • vue 鉤子寫(xiě)法
    // javascript
    <script>
    export default {
      beforeCreate() {},
      created() {},
      beforeMount() {},
      mounted() {},
      beforeUpdate() {},
      updated() {},
      activated() {},
      deactivated() {},
      beforeDestroy() {},
      destroyed() {},
      errorCaptured() {}
    }
    </script>
    
    // --------typescript--------
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator';
    
    @Component
    export default class App extends Vue {
      beforeCreate() {}
      created() {}
      beforeMount() {}
      mounted() {}
      beforeUpdate() {}
      updated() {}
      activated() {}
      deactivated() {}
      beforeDestroy() {}
      destroyed() {}
      errorCaptured() {}
    }
    
    </script>
    
    
    • Component
    
    // javascript
    
    import helloWorld from './helloWorld.vue';
    export default {
      components: {
        helloWorld
      }
    }
    
    // --------typescript--------
    
    import helloWorld from './helloWorld.vue';
    import { Component, Vue } from 'vue-property-decorator';
    
    @Component({
      components: {
        helloWorld
      }
    })
    export default class App extends Vue {}
    
    
    • Prop
    // javascript
    export default {
      props: {
        msg: {
          type: String,
          default: 'Hello world',
          required: true,
          validator: (val) => (val.length > 2)
        }
      }
    }
    
    
    // --------typescript--------
    
    import { Component, Vue, Prop } from 'vue-property-decorator';
    
    @Component
    export default class HelloWorld extends Vue {
      @Prop({
        type: String,
        default: 'Hello world',
        required: true,
        validator: (val) => (val.length > 2)
      }) msg!: string
    }
    
    
    • Watch
    // javascript
    
    export default {
      data() {
        return {
          value: ''
        };
      },
      watch: {
        value: {
          handler() {
            console.log(this.value);
          },
          deep: true,
          immediate: true
        }
      }
    }
    
    // --------typescript--------
    
    import { Component, Vue, Watch } from 'vue-property-decorator';
    
    @Component
    export default class App extends Vue {
      value: string = ''
      @Watch('value', { deep: true, immediate: true })
      valueWatch() {
        console.log(this.value);
      }
    }
    
    
    • Mixins
    // javascript
    
    // -info.js
    export default {
      methods: {
        mixinsShow() {
          console.log('徐蔡坤');
        }
      }
    }
    
    // -hello-world.vue
    import mixinsInfo from './info.js';
    export default {
      mixins: [mixinsInfo],
      mounted() {
        this.mixinsShow(); // 徐蔡坤
      }
    }
    
    // --------typescript--------
    // -info.ts
    import { Component, Vue } from 'vue-property-decorator';
    
    @Component
    export default class MixinsInfo extends Vue {
        mixinsShow() {
          console.log('徐蔡坤');
        }
    }
    
    // -hello-world.vue
    import { Component, Vue, Mixins } from 'vue-property-decorator';
    import mixinsInfo from './info.ts';
    
    @Component
    export default class HelloWorld extends Mixins(mixinsInfo) {
      mounted() {
        this.mixinsShow(); // 徐蔡坤
      }
    }
    
    
    • Emit
    // javascript
    
    // -children.vue
    <template>
      <button @click="$emit('submit', '唱, 跳')">提交</button>
    </template>
    
    // -parent.vue
    <template>
      <children @submit="submitHandle"/>
    </template>
    
    <script lang="ts">
    import children from './children.vue';
    export default {
      components: {
        children
      },
      methods: {
        submitHandle(msg) {
          console.log(msg); // 唱, 跳
        }
      }
    }
    </script>
    
    // --------typescript--------
    // -children.vue
    <template>
      <button @click="submit">提交</button>
    </template>
    
    <script lang="ts">
    import { Component, Vue, Emit } from 'vue-property-decorator';
    
    @Component
    export default class Children extends Vue {
      @Emit()
      submit() {
        return '唱, 跳';
      }
    }
    </script>
    
    // -parent.vue
    <template>
      <children @submit="submitHandle"/>
    </template>
    
    <script lang="ts">
    import children from './children.vue';
    import { Component, Vue } from 'vue-property-decorator';
    
    @Component({
      components: {
        children
      }
    })
    export default class Parent extends Vue {
      submitHandle(msg: string) {
        console.log(msg); // 唱, 跳
      }
    }
    </script>
    
    
    • Provide/Inject
    // javascript
    
    // -children.vue
    <script>
    export default {
      inject: ['root'],
      mounted() {
        console.log(this.root.name); // rap, 籃球
      }
    }
    </script>
    
    // -parent.vue
    
    <template>
      <children />
    </template>
    <script>
    import children from './children.vue';
    export default {
      components: {
        children
      },
      data() {
        return {
          name: 'rap, 籃球'
        };
      },
      provide() {
        return {
          root: this
        };
      }
    }
    </script>
    
    
    // --------typescript--------
    
    // -children.vue
    
    <script lang="ts">
    import { Component, Vue, Inject } from 'vue-property-decorator';
    
    @Component
    export default class Children extends Vue {
      @Inject() root!: any
      mounted() {
        console.log(this.root.name); // rap, 籃球
      }
    }
    </script>
    
    // -parent.vue
    
    <template>
      <children />
    </template>
    
    <script lang="ts">
    import children from './children.vue';
    import { Component, Vue, Provide } from 'vue-property-decorator';
    @Component({
      components: {
        children
      }
    })
    export default class Parent extends Vue {
      name: string = 'rap, 籃球'
      @Provide()
      root = this.getParent()
      getParent() {
        return this;
      }
    }
    </script>
    
    
    • 計(jì)算屬性
    // javascript
    
    export default {
      data() {
        return {
          hobby: '唱, 跳, rap, 籃球'
        };
      },
      computed: {
        msg() {
          return '我也會(huì)' + this.hobby;
        }
      },
      mounted() {
        console.log(this.msg); // 我也會(huì)唱, 跳, rap, 籃球
      }
    }
    
    // --------typescript--------
    
    // -hello-world.vue
    import { Component, Vue } from 'vue-property-decorator';
    
    @Component
    export default class HelloWorld extends Vue {
      hobby: string = '唱, 跳, rap, 籃球'
      get msg() {
        return '我也會(huì)' + this.hobby;
      }
      mounted() {
        console.log(this.msg); // 我也會(huì)唱, 跳, rap, 籃球
      }
    }
    
    

vuex-class 裝飾器使用

裝飾器 用途
State 獲取vuex state
Getter 獲取vuex getter
Mutation 獲取vuex mutation
Action 獲取vuex actions
  • vuex typescript 基礎(chǔ)定義

    // -store/store.ts
    
    import Vue from 'vue';
    import Vuex, { StoreOptions } from 'vuex';
    import user from './modules/user';
    
    Vue.use(Vuex);
    interface RootState {
      version: string;
    }
    const store: StoreOptions<RootState> = {
      strict: true,
      state: {
        version: '1.0.0'
      },
      modules: {
        user
      }
    };
    
    export default new Vuex.Store<RootState>(store);
    
    
    
    // -store/modules/user.ts
    
    import { Module } from 'vuex';
    export interface UserInfo {
      uId: string;
      name: string;
      age: number;
    }
    
    interface UserState {
      userInfo: UserInfo;
    }
    
    const user: Module<UserState, any> = {
      namespaced: true,
      state: {
        userInfo: {
          uId: '',
          name: '',
          age: 0
        }
      },
      getters: {
        isLogin(state) {
          return !!state.userInfo.uId;
        }
      },
      mutations: {
        updateUserInfo(state, userInfo: UserInfo): void {
          Object.assign(state.userInfo, userInfo);
        }
      },
      actions: {
        async getUserInfo({ commit }): Promise<void> {
          let { userInfo } = await getUserInfo();
          commit('updateUserInfo', userInfo);
        }
      }
    };
    
    export default user;
    
    
    • vuex-class 裝飾器
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator';
    import { State, Getter, Action } from 'vuex-class';
    import { UserInfo } from './store/modules/user';
    
    @Component
    export default class App extends Vue {
      @State('version') version!: string
      @State('userInfo', { namespace: 'user' }) userInfo!: UserInfo
      @Getter('isLogin', { namespace: 'user' }) isLogin!: boolean
      @Action('getUserInfo', { namespace: 'user' }) getUserInfo!: Function
      mounted() {
        this.getUserInfo();
        console.log(this.version); // 1.0.0
      }
    }
    
    </script>
    
    
    • 在非vue組件的.ts中獲取store數(shù)據(jù)
    // test.ts
    
    import store from './store/store';
    
    if (store.getters['user/isLogin'] === false) {
      console.log('未登錄');
    }
    
    

typescript按需轉(zhuǎn)換ES6 Api

默認(rèn)情況下typescript在轉(zhuǎn)換語(yǔ)法為es3或者es5時(shí)并不會(huì)轉(zhuǎn)換ES6 Api, 例如(Object.values, Array.fill ...)
在這種情況下我們可以使用 (ts-polyfill) [https://github.com/ryanelian/ts-polyfill] 定向指定需要轉(zhuǎn)換的api

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末髓堪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子空执,更是在濱河造成了極大的恐慌,老刑警劉巖侣集,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凛俱,死亡現(xiàn)場(chǎng)離奇詭異箩言,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)富雅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)掸驱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人没佑,你說(shuō)我怎么就攤上這事毕贼。” “怎么了蛤奢?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵鬼癣,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我啤贩,道長(zhǎng)待秃,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任痹屹,我火速辦了婚禮章郁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘志衍。我一直安慰自己暖庄,他們只是感情好聊替,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著培廓,像睡著了一般佃牛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上医舆,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天俘侠,我揣著相機(jī)與錄音,去河邊找鬼蔬将。 笑死爷速,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的霞怀。 我是一名探鬼主播惫东,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼毙石!你這毒婦竟也來(lái)了廉沮?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤徐矩,失蹤者是張志新(化名)和其女友劉穎滞时,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體滤灯,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坪稽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鳞骤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窒百。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖豫尽,靈堂內(nèi)的尸體忽然破棺而出篙梢,到底是詐尸還是另有隱情,我是刑警寧澤美旧,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布渤滞,位于F島的核電站,受9級(jí)特大地震影響陈症,放射性物質(zhì)發(fā)生泄漏蔼水。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一录肯、第九天 我趴在偏房一處隱蔽的房頂上張望趴腋。 院中可真熱鬧,春花似錦、人聲如沸优炬。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蠢护。三九已至雅宾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間葵硕,已是汗流浹背眉抬。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留懈凹,地道東北人蜀变。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像介评,于是被迫代替她去往敵國(guó)和親库北。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容