Laravel5.5 + Vue 開發單頁應用

Flyertutor發表於2018-03-23

上次我用 laravel5.3 + Vue 開發了一個簡單的單頁應用,這次我打算將其升級到 laravel5.5,在升級的過程中,做一下記錄,其原始碼放在 github 上面,原始碼地址

軟體包 版本
Laravel 5.5
Vue > 2.5.7
axios > 0.17
vue-router > 3.0.1
vuex > 3.0.1
# 安裝 laravel
composer create-project --prefer-dist laravel/laravel laravel-vue "5.5.*"

# 安裝前端依賴包
npm install

安裝完之後,為了便於開發,使用熱更新模式。

# 在 webpack.mix.js 中新增配置
mix.browserSync({
  proxy: 'localhost:8000'
});

# 執行 `php artisan serve`
php artisan serve

# 執行 npm run watch
npm run watch

開啟瀏覽器訪問 http://localhost:3000 就可以看到 laravel 的歡迎頁面了

開發三個頁面,首頁,列表,詳情,相對應準備 3 個 api 介面

migration

php artisan make:migration news --create=news
# database/migrations/create_news.php
Schema::create('news', function (Blueprint $table) {
  $table->increments('id');
  $table->string('title');
  $table->text('content');
  $table->integer('is_recommend')->default(0);
  $table->timestamps();
});

model

這裡使用命令生成 model 檔案,編輯對應的檔案內容。

php artisan make:model News
# app/News.php
namespace App;

use Illuminate\Database\Eloquent\Model;

class News extends Model
{
  public $fillable = ['title', 'content'];
}

controller

生成控制器,在控制器中定義三個介面對應的方法。

php artisan make:controller NewsController
# app/Http/Controllers/NewsController
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\News;

class NewsController extends Controller
{
  /**
   * 推薦列表
   */
  public function recommend()
  {
      $list = News::where('is_recommend', 1)->get();
      foreach ($list as $key => $value) {
          $list[$key]->created = $list[$key]->created_at->diffForHumans();
      }
      return $list;
  }

/**
 * 新聞列表
 */
  public function index()
  {
    $list = News::get();
      foreach ($list as $key => $value) {
          $list[$key]->created = $list[$key]->created_at->diffForHumans();
      }
      return $list;
  }

  /**
   * 新聞詳情
   */
  public function show($id)
  {
    $row = News::findOrFail($id);
    return $row;
  }
}

router

定義 api 的介面路由,在 routes/api.php 檔案中定義。

# routes/api.php
Route::get('/news', 'NewsController@index');
Route::get('/news/recommend', 'NewsController@recommend');
Route::get('/news/{id}', 'NewsController@show');

到這裡我們準備工作已經完畢,接下來正式開發 Vue 的單頁應用,在開發單頁應用中,對應 Route Api Vuex Components 這些,下面我們就來定義這些。

首先在首頁引入對應的 app.cssapp.js 檔案。

# resource/vies/welcome.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="csrf-token" content="@{{ csrf_token }}">
  <title>Laravel & Vue</title>
  <link rel="stylesheet" type="text/css" href="/css/app.css">
</head>
<body>
  <div id="app">
    <nav class="navbar navbar-inverse">
      <div class=" container">
         <div class="navbar-header">
            <a class="navbar-brand" href="/">LaravelVue</a>
        </div>
      </div>
    </nav>    
    <div class="container main">
      <router-view />
    </div>
  </div>
  <script type="text/javascript" src="/js/app.js"></script>
</body>
</html>

配置啟動 app.js

應用對應的入口檔案是 app.js,所以在這個入口檔案中,我們例項化 Vue 例項,初始化和載入所需的元件。

# resource/assets/js/app.js
require('./bootstrap');

window.Vue = require('vue');

import VueRouter from 'vue-router';
Vue.use(VueRouter);
import store from './store/'; // vuex 資料儲存所需物件
import routes from './routes';    // 路由配置檔案
// 例項化路由
const router = new VueRouter({
    routes
})

var vm = new Vue({
  store,
  router
}).$mount('#app');

路由

前端頁面主要有 3 個路由,如下

# resource/assets/js/routes.js
export default[
  { path: '', redirect: '/index' },
  { path: '/index', component: require('./page/App.vue') },
  { path: '/list', component: require('./page/List.vue') },
  { path: '/detail/:id', component: require('./page/Detail.vue') }
];

Vuex

Vuex 集中式儲存管理應用的所有元件的狀態,這裡使用的是多模組方式記錄資料。

# resource/assets/js/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import news from './news';
Vue.use(Vuex);
export default new Vuex.Store({
  // 可以設定多個模組
  modules: {
    news
  }
});

