1.Laravel5.4 + vue2.0 + vue-router + Element2.3 的搭建(2018.05.06)

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>  
说明:该组件是为了使用 Element 导航栏。

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>  
说明:该组件是 Header 组件里路由的出口。

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>
说明:该组件是为了使用 Element 图片轮询展示。使用到的图片放在: public/images/myimg 下。

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>
说明:该组件是为了使用 Element 表格。里面涉及访问后台数据,后台逻辑后面介绍。

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>
说明:该组件是为了使用 Element 时间范围选取。

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>
说明:该组件是为了使用 Element 标签页切换。里面用到上面 Index、Bar、Role 三个组件。

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>  
注:该组件使用到一张图片文件为: public/images/myimg/404.jpg


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





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值