基于 Laravel + Vue 构建 API 驱动的前后端分离应用系列(十二) —— 为 Vue Router 添加页面布局

Laravel 可以通过 Blade 模板为应用添加页面布局,不过由于我们构建的是单页面应用,可以通过 Vue Router 来实现:创建一个根级页面,其中包含在每个页面都会用到的 Vue 组件,如 Header 和 Footer。使用布局的另一个好处是你可以一次加载所有你需要的 Vuex 数据,它们会随着布局里的组件出现在所有子页面上。

第一步:重新组织嵌套路由

设置前端路由那篇教程中,我们并没有包含嵌套路由,而 Vue Router 的路由嵌套正是可以用于创建布局的小技巧,更多关于嵌套路由的细节,可以查看官方文档

首先,我们需要构建一个顶级路由作为布局,将路由文件 resources/assets/js/routes.js 中原来定义的路由删除,添加这段路由定义:

{
    path: '/',
    name: 'layout',
    component: Vue.component( 'Home', require( './pages/Layout.vue' ) ),
},

Layout.vue 布局文件将在下一步添加,现在我们继续编辑这个路由文件,我们上面定义的是一个顶级根路由,接下来我们要把原来的路由作为嵌套路由放到名为 children 的配置数组中:

export default new VueRouter({
    routes: [
        {
            path: '/',
            name: 'layout',
            component: Vue.component( 'Home', require( './pages/Layout.vue' ) ),
            children: [
                {
                    path: 'home',
                    name: 'home',
                    component: Vue.component( 'Home', require( './pages/Home.vue' ) )
                },
                {
                    path: 'cafes',
                    name: 'cafes',
                    component: Vue.component( 'Cafes', require( './pages/Cafes.vue' ) ),
                },
                {
                    path: 'cafes/new',
                    name: 'newcafe',
                    component: Vue.component( 'NewCafe', require( './pages/NewCafe.vue' ) )
                },
                {
                    path: 'cafes/:id',
                    name: 'cafe',
                    component: Vue.component( 'Cafe', require( './pages/Cafe.vue' ) )
                }
            ]
        }
    ]
});

接下来我们来添加 Layout.vue 页面。

第二步:添加 Layout.vue

resources/assets/js/pages 目录中创建 Layout.vue 作为我们的布局文件,编辑文件内容如下:

<style>

</style>

<template>
  <div id="app-layout">
        <router-view></router-view>
  </div>
</template>

<script>
  export default {

  }
</script>

<router-view></router-view> 组件将用于渲染导入父组件的子组件。

有了布局之后,就可以导入在上一篇教程创建的 Navigation.vue 公共组件了,我们需要在导出默认组件之前导入组件,并在导出组件的 components 配置对象中告诉页面使用这个导入的组件:

<script>
    import Navigation from '../components/global/Navigation.vue';

    export default {
        components: {
            Navigation
        }
    }
</script>

最后,我们将这个组件添加到页面模板中:

<style>

</style>

<template>
    <div id="app-layout">
        <navigation></navigation>
        <router-view></router-view>
    </div>
</template>

<script>
    import Navigation from '../components/global/Navigation.vue';

    export default {
        components: {
            Navigation
        }
    }
</script>

这样,Navigation 组件就会出现在所有页面中,因为每一个页面都是布局的子页面。

下面我们在所有页面上加载数据,由于我们已经在构建 Vuex 模块教程中构建好了相关 Vuex 模块,所以只需要将其绑定布局页面的 created() 钩子函数并加载响应数据即可。在本案例中,我们需要在导航组件中显示用户信息以及咖啡店列表信息,所以最终的 Layout.vue 代码如下:

<style>

</style>

<template>
    <div id="app-layout">
        <navigation></navigation>
        <router-view></router-view>
    </div>
</template>

<script>
    import Navigation from '../components/global/Navigation.vue';

    export default {
        components: {
            Navigation
        },
        created(){
            this.$store.dispatch( 'loadCafes' );
            this.$store.dispatch( 'loadUser' );
        }
    }
</script>

编写好上述代码之后,布局组件就可以加载咖啡店列表及当前登录用户信息。

第三步:调整认证控制器重定向

这是添加布局到应用的最后一个步骤。

由于现在顶级链接指向布局页面,所以需要在用户登录成功后将重定向路由修改为 /home,为此,打开 app/Http/Controllers/Web/AuthenticationController.php 文件,在 getSocialCallback() 方法中将重新向响应从

return redirect('/');

修改为

return redirect('/#/home');

至此,我们的页面布局已经创建完成,运行 npm run dev 重新构建应用,然后访问 http://roast.test,登录成功后,会重定向到 http://roast.test/#/home,此时页面显示如下:

