forget for get

覚えるために忘れる

Vue CLIチュートリアル

https://cli.vuejs.org/guide/prototyping.html

 

Docker環境構築

https://cli.vuejs.org/guide/prototyping.html を参考に

 

Dockerfile

FROM node:16
#ENV http_proxy http://xxx:3128/
#ENV https_proxy http://xxx:3128/
WORKDIR /app

docker-compose.yml
※commandのところをコメントアウトしておく

version: '3'
services:
  node:
    build:
      context: .
    tty: true
    environment:
      - NODE_ENV=development
    volumes:
    - ./:/app
    #command: sh -c "cd hello-world && npm run serve"
    ports:
    - "8054:8080"

コンテナを起動してVueCLIをインストールしてサンプルプロジェクトをつくる

※ create時に色々聞かれるけど、読んで適当に選ぶ

docker-compose up -d
docker exec -it vuecli_docker_node_1 /bin/bash
npm install -g @vue/cli
vue create hello-world

起動!

cd hello-world
npm run serve

ブラウザからアクセスするとトップページが表示される

docker-compose.ymlのコメントアウトしてるところを戻すと、
次回起動時にvueも起動する

 

どんなファイルができてるか確認してみる。

public/index.html
src/main.js
src/App.vue
src/components/HelloWorld.vue

 

プロキシ下でのVagrant,Docker環境構築で色々ハマる

Vagrantfile

if Vagrant.has_plugin?("vagrant-proxyconf")
    config.proxy.http     = "http://xxx:3128"
    config.proxy.https    = "http://xxx:3128"
    config.proxy.no_proxy = "localhost,127.0.0.1,aaa,bbb"
end    

 

プロキシ設定するプラグインを入れてからvagrant upする
その前にプロキシ設定しないとプラグインが落ちてこない

set http_proxy=http://xxx:3128
set https_proxy=http://xxx:3128
vagrant plugin install vagrant-proxyconf
vagrant up

 

Dockerfile

ENV http_proxy http://xxx:3128/
ENV https_proxy http://xxx:3128/

 

vi /etc/docker/daemon.json

{ "insecure-registries":["aaa:5000","bbb:5000"] }

 

service docker restart
docker-compose up

LaravelMixチュートリアル Vue.js導入まで

LaravelMixを使ってみようとしたけど、Laravel公式のドキュメントだと全然わからず、、

https://readouble.com/laravel/8.x/ja/mix.html

LaravelMix公式のドキュメントを見ることに。

https://readouble.com/laravel/8.x/ja/mix.html

事前準備

Laravel + Docker環境

https://lightwill.hatenablog.com/entry/2022/01/20/150821

 

nodeJSが入ってないので入れる

docker exec -it laravel_docker-app-1 /bin/bash
curl -sL https://deb.nodesource.com/setup_16.x -o setup.sh
sh setup.sh
apt-get install -y nodejs
npm install -g npm

チュートリアル

npm install

途中で固まったり、、時間がかかる

JS、CSSを適当に編集

vi resources/js/app.js

alert('Hello World');

vi resources/css/app.css

body {
    color: red;
}
npx mix

以下が作成される
public/js/app.js
public/css/app.css
webpack.mix.jsに書いてある

mix.js('resources/js/app.js', 'public/js').postCss('resources/css/app.css', 'public/css', []);

適当にviewをつくって、JSとCSSを読み込んで確認してみる。

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

npx mix watch

で変更があったら自動でコンパイルして反映してくれる

 

Vue.js導入

LaravelMixのドキュメントだと説明が足りない、、

https://laravel-mix.com/docs/6.0/vue

事前にvue関連をインストール

npm install vue vue-router vuex vue-template-compiler vue-loader --save-dev

LaravelMixのドキュメントを参考にwebpack.mix.jsとapp.jsを編集、Alert.vueを作成

bladeに以下を追加

<div id="app">
    <alert></alert>
</div>

 

npx mix watchしてるのにJSやCSSの変更が反映されない!

ブラウザをスーパーリロードしないと反映されない、、

webpack.mix.jsに.version()を追記

mix.js('resources/js/app.js', 'public/js').vue()
    .postCss('resources/css/app.css', 'public/css', []).version();

すると、
<link rel="stylesheet" href="{{mix('css/app.css')}}" />
<script src="{{mix('js/app.js')}}"></script>

<link rel="stylesheet" href="/css/app.css?id=709141171a3f4976580d" />
<script src="/js/app.js?id=184b0a397007d2da2e91"></script>
になって、スーパーリロードする必要がなくなる。

 

 

