Vue开始使用2.x
vue-cli使用
什么是vue-cli
vue-cli官方提供的一个脚手架,用于快速生成一个vue的项目模板; 预先定义好的目录结构及基础代码,就好比咱们在创建Maven项目时可以选择创建一个骨架项目,这个估计项目就是脚手架,我们的开发更加的快速; 项目的功能
- 统一的目录结构
- 本地调试
- 热部署
- 单元测试
- 集成打包上线
需要的环境
- Node.js:http://nodejs.cn/download/ 安装就是无脑的下一步就好,安装在自己的环境目录下
- Git:https://git-scm.com/doenloads 镜像:https://npm.taobao.org/mirrors/git-for-windows/
确认nodejs安装成功:
- cmd下输入
node -v,查看是否能够正确打印出版本号即可! - cmd下输入
npm -v,查看是否能够正确打印出版本号即可! 这个npm,就是一个软件包管理工具,就和linux下的apt软件安装差不多! 安装Node.js淘宝镜像加速器(cnpm) 这样的话,下载会快很多~
# -g 就是全局安装
npm install cnpm -g
# 或使用如下语句解决npm速度慢的问题
npm install --registry=https://registry.npm.taobao.org
#安装webpack
cnpm install -g webpack
安装成功位置
C:\Users\111\AppData\Roaming\npm
安装vue-cli
cnpm install --global vue-cli
#测试是否安装成功#查看可以基于哪些模板创建vue应用程序,通常我们选择webpack
vue list

