1.请先安装 Composer:
windows下安装软件请见附件【Composer-Setup.zip】,解压安装即可。查看是否安装成功:
| composer --version |
2.安装 Laravel:
通过 Composer 的 create-project 命令来安装 Laravel 应用,blog 为项目名称:
| composer create-project --prefer-dist laravel/laravel blog |
注意:详细说明请看 Laravel5.4 文档。建议配置虚拟主机。
3.文件配置:
在根目录下有个 package.json 文件,配置内容如下(自动生成的文件中有很多包依赖没有,如果想使用 Element ,请先修改成下面配置,也可以后期逐个安装依赖包):
{ "private": true, "scripts": { "dev": "npm run development", "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", "watch-poll": "npm run watch -- --watch-poll", "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", "prod": "npm run production", "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" }, "devDependencies": { "axios": "^0.16.2", "bootstrap-sass": "^3.3.7", "cross-env": "^5.0.1", "element-ui": "^2.3.7", "jquery": "^3.1.1", "laravel-mix": "^1.0", "lodash": "^4.17.4", "vue": "^2.1.10", "vue-router": "^2.4.0", "qs": "^6.5.2" } } |
注:请使用在线json检测工具确保该json格式正确。红色是原项目所带依赖包,其他是我们添加的依赖包。
4.安装相关依赖 ,进入到项目目录执行:
| cnpm install |
注:请自行安装 npm ,速度慢的请自行切换淘宝镜像 cnpm ,建议使用 cnpm。可以参考我的 Vue 笔记。
5.引入 Vue Router:
构建大型项目时,使用 Vue Router 将是一个好的方式,它可以帮助你更好的组织代码,优化路由。
1).在 resources/assets/js 目录下新建目录 router ,同时在 router 目录下新建 index.js 文件,内容如下:
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) //加入需要渲染的页面 import Home from '../components/Home' import Index from '../components/Index' import Bar from '../components/Bar' import Foo from '../components/Foo' import Role from '../components/Role' import Not_Found from '../components/Not_Found' Vue.use(VueRouter); const routes=[ { path: '/', component: Home, children:[ { path: '/', name: 'index', component: Index }, { path: 'index', name: 'goindex', redirect: '/' }, { path: 'bar', name: 'bar', component: Bar }, { path: 'foo', name: 'foo', component: Foo, }, { path: 'role', name: 'rolw', component: Role, }, { path: '404', name: '404', component: Not_Found }, { path: '*', name: 'to404', redirect: '/404' }, ] }, { path: '*', name: 'go404', redirect: '/404' } ]; const router = new VueRouter({ routes: routes, // mode: 'history' }); router.push('/index'); //一进入就跳到该路由 export default router |
注:具体组件下面将一一介绍。
2).设置顶级路由出口:
在 resources/assets/js 下新建 App.vue 文件,内容如下:
<template> <div id="app"> <router-view></router-view> </div> </template> |
6.配置Vue主文件:
修改 resources/assets/js/app.js 文件,引入路由、axios 和 Element 等组件:
/** * First we will load all of this project's JavaScript dependencies which * includes Vue and other libraries. It is a great starting point when * building robust, powerful web applications using Vue and Laravel. */ require('./bootstrap'); window.Vue = require('vue'); /** * Next, we will create a fresh Vue application instance and attach it to * the page. Then, you may begin adding components to this application * or customize the JavaScript scaffolding to fit your unique needs. */ import App from './App.vue' import router from './router' import Axios from "axios" import qs from 'qs' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' Vue.prototype.$axios = Axios Vue.use(ElementUI) const app = new Vue({ el: '#app', router, render: h => h(App) }); |
注:app.js 是构建 Vue 项目的主文件,最后所有组件都会被引入到该文件,在引入所有组件之前,bootstrap.js 文件做了一些初始化的操作。同时,因为有了 window.Vue = require('vue'); 这句话,就不再需要使用 import Vue from 'vue' 重复导入 Vue 了。
7.新建组件:(在 resources/assets/js/components 目录或者子目录下新建各个组件)
1). Header.vue
<template> <div class="header"> <el-menu :default-active="activeIndex2" class="el-menu-demo" mode="horizontal" @select="handleSelect" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b"> <el-menu-item index="/index">个人中心</el-menu-item> <el-submenu index="2"> <template slot="title">我的工作台</template> <el-menu-item index="/bar">选项1-Bar</el-menu-item> <el-menu-item index="/foo">选项2-Foo</el-menu-item> <el-menu-item index="/user">选项3-User</el-menu-item> <el-submenu index="2-4"> <template slot="title">选项4</template> <el-menu-item index="/2-4-1">选项1</el-menu-item> <el-menu-item index="/2-4-2">选项2</el-menu-item> <el-menu-item index="/2-4-3">选项3</el-menu-item> </el-submenu> </el-submenu> <el-menu-item index="3" disabled>消息中心</el-menu-item> <el-menu-item index="4"><a href="http://element-cn.eleme.io/#/zh-CN/component/table" target="_blank">订单管理</a></el-menu-item> <el-submenu index="user" id="user"> <template slot="title">{{name}}</template> <el-menu-item index="login_out" @click="logout">退出</el-menu-item> </el-submenu> </el-menu> </div> </template> <script> export default { data() { return { activeIndex: '1', activeIndex2: '1', name : '时间旅者' }; }, methods: { handleSelect(key, keyPath) { window.location.href = location.origin + "/#" + key; console.log(key,keyPath); }, logout(){ console.log("logout"); } } } </script> <style scoped> #user{ float : right; margin-right: 93px; } </style> |
2). Home.vue
<template> <div class="header"> <Header/> <router-view></router-view> </div> </template> <script> import Header from "./Header" export default { data() { return { }; }, components:{ Header, } } </script> <style scoped> </style> |
3). Bar.vue
<template> <div class="mycarousel"> <el-carousel :interval="5000" arrow="always" height="410px"> <el-carousel-item v-for="item in imgs" :key="item.key"> <img :src="item.path" height="415px" /> </el-carousel-item> </el-carousel> </div> </template> <script> export default { name:"foo", data(){ return { imgs: [ { key:1, path:"/images/myimg/1.jpg" }, { key:2, path:"/images/myimg/2.jpg" }, { key:3, path:"/images/myimg/3.jpg" }, { key:4, path:"/images/myimg/4.jpg" }, { key:5, path:"/images/myimg/5.jpg" }, { key:6, path:"/images/myimg/6.jpg" }, ] } } } </script> <style scoped> .el-carousel__item:nth-child(2n) { background-color: #99a9bf; text-align: center; } .el-carousel__item:nth-child(2n+1) { background-color: #d3dce6; text-align: center; } </style> |
4). Index.vue
<template> <div class="m_index"> <el-table :data="tableData" border height="540" style="width: 100%"> <el-table-column fixed prop="id" label="编号" width="80"> </el-table-column> <el-table-column fixed prop="date" label="日期" width="150"> </el-table-column> <el-table-column prop="name" label="姓名" width="150"> </el-table-column> <el-table-column prop="province" label="省份" width="150"> </el-table-column> <el-table-column prop="city" label="市区" width="150"> </el-table-column> <el-table-column prop="address" label="地址" width="350"> </el-table-column> <el-table-column prop="zip" label="邮编" width="150"> </el-table-column> <el-table-column fixed="right" label="操作" width="150"> <template slot-scope="scope"> <el-button @click="showDetail(scope.row)" type="text" size="small">查看</el-button> <el-button @click="showDetail(scope.row)" type="text" size="small">编辑</el-button> </template> </el-table-column> </el-table> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage" :page-sizes="[10, 20, 50, 100]" :page-size="pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total"> </el-pagination> <el-dialog title="提示" :visible.sync="dialogVisible" width="30%" :before-close="handleClose" > <span>日期:{{ gridData.date }}</span></br> <span>姓名:{{ gridData.name }}</span></br> <span>省份:{{ gridData.province }}</span></br> <span>市区:{{ gridData.city }}</span></br> <span>地址:{{ gridData.address }}</span></br> <span>邮编:{{ gridData.zip }}</span></br> <span slot="footer" class="dialog-footer"> <el-button @click="dialogVisible = false">取 消</el-button> <el-button type="primary" @click="dialogVisible = false">确 定</el-button> </span> </el-dialog> </div> </template> <script> export default { data() { return { pagesize: 10, currentPage: 1, total: 0, tableData: [], gridData:{}, dialogVisible : false, } }, methods:{ getData:function(){ this.$axios("/getData",{ params:{ pagesize:this.pagesize, currentPage:this.currentPage } }) .then(function(res) { this.tableData = res.data.data; this.total = res.data.total; console.log(this.tableData); }.bind(this)) .catch(function(error){ console.log(error); }) }, showDetail:function(row) { this.gridData = { address:row.address, city:row.city, date:row.date, name:row.name, province:row.province, zip:row.zip, }; this.dialogVisible = true; }, handleSizeChange: function (size) { this.pagesize = size; this.getData(); }, handleCurrentChange: function(currentPage){ this.currentPage = currentPage; this.getData(); }, handleClose:function(done) { this.$confirm('确认关闭?') .then(_ => { done(); }) .catch(_ => {}); } }, created:function(){ this.getData(); } } </script> <style> .el-pagination{ text-align: center; } </style> |
5). Role.vue
<template> <div class="block"> <span class="demonstration">时间范围选择</span><br/> <el-date-picker v-model="value5" type="datetimerange" :picker-options="pickerOptions2" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" align="right"> </el-date-picker><br/> 您选择的时间范围为:{{ value5[0] }} 至 {{ value5[1] }} </div> </template> <script> export default { data() { return { pickerOptions2: { shortcuts: [{ text: '最近一周', onClick(picker) { const end = new Date(); const start = new Date(); start.setTime(start.getTime() - 3600 * 1000 * 24 * 7); picker.$emit('pick', [start, end]); } }, { text: '最近一个月', onClick(picker) { const end = new Date(); const start = new Date(); start.setTime(start.getTime() - 3600 * 1000 * 24 * 30); picker.$emit('pick', [start, end]); } }, { text: '最近三个月', onClick(picker) { const end = new Date(); const start = new Date(); start.setTime(start.getTime() - 3600 * 1000 * 24 * 90); picker.$emit('pick', [start, end]); } }] }, value5: [new Date(2000, 10, 10, 10, 10), new Date(2000, 10, 11, 10, 10)], }; } }; </script> <style scoped> .block{ height: 500px; } </style> |
6). Foo.vue
<template> <div> <el-tabs type="border-card"> <el-tab-pane label="用户管理"> <Index/> </el-tab-pane> <el-tab-pane label="配置管理"> <Bar/> </el-tab-pane> <el-tab-pane label="角色管理"> <Role/> </el-tab-pane> <el-tab-pane label="定时任务补偿"> 定时任务补偿 </el-tab-pane> </el-tabs> </div> </template> <script> import Index from "./Index" import Bar from "./Bar" import Role from "./Role" export default { name:"foo", data(){ return { msg: "我是foo组件" } }, components:{ Index, Bar, Role } } </script> <style scoped> </style> |
7). Not_Found.vue
<template> <div class="notfound"> <a href="/"> <img src="/images/myimg/404.jpg" class="c404" /> </a> </div> </template> <script> export default { data() { return { }; }, } </script> <style scoped> .notfound{ width:100%; overflow: hidden; background-size:cover; } .c404{ width:100%; } </style> |
8.后台提供数据:
由于 Index.vue 组件使用了后台提供的数据,故我们需要在后台进行处理,以此将 Laravel 跟 Vue 结合起来。
1).在 app 目录下新建 DataFrom.php 文件:
<?php namespace App; class DataFrom { public $id; public $date; public $name; public $province; public $city; public $address; public $zip; } |
2).在 app\Http\Controllers 下新建 UserController.php 文件:
在控制台使用 artisan 工具生成:
| php artisan make:controller UserController |
修改代码如下:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use App\DataFrom; class UserController extends Controller { public function getData(Request $request){ //生成数据并进行分页传给前端 $arr = []; for($i=1;$i<1002;$i++){ $dataFrom = new DataFrom(); $dataFrom->id = $i; $dataFrom->date = '2016-05-0'.$i; $dataFrom->name = '王小虎'.$i; $dataFrom->province = '上海'.$i; $dataFrom->city = '普陀区'.$i; $dataFrom->address = '上海市普陀区金沙江路 1518 弄'.$i; $dataFrom->zip = '20033'.$i; $arr[] = $dataFrom; } $currentPage = $request->input('currentPage', 1); // 获取页码 $pagesize = $request->input('pagesize', 10); //每页的条数 $offset = ($currentPage * $pagesize) - $pagesize;//计算每页分页的初始位置 $res = new LengthAwarePaginator(array_slice($arr,$offset,$pagesize,true),count($arr),$pagesize, $currentPage,['path' => $request->url(), 'query' => $request->query()]); return response()->json($res); } } |
3).在 routes\web.php 文件中配置路由:
<?php /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/', function () { return view('welcome'); //主视图 }); Route::get('/getData', 'UserController@getData'); |
说明:以后如果要添加组件,只需要在 resources/assets/js/components 目录下新建 vue 文件,然后再在 resources/assets/js/router/index.js 文件里引入,并配置路由,然后就可以通过路由访问了。如果组件里面访问了后台数据,则在 app\Http\Controllers 下新建相应的 Controller 文件,并在 routes\web.php 文件配置后台路由即可。
9. 配置 Laravel 主视图(该文件也是Vue主入口文件):
修改 resources/view/welcome.blade.php 文件:
<!DOCTYPE html> <html lang="{{ config('app.locale') }}"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="csrf-token" content="{{csrf_token()}}"> <link href="{{ asset('css/main.css') }}" rel="stylesheet" type="text/css" /> <title>天下博客</title> </head> <body> <div id="app"></div> <!--<script src="{{ asset('js/manifest.js') }}"></script> --> <!--<script src="{{ asset('js/vendor.js') }}"></script> --> <script src="{{ asset('js/app.js') }}"></script> </body> </html> |
10. 运行:
| cnpm run dev |
该命令默认会去执行根目录下的 webpack.mix.js,使用 Laravel 提供的 Laravel Mix 编译资源,并将编译好的资源放在根目录 public 文件夹下。
说明:前端编译工具有许多,比如 gulp、webpack 等等,Laravel 也为自己提供了开箱即用的编译工具,比如 Laravel5.3 及更早版本提供基于 gulp 的 Laravel Elixir 和从 Laravel5.4 开始提供基于 webpack 的 Laravel Mix,当然你也可以不使用官方提供的工具,自己去配置编译工具。这些编译工具的作用都是一样的,使用方法也大同小异。
11. 热部署运行(开发过程中建议使用该方式):
| npm run watch |
注:如遇到跨域问题,请修改 resources/assets/js/bootstrap.js 文件。
12.CSRF token not found 错误:
环境搭建完成后,访问项目,打开开发者模式,切换到 Console ,会看到报 CSRF token not found: 错误。
Laravel 为了避免应用遭到跨站请求伪造攻击(CSRF),自动为每一个有效用户会话生成一个 CSRF 令牌,该令牌用于验证授权用户和发起请求者是否是同一个人。
如遇到该错误,请修改 welcome.blade.php 文件,增加下面代码(前面其实已经加上了):
| <meta name="csrf-token" content="{{csrf_token()}}"> |
13.代码拆分优化:
代码拆分是将一些不经常变动的代码提取出来,以避免每次都要重新编译,如果你频繁更新应用的 JavaScript,需要考虑对 vendor 库进行提取和拆分,这样的话,一次修改应用代码不会影响 vendor.js 文件的缓存。Laravel Mix 的 extract 方法可以实现这样的功能:修改项目根目录下的 webpack.mix.js 文件:
mix.js('resources/assets/js/app.js', 'public/js') .sass('resources/assets/sass/app.scss', 'public/css') .extract(['vue','axios']); |
extract 方法接收包含所有库的数组或你想要提取到 vendor.js 文件的模块,使用上述代码作为示例,Mix 将会生成如下文件:
文件 | 说明 |
public/js/manifest.js | Webpack manifest runtime |
public/js/vendor.js | vendor 库 |
public/js/app.js | 应用代码 |
修改主入口文件 resources/view/welcome.blade.php :
<!DOCTYPE html> <html lang="{{ config('app.locale') }}"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="X-CSRF-TOKEN" content="{{csrf_token()}}"> <meta name="csrf-token" content="{{csrf_token()}}"> <link href="{{ asset('css/main.css') }}" rel="stylesheet" type="text/css" /> <title>天下博客</title> </head> <body> <div id="app"></div> <script src="{{ mix('js/manifest.js') }}"></script> <script src="{{ mix('js/vendor.js') }}"></script> <script src="{{ mix('js/app.js') }}"></script> </body> </html> |
注:全局的 mix 函数会根据 public/mix-manifest.json 中的路径去加载对应的文件,同时注意引入三个 js 文件的顺序,以避免出错。
说明:我们在前面使用 assets 函数,此处却改为 mix 函数,这两个函数的主要区别就是 assets 函数会直接使用所填路径去 public 文件夹下找文件,而 mix 函数会自动加载带 hash 值的前端资源。这是和 Laravel 前端资源的缓存刷新相关的。
重新执行命令,就可以了:
| npm run watch |
14.总结:
到目前为止,项目搭建完毕。前端编译工具使用基于 webpack 的 Laravel Mix,一般情况下,它可以满足大部分的需求,当然你也可以完全抛弃 Laravel Mix,配置自己的 webpack。如果前端需要其他 Vue 插件,直接安装引入即可。
15.本文全部代码,可以到 github 上获取:
https://github.com/EighthRoute520/blog.git
本教程详细介绍如何使用Laravel框架与Vue.js前端框架相结合,包括安装配置、组件创建、路由设置、前后端交互等内容。
317

被折叠的 条评论
为什么被折叠?