# resource/assets/js/store/news.js
import api from '../api';
export default{
  state: {
    recommend: [], // 推薦
    lists: [],  // 列表
    detail: {}  // 詳情
  },
  mutations: {
    // 注意,這裡可以設定 state 屬性,但是不能非同步呼叫,非同步操作寫到 actions 中
    SETRECOMMEND(state, lists) {
      state.recommend = lists;
    },
    SETLISTS(state, lists) {
      state.lists = lists;
    },
    SETDETAIL(state, detail) {
      state.detail = detail;
    }
  },
  actions: {
    getNewsDetail({commit}, id) {
      // 獲取詳情,並呼叫 mutations 設定 detail
      api.getNewsDetail(id).then(function(res) {
        commit('SETDETAIL', res.data);
        document.body.scrollTop = 0;
      });
    },
    getNewsRecommend({commit}) {
      api.getNewsRecommend().then(function(res) {
        commit('SETRECOMMEND', res.data);
      });
    },
    getNewsLists({commit}) {
      api.getNewsLists().then(function(res) {
        commit('SETLISTS', res.data);
      });
    }
  }
}

api

我們在這裡定義前端請求資料 api,這裡使用的是 axios 包來請求資料,具體用法參考文件。

# resource/assets/js/api.js
import axios from 'axios'
export default {
  // 首頁推薦介面
  getNewsRecommend: function (params) {
    return axios.get('api/news/recommend', {
      params: params
    })
  },
  // 列表介面
  getNewsLists: function (params) {
    return axios.get('api/news', {
      params: params
    })
  },
  // 詳情介面
  getNewsDetail: function (id) {
    return axios.get('api/news/' + id)
  }
}

page

我們在這裡定義元件頁面,將其頁面放到 page 目錄下面,Vue 定義元件的方式參考文件。
三個頁面的具體寫法定義如下:

# resource/assets/js/page/App.vue
<template>
    <div class="panel panel-default">
        <div class="panel-heading">新聞推薦
            <router-link to="/list" class="pull-right">更多</router-link>
        </div>
        <ul class="list-group">
            <li class="list-group-item"
                v-for="row in recommend">
                <router-link :to="{path:'/detail/' + row.id}">
                    {{ row.title }}
                </router-link>
                <span class="pull-right">{{ row.created }}</span>
            </li>
        </ul>
    </div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default({
    // 對映 vuex 上面的屬性
    computed: mapState({
        recommend: state => state.news.recommend
    }),
    created() {
        // 獲取推薦列表
        this.getNewsRecommend();
    },
    methods: {
        // 對映 vuex 物件上的方法
        ...mapActions([
            'getNewsRecommend'
        ])
    }
});
</script>

# resource/assets/js/page/List.vue
<template>
  <div class="panel panel-default">
    <div class="panel-heading">新聞列表</div>
    <ul class="list-group">
      <li class="list-group-item"
        v-for="row in lists">
        <router-link :to="{path:'/detail/' + row.id}">
          <span class="label label-success" v-if="row.is_recommend">推薦</span>
          {{ row.title }}
        </router-link>
        <span class="pull-right">{{ row.created }}</span>
      </li>
    </ul>
  </div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default({
  computed: mapState({
    lists: state => state.news.lists
  }),
  created() {
    this.getNewsLists();  
  },
  methods: {
    ...mapActions([
        'getNewsLists'
    ])
  }
});
</script>

# resource/assets/js/page/Detail.vue
<template>
  <div>
    <ol class="breadcrumb">
      <li><a href="/">首頁</a></li>
      <li><router-link to="/list" class="pull-right">新聞</router-link></li>
      <li class="active">{{ detail.title }}</li>
    </ol>
    <h3><span class="label label-success" v-if="detail.is_recommend">推薦</span> {{ detail.title }}</h3>
    <p>建立時間:{{ detail.created_at }}</p>
    <div>
      {{ detail.content }}
    </div>
  </div>
</template>
<style>
.breadcrumb{
    padding: 8px 0;
}    
</style>
<script>
import { mapState, mapActions } from 'vuex';
export default({
  computed: mapState({
    detail: state => state.news.detail
  }),
  created() {
    // 獲取路由引數id
    // js 中用 this.$route 獲取當前路由,用 this.$router 獲路由物件,全部路由資訊
    // 在模板中用 $router  和 $router 直接呼叫
    var id = this.$route.params.id;
    this.getNewsDetail(id);
  },
  methods: {
    ...mapActions([
        'getNewsDetail'
    ])
  }
});
</script>

這裡我們簡單的單頁應用就開發完畢了,使用 npm run watch 可以熱更新,實時看到頁面的變化。
文章地址

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章