可以看到导航组件已经显示出来了。

项目源码位于 Github 上:nonfu/roastapp

学院君 has written 1083 articles

Laravel学院院长,终身学习者

积分:137487 等级:P12 职业:手艺人 城市:杭州

19 条回复

  1. 悟佛悟空 悟佛悟空 says:

    我的webpack.mix.js

    mix.js > webpackConfig 的
    resolve: { alias: { '@': path.resolve('resources/assets/sass') } }

    路径重命名不起作用,导致sass路径找不到, 不知道该如何调试 才能让其生效,不过我没用路径别名就好了。关于这一块还想请问一下学院君 。顺便说下,我好崇拜学院君!!!

  2. 风清扬 风清扬 says:
    @ gml1994

    app.js:2861 TypeError: Cannot read property 'getCafes' of undefined at Store.loadCafes (app.js:58849) 你的getCafes 方法是不是敲错了??

  3. lai416703504 lai416703504 says:
    app.js:47450 [Vue warn]: Failed to mount component: template or render function not defined.
    
    found in
    
    ---> <Anonymous>
           <Root>
    warn @ app.js:47450
    mountComponent @ app.js:49703
    ./node_modules/vue/dist/vue.common.js.Vue.$mount @ app.js:55484
    ./node_modules/vue/dist/vue.common.js.Vue.$mount @ app.js:57897
    init @ app.js:51069
    createComponent @ app.js:52535
    createElm @ app.js:52482
    createChildren @ app.js:52610
    createElm @ app.js:52511
    patch @ app.js:53073
    Vue._update @ app.js:49612
    updateComponent @ app.js:49733
    get @ app.js:50103
    Watcher @ app.js:50092
    mountComponent @ app.js:49740
    ./node_modules/vue/dist/vue.common.js.Vue.$mount @ app.js:55484
    ./node_modules/vue/dist/vue.common.js.Vue.$mount @ app.js:57897
    ./resources/js/app.js @ app.js:59046
    __webpack_require__ @ app.js:20
    0 @ app.js:59847
    __webpack_require__ @ app.js:20
    (anonymous) @ app.js:84
    (anonymous) @ app.js:87

    谁能帮我看看这是什么原因啊

  4. gml1994 gml1994 says:

    反复复制了所有的代码,还是一直前端报错。。。

    app.js:1713 [Vue warn]: Error in created hook: "TypeError: Cannot read property 'getCafes' of undefined"
    
    found in
    
    ---> <Home> at resources/js/pages/Layout.vue
           <Root>
    warn @ app.js:1713
    logError @ app.js:2857
    globalHandleError @ app.js:2852
    handleError @ app.js:2841
    callHook @ app.js:4043
    Vue._init @ app.js:5750
    VueComponent @ app.js:5918
    createComponentInstanceForVnode @ app.js:5430
    init @ app.js:5251
    createComponent @ app.js:6728
    createElm @ app.js:6675
    createChildren @ app.js:6802
    createElm @ app.js:6704
    patch @ app.js:7250
    Vue._update @ app.js:3780
    updateComponent @ app.js:3908
    get @ app.js:4262
    Watcher @ app.js:4251
    mountComponent @ app.js:3915
    Vue.$mount @ app.js:9660
    Vue.$mount @ app.js:12059
    (anonymous) @ app.js:22865
    __webpack_require__ @ app.js:20
    (anonymous) @ app.js:22815
    __webpack_require__ @ app.js:20
    (anonymous) @ app.js:63
    (anonymous) @ app.js:66
    app.js:2861 TypeError: Cannot read property 'getCafes' of undefined
        at Store.loadCafes (app.js:58849)
        at Array.wrappedActionHandler (app.js:58587)
        at Store.dispatch (app.js:58309)
        at Store.boundDispatch [as dispatch] (app.js:58215)
        at VueComponent.created (app.js:55789)
        at callHook (app.js:4041)
        at VueComponent.Vue._init (app.js:5750)
        at new VueComponent (app.js:5918)
        at createComponentInstanceForVnode (app.js:5430)
        at init (app.js:5251)
    logError @ app.js:2861
    globalHandleError @ app.js:2852
    handleError @ app.js:2841
    callHook @ app.js:4043
    Vue._init @ app.js:5750
    VueComponent @ app.js:5918
    createComponentInstanceForVnode @ app.js:5430
    init @ app.js:5251
    createComponent @ app.js:6728
    createElm @ app.js:6675
    createChildren @ app.js:6802
    createElm @ app.js:6704
    patch @ app.js:7250
    Vue._update @ app.js:3780
    updateComponent @ app.js:3908
    get @ app.js:4262
    Watcher @ app.js:4251
    mountComponent @ app.js:3915
    Vue.$mount @ app.js:9660
    Vue.$mount @ app.js:12059
    (anonymous) @ app.js:22865
    __webpack_require__ @ app.js:20
    (anonymous) @ app.js:22815
    __webpack_require__ @ app.js:20
    (anonymous) @ app.js:63
    (anonymous) @ app.js:66
    app.js:1713 [Vue warn]: Error in created hook: "TypeError: Cannot read property 'getCafes' of undefined"
    
    found in
    
    ---> <Home> at resources/js/pages/Home.vue
           <Home> at resources/js/pages/Layout.vue
             <Root>
    warn @ app.js:1713
    logError @ app.js:2857
    globalHandleError @ app.js:2852
    handleError @ app.js:2841
    callHook @ app.js:4043
    Vue._init @ app.js:5750
    VueComponent @ app.js:5918
    createComponentInstanceForVnode @ app.js:5430
    init @ app.js:5251
    createComponent @ app.js:6728
    createElm @ app.js:6675
    createChildren @ app.js:6802
    createElm @ app.js:6704
    patch @ app.js:7211
    Vue._update @ app.js:3780
    updateComponent @ app.js:3908
    get @ app.js:4262
    Watcher @ app.js:4251
    mountComponent @ app.js:3915
    Vue.$mount @ app.js:9660
    Vue.$mount @ app.js:12059
    init @ app.js:5257
    createComponent @ app.js:6728
    createElm @ app.js:6675
    createChildren @ app.js:6802
    createElm @ app.js:6704
    patch @ app.js:7250
    Vue._update @ app.js:3780
    updateComponent @ app.js:3908
    get @ app.js:4262
    Watcher @ app.js:4251
    mountComponent @ app.js:3915
    Vue.$mount @ app.js:9660
    Vue.$mount @ app.js:12059
    (anonymous) @ app.js:22865
    __webpack_require__ @ app.js:20
    (anonymous) @ app.js:22815
    __webpack_require__ @ app.js:20
    (anonymous) @ app.js:63
    (anonymous) @ app.js:66
    app.js:2861 TypeError: Cannot read property 'getCafes' of undefined
        at Store.loadCafes (app.js:58849)
        at Array.wrappedActionHandler (app.js:58587)
        at Store.dispatch (app.js:58309)
        at Store.boundDispatch [as dispatch] (app.js:58215)
        at VueComponent.created (app.js:56165)
        at callHook (app.js:4041)
        at VueComponent.Vue._init (app.js:5750)
        at new VueComponent (app.js:5918)
        at createComponentInstanceForVnode (app.js:5430)
        at init (app.js:5251)

    构建提示:

    WARNING in ./resources/js/modules/cafes.js
    26:6-13 "export 'default' (imported as 'CafeAPI') was not found in '../api/cafe.js'
     @ ./resources/js/modules/cafes.js
     @ ./resources/js/store.js
     @ ./resources/js/app.js
     @ multi ./resources/js/app.js ./resources/sass/app.scss
    
    WARNING in ./resources/js/api/cafe.js
    26:6-13 "export 'default' (imported as 'CafeAPI') was not found in '../api/cafe.js'
     @ ./resources/js/api/cafe.js
     @ ./resources/js/modules/cafes.js
     @ ./resources/js/store.js
     @ ./resources/js/app.js
     @ multi ./resources/js/app.js ./resources/sass/app.scss
    
    WARNING in ./resources/js/modules/cafes.js
    37:6-13 "export 'default' (imported as 'CafeAPI') was not found in '../api/cafe.js'
     @ ./resources/js/modules/cafes.js
     @ ./resources/js/store.js
     @ ./resources/js/app.js
     @ multi ./resources/js/app.js ./resources/sass/app.scss
    
    WARNING in ./resources/js/api/cafe.js
    37:6-13 "export 'default' (imported as 'CafeAPI') was not found in '../api/cafe.js'
     @ ./resources/js/api/cafe.js
     @ ./resources/js/modules/cafes.js
     @ ./resources/js/store.js
     @ ./resources/js/app.js
     @ multi ./resources/js/app.js ./resources/sass/app.scss
  5. zsping1989 zsping1989 says:

    感觉js代码中 api 部分跟model部分是不是可以合起来 里面感觉好多重复代码 浪费很大的工作时间做相同的事情 应该想法把它抽象出来公用

  6. Laragh Laragh says:

    没有 loadUser 方法

    还需要注释下面的内容才能加载出来

    <div class="right">
                <img class="avatar" :src="user.avatar" v-show="userLoadStatus == 2">
            </div>

登录后才能进行评论,立即登录?