gulp卒業 -> webpack一本化 [フロント環境]

今までフロント構築時は、sassからcssへの変換およびサーバ起動はgulpでやってたんだけど、webpackで両方とも出来るということがわかったので、gulpは卒業してwebpackに一本化します。

### 前準備
$ node -v
v12.18.3
$ npm -v
6.14.6
$ npm -y init
$ npm -D install webpack webpack-cli
$ npx webpack -v
4.44.0
$ npm -D i sass-loader node-sass style-loader css-loader
$ npm -D i mini-css-extract-plugin optimize-css-assets-webpack-plugin

### webpack-dev-server導入
$ npm i -D webpack-dev-server

package.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server"
  },

webpack.config.js

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');

module.exports = {
	entry: {
		anyname: `./src/scss/style.scss`,
		main: `./src/index.js`,
	},
	output: {
		filename: "[name].js",
		path: path.resolve(__dirname, 'dist'),
	},

	devServer: {
		host: '192.168.33.10',
		port: '8000',
		contentBase: "dist",
		open: true
	},

	module: {
		rules: [
			{
				test: /\.scss$/,
				use: [
					{ loader: MiniCssExtractPlugin.loader },
					{ loader: 'css-loader' },
					{ loader: 'sass-loader' },
				],
			}
		],
	},
	plugins: [
		new MiniCssExtractPlugin({ filename: 'css/style.css'}),
	],
	optimization: {
		minimizer: [new OptimizeCSSAssetsPlugin({})],
	},
}

### サーバ起動
$ npm run start

/dist/index.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<link rel="stylesheet" href="css/style.css">
	<script src="main.js"></script>
</head>
<body>
	<h1>hello</h1>
</body>
</html>

/src/style.scssや/src/index.jsを変更すると、ブラウザが自動更新されます。

gulp使ってると、まだgulp触ってるの?みたいな風潮があるからね。。
babel、linterとか他にも色々あるみたいだけど、とりあえず今回はこの環境でフロントを作り始めたいと思います。

webpack4でsassからcssとindex.jsからmain.jsにトランスパイル

$ node -v
v12.18.3
$ npm -v
6.14.6
$ npm -y init
$ npm -D install webpack webpack-cli
$ npx webpack -v
4.44.0
$ npm -D i sass-loader node-sass style-loader css-loader
$ npm -D i mini-css-extract-plugin optimize-css-assets-webpack-plugin

webpack.config.js

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');

module.exports = {
	entry: {
		anyname: `./src/scss/style.scss`,
	},
	output: {
		path: path.resolve(__dirname, 'dist'),
	},
	// entry: `./src/index.js`,

	// output: {
	// 	filename: "main.js"
	// },
	module: {
		rules: [
			{
				test: /\.scss$/,
				use: [
					{ loader: MiniCssExtractPlugin.loader },
					{ loader: 'css-loader' },
					{ loader: 'sass-loader' },
				],
			}
		],
	},
	plugins: [
		new MiniCssExtractPlugin({ filename: 'css/style.css'}),
	],
	optimization: {
		minimizer: [new OptimizeCSSAssetsPlugin({})],
	},
}

./src/scss/style.scss

body {
	background-color: aqua;
}

### npx実行
$ npx webpack –mode production

./dest/css/style.css

