LoginSignup
10
6

More than 5 years have passed since last update.

TypeScript と Vue(ClassComponent) と JSX な環境

Last updated at Posted at 2017-10-31

なんとなく。

依存パッケージ

以下のものが必要なのでインストールします。

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というものへ変換する必要があります。この変換は、

  1. .tsxで書く <div>hoge</div>
  2. .jsxに変換 <div>hoge</div>
  3. そしてBabelで.jsに変換 h('div', null, ['hoge'])

という感じでやります。

環境構築

TypeScript

こんな設定にします。

tsconfig.json
{
  "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を使います。

.babelrc
{
  "presets": ["env"],
  "plugins": ["transform-vue-jsx"]
}

Webpack

awesome-typescript-loaderoptionsuseBabel:trueすると、TypeScript変換後にバベるようになります。これで上記のコンパイルの流れで変換します。

webpack.config.js
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()
  ],
}

コードを書く

適当に用意しました。

app.tsx
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と付けるようにします。じゃないとエラーです。

methodsdataなんかで包んでいた値は、そのままプロパティ値やメソッドとして定義して使っても大丈夫で、componentsとかもスコープ上にあれば直接使えちゃいます。(Reactみたいに)
また、Reactで言うthis.props.childrenは、this.$slots.xxxに入ってますね。

ただ、上記だとrenderの戻り値がanyでエラーが出るみたいなのでJSX.Elementをこんな感じで型定義してVNodeにキャストできるようにしときます。

jsx.d.ts
import Vue, {VNode} from 'vue';
declare global {
  namespace JSX {
    interface Element extends VNode {}
    interface ElementClass extends Vue {}
    interface IntrinsicElements {
      [element: string]: any;
    }
  }
}

結果

上のコードを実行するとこんな感じで表示されます。

Webpack_App_🔊.png

  • あ、これReactだ。
  • .vueとどっちを使うかはアナタ次第って感じじゃ。はたまたミックスか🤔
10
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6