なんとなく。
依存パッケージ
以下のものが必要なのでインストールします。
npm i vue vue-class-component
npm i -D \
@types/vue \
typescript \
babel-core \
babel-preset-env \
babel-plugin-transform-vue-jsx \
babel-plugin-syntax-jsx \
babel-helper-vue-jsx-merge-props \
webpack \
awesome-typescript-loader
あと開発に便利なこんなのも入れる。
npm i -D webpack-dev-server html-webpack-plugin
コンパイルの流れ
Vueでは、JSXをVNode
というものへ変換する必要があります。この変換は、
-
.tsx
で書く<div>hoge</div>
-
.jsx
に変換<div>hoge</div>
-
そしてBabelで
.js
に変換h('div', null, ['hoge'])
という感じでやります。
環境構築
TypeScript
こんな設定にします。
{
"compilerOptions": {
"jsx": "preserve",
"experimentalDecorators": true,
"target": "es5",
"module": "commonjs",
"lib": ["es2015", "dom"],
"strict": true
}
}
重要なのが上2つで、jsx:preserve
を指定することで、JSX部分はそのまんまで.jsx
に変換します。experimentalDecorators:true
は、デコレーターを噛ますのに必要です。今回ではvue-class-component
の@Component
ですね。
(他はだいたい天ぷら🍤)
Babel
VNode
へ変換するために、babel-plugin-transform-vue-jsx
を使います。
{
"presets": ["env"],
"plugins": ["transform-vue-jsx"]
}
Webpack
awesome-typescript-loader
のoptions
でuseBabel:true
すると、TypeScript変換後にバベるようになります。これで上記のコンパイルの流れで変換します。
module.exports = {
entry: __dirname + '/app.tsx',
output: {
path: __dirname + '/dev',
filename: 'app.js',
},
module: {
rules: [
{
test: /\.tsx$/,
exclude: /node_modules/,
use: [
{
loader: 'awesome-typescript-loader',
options: {
useBabel: true
}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin()
],
}
コードを書く
適当に用意しました。
import Vue, {CreateElement, VNode} from 'vue';
import Component from 'vue-class-component';
@Component({
props: {
name: {
type: String,
required: true
},
}
})
class Hello extends Vue {
name: string;
// mounted() {}
// handleClick = () => {}
render(h: CreateElement): VNode {
return (
<div>
Hello, {this.name} {this.$slots.default}
</div>
);
}
}
@Component
class App extends Vue {
render(h: CreateElement): VNode {
return (
<div>
<Hello
name="nju33"
>!!!!!</Hello>
</div>
);
}
}
const div = document.createElement('div');
document.body.appendChild(div);
new Vue({
render(h: CreateElement): VNode {
return <App />;
}
}).$mount(div);
必ず@Component
と付けるようにします。じゃないとエラーです。
methods
やdata
なんかで包んでいた値は、そのままプロパティ値やメソッドとして定義して使っても大丈夫で、components
とかもスコープ上にあれば直接使えちゃいます。(Reactみたいに)
また、Reactで言うthis.props.children
は、this.$slots.xxx
に入ってますね。
ただ、上記だとrender
の戻り値がany
でエラーが出るみたいなのでJSX.Elementをこんな感じで型定義してVNode
にキャストできるようにしときます。
import Vue, {VNode} from 'vue';
declare global {
namespace JSX {
interface Element extends VNode {}
interface ElementClass extends Vue {}
interface IntrinsicElements {
[element: string]: any;
}
}
}
結果
上のコードを実行するとこんな感じで表示されます。
- あ、これReactだ。
-
.vue
とどっちを使うかはアナタ次第って感じじゃ。はたまたミックスか🤔