フロントエンドの開発環境を構築するのが意外に大変ですよね。。そんな時間かけてられないし。
ということで、Github
のRepository
で管理したのでその内容を書きます。
その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は@babel
のcore
とpreset-env
だけ。
そういえばこれだと非同期処理で 使用可能async await
が書けないのでPromise
を使った。いつか直す。
polyfillでasync/await対応
非同期処理をasync/awaitで書きたかったのでpolyfillを設定して対応しました。
polyfillは今まではグローバルで読み込んでいたのを、useBuiltIns: 'usage',
と書くと使用する箇所だけ適用する、かつグローバルを汚染させない。
"@babel/polyfill": "^7.4.4",
"core-js": "^3.0.1",
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」でエラーになります。
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
}),
]
npmでタスクランナー
「gulpなんて今どき使うんですか?」
ってことで、「npm scripts」でタスクランナー書いてます。
そんな記述は多くないですが、$ yarn dev
でsourcemap
を吐き出します。
あと「pug/css/js」ファイルの変更をwatch
してます。
"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。
@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
に書いてます
@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
"pug-cli": "github:pugjs/pug-cli#master",
なんかあんまり美しくないですよね。
他にやり方があれば教えてください。。
参考
_function.scssにz-indexの順番を管理
いまどきz-index
の指定で9999
という数字をみると寒気がしませんか?
その値を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の指定がファイルのあちこちに散らばるのがあまり好きじゃないので一元管理させました。
とりあえずはfadeIn
とfadeOut
だけでいいかなと。
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
ESLint設定
lintチェックされてないファイルはもう見たくないですよね?規則性がないソースは目が拒絶します。
こんな感じで設定しました。airbnb-base
を使用。
{
"extends": [
"airbnb-base"
],
"parserOptions": {},
"globals": {
"window": true,
"document": true,
"fetch": false
}
}
StyleLint設定
ESLintは結構前から使ってましたが、StyleLintは割と最近から使ってます。
これも設定するとコードに統一性が生まれて安眠できます。
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して読み込ませました。
@import "~yui/cssreset/cssreset-min";
TypeScript設定
tsconfig.json
にてTypeScriptの設定を書きました。
内容についてはこちらの記事を参照してください。「TypeScript導入編」
まとめ
これで急なコーディング依頼が来てもすぐに着手できますね。
まだ動的サイトを作るのにWordPressを使用する案件はありそうなので。。