Речь в данной статье пойдет о довольно необычном сочетании технологий: Vue.js + TypeScript + Webpack, в разрезе single-file компонентов. Решение данной задачи отняло у меня приличное количество времени с первого захода, поскольку исчерпывающее объяснение того, как использовать все это вместе, да и еще с рядом ограничений (NPM предоставляет нам runtime-only build Vue.js), найти в цельном виде практически невозможно. Если вас заинтересовала данная тема, то приглашаю к дальнейшему чтению.
Думаю причины, по которым вы можете захотеть использовать данное сочетание с Vue.js, довольно-таки очевидны:
- Типизацию для JS сейчас хотят почти на всех, крупных и не очень, проектах;
- Webpack — крайне простое, в использовании, и популярное, средство сборки
проектов (да еще и import/export может)
Теперь следует перейти от предисловия собственно желаемому результату нашей деятельности: на выходе мы хотим получить заготовку приложения, которую сколь угодно много можно расширять дополнительными компонентами и главное: все это будет собираться Webpack-ом.
Начнем мы, как водится, с демонстрации конфигураций package.json и webpack.config, и дальнейшего разбора последнего.
package.json
{
"name": "vuejs-webpack-ts",
"version": "1.0.0",
"description": "Sample project of Webpack+TS+Vue.js ",
"main": "webpack.config.js",
"scripts": {
"start": "webpack-dev-server --hot --inline --history-api-fallback"
},
"repository": "https://github.com/StepanZharychev/vue-ts-webpack.git",
"author": "Stepan Zharychev",
"license": "ISC",
"dependencies": {
"babel-core": "^6.24.0",
"babel-loader": "^6.4.1",
"css-loader": "^0.27.3",
"style-loader": "^0.16.0",
"ts-loader": "^2.0.3",
"typescript": "^2.2.1",
"webpack": "^2.3.2",
"vue": "^2.3.3",
"vue-class-component": "^5.0.1",
"vue-loader": "^12.1.0",
"vue-property-decorator": "^5.0.1",
"vue-template-compiler": "^2.3.3",
"webpack-dev-server": "^2.4.2"
}
}
webpack.config.js
module.exports = {
entry: './app/init.ts',
output: {
filename: 'dist/bundle.js',
path: __dirname,
publicPath: '/static/'
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
options: {
configFileName: 'tsconfig.json',
appendTsSuffixTo: [/\.vue$/]
}
},
{
test: /\.js/,
loaders: ['babel-loader']
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
ts: 'ts-loader'
},
esModule: true
}
},
{
test: /\.css/,
loaders: ['style-loader', 'css-loader']
}
]
},
devServer: {
compress: true,
port: 8001
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
devtool: 'source-map'
};
Момент, который интересует нас больше остальных, — это использование vue-loader. Как можно заметить в параметрах options, мы указываем еще одну коллекцию лоадеров, и понадобится она нам как раз для того, чтобы при парсинге vue темплейта webpack смог правильно обработать зависимости с отличным от стандартного типом. Теперь я предлагаю вспомнить (или изучить) базовую структуру vue компонента.
Компонент имеет абсолютно типичную для компонентного подхода структуру: темплейт — скрипт(ts в нашем случае) — CSS. И как раз скрипт и представляет для нас наибольшую проблему в данном случае: т.к. должен быть обработан предварительно. На стороне Webpack проблему мы решили, теперь ее осталось решить внутри компонента, делается добавлением атрибута lang с соответствующим расширением.
main.vue(рутовый компонент)
<template>
<hello :message="message"></hello>
</template>
<script src="./main.ts" lang="ts"></script>
<style src="./main.css"></style>
main.ts
import Vue from 'vue'
import Component from 'vue-class-component'
import HelloComponent from '../hello/hello.vue'
@Component({
components: {
hello: HelloComponent
}
})
export default class MainComponent extends Vue {
public message = 'Hello there, Vue works!'
}
Компонент такого вида будет без проблем собран Webpack-ом! Дело за малым, осталось добавить инициализацию нашего небольшого приложения аккурат в точку входа. Но именно тут может возникнуть проблема с импортом…
import MainComponent from './components/main/main.vue'
… поскольку TS ничего не знает о наших vue темплейтах, но это вообщем-то не проблема, все требуется сделать в данном случае — это задекларировать в d.ts файл модуль следующего вида:
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
Эта пара строчек кода «объяснит» TS-у как именно обрабатывать импорт *.vue файлов да и работает все по довольно очевидной причине: все наши компоненты наследовались от Vue.
Теперь можно закончить написание нашего index.ts:
import Vue from 'vue'
import MainComponent from './components/main/main.vue'
class AppCore {
private instance: Vue;
private init() {
this.instance = new Vue({
el: '#appContainer',
render: h => h(MainComponent),
})
}
constructor() {
this.init();
}
}
new AppCore();
Здесь происходит довольно типичный для инициализации Vue-приложения вызов конструктора, но может возникнуть вопрос зачем нужно указывать render, почему нельзя просто указать темплейт и использовать там рутовый компонент? Дело тут вот в чем, дефолтная версия vue.js из npm (она же по совместительству — лучшая по производительности) является runtime-only build версией, что обозначает невозможность парсинга наших темплейтов налету, и из-за этого мы должны указать render функцию с рутовым компонентом, как точку входа.
Надеюсь, что после данного разбора, вам стало более понятно, как нужно собирать vue компоненты c TS и Webpack. За полным примером можете пройти сюда.
Комментарии (10)
aliev
07.06.2017 18:56Есть примеры использование с typescript methods, computed и пр?
Vuex как приживается?
paulgray
08.06.2017 20:50Странно, у Вас стоит тэг «webpack 2», а конфиг в статье на первую версию
StepanZharychev
08.06.2017 20:51Если вы мне сможете сказать, чем должен отличать конфиг второго webpack, буду премного благодарен. Но увы документация говорит о том, что такая структура правильная. Да и использован в проекте-примере webpack 2.
paulgray
08.06.2017 23:30+1Первое что бросилось в глаза это старый ключ module.loaders вместо нового module.rules
StepanZharychev
09.06.2017 09:44Ну что же дельное замечание, тут стоит отметить, что пока webpack поддерживает и то, и другое, но в будущем loaders может уйти в deprecated, так что замечание в целом корректное.
eshimischi
12.06.2017 10:45Есть готовые решения для дополнительного изучения на вашу тему vue2+ts2, Vueify+ts2, Microsoft offisial template, Boilerplate, Vue-typed и тд
izzholtik
(\