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

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を入れます。

NuxtJSを触ってみる

公式: NUXT JS
Vue.js に基づいたプログレッシブフレームワーク
Universalモード、SPAモード、Generateモードの3つのモードで柔軟にサイトを設計できる

### 機能
– Vue ファイルで記述できること(*.vue)
– コードを自動的に分割すること
– サーバーサイドレンダリング
– 非同期データをハンドリングするパワフルなルーティング
– 静的ファイルの配信
– ES2015+ のトランスパイレーション
– JS と CSS のバンドル及びミニファイ化
– 要素の管理
– 開発モードにおけるホットリローディング
– プリプロセッサ: Sass, Less, Stylus など
– HTTP/2 push headers ready
– モジュール構造で拡張できること

### 動作
Vue2(Vue本体), Vue Router(Routing), Vuex(Vue版Flux), Vue Server Render(ServerSideレンダリング), Vue Meta(メタ情報管理)

### NuxtJSを始めよう
$ cat /etc/os-release
NAME=”Ubuntu”
VERSION=”18.04.4 LTS (Bionic Beaver)”
// 以下略

// npm, nodejsインストール
$ sudo apt install -y nodejs npm
$ node -v
v8.10.0
$ npm -v
3.5.2

// npmを最新化
$ sudo npm install npm@latest -g

$ sudo npm install -g vue-cli // sudo権限がないとエラーになる

$ vue init nuxt-community/starter-template sample
? Project name sample
? Project description Nuxt.js project
? Author hpscript

$ cd sample
$ ls
README.md components middleware package.json plugins store
assets layouts nuxt.config.js pages static
$ sudo npm install
$ sudo npm run dev

> sample@1.0.0 dev /home/vagrant/local/sample
> nuxt