第一个vue-cli应用程序
或者:
安装淘宝镜像。cmd命令输入:
npm install -g cnpm –registry=https://registry.npm.taobao.org
安装webpack ,编译vue文件
cnpm install -g webpack
安装vue
npm install -g vue-cli
查看是否安装成功
vue -V
- 创建一个Vue项目,我们随便建立一个空的文件夹在电脑上,我这里在D盘下新建一个目录
D:\job\y1\vue\vue\vue-study
- 创建一个基于webpack模板的vue应用程序
#1、首先需要进入到对应的目录 cd D:\job\y1\vue\vue\vue-study
#2、这里的myvue是顶日名称,可以根据自己的需求起名
vue init webpack myvue
一路都选择no即可; 说明:
- Project name:项目名称,默认回车即可
- Project description:项目描述,默认回车即可
- Author:项目作者,默认回车即可
- Install vue-router:是否安装vue-router,选择n不安装(后期需要再手动添加)
- Use ESLint to lint your code:是否使用ESLint做代码检查,选择n不安装(后期需要再手动添加)
- Set up unit tests:单元测试相关,选择n不安装(后期需要再手动添加)
- Setupe2etests with Nightwatch:单元测试相关,选择n不安装(后期需要再手动添加)
- Should we run npm install for you after the,project has been created:创建完成后直接初始化,选择n,我们手动执行;运行结果!
(1)初始化并运行
打开cmd
cd myvue
npm install
npm run dev
执行完成后,目录多了很多依赖
当出现问题时,可以查看提示进行处理如下
webpack使用
什么是Webpack
本质上, webpack是一个现代JavaScript应用程序的静态模块打包器(module bundler) 。当webpack处理应用程序时, 它会递归地构建一个依赖关系图(dependency graph) , 其中包含应用程序需要的每个模块, 然后将所有这些模块打包成一个或多个bundle. Webpack是当下最热门的前端资源模块化管理和打包工具, 它可以将许多松散耦合的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分离,等到实际需要时再异步加载。通过loader转换, 任何形式的资源都可以当做模块, 比如Commons JS、AMD、ES 6、CSS、JSON、Coffee Script、LESS等; 伴随着移动互联网的大潮, 当今越来越多的网站已经从网页模式进化到了WebApp模式。它们运行在现代浏览器里, 使用HTML 5、CSS 3、ES 6等新的技术来开发丰富的功能, 网页已经不仅仅是完成浏览器的基本需求; WebApp通常是一个SPA(单页面应用) , 每一个视图通过异步的方式加载,这导致页面初始化和使用过程中会加载越来越多的JS代码,这给前端的开发流程和资源组织带来了巨大挑战。 前端开发和其他开发工作的主要区别,首先是前端基于多语言、多层次的编码和组织工作,其次前端产品的交付是基于浏览器的,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源,并且保证他们在浏览器端快速、优雅的加载和更新,就需要一个模块化系统,这个理想中的模块化系统是前端工程师多年来一直探索的难题。
模块化的演进
Script标签
<script src = "module1.js"></script>
<script src = "module2.js"></script>
<script src = "module3.js"></script>
这是最原始的JavaScript文件加载方式,如果把每一个文件看做是一个模块,那么他们的接口通常是暴露在全局作用域下,也就是定义在window对象中,不同模块的调用都是一个作用域。 这种原始的加载方式暴露了一些显而易见的弊端:
- 全局作用域下容易造成变量冲突
- 文件只能按照
<script>的书写顺序进行加载 - 开发人员必须主观解决模块和代码库的依赖关系
- 在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪
CommonsJS
服务器端的NodeJS遵循CommonsJS规范,该规范核心思想是允许模块通过require方法来同步加载所需依赖的其它模块,然后通过exports或module.exports来导出需要暴露的接口。
require("module");
require("../module.js");
export.doStuff = function(){};
module.exports = someValue;
优点:
- 服务器端模块便于重用
- NPM中已经有超过45万个可以使用的模块包
- 简单易用
缺点:
- 同步的模块加载方式不适合在浏览器环境中,同步意味着阻塞加载,浏览器资源是异步加载的
- 不能非阻塞的并行加载多个模块
实现:
- 服务端的NodeJS
- •Browserify,浏览器端的CommonsJS实现,可以使用NPM的模块,但是编译打包后的文件体积较大
- modules-webmake,类似Browserify,但不如Browserify灵活
- wreq,Browserify的前身
AMD
Asynchronous Module Definition规范其实主要一个主要接口define(id?,dependencies?,factory);它要在声明模块的时候指定所有的依赖dependencies,并且还要当做形参传到factory中,对于依赖的模块提前执行。
define("module",["dep1","dep2"],functian(d1,d2){
return someExportedValue;
});
require(["module","../file.js"],function(module,file){});
优点
- 适合在浏览器环境中异步加载模块
- 可以并行加载多个模块
缺点
- 提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不畅
- 不符合通用的模块化思维方式,是一种妥协的实现
实现
- RequireJS
- curl
CMD
Commons Module Definition规范和AMD很相似,尽保持简单,并与CommonsJS和NodeJS的Modules规范保持了很大的兼容性。
define(function(require,exports,module){
var $=require("jquery");
var Spinning = require("./spinning");
exports.doSomething = ...;
module.exports=...;
});
优点:
- 依赖就近,延迟执行
- 可以很容易在NodeJS中运行缺点
- 依赖SPM打包,模块的加载逻辑偏重
实现
- Sea.js
- coolie
ES6模块
EcmaScript 6标准增加了JavaScript语言层面的模块体系定义。ES 6模块的设计思想, 是尽量静态化, 使编译时就能确定模块的依赖关系, 以及输入和输出的变量。Commons JS和AMD模块,都只能在运行时确定这些东西。
import "jquery"
export function doStuff(){}
module "localModule"{}
优点
- 容易进行静态分析
- 面向未来的Ecma Script标准
缺点
- 原生浏览器端还没有实现该标准
- 全新的命令,新版的Node JS才支持
实现
- Babel
大家期望的模块 系统可以兼容多种模块风格, 尽量可以利用已有的代码, 不仅仅只是JavaScript模块化, 还有CSS、图片、字体等资源也需要模块化。
安装Webpack
WebPack是一款模块加载器兼打包工具, 它能把各种资源, 如JS、JSX、ES 6、SASS、LESS、图片等都作为模块来处理和使用。 安装:
npm install webpack -g
npm install webpack-cli -g
测试安装成功
webpack -vwebpack-cli -v

报webpack-dev-server not installed
注意:webpack-dev-server是一个独立的NPM包,你可以通过npm install webpack-dev-server来安装它.
配置
创建 webpack.config.js配置文件
- entry:入口文件, 指定Web Pack用哪个文件作为项目的入口
- output:输出, 指定WebPack把处理完成的文件放置到指定路径
- module:模块, 用于处理各种类型的文件
- plugins:插件, 如:热更新、代码重用等
- resolve:设置路径指向
- watch:监听, 用于设置文件改动后直接打包
module.exports = {
entry:"",
output:{
path:"",
filename:""
},
module:{
loaders:[
{test:/\.js$/,;\loade:""}
]
},
plugins:{},
resolve:{},
watch:true
}
直接运行webpack命令打包
使用webpack

