目录
(1) 在Components目录下新建公共组件Header.Vue
一. Vue-CLI概述
1. Vue-CLI简介
Vue-CLI是一个官方推荐的 vue.js 项目开发脚手架,全称为Vue命令行接口。Vue-CLI通过几个简单的命令,将可以快速帮助我们构建一套完整、规范的Vue前端项目。看似笨拙的命令行工具却极大简化了我们的开发。
- vue-cli 3.x版本:提供了用户界面,产生了很多新特性,开发中还未普及。
- vue-cli 2.x版本:传统的命令行界面,开发使用广泛,本次使用2.x版本。
2. Vue-CLI优点
- 快速构建。vue-cli提供了一套完整的vue项目开发规范,使得项目开发灵活、方便
- 依赖管理。vue-cli通过npm管理各种js依赖,并能随时升级
- 打包方式。vue-cli通过webpack进行项目打包,带有合理的默认配置 ,并且支持es6->es5语法的自动转化。支持热部署。
二. Vue-CLI安装
1. 安装node.js
node.js是一个基于 Chrome V8 引擎的 JavaScript 运行环境,其使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。 Node 本质上是一个让 JavaScript 能运行在服务端的开发平台,即前端服务器。
我们在vue-cli中需要使用npm来管理js依赖,使用webpack来打包项目,这二者都是基于nodejs运行的,因此我们需要先安装nodejs环境。
1.1 下载node.js
- win7 : nodeJs到14.0.0+就不支持win7系统了,只能选择之前的版本,比较新的是13.14.0版本,Node.js Mirror
- win8+:直接安装最新版,下载 | Node.js 中文网
1.2 解压到文件夹
1.3 配置nodejs环境变量
在实际开发中,我们不能每次都手动启动node或者每次都必须进入nodejs目录下才能使用npm等node相关指令,因此我们需要配置nodejs的环境变量,让计算机随时识别/找到相关命令。
1.4 验证nodejs环境是否成功
在cmd任意位置,执行node -v指令即可。若出现版本信息,则表示安装并配置环境成功。
2. 配置npm
npm全称为node package mangager,即node依赖包管理器。npm是基于nodejs(内置工具)的一个用来管理前端所有JS依赖包的工具,类似于Java后端的maven仓库。它也通过坐标在远程仓库中下载、管理依赖,同时提供配置本地仓库,管理本地JS文件。
2.1 配置本地仓库
配置本地仓库缓存位置: npm config set cache "F:\nodeJs\noderep"
配置本地仓库全局位置: npm config set prefix "F:\nodeJs\noderep"
2.2 配置远程仓库下载镜像
使用淘宝镜像 : npm config set registry https://registry.npm.taobao.org
2.3 验证npm配置
执行npm config ls,观察配置列表
3. 配置Vue-CLI脚手架
3.1 安装CLI脚手架
# 1.Vue Cli官方网站
介绍 | Vue CLI# 2.安装vue Cli
npm install -g vue-cli (安装2.x版本)npm install -g @vue/cli (安装3.x版本)
3.2 配置本地仓库环境变量
npm将Vue-Cli安装到本地仓库,要想在任意地方使用CLI相关指令和操作,必须配置本地仓库的环境变量,让计算机识别指令。
3.3 测试Cli配置是否成功
执行 vue init 指令,若出现命令提示,则表示CLI脚手架安装和配置成功。
三. Vue-Cli项目构建与结构解析
1. 使用Cli构建项目
在项目目标创建目录下,执行指令 vue init webpack projectName 构建以projectName为名称的vue项目。
- vue init : cli项目创建指令
- webpack : 指定项目打包方式为webpack
- projectName : 项目名称
2. 项目运行
在项目根目录下,执行指令 npm run dev ,运行成功后,就可以通过浏览器访问项目了 http://localhost:8080 。
3. 项目结构解析
vue-cli脚手架搭建的项目具有自己的项目结构和开发规范,下面就项目结构进行解析。
1.build:webpack打包相关配置文件,推荐默认
2.config:vue基本配置文件,可修改项目port、host等基本属性,推荐默认
3.node_modules:用来存放和管理项目中所用到的所有依赖。由于文件较大,一般不作为项目传输内容,后面通过package.json构建
4.src:开发者的vue源代码存放文件[重点]
- assets: 存放所有的静态资源(css、img、mp3...)
- components:存放所有的开发组件
- router:配置项目路由管理器,以及路由规则
- App.vue:项目根组件
- main.js:入口文件,挂载唯一Vue实例
5.static:用来存放静态资源,但现已被 src/assets 代替
6.babelrc、editorconfig、postcssrc.js:隐藏文件,一些配置文件,将es6语法打包转化为es5
7.gitignore:git版本控制忽略文件配置
8.index.html:项目单页面应用入口,主页
9.package.json:项目管理依赖坐标文件,类似于pom.xml。可用于 npm install 重建node_modules
10.package-lock.json:对package.json加锁的锁文件
11.README.md:项目描述/说明文件
4. Src核心文件讲解
(1)index.html
index.html仅作为单页面应用的入口,挂载Vue实例。具体样式、功能等开发由组件完成,不再对index.html修改。即一般只定义一个空的根节点,在main.js里面定义的实例将挂载在根节点下,内容都通过vue组件来填充。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>secondvue</title>
</head>
<body>
<!--定义的Vue实例挂载节点-->
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
(2)根组件App.vue
根组件App直接挂载到Vue根实例上,默认渲染,不被router管理,是所有子组件的容器。一个Vue组件在Cli中以.vue为后缀,通常由三部分分离组成:模板(template)、js代码(script)、样式(style),说明如下:
- template:模板只能包含一个父节点,也就是说顶层的div只能有一个。而<router-view/>是子路由视图插槽,后面的组件页面都显示在此处。
- script:JS代码书写区。vue通常用es6来写,用export default导出,其下面可以包含数据data,生命周期(mounted等),方法(methods)等。
- style:样式通过style标签<style></style>包裹,默认是影响全局的。如需定义作用域只在该组件下起作用,需在标签上加scoped,即<style scoped></style>
<!--1.html标签模板-->
<template>
<div id="app">
<img src="./assets/logo.png">
<!--子组件展示-->
<router-view/>
</div>
</template>
<!--2.JavaScript代码-->
<script>
export default {
//定义名称,可以不要
name: 'App'
//data(){}
//methods:{}
//computed:{}
//... ...
}
</script>
<!--3.样式style(影响全局,所有子组件都遵循该样式)-->
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
(3)入口main.js
Vue-Cli为了方便解耦,将页面index.html、Vue实例、组件、路由等分离开来,不再在一个页面中进行开发,各个部分之间通过入口main.js联系起来, main.js为项目的入口文件,即单入口,主要是引入vue框架,根组件及路由设置,并且定义vue实例。
import Vue from 'vue' //引入Vue框架依赖
import App from './App' //引入根组件App.vue
import router from './router' //引入路由配置
// 关闭生产模式下给出的提示
Vue.config.productionTip = false
//定义唯一Vue实例
new Vue({
//绑定挂载到index.html中的元素下
el: '#app',
//绑定路由router
router,
//注册根组件 App.vue
components: { App },
//用根组件样式<App></App> 替换和覆盖掉 index.html中挂载的元素<div id="app"></div>
template: '<App/>'
})
(4)components/HelloWorld.vue组件
<!--组件 template模板-->
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<!--组件 js代码-->
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
<!--组件 style样式 scoped只对该组件有效-->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
(5)router/index.js路由配置
import Vue from 'vue' //引入vue框架依赖
import Router from 'vue-router' //引入router依赖
import HelloWorld from '@/components/HelloWorld' //引入组件HelloWorld
//使用路由依赖
Vue.use(Router)
export default new Router({
//配置路由信息
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
四. WebStorm Vue项目开发
1. 环境准备
下载安装WebStorm后,新建空文件夹(Empty Project),在下方的Terminal中,进行vue-cli指令开发即可。
2. 标准前端开发方式(以员工管理系统为例)
(1)项目目录结构
- assets:存放静态资源(img、mp3、css..)
- components:存放项目公共组件(提取出来的公共组件)
- router:项目路由配置
- utils:项目工具js(比如axios实例)
- views:项目各部分功能组件
- dept:部门相关的组件
- Index.vue:部门的主页组件
- emp:员工相关的组件
- Index.vue:员工的主页组件
- Index.vue:项目总主页组件
- Login.vue:登录组件
- 404.vue:错误提示组件
- App.vue:根组件
- main.js:主入口(挂载vue实例)
(2)路由配置router/index.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path:'/login',
name:'Login',
//component:Login
component:()=>import('../views/Login') //按需加载
},
{
path:'/',
redirect:'/index'
},
{
path:'/index',
name:'Index',
component:()=>import('../views/Index'),
//子路由
children:[
{
path:'emps',
name:'Emps',
component:()=>import('../views/emp/Index')
},
{
path:'depts',
name:'Depts',
component:()=>import('../views/dept/Index')
}
]
},
{
path:'*',
name:'Error',
component:()=>import('../views/404')
}
]
})
(3)工具 request.js
import axios from 'axios' //引入axios
//创建默认实例
const instance = axios.create({
baseURL:'http://localhost:8989',
timeout:5000
})
//设置响应拦截器,统一处理错误
instance.interceptors.response.use(response=>{
console.log('响应成功');
//注意:一定返回响应,不然会被拦截住不再下传!
return response;
},err=>{
//this.$router.push({name:'Error'});
console.log(this);
})
//注意:暴露instance实例对象,才能让其他文件可以使用!
export default instance;
(4)Login.vue登录组件
<template>
<div>
<h1>用户登录</h1>
<form action="">
用户名:<input type="text" v-model="admin.adminName"><br>
密码:<input type="password" v-model="admin.adminPassword"><br>
<button @click="login">登录</button>
</form>
</div>
</template>
<script>
import instance from "../utils/request";
export default {
name: "Login",
data(){
return {
admin:{}
}
},
methods:{
login(){
//发送axios请求
instance.post('/admin/login',this.admin).then(res=>{
console.log(res);
let result = res.data;
console.log(result);
if(result.success){
//保存用户信息,后期开发可以保存token,结合shiro进行权限管理
//注意:localStorage只能存储简单的键值对,要存储对象必须先转化为json格式字符串!
localStorage.setItem("loginAdmin",JSON.stringify(result.data));
//登录成功
this.$router.push({name:'Index'});
}else{
this.admin = {};
//展示错误提示信息
alert(result.msg);
}
});
}
}
}
</script>
<style scoped>
</style>
(5)Index.vue主页组件
<template>
<div>
<div>
<h1>进入员工管理系统首页<span v-if="isLogin">,欢迎:{{loginAdmin.adminName}}</span></h1>
<a href="javascript:;" @click="exit" v-if="isLogin">退出登录</a>
<router-link :to="{name:'Login'}" v-else>点我登录</router-link>
<hr>
</div>
<div v-if="isLogin">
<router-link :to="{name:'Emps'}">员工管理</router-link>
<router-link :to="{name:'Depts'}">部门管理</router-link>
<a href="javascript:;">用户管理</a>
<a href="javascript:;">订单管理</a>
</div>
<!--各组件 展示主页-->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "Index",
data(){
return {
loginAdmin:{}
}
},
methods:{
exit(){
localStorage.removeItem("loginAdmin");
this.loginAdmin = null;
}
},
created(){
//注意:将json字符串转化为json对象!
this.loginAdmin = JSON.parse(localStorage.getItem("loginAdmin"));
console.log(this.loginAdmin);
},
computed:{
isLogin(){
//判断是否为空对象
//return this.loginAdmin
return this.loginAdmin!=null
//return object.keys(this.loginAdmin).length!=0
}
}
}
</script>
<style scoped>
</style>
(6)emp/Index.vue 员工组件主页
<template>
<div>
<h1>员工管理列表</h1>
<table border="1" width="100%">
<tr class="aa">
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
<th>工资</th>
<th>操作</th>
</tr>
<tr v-for="(emp,index) in emps">
<td>{{emp.empId}}</td>
<td>{{emp.empName}}</td>
<td>{{emp.empAge}}</td>
<td>{{emp.empSalary}}</td>
<td><a href="javascript:;">删除</a> <a href="javascript:;" @click="getEmp(emp.empId)">修改</a></td>
</tr>
</table>
<hr>
<form action="">
姓名:<input type="text" v-model="emp.empName"><br>
年龄:<input type="text" v-model="emp.empAge"><br>
工资:<input type="text" v-model="emp.empSalary"><br>
<button @click="saveOrUpdate">保存|修改</button>
</form>
</div>
</template>
<script>
import instance from "../../utils/request";
export default {
name: "Index",
data(){
return {
emps:[],
emp:{}
}
},
methods:{
saveOrUpdate(){
},
getEmp(id){
instance.get('/emp/selectOne',{params:{id:id}}).then(res=>{
this.emp = res.data.data;
});
},
findAll(){
//获取员工列表
instance.get('/emp/selectAll').then(res=>{
let result = res.data;
this.emps = result.data;
console.log(this.emps);
});
}
},
created(){
//初始化员工列表
this.findAll();
}
}
</script>
<style scoped>
.aa{
background: chartreuse;
}
</style>
(7)后端ResultVo
package com.sdust.utils;
public class ResultVo {
//响应状态
private boolean success;
//响应消息
private String msg;
//携带对象
private Object data;
private ResultVo (boolean success,String msg,Object data){
this.success = success;
this.msg = msg;
this.data = data;
}
public static ResultVo success(String msg,Object data){
return new ResultVo(true,msg,data);
}
public static ResultVo error(String msg,Object data){
return new ResultVo(false,msg,data);
}
public static ResultVo error(String msg){
return new ResultVo(false,msg,null);
}
//注意:必须设置set get方法,fastJson才能使用get方法构建json对象!
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
3. 抽取公共组件
- 定义:在Vue项目开发时,有时很多页面都有一个共同的部分(比如页面头、页面侧边栏、页面底部等等),为了减少代码冗余,简化开发,我们可以把公共部分抽取出来进行复用,这就是Vue的公共组件抽取。
- 举例:在上面例子中,Index主页面中的标题、登录和导航栏部分是一些页面共有的,因此我们可以把这部分抽取出来做成公共组件。
(1) 在Components目录下新建公共组件Header.Vue
Components目录专门用来存放公共组件Vue。注意公共组件也是一个Vue组件,应该包括完整的组件样式、组件相关数据、JS代码等信息!
<template>
<!--1.抽取公共组件部分 [样式]-->
<div>
<div>
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<h1 class="text-center text-info">欢迎进入员工管理系统首页 <span v-show="isLogin">欢迎: {{ admin.username }}</span></h1>
</div>
<div class="col-sm-12">
<div style="float: right">
<a href="javascript:;" @click="logout" v-show="isLogin">退出登录</a>
<router-link v-show="!isLogin" :to="{name:'Login'}">立即登录</router-link>
</div>
</div>
</div>
</div>
<div class="clearfix"></div>
<div style="text-align: center">
<a href="#/index/emps">员工管理</a>
<a href="#/index/depts">部门管理</a>
<a href="">用户管理</a>
<a href="">订单管理</a>
</div>
</div>
</div>
</template>
<script>
//2.抽取公共组件部分 [JS代码](包括数据、登录、登出验证)
import instance from "../utils/request";
export default {
name: "Header",
data() {
return {
admin: {}
}
},
methods: {
logout() {
let token = localStorage.getItem("token");
//根据token删除redis中用户登录标记
instance.delete("/token?token=" + token).then(res => {
//this.$router.push({path:'/'});//切换到主页
this.admin = {};
});
}
},
computed: {
isLogin() {
return this.admin.username;
},
},
created() {
//从localStorage获取token
let token = localStorage.getItem("token");
//发送axios 根据token获取用户信息
instance.get("/token?token=" + token).then(res => {
this.admin = res.data;
});
}
}
</script>
<style scoped>
</style>
(2)在页面中使用组件
在页面的<template/>样式内可以直接引用<Header></Header>标签来引用组件Header.Vue,Vue会自动将该公共组件注册为当前页面的Components来使用,并在运行打包时将内容插入到当前位置显示。
<template>
<div>
<!-- 1.使用公共组件Header -->
<Header></Header>
<!-- 显示员工组件 部门组件 用户组件 订单组件 子路由-->
<router-view></router-view>
</div>
</template>
<script>
//2.自动引入Header.Vue
import Header from "../components/Header";
export default {
name: "Index",
//3.自动注册Header.Vue为当前页面的components
components: {Header},
}
</script>
<style scoped>
</style>
五. VueX
1. 什么是VueX
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式器。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。简而言之,VueX用于解决Vue项目中不同组件之间的数据共享和动态管理问题,公共管理数据在任何一个组件中被修改,所有用到该数据的地方都会随之动态的发生改变。
2. VueX安装
npm install vuex --save
3. VueX配置
- 在src目录下新建store/index.js作为Vuex实例对象的配置文件
//1.引入外部依赖
import Vue from "vue"
import Vuex from "vuex"
//2.在Vue组件中全局声明Vuex
Vue.use(Vuex)
//3.构建Vuex实例对象,并暴露给外部访问(public)
export default new Vuex.Store({
//声明Vuex属性
})
//4.3等价于以下写法
//const store = new Vuex.Store({
// //声明Vuex属性
//})
//export default store;
- 向Vue根组件唯一实例中注册并挂载我们的Vuex对象,将Vuex注册为当前Vue实例的属性来使用 this.$store
import Vue from 'vue'
import App from './App'
import router from './router'
import store from "./store";//引入vuex实例对象
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
//给当前唯一的Vue实例注册Vuex对象
//这样我们就能通过 this.$store 来访问当前vue实例中的vuex对象及其属性了
store,
components: { App },
template: '<App/>'
})
4. Vuex属性
(1)state
state属性是Vuex的基本属性之一,用于存储Vuex实例对象中的共享数据/状态。其声明方式如下:
//引入依赖
import Vue from "vue"
import Vuex from "vuex"
//在Vue组件中声明Vuex
Vue.use(Vuex)
//构建Vuex实例对象,并暴露给外部(public)
export default new Vuex.Store({
//声明Vuex state属性
state:{
counter:0,
isChanged: false
}
})
那么怎么在其他组件中使用/获取我们的state共享数据的值呢?因为我们已经向Vue实例中注册了Vuex对象,所以我们可以直接通过 this.$store.state.属性名称 的方式来取值。
<template>
<div class="m-container">
<!--Vuex实例取值-->
<h2>{{blog.title}}----{{this.$store.state.counter}}</h2>
</div>
</template>
(2)mutations
VueX state中的属性值是不能直接进行修改的,如果我们想要动态修改state中的属性值该怎么办呢?Vuex给我们提供了 mutations 属性。更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件,它声明了一些回调函数对state进行修改,并且它会默认接受 state实例 作为第一个参数。
export default new Vuex.Store({
//声明Vuex state属性
state:{
counter:0,
isChanged: false
},
//声明Vuex mutations属性(默认第一个参数为state实例)
mutations:{
//counter++ 方法
increCounter(state){
state.counter++;
},
//counter--方法
decreCounter(state){
state.counter--;
}
}
})
那么怎么在其他组件中使用我们声明的mutation方法呢?和state一样,它也是Vuex对象的一个属性,但是我们需要通过一个 this.$store.commit('方法名称') 来触发这个方法事件。注意:触发事件时会自动将该Vuex实例对象的state属性作为方法的第一个默认参数传入,不用额外声明!
export default {
name: "BlogDetail",
data(){
return{
blog:{
title: "",
content: ""
}
}
},
methods: {
//触发Vuex修改事件 increCounter
clickIncre(){
this.$store.commit('increCounter')
}
}
}
可以向
store.commit
传入额外的参数供mutation调用,即 mutation 的载荷(payload)。传递参数有两种:
- 传递单个参数:直接传递。 this.$store.commit('方法名',11);
- 传递多个参数:封装为对象传递。this.$store.commit('方法名',{name:'小陈',count:10});
export default new Vuex.Store({
//声明Vuex state属性
state:{
counter:0,
isChanged: false
},
//声明Vuex mutations属性(默认第一个参数为state实例)
mutations:{
//counter++ 方法:传递单个参数
increCounter(state,cnt){
state.counter+=cnt;
},
//counter--方法:传递多个参数
decreCounter(state,data){
console.log("vuex:",data.name);
state.counter-=data.count;
}
}
})
//调用
this.$store.commit('increCounter',10);
this.$store.commit('decreCounter',{name:'小陈',count:10} );
(3)getters
getters属性允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算(多次使用只计算一次)。简而言之,它是用来定义对共享的数据的计算相关的一系列函数 相当于 computed 属性 会对结果进行缓存。
# 2.语法
getters:{
//平方
mathSqrts(state){
console.log("--------");
return state.counter*state.counter;
},
//乘以一个数字
mathSqrtsNumber(state,getters){
return getters.mathSqrts*3;
},
//传递参数
mathSqrtsNumbers(state,getters){
return function (number){
return number;
}
}
}
# 3.使用
- 1.{{$store.getters.mathSqrts}}
- 2.{{$store.getters.mathSqrtsNumber}}
- 3.{{$store.getters.mathSqrtsNumbers(3)}}