⬆️ ⬇️

Typed components in Vue.js, or how to make friends Vue, TypeScript and Webpack



This article focuses on a rather unusual combination of technologies: Vue.js + TypeScript + Webpack, in the context of single-file components . The solution to this problem took me a decent amount of time from the first call, since a comprehensive explanation of how to use all this together, and even with a number of restrictions (NPM provides us with runtime-only build Vue.js ), is almost impossible to find in a solid form. If you are interested in this topic, then I invite you to further reading.



I think the reasons why you might want to use this combination with Vue.js are pretty obvious:





Now we should move from the preface to the desired result of our activity: at the output, we want to get a blank application, which can be expanded as much as you like with additional components and the main thing: it will be assembled by the Webpack.



We begin, as usual, with a demonstration of the configuration package.json and webpack.config, and further analysis of the latter.

')

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' }; 


The moment that interests us more than others is the use of a vue-loader . As you can see in the options parameters, we specify another collection of loaders, and we will need it just so that when parsing the vue template, the webpack can correctly handle dependencies with a different type than the standard one. Now I propose to recall (or study) the basic structure of the vue component.





The component has a structure that is absolutely typical for the component approach: the template - the script (ts in our case) - CSS . And just the script is the greatest problem for us in this case: must be processed beforehand. On the Webpack side, we solved the problem, now it remains to be solved inside the component, it is done by adding the lang attribute with the corresponding extension.



main.vue (root component)

 <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!' } 


A few words about @Component
As you can see before the class of our component is the decorator , who is committed to "translates" the component code into a more familiar (in the form of an object) format for Vue.js, the use of this decorator is not mandatory, but allows you to write more readable code. ( property decorators are also a good addition)



A component of this type will be assembled without any problems by the Webpack! Things are small, it remains to add the initialization of our small application exactly to the entry point. But this is where a problem with import may arise ...



 import MainComponent from './components/main/main.vue' 


... since TS does not know anything about our vue templates, but this is not a problem at all, all we need to do in this case is to declare a module of the following form in d.ts :



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


This pair of lines of code “will explain” to TS how exactly to process import of * .vue files and everything works for a fairly obvious reason: all our components are inherited from Vue.



Now you can finish writing our 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(); 


Here, the constructor's call is quite typical for initializing a Vue application, but you may be wondering why you need to specify render, why not just specify the template and use the root component there? The point here is that the default version of vue.js from npm (which is also the best in performance) is the runtime-only build version, which means that it is impossible to parse our templates on the fly, and because of this we must specify the render function with root component as the entry point.



Little about build
There may be an even more reasonable question: why is the component inside the component rendered normally? The point here is that, inside a vue-loader, a special component lives, which is engaged in translating our templates into render functions at the build stage, and later vue.js uses the functions already assembled without the need to perform parsing on its own (obvious + performance ). Summarizing: all the nested components will be used already in the form of render-functions, but for the initialization of the root it will be necessary to prescribe it for obvious reasons.



I hope that after this analysis, it became more clear to you how to assemble vue components with TS and Webpack. For a complete example, go here .

Source: https://habr.com/ru/post/330400/



All Articles