Laravel+Vuetify 前后端分离项目中 Vue.js 的初始化
Vue.js 初始化
安装相关
webpack.mix.js
解决浏览器缓存问题,追加 .version() 方法;
const mix = require('laravel-mix');
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css').version();
安装 Vue 2、 Vuetify 和相关字体图标
npm install vue
npm install --save vuetify
npm install material-design-icons-iconfont -D
npm install @mdi/font -D
移除 Bootstrap
npm remove bootstrap
npm remove bootstrap-sass
接下来打开 resources/assets/js/app.js 移除对 resources/assets/js/bootstrap.js 文件的引用。
require('./bootstrap');
安装 Vue Router 和 Vuex
npm install vue-router --save-dev
npm install vuex --save-dev
初始化文件
touch resources/js/config.js \
resources/js/event-bus.js \
resources/js/routes.js \
resources/js/store.js \
resources/js/config.js
配置文件
resources/js/config.js
/**
* Defines the API route we are using.
*/
var api_url = '';
var app_url = '';
switch( process.env.NODE_ENV ){
case 'development':
api_url = 'http://www.hypercell.com/api/v1';
app_url = 'http://www.hypercell.com';
break;
case 'production':
api_url = 'http://www.hypercell.com/api/v1';
app_url = 'http://www.hypercell.com';
break;
}
export const SUPCELL_CONFIG = {
API_URL: api_url,
APP_URL: app_url,
};
事件总线文件
resources/js/event-bus.js
import Vue from 'vue';
export const EventBus = new Vue();
resources/js/routes.js 路由文件
/*
|-------------------------------------------------------------------------------
| routes.js
|-------------------------------------------------------------------------------
| Contains all of the routes for the application
*/
/**
* Imports Vue and VueRouter to extend with the routes.
*/
import Vue from 'vue'
import VueRouter from 'vue-router'
/**
* Extends Vue to use Vue Router
*/
Vue.use( VueRouter )
/**
* Makes a new VueRouter that we will use to run all of the routes for the app.
*/
import store from './store.js';
export default new VueRouter({
routes: [
{
path: '/',
name: 'Layout',
components: Vue.component( 'Layout', require( './pages/Layout' ) ),
},
]
});
Vuex 模块的起点文件
resources/js/store.js
/*
|-------------------------------------------------------------------------------
| VUEX store.js
|-------------------------------------------------------------------------------
| Builds the data store from all of the modules for the Supcell app.
*/
/**
* Adds the promise polyfill for IE 11
*/
require('es6-promise').polyfill();
/**
* Import Vue and Vuex
*/
import Vue from 'vue'
import Vuex from 'vuex'
/**
* Initializes Vuex on Vue.
*/
Vue.use( Vuex );
/**
* Export our data store.
*/
export default new Vuex.Store({
modules: {
}
});
初始化目录
mkdir -p resources/js/api \
resources/js/components \
resources/js/mixins \
resources/js/pages \
resources/js/modules \
resources/js/plugins \
resources/js/common/lang
-
resources/js/api 目录,用于存放前端 API 供路由调用
-
resources/js/components 目录,用于存放 Vue 组件
-
resources/js/mixins 目录,用于存放 mixins
-
resources/js/pages 目录,用来存放页面
-
resources/js/modules 目录,用于数据存储,Vuex 将数据分割到多个组件并存放到这个目录。
-
resources/js/plugins 目录,用于存放自定义的插件。
-
resources/js/common/lang 目录,用于存放语言包。
i18n 国际化
npm install vue-i18n
新建插件目录
mkdir -p resources/js/plugins/
新建 vue-i18n 插件
touch resources/js/plugins/vue-i18n.js
编辑 vue-i18n.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n);
//定义标识符
const i18n = new VueI18n({
locale: 'zh-CN', // 语言标识
//this.$i18n.locale // 通过切换locale的值来实现语言切换
messages: {
'zh-CN': require('../common/lang/cn'), // 中文语言包
'en': require('../common/lang/en') // 英文语言包
}
});
export {i18n}
新建语言包目录
mkdir -p resources/js/common/lang
touch resources/js/common/lang/en.js
touch resources/js/common/lang/cn.js
编辑语言包
resources/js/common/lang/cn.js
export const m ={
hello: '你好'
};
// resources/js/common/lang/en.js
export const m ={
hello: 'hello'
};
配置 app.js
window._ = require('lodash');
// try {
// window.$ = window.jQuery = require('jquery');
// } catch (e) {}
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/**
* Next we will register the CSRF Token as a common header with Axios so that
* all outgoing HTTP requests automatically have it attached. This is just
* a simple convenience so we don't have to attach every token manually.
*/
// 添加请求拦截器,在请求头中加token
axios.interceptors.request.use(
config => {
if (localStorage.getItem('Authorization')) {
config.headers.Authorization = localStorage.getItem('Authorization');
}
return config;
},
error => {
return Promise.reject(error);
});
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
import Vue from 'vue';
import router from './routes.js'
import store from './store.js'
// 引入vuetify
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css'
import 'material-design-icons-iconfont/dist/material-design-icons.css'
import '@mdi/font/css/materialdesignicons.css'
import {i18n} from './plugins/vue-i18n'
Vue.use(Vuetify);
new Vue({
//定义Vue绑定的根元素
el: '#app',
//将上面声明的路由器传递到根Vue实例
router,
store,
i18n,
//初始化Vuetify
vuetify: new Vuetify()
}).$mount('#app'); //将这个实例挂载到id=app的根元素上
编辑父模板 Layout.vue,其他模板继承此模板
touch resources/js/pages/Layout.vue
<template>
<v-app id="inspire">
<v-navigation-drawer
v-model="drawer"
:clipped="$vuetify.breakpoint.lgAndUp"
app
>
<v-list dense>
<template v-for="item in items">
<v-row
v-if="item.heading"
:key="item.heading"
align="center"
>
<v-col cols="6">
<v-subheader v-if="item.heading">
{{ item.heading }}
</v-subheader>
</v-col>
<v-col
cols="6"
class="text-center"
>
<a
href="#!"
class="body-2 black--text"
>EDIT</a>
</v-col>
</v-row>
<v-list-group
v-else-if="item.children"
:key="item.text"
v-model="item.model"
:prepend-icon="item.model ? item.icon : item['icon-alt']"
append-icon=""
>
<template v-slot:activator>
<v-list-item-content>
<v-list-item-title>
{{ item.text }}
</v-list-item-title>
</v-list-item-content>
</template>
<v-list-item
v-for="(child, i) in item.children"
:key="i"
link
>
<v-list-item-action v-if="child.icon">
<v-icon>{{ child.icon }}</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title>
{{ child.text }}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-group>
<v-list-item
v-else
:key="item.text"
link
>
<v-list-item-action>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title>
{{ item.text }}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</template>
</v-list>
</v-navigation-drawer>
<v-app-bar
:clipped-left="$vuetify.breakpoint.lgAndUp"
app
color="blue darken-3"
dark
>
<v-app-bar-nav-icon @click.stop="drawer = !drawer" />
<v-toolbar-title
style="width: 300px"
class="ml-0 pl-4"
>
<span class="hidden-sm-and-down">Google Contacts</span>
</v-toolbar-title>
<v-text-field
flat
solo-inverted
hide-details
prepend-inner-icon="mdi-magnify"
label="Search"
class="hidden-sm-and-down"
/>
<v-spacer />
<v-btn icon
@click="changeLang"
>
<v-icon>mdi-apps</v-icon>
</v-btn>
<v-btn icon>
<v-icon>mdi-bell</v-icon>
</v-btn>
<v-btn
icon
large
>
<v-avatar
size="32px"
item
>
<v-img
src="https://cdn.vuetifyjs.com/images/logos/logo.svg"
alt="Vuetify"
/></v-avatar>
</v-btn>
</v-app-bar>
<v-content>
<v-container
class="fill-height"
fluid
>
<v-row
align="center"
justify="center"
>
<v-tooltip right>
<template v-slot:activator="{ on }">
{{$t('m.hello')}}
</template>
<span>Source</span>
</v-tooltip>
</v-row>
</v-container>
</v-content>
<v-btn
bottom
color="pink"
dark
fab
fixed
right
@click="dialog = !dialog"
>
<v-icon>mdi-plus</v-icon>
</v-btn>
<v-dialog
v-model="dialog"
width="800px"
>
<v-card>
<v-card-title class="grey darken-2">
Create contact
</v-card-title>
<v-container>
<v-row class="mx-2">
<v-col
class="align-center justify-space-between"
cols="12"
>
<v-row
align="center"
class="mr-0"
>
<v-avatar
size="40px"
class="mx-3"
>
<img
src="//ssl.gstatic.com/s2/oz/images/sge/grey_silhouette.png"
alt=""
>
</v-avatar>
<v-text-field
placeholder="Name"
/>
</v-row>
</v-col>
<v-col cols="6">
<v-text-field
prepend-icon="business"
placeholder="Company"
/>
</v-col>
<v-col cols="6">
<v-text-field
placeholder="Job title"
/>
</v-col>
<v-col cols="12">
<v-text-field
prepend-icon="mail"
placeholder="Email"
/>
</v-col>
<v-col cols="12">
<v-text-field
type="tel"
prepend-icon="phone"
placeholder="(000) 000 - 0000"
/>
</v-col>
<v-col cols="12">
<v-text-field
prepend-icon="notes"
placeholder="Notes"
/>
</v-col>
</v-row>
</v-container>
<v-card-actions>
<v-btn
text
color="primary"
>More</v-btn>
<v-spacer />
<v-btn
text
color="primary"
@click="dialog = false"
>Cancel</v-btn>
<v-btn
text
@click="dialog = false"
>Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-app>
</template>
<script>
export default {
props: {
source: String,
},
data: () => ({
dialog: false,
drawer: null,
items: [
{ icon: 'mdi-contacts', text: 'Contacts' },
{ icon: 'mdi-history', text: 'Frequently contacted' },
{ icon: 'mdi-content-copy', text: 'Duplicates' },
{
icon: 'mdi-chevron-up',
'icon-alt': 'mdi-chevron-down',
text: 'Labels',
model: true,
children: [
{ icon: 'mdi-plus', text: 'Create label' },
],
},
{
icon: 'mdi-chevron-up',
'icon-alt': 'mdi-chevron-down',
text: 'More',
model: false,
children: [
{ text: 'Import' },
{ text: 'Export' },
{ text: 'Print' },
{ text: 'Undo changes' },
{ text: 'Other contacts' },
],
},
{ icon: 'mdi-settings', text: 'Settings' },
{ icon: 'mdi-message', text: 'Send feedback' },
{ icon: 'mdi-help-circle', text: 'Help' },
{ icon: 'mdi-cellphone-link', text: 'App downloads' },
{ icon: 'mdi-keyboard', text: 'Go to the old version' },
],
}),
methods:{
changeLang(){
this.$i18n.locale = 'en'
}
}
}
</script>
此模板为 Vuetify 的预制布局,我新建了一个 changeLang() 方法,将英文输出转为中文输出,并且第 99 行给一个按钮绑定了点击事件,当用户点击按钮后将** changeLang() 方法,完成语言的切换。
编译资源
npm run dev
浏览器输入我们的项目地址:http://www.hypercell.com 查看运行结果:
Vuetify 首秀 !
点击右上角的魔方图标,屏幕中间的 “你好” 就变成了 “hello” ,说明我们的 Vue-i18n 国际化也配置成功了。
版本提交
到此完成了项目的初始化工作,以后的工作就是在此基础上添枝加叶。
git add -A
git commit -m "初始化 Vue.js"