- 创建项目
- 创建一个名为modules的目录,用于放置JS模块等资源文件
- 新建modules包,在modules下创建模块文件,如hello.js,用于编写JS模块相关代码
//暴露一个方法:sayHi
exports.sayHi = function(){
document.write("<div>Hello Webpack</div>");
}
在modules下创建一个名为main.js的入口文件,用于打包时设置entry属性
//require 导入一个模块,就可以调用这个模块中的方法了
var hello = require("./hello");
hello.sayHi();
在项目目录下创建webpack.config.js配置文件,使用webpack命令打包
module.exports = {
entry:"./modules/main.js",//用于打包的js
output:{
filename:"./js/bundle.js"//生成的js目录
}
}
在项目目录下创建HTML页面,如index.html,导入webpack打包后的JS文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--前端模块化-->
<script src="dist/js/bundle.js"></script>
</body>
</html>
在IDEA控制台中直接执行webpack;如果失败的话,就使用管理员权限运行即可!
# 参数--watch
用于监听变化 webpack --watch
vue-router路由
Vue Router是Vue.js官方的路由管理器。它和Vue.js的核心深度集成, 让构建单页面应用变得易如反掌。包含的功能有:
- 嵌套的路由/视图表
- 模块化的、基于组件的路由配置
- 路由参数、查询、通配符
- 基于Vue js过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的CSS class的链接
- HTML5 历史模式或hash模式, 在IE 9中自动降级
- 自定义的滚动行为
安装
基于第一个vue-cli进行测试学习; 先查看node modules中是否存在vue-router
vue-router是一个插件包, 所以我们还是需要用npm/cnpm来进行安装的。打开idea命令行工具,进入你的项目目录,输入下面命令。
npm install vue-router --save-dev
如果在一个模块化工程中使用它,必须要通过Vue.use()明确地安装路由功能:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter);
测试
1、先删除没有用的东西
2、components 目录下存放我们自己编写的组件
定义一个Content.vue 的组件
<template>
<h1>内容页</h1>
</template>
<script>
export default {
name: "Content"
}
</script>
<style scoped>
</style>
Main.vue组件
<template>
<h1>首页</h1>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped>
</style>
安装路由,在src目录下,新建一个文件夹:router,专门存放路由,配置路由index.js,如下
import Vue from'vue'
//导入路由插件
import Router from 'vue-router'
//导入上面定义的组件
import Content from '../components/Content'
import Main from '../components/Main'
//安装路由
Vue.use(Router) ;
//配置路由
export default new Router({
routes:[
{
//路由路径
path:'/content',
//路由名称
name:'content',
//跳转到组件
component:Content,
//children : [ //如果想嵌套子路由写
// {
//路由路径
// path:'/Work1',
//路由名称
// name:'/work1',
//跳转到组件
// component:Work1,
// }
// ]
},{
//路由路径
path:'/main',
//路由名称
name:'main',
//跳转到组件
component:Main
}
]
});
在main.js中配置路由
import Vue from 'vue'
import App from './App'
//导入上面创建的路由配置目录
import router from './router'//自动扫描里面的路由配置
//来关闭生产模式下给出的提示
Vue.config.productionTip = false
new Vue({
el: '#app',
//配置路由
router,
components: { App },
template: '<App/>'
})
在App.vue中使用路由
<template>
<div id="app">
<h1>vue-router</h1>
<!--
router-link:默认会被渲染成一个<a>标签,to属性为指定链接
router-view:用于渲染路由匹配到的组件
-->
<router-link to="/main">首页</router-link>
<router-link to="/content">内容</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
<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>
小小案例
App.vue
<template>
<div id="app">
<h1>Hello Word</h1>
<!--
引入InputText和TodoLIst组件标签
-->
<InputText></InputText>
<TodoLIst></TodoLIst>
</div>
</template>
<script>
//引入InputText和TodoLIst组件
import InputText from './components/InputText.vue'
import TodoLIst from "./components/TodoLIst";
export default {
name: 'App',
components: {
InputText:InputText,
TodoLIst:TodoLIst
}
}
</script>
<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>
InputText.vue
<template>
<div>
<label>
<!--
@keydown.enter="addItme":回车事件,鼠标回车时触发
v-model="dotoItme.name":用户输入的的字符,或字符串
-->
<input type="text" v-model="dotoItme.name" @keydown.enter="addItme"/>
</label>
</div>
</template>
<script>
export default {
name: "InputText",
data(){
return{
dotoItme:{id:null,name:""}
}
},
methods:{
addItme:function () {
// //触发父组件的事件,然夫组件新增
this.$bus.$emit("addtoItme",this.dotoItme)
}
}
}
</script>
<style scoped>
.in{
width: 500px;
height: 50px;
}
</style>
TodoItem.vue
<template>
<li>编号:{{toDoItme.id}} 名称:{{toDoItme.name}} <button @click="deletes(index)" type="button">删除</button></li>
</template>
<script>
export default {
name: "TodoItem",
props:["toDoItme","index"],//自定义属性名称
methods:{
deletes:function (index) {
//触发父组件的事件,然夫组件删除
this.$emit("deleteItme",index)
}
}
}
</script>
<style scoped>
</style>
TodoLIst.vue
<template>
<div>
<ul>
<!--
:toDoItme="itme",:index="index" :自定义组件名称:
@deleteItme="del(index)" :子类自定义的组件事件
:toDoItme="" :index="" 对应 props:["toDoItme","index"]
-->
<TodoItem :toDoItme="itme" :index="index" v-for="(itme,index) in toDoList" @deleteItme="del(index)"></TodoItem>
</ul>
</div>
</template>
<script>
import TodoItem from "./TodoItem";
export default {
name: "TodoLIst",
components:{
TodoItem:TodoItem
},
data(){
return {
toDoList:[
{id:1001,name:"aaaa"},
{id:1002,name:"bbb"},
{id:1003,name:"ccc"}
]
}
},
methods:{
//删除数据
del(index){
this.toDoList.splice(index,1)
}
},
mounted() {
//新增
this.$bus.$on("addtoItme",(item)=>{
//转成josn数据
const todoIteme2=JSON.parse(JSON.stringify(item))
//将json数据渲染上去
this.toDoList.unshift(todoIteme2)
})
}
}
</script>
<style scoped>
</style>
main.js
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
//定义一个空的vue对象用于传输数据
Vue.prototype.$bus=new Vue();
new Vue({
el: '#app',
components: { App },
template: '<App/>'
})
vue+elementUI
创建工程
创建一个名为hello-vue的工程vue init webpack hello-vue
安装依赖, 我们需要安装vue-router、element-ui、sass-loader和node-sass四个插件
#进入工程目录
cd hello-vue
#安装vue-routern
npm install vue-router --save-dev
#安装element-ui
npm i element-ui -S
#安装依赖
npm install
# 安装SASS加载器
cnpm install sass-loader node-sass --save-dev
#启功测试
npm run dev
Npm命令解释:
npm install moduleName:安装模块到项目目录下npm install -g moduleName:-g的意思是将模块安装到全局,具体安装到磁盘哪个位置要看npm config prefix的位置npm install -save moduleName:–save的意思是将模块安装到项目目录下, 并在package文件的dependencies节点写入依赖,-S为该命令的缩写npm install -save-dev moduleName:–save-dev的意思是将模块安装到项目目录下,并在package文件的devDependencies节点写入依赖,-D为该命令的缩写
创建登录页面
在源码目录中创建如下结构:
- assets:用于存放资源文件
- components:用于存放Vue功能组件
- views:用于存放Vue视图组件
- router:用于存放vue-router配置
创建首页视图,在views目录下创建一个名为Main.vue的视图组件:
<template>
<h1>首页</h1>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped>
</style>
*创建登录页视图在views目录下创建名为Login.vue的视图组件,其中el-的元素为ElementUI组件;
<template>
<div>
<el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box">
<h3 class="login-title">欢迎登录</h3>
<el-form-item label="账号" prop="username">
<el-input type="text" placeholder="请输入账号" v-model="form.username"/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" placeholder="请输入密码" v-model="form.password"/>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="onsubmit('loginForm')">登录</el-button>
</el-form-item>
</el-form>
<el-dialog title="温馨提示" width="30%">
<span>请输入账号和密码</span>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: "Login",
data(){
return{
form:{
username:'',
password:''
},
//表单验证,需要在 el-form-item 元素中增加prop属性
rules:{
username:[
{required:true,message:"账号不可为空",trigger:"blur"}
],
password:[
{required:true,message:"密码不可为空",tigger:"blur"}
]
},
//对话框显示和隐藏
dialogVisible:false
}
},
methods:{
onsubmit(formName){
//为表单绑定验证功能
this.$refs[formName].validate((valid)=>{
if(valid){
//使用vue-router路由到指定界面,该方式称为编程式导航
this.$router.push('/main');
}else{
this.dialogVisible=true;
return false;
}
});
}
}
}
</script>
<style type="text/css">
.login-box{
border:1px solid #DCDFE6;
width: 350px;
margin:180px auto;
padding: 35px 35px 15px 35px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow: 0 0 25px #909399;
}
</style>
创建路由,在router目录下创建一个名为index.js的vue-router路由配置文件
import Vue from "vue"
//导入路由router自定义名字
import Router from "vue-router"
//导入自定义组件
import Login from "../views/Login";
import Main from "../views/Main";
//使用路由
Vue.use(Router)
//配置
export default new Router({
routes:[
//登录页
{
//路由路径
path : "/main",
//跳转的路由组件
component : Main
},
{
path : "/login",
component : Login
}
]
})
APP.vue
<template>
<div id="app">
<!-- <router-link to="/login">登录</router-link>-->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
<style>
</style>
main.js
import Vue from 'vue'
import App from './App'
//导入创建路由的配置 router自定义名称
import router from './router'//自动扫描你面的路由
//引入ElementUI
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
Vue.use(router)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
ElementUI,
components: { App },
template: '<App/>'
})
测试:在浏览器打开 http://localhost:8080/#/login
vue-router传参
使用Restfull传参
{
//路由路径Restfull风格传一个名称和一个密码
path:'/About/:username/:pass',
//路由名称
name:'about',
//跳转到组件
component:About
}
<!--在前端通过this.$route.params.自定义的名称镜像取值-->
<div>
<h1>About</h1>
{{this.$route.params.username}}
{{this.$route.params.pass}}
</div>
通过?号传参
<!--通过路由?直接带参数明传参-->
<router-link to="/home?id=1001">主页</router-link>
<!--this.$route.query.进行取值-->
{{this.$route.query.id}}
编程时路由
除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。
想要导航到不同的 URL,可以使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL
当你点击 <router-link> 时,内部会调用这个方法,所以点击 <router-link :to="..."> 相当于调用 router.push(...)
| 声明式 | 编程式 |
|---|---|
<router-link to="..."> |
router.push(...) |
该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如
// 字符串路径
router.push('/users/eduardo')
// 带有路径的对象
router.push({ path: '/users/eduardo' })
// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })
// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })
// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })
示例:
<template>
<div>
<h1>Home</h1>
<router-link to="/work1">work1</router-link>
<button @click="handlerClick">work2</button>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "Home",
methods:{
handlerClick(){
//this.$router.push("/work2?name=哈哈哈")
如果想使用restfull就把 query改成params
this.$router.push({path:'/work2', query:{name:'eduardo'}})
}
}
}
</script>
<style scoped>
</style>
导航守卫
路由router.beforeEach 注册一个全局前置守卫
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中
每个守卫方法接收三个参数:
to: 即将要进入的目标from: 当前导航正要离开的路由next: 下一个组件(放行)
全局守位
const router=new Router({
routes:[
....
]
});
router.beforeEach((to, from,next) => {
console.log("to======>"+to.path);
console.log("from======>"+from.path);
const isLogin = true; //
if(to.path=="/"){
if(isLogin){
next();
}
return false; // 返回 false 以取消导航
}
return next();
});
export default router;路由独享的守卫
你可以直接在路由配置上定义 beforeEnter 守卫
new router({
routes:[
{
path:'/',
component: ()=>import("@/components/Home"),
beforeEnter: (to, from) => {
return false
},
children:[
{
path:'work1',
component: ()=>import("@/components/Work1"),
},
{
path:'work2',
component: ()=>import("@/components/Work2"),
}
]
},
beforeEnter 守卫 只在进入路由时触发,不会在 params、query 或 hash 改变时触发。例如,从 /users/2 进入到 /users/3 或者从 /users/2#info 进入到 /users/2#projects。它们只有在 从一个不同的 路由导航时,才会被触发。
组件内的守卫
export default {
name: "Home",
methods:{
work2(){
//this.$router.push("/work2")
//this.$router.push({path:"/work2"})
this.$router.push({ path: '/work2', query: { plan: 'private' } })
}
},
beforeRouteEnter(to, from) {
// 在渲染该组件的对应路由被验证前调用
// 不能获取组件实例 `this` , 因为当守卫执行时,组件实例还没被创建!
return false;
},
}
其他可用的配置 API
beforeRouteUpdate(to, from) { }
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /users/:id,在 /users/1 和 /users/2 之间跳转的时候,
// 由于会渲染同样的 UserDetails 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 this
beforeRouteLeave(to, from) { }
// 在导航离开渲染该组件的对应路由时调用
// 与 beforeRouteUpdate 一样,它可以访问组件实例 this
解决跨区问题
package com.ketai.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
*解决跨域问题
*/
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")//允许所有路径访问
.allowedOrigins("*")//允许所有原始请求
.allowedHeaders("*")//允许所有请求头
.allowedMethods("GET","POST","DELETE","HEAD","PUT","OPTIONS")//请求路径的方式
.maxAge(3600)//设置过期时间
// 暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
.exposedHeaders("Server","Content-Length", "Authorization",
"Access-Token",
"Access-Control-Allow-Origin",
"Access-Control-Allow-Credentials");
}
}
嵌套路由
嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成。
实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如:


在views下新建一个user文件夹,存放List.vue和ProFile.vue
创建用户信息组件,在 views/user 目录下创建一个名为 Profile.vue 的视图组件;
Profile.vue
<template>
<h1>个人信息</h1>
</template>
<script>
export default {
name: "UserProFile"
}
</script>
<style scoped>
</style>
在用户列表组件在 views/user 目录下创建一个名为 List.vue 的视图组件;
<template>
<h1>用户列表</h1>
</template>
<script>
export default {
name: "UserList"
}
</script>
<style scoped>
</style>
修改首页视图,我们修改 Main.vue 视图组件,此处使用了 ElementUI 布局容器组件,代码如下: Main.vue
<template>
<div>
<el-container>
<el-aside width="200px">
<el-menu :default-openeds="['1']">
<el-submenu index="1">
<template slot="title"><i class="el-icon-caret-right"></i>用户管理</template>
<el-menu-item-group>
<el-menu-item index="1-1">
<!--插入的地方-->
<router-link to="/user/profile">个人信息</router-link>
</el-menu-item>
<el-menu-item index="1-2">
<!--插入的地方-->
<router-link to="/user/list">用户列表</router-link>
</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="2">
<template slot="title"><i class="el-icon-caret-right"></i>内容管理</template>
<el-menu-item-group>
<el-menu-item index="2-1">分类管理</el-menu-item>
<el-menu-item index="2-2">内容列表</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="text-align: right; font-size: 12px">
<el-dropdown>
<i class="el-icon-setting" style="margin-right: 15px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<!--在这里展示视图-->
<router-view />
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped>
</style>
配置嵌套路由修改 router 目录下的 index.js 路由配置文件,使用children放入main中写入子模块,代码如下 index.js
import Vue from "vue"
//导入路由router自定义名字
import Router from "vue-router"
//导入自定义组件
import Login from "../views/Login";
import Main from "../views/Main";
//导入字模块
import List from "../views/user/List";
import ProFile from "../views/user/ProFile";
//使用路由
Vue.use(Router)
//配置
export default new Router({
routes:[
//登录页
{
//路由路径
path : "/main",
//跳转的路由组件
component : Main,
//写入子模块
children:[
{
//跳转的路径
path : "/user/profile",
//对应导包的名字
component: ProFile,
},{
path : "/user/list",
component: List
}
]
},
{
path : "/login",
component : Login
}
]
})
参数传递及重定向
修改Main.vue
<template>
<div>
<el-container>
<el-aside width="200px">
<el-menu :default-openeds="['1']">
<el-submenu index="1">
<template slot="title"><i class="el-icon-caret-right"></i>用户管理</template>
<el-menu-item-group>
<el-menu-item index="1-1">
<!--name-传组件名,params传递参数-->
<router-link :to="{name:'userProile',params:{id:1}}">个人信息</router-link>
</el-menu-item>
<el-menu-item index="1-2">
<!--插入的地方-->
<router-link to="/user/list">用户列表</router-link>
</el-menu-item>
<el-menu-item index="1-3">
<!--重定向-->
<router-link to="/goHome">跳回至首页</router-link>
</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="2">
<template slot="title"><i class="el-icon-caret-right"></i>内容管理</template>
<el-menu-item-group>
<el-menu-item index="2-1">分类管理</el-menu-item>
<el-menu-item index="2-2">内容列表</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="text-align: right; font-size: 12px">
<el-dropdown>
<i class="el-icon-setting" style="margin-right: 15px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<!--在这里展示视图-->
<router-view />
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped>
</style>
修改router下的index.js
import Vue from "vue"
//导入路由router自定义名字
import Router from "vue-router"
//导入自定义组件
import Login from "../views/Login";
import Main from "../views/Main";
//导入字模块
import List from "../views/user/List";
import ProFile from "../views/user/ProFile";
//使用路由
Vue.use(Router)
//配置
export default new Router({
routes:[
//登录页
{
//路由路径
path : "/main",
//跳转的路由组件
component : Main,
//写入子模块
children:[
{
//跳转的路径/:id接收main的参数
path : "/user/profile/:id",
name : "userProile",
//对应导包的名字
component: ProFile,
//设置为trueProFile的props['id']解耦
props:true
},{
path : "/user/list",
component: List
},
{
//重定向
path : "/goHome",
//至main页面
redirect:"/main"
}
]
},
{
path : "/login",
component : Login
}
]
})
修改ProFile.vue
<template>
<div>
<h1>个人信息</h1>
<!--不解耦-->
<p>{{$route.params.id }}</p>
<!--解耦-->
<p>{{id}}</p>
</div>
</template>
<script>
export default {
//对应上面的{{id}}
props:['id'],
name: "UserProFile"
}
</script>
<style scoped>
</style>
404与路由钩子
路由模式与 404
路由模式有两种
hash:路径带#符易。如http://localhost/#/login
history:路径不带#符号,如http://localhost/login
修改路由配置,代码如下:
export default new Router({
mode :'history',
routes:[
]
});
在views创建一个NotFound.vue视图组件
NotFound.vue
<template>
<div>
404 页面不存在,请重试
</div>
</template>
<script>
export default {
name: "NotFound"
}
</script>
<style scoped>
</style>
修改router配置index.js
import NotFound from '../views/NotFound'
{
path: '*',
component: NotFound
}
路由钩子与异步请求
beforeRouteEnter:在进入路由前执行 beforeRouteLeave:在离开路由前执行
在veiws包下的List.vue中写
export default {
name: "UserProfile",
beforeRouteEnter: (to, from, next) => {
console.log("准备进入个用户列表");
next();
},
beforeRouteLeave: (to, from, next) => {
console.log("准备离开个用户列表");
next();
}
}
参数说明: to:路由将要跳转的路径信息 from:路径跳转前的路径信息 next:路由的控制参数 next() 跳入下一个页面 next(’/path’) 改变路由的跳转方向,使其跳到另一个路由 next(false) 返回原来的页面 next((vm)=>{}) 仅在 beforeRouteEnter 中可用,vm 是组件实例
在钩子函数中使用异步请求
安装axios
cnpm install axios -s
main.js引用 Axios
//导入axios
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
准备数据 : 只有我们的 static 目录下的文件是可以被访问到的,所以我们就把静态文件放入该目录下。 数据和之前用的json数据一样 需要的去上述axios例子里,在static新建一个mock目录存放json数据、
{
"name": "Java",
"url": "https://blog.kuangstudy.com",
"page": 1,
"isNonProfit": true,
"address": {
"street": "含光门",
"city": "陕西西安",
"country": "中国"
},
"links": [
{
"name": "bilibili",
"url": "https://space.bilibili.com/95256449"
},
{
"name": "Java",
"url": "https://blog.yuanstudy.com"
},
{
"name": "百度",
"url": "https://www.baidu.com/"
}
]
}
.在 beforeRouteEnter 中进行异步请求
<template>
<h1>用户列表</h1>
</template>
<script>
export default {
name: "UserList",
//钩子函数 过滤器
beforeRouteEnter: (to, from, next) => {
//加载数据
console.log("准备进入个用户列表");
next(vm => {
//进入路由之前执行getData方法
vm.getData()
});
},
beforeRouteLeave: (to, from, next) => {
console.log("准备离开个用户列表");
next();
},
methods:{
getData:function () {
this.axios({
//请求方式
method:"get",
//json存放的路径
url:"http://localhost:8080/static/mock/data.json",
}).then(function (response) {
console.log(response)
})
}
}
}
</script>
<style scoped>
</style>
el-tabl,u-table列相同单元格合并
以下为U-TABLE展示
<u-table
ref="dataTable"
id="lyTable"
highlight-current-row
:span-method="spanMethod"
border
size="mini"
:data="dataTable"
style="width: 100%"
max-height="500px"
:header-cell-style="{
'text-align': 'center',
'color': 'white',
'height': '8',
'fontsize': '1.8rem'
}"
:cell-class-name="addTableRowAndColClass">
<u-table-column type="index" prop="xh" width="50" label="序号" fixed></u-table-column>
<u-table-column prop="DUTY_DEPT" label="责任部门" :show-overflow-tooltip="true" width="90" fixed></u-table-column>
<u-table-column prop="EQPT_NAME" label="设备名称" :show-overflow-tooltip="true" width="200" fixed></u-table-column>
<u-table-column prop="IND_NAME" label="对标指标名称" :show-overflow-tooltip="true" width="120" fixed></u-table-column>
<u-table-column prop="UINT" label="单位" :show-overflow-tooltip="true" width="90" fixed></u-table-column>
</u-table>
/**
* 单元格合并
* @param row
* @param column
* @param rowIndex
* @param columnIndex
* @returns {{colspan: number, rowspan: *}}
*/
spanMethod({ row, column, rowIndex, columnIndex }) {
// alert("aaaaaa")
// console.log('row', row, 'column', column, 'rowIndex', rowIndex, 'columnIndex', columnIndex);
//定义需要合并的列字段,有哪些列需要合并,就自定义添加字段即可
const fields = ['DUTY_DEPT']
// 当前行的数据
// console.log("column.property]",row[column.property])
const cellValue = row[column.property]
// 判断只合并定义字段的列数据dataTable
if (cellValue && fields.includes(column.property)) {
const prevRow = this.dataTable[rowIndex - 1] //上一行数据
let nextRow = this.dataTable[rowIndex + 1] //下一行数据
// 当上一行的数据等于当前行数据时,当前行单元格隐藏
if (prevRow && prevRow[column.property] === cellValue) {
return { rowspan: 0, colspan: 0 }
} else {
// 反之,则循环判断若下一行数据等于当前行数据,则当前行开始进行合并单元格
let countRowspan = 1 //用于合并计数多少单元格
while (nextRow && nextRow[column.property] === cellValue) {
nextRow = this.dataTable[++countRowspan + rowIndex]
}
if (countRowspan > 1) {
return { rowspan: countRowspan, colspan: 1 }
}
}
}
},
表格复制
<div style="position: absolute;left: 34rem;z-index: 10">
<img src="../VS/NY/image/lz00/copy.png" class="date_img" @click="copyTable('#lzzhfz')" alt=""/>
</div>
/**
* 表格复制
* @param sel
*/
copyTable(sel){
let html = document.querySelector(sel).outerHTML;
html = html.replace(/<!img.*?>/g, "");
html = html.replace(/white/g, "black");
html = html.replace(/16px/g, "16px");
html = html.replace(/18px/g, "16px");
html = html.replace(/<p.*?>/g, "");
html = html.replace(/<\/p>/g, "");
html = html.replace(/<div.*?>/g, "");
html = html.replace(/<\/div>/g, "");
html = html.replace(/class="[ A-Za-z0-9_-]*["]/g, "");
html = html.replace(/name="[ A-Za-z0-9_-]*["]/g, "");
html = html.replace(/colspan="1"/g, "");
html = html.replace(/rowspan="1"/g, "");
html = html.replace(/data-v-\w*[=]["]{2}/g, "");
if(navigator.clipboard){//解决浏览器兼容性和安全上下文等问题
navigator.clipboard.writeText(html)
.then(() => {
this.$message({
/*这是使用了element-UI的信息弹框*/ message: "复制成功!",
type: "success",
});
})
.catch(() => {
this.$message({
message: "复制失败,请手动选择复制!",
type: "error",
});
});
return;
}
//上面失效则走下面代码
const textArea = document.createElement("textarea");
textArea.value = html;
document.body.appendChild(textArea);
textArea.select();
document.execCommand("copy");
document.body.removeChild(textArea);
this.$message({
message: "复制成功!",
type: "success",
});
},
el-table数据量太大改成u-table形式进行展示
官方网站:http://www.umyui.com/umycomponent/installation
安装:
npm install umy-ui
CDN方式
<!--引入表格样式-->
<link rel="stylesheet" href="https://unpkg.com/umy-ui/lib/theme-chalk/index.css">
<!-- import Vue -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/umy-ui/lib/index.js"></script>
<!-- 真实项目不建议你直接引入 <script src="https://unpkg.com/umy-ui/lib/index.js"></script>-->
<!-- 这样去引如会直接下最新版本,如果你的项目打包发布了,然后遇见umy-ui大更新 你可能项目会报错。-->
<!--推荐你这样引入: https://unpkg.com/umy-ui$1.0.1/lib/index.js 加入版本号!-->
<!-- 这样去引如会直接下最新版本,如果你的项目打包发布了,然后遇见umy-ui大更新 你可能项目会报错。-->
<!--推荐你这样引入: https://unpkg.com/umy-ui@1.0.1/lib/index.js 加入版本号!-->
基础语法
<template>
<u-table
:data="tableData"
:border="false"
style="width: 100%">
<template slot="empty">
没有查询到符合条件的记录
</template>
<u-table-column
prop="name"
label="名字"
width="180">
</u-table-column>
<u-table-column
prop="sex"
label="性别"
width="180">
</u-table-column>
<u-table-column
prop="age"
label="年龄">
<template v-slot="scope">
<el-select v-model="scope.row.sex">
<el-option v-for="item in sexList" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</template>
</u-table-column>
</u-table>
</template>
<script>
export default {
data() {
return {
sexList: [
{ value: 1,label: '男' },
{ value: 2,label: '女'},
{ value: 3,label: '未知'}
],
tableData: [{
sex: '男',
name: '王小虎',
age: '15'
}, {
sex: '女',
name: '王小明',
age: '15'
}, {
sex: '女',
name: '王小丽',
age: '15'
}, {
sex: '未知',
name: '王小狗',
age: '15'
}]
}
}
}
</script>
或者是ux-grid
<ux-grid
use-virtual
ref="dataTable"
id="lyTable"
use-virtual
row-height="25"
:span-method="arraySpanMethod"
border
size="mini"
:data="dataTable"
style="width: 100%"
max-height="500px"
:header-cell-style="{
'text-align': 'center',
'color': 'white',
'height': '8',
'fontsize': '1.8rem'
}"
:cell-class-name="addTableRowAndColClass">
<ux-table-column type="index" field="xh" width="50" title="序号" fixed></ux-table-column>
<ux-table-column field="DUTY_DEPT" title="责任部门" :show-overflow-tooltip="true" width="90" fixed></ux-table-column>
<ux-table-column field="EQPT_NAME" title="设备名称" :show-overflow-tooltip="true" width="200" fixed></ux-table-column>
<ux-table-column field="IND_NAME" title="对标指标名称" :show-overflow-tooltip="true" width="120" fixed></ux-table-column>
<ux-table-column field="UINT" title="单位" :show-overflow-tooltip="true" width="90" fixed></ux-table-column>
</ux-grid>