• Laravel 5.5 + Vue 开发单页应用


    上次我用 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 IlluminateDatabaseEloquentModel;
    
    class News extends Model
    {
      public $fillable = ['title', 'content'];
    }
     

    controller

    生成控制器,在控制器中定义三个接口对应的方法。

    php artisan make:controller NewsController
    # app/Http/Controllers/NewsController
    <?php
    namespace AppHttpControllers;
    
    use IlluminateHttpRequest;
    use AppNews;
    
    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 ApiVuex Components 这些,下面我们就来定义这些。

    首先在首页引入对应的 app.css 和 app.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 可以热更新,实时看到页面的变化。
    文章地址

  • 相关阅读:
    Python文件File方法
    python的slice notation的特殊用法
    函数
    字典
    python 笔记7
    列表解析与生成器
    [CenOS7][Mac] MAC环境中dubbo连接zookeeper超时
    [JAVA][Thread] 实现Runnable接口和继承Thread类创建线程哪种方式更好?
    [Java] String字符常量类型作为参数传递的一些问题
    [JAVA]hashCode()和identityHashCode()的区别
  • 原文地址:https://www.cnblogs.com/mouseleo/p/10344695.html
Copyright © 2020-2023  润新知