LoginSignup
29
22

More than 3 years have passed since last update.

爆速でフロントエンドのコーディングを始める開発環境(webpack4/jQuery)

Last updated at Posted at 2019-04-28

フロントエンドの開発環境を構築するのが意外に大変ですよね。。そんな時間かけてられないし。
ということで、GithubRepositoryで管理したのでその内容を書きます。
そのRepositoryはこちら「macotok/frontend_develop_environment

想定する作業

  • WordPressに落とし込む前のHTMLコーディング
  • 依頼者がjQueryでの制作を求めてるとき
  • VueやReactを使わない案件(LPとか)

作業したこと

  • webpackでbundle
    • ES6(@babel/polyfill(async/await対応))
    • TypeScript
    • Sass
    • autoprefixerを自動生成
    • productionモードでcss/jsファイル圧縮
    • imageファイル圧縮
    • WebFont対応(woff/eot/ttf/svg)
    • media対応(mp4/webm/ogg)
    • jQueryに依存したライブラリに対応
  • npm scriptsでタスクランナー
  • jQuery
  • font「NotoSansJP」設定
  • fontawesomeのcssをnode_modulesで管理
  • pugでHTMLコーディング
    • _include
    • index.pug
    • buildすると_includeを削除
  • cssファイル(smacss設計)
    • base
    • function
    • layout
    • mixin
    • module
    • responsive
    • state
    • variable
    • animation
    • font
  • Lint
    • ESLint
    • StyleLint

Tree

├── babel.config.js
├── package.json
├── src
│   ├── images
│   ├── js
│   │   ├── app.ts
│   ├── media
│   ├── pug
│   │   ├── _include
│   │   │   └── meta.pug
│   │   └── index.pug
│   ├── sass
│   │   ├── _animation.scss
│   │   ├── _base.scss
│   │   ├── _function.scss
│   │   ├── _layout.scss
│   │   ├── _mixin.scss
│   │   ├── _module.scss
│   │   ├── _responsive.scss
│   │   ├── _state.scss
│   │   ├── _variable.scss
│   │   ├── _webfont.scss
│   │   └── style.scss
│   └── webfonts
├── stylelint.config.js
├── webpack.config.js
└── yarn.lock

package.json

"scripts": {
  "start": "concurrently \"webpack-dev-server\" \"yarn run watch:pug\" \"yarn run tsc -- -w\"",
  "dev": "webpack --mode development",
  "build": "concurrently \"rm -rf dist\" \"webpack\" \"yarn run pug\" --mode production",
  "pug": "pug src/pug/ --hierarchy -o dist/html -P",
  "watch:pug": "pug src/pug/ --hierarchy -o dist/html -P -w",
  "tsc": "tsc -p ./"
},
"dependencies": {
  "jquery": "^3.3.1",
  "yui": "^3.18.1"
},
"devDependencies": {
  "@babel/core": "^7.4.0",
  "@babel/polyfill": "^7.4.4",
  "@babel/preset-env": "^7.4.2",
  "@fortawesome/fontawesome-free": "^5.8.1",
  "autoprefixer": "^9.5.0",
  "babel-loader": "^8.0.5",
  "concurrently": "^4.1.0",
  "copy-webpack-plugin": "^5.0.2",
  "core-js": "^3.0.1",
  "css-loader": "^2.1.1",
  "eslint": "^5.16.0",
  "eslint-config-airbnb-base": "^13.1.0",
  "eslint-plugin-import": "^2.17.2",
  "file-loader": "^3.0.1",
  "imagemin-webpack-plugin": "^2.4.2",
  "mini-css-extract-plugin": "^0.5.0",
  "node-sass": "^4.11.0",
  "optimize-css-assets-webpack-plugin": "^5.0.1",
  "postcss-loader": "^3.0.0",
  "pug": "^2.0.3",
  "pug-cli": "github:pugjs/pug-cli#master",
  "pug-loader": "^2.4.0",
  "sass-loader": "^7.1.0",
  "style-loader": "^0.23.1",
  "stylelint": "^10.0.1",
  "stylelint-config-recommended": "^2.2.0",
  "stylelint-config-recommended-scss": "^3.3.0",
  "stylelint-scss": "^3.6.0",
  "ts-loader": "^6.1.0",
  "typescript": "^3.6.3",
  "uglifyjs-webpack-plugin": "^2.1.2",
  "url-loader": "^1.1.2",
  "webpack": "^4.29.6",
  "webpack-cli": "^3.3.0",
  "webpack-dev-server": "^3.2.1"
}