docker-composeでLaravel環境構築(PHP/nginx/MySQL)

これを参考に

【超入門】20分でLaravel開発環境を爆速構築するDockerハンズオン

https://qiita.com/ucan-lab/items/56c9dc3cf2e6762672f4

ディレクトリ・ファイルを作成

ディレクトリ構成

laravel_docker/
  docker-compose.yml
  docker/
    php/
      Dockerfile
      php.ini
    nginx/
      default.conf
    mysql/
      Dockerfile
      my.cnf
  laravel/

ディレクトリ作成

mkdir docker
mkdir docker/php
mkdir docker/nginx
mkdir docker/mysql
mkdir laravel

vi docker-compose.yml

version: "3.9"
services:
  app:
    build: ./docker/php
    volumes:
      - ./laravel:/work
  web:
    image: nginx:1.20-alpine
    ports:
      - 8054:80
    volumes:
      - ./laravel:/work
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    working_dir: /work
  db:
    build: ./docker/mysql
    volumes:
      - db-store:/var/lib/mysql

volumes:
  db-store:

vi docker/php/Dockerfile

FROM php:8.0-fpm-buster
SHELL ["/bin/bash", "-oeux", "pipefail", "-c"]

#プロキシ設定が必要な場合
#ENV http_proxy http://xxx:3128/
#ENV https_proxy http://xxx:3128/

ENV COMPOSER_ALLOW_SUPERUSER=1 \
  COMPOSER_HOME=/composer

COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer

RUN apt-get update && \
  apt-get -y install git unzip libzip-dev libicu-dev libonig-dev && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/* && \
  docker-php-ext-install intl pdo_mysql zip bcmath

COPY ./php.ini /usr/local/etc/php/php.ini

WORKDIR /work

vi docker/php/php.ini

zend.exception_ignore_args = off
expose_php = on
max_execution_time = 30
max_input_vars = 1000
upload_max_filesize = 64M
post_max_size = 128M
memory_limit = 256M
error_reporting = E_ALL
display_errors = on
display_startup_errors = on
log_errors = on
error_log = /dev/stderr
default_charset = UTF-8

[Date]
date.timezone = Asia/Tokyo

[mysqlnd]
mysqlnd.collect_memory_statistics = on

[Assertion]
zend.assertions = 1

[mbstring]
mbstring.language = Japanese

vi docker/nginx/default.conf

server {
    listen 80;
    server_name example.com;
    root /work/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass app:9000;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

vi docker/mysql/Dockerfile

FROM mysql/mysql-server:8.0

ENV MYSQL_DATABASE=laravel_local \
  MYSQL_USER=phper \
  MYSQL_PASSWORD=secret \
  MYSQL_ROOT_PASSWORD=secret \
  TZ=Asia/Tokyo

COPY ./my.cnf /etc/my.cnf
RUN chmod 644 /etc/my.cnf

vi docker/mysql/my.cnf

[mysqld]
# default
skip-host-cache
skip-name-resolve
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
secure-file-priv=/var/lib/mysql-files
user=mysql

pid-file=/var/run/mysqld/mysqld.pid

# character set / collation
character_set_server = utf8mb4
collation_server = utf8mb4_ja_0900_as_cs_ks

# timezone
default-time-zone = SYSTEM
log_timestamps = SYSTEM

# Error Log
log-error = mysql-error.log

# Slow Query Log
slow_query_log = 1
slow_query_log_file = mysql-slow.log
long_query_time = 1.0
log_queries_not_using_indexes = 0

# General Log
general_log = 1
general_log_file = mysql-general.log

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4

環境構築

docker-compose up -d --build
docker exec -it laravel_docker-app-1 composer create-project laravel/laravel .

ブラウザからアクセスしてトップページが表示できる。

 

.envのDB設定を書き換え

vi laravel/.env

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel_local
DB_USERNAME=phper
DB_PASSWORD=secret

マイグレーション

docker exec -it laravel_docker-app-1 php artisan migrate

環境を破棄するには

docker-compose down --rmi all --volumes --remove-orphans

 

 

Vue.js axiosを使ったサンプル

配列を更新するとき、

app.gachaResult = response.data

では表示が更新されなくて、

app.gachaResult.splice(0, app.gachaResult.length, ...response.data)

配列のメソッドで更新しないとリアクティブに更新されない。

 

<html>
<head>
<meta charset="utf-8"/>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vue-router"></script>
<script src="https://unpkg.com/vuex"></script>
<script src="https://unpkg.com/axios"></script>
<style>
#title {
  background-color:#1ce;
}
</style>
</head>
<body>
<div id="app">
  <div id="title"><a href="/game.php">GAME</a></div>
  <user-info v-bind:user="user"></user-info>
  <p>
    <button class="btn btn-primary" v-on:click="viewGacha">gacha</button>
    <button class="btn btn-primary" v-on:click="viewChara">chara</button>
    <button class="btn btn-primary" v-on:click="viewItem">item</button>
  </p>
  <router-view></router-view>
  <hr>
  <div><button class="btn btn-danger" v-on:click="reset">reset</button></div>
</div>
<script>
const UserInfo = {
  props: ['user'],
  template: `<div>{{user.id}}:{{user.name}} Lv.{{user.level}}</div>`
}
Vue.component('user-info', UserInfo)
const Gacha = {
  props: ['gachaResult'],
  template: `
    <div>
      <p>ガチャ</p>
      <button class="btn btn-primary" v-on:click="executeGacha(1)">1回</button>
      <button class="btn btn-primary" v-on:click="executeGacha(3)">3回</button>
      <div v-for="result in gachaResult">
        {{result.name}} Lv.{{result.level}}
      </div>
    </div>
  `,
  methods: {
    executeGacha(count) {
      axios.get('/api/execute_gacha.php?count='+count).then(response => {
        app.gachaResult.splice(0, app.gachaResult.length, ...response.data)
        app.charas.push(...response.data)
      })
    }
  }
}
const Chara = {
  props: ['charas'],
  template: `
    <div>
      <p>キャラ</p>
      <div v-for="chara in charas">
        {{chara.name}} Lv.{{chara.level}}
        <router-link :to="{name: 'chara-detail', params: {name: chara.name}}" class="btn btn-primary">detail</router-link>
      </div>
    </div>
  `
}
const CharaDetail = {
  props: ['name'],
  template: `
    <div>
      <p>キャラ詳細:{{name}}</p>
      <router-link :to="{name: 'chara-profile', params: {name: name}}" class="btn btn-primary">profile</router-link>
      <router-link :to="{name: 'chara-status', params: {name: name}}" class="btn btn-primary">status</router-link>
      <router-view></router-view>
    </div>
  `
}
const CharaProfile = {template: '<p>Profile</p>'};
const CharaStatus = {template: '<p>Status</p>'};
const Item = {
  template: `
    <div>
      <p>アイテム</p>
      <button class="btn btn-primary" v-on:click="levelUp">レベルの実</button>
    </div>
  `,
  methods: {
    levelUp() {
      axios.get('/api/use_item.php').then(response => (app.user = response.data))
      //app.user.level++
    }
  }
}
const NotFound = { template: '<div>404</div>' }

const router = new VueRouter({
  routes: [
    { path: '/gacha', name: '/gacha', component: Gacha, props: true },
    { path: '/chara', name: '/chara', component: Chara, props: true, },
    { path: '/chara/:name',
      name: 'chara-detail',
      component: CharaDetail,
      props: true,
      children: [
        {
          path: 'profile',
          name: 'chara-profile',
          component: CharaProfile
        },
        {
          path: 'status',
          name: 'chara-status',
          component: CharaStatus
        },
      ]
    },
    { path: '/item', component: Item },
    //{ path: '/*', component: NotFound }
  ]
})
const data = {
  user: {id: 0, name: '', level: 1},
  charas: ,
  gachaResult:

}
const app = new Vue({
  router,
  data,
  mounted () {
    axios.get('/api/get_user.php').then(response => (app.user = response.data)),
    axios.get('/api/get_charas.php').then(response => (app.charas = response.data))
  },
  methods: {
    viewGacha() {router.push({name: '/gacha', params: {gachaResult: app.gachaResult}})},
    viewChara() {router.push({name: '/chara', params: {charas: app.charas}})},
    viewItem() {router.push('/item')},
    reset() {
      axios.get('/api/reset.php').then(response => {
        app.user = response.data.user
        app.charas.splice(0, app.charas.length, ...response.data.charas)
      })
    }
  }
}).$mount('#app')
</script>
</body>
</html>

VueRouterチュートリアル

https://router.vuejs.org/ja/guide/

 

VueRouterの「基本的な使い方」を見ながらサンプルをつくってみた。

 

<html>
<head>
<meta charset="utf-8"/>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<style>
#title {
  background-color:#1ce;
}
</style>
</head>
<body>
<div id="app">
  <div id="title"><a href="/vrouter.html">GAME</a></div>
  <p>
    <router-link to="/gacha" class="btn btn-primary">gacha</router-link>
    <router-link to="/chara" class="btn btn-primary">chara</router-link>
    <button class="btn btn-primary" v-on:click="viewItem">item</button>
    <router-link to="/goods" class="btn btn-primary">goods</router-link>
    <router-link to="/tool" class="btn btn-primary">tool</router-link>
    <router-link to="/xxx" class="btn btn-primary">xxx</router-link>
  </p>
  <router-view></router-view>
</div>
<script>
const Gacha = { template: '<div>ガチャ</div>' }
const Chara = {
  props: ['charas'],
  template: `
    <div>
      <p>キャラ</p>
      <div v-for="chara in charas">
        {{chara.name}} Lv.{{chara.level}}
        <router-link :to="{name: 'chara-detail', params: {name: chara.name}}" class="btn btn-primary">detail</router-link>
      </div>
    </div>
  `
}
const CharaDetail = {
  props: ['name'],
  template: `
    <div>
      <p>キャラ詳細:{{name}}</p>
      <router-link :to="{name: 'chara-profile', params: {name: name}}" class="btn btn-primary">profile</router-link>
      <router-link :to="{name: 'chara-status', params: {name: name}}" class="btn btn-primary">status</router-link>
      <router-view></router-view>
    </div>
  `
}
const CharaProfile = {template: '<p>Profile</p>'};
const CharaStatus = {template: '<p>Status</p>'};
const Item = { template: '<div>アイテム</div>' }
const NotFound = { template: '<div>404</div>' }

const router = new VueRouter({
  routes: [
    { path: '/gacha', component: Gacha },
    { path: '/chara',
      component: Chara,
      props: {
        charas: [
          {name: 'ドッグ', level: 3},
          {name: 'モンキー', level: 5},
          {name: 'バード', level: 4}
        ]
      }
    },
    { path: '/chara/:name',
      name: 'chara-detail',
      component: CharaDetail,
      props: true,
      children: [
        {
          path: 'profile',
          name: 'chara-profile',
          component: CharaProfile
        },
        {
          path: 'status',
          name: 'chara-status',
          component: CharaStatus
        },
      ]
    },
    { path: '/item', component: Item, alias: '/goods' },
    { path: '/tool', redirect: '/item' },
    { path: '/*', component: NotFound }
  ]
})
const app = new Vue({
  router,
  methods: {
    viewItem: function() {
      router.push('/item')
    }
  }
}).$mount('#app')
</script>
</body>
</html>

Vue.jsチュートリアル

https://jp.vuejs.org/v2/guide/index.html

 

Vue.jsの「はじめに」を見ながらサンプルをつくってみた。
v-ifとv-on:clickで表示切替、
キャラ一覧はv-forとcomponentの2パターンで試してみた。

 

<html>
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<style>
#title {
  background-color:#1ce;
}
</style>
</head>
<body>
  <div id="app">
    <div id="title"><a href="/vue.php">{{message}}</a></div>
    <button class="btn btn-primary" v-on:click="showGacha">gacha</button>
    <button class="btn btn-primary" v-on:click="showChara">chara</button>
    <button class="btn btn-primary" v-on:click="showItem">item</button>
    <div v-if="isGacha">
      <p>ガチャ</p>
    </div>
    <div v-if="isChara">
      <p>キャラ</p>
      <div v-for="chara in charas">
        {{chara.name}} Lv.{{chara.level}}
      </div>
      <hr>
      <chara-detail v-for="chara in charas"
        v-bind:chara="chara"></chara-detail>
      <button class="btn btn-primary" v-on:click="addChara">add</button>
    </div>
    <div v-if="isItem">
      <p>アイテム</p>
    </div>
  </div>
<script>
Vue.component('chara-detail', {
  props: ['chara'],
  template: `
    <div>{{chara.name}} Lv.{{chara.level}}</div>
  `
});
var app = new Vue({
  el: '#app',
  data: {
    message: 'GAME',
    isGacha: false,
    isChara: false,
    isItem: false,
    charas: [
      {name: 'ドッグ', level: 3},
      {name: 'モンキー', level: 5},
      {name: 'バード', level: 4}
    ],
  },
  methods: {
    addChara: function() {
      app.charas.push({name: 'タイガー', level: 22});
    },
    hideAll: function() {
      app.isGacha = false;
      app.isChara = false;
      app.isItem = false;
    },
    showGacha: function() {
      app.hideAll();
      app.isGacha = true;
    },
    showChara: function() {
      app.hideAll();
      app.isChara = true;
    },
    showItem: function() {
      app.hideAll();
      app.isItem = true;
    },
  }
});
app.isGacha = true;
</script>
</body>
</html>