Laravel + Vue 开发单页应用

开发环境

Homestead + Laravel5.3 + vue2 + vue-resource1 + vue-router2 + vuex2

源码放在 github 上面,源码地址

环境安装就不做介绍了,由于 5.3 自带 vue2vue-resource,但是需要安装 vue-routevuex
package.json 中添加

...
"devDependencies": {
    "bootstrap-sass": "^3.3.7",
    "extract-text-webpack-plugin": "^2.0.0-beta.4",
    "gulp": "^3.9.1",
    "jquery": "^3.1.0",
    "laravel-elixir": "^6.0.0-9",
    "laravel-elixir-vue-2": "^0.2.0",
    "laravel-elixir-webpack-official": "^1.0.2",
    "lodash": "^4.16.2",
    "vue": "^2.0.1",
    "vue-resource": "^1.0.3",
    // 添加
    "vue-router": "^2.0.1",
    "vuex": "^2.0.0"
  }
...

执行 npm install 安装

介绍下开发的需求,主要有三个页面:

  1. 首页展示推荐新闻
  2. 新闻列表
  3. 新闻详情

入口文件 app.js

resources/asserts/js/app.js

import Vue from 'vue';

import VueResource from 'vue-resource';
Vue.use(VueResource);
import VueRouter from 'vue-router';
Vue.use(VueRouter);

import store from './store/'; // vuex 数据存储所需对象

import routes from 'routes';    // 路由配置文件

// 实例化路由
const router = new VueRouter({
    routes
});

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

数据vuex

vuex 我们使用多模块的开发模式,这在项目中设计十分有必要的,如果把所有的数据一个实例上面,管理显得还是十分的混乱。
resources/asserts/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
    }
});

书写 news 模块代码
resources/asserts/store/news.js

import api from '../api';   // 主要是异步调用接口文件,下面有介绍

export default{
    state: {
        recommend: [],  // 存储推荐列表
        lists: [],  // 存储列表
        detail: {}  // 存储详情
    },
    mutations: {
        // 注意,这里可以设置 state 属性,但是不能异步调用,异步操作写到 actions 中
        SETRECOMMEND(state, recommend) {
            state.recommend = recommend;
        },
        SETLISTS(state, lists) {
            state.lists = lists;
        },
        SETDETAIL(state, detail) {
            state.detail = detail;
        }
    },
    actions: {
        GETDETAIL({commit}, id) {
            // 获取详情,并调用 mutations 设置 detail
            api.getNewsDetail(id).then(function(res) {
                commit('SETDETAIL', res.data);
                document.body.scrollTop = 0;
            });
        },
        GETRECOMMEND({commit}) {
            // 获取推荐,并调用 mutations 设置 recommend
            api.getNewsRecommend().then(function(res) {
                commit('SETRECOMMEND', res.data);
            });
        },
        GETLISTS({commit}) {
            // 获取列表,并调用 mutations 设置 lists
            api.getNewsLists().then(function(res) {
                commit('SETLISTS', res.data);
            });
        }
    }
};

看到上面的代码中引入了 api ,我将接口的请求全部放到 api.js 中,便于管理,其实这里也可以分成模块。
resources/asserts/api.js

import Vue from 'vue';
import VueResource from 'vue-resource';
Vue.use(VueResource);

// 设置 Laravel 的 csrfToken
Vue.http.interceptors.push((request, next) => {
    request.headers.set('X-CSRF-TOKEN', Laravel.csrfToken);
    next();
});

const API_ROOT = '';    // 可以根据自己的开发环境设置

export default({
    // 首页推荐信息
    getNewsRecommend: function() {
        return Vue.resource(API_ROOT + '/news').get();
    },
    // 列表信息
    getNewsLists: function() {
        return Vue.resource(API_ROOT + '/newslist').get();
    },
    // 详情
    getNewsDetail: function(id) {
        return Vue.resource(API_ROOT + '/newsdetail/' + id).get();
    }
});

路由信息 routes.js

resources/asserts/js/routes.js

export default[
    { path: '', redirect: '/index' },
    { path: '/index', component: require('./page/App.vue') },
    { path: '/newslist', component: require('./page/List.vue') },
    { path: '/newsdetail/:id', component: require('./page/Detail.vue') }
];

组件 App.vue

resources/asserts/js/page/App.vue

<template>
    <div>
        <h3>首页推荐</h3>
        <ul class="list-group">
            <li class="list-group-item"
                v-for="row in recommend">
                <router-link :to="{path:'/newsdetail/' + row.id}">
                    {{ row.title }}
                </router-link>
                <li class="list-group-item">
                    <router-link to="/newslist">更多</router-link>
                </li>
            </li>
        </ul>
    </div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default({
    // 映射 vuex 上面的属性
    computed: mapState({
        recommend: state => state.news.recommend
    }),
    created() {
        // 获取推荐列表
        this.GETRECOMMEND();
    },
    methods: {
        // 映射 vuex 对象上的方法
        ...mapActions([
            'GETRECOMMEND'
        ])
    }
});
</script>

