Laravel + Vue 前后台分开打包解决方案
当我们使用 Laravel + Vue 开发项目时候,经常会碰到需要开发 后台 的项目,我们会希望 前台 和 后台 分开构建,当 前台 和 后台 都使用 laravel-mix 进行构建时,我们希望可以分开处理,下面根据这些需求提供了如下的解决方案:
- 后台使用单独的
mix
构建文件,命名为webpack.admin.js
,与webpack.mix.js
在同一目录下- 打包出来后的文件单独为一个文件夹,方便访问,例如打包到
public/back
文件夹下(这里不能用admin
,至于为什么在 这里 会讲解)- 修改
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 dev
和 yarn run dev-admin
之后,得到的 public 目录如下所示:
其中 /back 目录为后台目录,/frontend 目录为前台目录
注意:这里不能用 admin 作为后台目录,也就是不能使用和路由同名的目录名,因为在访问
/admin
的时候,如果后台使用同名的 admin 浏览器会以为你是在访问/public/admin
目录,而我们希望的是访问public/index.php
,所以这里我用了 back 作为后台项目打包后的目录。
然后访问页面 /admin
会报出找不到 mix-manifest.json
的错误:
原因是因为我在 admin.blade.php
中使用了 mix
方法进行加载 css
和 js
,而 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>
这样就可以正常访问了:
另外还有前台页面:
总结
以上是对前后台分开打包的一种解决方案,以上方案其实也可以更方便的分开部署项目,例如我要分开部署后台项目为另一个二级域名 admin.domain.com
的话只需要修改 nginx 的目录指向 /public/back
就可以了,如果有其他更加好的方案欢迎评论~
以上代码可以访问我的 Github
本作品采用《CC 协议》,转载必须注明作者和本文链接
我比较倾向完全分离的做法, 这样前端VUE就用 TYPESCRIPT ,感觉超爽
@OneStep 是的,前台后台分开技术栈,也可以用react,维护起来更方便点
good
还是完全分离吧
vue + lumen很舒服
我用的laravel-mix 又封装了一层
宝宝的架子
不错
我按照你的文章做了分开打包,一切都还很顺利,只是scss文件编译出来后css文件是空的。这个问题是在很莫名其妙了。