文章目录
一、入门概述
Vue是一套用于构建用户界面的渐进式JavaScript框架
特点:
轻量级:体积小
渐进式:不需要学完全部功能,用到什么学什么
响应式:数据更新之后,视图不需要刷新,会自动更新
学习成本低:基于HTML和js,官方文档都是中文
官方教程文档:https://cn.vuejs.org/v2/guide/
安装引入
Vue有两年环境版本
开发版本:
包含完整的警告和调试模式
本地下载:https://cn.vuejs.org/js/vue.js
生产版本:
删除了警告,33.30KB min+gzip
本地下载:https://cn.vuejs.org/js/vue.min.js
CDN引入:
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
二、基础语法
1、声明式渲染
示例:
<div id="app">{{message}}</div>
<script type="text/javascript">
var vm =new Vue({
el:"#app",
data:{
message:"hello Vue!"
}
})
</script>
页面显示结果:
hello Vue!
el叫做挂载点,对应的值是html元素的id。一个Vue应用会将其挂载到一个DOM元素上,即这个id对应的元素。挂载点支持css选择器,推荐使用id选择器。挂载点的DOM只支持双标签(和标签除外),不支持单标签。
data是渲染的时候动态的数据,当这些数据改变时,视图会进行重渲染(不需要手动刷新),也就是响应式。
在Vue中使用{{属性名}}
(插值)来获取data中的值,如上面代码中的{{message}}
对应的就是data中的message
2、条件渲染
指令:v-if
控制一个元素是否显示,使用指令。在Vue中,元素中V-前缀的特殊属性叫做指令。指令的职责是,当表达式的值改变时,将其产生的连带影响。
<div id="app">
<p v-if="seen">直接显示的内容</p>
<p v-else >默认隐藏</p>
</div>
var vm =new Vue({
el:"#app",
data:{
seen:true
}
})
浏览器显示:
直接显示的内容
指令:v-show
也是控制一个元素是否显示,不过改变的是样式(适合频繁切换的元素),而v-if
改变的是DOM树。
<div id="app">
<p v-show="seen">直接显示的内容</p>
</div>
<script type="text/javascript">
var vm =new Vue({
el:"#app",
data:{
seen:false
}
})
</script>
浏览器显示:
当seen为false改变了css样式
<p style="display: none;">直接显示的内容</p>
3、列表渲染
指令:v-for
显示的数据是一个数组
<div id="app">
<ul>
<li v-for="todo in todos">{{todo.text}}</li>
</ul>
</div>
<script type="text/javascript">
var vm =new Vue({
el:"#app",
data:{
todos:[{text:"爱学习,爱java"},{text: "java是世界上最好的语言"},{text: "day day up"}]
}
})
</script>
页面效果:
爱学习,爱java
java是世界上最好的语言
day day up
在控制台里,输入vm.todos.push({ text: '新的内容' })
,列表最后会添加新内容。
复杂一点的数据
<div id="app">
<ul>
<li v-for="goods in goodsList">商品名:{{goods.name}}<br>商品价格:{{goods.price}}</li>
</ul>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
goodsList: [{"id": 1, "name": "苹果", "price": 10},
{"id": 2, "name": "香蕉", "price": 12},
{"id": 3, "name": "梨", "price": 8}]
}
})
</script>
浏览器显示:
商品名:苹果
商品价格:10
商品名:香蕉
商品价格:12
商品名:梨
商品价格:8
获取索引可以使用v-for="(goods,index) in goodsList"
,其中index是当前元素的索引,{{index}}
可以获取到当前元素的索引
4、原始html
如果响应式的内容是HTML,就不能使用双大括号,需要使用v-html指令
<div id="app">
<ul>
<li>差值表达式支持输出的内容:{{rawHtml}}</li>
<li>指令v-html输出的内容:<span v-html="rawHtml"></span></li>
</ul>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
rawHtml:"<span><a href='#' style='color: red'>文字内容</a></span>"
}
})
</script>
浏览器显示:
差值表达式支持输出的内容:<span><a href=’#’ style=‘color: red’>文字内容</a></span>`
指令v-html输出的内容:文字内容
5、表达式
对于所有的数据绑定,Vue 都提供了完全的JavaScript 表达式支持。每个绑定都只能包含单个表达式
<div id="app">
<ul>
<li>{{!seen?"false":"true"}}</li>
<li>{{num+1}}</li>
<li>{{text+",你来自哪里"}}</li>
</ul>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
seen:true,
num:1,
text:"你是谁"
}
})
</script>
浏览器显示:
true
2
你是谁,你来自哪里
6、绑定属性
指令:v-bind
缩写是::属性名
用于响应式地更新HTML属性
<div id="app">
<ul>
<li v-bind:class="id"></li>
<li :class="id"></li>
</ul>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
id:"top"
}
})
</script>
审查元素看元素节点属性:
<ul>
<li class="top"></li>
<li class="top"></li>
</ul>
7、绑定样式
可以传给v-bind:class
一个对象,以动态地切换class, 也可以传递数组,或者进行一些计算。
使用v-bind:style
绑定css样式。
<style type="text/css">
.active{
color: red;
}
.large-cls{
font-weight: bolder;
}
</style>
<div id="app">
<ul>
<li v-bind:class="{active:isActive}">文本内容1</li>
<!--静态和指令共存-->
<li :class="{active:isActive}" class="large-cls">文本内容2</li>
<!--绑定数组-->
<li :class="[activeClass,largeClass]">文本内容3</li>
<!--绑定对象-->
<li :class="classObj1">文本内容4</li>
<!--绑定计算的对象-->
<li :class="classObj2">文本内容5</li>
</ul>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
isActive:false,
activeClass:"active",
largeClass:"large-cls",
classObj1:{active:true}
},
computed:{
classObj2:function () {
return{
active:this.isActive,
'large-cls':!this.isActive//名字中有特殊符号,如“-”,用单引号
}
}
}
})
</script>
生成的静态class属性
<ul>
<li class="">文本内容1</li>
<li class="large-cls">文本内容2</li>
<li class="active large-cls">文本内容3</li>
<li class="active">文本内容4</li>
<li class="large-cls">文本内容5</li>
</ul>
8、绑定事件
指令:v-on:事件名
简写:@事件名
使用v-on绑定事件
<div id="app">
<ul>
<li >num={{num}} <button v-on:click="num+=1" >add 1</button></li>
<li ><button v-on:click="method1(str)">点击领红包</button></li>
<li ><button @click="method2">hello</button></li>
</ul>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
num:0,
str:"恭喜你获得500元"
},
methods:{
method1:function (str) {
alert(str)
},
method2:function () {
console.log("helllo vue")
}
}
})
</script>
显示效果:
9、表单数据
指令:v-model
在表单元素上创建双向数据绑定。v-model会忽略表单元素默认值,而将Vue实例的数据作为数据来源。
<div id="app">
<ul>
<li >用户名:<input type="text" name="userName" v-model="nameVal"></li>
<li >密码:<input type="password" name="password" v-model="passwordVal"></li>
<li >性别:<input type="radio" name="sex" value="1" v-model="sexVal">男<input type="radio" name="sex" value="2" v-model="sexVal">女</li>
<li >爱好:<input type="checkbox" name="hobby" value="1" v-model="hobbyVal">看书<input type="checkbox" name="hobby" value="2" v-model="hobbyVal">电影<input type="checkbox" name="hobby" value="3" v-model="hobbyVal">唱歌</li>
<li ><select name="city" v-model="sityVal">
<option value="1">深圳</option>
<option value="2">上海</option>
</select></li>
</ul>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
nameVal:"张三",
passwordVal:"a66666",
sexVal:1,
hobbyVal:[1,2],
sityVal:1
}
})
</script>
浏览器显示:
10、过滤器
<div id="app">
<ul>
<li >{{message|filter1}}</li>
<li >{{message|filter2('arg1', 'arg2')}}</li>
</ul>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
message:"Hello Vue"
},
filters:{
filter1:function (value) {
return value.toUpperCase();
},
filter2:function (value,arg1,arg2) {
return value+arg1+arg2;
}
}
})
</script>
浏览器显示:
HELLO VUE
Hello Vuearg1arg2
11、计算属性
对于复杂逻辑,需要使用计算属性。computed将不经常变化的计算结果进行缓存,以节约系统开销。
<div id="app">
<ul>
<li >{{getTime1}}</li>
<li >{{getTime2()}}</li>
</ul>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
computed:{
getTime1:function () {
return new Date();
}
},
methods:{
getTime2:function () {
return new Date();
}
}
})
</script>
methods和computed区别是:
methods需要带括号, computed不需要
computed计算的值会缓存起来,多次调用,其值不变:
12、声明组件
组件可复用的Vue实例。可以把组件看成小型的,独立的U模块。
一个组件必须有一个根节点
全局组件,在当前Vue实例中都可用,局部注册的组件只在父组件中可用。
父子组件data无法共享
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<comp2></comp2>
</div>
<script type="text/javascript">
//声明组件
Vue.component('button-counter', {
template:'<div>num={{num}} <button v-on:click="num+=1">add 1</button> <button v-on:click="method1(str)">点击领红包</button> <button @click="method2()">hello</button><innerChild></innerChild></div>',
methods: {
method1: function (str) {
alert(str)
},
method2: function () {
alert("hello vue")
}
},
data:function () {
return{
num:0,
str: "恭喜你获得500元"
}
},
components:{
innerChild:{
template:'<span>局部组件</span>'
}
}
})
Vue.component('comp2',{
template:'<button-counter></button-counter>'
})
var vm = new Vue({
el: "#app"
})
</script>
13、axios动态数据
引入文件
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="app">
<button @click="search">查询</button>
<ul><li v-for="data in dataList">{{data.id}}-{{data.name}}-{{data.price}}</li></ul>
</div>
<script type="text/javascript">
var vm= new Vue({
el:"#app",
data:{
dataList:[]
},
methods:{
search:function () {
axios.get("./data.json").then(res=>{
console.log(res.data)
this.dataList=res.data;
});
}
}
})
</script>
data.json
[{"id": 1, "name": "苹果", "price": 10},
{"id": 2, "name": "香蕉", "price": 12},
{"id": 3, "name": "梨", "price": 8}]
打开浏览器点击查询显示数据
三、组件详解
1、属性(父传子)
自定义属性props:组件的props中声明的属性
原生属性attrs:没有声明的属性,默认自动挂载到组件根元素上。
特殊属性class、style:挂载到组件根元素上,支持字符串、对象。数组等多种语法
属性可以通过数组的形式列出:
props:['title','commentIds','author']
但在正式开发的时候,为了系统的维护性,我们还是推荐使用如下形式,注意组件中的属性名,在html标签中并不是使用驼峰。如果使用的是数字类型或者其它动态值,需要使用v-bind:
<div id="app">
<!--数字和布尔绑定需要动态赋值,不然会报错,:prop-c="abc"会报错,字母动态赋值会被认为是方法或变量-->
<button-counter :prop-a="1" :prop-b="1" prop-c="abc" :prop-d="20" prop-f="success"></button-counter>
</div>
<script type="text/javascript">
//声明组件
Vue.component('button-counter', {
template: '<div>propA:{{propA}}<br/>propB:{{propB}}<br/>propC:{{propC}}<br/>propD:{{propD}}<br/>propE:{{propE}}<br/>propF:{{propF}}</div>',
//声明属性
props: {
//基础的类型检查('null'和、'undefined'会通过任何类型验证)
propA: Number,
//多个可能的类型
propB: [String, Number],
//必填的字符串
propC: {
type: String,
required: true
},
//带有默认值的数字类型
propD: {
type: Number,
default: 100
},
//带有默认值的对象类型
propE: {
type: Object,
//对象或数组默认值必须从一个工厂函数获取
default: function () {
return {
message:"hello Vue!"
}
}
},
propF:{
validator:function (value) {
//这个值必须匹配下列字符串的一个
return ['success','warning','danger'].indexOf(value)!==-1
}
}
}
})
var vm = new Vue({
el: "#app"
})
</script>
浏览器显示:
propA:1
propB:1
propC:abc
propD:20
propE:{ "message": "hello Vue!" }
propF:success
2、事件
普通事件:@click
、@change
等事件, 通过this.$emit( 'myEvent' )
触发(子传父)。
修饰符事件:@input.trim
、@click.stop
等, 自定义组件需要自行开发。
<div id="app">
<my-component></my-component>
</div>
<script type="text/javascript">
Vue.component('my-component',{
template: '<div @click="HandlerDivClick"><button @click="HandlerButClick">向上传递</button><button @click.stop="HandlerButClick">阻止冒泡</button></div>',
methods:{
HandlerDivClick:function () {
console.log("aaa")
},
HandlerButClick:function () {
console.log("bbb")
}
}
})
var vm=new Vue({
el:"#app"
})
</script>
浏览器显示:
父传子靠属性,子传父靠事件
<div id="app">
<!--不带括号默认传event,默认写法-->
<child @myevent="father"></child>
<!--带括号$event固定写法-->
<child @myevent="father($event)"></child>
</div>
<script type="text/javascript">
Vue.component('child', {
template: ` <div>
<button @click="callParent()">子传父</button>
</div>`,
data:function () {
return {
childName:"aaa"
}
},
methods: {
callParent: function () {
console.log("child")
this.$emit('myevent',this.childName)//子组件触发父组件的方法
}
}
})
var vm = new Vue({
el: "#app",
methods: {
father:function (e) {
console.log("I’m father:"+e)
}
}
})
</script>
控制台输出:
child
I’m father:aaa
3、插槽
在Vue中如果在自定义组件的标签内部写东西,默认是不显示的。使用slot
实现插槽功能,可以显示在组件中写的内容。
<div id="app">
<div-counter>默认不显示</div-counter>
</div>
<script type="text/javascript">
//声明组件
Vue.component('div-counter', {
template: ` <div>
<ul>
<slot></slot>
<li>这是内容1</li>
<li>这是内容1</li>
</ul>
</div>`,
})
var vm = new Vue({
el: "#app"
})
</script>
浏览器显示:
默认不显示
这是内容1
这是内容1
具名插槽
把内容插入到指定的插槽中,当插槽名都有名字,而插入的内容没有名字指向,则内容不会显示
<div id="app">
<div-counter><div slot="a">插槽a的内容</div><div slot="b">插槽b的内容</div></div-counter>
</div>
<script type="text/javascript">
//声明组件
Vue.component('div-counter', {
template: ` <div>
<ul>
<slot name="a"></slot>
<li>这是内容1</li>
<li>这是内容1</li>
<slot name="b"></slot>
</ul>
</div>`,
})
var vm = new Vue({
el: "#app"
})
</script>
浏览器显示:
插槽a的内容
这是内容1
这是内容1
插槽b的内容
4、中央事件组件
兄弟组件之间相互调用,可以使用中央事件组件
<div id="app">
<c1></c1>
<c2></c2>
</div>
<script type="text/javascript">
var bus=new Vue();
Vue.component('c1', {
template: `<div><input ref="msg">
<button @click="HandleClick">这是组件1</button>
</div>`,
methods:{
HandleClick:function () {
console.log(this.$refs.msg.value)
bus.$emit('myListener',this.$refs.msg.value)//发布携带的事件myListener
}
}
})
Vue.component('c2', {
template: `<div>
<button>这是组件2</button>
</div>`,
mounted:function () {
bus.$on('myListener',function (data) {//订阅事件
console.log('组件2接收的内容:'+data)
})
}
})
var vm = new Vue({
el: "#app"
})
</script>
控制台显示内容
5、动态组件
动态组件使用<component>
标签占位,使用is
属性指定具体替换为哪个组件。每次切换都会重新渲染组件。使用<keep-alive>
<div id="app">
<!--组件只被初始化一次-->
<keep-alive>
<component :is="nav"></component>
</keep-alive>
<!--点击改变nav值切换组件-->
<button @click="nav='home'">home</button>
<button @click="nav='list'">list</button>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
components:{
list:{
template:`<div>list<input></div>`,
mounted:function () {
console.log('mounted')//查看组件初始化次数
}
},
home:{
template: ` <div>home</div>
`
}
},
data:{
nav:"list"
}
})
</script>
浏览器显示:
四、生命周期
每个Vue实例在被创建时都要经过一系列的初始化过程,叫做生命周期钩子
<div id="app">
<button @click="message='content changed'" >{{ message }}</button>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
message : "Hello Vue!"
},
beforeCreate: function () {
console.group('beforeCreate 创建前状态===============》');
console.log("%c%s", "color:red" , "el : " + this.$el); //undefined
console.log("%c%s", "color:red","data : " + this.$data); //undefined
console.log("%c%s", "color:red","message: " + this.message)//undefined
},
created: function () {
console.group('created 创建完毕状态===============》');
console.log("%c%s", "color:red","el : " + this.$el); //undefined
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
beforeMount: function () {
console.group('beforeMount 挂载前状态===============》');
console.log("%c%s", "color:red","el : " + (this.$el)); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
mounted: function () {
console.group('mounted 挂载结束状态===============》');
console.log("%c%s", "color:red","el : " + this.$el); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
beforeUpdate: function () {
console.group('beforeUpdate 更新前状态===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
updated: function () {
console.group('updated 更新完成状态===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
beforeDestroy: function () {
console.group('beforeDestroy 销毁前状态===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
destroyed: function () {
console.group('destroyed 销毁完成状态===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message)
}
})
</script>
生命周期代码来源:https://segmentfault.com/a/1190000008010666
挂载结束前的状态
更新状态
五、脚手架Vue CLI
1、安装node.js
官网下载:https://nodejs.org/en/download/
安装版直接双击安装,解压版解压文件
解压后创建两个文件夹
配置环境变量,在系统变量下新建一个NODE_HOME变量,把新建的NODE_HOME添加到PATH中
cmd配置刚才新建的两个文件夹
执行命令
npm config set cache "E:\study\node-v12.18.2-win-x64\npm_cache"
npm config set prefix "E:\study\node-v12.18.2-win-x64\npm_global"
再添加环境变量
%NODE_PATH%\npm_global
安装cnmp,加快下载速度
npm install -g cnpm --registry=https://registry.npm.taobao.org
安装vue脚手架:
cnpm install -g @vue/cli
2、创建项目
创建一个项目路径,在该项目路径中执行vue create 名字,按上下箭头切换,选择手动配置Manually这个再回车进入下一步
空格选中需要安装的模块
后面几步选择,右侧有对应值
之后开始下载…
工程下载完成
按照提示信息,进入到项目内,执行命令
npm run serve
提示访问地址
根据提示访问地址,出现如下页面表示项目创建成功
idea中打开启动方式
idea中的npm工具启动
在工具中启动,默认端口8080,修改端口在Arguments使用-- --port 80
3、单文件组件
在App.vue写入如下内容:
单页面分为三部分:模板标签、js和css
<template>
<div id="app">
<div>
<input ref="msg">
<button @click="add()">添加</button>
<ul>
<li v-for="data in dataList" :key="data">{{data}}</li>
</ul>
</div>
</div>
</template>
<script>
export default {
// ()相当于:funtion()
data () {
return {
dataList: []
}
},
methods: {
add () {
console.log(this.$refs.msg.value)
this.dataList.push(this.$refs.msg.value)
}
}
}
</script>
<style>
ul,li{
list-style: none;
background-color: #ccc;
}
</style>
4、项目打包
使用nmp run lint
可以将代码格式化,符合ES6风格。
使用npm run build
可以将项目打包,打包成功后会有一个dist文件夹, 将这个文件夹中的内容给后端开发人员即可(emmm就是你自己咯)
5、axios反向代理
跨域:端口、协议或域名不一致
模块化开发需要在当前的工程下安装axios模块npm install axios ,在页面中引入axios模块,跨域的时候,axios的请求路径不写域名,交给代理实现
<template>
<div>
<ul>
<button @click="search">查询</button>
<li v-for="data in dataList">{{data.id}}-{{data.name}}</li>
</ul>
</div>
</template>
<script>
import Vue from 'vue'
import axios from 'axios'
export default {
data () {
return {
dataList: []
}
},
methods: {
search: function () {
axios.get('/tupes')
.then(res => {
this.dataList = res.data
})
}
}
}
</script>
axios跨域请求时会报错,可以请求到数据,但无法使用
这时需要配置反向代理,在项目根目录src下创建vue.config.js文件:
module.exports = {
devServer: {
proxy: {
'/types': {
target: 'http://localhost:8099',
changeOrigin: true
}
}
}
}
六、路由
1、一级路由
对於单页面应用,使用路由切换组件,先创建两个组件
<template>
<div>
<h1>用户</h1>
</div>
</template>
<script>
export default {}
</script>
在router文件夹下的index.js中配置路由信息。这个路径是可以自定义的,和main.js中的路径相对应
import Vue from 'vue'
import VueRouter from 'vue-router'
import User from '../views/User'
import Order from '../views/Order'
Vue.use(VueRouter)
const routes = [
{
path: '/user', // 对应页面中的#/user
component: User // 组件User
},
{
path: '/order',
component: Order
}
]
const router = new VueRouter({
routes
})
export default router
main.js中引入路由文件:此处创建项目会默认配置
在App.vue主页面中,需要展示组件的地方加上<router-view></ router-view>
<ul>
<li><a href="#/user">用户页面</a></li>
<li><a href="#/order">订单页面</a></li>
</ul>
<!--类似插槽,渲染切换页面的组件-->
<router-view></router-view>
访问路径: http://localhost:8080/#/order, 其中的/#/order就是路由的请求路径了
2、声明式导航
使用路由标签,可以管理路由连接和高亮样式
to:路径
tag:渲染成什么标签
activeClass:高亮的样式
<div>
<ul>
<router-link to="/user" tag="button" active-class="myActive">用户管理</router-link>
<router-link to="/order" tag="button" active-class="myActive">订单管理</router-link>
</ul>
<!--类似插槽,渲染切换页面的组件-->
<router-view></router-view>
</div>
<style>
.myActive{
color: #444;
background-color: coral;
}
button:hover{
cursor:pointer
}
</style>
浏览器显示:
3、重定向
路由中指定一个默认的地址,当访问路径没有匹配到组件的时候,会重定向到指定页面:
{
path: '*',
redirect: '/order'
}
4、二级路由
如果在子组件中更新部分内容们可以使用二级路由,如Order.vue中 :
<template>
<div>
<h1>订单页面</h1>
<ul>
<router-link to="paid" tag="button" active-class="myActive">已支付</router-link>
<router-link to="notpay" tag="button" active-class="myActive">未支付</router-link>
</ul>
<router-view></router-view>
</div>
</template>
<script>
export default {}
</script>
路由配置(在一级路由的节点上加children节点):
{
path: '/order',
component: Order,
children: [{
path: 'paid',
component: Paid
}, {
path: '/order/notpay',
component: NotPay
}, {
path: '', // 默认重定向
redirect: '/order/notpay'
}]
}
浏览器效果:
5、动态路由
如果路由中有动态的参数,可以进行如下配置:
发起请求的组件(Nopay.vue):
<template>
<div>
<h2>未支付订单</h2>
<ul>
<li v-for="num in dataList" :key="num" @click="changePage(num)">文章{{num}}</li>
</ul>
</div>
</template>
<script>
export default {
data () {
return {
dataList: ['1', '2', '3']
}
},
methods: {
changePage: function (id) {
console.log(id)
this.$router.push(`/order/detail/${id}.html`) // 传url动态参数
}
}
}
</script>
路由配置:
{
path: '/order/detail/:myid.html', // myid是动态参数
component: Detail
},
详情页面组件
<template>
<div>
<h3>订单{{num}}的详情内容</h3>
</div>
</template>
<script>
export default {
mounted () {
console.log(this.$route.params.myid) // 输出动态id
},
data () {
return {
num: this.$route.params.myid
}
}
}
</script>
浏览器显示:
6、命名路由
配置路由的时候设置一个name属性
{
name: 'od',
path: '/order/detail/:myid.html', // myid是动态参数
component: Detail
}
跳转的时候
this.$router.push({ name: 'od', params: { myid: id } })
7、history模式
在定义路由的地方加上mode: ’ history’可以去掉路径中的#:
const router = new VueRouter({
mode: 'history',
routes
})
这种模式会真的向后端发送一个请求, 后端需要配置:如果URL 匹配不到任何资源,则返回同一个index.html
8、路由守卫
全局解析守卫,定义在路由js里
使用路由守卫,相当于一个过滤器:
// 模拟用户没有登录
const auth = {
isLogin () {
return false
}
}
// to去哪个页面,form从哪个页面来,下一个页面
router.beforeEach((to, from, next) => {
console.log(to.path)
if (/\/order\/.*/.test(to.path)) { // 匹配正则路径,所有以order开头的页面
console.log('拦截')
if (auth.isLogin()) {
next()
} else {
next('/login')
}
} else { // 不需要登录的页面直接放行
next()
}
})
组件内的守卫,定义在组件内
<script>
// 模拟用户没有登录
const auth = {
isLogin () {
return false
}
}
export default {
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被confirm前调用
// 不能获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
if (auth.isLogin()) {
next()
} else {
next('/login')
}
}
}
</script>