webpack.config.js

const Autoprefixer = require('autoprefixer');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ImageminPlugin = require('imagemin-webpack-plugin').default;
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const path = require('path');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const webpack = require('webpack');

module.exports = {
  entry: {
    app: [
      './src/js/app.js',
      './src/sass/style.scss',
    ],
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'js/[name].js',
    publicPath: '/',
  },
  devServer: {
    contentBase: './dist/html/',
    watchContentBase: true,
    port: 3000,
    open: true,
  },
  module: {
    rules: [
      {
        test: /\.js?$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
      },
      {
        test: /\.ts$/,
        use: 'ts-loader',
      },
      {
        test: /\.scss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          {
            loader: 'css-loader',
            options: {
              url: false,
              sourceMap: true,
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: [
                Autoprefixer(
                  {
                    browsers: ['last 2 versions', 'Android >= 4'],
                  },
                ),
              ],
            },
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true,
            },
          },
        ],
      },
      {
        test: /\.(jpe?g|png|gif|svg)(\?[a-z0-9=.]+)?$/,
        use: [
          {
            loader: 'url-loader',
          },
        ],
      },
      {
        test: /\.(woff|woff2|eot|ttf|svg)(\?[a-z0-9=.]+)?$/,
        use: [{
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: './webfonts',
            publicPath: '../webfonts',
          },
        }],
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]',
            },
          },
        ],
      },
    ],
  },
  optimization: {
    minimizer: [
      new OptimizeCSSAssetsPlugin(),
      new UglifyJsPlugin(),
    ],
  },
  resolve: {
    extensions: ['.js', '.ts'],
  },
  devtool: 'source-map',
  plugins: [
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, 'src/images/'),
        to: path.resolve(__dirname, 'dist/images/'),
      },
      {
        from: path.resolve(__dirname, 'src/webfonts/'),
        to: path.resolve(__dirname, 'dist/webfonts/'),
      },
      {
        from: path.resolve(__dirname, 'src/media/'),
        to: path.resolve(__dirname, 'dist/media/'),
      },
    ]),
    new ImageminPlugin({
      test: /\.(jpe?g|png|gif|svg)$/i,
      pngquant: {
        quality: '95-100',
      },
    }),
    new MiniCssExtractPlugin({
      filename: 'css/style.css',
    }),
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
      'window.jQuery': 'jquery',
    }),
  ],
};

説明

webpack4でbabel

webpackのversionが4なのでbabelは@babelcorepreset-envだけ。
そういえばこれだと非同期処理でasync awaitが書けないのでPromiseを使った。いつか直す。 使用可能

polyfillでasync/await対応

非同期処理をasync/awaitで書きたかったのでpolyfillを設定して対応しました。
polyfillは今まではグローバルで読み込んでいたのを、useBuiltIns: 'usage',と書くと使用する箇所だけ適用する、かつグローバルを汚染させない。

package.json
"@babel/polyfill": "^7.4.4",
"core-js": "^3.0.1",
babel.config.js
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        useBuiltIns: 'usage',
        corejs: '3',
        targets: {
          browsers: ['last 2 versions', '> 3% in JP'],
        },
      },
    ],
  ],
};

参考

ベンダープレフィックスを自動生成

ベンダープレフィックスを書くのが面倒なのでpostcssで自動で生成するようにwebpackで行なう。これで作業するときはベンダープレフィックスを書かなくてOK。

cssとjsをoptimize

cssとjsをminify化したいとき、いわゆるproductionモードにしたいときは$ yarn buildのコマンドでdistに吐き出される。

jQueryのライブラリ対応

slickとかfancyboxのjQueryライブラリをnpmでinstallしたときnode_modulesの中の$記述に対応させるためにwebpackのpluginで対応。
これ書かないと「$ is not function」でエラーになります。

weboack.config.js
plugins: [
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    'window.jQuery': 'jquery',
  }),
]

npmでタスクランナー

「gulpなんて今どき使うんですか?」
ってことで、「npm scripts」でタスクランナー書いてます。
そんな記述は多くないですが、$ yarn devsourcemapを吐き出します。
あと「pug/css/js」ファイルの変更をwatchしてます。

package.json
"scripts": {
    "start": "concurrently \"webpack-dev-server\" \"yarn run watch:pug\" --watch",
    "dev": "webpack --mode development",
    "build": "concurrently \"rm -rf dist\" \"webpack\" \"yarn run pug\" --mode production",
    "pug": "pug src/pug/ --hierarchy -o dist/html -P",
    "watch:pug": "pug src/pug/ --hierarchy -o dist/html -P -w"
  },

