1.安装手脚架
npm install -g @vue/cli
2.模板语法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<h1>hello,{{name}}</h1>
</div>
</body>
<script type="text/javascript">
//阻止生成生产提示
Vue.config.productionTip = false
//创建vue实例
const x = new Vue({
//el用于指定容器,容器和vue实例的关系的一一对应
el:"#root",
//存储数据
data:{
name:"你好"
}
})
</script>
</html>
3.数据绑定
- 单向绑定 bind 数据只能从data流向页面
- 双向绑定 v-model 数据不仅能从data流向页面,还能从页面流向data
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<h1>插值语法</h1>
<h2>hello,{{name}}</h2>
<h1>单向数据绑定</h1>
<input type="text" v-bind:value="str">
<!-- 简写 -->
<!-- <input type="text" v-bind:value="str"> -->
<h1>双向数据绑定</h1>
<input type="text" v-model:value="str">
<!-- 简写 -->
<!-- <input type="text" v-model="str"> -->
</div>
</body>
<script type="text/javascript">
//阻止生成生产提示
Vue.config.productionTip = false
//创建vue实例
const v = new Vue({
//el用于指定容器,容器和vue实例的关系的一一对应
el:"#root",
//存储数据
data:{
name:"你好",
str:''
}
})
console.log(v)
</script>
</html>
4.MVVM模型
- M:模型(model):对应data中的数据
- V:视图(View):模板
- Vm:视图模型(ViewModel):Vue实例对象
5.数据代理的实现
vue中的数据放在实例_data上
_data相当于下面例子中的number
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script type="text/javascript">
let number = 18
let person = {
name:'zhangsan',
sex:'男'
}
Object.defineProperty(person,'age',{
// value:18,
// enumerable:true,//是否可枚举,默认为false
// writable:true,//是否可被修改,默认为false
// configurable:true,//是否可被删除,默认为false
// 当有人读取age时调用get函数,且age的值为返回值
get(){
console.log('读取age')
return number
},
// 当有人修改age时会调用set函数且会收到修改值
set(value){
console.log('修改age')
number = value
}
})
console.log(person)
console.log(Object.keys(person))
</script>
</html>
6.事件
6.1事件处理
-
使用v-on:xxx或@xxx绑定事件,其中xxx是事件名
-
事件的回调需要配置在methods对象中,最终会在vm上
-
methods中配置的函数,都是被Vue管理的函数,this的指向是vm或组件实例对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<h1>hello,{{name}}</h1>
<button @click="showInfo(123)">按钮1</button>
<button v-on:click="showInfo2(123,$event)">按钮2</button>
</div>
</body>
<script type="text/javascript">
//阻止生成生产提示
Vue.config.productionTip = false
//创建vue实例
const x = new Vue({
//el用于指定容器,容器和vue实例的关系的一一对应
el:"#root",
//存储数据
data:{
name:"你好"
},
methods: {
showInfo(number){
console.log(number);
},
showInfo2(number,event){
console.log(number,event)
}
},
})
</script>
</html>
6.2事件修饰符
-
prevent:阻止默认事件
-
stop:阻止事件冒泡
-
once:事件只触发一次
-
capture:使用事件的捕获模式
-
self:只有event.target是当前操作的元素时才触发事件
-
passive:事件的默认行为立即执行,无需等待事件回调执行完成
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<style>
.demo{
background: red;
}
.demo1{
margin-top: 20px;
margin-bottom: 20px;
}
</style>
<body>
<div id="root">
<h1>hello,{{name}}</h1>
<!-- 阻止默认事件 -->
<a href="https://www.baidu.com/" @click.prevent="func1">阻止默认事件</a>
<!-- 阻止事件冒泡 -->
<div class="demo" @click="func2">
<button class="demo1" @click.stop="func2">阻止事件冒泡</button>
</div>
<!-- 事件只触发一次 -->
<button @click.once="func3">事件只触发一次</button>
</div>
</body>
<script type="text/javascript">
//阻止生成生产提示
Vue.config.productionTip = false
//创建vue实例
const x = new Vue({
//el用于指定容器,容器和vue实例的关系的一一对应
el:"#root",
//存储数据
data:{
name:"你好"
},
methods: {
func1(){
alert('阻止默认事件');
},
func2(){
alert('阻止事件冒泡');
},
func3(){
alert('事件只触发一次');
}
},
})
</script>
</html>
6.3键盘事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<h1>hello,{{name}}</h1>
<input type="text" placeholder="按下回车提示输入" @keyup="showInfo">
</div>
</body>
<script type="text/javascript">
//阻止生成生产提示
Vue.config.productionTip = false
//创建vue实例
const x = new Vue({
el:"#root",
data:{
name:"你好"
},
methods: {
showInfo(event){
if(event.keyCode == 13){
console.log(event.target.value)
}
}
},
})
</script>
</html>
7.计算属性
- 要用的属性不存在,需要通过计算已有的属性得到
- 底层通过Object.defineProperty()实现
- 与methods实现相比,内部有缓存机制,效率更高
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<input type="text" placeholder="姓" v-model:value="firstName">
<input type="text" placeholder="名" v-model:value="lastName">
<p>全名:{{fullName}}</p>
<button @click="func">点我修改全名</button>
</div>
</body>
<script type="text/javascript">
//阻止生成生产提示
Vue.config.productionTip = false
//创建vue实例
const x = new Vue({
el:"#root",
data:{
firstName:'',
lastName:''
},
methods: {
func(){
this.fullName = 'j-zn';
}
},
computed:{
fullName:{
get(){
return this.firstName+this.lastName;
},
set(value){
console.log(value);
var arr = value.split('-');
this.firstName = arr[0];
this.lastName = arr[1];
}
}
// 简写
// fullName(){
// return this.firstName+this.lastName;
// }
}
})
</script>
</html>
8.监视属性
-
当被监视的属性变化时,回调函数自动调用
-
监视属性必须存在,才能进行监视
-
监视的两种写法:(1)new Vue时传入watch配置(2)$watch监视
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<h1>今天天气很{{info}}</h1>
<button @click="change">点我切换天气</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const x = new Vue({
el:"#root",
data:{
isHot:true
},
methods: {
change(){
this.isHot = !this.isHot;
}
},
computed:{
info(){
return this.isHot?'炎热':'凉爽';
}
},
watch:{
isHot:{
// immediate:true,// 初始化的时候调用一次handler
// deep:true,// 深度监视
handler(newValue,oldValue){
console.log('isHot',newValue,oldValue);
}
}
}
})
</script>
</html>
9.深度监视
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<h1>a:{{numbers.a}}</h1>
<button @click="add">点我a++</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const x = new Vue({
el:"#root",
data:{
numbers:{
a:0,
b:1
}
},
methods: {
add(){
this.numbers.a++;
}
},
watch:{
numbers:{
deep:true,
handler(newValue,oldValue){
console.log('numbers',newValue,oldValue);
}
}
}
})
</script>
</html>
10.绑定class
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<style>
.class1 {
width: 300px;
height: 300px;
background: red;
}
.class2 {
width: 300px;
height: 300px;
background: blue;
}
</style>
<body>
<div id="root" :class="str" @click="change">
</div>
</body>
<script type="text/javascript">
//阻止生成生产提示
Vue.config.productionTip = false
//创建vue实例
const x = new Vue({
//el用于指定容器,容器和vue实例的关系的一一对应
el:"#root",
//存储数据
data:{
str:"class1"
},
methods: {
change(){
this.str = this.str=='class1'? 'class2':'class1';
}
},
})
</script>
</html>
11.条件渲染
v-if
- 适用于切换频率较低的场景,不展示的DOM元素会被直接移除
- v-if,v-else-if,v-else一起使用时结构不能被打断
v-show
- 适用于切换频率较高的场景
- 不展示的DOM不会被移除,使用样式display:none隐藏
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<h1 v-if="n==0">0</h1>
<h1 v-else-if="n==1">1</h1>
<h1 v-else>2</h1>
<h1 v-show="isShow">show</h1>
<button @click="change">change</button>
<button @click="show">show</button>
</div>
</body>
<script type="text/javascript">
//阻止生成生产提示
Vue.config.productionTip = false
//创建vue实例
const x = new Vue({
//el用于指定容器,容器和vue实例的关系的一一对应
el:"#root",
//存储数据
data:{
n:0,
isShow:true
},
methods: {
change(){
this.n++;
if(this.n>=3){
this.n=0;
}
},
show(){
this.isShow = !this.isShow;
}
},
})
</script>
</html>
12.列表渲染
v-for中的key就可以是index,但最好是被遍历对象的唯一标识
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<!-- 遍历数组 -->
<ul>
<li v-for="(person,index) of persons" :key="person.id">
{{index}}:{{person.id}}-{{person.name}}-{{person.age}}
</li>
</ul>
<!-- 遍历对象 -->
<ul>
<li v-for="(value,key) of lrc" :key="key">
{{key}}:{{value}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const x = new Vue({
el:"#root",
data:{
persons:[
{id:1,name:'zhangsan',age:20},
{id:2,name:'lrc',age:22},
{id:3,name:'ryh',age:21},
],
lrc:{
id:0,
name:'黎汝聪',
age:20,
}
}
})
</script>
</html>
13.列表过滤
当一个对象为计算属性时。在其方法中,使用的变量发生改变了,就会触发这个计算函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<h1>人员列表</h1>
<input type="text" v-model:value="keyWord">
<!-- 遍历数组 -->
<ul>
<li v-for="(person,index) of filterPersons" :key="person.id">
{{index}}:{{person.id}}-{{person.name}}-{{person.age}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const x = new Vue({
el:"#root",
data:{
keyWord:'',
persons:[
{id:1,name:'张三',age:20},
{id:2,name:'李四',age:22},
{id:3,name:'王四',age:21},
{id:4,name:'张五',age:22},
{id:5,name:'王五',age:21},
],
},
computed:{
filterPersons(){
return this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord) !=-1;
})
}
}
})
</script>
</html>
14.更新(Vue.set())
Vue通过setter实现监控,且要在new Vue()时就传入要监测的数据
对象创建后追加的属性,默认不做响应式处理
如需给后添加的属性做响应式,可用Vue.set()
Vue.set(target,propertyName/index,value)
vm.$set(target,propertyName/index,value)
在Vue修改数组中的元素一定要用如下方法
push(),pop(),unshift(),shift(),splice(),sort(),reverse()这几个方法被Vue重写了
Vue.set和vm.$set不能给vm或vm的根数据(data)添加属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<h1>人员列表</h1>
<button @click.once="change">修改</button>
<!-- 遍历数组 -->
<ul>
<li v-for="(person,index) of persons" :key="person.id">
{{index}}:{{person.id}}-{{person.name}}-{{person.age}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
//阻止生成生产提示
Vue.config.productionTip = false
//创建vue实例
const x = new Vue({
el:"#root",
data:{
persons:[
{id:1,name:'张三',age:20},
{id:2,name:'李四',age:22},
{id:3,name:'王四',age:21},
{id:4,name:'张五',age:22},
{id:5,name:'王五',age:21},
],
},
methods: {
change(){
// this.persons[0] = {id:1,name:'zhangsan',age:20}; // 无效
this.$set(this.persons,0,{id:1,name:'zhangsan',age:20})
this.persons.push({id:6,name:'zhangsan',age:20})
}
},
})
</script>
</html>
15.收集表单数据
v-model默认收集value值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<form>
帐号:<input type="text" v-model="userInfo.account">
<br/>
密码:<input type="password" v-model="userInfo.password">
<br/>
男<input type="radio" name="sex" value="male" v-model="userInfo.sex">
女<input type="radio" name="sex" value="female" v-model="userInfo.sex">
<br/>
爱好:
学习<input type="checkbox" value="study" v-model="userInfo.hobby">
打游戏<input type="checkbox" value="game" v-model="userInfo.hobby">
吃饭<input type="checkbox" value="eat" v-model="userInfo.hobby">
<br/>
地址:
<select v-model="userInfo.address">
<option value="">请选择</option>
<option value="jdz">景德镇</option>
<option value="xian">西安</option>
</select>
<br/>
<button @click.prevent="submit">提交</button>
</form>
</div>
</body>
<script type="text/javascript">
//阻止生成生产提示
Vue.config.productionTip = false
const x = new Vue({
el:"#root",
data:{
userInfo:{
account:'',
password:'',
sex:'',
hobby:[],
address:'',
}
},
methods: {
submit(){
console.log(this.userInfo);
}
},
})
</script>
</html>
16.Vue内置指令
之前用过的指令
- v-bind 单向绑定解析表达式,可简写为:
- v-model 双向数据绑定
- v-for 遍历数组 / 对象 / 字符串
- v-on 绑定事件监听,可简写为@
- v-show 条件渲染 (动态控制节点是否展示)
- v-if 条件渲染(动态控制节点是否存存在)
- v-else-if 条件渲染(动态控制节点是否存存在)
- v-else 条件渲染(动态控制节点是否存存在)
16.1v-text
向其所在的节点中渲染文本内容,会替换节点中的内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<h1 v-text="name">hello</h1>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const x = new Vue({
el:"#root",
data:{
name:"你好"
}
})
</script>
</html>
16.2v-html
v-html会替换掉节点中所有的内容,可以识别html结构
在网站上动态渲染任意html是非常危险的,容易导致 XSS 攻击,不要用在用户提交的内容上
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<h1 v-html="str">hello</h1>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const x = new Vue({
el:"#root",
data:{
str:"<a href='https://www.baidu.com/'>百度</a>"
}
})
</script>
</html>
16.3v-cloak
本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
当网速过慢的时候不让未经解析的模板展示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<style>
[v-cloak] {
display:none;
}
</style>
<body>
<div id="root">
<h1 v-cloak>{{name}}</h1>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const x = new Vue({
el:"#root",
data:{
name:'你好'
}
})
</script>
</html>
16.4v-once
v-once所在节点在初次动态渲染后,就视为静态内容了,之后数据改变不会更新
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<style>
[v-cloak] {
display:none;
}
</style>
<body>
<div id="root">
<h2 v-once>初始化的n值是: {{n}}</h2>
<h2>当前的n值是: {{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const x = new Vue({
el:"#root",
data:{
n:1
}
})
</script>
</html>
16.5v-pre
跳过v-pre所在节点的编译过程
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<style>
[v-cloak] {
display:none;
}
</style>
<body>
<div id="root">
<h2 v-pre>当前的n值是: {{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const x = new Vue({
el:"#root",
data:{
n:1
}
})
</script>
</html>
17.生命周期
- Vue在关键时刻帮我们调用的一些特殊名称的函数,又名生命周期回调函数、生命周期函数、生命周期钩子
- 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
- 生命周期函数中的 this 指向是vm或组件实例对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<h2 :style="{opacity}">欢迎学习Vue</h2>
<button @click="stop">点我停止变换</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root',
data: {
opacity: 1
},
methods: {
stop() {
this.$destroy()
}
},
// 数据代理还未开始,无法访问data和methods
beforeCreate() {
console.log('beforeCreate');
},
// 可以访问data和methods
created() {
console.log('created');
},
// 页面呈现的是未经Vue编译的DOM结构,此时对DOM的操作最终都不会奏效
beforeMount() {
console.log('beforeMount');
},
// Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
mounted() {
console.log('mounted')
this.timer = setInterval(() => {
this.opacity -= 0.01
if (this.opacity <= 0) {
this.opacity = 1
}
}, 16)
},
// 此时数据是新的,但页面是旧的
beforeUpdate() {
console.log('beforeUpdate');
},
// 此时数据和页面都是新的
updated() {
console.log('updated');
},
// 马上执行销毁过程,data、methods,指令等均可用,此时一般执行关闭定时器,取消订阅消息等操作
beforeDestroy() {
console.log('beforeDestroy');
clearInterval(this.timer);
},
destroyed() {
console.log('destroyed')
},
})
</script>
</html>
18.组件
组件名
一个单词组成
- 第一种写法(首字母小写):school
- 第二种写法(首字母大写):School
多个单词组成
-
第一种写法(kebab-case 命名):my-school
-
第二种写法(CamelCase 命名):MySchool(需要Vue脚手架支持)
可以使用name配置项指定组件在开发者工具中呈现的名字。
组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,而是 Vue.extend() 生成的。Vue 解析时会帮我们创建组件的实例对象,即Vue帮我们执行的new VueComponent(options)。每次调用Vue.extend,返回的都是一个全新的VueComponent,即不同组件是不同的对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1.html</title>
<!-- 引入vue -->
<script type="text/javascript" src="./js/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
</head>
<body>
<div id="root">
<h1>hello,{{name}}</h1>
<school></school>
<student></student>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
// 定义组件
const school = Vue.extend({
template:`
<div>
<h1>学校名称:{{name}}</h1>
<h1>学校地址:{{address}}</h1>
<button @click="show">show</button>
</div>`,
data(){
return{
name:'景德镇一中',
address:'新厂',
}
},
methods: {
show(){
alert(this.name);
}
},
});
const student = Vue.extend({
template:`
<div>
<h1>学生姓名:{{name}}</h1>
<h1>学生年龄:{{age}}</h1>
</div>`,
data(){
return{
name:'黎汝聪',
age:21,
}
}
});
// 全局注册组件
Vue.component('school',school);
const x = new Vue({
el:"#root",
data:{
name:"你好"
},
// 注册组件
components:{
student
}
})
</script>
</html>
19.脚手架
使用命令
vue create projectName
脚手架文件结构
.文件目录
├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ └── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
└── package-lock.json: 包版本控制文件
组件在脚手架中的使用
School.vue
<template>
<div>
<h1>学校名称:{{name}}</h1>
<h1>学校地址:{{address}}</h1>
<button @click="show">show</button>
</div>
</template>
<script>
export default {
data(){
return {
name:'景德镇一中',
address:'新厂',
}
},
methods: {
show(){
alert(this.name);
}
}
}
</script>
Student.vue
<template>
<div>
<h1>学生姓名:{{name}}</h1>
<h1>学生年龄:{{age}}</h1>
</div>
</template>
<script>
export default {
data(){
return{
name:'黎汝聪',
age:21,
}
}
}
</script>
App.vue
<template>
<div id="app">
<School></School>
<Student></Student>
</div>
</template>
<script>
import School from './components/School.vue';
import Student from './components/Student.vue';
export default {
name: 'App',
components: {
School,
Student
}
}
</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>
20.render函数
import Vue from 'vue’引入的是dist/vue.runtime.esm.js,只包含核心功能,没有模板解析器。因为vue.runtime.esm.js没有模板解析器,所以要使用render函数接收到的createElement函数去指定具体内容。
render: h => h(App)
// 完整形式
render(createElement){
return createElement(App);
}
21.vue.config.js配置
/* 引入打包分析插件 */
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
/* 引入压缩插件 但是导致项目启动的比较慢*/
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
let isProduction = process.env.NODE_ENV === 'production'; // 判断是否是生产环境
let isTest = process.env.NODE_ENV === 'test'; // 判断是否是测试环境
const path = require('path')
const webpack = require('webpack')
function resolve(dir) {
return path.join(__dirname, dir)
}
// 服务端ip -- Uat环境
const api = 'http://10.1.12.181:26341';
module.exports = {
/* 打包后的文件夹名字及路径 根目录下 */
outputDir: 'video',
/* 生产的项目名 可以用三元运算符*/
publicPath: process.env.NODE_ENV === 'production' ? '/video/' : '/videoDev/',
/* 保存时是否校验 */
lintOnSave: false,
/* 开发环境跨域情况的代理配置 */
devServer: {
// https: true, // 链接是否使用https访问 但可能在微信中不能打开(安全性问题) 不写此行配置默认 使用http协议打开
port: '31001', // 指定要监听请求的端口号
open: true, // 是否运行好直接在默认浏览器打开
inline: true, // 用于设置代码保存时是否自动刷新页面。
hot: true, // 用于设置代码保存时是否进行热更新(局部刷新,不刷新整个页面)
disableHostCheck: true, //可以被外网访问
/* 当出现编译器错误或警告时,在浏览器中显示全屏覆盖层。默认禁用。两种写法*/
// overlay: true, // 第一种
overlay: {
//第二种
warnings: false, //是否警告
errors: false,
},
/* 接口代理器设置 可以配置多个*/
proxy: {
'/backend': {
// 实际访问的服务器地址
target: api,
// 控制服务器收到的请求头中Host字段的值 host就是主机名加端口号 实际上就是欺骗后端服务器
changeOrigin: true,
// 是否启用websockets
ws: false,
// 重写请求路径 开头是/api的替换为 空串
pathRewrite: { '/api': '' },
},
},
},
/* css相关设置 */
css: {
extract: false, // 是否使用css分离插件 ExtractTextPlugin 开启extract后,组件样式以内部样式表的形式加载的, 打包的结果会多出一个 css 文件夹以及css 文件。
sourceMap: true, // 开启 CSS source maps?
/* 向所有 Sass/Less 样式传入共享的全局变量 */
loaderOptions: {
//注意:在 sass-loader v8 中,这个选项名是 "prependData" global.scss这里面定义的是一些全局变量
scss: {
prependData: '@import "~@/assets/scss/global.scss";',
},
},
},
/* webpack相关配置
*该对象将会被 webpack-merge 合并入最终的 webpack 配置
*/
configureWebpack: (config) => {
// 为生产环境修改配置...
if (process.env.NODE_ENV === 'production') {
// 打包可视化分析
config.plugins.push(new BundleAnalyzerPlugin());
/* js 压缩 */
config.plugins.push(new UglifyJsPlugin({
uglifyOptions: {
uglifyOptions: {
compress: {
drop_debugger: true,
drop_console: true // 生产环境自动删除console
},
warnings: false
},
sourceMap: false,
parallel: true // 使用多进程并行运行来提高构建速度。默认并发运行数:os.cpus().length - 1。
}
}))
} else {
// 为开发环境修改配置...
}
},
/* 对内部的 webpack 配置(比如修改、增加Loader选项)(链式操作) */
chainWebpack: (config) => {
//添加别名
config.resolve.alias
.set('@', resolve('src'))
.set('assets', resolve('src/assets'))
.set('components', resolve('src/components'))
.set('layout', resolve('src/layout'))
.set('base', resolve('src/base'))
.set('static', resolve('src/static'));
// 压缩图片
config.module
.rule('images')
/* 需要下载这个图片压缩loader cnpm install --save-dev image-webpack-loader */
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
mozjpeg: { progressive: true, quality: 65 },
optipng: { enabled: false },
pngquant: { quality: [0.8,0.9], speed: 4 },
gifsicle: { interlaced: false },
webp: { quality: 75 },
});
},
/* 第三方插件配置 */
pluginOptions: {
'process.env': {
NODE_ENV: '"development"',
},
// 我这里用的是 vue-cli-plugin-mock 插件;用来开发前期模拟后端的请求
// debug 为true时 vscode的控制台会打印接口日志
mock: { entry: './mock/index.js', debug: true },
},
};
22.ref
类似于id,应用在html标签上获取的是真实DOM元素,应用在组件上获取的是组件实例对象
获取组件
<template>
<div id="app">
<Student ref="student"></Student>
<button @click="show">打印组件</button>
</div>
</template>
<script>
import Student from './components/Student.vue';
export default {
name: 'App',
components: {
Student
},
methods:{
show(){
console.log(this.$refs.student);
}
}
}
</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>
获取真实DOM
<template>
<div>
<h1 ref="name">学生姓名:{{name}}</h1>
<h1>学生年龄:{{age}}</h1>
<button @click="show">打印DOM</button>
</div>
</template>
<script>
export default {
data(){
return{
name:'黎汝聪',
age:21,
}
},
methods:{
show(){
console.log(this.$refs.name);
}
}
}
</script>
<style>
</style>
23.props
让组件接收外部传过来的数据
props是只读的,且优先级高于data
<template>
<div>
<h1>学生姓名:{{name}}</h1>
<h1>学生年龄:{{age+1}}</h1>
</div>
</template>
<script>
export default {
data(){
return{
}
},
// 简单接收
// props:['name','age'],
// 限制类型
// props:{
// name:String,
// age:Number
// },
// 限制类型、必要性、默认值
props:{
name:{
type:String,
require:true,
default:'less'
},
age:{
type:Number,
require:false,
default:99
}
}
}
</script>
传参
<template>
<div id="app">
<Student name="黎汝聪" :age="18"></Student>
<Student name="菜妈"></Student>
</div>
</template>
<script>
import Student from './components/Student.vue';
export default {
name: 'App',
components: {
Student
}
}
</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>
24.本地存储
存储内容大小一般支持 5MB 左右,浏览器端通过Window.sessionStorage和Window.localStorage属性来实现本地存储机制。
-
xxxStorage.setItem(‘key’, ‘value’)该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值
-
xxxStorage.getItem(‘key’)该方法接受一个键名作为参数,返回键名对应的值
-
xxxStorage.removeItem(‘key’)该方法接受一个键名作为参数,并把该键名从存储中删除
-
xxxStorage.clear()该方法会清空存储中的所有数据
-
SessionStorage存储的内容会随着浏览器窗口关闭而消失
-
LocalStorage存储的内容,需要手动清除才会消失
-
xxxStorage.getItem(xxx)如果 xxx 对应的 value 获取不到,那么getItem()的返回值是null
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>localStorage</h2>
<button onclick="saveDate()">点我保存数据</button><br/>
<button onclick="readDate()">点我读数据</button><br/>
<button onclick="deleteDate()">点我删除数据</button><br/>
<button onclick="deleteAllDate()">点我清空数据</button><br/>
</body>
<script>
let person = {name:"黎汝聪",age:20}
function saveDate(){
localStorage.setItem('msg','localStorage')
localStorage.setItem('person',JSON.stringify(person))
}
function readDate(){
console.log(localStorage.getItem('msg'))
const person = localStorage.getItem('person')
console.log(JSON.parse(person))
}
function deleteDate(){
localStorage.removeItem('msg')
localStorage.removeItem('person')
}
function deleteAllDate(){
localStorage.clear()
}
</script>
</html>
25.自定义事件
一种组件间通信方式,适用于子组件传参数给父组件
- 第一种方式,在父组件中<Child @事件名=“方法”/>或<Demo v-on:事件名=“回调方法”/>
- 第二种方式,在父组件中this. r e f s . d e m o . refs.demo. refs.demo.on(‘事件名’,回调方法)
- 触发自定义事件this.$emit(‘事件名’,数据)
- 解绑自定义事件this.$off(‘事件名’)
- 注意:通过this. r e f s . x x x . refs.xxx. refs.xxx.on(‘事件名’,回调函数)绑定自定义事件时,回调函数要么配置在methods中,要么用箭头函数,否则 this 指向会出问题
App.vue
<template>
<div id="app">
<!-- 通过给子组件绑定一个自定义事件实现子组件给父组件传参 -->
<Student @send="printStudentName"></Student>
<!-- 通过ref绑定自定义事件实现子组件给父组件传参 -->
<Student ref="student"></Student>
<!-- 通过props传函数参数的方式实现子组件给父组件传参 -->
<School :printSchoolName='printSchoolName'></School>
</div>
</template>
<script>
import Student from './components/Student.vue';
import School from './components/School.vue';
export default {
name: 'App',
components: {
Student,
School
},
methods:{
printSchoolName(name){
console.log('Apps收到了schoolName:'+name);
},
printStudentName(name){
console.log('Apps收到了studentName:'+name);
}
},
mounted(){
// 绑定send事件和对应的回调函数
this.$refs.student.$on('send',this.printStudentName);
}
}
</script>
<style>
</style>
Student.vue
<template>
<div>
<h1>学生姓名:{{name}}</h1>
<h1>学生年龄:{{age}}</h1>
<button @click="send">send学生名</button>
</div>
</template>
<script>
export default {
data(){
return{
name:'黎汝聪',
age:21
}
},
methods:{
send(){
// 触发Student实例身上的send事件
this.$emit("send",this.name);
}
}
}
</script>
School.vue
<template>
<div>
<h1>学校名称:{{name}}</h1>
<h1>学校地址:{{address}}</h1>
<button @click="send">send学校名</button>
</div>
</template>
<script>
export default {
data(){
return {
name:'景德镇一中',
address:'新厂',
}
},
methods: {
send(){
this.printSchoolName(this.name);
}
},
props:['printSchoolName']
}
</script>
26.全局事件总线
一种可以在任意组件间通信的方式,本质上就是一个对象,它必须满足以下条件
- 所有的组件对象都必须能看见他
- 这个对象必须能够使用 o n on onemit$off方法去绑定、触发和解绑事件
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render(createElement){
return createElement(App);
},
beforeCreate(){
// 安装全局事件总线,$bus就是当前应用的vm
Vue.prototype.$bus = this
}
}).$mount('#app')
Student.vue(发送消息的组件)
<template>
<div>
<h1>学生姓名:{{name}}</h1>
<h1>学生年龄:{{age}}</h1>
<button @click="send">send学生名</button>
</div>
</template>
<script>
export default {
data(){
return{
name:'黎汝聪',
age:21
}
},
methods:{
send(){
// 触发事件
this.$bus.$emit('getStudentName',this.name);
}
},
mounted(){
}
}
</script>
School.vue(接收消息的组件)
<template>
<div>
<h1>学校名称:{{name}}</h1>
<h1>学校地址:{{address}}</h1>
</div>
</template>
<script>
export default {
data(){
return {
name:'景德镇一中',
address:'新厂',
}
},
methods: {
printStudentName(name){
console.log('studentName:',name);
}
},
mounted(){
// 绑定事件,接收学生名
this.$bus.$on('getStudentName',this.printStudentName);
},
beforeDestroy(){
// 销毁前解绑事件
this.$bus.$off('getStudentName');
}
}
</script>
27.消息订阅和发布的使用
一种可以在任意组件间通信的方式
安装pubsub-js
npm i pubsub-js
Student.vue(发布消息的组件)
<template>
<div>
<h1>学生姓名:{{name}}</h1>
<h1>学生年龄:{{age}}</h1>
<button @click="send">发布学生名</button>
</div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
data(){
return{
name:'黎汝聪',
age:21
}
},
methods:{
send(){
// 发布消息
pubsub.publish('getStudentName',this.name);
}
},
mounted(){
}
}
</script>
School.vue(接收消息的组件)
<template>
<div>
<h1>学校名称:{{name}}</h1>
<h1>学校地址:{{address}}</h1>
</div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
data(){
return {
name:'景德镇一中',
address:'新厂',
pubId:0
}
},
methods: {
printStudentName(messageName,name){
console.log('有人发布了消息');
console.log('messageId:',this.pubId);
console.log('messageName:',messageName);
console.log('studentName:',name);
}
},
mounted(){
// 订阅消息
this.pubId = pubsub.subscribe('getStudentName',this.printStudentName);
},
beforeDestroy(){
// 取消订阅
pubsub.unsubscribe(this.pubId);
}
}
</script>
28.$nextTick
- 这是一个生命周期钩子,this.$nextTick(回调函数)在下一次DOM更新结束后执行其指定的回调
- 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行
<template>
<div id="app">
<!-- <Student/>
<School/> -->
<h1>{{n}}</h1>
<button @click="add">n++</button>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
n:1
}
},
methods:{
add(){
if(this.n<5){
this.$nextTick(()=>{
console.log('update',this.n);
});
}
this.n++;
}
}
}
</script>
29.配置代理
vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
// 关闭语法检查
lintOnSave:false,
publicPath:'./',
devServer: {
port: 8080,
// proxy:'http://127.0.0.1:8001',
proxy:{
'/proxy':{
target:'http://127.0.0.1:8001',
pathRewrite:{'/proxy':'/proxy'},
// ws: true, //用于支持websocket,默认值为true
// changeOrigin: true //用于控制请求头中的host值,默认值为true,服务器收到的请求头中的host值为target中的值
}
}
},
})
App.vue
<template>
<div>
<button @click="get1">获取1</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'App',
data(){
return {
}
},
methods:{
get1(){
axios.get('http://localhost:8080/proxy/').then(response =>{
console.log(response.data);
},error=>{
console.log(error.message);
})
}
},
components: {
}
}
</script>
nodejs
const express = require("express");
const cors = require("cors");
const bodyParser = require("body-parser");
//创建web服务器
let app = express();
// app.use(cors());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
const port = 8001;
//设置监听端口和启动成功回调函数
app.listen(port, () => {
console.log('run at http://127.0.0.1:' + port);
});
//托管静态资源.可以访问public目录中的所有文件,路径中不含public
app.get("/proxy", function(req, res) {
//在命令行中查看传递过来的参数
console.log('1');
res.send("proxyTest");
});
30.插槽
让父组件向子组件中的指定位置插入html结构
30.1默认插槽
App.vue(父组件)
<template>
<div>
<Test>
<h1>你好</h1>
</Test>
<Test>
<button @click="show">点击弹窗</button>
</Test>
</div>
</template>
<script>
import Test from './components/Test.vue'
export default {
name: 'App',
data(){
return {
}
},
methods:{
show(){
alert('hello!');
}
},
components: {
Test
}
}
</script>
Test.vue(子组件)
<template>
<div>
<span>哈哈</span>
<slot></slot>
</div>
</template>
<script>
export default {
data(){
return {
isShow:true
}
},
methods:{
}
}
</script>
<style scoped>
h1{
background-color: orange;
}
</style>
30.2具名插槽
使用name指定填入的插槽
App.vue(父组件)
<template>
<div>
<Test>
<h1 slot="center">你好</h1>
<a href="https://www.baidu.com/" slot="footer">百度</a>
</Test>
</div>
</template>
<script>
import Test from './components/Test.vue'
export default {
name: 'App',
data(){
return {
}
},
methods:{
show(){
alert('hello!');
}
},
components: {
Test
}
}
</script>
Test.vue(子组件)
<template>
<div>
<span>哈哈</span>
<slot name="center"></slot>
<slot name="footer"></slot>
</div>
</template>
<script>
export default {
data(){
return {
isShow:true
}
},
methods:{
}
}
</script>
<style scoped>
h1{
background-color: orange;
}
</style>
30.3作用域插槽
将插槽所在组件的数据传给插槽使用者
App.vue(父组件)
<template>
<div>
<Test>
<template slot-scope="data">
<ul>
<li v-for="(student,index) of data.students" :key="index">{{student}}</li>
</ul>
</template>
</Test>
</div>
</template>
<script>
import Test from './components/Test.vue'
export default {
name: 'App',
data(){
return {
}
},
methods:{
},
components: {
Test
}
}
</script>
Test.vue(子组件)
<template>
<div>
<slot :students="students"></slot>
</div>
</template>
<script>
export default {
data(){
return {
students:['黎汝聪','刘人恺','饶以恒','饶伟']
}
},
methods:{
}
}
</script>
<style scoped>
h1{
background-color: orange;
}
</style>
31.Vuex
专门在Vuez实现集中式数据管理的一个Vue插件
安装vuex
vue2只能用vuex的3版本,vue3中只能用vuex的4版本
npm install vuex@3
使用vuex
main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store/index'
Vue.config.productionTip = false
new Vue({
store:store,
render(createElement){
return createElement(App);
}
}).$mount('#app')
创建store/index.js
// 该文件用于创建Vuexz最为核心的store
import Vue from 'vue'
// 引入vuex
import Vuex from 'vuex'
Vue.use(Vuex);
// 用于响应组件中的动作
const actions = {}
// 用于操作数据
const mutations = {}
// 用于存储数据
const state = {}
// 创建store
const store = new Vuex.Store({
actions:actions,
mutations:mutations,
state:state
})
export default store
App.vue
<template>
<div>
<h1>{{$store.state.number}}</h1>
<button @click="add">add</button>
<button @click="sub">sub</button>
</div>
</template>
<script>
import Test from './components/Test.vue'
export default {
name: 'App',
data(){
return {
}
},
methods:{
add(){
this.$store.dispatch('add',1);
},
sub(){
this.$store.commit('SUB',1);
}
},
mounted(){
console.log(this.$store)
}
}
</script>
32.路由
路由即一组映射关系,key为路径,value可能是function或component
安装
vue-router4只能在vue3中使用,vue-router3才能在vue2中使用
npm i vue-router@3
32.1基本使用
编写路由配置,src/router/index.js
// 该文件用于创建整个项目的路由
import VueRouter from 'vue-router'
import About from '../components/About'
import Home from '../components/Home'
import News from '../components/News'
import Message from '../components/Message'
export default new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/Home',
component:Home,
// 多级路由
children: [
{
path:'news',
component:News
},
{
path:'message',
component:Message
}
],
}
]
})
main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import router from './router'
Vue.use(VueRouter)
Vue.config.productionTip = false
new Vue({
render(createElement){
return createElement(App);
},
router:router
}).$mount('#app')
App.vue
<template>
<div>
<h1>Vue router demo</h1>
<div class="container clearfix">
<div class="left">
<router-link to="/about" active-class="active">About</router-link>
<router-link to="/home" active-class="active">Home</router-link>
</div>
<div class="right">
<!-- 指定组件的呈现位置 -->
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
}
},
methods:{
},
mounted(){
}
}
</script>
<style scoped>
.clearfix::before,
.clearfix::after {
content: "";
display: table;
clear: both;
}
.right{
float: left;
margin-left: 20px;
}
.left{
float: left;
border: #A3A4A3 1px solid;
padding: 0;
}
.left > a{
display: block;
text-decoration:none;
padding-top: 20px;
padding-bottom: 20px;
padding-left: 50px;
padding-right: 50px;
}
.left > a:hover,.active{
background: #2471BA;
color: #FFFFFF;
}
</style>
About.vue
<template>
<div>
<ul>
<li v-for="(student,index) of students" :key="index">{{student}}</li>
</ul>
</div>
</template>
<script>
export default {
data(){
return {
students:['黎汝聪','刘人恺','饶以恒','饶伟炎']
}
},
methods:{
}
}
</script>
<style scoped>
h1{
background-color: orange;
}
</style>
Home.vue
<template>
<div>
<h1>I'm Home!!!</h1>
<router-link to="/home/news" active-class="active">News</router-link>
<router-link to="/home/message" active-class="active">Message</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
}
</script>
<style>
a{
display: inline-block;
text-decoration:none;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 20px;
padding-right: 20px;
}
a:hover,.active{
background: #2471BA;
color: #FFFFFF;
}
</style>
News.vue
<template>
<div>
<ul>
<li>news001</li>
<li>news002</li>
<li>news003</li>
</ul>
</div>
</template>
<script>
export default {
}
</script>
Message.vue
<template>
<div>
<ul>
<li>message001</li>
<li>message002</li>
<li>message003</li>
</ul>
</div>
</template>
<script>
export default {
}
</script>
32.2注意事项
- 通过切换“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载
- 每个组件都有自己的$route属性,里面存储着自己的路由信息
- 整个应用只有一个router,可以通过组件的$router属性获
32.3跳转传参
32.3.1query传参
App.vue(父组件)
<template>
<div>
<h1>Vue router demo</h1>
<div class="container clearfix">
<div class="left">
<!-- 路由跳转 -->
<router-link :to="{
path:'/about',
query:{
data:queryData
},
}" active-class="active">About</router-link>
<router-link to="/home" active-class="active">Home</router-link>
</div>
<div class="right">
<!-- 指定组件的呈现位置 -->
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
queryData:'query参数',
}
},
methods:{
},
mounted(){
}
}
</script>
<style scoped>
.clearfix::before,
.clearfix::after {
content: "";
display: table;
clear: both;
}
.right{
float: left;
margin-left: 20px;
}
.left{
float: left;
border: #A3A4A3 1px solid;
padding: 0;
}
.left > a{
display: block;
text-decoration:none;
padding-top: 20px;
padding-bottom: 20px;
padding-left: 50px;
padding-right: 50px;
}
.left > a:hover,.active{
background: #2471BA;
color: #FFFFFF;
}
</style>
About.vue(子组件)
<template>
<div>
<h1>{{$route.query.data}}</h1>
</div>
</template>
<script>
export default {
data(){
return {
}
},
methods:{
},
mounted(){
console.log(this.$route);
}
}
</script>
32.3.2params传参
必须使用命名路由name,并在path中给参数占位
router/index.js
// 该文件用于创建整个项目的路由
import VueRouter from 'vue-router'
import About from '../components/About'
export default new VueRouter({
routes:[
{
name:'guanyu',
path:'/about/:data',
component:About
}
]
})
App.vue(父组件)
<template>
<div>
<h1>Vue router demo</h1>
<div class="container clearfix">
<div class="left">
<!-- 路由跳转 -->
<router-link :to="{
name:'guanyu',
params:{
data:paramData
}
}" active-class="active">About</router-link>
</div>
<div class="right">
<!-- 指定组件的呈现位置 -->
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
queryData:'query参数',
paramData:'params参数'
}
},
methods:{
},
mounted(){
}
}
</script>
<style scoped>
.clearfix::before,
.clearfix::after {
content: "";
display: table;
clear: both;
}
.right{
float: left;
margin-left: 20px;
}
.left{
float: left;
border: #A3A4A3 1px solid;
padding: 0;
}
.left > a{
display: block;
text-decoration:none;
padding-top: 20px;
padding-bottom: 20px;
padding-left: 50px;
padding-right: 50px;
}
.left > a:hover,.active{
background: #2471BA;
color: #FFFFFF;
}
</style>
About.vue(子组件)
<template>
<div>
<h1>{{$route.params.data}}</h1>
</div>
</template>
<script>
export default {
data(){
return {
}
},
methods:{
},
mounted(){
console.log(this.$route);
}
}
</script>
32.3.3props配置
router/index.js
// 该文件用于创建整个项目的路由
import VueRouter from 'vue-router'
import About from '../components/About'
export default new VueRouter({
routes:[
{
name:'guanyu',
path:'/about/:data',
component:About,
// 第一种写法,该对象中所有的key-value会通过props传给About组件
// props:{a:1,b:'lrc'}
// 第二种写法:props值为函数,该函数返回的对象中的key-value都会通过props传给Detail组件
// props($route){
// return {
// id: $route.query.id,
// title: $route.query.title
// }
// }
// 第三种写法,props为布尔值,当为true时,路由受到的所有参数都会通过props传给About组件
props:true
},
]
})
App.vue(父组件)
<template>
<div>
<h1>Vue router demo</h1>
<div class="container clearfix">
<div class="left">
<!-- 路由跳转 -->
<router-link :to="{
name:'guanyu',
params:{
data:paramData
}
}" active-class="active">About</router-link>
<router-link to="/home" active-class="active">Home</router-link>
</div>
<div class="right">
<!-- 指定组件的呈现位置 -->
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
paramData:'params参数'
}
},
methods:{
},
mounted(){
}
}
</script>
<style scoped>
.clearfix::before,
.clearfix::after {
content: "";
display: table;
clear: both;
}
.right{
float: left;
margin-left: 20px;
}
.left{
float: left;
border: #A3A4A3 1px solid;
padding: 0;
}
.left > a{
display: block;
text-decoration:none;
padding-top: 20px;
padding-bottom: 20px;
padding-left: 50px;
padding-right: 50px;
}
.left > a:hover,.active{
background: #2471BA;
color: #FFFFFF;
}
</style>
About.vue(子组件)
<template>
<div>
<h1>{{$route.params.data}}</h1>
<h1>{{data}}</h1>
</div>
</template>
<script>
export default {
data(){
return {
}
},
methods:{
},
mounted(){
console.log(this.$route);
},
props:['data']
}
</script>
32.4replace
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入方式:push和replace
- push是追加历史记录
- replace是替换当前记录,路由跳转时候默认为push方式
- 开启replace模式
<router-link :replace="true" ...>News</router-link>
简写
<router-link replace ...>News</router-link>
总结:浏览记录本质是一个栈,默认push,点开新页面就会在栈顶追加一个地址,后退,栈顶指针向下移动,改为replace就是不追加,而将栈顶地址替换
32.5编程式路由跳转
- 作用:不借助实现路由跳转,让路由跳转更加灵活
- this.$router.push({}) 内传的对象与中的to相同
- this.$router.replace({})
- this.$router.forward() 前进
- this.$router.back() 后退
- this.$router.go(n) 可前进也可后退,n为正数前进n,为负数后退
32.6缓存路由组件
让不展示的路由不被销毁
// 缓存一个路由组件
<keep-alive include="News"> // include中写想要缓存的组件名,不写表示全部缓存
<router-view></router-view>
</keep-alive>
// 缓存多个路由组件
<keep-alive :include="['News','Message']">
<router-view></router-view>
</keep-alive>
32.7路由守卫
32.7.1全局路由守卫
// 该文件用于创建整个项目的路由
import VueRouter from 'vue-router'
import About from '../components/About'
import Home from '../components/Home'
import News from '../components/News'
import Message from '../components/Message'
const router = new VueRouter({
routes:[
{
name:'guanyu',
path:'/about',
component:About,
meta:{isAuth:true,title:'关于'}
},
{
path:'/Home',
component:Home,
// 多级路由
children: [
{
path:'news',
component:News
},
{
path:'message',
component:Message
}
],
}
]
})
// 全局前置路由守卫,初始化的时候,每次路由切换前被调用
router.beforeEach((to, from, next) => {
console.log('全局前置路由守卫')
console.log('from',from);
console.log('to',to)
next();
})
// 全局前置路由守卫,初始化的时候,每次路由切换后被调用
router.afterEach((to,from)=>{
console.log('全局后置路由守卫')
console.log('from',from);
console.log('to',to)
// 修改页面标题
if(to.meta.isAuth){
document.title = to.meta.title;
}
})
export default router
32.7.2独享路由守卫
// 该文件用于创建整个项目的路由
import VueRouter from 'vue-router'
import About from '../components/About'
import Home from '../components/Home'
import News from '../components/News'
import Message from '../components/Message'
const router = new VueRouter({
routes:[
{
name:'guanyu',
path:'/about',
component:About,
meta:{isAuth:true,title:'关于'}
},
{
path:'/Home',
component:Home,
// 多级路由
children: [
{
path:'news',
component:News,
beforeEnter:(to,from,next)=>{
console.log('News独享前置路由守卫')
console.log('from',from);
console.log('to',to)
next();
},
},
{
path:'message',
component:Message
}
],
}
]
})
export default router
32.7.3组件内路由守卫
<template>
<div>
<ul>
<li>news001</li>
<li>news002</li>
<li>news003</li>
</ul>
</div>
</template>
<script>
export default {
// 通过路由规则进入组件前调用
beforeRouteEnter(to,from,next){
console.log('beforeRouteEnter');
console.log('to',to);
console.log('from',from)
next();
},
// 通过路由规则离开该组件时被调用
beforeRouteLeave(to,from,next){
console.log('beforeRouteLeave');
console.log('to',to);
console.log('from',from)
next();
}
}
</script>
32.8路由的两种工作模式
对于一个url来说,及其后面的内容就是hash值
hash值不会包含在HTTP请求中,即:hash值不会带给服务器
hash模式
- 地址中永远带着#号,不美观
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
- 兼容性较好
history模式
-
地址干净,美观
-
兼容性和hash模式相比略差
-
应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题
const router = new VueRouter({ mode:'history', routes:[...] }) export default router