菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
1507
17

Laravel5.5 + Vue 开发单页应用

原创
05/13 14:22
阅读数 1487

上次我用 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单页

到这里我们准备工作已经完毕,接下来正式开发 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 可以热更新,实时看到页面的变化。
文章地址

发表评论

0/200
1507 点赞
17 评论
收藏
为你推荐 换一批