Проведя два года с Polymer и еще один с Angular 2, я недавно начал заново открывать для себя свою любовь к Vue.js.

Quasar - очень хороший Ionic-подобный мобильный фреймворк для Vue, и, поскольку Angular 2 заставил меня использовать TypeScript, я решил пройти процесс его интеграции в приложение Quasar. Вот 11 простых шагов, чтобы добавить поддержку TypeScript.

Шаг 1. Создайте приложение

Предполагая, что вы установили его, мы просто следуем инструкциям с помощью Quasar CLI.

Шаг 2. Установите зависимости TypeScript

Нам нужно несколько зависимостей для сборки нашего проекта TypeScript.

npm install --save-dev ts-loader typescript @types/node

Нам также понадобятся декораторы классов Vue.js для TypeScript / ES2015.

npm install --save vue-class-component

Шаг 3. Добавьте файл tsconfig.json

{
    "compilerOptions": {
        "target": "es5",
        "lib": [
            "dom",
            "es2015"
        ],
        "types": [
            "node"
        ],
        "module": "commonjs",
        "moduleResolution": "node",
        "isolatedModules": false,
        "experimentalDecorators": true,
        "noImplicitThis": true,
        "strictNullChecks": true,
        "removeComments": true,
        "suppressImplicitAnyIndexErrors": true
    },
    "include": [
        "./src/**/*.ts"
    ],
    "compileOnSave": false
}

Шаг 4. Обновите webpack.base.conf.js

var
  path = require('path'),
  webpack = require('webpack'),
  config = require('../config'),
  cssUtils = require('./css-utils'),
  env = require('./env-utils'),
  merge = require('webpack-merge'),
  projectRoot = path.resolve(__dirname, '../'),
  ProgressBarPlugin = require('progress-bar-webpack-plugin'),
  useCssSourceMap =
    (env.dev && config.dev.cssSourceMap) ||
    (env.prod && config.build.productionSourceMap)

module.exports = {
  entry: {
    app: './src/main.ts'
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    publicPath: config[env.prod ? 'build' : 'dev'].publicPath,
    filename: 'js/[name].js',
    chunkFilename: 'js/[id].[chunkhash].js'
  },
  resolve: {
    extensions: ['.ts', '.js'],
    modules: [
      path.join(__dirname, '../src'),
      'node_modules'
    ],
    alias: config.aliases
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        exclude: /node_modules|vue\/src/,
        loader: 'ts-loader',
        options: {
          appendTsSuffixTo: [/\.vue$/]
        }
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          esModule: true,
          postcss: cssUtils.postcss,
          loaders: merge(cssUtils.styleLoaders({
            sourceMap: useCssSourceMap,
            extract: env.prod
          }))
        }
      },
      {
        test: /\.json$/,
        loader: 'json-loader'
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: 'img/[name].[hash:7].[ext]'
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: 'fonts/[name].[hash:7].[ext]'
        }
      }
    ]
  },
  plugins: [
    /* Uncomment if you wish to load only one Moment locale: */
    // new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/),

    new webpack.DefinePlugin({
      'process.env': config[env.prod ? 'build' : 'dev'].env,
      'DEV': env.dev,
      'PROD': env.prod,
      '__THEME': '"' + env.platform.theme + '"'
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: env.prod,
      options: {
        context: path.resolve(__dirname, '../src'),
        eslint: {
          formatter: require('eslint-friendly-formatter')
        },
        postcss: cssUtils.postcss
      }
    }),
    new ProgressBarPlugin({
      format: config.progressFormat
    })
  ],
  performance: {
    hints: false
  },
  devtool: 'source-map'
}

Главное, что мы здесь сделали, - это заменили загрузчик babel на загрузчик машинописного текста и добавили ts к расширениям. Все остальное может остаться прежним.

Шаг 5: Добавьте файл sfc.d.ts в папку src

declare module "*.vue" {
    import * as Vue from 'vue';
    export default typeof Vue
}

declare module "quasar"
declare const __THEME

