Edited at

Vue.jsでSPAサイトを作成するチュートリアル【4. CSS編】

More than 1 year has passed since last update.

Vue.jsを使い始めていろいろできることが多くなってきたので、整理する意味も兼ねてチュートリアルにまとめます。

今回はコーポレートサイトを想定して作成していきます。

※記事が長くなったのでチュートリアルを分割しました。


目次


前提


  • タスクはnpm scriptsで一限管理

  • コマンドはyarnを使用


  • vue-cliwebpack-simpleを使用

  • CSSはSCSSを使用し用途に合わせてPostCSSを使用


バージョン


  • "vue": "^2.5.11"

  • "webpack": "^3.6.0"

  • "node-sass": "^4.7.2"

  • "postcss": "^6.0.16"

  • "stylelint": "^8.4.0"


CSSの環境構築

SassとPostCSSを使用するので以下のコマンドでパッケージを追加します。

※今回はautoprefixerでPostcSSの動作確認をおこないます。

$ yarn add -D node-sass postcss postcss-loader autoprefixer

PostCSSの設定ファイルを追加します。


postcss.config.js

module.exports = (ctx) => ({

plugins: [
require('autoprefixer')
]
});


webpackの設定ファイルを修正します。


webpack.config.js

var path = require('path')

var webpack = require('webpack')

