Help us understand the problem. What is going on with this article?

Nuxtベースで静的・Wordpress共用の開発環境を整えました

19/06/13 追記

その後、実環境で使用していくつか改善したので、編集加えました :raising_hand:
そして大したものでもないのですが、せっかくなのでgitでファイル公開しました

https://github.com/webiscuit/Boilerplate-Nuxt-Wordpress

20/01/24 追記

↓REST APIなどを使用しないライトな構造です。
なんだかんだ受託案件などで急にREST API導入は難しい場合にご参考になれば

こんにちは、はじめまして:relaxed:
フリーランスでフロントエンドデベロッパーをしています。
普段は静的サイトのHTML,CSS,JavaScriptコーディング、WordPressテーマの開発なんかやっていることが多いです。

Qiitaは以前から使ってたのですが見る専でしたが
最近、開発環境を見直して整え直したので、せっかくなので備忘録も兼ねてシェアさせていただきます✨

ゴリゴリクリエイティブなことをしているというよりは
黙々とプロジェクトのコードと遊んでいる地味~な環境ですが、もし少しでも需要があれば幸いです:relaxed:

まず結論から。

  • 静的サイトのコンパイルおよびもろもろ: Nuxt.js
  • モジュールバンドラー(WordPress用): Webpack
  • WordPressローカル環境: Local by Flywheel

静的サイトの場合もWordpressテーマの場合も、
まずは静的ファイルとしてコーディングすることが多いので
一つのプロジェクト内でNuxtとWordpressを共存させました。

ひとつの環境で両方をカバーしようと思ったのは、単にめんどくさかったからです()
あとWordpress用にwebpackを併用したのは、やっぱりめんどくさかったからです…()

今まではどうしていたか

以前はgulpを使用していましたが、数年前にwebpackメインに切り替えていました。
こんな感じ。

  • タスクランナー: npm script
  • モジュールバンドラー: Webpack
  • CSS: PostCSS(プラグインなどで必要な場合のみ Sass を併用)
  • テンプレートエンジン: Pug

Wordpressはローカルサーバー内に立ててましたが、いい機会なのでこれも止めていこうと思ってます。

Nuxtを導入した理由と変わったこと

なんとなく、そろそろ見直したいなぁと思ったのがきっかけ。

NuxtはVueを利用しているので、ページごとにHTMLべたうちしたりPugにしたり
外部ファイルももっと簡単にSassやPostCSSを混在出来たりするので
チームで開発する際にいいかなーと思っています。

自分自身が新しいものにも挑戦したくなった時にやりやすいですしね:relaxed:

ディレクトリ構造

では、新環境について記載していきます。
キャプチャ.PNG
Wordpress環境を併用する場合は、Localで作成したディレクトリのapp内に上記を入れます。

各設定ファイル

Nuxtや各種ツールの導入方法はたくさんの記事があるし何より説明が下手なので割愛します…

これから使っていくにつれてアップデートをしていこうとは思っていますが
とりあえず現段階の設定ファイルたちです

package.json

package.json
{
  "name": "boilerplate-nuxt",
  "version": "1.0.0",
  "description": "Boilerplate for static pages",
  "author": "Hiromi Kozai",
  "private": true,
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate",
    "lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
    "precommit": "npm run lint",
    "wp:dev": "webpack -w --env.development",
    "wp:build": "webpack --env.production"
  },
  "babel": {
    "presets": [
      "@babel/preset-env"
    ]
  },
  "dependencies": {
    "@nuxtjs/dotenv": "^1.3.0",
    "bootstrap": "^4.1.3",
    "bootstrap-vue": "^2.0.0-rc.11",
    "cross-env": "^5.2.0",
    "nuxt": "^2.4.0"
  },
  "devDependencies": {
    "@babel/core": "^7.4.4",
    "@babel/preset-env": "^7.4.4",
    "@babel/register": "^7.4.4",
    "@nuxtjs/eslint-config": "^0.0.1",
    "babel-eslint": "^10.0.1",
    "eslint": "^5.15.1",
    "eslint-config-prettier": "^4.1.0",
    "eslint-config-standard": ">=12.0.0",
    "eslint-loader": "^2.1.2",
    "eslint-plugin-import": ">=2.16.0",
    "eslint-plugin-jest": ">=22.3.0",
    "eslint-plugin-node": ">=8.0.1",
    "eslint-plugin-nuxt": ">=0.4.2",
    "eslint-plugin-prettier": "^3.0.1",
    "eslint-plugin-promise": ">=4.0.1",
    "eslint-plugin-standard": ">=4.0.0",
    "eslint-plugin-vue": "^5.2.2",
    "imagemin-mozjpeg": "^8.0.0",
    "imagemin-webpack-plugin": "^2.4.2",
    "jquery": "^3.4.1",
    "mini-css-extract-plugin": "^0.6.0",
    "node-sass": "^4.12.0",
    "nodemon": "^1.18.9",
    "nuxt-imagemin": "^1.0.0",
    "optimize-css-assets-webpack-plugin": "^5.0.1",
    "path": "^0.12.7",
    "postcss-calc": "latest",
    "postcss-cli": "latest",
    "postcss-css-variables": "latest",
    "postcss-cssnext": "^3.1.0",
    "postcss-custom-media": "^7.0.8",
    "postcss-discard-duplicates": "latest",
    "postcss-import": "^12.0.1",
    "postcss-loader": "^3.0.0",
    "postcss-nested": "latest",
    "postcss-preset-env": "latest",
    "postcss-short": "latest",
    "prettier": "^1.16.4",
    "pug": "^2.0.3",
    "pug-plain-loader": "^1.0.0",
    "sass-loader": "^7.1.0",
    "terser-webpack-plugin": "^1.2.4",
    "url-loader": "^1.1.2",
    "vue-scrollto": "^2.15.0",
    "webpack": "^4.31.0",
    "webpack-cli": "^3.3.2"
  }
}