NotoSansJPに対応

意外にfont-familyでNotoSansJPを使いたいというオーダーがあったので予め設定しておきました。
使わない場合はソースを削除すればOK。

sass/_base.scss
@font-face {
  font-family: 'Noto Sans JP';
  font-style: normal;
  font-weight: 400;
  src:
          url(//fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Regular.woff2) format('woff2'),
          url(//fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Regular.woff) format('woff'),
          url(//fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Regular.otf) format('opentype');
}

body {
  font-family: 'Noto Sans JP', 'Hiragino Kaku Gothic ProN', 'メイリオ', sans-serif;
}

fontawesomeのcssファイルをnode_modulesで管理

webfontはどのサイトでも使うと思うのでこれも初期設定しておきました。
有名なfontawesomeを使うことを想定してcssファイルをnpmでinstallしてます。

$ yarn add -D @fortawesome/fontawesome-free

そして使うときはfont.scssに書いてます

sass/_font.scss
@import "~@fortawesome/fontawesome-free/scss/brands.scss";
@import "~@fortawesome/fontawesome-free/scss/fontawesome.scss";
.instagram {
  &::before {
    content: '\f16d';
    font-style: normal;
    font-variant: normal;
    text-rendering: auto;
    -webkit-font-smoothing: antialiased;
    font-family: 'Font Awesome 5 Brands', sans-serif;
  }
}

参考

HTMLテンプレートエンジンにPugを使用

Vueのtemplateを書くときもPugで書いてますよね。もはやHTMLを生で書く時代は終わりました。
$ yarn buildしたときに_includeディレクトリも出力されるのでその回避策として、下記のpug-cliをinstallするとのこと

$ yarn add github:pugjs/pug-cli#master
package.json
"pug-cli": "github:pugjs/pug-cli#master",

なんかあんまり美しくないですよね。
他にやり方があれば教えてください。。

参考

_function.scssにz-indexの順番を管理

いまどきz-indexの指定で9999という数字をみると寒気がしませんか?
その値をfunction.scssで管理してます。

sass/_function.scss
$z-map: (
  main,
  nav,
  header,
  drawer
);

@function z($name) {
  @return index($z-map, $name);
}

// 使うとき
z-index: z(main);

参考

_animation.scssに@keyframes記述

animationのkeyframeの指定がファイルのあちこちに散らばるのがあまり好きじゃないので一元管理させました。
とりあえずはfadeInfadeOutだけでいいかなと。

sass/_animation.scss
@keyframes fadeIn {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

@keyframes fadeOut {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

ESLint設定

lintチェックされてないファイルはもう見たくないですよね?規則性がないソースは目が拒絶します。
こんな感じで設定しました。airbnb-baseを使用。

.eslintrc
{
  "extends": [
    "airbnb-base"
  ],
  "parserOptions": {},
  "globals": {
    "window": true,
    "document": true,
    "fetch": false
  }
}

StyleLint設定

ESLintは結構前から使ってましたが、StyleLintは割と最近から使ってます。
これも設定するとコードに統一性が生まれて安眠できます。

stylelint.config.js
module.exports = {
  plugins: [
    'stylelint-scss',
  ],
  extends: [
    'stylelint-config-recommended',
    'stylelint-config-recommended-scss',
  ],
  rules: {
    'font-family-no-missing-generic-family-keyword': true,
    'declaration-block-no-shorthand-property-overrides': true,
    'declaration-block-trailing-semicolon': 'always',
    'selector-pseudo-element-colon-notation': 'double',
    'number-leading-zero': 'never',
    'block-closing-brace-empty-line-before': 'never',
    'selector-max-empty-lines': 0,
    'max-empty-lines': 0,
  },
};

ruleの設定に苦労したので、ruleの内容を事細かにまとめてくださったサイトを参考にしました。

参考

reset.cssはyuiを読み込む

reset.cssを外部から持ってくることが多く、且つ改変すると著作権に抵触する可能性があるので、予めpackageでinstallして読み込ませました。

sass/style.scss
@import "~yui/cssreset/cssreset-min";

TypeScript設定

tsconfig.jsonにてTypeScriptの設定を書きました。
内容についてはこちらの記事を参照してください。「TypeScript導入編

まとめ

これで急なコーディング依頼が来てもすぐに着手できますね。
まだ動的サイトを作るのにWordPressを使用する案件はありそうなので。。

29
22
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
29
22