FATAL Unexpected token { 13:12:14

} catch {
^

ん? nodeがv8.10.0では古いよう。
最新に上げます。

$ sudo npm install n -g
$ sudo n stable
$ sudo apt purge -y nodejs npm
$ exec $SHELL -l
$ node -v
v12.18.3
$ npm -v
6.14.6
$ sudo npm run dev

vagrantのprivate networkが192.168.33.10なので、
nust.config.jsのサーバの設定も変更する必要があります。

module.exports = {
  // 省略
  server: {
    port: 8000, // デフォルト: 3000
    host: '192.168.33.10' // デフォルト: localhost
  },
  // 省略 
}

/pages/users/index.vue

<template>
	<section class="container">
		<div>
			<h1>user index page</h1>
			<p>count={{count}}</p>
			<button @click="addCount">カウントアップ</button>
		</div>
	</section>
</template>

<script>
export default {
	computed: {
		count() { return this.$store.state.counter.count }
	},
	methods: {
		addCount(e){
			this.$store.commit('counter/add')
		}
	}
}
</script>

/store/counter.js

export const state = () => ({
	count: 0
})

export const mutations = {
	add (state){
		state.count += 1
	}
}

Djangoで販売管理システムを開発していくロードマップ

– 今回はDjangoを学習するのが目的の一つでもあるため、設計書はざっくりと作る
– 販売管理システムは財務会計システムとの連携(ERP化)を見据えて作る
– できればRPA、機械学習、Docker、レジシステム連携、ECサイトのAPI連携、ラズパイ/Arduino連携を盛り込みたい
———–
1.全体
 1-1. 何故、販売管理システムを導入するか?
 1-2. 販売管理システムで出来る事(後から作る)
2. システム要求
 2-1. 機能要求
 2-2. 競合のシステムとの差別化
 2-3. 財務会計との連携(後から作る)
3. 基本設計
 3-1. 業務フロー
 3-2. ユースケース
 3-3. 機能詳細
4. 外部設計
4-1 システム概要図
 4-2 インフラ構成図
 4-3 ハードウェア構成図
 4-4 ミドルウェア構成図
5. UI設計
 5-1. URL一覧
 5-2. 画面遷移図
 5-3. 画面定義(別紙)
 5-4. メール設計
 5-5. バリデーション
6. 詳細設計
 6-1. MVC構成図
 6-2. シーケンス図
 6-3. コントローラ処理
 6-4. バッチ処理
 6-5. その他

設計書はサクッと作ろうと思ったけど、最低限の事はちゃんとやろうと思うとそこそこのボリューム感になってしまうな。

SAPのようなERPをDjangoで作りたい

ERPとは? : Enterprise Resource Planning(統合パッケージ)
-> 調達システム、経理システム、在庫システムなどヒトモノカネ情報の管理を一緒にした

### SAPの主なモジュール
– Financial Accounting(FI): 財務会計、業務データが流れてくる
– Controling(CO): 管理会計、業績管理や間接費の管理
– Sales and Distribution(SD): 販売管理、注文を受けてから商品を出荷、請求書
– Material Management(MM): 在庫購買管理、どこに何を発注したか、値段、在庫

### モジュール一覧
Sales and Distribution(販売管理), Material Management(在庫購買管理), Production Planning and Control(生産計画/管理), Plant Maintenance(プラント保全), Warehouse Management(倉庫管理), Financial Accounting(財務会計), Controlling(管理会計), Human Resources(人事管理), Project System(プロジェクト管理), Quality Management(品質管理), Investment Management(設備投資管理), Real Estate(不動産管理), Document Management System(文書管理), Classification(分類), クロスアプリケーション(Cross Application)

SAP Tutorial

– 各システムのログイン画面
– メニュー:各システム一覧(office, logistics, accounting, human resources….)
 L 各システムの帳票ページに遷移できる
– 各入力メニュー
 L ユーザが入力データのパラメータを編集できる
– IDEがトップにあり、その下に、地域、国、システム、従業員、ポジション、機能などでカテゴライズできる
– Masterデータ(client, company, sales area, personal record)と入力データを切り分ける
– メニューはツリー構造で表示する

SAP Engineer

ざっくりとは、各モジュールごとにアプリケーションを作っていき、データ連携させるってことだな。
ゼロから作るなら、(1)販売管理、(2)在庫購買管理、(3)財務会計 ぐらいから始めたいところか。
問題はどこまでやり込むかだな。

[Django]ユニットテスト

– from django.test import TestCaseでTestCaseを継承
– メソッドはtest_* にしなければならない
/sns/tests.py

from django.test import TestCase

class SnsTests(TestCase):

	def test_check(self):
		x = True
		self.assertTrue(x)
		y = 100
		self.assertGreater(y, 0)
		arr = [10, 20, 30]
		self.assertIn(20, arr)
		nn = None
		self.assertIsNone(nn)

$ python manage.py test sns
Creating test database for alias ‘default’…
System check identified no issues (0 silenced).
.
———————————————————————-
Ran 1 test in 0.001s

OK
Destroying test database for alias ‘default’…

### チェックメソッド
– assertTrue, assertFalse, assertIsl, assertIsNot, assertEqual, assertNoEqual, assertGreater, assertGreaterEqual, assertLess, assertLessEqual, assertIsNone, assertIsNotNone, assertIsIn, assertNotIn

### データベースのチェック

from django.test import TestCase

from django.contrib.auth.models import User
from .models import Message

class SnsTests(TestCase):

	def test_check(self):
		usr = User.object.first()
		self.assertIsNotNone(usr)
		msg = Message.objects.first()
		self.assertIsNotNone(msg)

– テスト用のデータベースを都度作って使用している

from django.test import TestCase

from django.contrib.auth.models import User
from .models import Group, Message

class SnsTests(TestCase):

	@classmethod
	def setUpClass(cls):
		super().setUpClass()
		(usr, grp) = cls.create_user_and_group()
		cls.create_message(usr, grp)

	@classmethod
	def create_user_and_group(cls):
		# Create public user & public group
		User(username="public", password="public", is_staff=False, is_active=True).save()
		pb_usr = User.objects.filter(username='public').first()
		Group(title='public', owner_id=pb_usr.id).save()
		pb_grp = Group.objects.filter(title='public').first()

		# Create test user
		User(username="test", password="test", is_staff=True, is_active=True).save()
		usr = User.objects.filter(username='test').first()

		return (usr, pb_grp)

	@classmethod
	def create_message(cls, usr, grp):
		# Create test massage
		Message(content='this is test message.', owner_id=usr.id, group_id=grp.id).save()
		Message(content='test', owner_id=usr.id, group_id=grp.id).save()
		Message(content="ok", owner_id=usr.id, group_id=grp.id).save()
		Message(content="ng", owner_id=usr.id, group_id=grp.id).save()
		Message(content='finish', owner_id=usr.id, group_id=grp.id).save()

	def test_check(self):
		usr = User.objects.first()
		self.assertIsNotNone(usr)
		msg = Message.objects.first()
		self.assertIsNotNone(msg)