body{background-color:#0ff}

sassからcssには出来た。

### sassはcssに、jsはdist/main.jsにトランスパイル
sassからcssにする場合と、index.jsからmain.jsにする場合に、一々webpack.config.jsのコメントアウトを外すのは面倒。entryを複数に書いてみる。

module.exports = {
	entry: {
		anyname: `./src/scss/style.scss`,
		main: `./src/index.js`,
	},
	output: {
		filename: "[name].js",
		path: path.resolve(__dirname, 'dist'),
	},
	// 省略
}

こうすると、distフォルダにanyname.jsとmain.jsが作られる。

あとはローカルサーバか。

webpack環境設定: JS内にCSSをバンドルしない場合

– 一般的なヘッダ要素

<link rel="stylesheet" href="style.css">
<script src="main.js"></script>

– webpackの “MiniCssExtractPlugin” を使用する
– バンドルしたjsから、stylesheet部分を別ファイルとして出力できる

– webpack, webpack-cli, webpack-dev-server, style-loader, css-loader, sass, sass-loaderインストール済

### モジュールインストール
$ npm i -D postcss-loader autoprefixer

webpack.config.js

module.exports = {

	mode: "development",
	devtool: "source-map",

	entry: `./src/index.js`,

	output: {
		filename: "main.js"
	},

	devServer: {
		host: '192.168.33.10',
		port: '8000',
		contentBase: "dist",
		open: true
	},

	module: {
		rules: [
			{
				test: /\.scss/,
				use: [
					"style-loader",
					{
						loader: "css-loader",
						options: { 
							url: false,
							sourceMap: true,
							importLoaders: 2
						}
					},
					{
						loader: "postcss-loader",
						options: {
							sourceMap: true,
							plugins: [
								require("autoprefixer")({
									grid: true
								})
							]
						}
					},
					{
						loader: "sass-loader",
						options: {
							sourceMap: true
						}
					}
				]
			}
		]
	}
};

webpack環境設定: Sass

– style.scssをmain.jsにバンドルさせる
– webpack, webpack-cli, webpack-dev-server, style-loader, css-loaderインストール済

### Sassモジュールインストール
$ npm i -D sass sass-loader

### webpack.config.js
– scssファイル -> sass-loader -> css-loader -> style-loader

const MODE = "development";
const enabledSourceMap = MODE === "development";

module.exports = {

	entry: `./src/index.js`,

	output: {
		filename: "main.js"
	},

	devServer: {
		host: '192.168.33.10',
		port: '8000',
		contentBase: "dist",
		open: true
	},

	module: {
		rules: [
			{
				test: /\.scss/,
				use: [
					"style-loader",
					{
						loader: "css-loader",
						options: { 
							url: false,
							sourceMap: enabledSourceMap,

							// 0 => no loaders(default);
							// 1 => postcss-loader;
							// 2 => postcss-loader, sass-loader;
							importLoaders: 2
						}
					},
					{
						loader: "sass-loader",
						options: {
							sourceMap: enabledSourceMap
						}
					}
				]
			}
		]
	}
};

/src/style.scss

/src/index.js

import "./style.scss";

$ npm run build

webpack環境設定: style.cssの設定

### 前提条件
– node:v12.18.3、npm:6.14.6
– webpack, webpack-cli, webpack-dev-serverインストール済

### Style LoaderとCSS Loaderのインストール
$ npm i -D style-loader css-loader

– 拡張子.cssファイルに対して、useで指定したLoaderが後ろから順番に適用される
– cssファイル -> css-loader -> style-loader -> main.js
webpack.config.js

module.exports = {
	// 省略

	module: {
		rules: [
			{
				test: /\.css/,
				use: [
					"style-loader",
					{
						loader: "css-loader",
						options: { url: false }
					}
				]
			}
		]
	}
};

/src/index.js

import "./style.css";

/src/style.css

$ npm run build
-> style.cssが /dist/main.jsにバンドルされる

### ソースマップの出力
– ソースマップとは変換前のコード情報
webpack.config.js

const MODE = "development";
const enabledSourceMap = MODE === "development";

module.exports = {

	entry: `./src/index.js`,

	output: {
		filename: "main.js"
	},

	devServer: {
		host: '192.168.33.10',
		port: '8000',
		contentBase: "dist",
		open: true
	},

	module: {
		rules: [
			{
				test: /\.css/,
				use: [
					"style-loader",
					{
						loader: "css-loader",
						options: { 
							url: false,
							sourceMap: enabledSourceMap
						}
					}
				]
			}
		]
	}
};

さあ、続いてSassに行きます。

フロント構築時のwebpack環境設定1

$ node -v
v12.18.3
$ npm -v
6.14.6

// package.json生成
$ npm init -y

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

// webpack本体のインストール
$ npm i -D webpack webpack-cli
$ ls
node_modules package-lock.json package.json

### src, distフォルダの作成
$ ls
dist node_modules package-lock.json package.json src

/src/index.js

import { hello } from "./sub";

hello();
[/jqvascript]

/src/sub.js
[javascript]
export function hello(){
	alert("hello method is executed.");
}

### webpackによるbuild
$ npx webpack
Hash: 910d84f6d37763164f2d
Version: webpack 4.43.0
Time: 127ms
Built at: 07/25/2020 12:01:30 AM
Asset Size Chunks Chunk Names
main.js 986 bytes 0 [emitted] main
Entrypoint main = main.js
[0] ./src/index.js + 1 modules 103 bytes {0} [built]
| ./src/index.js 40 bytes [built]
| ./src/sub.js 63 bytes [built]

### package.jsonのカスタマイズ
最低限の記述だけにし、npm run buildで実行できるようにする

{
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },
  "devDependencies": {
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12"
  }
}

$ npm run build

### webpack.config.js
– エントリーポイントを指定しなければ自動的に「src/index.js」がエントリーポイントに、出力先を指定しなければ自動的に「dist/main.js」に出力される
– modeを”development”にするとソースマップが出力される
/webpack.config.js

module.exports = {
	entry: `./src/index.js`,

	output: {
		filename: "main.js"
	}
};

### webpackでローカルサーバを起動し、変更時にブラウザリロード
$ npm i -D webpack-dev-server

