@wakamshaさんの AltJS, AltCSS 課題を webpack, SCSS, TypeScript, React でやってみた
はじめに
こんにちは、すわ@秋葉原オフィスです。先日 Naoki YAMADA (@wakamsha) さんがSpeakerdeckで公開したAltJS/AltCSSに関するスライドを大変興味深く読ませてもらいました。
このスライドは社内の新卒社員向け研修の内容とのことで、最後に「課題」があったため、今回私なりに回答してみました。
…といったような、植木が執筆したブログに触発され、私が最近業務で使っている技術を織り交ぜた別解を作ってみました。
お題
- JavaScript, CSS いずれも何かしらのプリプロセッサを使用すること(※HTMLは任意)
- ローカルサーバーの起動
- ファイルを変更したら自動的にビルド処理が実行
- コンテンツは「Hello, world」でOK
- 自信がある人は 何かしらのフレームワークも導入してみよう
回答
今回は以下の回答をしてみました。
- webpack でビルドする
- AltJSとして TypeScript を使う
- AltCSSとして Sass (SCSS) を使う
- webpack-dev-server で自動ビルド&ローカルサーバー起動を行う
- ブラウザのホットリロードも行う
- フレームワークとして React を使う
検証環境
- macOS Sierra 10.12.4
- Homebrew 1.2.2
- Node.js v6.10.3
- yarn v0.24.6
環境構築
今回は npm だけだと味気ないので、yarn を使ってみました。コマンドは、npm
を yarn
に変えるだけでほぼ済みます。
$ brew install yarn
yarn init
は使わず、直接 package.json
を作成します。結局あとで編集する場合はこっちの方が楽です。
{ "name": "webpack-react-sample", "version": "1.0.0", "description": "webpack & React sample", "main": "app.js", "scripts": { "start": "$(yarn bin)/webpack-dev-server", "build": "$(yarn bin)/webpack --progress --colors", "postinstall": "yarn run build" }, "keywords": [ "webpack", "react", "typescript", "scss" ], "author": "suwa.yuki <[email protected]>", "license": "MIT", "dependencies": { "react": "^15.5.4", "react-dom": "^15.5.4" }, "devDependencies": { "@types/react": "^15.0.27", "@types/react-dom": "^15.5.0", "css-loader": "^0.28.4", "extract-text-webpack-plugin": "^2.1.0", "file-loader": "^0.11.2", "html-webpack-plugin": "^2.28.0", "node-sass": "^4.5.3", "sass-loader": "^6.0.5", "style-loader": "^0.18.2", "ts-loader": "^2.1.0", "typescript": "^2.3.4", "url-loader": "^0.5.8", "webpack": "^2.6.1", "webpack-dev-server": "^2.4.5" } }
scripts
に start
を書くことで、yarn start
で実行されます。また build
はカスタムコマンドです。こちらは yarn run build
で走ります。
なお postinstall
を書くことで、yarn install
実行時にビルドを走らせることができます。優しさです。
コード
ファイルがたくさんあるので GitHub にアップしました。
今回は React と TypeScript を組み合わせて使っています。
index.ts
では ReactDOM を使って div 要素にコンポーネントをレンダリングしています。
import * as React from 'react'; import * as ReactDOM from 'react-dom'; import App from "js/App"; import 'scss/main'; ReactDOM.render( <App content="Hello World!"/>, document.getElementById('app') );
index.ts
の中で呼んでいる App クラスは以下のような感じです。TypeScript で JSX がサポートされているので、このような書き方ができます。
import * as React from 'react'; export interface Props { content: string; } export default class MyComponent extends React.Component<Props, {}> { render() { return <div><h1>{this.props.content}</h1></div> } }
また、Webpack の設定は下記の通りです。ポイントは HtmlWebpackPlugin を使っているところです。これを使うと HTML ファイルをテンプレートを元に生成できます。ビルド対象のファイルを含めるタグ (script
や link
) を自動で付与してくれます。便利です。
'use strict'; const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const path = require('path'); module.exports = [ { name: 'app', context: path.join(__dirname, 'src'), entry: { index: './js/index.tsx', }, output: { path: path.join(__dirname, 'dist'), filename: 'assets/[name].js', publicPath: '' }, devServer: { contentBase: 'dist', port: 8000 }, devtool: 'source-map', module: { loaders: [ { test: /\.ts$|\.tsx$/, exclude: /node_modules/, loader: 'ts-loader' }, { test: /\.scss$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: [ { loader: 'css-loader?modules' }, { loader: 'sass-loader' } ] }) } ] }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx', '.css', '.scss'], modules: [ 'src', 'node_modules' ] }, plugins: [ new HtmlWebpackPlugin({ title: 'Example App', template: 'template/index.ejs' }), new ExtractTextPlugin('assets/[name].css') ] } ];
実行してみる
yarn run build
を実行すると、ビルドした後にローカルサーバーを起動し、待機モードに入ります。
$ yarn run build yarn run v0.24.6 $ $(yarn bin)/webpack --progress --colors 10% [0] building modules 0/1 modules 1 active ...webpack-react-sample/src/js/index.tsxts-loader: Using [email protected] and /Users/Yuki/work/webpack-react-sample/tsconfig.json [0] Hash: 163cc2fa966552a21ffb Version: webpack 2.6.1 Child app: Hash: 163cc2fa966552a21ffb Time: 3576ms Asset Size Chunks Chunk Names assets/index.js 740 kB 0 [emitted] [big] index assets/index.css 95 bytes 0 [emitted] index assets/index.js.map 882 kB 0 [emitted] index assets/index.css.map 93 bytes 0 [emitted] index index.html 232 bytes [emitted] [0] ../~/process/browser.js 5.42 kB {0} [built] [10] ../~/react-dom/lib/ReactUpdates.js 9.53 kB {0} [built] [19] ../~/react/lib/React.js 3.32 kB {0} [built] [50] ../~/react/react.js 56 bytes {0} [built] [81] ./scss/main.scss 41 bytes {0} [built] [82] ../~/react-dom/index.js 59 bytes {0} [built] [83] ./js/App.tsx 951 bytes {0} [built] [113] ../~/react-dom/lib/ReactDOM.js 5.14 kB {0} [built] [177] ../~/react/lib/ReactPureComponent.js 1.32 kB {0} [built] [178] ../~/react/lib/ReactVersion.js 350 bytes {0} [built] [181] ../~/react/lib/onlyChild.js 1.34 kB {0} [built] [183] ./js/index.tsx 225 bytes {0} [built] [184] ../~/css-loader?modules!../~/sass-loader/lib/loader.js!./scss/main.scss 229 bytes [built] [186] ../~/style-loader/lib/addStyles.js 8.7 kB [built] [187] ../~/style-loader/lib/urls.js 3.01 kB [built] + 173 hidden modules Child html-webpack-plugin for "index.html": [0] ../~/lodash/lodash.js 540 kB {0} [built] [1] ../~/html-webpack-plugin/lib/loader.js!./template/index.ejs 551 bytes {0} [built] [2] ../~/webpack/buildin/global.js 509 bytes {0} [built] [3] ../~/webpack/buildin/module.js 517 bytes {0} [built] Child extract-text-webpack-plugin: [0] ../~/css-loader/lib/css-base.js 2.26 kB {0} [built] [1] ../~/css-loader?modules!../~/sass-loader/lib/loader.js!./scss/main.scss 229 bytes {0} [built] ✨ Done in 4.58s.
ローカルの 8000 ポートで Web サーバーが起動するので、ブラウザで http://localhost:8000/ にアクセスするとページが表示されます。
ソースコード (JavaScript ファイル または SCSS ファイル) が修正されると再ビルドが走り、自動的にブラウザの再読み込みが行われます。
まとめ
Naoki YAMADA (@wakamsha) さんの課題は、最近の JavaScript 界隈のほんの端っこを知る機会としてとても良い内容だと思いました。ぜひ皆さんも実際にコードを書いて動かしてみてください。