Это должно показаться знакомым любому, кто видел пример TypeScript Vue, но у нас есть пара дополнений. Первое объявление состоит в том, чтобы компилятор TypeScript не прерывал работу, когда мы загружаем модули * .vue в наши модули * .ts. Второе объявление призвано восполнить отсутствие объявлений типа Quasar. Последний оператор объявления - это глобальная константа, которую Quasar использует для назначения вашей темы. Он вводится во время сборки в конфигурацию webpack.

Шаг 6. Переименуйте модули * .js в * .ts

Достаточно просто, мы просто переименовываем все файлы JavaScript в нашем каталоге src как файлы TypeScript.

Шаг 7. Исправьте импорт и модули Vue

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

Становится

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

Также важно отметить, что мы сделаем то же самое для всех наших операторов импорта Quasar.

Шаг 8. Обновите компоненты Vue, чтобы использовать декоратор классов

Замените скрипт в Index.vue следующим

var moveForce = 30
var rotateForce = 40

import * as Quasar from 'quasar'
import * as Vue from 'vue'
import Component from 'vue-class-component'

@Component({
    props: {
        propMessage: String
    }
})
export default class Index extends Vue {
    quasarVersion = Quasar.version
    moveX = 0
    moveY = 0
    rotateY = 0
    rotateX = 0
    get position() {

        let transform = `rotateX(${this.rotateX}deg) rotateY(${this.rotateY}deg)`
        return {
            top: this.moveY + 'px',
            left: this.moveX + 'px',
            '-webkit-transform': transform,
            '-ms-transform': transform,
            transform
        }
    }

    move(event) {
        const {width, height} = Quasar.Utils.dom.viewport()
        const {top, left} = Quasar.Utils.event.position(event)
        const halfH = height / 2
        const halfW = width / 2

        this.moveX = (left - halfW) / halfW * -moveForce
        this.moveY = (top - halfH) / halfH * -moveForce
        this.rotateY = (left / width * rotateForce * 2) - rotateForce
        this.rotateX = -((top / height * rotateForce * 2) - rotateForce)
    }
    mounted() {
        this.$nextTick(() => {
            document.addEventListener('mousemove', this.move)
            document.addEventListener('touchmove', this.move)
        })
    }
    beforeDestroy() {
        document.removeEventListener('mousemove', this.move)
        document.removeEventListener('touchmove', this.move)
    }
}

Также замените скрипт в Error404.vue

var Quasar = require('quasar')
import * as Vue from 'vue'
import Component from 'vue-class-component'

@Component({})
export default class ErrorComponent extends Vue {
    canGoBack = window.history.length > 1
    goBack() {
        window.history.go(-1)
    }
    data() {
        return {
            canGoBack: window.history.length > 1
        }
    }
}

Большая разница в том, что теперь мы используем классы, обернутые декоратором компонентов. Это очень похоже на синтаксис Angular 2. Подробнее о том, почему мы это делаем, читайте здесь.

Шаг 9. Замените системный загрузчик в router.ts

Я предпочитаю напрямую импортировать свои компоненты, но вы также можете использовать здесь require.

import * as Vue from 'vue'
import * as VueRouter from 'vue-router'
import HomeComponent from './components/Index.vue'
import ErrorComponent from './components/Error404.vue'

Vue.use(VueRouter)

export const AppRouter = new VueRouter({
  routes: [
    { path: '/', component: HomeComponent }, // Default
    { path: '/login', component: LoginComponent }, // Default
    { path: '*', component: ErrorComponent } // Not found
  ]
})

export default AppRouter

Шаг 10: Обновите main.ts

import * as Vue from 'vue'
import * as Quasar from 'quasar'

import router from './router'
import app from './App.vue';

Vue.use(Quasar) // Install Quasar Framework

Quasar.start(() => {
  /* eslint-disable no-new */
  new Vue({
    el: '#q-app',
    router,
    render: h => h(app)
  })
})

Шаг 11: Уборка и постройка круто!

Вот и все. Теперь у нас должна быть возможность запустить quasar dev, чтобы запустить наш сервер, и теперь у нас есть инструментальная поддержка TypeScript. Любые зависимости, которые нам больше не нужны, можно безопасно удалить. Удачного программирования!