– package.jsonに”start”を追加する
/package.json

{
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "start": "webpack-dev-server"
  },
  "devDependencies": {
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0"
  }
}

– config.jsでhostとportを指定する
/webpack.config.js

module.exports = {
	entry: `./src/index.js`,

	output: {
		filename: "main.js"
	},

	mode: "development",

	devServer: {
		host: '192.168.33.10',
		port: '8000',
		contentBase: "dist",
		open: true
	}
};

$ npm run start

OK、次は、webpackにsassを入れます。

vagrantでwebpack-dev-server

built-in serverがwebpackにある??

[vagrant@localhost react]$ ./node_modules/webpack-dev-server/bin/webpack-dev-server.js –content-base src –mode development
ℹ 「wds」: Project is running at http://0.0.0.0:8080/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from /home/vagrant/react/src
ℹ 「wdm」: Hash: f58893047107846a01ed
Version: webpack 4.41.2
Time: 1464ms
Built at: 2019-11-20 18:36:59
Asset Size Chunks Chunk Names
client.min.js 1.43 MiB main [emitted] main
Entrypoint main = client.min.js
[0] multi ../node_modules/webpack-dev-server/client?http://0.0.0.0:8080 ./js/client.js 40 bytes {main} [built]
[../node_modules/ansi-html/index.js] 4.16 KiB {main} [built]
[../node_modules/html-entities/index.js] 231 bytes {main} [built]
[../node_modules/react-dom/index.js] 1.33 KiB {main} [built]
[../node_modules/react/index.js] 190 bytes {main} [built]
[../node_modules/webpack-dev-server/client/index.js?http://0.0.0.0:8080] ../node_modules/webpack-dev-server/client?http://0.0.0.0:8080 4.29 KiB {main} [built]
[../node_modules/webpack-dev-server/client/overlay.js] 3.51 KiB {main} [built]
[../node_modules/webpack-dev-server/client/socket.js] 1.53 KiB {main} [built]
[../node_modules/webpack-dev-server/client/utils/createSocketUrl.js] 2.89 KiB {main} [built]
[../node_modules/webpack-dev-server/client/utils/log.js] 964 bytes {main} [built]
[../node_modules/webpack-dev-server/client/utils/reloadApp.js] 1.59 KiB {main} [built]
[../node_modules/webpack-dev-server/client/utils/sendMessage.js] 402 bytes {main} [built]
[../node_modules/webpack-dev-server/node_modules/strip-ansi/index.js] 161 bytes {main} [built]
[../node_modules/webpack/hot sync ^\.\/log$] ../node_modules/webpack/hot sync nonrecursive ^\.\/log$ 170 bytes {main} [built]
[./js/client.js] 2.69 KiB {main} [built]
+ 29 hidden modules
ℹ 「wdm」: Compiled successfully.

あれ、vagrantでポートフォワーディングしているipを叩いても、ダメだ。
192.168.34.10:8080
なぜ?

webpack.config.jsに以下を追加

devServer: {
    contentBase: __dirname + '/public',
    host: "0.0.0.0"
	},

phpのビルトインより楽そうですね^^

npmでReact.jsの環境を作る

react-calendarがnpmなので、npmで環境を作ります。

$ npm init
$ npm install –save-dev webpack webpack-cli webpack-dev-server
$ npm install –save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader

ここまでは、これまでやってきた通り

$ npm install –save-dev react react-dom
npm ERR! code ENOSELF
npm ERR! Refusing to install package with name “react” under a package

?? package.jsonのnameがreactで同じだからインストールできないとのこと
“name”: “react1” に変更して、再度実行

$ npm install –save-dev react react-dom

続いてwebpackの設定

var debug = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
var path = require('path');

module.exports = {
	context:math.josin(__dirname, "src"),
	entry: "./js/client.js",
	module: {
		rules: [{
			text: /\.jsx?$/,
				exclude: /(node_modules|bower_components)/,
				use: [{
					loader: 'babel-loader',
					options: {
						presents: ['@babel/preset-react', '@babel/preset-env']
					}
				}]
		}]
	},
	output: {
		path: __dirname + "/src/",
		filename: "client.min.js"
	},
	plugins: debug ? [] : [
		new webpack.optimize.OccurrenceOrderPlugin(),
		new webpack.optimize.UglifyJsPlugin({ mangle: false, sourcemap: false}),
	]
};

./src/index.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>React</title>
	<link href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.6/cosmo/bootstrap.min.css" type="text/css" rel="stylesheet"/>
</head>
<body>
	<div id="app"></div>
	<script src="client.min.js"></script>
</body>
</html>

ここまでで、Reactの環境構築完了!?

import React from "react"
import ReactDOM from "react-dom"

class Layout extends React.Component {
	render(){
		return (
			<h1>Welcome</h1>
		);
	}
}

const app = document.getElementById('app');
ReactDOM.render(<Layout/>, app);

[vagrant@localhost react]$ webpack –mode development
Hash: 12eaf7dc67869ad64caf
Version: webpack 4.41.2
Time: 1874ms
Built at: 2019-11-20 15:39:17
Asset Size Chunks Chunk Names
client.min.js 1.08 MiB main [emitted] main
Entrypoint main = client.min.js
[./js/client.js] 2.69 KiB {main} [built]
+ 11 hidden modules

何言いいいいいいいいいいいいいい

babel + webpack

webpack.config.js

const webpack = require('webpack');

module.exports = {
    target: 'node',
    entry: `./entry.js`,

    output: {
        path: `${__dirname}/output/`,
        filename: "main.js"
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader",
            }
        ]
    }
};

