Max
Max

Reputation: 7080

Vue + Typescript + webpack: Import html into component

I have a typescript + vue + webpack application and I want separate html from code.
I have follow this tutorial and I have made a simple Hello Word.

Webpack config

const path = require('path');

module.exports = {
    mode: "development",
    entry: './src/app.ts',
    output: {
        path: path.resolve('dist'),
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.(ts|tsx)?$/,
                loader: 'ts-loader',
                exclude: /node_modules/                
            },
            {
                test: /.html$/,
                loader: "vue-template-loader",
                exclude: /index.html/
            }
        ]
    },
    resolve: {
        extensions: [
            '.js',
            '.vue',
            '.tsx',
            '.ts'
        ]
    }
};

Html

<div>
    <h2>Hello from {{message}}</h2>
</div>

Vue Component

import Vue from "vue";
import Component from "vue-class-component";
// template: '<button @click="onClick">Click!</button>'
import WithRender from "./home.html";

@WithRender
@Component
export default class HomeComponent extends Vue {

    public message: string = "Word";

    constructor() {
        super();
    }

    mounted() { }
}

After I have added this shim

declare module '*.html' {

        import Vue, { ComponentOptions, FunctionalComponentOptions } from 'vue'
        interface WithRender {
            <V extends Vue, U extends ComponentOptions<V> | FunctionalComponentOptions>(options: U): U
            <V extends typeof Vue>(component: V): V
        }

        const withRender: WithRender
        export default withRender
}

I have (almost) understand how typescript decorators work but I don't understand the shim code, how it is possible that this code inject the html into the Vue component ?

I have readed about Decorators from Typescript site

Upvotes: 0

Views: 941

Answers (2)

Olena Soroka
Olena Soroka

Reputation: 29

Try something like this.

src/view/FirstView.ts

import "ts-polyfill";
import {defineComponent} from "vue";
import {route} from "@product/router/Routes";

const component = defineComponent({
    name: 'FirstView',
    components: {},
    template: require("@product/template/FirstView.html"),
    data() {
        return {
            message: 'FirstView',
            route
        };
    }
})
export default component

src/template/FirstView.html

<div class="flex space-between">
    <h1>{{ message }}</h1>
    <router-link :to="{ name: route.second }">To second</router-link>
</div>

Webpack config

const path = require('path');
const webpack = require('webpack');

const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
        entry: {
            'product': './src/App.ts',
        },
        output: {
            path: path.resolve(__dirname, 'public/'),
            publicPath: '/',
            filename: 'js/[name].js'
        },
        module: {
            rules: [
                {
                    test: /\.tsx?$/,
                    loader: 'ts-loader',
                    options: {
                        appendTsSuffixTo: [/\.vue$/],
                        configFile: 'tsconfig.json',
                        allowTsInNodeModules: true
                    }
                },
                {
                    test: /\.jsx?$/,
                    type: 'javascript/esm',
                    use: {
                        loader: 'babel-loader',
                    },
                    exclude: /node_modules/
                },
                {
                    test: /\.html$/,
                    use: ['html-loader']
                }
            ]
        },
        plugins: [],
        resolve: {
            plugins: [new TsconfigPathsPlugin({configFile: "tsconfig.json"})],
            alias: {
                'vue$': 'vue/dist/vue.esm-bundler.js',
            },
            extensions: ['*', '.ts', '.js', '.json']
        }
};

tsconfig.json

module.exports = {
    "runtimeCompiler": true,
    "compilerOptions": {
        "rootDirs": [
            "src/"
        ],
        "paths": {
            "@product/*": ["src/*"]
        },
    },
    "include": [
        "src/**/*.ts"
    ]
}

Upvotes: 0

tony19
tony19

Reputation: 138226

vue-template-loader compiles the HTML template into a render function, which @WithRender inserts into the class definition.

For instance, this HTML:

<div>Hello world</div>

is converted into this render function:

render(h) {
  return h('div', 'Hello world')
}

Then, applying @WithRender (the result of importing the example HTML template above) to class Foo extends Vue {} results in:

class Foo extends Vue {
  render(h) {
    return h('div', 'Hello world')
  }
}

Upvotes: 1

Related Questions