module.exports = {
// 省略
module: {
rules: [
// 省略
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
'scss': [
'vue-style-loader',
'css-loader',
'postcss-loader', // ← 追加
'sass-loader'
],
// 省略


Index.scssとAbout.scssで動作確認用のスタイルを記述します。


/src/pages/Index/Index.scss

.block {

color: red;
display: flex;
}



/src/pages/About/About.scss

.block {

color: blue;
display: flex;
}


ブラウザでindexとaboutをそれぞれ確認し、開発ツールでdisplayがプレフィックスがついていて、それぞれ赤文字、青文字になっていたら成功です。

ちなみに、前回vueファイル側でstyle部分にscopedを記述していました。


/src/pages/Index/Index.vue

<template

lang="pug"
src="./Index.pug"
/>

<script src="./Index.js" />

<style
lang="scss"
src="./Index.scss"
scoped
/>


ページ単位では上書きされないようにローカルなスタイルにする意味でscopedを記述していました。

上書きさせたい場合、scopedを記述しなければ通常の記述になります。

参考:スコープ付き CSS


グローバルなCSSの環境構築

上記のCSSの設定はvueコンポーネントごとの個別用で使用し、全体で使用するスタイルは別で用意します。

※reset.css, normarize.css, 各共通コンポーネント等です。

CSSディレクトリを作成し、CSS設計をおこないます(以下は参考)

/src

/css
/base
_variable.scss
_mixin.scss
_function.scss
_base.scss
/components
_typography.scss
_button.scss
_icon.scss
_form.scss
_table.scss
/modules
_header.scss
_footer.scss
/layouts
_container.scss
/templates
_temp-list.scss
_temp-article.scss
/vendor
_normarize.scss
style.sscss
/layouts
Default.vue
/pages
Index.vue
Hoge.vue
App.vue
main.js
router.js
index.html
その他設定ファイル等

cssファイルを読み込む記述を追加します。


/src/index.html

<!DOCTYPE html>

<html lang="en">
<head>
<meta charset="utf-8">
<title>vue-skeleton</title>
<link rel="stylesheet" href="/css/style.css"> <!-- ← 追加 -->
</head>
<body>
<div id="app"></div>
<script src="/js/bundle.js"></script>
</body>
</html>

normalize.scssをダウンロードして以下に格納します。

/src/css/vendor/

グローバルなSCSSはwebpackとは別で監視したほうがwebpackに依存しなくて良いので、node-sassを使って監視します。


package.json

{

// 省略
"scripts": {
"watch:server": "browser-sync start --config bs-config.js",
"watch:file": "cpx \"./src/**/{*.html,*.jpg,*.png,*.gif,*.svg,*.eot,*.ttf,*.woff,package.json}\" ./dist",
// ↓ 追加
"watch:css": "node-sass src/css/style.scss dist/css/style.css -w --source-map true",
// ↑ 追加
"watch:js": "webpack -w",
"start": "run-p watch:*",
// 省略
},
// 省略
}


vue-body-classの追加

CSS設計をおこなう際に、ページごとにスタイルをテンプレートとして出し分けたい場合があります。

そんな時にbodyにページごとのclassを振って出し分けるのですが、vue-routerはrouter-view部分の差分変更をおこなうので、基本的にはbodyの操作をしないようになっていると思います。

そんな時にrouter-viewでpathの変更ごとにbodyのclass設定をおこなえるようにしたのがvue-body-classです。

さっそく追加しましょう。

$ yarn add vue-body-class

main.jsに追記しましょう。

この時、Vue.use(BodyClass, router)const router = ...の後に記述しないときちんと反応しないので気をつけましょう。


/src/main.js

import Vue from 'vue'

import App from './App.vue'
import VueRouter from 'vue-router'
import Routes from './router.js'
import BodyClass from 'vue-body-class' // ← 追記

Vue.use(VueRouter)

const router = new VueRouter({
mode: 'history',
routes: Routes
})

Vue.use(BodyClass, router) // ← 追記

const app = new Vue({
router,
render:h => h(App)
}).$mount('#app');


次にrouter.jsに実際に各ページに付与するclass名を追記していきましょう。


/src/router.js

export default [

{
path: '/',
name: '',
component: require('./layouts/Default.vue').default,
children: [
{
path: '',
name: 'index',
meta: { bodyClass: 'page-index' }, // ← 追記
component: require('./pages/Index/Index.vue').default,
},
{
path: '/about/',
name: 'about',
meta: { bodyClass: 'page-about' }, // ← 追記
component: require('./pages/About/About.vue').default
},
{
path: '/service/',
name: 'service',
meta: { bodyClass: 'page-service' }, // ← 追記
component: require('./pages/Service/Service.vue').default
},
{
path: '/recruite/',
name: 'recruite',
meta: { bodyClass: 'page-recruite' }, // ← 追記
component: require('./pages/Recruite/Recruite.vue').default
},
{
path: '/contact/',
name: 'contact',
meta: { bodyClass: 'page-contact' }, // ← 追記
component: require('./pages/Contact/Contact.vue').default
}
]
}
]


ブラウザで確認してページごとにbodyのclassが変更されていれば成功です。

ちなみに、親で付与したclassを子に引き継いだり上書きしたり、ということもできます。

詳しくは公式ドキュメントを参考にしてください。

参考:vue-body-class


stylelintの設定

グローバルなCSSとwebpack環境下のCSS両方にstylelintを設定します。

パッケージを追加します。

yarn add -D stylelint stylelint-webpack-plugin

stylelintの設定ファイルを用意します。

今回は以下を/srcに格納します。

stylelintの設定ファイル

エディタで監視する設定をおこないます。

以下を参考におこなってみてください。

PostCSSとstylelintの環境構築


グローバルなCSS

他所から持ってきたSCSSファイルにstylelintを反応させたくないので、.stylelintignoreを/src直下に作成して以下を記述します。

/src/css/vendor/**/*.scss

/src/cssのscssファイルでエラーや警告表示になる記述をして、stylelintがエディタで反応したら成功です。


webpack環境下のCSS

webpackの設定ファイルに追記します。


/src/webpack.config.js

// 省略

var StylelintPlugin = require('stylelint-webpack-plugin')

module.exports = {
// 省略
plugins: [
new StylelintPlugin({
files: ['**/*.vue', '**/*.scss']
})
],
// 省略
}


参考:A nice way to lint .vue files with Stylelint? #303

Index.vueやAbout.scssでstylelintがエディタで反応していたら成功です。

エラー・警告が出ている場所を確認して修正しましょう。


postcss-sortingの追加

プロパティ順をソートできるようにしましょう。

以下の記事を参考に導入しましょう。scssファイルでもいけます。

参考:PostCSS SortingでCSSの@ルールやプロパティの記述順を整理


まとめ

CSSは設定しやすくハンドリングが難しいので、設計がとても大事です。

scopedはどれに適用させるのか、共通コンポーネントのスタイルはどこに書くべきか、等の判断も必要になってくるかと思います。

全体を俯瞰し、破綻しにくく運用しやすい設計を意識すると良いでしょう。