Laravel + Vue 前后台分开打包解决方案

当我们使用 Laravel + Vue 开发项目时候,经常会碰到需要开发 后台 的项目,我们会希望 前台后台 分开构建,当 前台后台 都使用 laravel-mix 进行构建时,我们希望可以分开处理,下面根据这些需求提供了如下的解决方案:

  1. 后台使用单独的 mix 构建文件,命名为 webpack.admin.js,与 webpack.mix.js 在同一目录下
  2. 打包出来后的文件单独为一个文件夹,方便访问,例如打包到 public/back 文件夹下(这里不能用 admin,至于为什么在 这里 会讲解)
  3. 修改 webpack.mix.js 使其支持 env,这样可以通过参数指定是构建 后台 还是 前台 项目

方案实现

首先在 web.php 中指定两个路由,分别访问前台项目及后台项目

// web.php

Route::get('/', function () {
    return view('index');
});

Route::get('/admin', function () {
    return view('admin');
});

创建新的 webpack.admin.js 并指定构建规则:

const mix = require('laravel-mix')

if (mix.inProduction()) {
  mix.version()
}

mix.webpackConfig({
  output: {
    publicPath: '/back/', // 设置默认打包目录
    chunkFilename: `js/[name].${mix.inProduction() ? '[chunkhash].' : ''}js` // 路由懒加载的时候打包出来的js文件
  }
})

mix.js('resources/admin/assets/js/app.js', 'public/back/js') // 打包后台js
  .sass('resources/admin/assets/sass/app.scss', 'public/back/css') // 打包后台css
  .extract(['vue', 'iview', 'vue-router', 'axios']) // 提取依赖库
  .setResourceRoot('/back/') // 设置资源目录
  .setPublicPath('public/back') // 设置 mix-manifest.json 目录

修改 webpack.mix.js,添加 minimist 包使其支持 env,可以获取 node 环境参数

const mix = require('laravel-mix')

// 得到package.json中的参数 --env.admin 转换成 一个对象 {admin: true}
const { env } = require('minimist')(process.argv.slice(2))

// 判断如果是admin那就执行 webpack.admin.js 构建后台项目,构建之后return就不会往下执行了
if (env && env.admin) {
  require(`${__dirname}/webpack.admin.js`)
  return
}

// 前台项目的构建规则
if (mix.inProduction()) {
  mix.version()
}

mix.webpackConfig({
  output: {
    publicPath: '/frontend/', // 设置默认打包目录
    chunkFilename: `js/[name].${mix.inProduction() ? '[chunkhash].' : ''}js` // 路由懒加载的时候打包出来的js文件
  }
})

mix.js('resources/frontend/assets/js/app.js', 'public/frontend/js')
  .sass('resources/frontend/assets/sass/app.scss', 'public/frontend/css')
  .extract(['vue'])
  .setResourceRoot('/frontend/') // 设置资源目录
  .setPublicPath('public/frontend/') // 设置 mix-manifest.json 目录

修改 package.json 添加构建后台的命令:

// package.json

"scripts": {
    "dev-admin": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js --env.admin",
    "prod-admin": "npm run production-admin",
    "production-admin": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js --env.admin",
}

执行 yarn run devyarn run dev-admin 之后,得到的 public 目录如下所示:
public
其中 /back 目录为后台目录,/frontend 目录为前台目录

注意:这里不能用 admin 作为后台目录,也就是不能使用和路由同名的目录名,因为在访问 /admin 的时候,如果后台使用同名的 admin 浏览器会以为你是在访问 /public/admin 目录,而我们希望的是访问 public/index.php,所以这里我用了 back 作为后台项目打包后的目录。

然后访问页面 /admin 会报出找不到 mix-manifest.json 的错误:
error

原因是因为我在 admin.blade.php 中使用了 mix 方法进行加载 cssjs,而 mix 方法会找 mix-manifest.json 这个文件去匹配资源路径(为的是可以添加版本号),mix 方法如下所示:

    function mix($path, $manifestDirectory = '')
    {
        static $manifests = [];

        if (! Str::startsWith($path, '/')) {
            $path = "/{$path}";
        }

        if ($manifestDirectory && ! Str::startsWith($manifestDirectory, '/')) {
            $manifestDirectory = "/{$manifestDirectory}";
        }

        if (file_exists(public_path($manifestDirectory.'/hot'))) {
            $url = file_get_contents(public_path($manifestDirectory.'/hot'));

            if (Str::startsWith($url, ['http://', 'https://'])) {
                return new HtmlString(Str::after($url, ':').$path);
            }

            return new HtmlString("//localhost:8080{$path}");
        }

        $manifestPath = public_path($manifestDirectory.'/mix-manifest.json');

        if (! isset($manifests[$manifestPath])) {
            if (! file_exists($manifestPath)) {
                throw new Exception('The Mix manifest does not exist.');
            }

            $manifests[$manifestPath] = json_decode(file_get_contents($manifestPath), true);
        }

        $manifest = $manifests[$manifestPath];

        if (! isset($manifest[$path])) {
            report(new Exception("Unable to locate Mix file: {$path}."));

            if (! app('config')->get('app.debug')) {
                return $path;
            }
        }

        return new HtmlString($manifestDirectory.$manifest[$path]);
    }

发现了 mix 方法第二个参数可以指定 mix-manifest.json 文件的目录,因此我在 admin.blade.php 中修改为:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <title>Document</title>
    <link rel="stylesheet" href="{{mix('/css/app.css', 'back')}}">
</head>
<body>
<div id="app"></div>
<script src="{{mix('/js/manifest.js', 'back')}}"></script>
<script src="{{mix('/js/vendor.js', 'back')}}"></script>
<script src="{{mix('/js/app.js', 'back')}}"></script>
</body>
</html>

还有 index.blade.php 前台页面的 css 和 js 的加载路径:

<link rel="stylesheet" href="{{mix('css/app.css', 'frontend')}}">
<script src="{{mix('/js/manifest.js', 'frontend')}}"></script>
<script src="{{mix('/js/vendor.js', 'frontend')}}"></script>
<script src="{{mix('/js/app.js', 'frontend')}}"></script>

这样就可以正常访问了:
login
另外还有前台页面:
frontend

总结

以上是对前后台分开打包的一种解决方案,以上方案其实也可以更方便的分开部署项目,例如我要分开部署后台项目为另一个二级域名 admin.domain.com 的话只需要修改 nginx 的目录指向 /public/back 就可以了,如果有其他更加好的方案欢迎评论~

以上代码可以访问我的 Github

本作品采用《CC 协议》,转载必须注明作者和本文链接
kwen
本帖由系统于 5年前 自动加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 6

我比较倾向完全分离的做法, 这样前端VUE就用 TYPESCRIPT ,感觉超爽

5年前 评论

@OneStep 是的,前台后台分开技术栈,也可以用react,维护起来更方便点

5年前 评论

还是完全分离吧
vue + lumen很舒服
我用的laravel-mix 又封装了一层
宝宝的架子

5年前 评论
kinyou

不错

5年前 评论

我按照你的文章做了分开打包,一切都还很顺利,只是scss文件编译出来后css文件是空的。这个问题是在很莫名其妙了。

4年前 评论

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