nuxt.config.js

nuxt.config.js
/* -∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-
  Import
-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-*/
import pkg from './package'
import webpack from 'webpack'
import path from 'path';
const imageminMozjpeg = require('imagemin-mozjpeg');



/* -∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-
  Use Global Variables
-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-*/
require('dotenv').config(); // from .env


/* -∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-
  Settings
-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-∵-∴-*/
export default {
  dev: (process.env.NODE_ENV !== 'production'),
  mode: 'universal',

  /*
   ** Headers of the page
   */
  head: {
    htmlAttrs: {
      lang: 'ja',
    },
    bodyAttrs: {
    },
    titleTemplate: '%s|' + process.env.site_name,
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: process.env.viewport },
      { hid: 'description', name: 'description', content: process.env.def_description },
      { hid: 'og:type', property: 'og:type', content: 'website' },
      { hid: 'og:url', property: 'og:url', content: process.env.def_url },
      { hid: 'og:title', content: process.env.site_name },
      { hid: 'og:description', property: 'og:description', content: process.env.def_description },
      { hid: 'og:image', property: 'og:image', content: process.env.def_ogimage },
    ],
    // link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
  },

  /*
   ** Customize the progress-bar color
   */
  loading: { color: '#333' },

  /*
   ** Global CSS
   */
  css: [
    '@/assets/css/bootstrap-settings.scss',
    '@/assets/css/style.css'
  ],

  /*
   ** Plugins to load before mounting the App
   */
  plugins: [
    { src: '@/assets/js/script.js', mode: 'client' },  // Old-style Common JS
    // '~/plugins/vue-scrollto.js' // scroll
  ],

  /*
   ** Nuxt.js modules
   */
  modules: [
    // Doc: https://bootstrap-vue.js.org/docs/
    'bootstrap-vue/nuxt',
    '@nuxtjs/dotenv',
    ["nuxt-imagemin", {
      pngquant: { quality: '80' },
      plugins: [
        imageminMozjpeg( {quality: '80'} )
      ]
    }]
  ],
    bootstrapVue: {
      bootstrapCSS: false,
      bootstrapVueCSS: false
    },

  /*
    ** Source Directory
    */
  srcDir: 'src/',

  /*
   ** Build configuration
   */
  build: {
    // quiet: true,
    fallback: false,
    publicPath: '/assets/',
    devtools: process.env.NODE_ENV === 'production',
    extractCSS: process.env.NODE_ENV === 'production',
    // subFolders: false,
    filenames: {
      app: () => 'js/[name].js',
      chunk: () => 'js/[name].js',
      css: () => 'css/[name].css',
      img: () => 'img/[folder]/[name].[ext]',
      font: () => 'font/[name].[ext]'
    },

    /*
     ** You can extend webpack config here
     */
    extend(config) {
      // // Run ESLint on save
      // if (ctx.isDev && ctx.isClient) {
      //   config.module.rules.push({
      //     enforce: 'pre',
      //     test: /\.(js|vue)$/,
      //     loader: 'eslint-loader',
      //     exclude: /(node_modules)/
      //   })
      // }
    }
  }
}

