2017/04/29에 내용을 수정했습니다.
- version() API는 적용하지 않았습니다.
- 빌드 레시피를 변경했습니다.
변경 전에 이 문서를 따라하셨던 분이라면, 아래 두 개의 파일의 변경 사항을 반영해 주세요.
resources/views/layouts/app.blade.php
webpack.mix.js
라라벨 5.4부터 "Elixir"라는 이름이 컴퓨터 랭귀지 "Elixir"와 헷갈린다는 이유로 "Mix"로 이름이 변경되고, 빌드 시스템도 gulp에서 webpack으로 완전히 변경되었습니다. 라라벨 5.4로 학습하시는 분들은 다음 절차대로 시도해 보시기 바랍니다.
프로젝트를 처음 시작해서 package.json
에 아무것도 없는 상태로 가정합니다. 저도 이렇게 해서 테스트했습니다.
~ $ laravel new laravel-mix-test && cd laravel-mix-test
# ...
~/laravel-mix-test $ php artisan --version
# Laravel Framework 5.4.19
Node.js 패키지를 설치하고, 프런트엔드 에셋 빌드가 잘 작동하는 지 확인합니다.
~/laravel-mix-test $ npm install
# ...
~/laravel-mix-test $ npm run dev
# ...
책에서 사용한 Node.js 패키지들을 설치합니다. vue
와 vue-resource
는 버전을 지정해야 합니다.
~/laravel-mix-test $ npm install autosize bootstrap-sass dropzone \
font-awesome highlightjs jquery-tabby marked select2 select2-bootstrap-css \
[email protected] [email protected] --save-dev
# ...
설치 후에 package.json
파일에 설치한 버전이 잘 기록되었나 확인합니다.
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"autosize": "^3.0.20",
"axios": "^0.15.3",
"bootstrap-sass": "^3.3.7",
"cross-env": "^3.2.3",
"dropzone": "^4.3.0",
"font-awesome": "^4.7.0",
"highlightjs": "^9.10.0",
"jquery": "^3.1.1",
"jquery-tabby": "^0.13.1",
"laravel-mix": "0.*",
"lodash": "^4.17.4",
"marked": "^0.3.6",
"select2": "^4.0.3",
"select2-bootstrap-css": "^1.4.6",
"vue": "^1.0.26",
"vue-resource": "^0.9.3"
}
}
webpack.mix.js
파일을 수정합니다.
const { mix } = require('laravel-mix');
mix.sass('resources/assets/sass/app.scss', 'public/css');
mix
.scripts('node_modules/highlightjs/highlight.pack.js', 'public/js/temp.js')
.js('resources/assets/js/app.js', 'public/js')
.scripts([
'node_modules/highlightjs/highlight.pack.js',
'public/js/app.js',
'node_modules/select2/dist/js/select2.js',
'node_modules/dropzone/dist/dropzone.js',
'node_modules/marked/lib/marked.js',
'node_modules/jquery-tabby/jquery.textarea.js',
'node_modules/autosize/dist/autosize.js',
'resources/assets/js/forum.js'
], 'public/js/app.js');
mix.copy('node_modules/font-awesome/fonts', 'public/fonts');
mix.version()
API를 이용한 캐시 버스팅을 이용하지 않으므로, 마스터 레이아웃은 다음과 같은 모양이어야 합니다.
resources/views/layouts/app.blade.php
파일을 수정합니다.
-<link href="{{ elixir('css/app.css') }}" rel="stylesheet">
+<link href="/css/app.css" rel="stylesheet">
-<div class="container">
+<div class="container" id="app">
<!--//...--></div>
-<script src="{{ elixir('js/app.js') }}"></script>
+<script src="/js/app.js"></script>
resources/assets/js/app.js
파일을 수정합니다.
require('./bootstrap');
window.Vue = require('vue');
// Vue.component('example', require('./components/Example.vue'));
const app = new Vue({
el: '#app',
ready() {
hljs.initHighlightingOnLoad();
this.removeFlashMessages();
this.setJqueryAjaxHeaders();
this.initBackToTopButton();
},
methods: {
removeFlashMessages() {
if ($('.alert')) {
$('.alert').delay(5000).fadeOut();
}
},
setJqueryAjaxHeaders() {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
},
initBackToTopButton() {
$('#back-to-top').on('click', function () {
$('body,html').animate({
scrollTop: 0
}, 800);
return false;
});
$(window).on('scroll', function () {
var scrollPos = $(window).scrollTop();
if (scrollPos > 50) {
$('#back-to-top').fadeIn();
} else {
$('#back-to-top').fadeOut();
}
});
}
}
});
resources/assets/sass/app.scss
파일을 수정합니다.
// Fonts
@import url(https://fonts.googleapis.com/css?family=Raleway:300,400,600);
// Variables
@import "variables";
// Bootstrap
@import "node_modules/bootstrap-sass/assets/stylesheets/bootstrap";
// FontAwesome
@import "node_modules/font-awesome/scss/font-awesome";
// Select2
@import 'node_modules/select2/src/scss/core';
.select2-dropdown,
.select2-container--default .select2-selection--multiple,
.select2-container--default.select2-container--focus .select2-selection--multiple {
border: 1px solid $input-border;
border-radius: 4px;
}
.select2-container,
.select2-container--default .select2-selection--multiple {
font-size: $font-size-base;
}
// Dropzone
@import 'node_modules/dropzone/src/dropzone';
.dropzone {
border: 1px solid $input-border;
border-radius: 4px;
}
.dropzone {
display: none;
}
label[for=my-dropzone]>small {
cursor: pointer;
}
.preview__content {
display: none;
margin-top: $font-size-base * 1.6;
height: auto;
@extend .form-control;
box-shadow: none;
}
// Application Layouts
.container__article{
padding: 1em 0;
}
.action__article {
margin-top: 1em;
}
.footer__master {
margin-top: 1em;
a {
display: inline-block;
color: $text-color;
text-decoration: none;
padding: 0 5px;
}
ul {
li {
padding: 0;
&:first-child {
margin-right: 5px;
}
}
li:not(:first-child) {
a:hover,
a:active,
a:focus {
border-radius: 3px;
background-color: darken($body-bg, 5%);
transition: all 0.3s ease;
}
&.active > a {
border-radius: 3px;
background-color: $body-bg;
}
}
}
}
.back-to-top {
cursor: pointer;
position: fixed;
bottom: 5px;
right: 5px;
display: none;
z-index: 9998;
transition: all 0.5s ease-in-out;
padding: 8px 10px;
}
// Sidebar
.sidebar__article,
.tags__article,
.attachments__article {
a {
color: $text-color;
text-decoration: none;
}
}
.sidebar__article {
ul {
list-style: none;
margin: 0;
padding: 0;
}
li {
a {
padding: 0.5rem;
display: block;
}
a:hover,
a:active,
a:focus {
border-radius: 3px;
background-color: darken($body-bg, 5%);
transition: all 0.3s ease;
}
&.active>a {
border-radius: 3px;
background-color: darken($body-bg, 10%);
}
}
.badge {
float: right;
margin-right: 1rem;
}
form {
margin-bottom: 30px;
}
}
// Article
.content__article {
margin: 2em;
img {
display: block;
height: auto;
max-width: 100%;
margin: 0 auto;
transition: all .2s ease-in-out
}
}
// Tags Line, Attachments Line
.tags__article,
.attachments__article {
list-style: none;
margin: 0;
padding: 0;
font-weight: 100;
font-size: $font-size-base * 0.8;
li {
display: inline-block;
}
li:first-child {
margin-right: 10px;
}
li:not(:first-child) {
padding: 4px 8px;
background-color: darken($body-bg, 5%);
border-radius: 3px;
&:hover {
background-color: darken($body-bg, 10%);
}
}
}
// Comments
.link__login__comment {
padding: 1em;
margin-bottom: 1em;
border-radius: 3px;
border: 1px solid $input-border;
width: 100%;
}
.item__comment.top {
padding: 1rem;
border-radius: 4px;
position: relative;
overflow: visible;
float: right;
width: 100%;
border: 1px solid $input-border;
}
.media__edit__comment,
.media__create__comment.sub {
display: none;
}
.form__new__comment,
.list__comment {
.form-group {
width: 100%;
margin: auto;
}
div.text-right {
margin: 10px auto;
}
}
.content__comment {
p {
margin: 0;
}
}
.action__comment {
button, span {
font-size: $font-size-base * 0.8;
font-weight: 400;
text-decoration: none;
}
button {
border: none;
background: none;
margin: 0;
padding: 0;
}
button:hover,
button:active,
button:focus {
transition: all .2s ease-in-out
}
&>span {
margin: 0 5px;
}
}
// Form
.form-control {
box-shadow: none;
}
.form__auth {
max-width: 400px;
width: 100%;
padding: 15px;
margin: 0 auto;
.checkbox {
margin-bottom: 1rem;
}
.checkbox {
font-weight: normal;
}
.form-control {
position: relative;
height: auto;
box-sizing: border-box;
padding: 0.8rem;
}
.form-control:focus {
z-index: 2;
border-color: $input-border;
box-shadow: none;
}
input {
margin-bottom: 10px;
}
.login-or {
position: relative;
margin-top: 20px;
margin-bottom: 20px;
padding-top: 15px;
padding-bottom: 15px;
}
.span-or {
display: block;
position: absolute;
left: 50%;
top: -1px;
margin-left: -25px;
background-color: $body-bg;
width: 50px;
text-align: center;
}
.hr-or {
margin-top: 0px !important;
margin-bottom: 0px !important;
}
.fa-github {
margin-right: 10px;
}
}
span.form-error {
margin-top: 5px;
display: block;
font-size: 0.8em;
color: $brand_danger;
font-style: italic;
font-weight: 100
}
// Alert, Flash Messages
.alert {
display: inline-block;
position: fixed;
bottom: 50px;
right: 15px;
max-width: 450px;
opacity: .8;
z-index: 999;
}
// Markdown Viewer
.docs__sidebar {
margin-top: 20px;
}
#app-layout {
pre {
background: #000000;
border-radius: 0;
}
// 이하 데일 리즈의 칼라 스킴에서 복사한 스타일시트
.hljs {
display: block;
padding: 0.5em;
background: #000000;
color: #BABABA;
}
.hljs-comment,
.hljs-template_comment,
.hljs-javadoc {
color: #494B4D;
}
.hljs-keyword,
.ruby .hljs-function .hljs-keyword,
.hljs-request,
.hljs-status,
.nginx .hljs-title {
color: #F08D24;
}
.hljs-function .hljs-keyword,
.hljs-sub .hljs-keyword,
.method,
.hljs-list .hljs-title {
color: #68C244;
}
.hljs-string,
.hljs-tag .hljs-value,
.hljs-cdata,
.hljs-filter .hljs-argument,
.hljs-attr_selector,
.apache .hljs-cbracket,
.hljs-date,
.tex .hljs-command,
.coffeescript .hljs-attribute {
color: #F2D42C;
}
.hljs-subst {
color: #DAEFA3;
}
.hljs-regexp {
color: #E9C062;
}
.hljs-title,
.hljs-sub .hljs-identifier,
.hljs-pi,
.hljs-tag,
.hljs-tag .hljs-keyword,
.hljs-decorator,
.hljs-shebang,
.hljs-prompt {
color: #E8341C;
}
.hljs-symbol,
.ruby .hljs-symbol .hljs-string,
.hljs-number {
color: #8E69C9;
}
.hljs-params,
.hljs-variable,
.clojure .hljs-attribute {
color: #1CC3E8;
}
.css .hljs-tag,
.hljs-rules .hljs-property,
.hljs-pseudo,
.tex .hljs-special {
color: #E8341C;
}
.css .hljs-class {
color: #1CC3E8;
}
.hljs-rules .hljs-keyword {
color: #1CC3E8;
}
.hljs-rules .hljs-value {
color: #E8341C;
}
.css .hljs-id {
color: #8B98AB;
}
.hljs-annotation,
.apache .hljs-sqbracket,
.nginx .hljs-built_in {
color: #9B859D;
}
.hljs-preprocessor,
.hljs-pragma {
color: #8996A8;
}
.hljs-hexcolor,
.css .hljs-value .hljs-number {
color: #DD7B3B;
}
.css .hljs-function {
color: #DAD085;
}
.diff .hljs-header,
.hljs-chunk,
.tex .hljs-formula {
background-color: #0E2231;
color: #F8F8F8;
font-style: italic;
}
.diff .hljs-change {
background-color: #4A410D;
color: #F8F8F8;
}
.hljs-addition {
background-color: #253B22;
color: #F8F8F8;
}
.hljs-deletion {
background-color: #420E09;
color: #F8F8F8;
}
.coffeescript .javascript,
.javascript .xml,
.tex .hljs-formula,
.xml .javascript,
.xml .vbscript,
.xml .css,
.xml .hljs-cdata {
opacity: 0.5;
}
// 이상 데일 리즈의 칼라 스킴에서 복사한 스타일시트
}
// 미디어 쿼리
@media screen and (max-width: 991px) {
.action__article {
margin-bottom: 1em;
}
.sidebar__article {
margin-bottom: 2em;
}
.item__comment {
&>a.pull-left {
display: none;
}
}
}
@media screen and (max-width: 767px) {
}
@media screen and (max-width: 479px) {
}
resources/assets/js/forum.js
파일을 다운로드 합니다.
~/laravel-mix-test $ wget https://raw.githubusercontent.com/appkr/l5code/master/resources/assets/js/forum.js -O resources/assets/js/forum.js
에셋 빌드를 수행합니다.
~/laravel-mix-test $ npm run dev
![laravel-mix](https://cloud.githubusercontent.com/assets/6471947/24951358/14dfd1d2-1faf-11e7-9334-43de96f73066.png)
![mix](https://cloud.githubusercontent.com/assets/6471947/25555413/330638ca-2d21-11e7-8b96-c1c06658e401.png)