[vagrant@localhost front]$ npm run build

> @ build /home/vagrant/front
> webpack

Hash: eb8d6365b20e87793006
Version: webpack 4.41.2
Time: 421ms
Built at: 2019-11-20 13:15:11
1 asset
Entrypoint main = main.js
[0] ./entry.js 2 KiB {0} [built] [failed] [1 error]

WARNING in configuration
The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment.
You can also set it to ‘none’ to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

ERROR in ./entry.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
TypeError: /home/vagrant/front/entry.js: Cannot read property ‘bindings’ of null
at Scope.moveBindingTo (/home/vagrant/front/node_modules/@babel/traverse/lib/scope/index.js:822:13)
at BlockScoping.updateScopeInfo (/home/vagrant/front/node_modules/babel-plugin-transform-es2015-block-scoping/lib/index.js:364:17)
at BlockScoping.run (/home/vagrant/front/node_modules/babel-plugin-transform-es2015-block-scoping/lib/index.js:330:12)
at PluginPass.BlockStatementSwitchStatementProgram (/home/vagrant/front/node_modules/babel-plugin-transform-es2015-block-scoping/lib/index.js:70:24)
at newFn (/home/vagrant/front/node_modules/@babel/traverse/lib/visitors.js:179:21)
at NodePath._call (/home/vagrant/front/node_modules/@babel/traverse/lib/path/context.js:55:20)
at NodePath.call (/home/vagrant/front/node_modules/@babel/traverse/lib/path/context.js:42:17)
at NodePath.visit (/home/vagrant/front/node_modules/@babel/traverse/lib/path/context.js:90:31)
at TraversalContext.visitQueue (/home/vagrant/front/node_modules/@babel/traverse/lib/context.js:112:16)
at TraversalContext.visitSingle (/home/vagrant/front/node_modules/@babel/traverse/lib/context.js:84:19)
at TraversalContext.visit (/home/vagrant/front/node_modules/@babel/traverse/lib/context.js:140:19)
at Function.traverse.node (/home/vagrant/front/node_modules/@babel/traverse/lib/index.js:84:17)
at traverse (/home/vagrant/front/node_modules/@babel/traverse/lib/index.js:66:12)
at transformFile (/home/vagrant/front/node_modules/@babel/core/lib/transformation/index.js:119:29)
at runSync (/home/vagrant/front/node_modules/@babel/core/lib/transformation/index.js:48:5)
at runAsync (/home/vagrant/front/node_modules/@babel/core/lib/transformation/index.js:35:14)
at process.nextTick (/home/vagrant/front/node_modules/@babel/core/lib/transform.js:34:34)
at processTicksAndRejections (internal/process/task_queues.js:79:9)
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! @ build: `webpack`
npm ERR! Exit status 2
npm ERR!
npm ERR! Failed at the @ build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! /home/vagrant/.npm/_logs/2019-11-20T04_15_11_600Z-debug.log

webconfigで書くのはわかったけど、なんか上手くいかんな。。

webpack

import { increment } from './modules/increment';

console.log('before:', 0);
console.log('**increment**');
console.log('after', increment(0));

[vagrant@localhost front]$ npm run build && node ./output/main.js

> @ build /home/vagrant/front
> webpack

Hash: bd6e844b721278940d6c
Version: webpack 4.41.2
Time: 147ms
Built at: 2019-11-20 01:08:18
Asset Size Chunks Chunk Names
main.js 1.01 KiB 0 [emitted] main
Entrypoint main = main.js
[0] ./entry.js + 1 modules 193 bytes {0} [built]
| ./entry.js 143 bytes [built]
| ./modules/increment.js 50 bytes [built]

WARNING in configuration
The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment.
You can also set it to ‘none’ to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
before: 0
**increment**
after 2

使い方わかってきた^^