组件 List.vue

resources/asserts/js/page/List.vue

<template>
    <div>
        <h3>列表信息</h3>
        <ul class="list-group">
            <li class="list-group-item"
                v-for="row in lists">
                <router-link :to="{path:'/newsdetail/' + row.id}">
                    {{ row.title }}
                </router-link>
            </li>
        </ul>
    </div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default({
    computed: mapState({
        lists: state => state.news.lists
    }),
    created() {
        this.GETLISTS();
    },
    methods: {
        ...mapActions([
            'GETLISTS'
        ])
    }
});
</script>

组件 Detail.vue

resources/asserts/js/page/Detail.vue

<template>
    <div>
        <table class="table table-bordered">
            <tr>
                <td width="20%">ID</td>
                <td>{{ detail.id }}</td>
            </tr>
            <tr>
                <td width="20%">标题</td>
                <td>{{ detail.title }}</td>
            </tr>
            <tr>
                <td width="20%">内容</td>
                <td>{{ detail.content }}</td>
            </tr>
            <tr>
                <td width="20%">创建时间</td>
                <td>{{ detail.created_at }}</td>
            </tr>
        </table>
    </div>
</template>
<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.GETDETAIL(id);
    },
    methods: {
        ...mapActions([
            'GETDETAIL'
        ])
    }
});
</script>

模板文件

resources/views/welcome.blade.php

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="/css/bootstrap.min.css">
        <title>Laravel</title>
    </head>
    <body>
        <div id="app" class="container"><router-view class="view"></router-view>
        </div>
    </body>
<script type="text/javascript">
    var Laravel = {
        // 设置 csrfToken
       csrfToken: '{{ csrf_token() }}' 
    };
</script>
<script src="/js/app.js"></script>
</html>

服务端 API

服务端的 api 只是一个数据的模拟,没有做深入的开发
routes/web.php

Route::get('/news', function() {
    return [
        ['id' => 1, 'title' => 'new1'],
        ['id' => 2, 'title' => 'new2'],
        ['id' => 3, 'title' => 'new3'],
        ['id' => 4, 'title' => 'new4'],
    ];
});
Route::get('/newslist', function() {
    return [
        ['id' => 1, 'title' => 'new1'],
        ['id' => 2, 'title' => 'new2'],
        ['id' => 3, 'title' => 'new3'],
        ['id' => 4, 'title' => 'new4'],
    ];
});
Route::get('/newsdetail/{id}', function($id) {
    return [
        'id' => 1,
        'title' => 'news',
        'content' => 'content',
        'created_at' => date('Y-m-d H:i:s')
    ];
});

运行

php artisan serve   // 开启 web
gulp  // 打包前端资源,开发的时候用 gulp watch 监听实时浏览

打开浏览器,访问 http://localhost:8000 查看效果。

这个例子只是简单的使用了 Laravelvue,详细开发请查看官方文档。

©版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 & 作者信息
文章链接

本帖已被设为精华帖!
本帖由系统于 7年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 11
chongyi

可以的。

不过我们那套系统层级太复杂太复杂,最终我们还是决定,前端部分拆成独立项目开发,这样好管理分支一些。

7年前 评论

webpack 打包吗

7年前 评论

学习下,谢谢分享

7年前 评论

不错。但是我一直想知道,laravel resource/assets/js/bootstrap.js 这文件,怎么没有require到 app.js里面?或者说这个bootstrap.js怎么用?

7年前 评论

@839891627 这个可以 require 到 app.js 里面的,bootstrap.js 文件主要默认加载了 lodash,jQuery 等全局组件,只是我演示的项目中没用到,所以就没有引用。

7年前 评论

jQuery与vue之间不会有冲突么?如果说jquery和vue都用的话,两者的分工是否不完全一样?
jquery负责ajax?vue负责双向数据绑定?

7年前 评论

@agogos 如果只使用 jQuery 的 ajax,不推荐使用,可以用 vue-resource 或 axios,如果还用到 jQuery 其他功能的话,完全可以混用。

7年前 评论

@Flyertutor 但如果不用jQuery的话,bootstrap那些特效就要自己写了。
呜呜呜呜

7年前 评论

@agogos 你可以参考这个 项目 看能不能满足你的需求

7年前 评论

@Flyertutor 谢谢啦。我看看。

7年前 评论
Toiu

学习了~ 顺便问一下 vue和vue-router不需要另行在blade模板中引入了对吧?

6年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!