webpack.config.babel.js

webpack.config.babel.js
import webpack from 'webpack';
import path from 'path';

const env = process.env.NODE_ENV;
const TerserJSPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const ImageminPlugin = require('imagemin-webpack-plugin').default;
const imageminMozjpeg = require('imagemin-mozjpeg');

const srcpath = './src/assets/';
const csspath = './style.css';
const outputpath = './public/wp-content/themes/sample/assets/';

module.exports = env => {
  const mode = (env && env.production) ? 'production' : 'development';

  return {
    mode: mode,
    devtool: (mode == 'production') ? false : 'source-map',
    entry:  path.resolve(__dirname, srcpath + 'index.js'),
    output: {
      path: path.resolve(__dirname, outputpath),
      filename: 'app.js'
    },
    devServer: {
      clientLogLevel  : 'none',
    },
    stats: {
      children: false,
      hash: false,
      warnings: false,
      performance: false,
      modules: false,
    },
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          loader: 'babel-loader',
          query:{
            presets: ['@babel/preset-env']
          }
        },
        {
          test: /\.(png|jpe?g|gif|svg|webp)$/,
          loader: 'url-loader',
          query: {
            limit: 1000, // 1kB
            name: 'img/[name].[ext]'
          }
        },
        {
          test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
          loader: 'url-loader',
          query: {
            limit: 1000, // 1kB
            name: 'fonts/[name].[ext]'
          }
        },
        {
          test: /\.(sa|sc|c)ss$/,
          use: [
            {
            loader: MiniCssExtractPlugin.loader,
            options: {
              hmr: process.env.NODE_ENV === 'development',
            },
          },
            { loader: 'css-loader', options: {
              importLoaders: 1,
              url: true,
              sourceMap: (env && env.production) ? false : true,
            } },
            { loader: 'postcss-loader', options: {
              ident: 'postcss',
              sourceMap:  (env && env.production) ? false : true,
            } },
            { loader: 'sass-loader', options: {
              sourceMap:  (env && env.production) ? false : true,
            } }
          ]
        }
      ]
    },
    resolve: {
      alias: {
        'assets': path.resolve(__dirname, 'src/assets')
      },
      extensions: ['.js', '.jsx']
    },
    optimization: {
      minimizer: [
        new TerserJSPlugin({ extractComments: false }),
        new OptimizeCssAssetsPlugin({
          cssProcessor: require('cssnano'),
          cssProcessorPluginOptions: {
            preset: ['default', { discardComments: { removeAll: true } }],
          }
        }),
      ],
    },
    plugins: [
      new MiniCssExtractPlugin({
        filename: csspath
      }),
      new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery'
      }),
      new ImageminPlugin({
        disable: !env.production,
        test: /\.(jpe?g|png|gif|svg)$/i,
        pngquant: { quality: '80' },
        plugins: [
          imageminMozjpeg( {quality: '80'} )
        ]
      })
    ],
    performance: { hints: false }
  }
};

postcss.config.js

postcssの設定をNuxt内に書かず外に置くのは非推奨で
次バージョンでは対応しないようなのですが、向こうしばらく環境改変の予定はないのでとりあえず。

postcss.config.js
module.exports = {
  plugins: {
    'postcss-import': {},
    'postcss-discard-duplicates': {},
    'postcss-discard-comments': { removeAllButFirst: false },
    'postcss-custom-media': {},
    'postcss-preset-env': {
      stage: 0,
      browsers: "last 2 versions",
      features: {
        'nesting-rules': false
      }
    },
    'postcss-short': {},
    'postcss-calc': {},
    'postcss-nested': {}
  }
}

ページVueファイル

ページはこんな感じ。

index.vue
<template>
</template>

<script>
export default {
  scrollToTop: true,

  data() {
    return {
      bodyClass: 'home', /* bodyに設定するclass */
    }
  },
  head() {
    return {
      title: process.env.site_name, /* ページタイトル(初期値:サイト名) */
      titleTemplate: null,
      meta: [
        { hid: 'description', name: 'description', content: process.env.def_description } /* descriptionを変更するときは"process.env.def_description"を差し替え */
      ],
      bodyAttrs: {
        class: this.bodyClass
      }
    }
  }
}
</script>

<style scoped>
</style>

記載を忘れていました…!
更新版でディレクトリ整理のため、参考にさせていただきました。
https://qiita.com/amishiro/items/9c31a3a9424de27efe28

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした