tag:crieit.net,2005:https://crieit.net/users/_Masahiro_H_/feed Masahiro Haradaの投稿 - Crieit CrieitでユーザーMasahiro Haradaによる最近の投稿 2019-01-14T01:07:09+09:00 https://crieit.net/users/_Masahiro_H_/feed tag:crieit.net,2005:PublicArticle/14719 2019-01-14T01:06:52+09:00 2019-01-14T01:07:09+09:00 https://crieit.net/posts/Vue-Vue-Router-Vuex-Laravel-16 Vue + Vue Router + Vuex + Laravel チュートリアル(全16回)を書きました。 <p><img src="https://cdn.hypertextcandy.com/posts/vue-laravel-tutorial/eye-catch.jpg" alt="" /></p> <p><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com">技術ブログ</a>に<br /> 『<a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-introduction/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう</a>(全16回)』<br /> を公開しました 😄👏🎉</p> <p>出来るだけたくさんの方に見てほしいので宣伝エントリを書きます。</p> <h2 id="コンテンツ"><a href="#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84">コンテンツ</a></h2> <p>フロントエンドに Vue.js + Vue Router + Vuex と<br /> サーバーサイドに Laravel を使用して写真共有アプリを開発するという内容です。</p> <p>こんなやつ 👇 ができます。</p> <p><img src="https://cdn.hypertextcandy.com/posts/vue-laravel-tutorial/VLT-vuesplash-1.jpg" alt="トップページ" /></p> <p>写真を投稿できます。</p> <p><img src="https://cdn.hypertextcandy.com/posts/vue-laravel-tutorial/VLT-vuesplash-7.jpg" alt="写真投稿パネル" width="480" /></p> <p>コメント投稿、いいね、ダウンロード機能も実装します。</p> <p><img src="https://cdn.hypertextcandy.com/posts/vue-laravel-tutorial/VLT-vuesplash-5.jpg" alt="写真閲覧ページ" /></p> <p>ログイン・会員登録といった認証機能もカバーします。</p> <p><img src="https://cdn.hypertextcandy.com/posts/vue-laravel-tutorial/VLT-vuesplash-3.jpg" alt="ログイン / 会員登録ページ" /></p> <p>ちなみにチュートリアルで扱うツールなどのバージョンは以下の通りです。</p> <div class="table-responsive"><table> <thead> <tr> <th align="center">Node</th> <th align="center">npm</th> <th align="center">Vue.js</th> <th align="center">Vue Router</th> <th align="center">Vuex</th> <th align="center">PHP</th> <th align="center">Laravel</th> </tr> </thead> <tbody> <tr> <td align="center">10.7.0</td> <td align="center">6.4.1</td> <td align="center">2.5.21</td> <td align="center">3.0.2</td> <td align="center">3.0.1</td> <td align="center">7.2.10</td> <td align="center">5.7.19</td> </tr> </tbody> </table></div> <h2 id="学べること"><a href="#%E5%AD%A6%E3%81%B9%E3%82%8B%E3%81%93%E3%81%A8">学べること</a></h2> <ul> <li>Vue.js と Laravel を組み合わせる</li> <li>ルーティングライブラリ Vue Router を取り入れる</li> <li>状態管理ライブラリ Vuex を取り入れる</li> <li>タブやローディングを作る</li> <li>Vue.js + Laravel の構成でクッキー認証と CSRF 対策を行う</li> <li>SPA でエラー処理を行う</li> </ul> <h2 id="こんな人に読んでほしい"><a href="#%E3%81%93%E3%82%93%E3%81%AA%E4%BA%BA%E3%81%AB%E8%AA%AD%E3%82%93%E3%81%A7%E3%81%BB%E3%81%97%E3%81%84">こんな人に読んでほしい</a></h2> <p>Vue や Laravel について、</p> <ul> <li>入門書を読んだあとに実際に何か作ってみたい!という人</li> <li>仕事などで触れて基本的な機能はわかったけどゼロからアプリを作ったことはない人</li> </ul> <p>より複雑なアプリを作れるようになるためのきっかけになればいいなと思います 🤗</p> <h2 id="連載記事"><a href="#%E9%80%A3%E8%BC%89%E8%A8%98%E4%BA%8B">連載記事</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-introduction/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (1) イントロダクション</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-application-design/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (2) アプリケーションの設計</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-setting-up-spa-project/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (3) SPA開発環境とVue Router</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-authentication/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (4) 認証API</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-authentication-part-2/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (5) 認証ページ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-authentication-part-3/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (6) 認証機能とVuex</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-authentication-part-4/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (7) 認証機能とVuex Part.2</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-error-handling/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (8) エラーハンドリング</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-submit-photo/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (9) 写真投稿API</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-submit-photo-part-2/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (10) 写真投稿フォーム</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-list-photos/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (11) 写真一覧取得API</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-list-photos-part-2/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (12) 写真一覧ページ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-photo-detail/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (13) 写真詳細ページ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-add-comment/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (14) コメント投稿機能</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-likes/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (15) いいね機能</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/vue-laravel-tutorial-error-handling-part-2/">Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (16) エラーハンドリング Part.2</a></li> </ul> <p>🚀🚀🚀</p> <p>以上です。ぜひチェックしてみてください。</p> Masahiro Harada tag:crieit.net,2005:PublicArticle/14599 2018-11-12T00:32:00+09:00 2018-11-12T00:32:00+09:00 https://crieit.net/posts/Gulp-4-SCSS-Stylelint-Webpack-ESLint-Edge-js-BrowserSync Gulp 4 で SCSS + Stylelint + Webpack + ESLint + Edge.js + BrowserSync なフロントエンド環境構築 <p>(<a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/gulp-4-frontend-development-starter/">ブログ</a>からのクロス投稿です。)</p> <p><img src="https://cdn.hypertextcandy.com/posts/gulp-4-frontend-development-starter/EyeCatch.jpg" alt="Eye catch" /></p> <p>この記事では、Gulp 4でフロントエンドの開発環境を構築する方法を紹介します。Gulp 4をタスクランナーとして、CSSのトランスパイルに SCSS + Stylelint、JavaScript のトランスパイルに Webpack + ESLint、、テンプレートエンジンとして Edge.js、さらにローカルサーバーとして BrowserSync を組み合わせます。</p> <p>私はどちらかというとサーバサイドな人でフロントエンド専門ではないのですが、モックアップ作ったりで HTML コーディングしたくなって、いまさらながら Gulp で自分なりの環境構築をしたので結果を共有します。</p> <p>フロントエンドと言っても全面的に React や Vue でゴリゴリアプリケーションを書くというより HTML コーディングの範疇内でのユースケースを想定しています(フレームワークでゴリゴリしたいなら <a target="_blank" rel="nofollow noopener" href="https://github.com/facebook/create-react-app">create-react-app</a> とか <a target="_blank" rel="nofollow noopener" href="https://cli.vuejs.org/">vue-cli</a> とか使うといいです)。</p> <h2 id="概要"><a href="#%E6%A6%82%E8%A6%81">概要</a></h2> <p>サンプルコードも含めた設定の全体については<a target="_blank" rel="nofollow noopener" href="https://github.com/MasahiroHarada/gulp-4-starter">こちらのリポジトリ</a>を参照してください。</p> <h3 id="依存パッケージ"><a href="#%E4%BE%9D%E5%AD%98%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8">依存パッケージ</a></h3> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/MasahiroHarada/gulp-4-starter/blob/master/package.json">https://github.com/MasahiroHarada/gulp-4-starter/blob/master/package.json</a></p> <p>ちなみに開発環境は Node v10.7.0、npm v6.4.1 です。</p> <h3 id="ビルドコマンド"><a href="#%E3%83%93%E3%83%AB%E3%83%89%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89">ビルドコマンド</a></h3> <p>最終的なビルドコマンドは以下の通りです。</p> <pre><code class="console">// 開発ビルド:監視モードで実行されます。 $ npm run dev // 本番ビルド:JavaScript が圧縮されます。 $ npm run prod </code></pre> <h2 id="ビルドタスク"><a href="#%E3%83%93%E3%83%AB%E3%83%89%E3%82%BF%E3%82%B9%E3%82%AF">ビルドタスク</a></h2> <h3 id="gulpfile.babel.js"><a href="#gulpfile.babel.js">gulpfile.babel.js</a></h3> <p>ES Modules(import/export)を使用したかったのでファイル名に <code>.babel</code> をつけて Babel のトランスパイルがかかるようにしました。</p> <p>gulpfile.babel.js</p> <pre><code class="js">import { series, parallel, watch } from 'gulp'; import { reload, serve } from './tasks/server'; import { styles } from './tasks/styles'; import { scripts } from './tasks/scripts'; import { templates } from './tasks/templates'; import { images } from './tasks/images'; import { clean } from './tasks/clean'; import { sass as sassConfig, scripts as jsConfig, images as imagesConfig, templates as templatesConfig } from './tasks/config'; /** * ファイルの変更を監視 */ function watchFiles() { // Sass watch(sassConfig.src, series(styles, reload)); // Templates watch( [templatesConfig.edges, templatesConfig.data, templatesConfig.helper], series(templates, reload) ); // JavaScript watch(jsConfig.src, series(scripts, reload)); // Images watch(imagesConfig.src, series(images, reload)); } /** * 開発用ビルド */ export const dev = series( clean, parallel(styles, templates, scripts, images), serve, watchFiles ); /** * 本番用ビルド */ export const build = series( clean, parallel(styles, templates, scripts, images) ); </code></pre> <p>前述の通り gulpfile を ES2015 以降の構文で書くために Babel の設定ファイルも追加します。</p> <p>.babelrc</p> <pre><code class="json">{ "presets": [ [ "@babel/preset-env", { "targets": { "node": "current" } } ] ] } </code></pre> <p>タスクを色々書くと長くなってしまうので、個別のタスクはそれぞれ別ファイルに分け、<code>tasks</code> ディレクトリに格納しています。</p> <p>それぞれ見ていきましょう。</p> <h3 id="設定ファイル"><a href="#%E8%A8%AD%E5%AE%9A%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB">設定ファイル</a></h3> <p>まずは設定ファイルです。パスの設定が入っています。</p> <p>tasks/config.js</p> <pre><code class="js">const ASSET_ROOT = 'src'; const DEST_ROOT = 'public'; export const sass = { src: `${ASSET_ROOT}/sass/**/*.scss`, dest: `${DEST_ROOT}/styles` }; export const scripts = { srcRoot: `${ASSET_ROOT}/js`, src: `${ASSET_ROOT}/js/**/*.js`, dest: `${DEST_ROOT}/js`, babelrc: { presets: [['@babel/env', { targets: '> 0.25%, not dead' }]] } }; export const templates = { root: `${ASSET_ROOT}/templates`, edges: `${ASSET_ROOT}/templates/**/*.edge`, pages: `${ASSET_ROOT}/templates/pages/**/*.edge`, data: `${ASSET_ROOT}/templates/data.json`, helper: `${ASSET_ROOT}/templates/helper.js`, dest: DEST_ROOT }; export const images = { src: `${ASSET_ROOT}/images/**/*.*`, dest: `${DEST_ROOT}/images` }; export const isProd = process.env.NODE_ENV === 'production'; </code></pre> <h3 id="BrowserSync"><a href="#BrowserSync">BrowserSync</a></h3> <p>ファイルの変更を検知して自動でブラウザをリロードさせるため、<a target="_blank" rel="nofollow noopener" href="https://browsersync.io/">BrowserSync</a> のタスクを作成します。設定方法は<a target="_blank" rel="nofollow noopener" href="https://github.com/gulpjs/gulp/blob/4.0/docs/recipes/minimal-browsersync-setup-with-gulp4.md">こちら(Minimal BrowserSync setup with Gulp 4)</a>を参考にしました。</p> <p>tasks/server.js</p> <pre><code class="js">import browserSync from 'browser-sync'; const server = browserSync.create(); /** * 開発用サーバ再起動 */ export function reload(cb) { server.reload(); cb(); } /** * 開発用サーバ起動 */ export function serve(cb) { server.init({ server: { baseDir: './public' } }); cb(); } </code></pre> <h3 id="Clean"><a href="#Clean">Clean</a></h3> <p>ファイルの出力前に出力先の既存ファイルを削除しておくためのタスクです。</p> <p>tasks/clean.js</p> <pre><code class="js">import del from 'del'; /** * 出力先のディレクトリを空にする */ export function clean() { return del(['public']); } </code></pre> <h3 id="スタイル"><a href="#%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB">スタイル</a></h3> <p>スタイルに関しては2つのタスクを定義しています。</p> <p>まず1つめはSCSS を CSS に変換するためのタスクです。ソースマップ生成(<a target="_blank" rel="nofollow noopener" href="https://github.com/gulp-sourcemaps/gulp-sourcemaps">gulp-sourcemaps</a>)とファイル圧縮(<a target="_blank" rel="nofollow noopener" href="https://github.com/scniro/gulp-clean-css">gulp-clean-css</a>)も実行しています。</p> <p>2つめは CSS(今回は SCSS)の構文チェックツール <a target="_blank" rel="nofollow noopener" href="https://stylelint.io/">Stylelint</a> を実行するためのタスクです。<a target="_blank" rel="nofollow noopener" href="https://github.com/olegskl/gulp-stylelint">gulp-stylelint</a> を使用します。またファイルの監視中に毎回すべてのファイルを lint せず変更があったファイルのみチェックするために <a target="_blank" rel="nofollow noopener" href="https://github.com/alexgorbatchev/gulp-changed-in-place">gulp-changed-in-place</a> というプラグインも使用します。</p> <p>tasks/styles.js</p> <pre><code class="js">import gulp from 'gulp'; import gulpSass from 'gulp-sass'; import sourcemaps from 'gulp-sourcemaps'; import cleancss from 'gulp-clean-css'; import plumber from 'gulp-plumber'; import gulpStylelint from 'gulp-stylelint'; import changed from 'gulp-changed-in-place'; import { sass as config, isProd } from './config'; /** * SCSS -> CSS */ export function sass() { return gulp .src(config.src) .pipe(plumber()) .pipe(sourcemaps.init()) .pipe(gulpSass().on('error', gulpSass.logError)) .pipe(cleancss()) .pipe(sourcemaps.write('.')) .pipe(gulp.dest(config.dest)); } /** * Stylelint */ export function stylelint() { return gulp .src(config.src) // firstPass は初回実行時に全ファイルを対象とするかどうかのオプション .pipe(changed({ firstPass: true })) .pipe(gulpStylelint({ failAfterError: isProd, reporters: [{ formatter: 'verbose', console: true }], syntax: 'scss' })); } export const styles = gulp.series(stylelint, sass); </code></pre> <p>Stylelint の設定ファイルです。</p> <p>```json:.stylelintrc<br /> {<br /> "extends": "stylelint-config-standard",<br /> "plugins": ["stylelint-order"],<br /> "rules": {<br /> "order/properties-alphabetical-order": true<br /> }<br /> }</p> <pre><code><br />### 画像圧縮 [gulp-imagemin](https://github.com/sindresorhus/gulp-imagemin) を使って画像圧縮を行うタスクです。 オプションはお好みでどうぞ。 tasks/images.js ```js import gulp from 'gulp'; import imagemin from 'gulp-imagemin'; import { images as config } from './config'; export function images() { return gulp .src(config.src) .pipe(imagemin()) .pipe(gulp.dest(config.dest)); } </code></pre> <h3 id="Edge.js → HTML"><a href="#Edge.js+%E2%86%92+HTML">Edge.js → HTML</a></h3> <p>テンプレートエンジン <a target="_blank" rel="nofollow noopener" href="https://edge.adonisjs.com/">Edge.js</a> のファイルを HTML に変換するためのタスクです。</p> <p>Edge.js について詳しくは<a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/build-frontend-with-edgejs">こちらの記事</a>もご覧ください。</p> <p>tasks/templates.js</p> <pre><code class="js">import gulp from 'gulp'; import edge from 'edge.js'; import tap from 'gulp-tap'; import rename from 'gulp-rename'; import fs from 'fs'; import path from 'path'; import { templates as config } from './config'; /** * Edge.js -> HTML */ export function templates() { // テンプレートを読み込む edge.registerViews(path.join(__dirname, `../${config.root}`)); // データファイルを読み込む const data = fs.existsSync(config.data) ? JSON.parse(fs.readFileSync(config.data, 'utf8')) : {}; // ヘルパー関数を読み込む fs.existsSync(config.helpers) && require(`../${config.helper}`); return gulp .src(config.pages) .pipe( tap(file => { const contents = edge.renderString(String(file.contents), data); file.contents = new Buffer(contents); }) ) .pipe(rename({ extname: '.html' })) .pipe(gulp.dest(config.dest)); } </code></pre> <h3 id="JavaScript"><a href="#JavaScript">JavaScript</a></h3> <p>タスク <code>esTranspile</code> は <a target="_blank" rel="nofollow noopener" href="https://www.npmjs.com/package/webpack-stream">Webpack</a> により JavaScript のトランスパイル〜バンドルを行います。</p> <p>また <code>esLint</code> タスクではコードのチェックのために <a target="_blank" rel="nofollow noopener" href="https://eslint.org/">ESLint</a> を実行します。<a target="_blank" rel="nofollow noopener" href="https://github.com/adametry/gulp-eslint">gulp-eslint</a> プラグインを使用します。</p> <p>tasks/scripts.js</p> <pre><code class="js">import gulp from 'gulp'; import gulpIf from 'gulp-if'; import plumber from 'gulp-plumber'; import gulpEslint from 'gulp-eslint'; import webpack from 'webpack'; import gulpWebpack from 'webpack-stream'; import changed from 'gulp-changed-in-place'; import { scripts as config, isProd } from './config'; export function esTranspile() { return gulp .src(config.src) .pipe(plumber()) // gulp.watch と一緒に使う場合は第二引数が必須らしい。 // https://www.npmjs.com/package/webpack-stream#usage-with-gulp-watch .pipe(gulpWebpack(require('../webpack.config.js'), webpack)) .pipe(gulp.dest(config.dest)); } export function esLint() { return gulp .src(config.src) .pipe(changed({ firstPass: true })) .pipe(gulpEslint()) .pipe(gulpEslint.format()) .pipe(gulpIf(isProd, gulpEslint.failAfterError())); } export const scripts = gulp.series(esLint, esTranspile); </code></pre> <p>Webpack の設定ファイルです。</p> <p>webpack.config.js</p> <pre><code class="js">import { scripts as config } from './tasks/config'; module.exports = { mode: process.env.NODE_ENV ? 'production' : 'development', entry: { app: `./${config.srcRoot}/app.js` }, module: { rules: [ { test: /\.js$/, use: 'babel-loader' } ] }, output: { filename: '[name].js', }, devtool: 'source-map' } </code></pre> <p>複数のファイルを出力したい場合は、以下のように <code>entry</code> に追記すれば OK です。</p> <pre><code class="js">entry: { app: `./${config.srcRoot}/app.js`, foo: `./${config.srcRoot}/foo.js` }, </code></pre> <p>こちらはESLint の設定ファイルです。</p> <p>.eslintrc</p> <pre><code class="json">{ "extends": ["eslint:recommended"], "parserOptions": { "ecmaVersion": 6, "sourceType": "module" }, "env": { "browser": true, "node": true } } </code></pre> <h3 id="フォーマッタ"><a href="#%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%82%BF">フォーマッタ</a></h3> <p>フォーマッタはオマケです。お好みの設定&エディタと連携していただければ。</p> <h4 id="EditorConfig"><a href="#EditorConfig">EditorConfig</a></h4> <p>.editorconfig</p> <pre><code class="ini">root = true [*] end_of_line = lf insert_final_newline = true [*.{html,edge,scss,css,js,svg}] indent_style = space indent_size = 2 </code></pre> <h4 id="Prettier"><a href="#Prettier">Prettier</a></h4> <p>.prettierrc</p> <pre><code class="json">{ "singleQuote": true } </code></pre> <p>以上です。参考になれば幸いです。</p> Masahiro Harada