1.挂载点:
1. 通过 el属性设置挂载点: el:'#app'
2. 通过 $mount('#app') 方法设置挂载点
vue 中插值语法: {{data中的属性名}}
设置挂载点
<div id="app">
<p>{{msg}}</p>
</div>
<script src="./js/vue.js"></script>
<script>
// 第一种方式: 通过el属性设置挂载点
// const app = new Vue({
// el:'#app',
// data:{
// msg:'hello vue'
// }
// })
// 第二种方式: 通过 $mount() 方法设置挂载点
const app = new Vue({
data:{
msg:'hello world'
}
}).$mount('#app')
</script>
2.vue 数据绑定
vue 的数据存储: 都存储在data属性中;
不管是你一开始需要用的数据,还是请求接口返回的数据,或者是将来可能会使用的数据(还要给他一个初始的默认值); 都需要放到data中,
vue 是数据驱动的:双向数据绑定
3.vue 指令
vue 的指令:
v-html : 解析html标签的。插值 , 类似于 {{}}
v-text :插值, 插入的是纯文本内容 , 类似于 {{}}
v-for : 列表渲染(循环: 遍历数组和对象)
v-if : 条件渲染 (用法和 js 的 if-else 语句一样)
v-else-if
v-else
v-on : 事件绑定
v-bind : 属性绑定
v-model : 数据双向绑定, 针对的是form表单
v-cloak : 防止闪烁,需要结合 css样式 display:none 一起使用
v-pre : 跳过编译
v-once : 执行一次
v-slot : 插槽
v-show : 显示/隐藏
重要的指令:
v-if v-show
v-for
v-on
v-bind
v-model
v-slot
-01 v-for:
<!DOCTYPE html>
<html lang="zh-CN">
<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>
<!--
v-for: 列表渲染(循环, 更类似于 for-in 循环)
只要使用 v-for , 就一定要绑定key值 ; key值要具有唯一性
格式:
遍历数组:
<标签 v-for='item in data中的数据(array)' :key='value'>{{item}} item是自定义的(不要使用key),代表的是数组中的每个值; value 是唯一的 </标签>
<标签 v-for='(item, index) in data中的数据(array)' :key='value'> item 是自定义的,是数组的每个元素, index 也是自定义的,代表数组的索引值 </标签>
遍历对象:
<标签 v-for='value in data中的数据(object)'> {{value }} value 是对象中的属性值 </标签>
<标签 v-for='(value, name) in data中的数据(object)'>{{value }} value 是对象中的属性值 , name 是对象中的属性名</标签>
<标签 v-for='(value, name, index) in data中的数据(object)'>{{value }} value 是对象中的属性值 , name 是对象中的属性名, index 是索引值</标签>
-->
<div id="app">
<!-- <p v-for='item in list'>{{item}}</p> -->
<!-- <p v-for='(item, index) in list'>{{item}} ========== 索引值:{{index}}</p> -->
<ul>
<!-- <li v-for='item in person'>{{item}}</li> -->
<!-- <li v-for='(item, name) in person'>{{item}} ========== {{name}}</li> -->
<!-- <li v-for='(item, name, index) in person'>{{item}} ========== {{name}} ----------- {{index}}</li> -->
</ul>
<!-- 了解 key值 -->
<!-- <p v-for='(item, aa) in arr' v-bind:key='aa'>{{item}}</p> -->
<!-- <span v-for='item in arr' :key='item'></span> -->
<!-- 练习 v-for -->
<ul>
<li v-for='item in movie'>
<strong>{{item.title}}</strong>
<span>{{item.time}}</span>
</li>
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
// list:[1, 3, 465, 4567, 3423]
// person:{
// name:'张三',
// age:18,
// sex:'男'
// }
// key值
// arr:[1, 1, 1, 234, 345, 5634]
// json 格式
movie:[
{
title:'电影1',
time:'120分钟'
}, {
title:'电影2',
time:'150分钟'
}, {
title:'电影3',
time:'90分钟'
} , {
title:'电影4',
time:'190分钟'
}
]
}
})
// var arr = [1,2345,2345,356]
// for(let i in arr){}
// for(let i = 0; i < arr.length; i++){}
</script>
</body>
</html>
-02 vue的属性绑定 v-bind:
<!DOCTYPE html>
<html lang="zh-CN">
<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>
<!--
v-bind: 绑定属性 (绑定的是标签的属性 或者是 组件上绑定属性用来传值的)
v-bind格式: 可以给标签绑定多个属性; 如果标签的属性需要动态更新,使用v-bind进行绑定
<标签 v-bind:属性名='data中的数据'></标签>
v-bind 可以进行简写: v-bind: 整体简写为 :
<标签 :属性名='data中的数据'></标签>
绑定类名
绑定行内样式
-->
<p title="" id="" class=""></p>
<div id="app">
<!-- <img src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2F1812.img.pp.sohu.com.cn%2Fimages%2Fblog%2F2009%2F11%2F18%2F18%2F8%2F125b6560a6ag214.jpg&refer=http%3A%2F%2F1812.img.pp.sohu.com.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1624589594&t=6cbdfb23b53dec1bd4e1a181cffbcfb9" alt="">
<p v-bind:title="msg" v-bind:index='count' >hello world</p> -->
<!-- 字符串拼接 : 如果要使用字符串, 需要给值添加引号 ;
如果没有添加引号,则映射data中的数据, 如果 添加引号,则是原始的字符串类型 -->
<!-- <p v-bind:title='msg + count'>helllo 变量与变量拼接</p>
<p v-bind:title='msg + "count" '>helllo 变量和字符串拼接 (区别 变量和 字符串) </p> -->
<!-- <p>{{a}}</p> -->
<!-- <img v-bind:src="b" alt=""> -->
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
b:'https://gimg2.baidu.com/image_search/src=http%3A%2F%2F1812.img.pp.sohu.com.cn%2Fimages%2Fblog%2F2009%2F11%2F18%2F18%2F8%2F125b6560a6ag214.jpg&refer=http%3A%2F%2F1812.img.pp.sohu.com.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1624589594&t=6cbdfb23b53dec1bd4e1a181cffbcfb9',
msg:'hello vue v-bind',
count:100
}
})
</script>
</body>
</html>
-03 v-bind:class 绑定类名
<!DOCTYPE html>
<html lang="zh-CN">
<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>
<style>
.txt{
color:red;
font-size:30px;
}
.active{
border:1px solid red;
}
</style>
</head>
<body>
<!--
绑定类名
v-bind:class='value'
格式:
绑定单一类名:有条件决定
<标签 v-bind:class='data中的数据(boolean) ? "添加的类名1" : '添加的类名2' '></标签>
使用数组:绑定多个类名,可以添加固有的类名
<标签 v-bind:class='[ "固有类名1" , "固有类名2" ,..., 动态绑定的类名 ]'></标签>
绑定类名:
<标签 v-bind:class='data中的数据'></标签>
绑定多个类名:
<标签 v-bind:class='[ "固有类名1" , "固有类名2" ,..., 动态绑定的类名(可以是条件决定的,也可以是data中的数据) ]'></标签>
在属性绑定中,可以使用三元运算符 ?:
-->
<div id="app">
<!-- 如果条件为true: 则添加 active类名, 如果条件 false: 则不添加 -->
<p class="txt active">绑定类名</p>
<!-- <p v-bind:class="isActive ? 'active' : '' ">绑定类名</p> -->
<!-- 也可以短路写法
<p v-bind:class="isActive==(data中的值)&&active">绑定类名</p> -->
<!-- <p v-bind:class="[ 'txt', isActive ? 'active' : '' ]" >绑定类名, 固定类名和 动态类名同时存在</p> -->
<p v-bind:class='str'>绑定类名</p>
<p v-bind:class='[str, name1]'>绑定类名, 多个类名,都要使用数组的形式</p>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
isActive:true,
str:'active',
name1:'txt'
}
})
</script>
</body>
</html>
-04 v-bind:style 绑定样式
<!DOCTYPE html>
<html lang="zh-CN">
<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>
<div id="app">
<p style="color:red; font-size:30px">行内样式</p>
<!-- vue 绑定行内样式 :绑定样式属性值,需要使用对象的形式; 对象中的key 就是样式属性名, 对象中的value 就是属性值,可以是data中的数据 -->
<p v-bind:style='{color:col}'>绑定样式值</p>
<hr><hr>
<!-- vue 绑定样式对象: -->
<p :style="cssStyle">绑定样式对象</p>
<hr><hr>
<!-- vue 绑定多个样式对象或者绑定多个样式值,综合用法使用数组的形式 -->
<h2 :style='[cssStyle, border, {backgroundColor:bc}]'>绑定多种样式对象; 使用数组的形式</h2>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
col:'red',
// 样式对象
cssStyle:{
fontSize:'30px',
color:'orange'
},
border:{
border:'2px solid #000'
},
bc:'pink'
}
})
</script>
</body>
</html>
-05 v-html v-text 使用差值
<!DOCTYPE html>
<html lang="zh-CN">
<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>
<!--
vue 使用插值时:
1. 使用 模板语法 {{}} 语法进行插值(纯文本) 用的最多的方式
2. 使用指令的形式:
v-html : 插入 html结构
v-text : 插入 纯文本 内容
-->
<div id="app">
<p>{{msg}}</p>
<!-- v-text 指令 -->
<h2 v-text='msg'></h2>
<!-- v-html 指令 : 解析html标签 -->
<div v-html='msg'></div>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
msg:'<em>hello world</em>'
}
})
</script>
</body>
</html>
-06 v-if 条件渲染
<!DOCTYPE html>
<html lang="zh-CN">
<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>
<!--
vue 的条件渲染 : 用法类似于 js 的 if-else
v-if=''
v-else-if=''
v-else : 没有表达式
如果满足条件: 则会渲染生成DOM结构, 如果不满足条件,则不会生成DOM结构
v-if 可以控制元素的出现和隐藏:实现原理是通过创建和销毁DOM
-->
<div id="app">
<!-- <h1 v-if='false'>hello world</h1>
<h2 v-if='true'>hello world =======</h2> -->
<!-- <p v-if='score >= 90'>优秀</p>
<p v-else-if='score >= 80'>良好</p>
<p v-else>几个</p> -->
<p v-if='score >= 80 && score <= 90'>限定范围</p>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
score:88
}
})
</script>
</body>
</html>
-07 v-on: 事件绑定
<!DOCTYPE html>
<html lang="zh-CN">
<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>
<!--
vue 中的事件: 通过 v-on 指令进行绑定
<标签 v-on:事件类型='methods中的方法名'></标签>
v-on的简写形式: v-on: 整体 简写为 @
<标签 @事件类型='methods中的方法名'></标签>
methods属性: 是 vue 的方法( 函数 / 事件处理程序)
事件类型: js 的原生事件类型都可以使用
click
mouseenter
mouseover
keyup
keydown
....
-->
<div id="app">
<button v-on:click='print'>绑定事件</button>
<button @click='show'>绑定事件</button>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
methods:{
// vue中所有的方法都放到 methods 属性中, 中间使用逗号隔开
print(){
console.log(111);
},
show(){
console.log('show');
}
}
})
</script>
</body>
</html>
-08 v-model 表单数据双向绑定
<!DOCTYPE html>
<html lang="zh-CN">
<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>
<style>
.err{
color:red;
}
</style>
</head>
<body>
<!--
v-model: 实现数据双向绑定 (针对的是form表单)
-->
<!-- <input type="text"> -->
<div id="app">
<p class="err" v-if='show'>用户名或密码错误 </p>
<p>
<input type="text" v-model='username'>
</p>
<p>
<input type="password" v-model='pwd'>
</p>
<p>
<!-- {{username}} -->
<button @click='login'>登录</button>
</p>
<hr>
<hr>
<!-- 绑定多选框: 绑定的data中的数据必须是数组类型 -->
<div>
<input type="checkbox" value='篮球' v-model='list'>篮球
<input type="checkbox" value='足球' v-model='list'>足球
<input type="checkbox" value="羽毛球" v-model='list'>羽毛球
</div>
<p>{{list}}</p>
</div>
<script src="./js/vue.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
username:'',
pwd:'',
show:false,
list:[]
},
methods:{
login(){
axios.post('http://localhost:3000/users', {
username: this.username,
pwd:this.pwd
}).then(res => {
console.log(res);
if(res.data.code == 404){
this.show = true;
}else if(res.data.code == 200){
location.href = 'index.html'
}
})
}
}
})
</script>
</body>
</html>
ex案例 实现tab切换
<!DOCTYPE html>
<html lang="zh-CN">
<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>
<style>
.nav{
width: 500px;
display: flex;
justify-content: space-around;
}
.content{
width: 500px;
height: 400px;
overflow: hidden;
}
.content div{
width: 500px;
height: 400px;
float: left;
display: none;
}
.nav-active{
color:red;
}
.content .cont-active{
display: block;
}
</style>
</head>
<body>
<!--
vue 实现tab切换
-->
<div id="app">
<!-- <ul class="nav">
<li :class='isNav ? "nav-active" : ""' @click='change'>首页</li>
<li @click='change'>新闻</li>
<li @click='change'>关于</li>
<li @click='change'>练习</li>
</ul>
<div class="content">
<div>首页1</div>
<div>新闻2</div>
<div>关于3</div>
<div>练习4</div>
</div> -->
<ul class="nav">
<li
v-for='(item, index) in tab'
:class='isNav === index ? "nav-active" : "" '
@click="change(index)"
>{{item.tabNav}} === {{index}}</li>
</ul>
<div class="content">
<div
v-for='(item, index) in tab'
:class='isNav === index ? "cont-active" :"" '>{{item.content}}</div>
</div>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
isNav:0,
tab:[
{
tabNav:'首页',
content:'首页内容'
}, {
tabNav:'新闻',
content:'新闻页面内容'
}, {
tabNav:'关于',
content:'关于内容'
}, {
tabNav:'联系',
content:'联系我们内容'
}
]
},
methods:{
change(index){
console.log(index);
this.isNav = index;
}
}
})
</script>
</body>
</html>
-09 v-show
<div id="app">
<p v-if='flag'>hello v-if</p>
<p v-show='flag'>hello v-show</p>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
flag:false
}
})
</script>
<!--
v-if 和 v-show 的区别
v-if 和 v-show 都可以控制元素的出现和隐藏;
v-if原理: 通过创建 和 销毁DOM 实现
v-show原理: 通过 css 样式 display:none 实现的
应用场景:
v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销(v-show不管条件为真还是假,
都会渲染DOM)。
因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,
则使用 v-if(v-if是惰性的,只有条件为真才渲染DOM) 较好。
-->
-10 v-pre,v-cloak,v-once
这三个指令都没有表达式
v-pre (跳过编译) : 跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。
跳过大量没有指令的节点会加快编译。
v-cloak (避免闪烁) :必须结合css样式一起使用 display:none, 通过属性选择器设置css样式;
官网解析: 这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak]
{ display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。
v-once (只渲染一次) :只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将
被视为静态内容并跳过。这可以用于优化更新性能。
v-if=''
v-for=''
<style>
[v-cloak]{
display: none;
}
p{
color:red;
font-size:50px;
}
</style>
<div id="app">
<!-- <p>{{msg}}</p>
<p v-pre>n内容就是 需要 {{msg}} {{msg}}</p> -->
<!-- <p >v-cloak {{msg}}</p>
<p v-cloak>v-cloak {{msg}}</p> -->
<p>{{msg}}</p>
<p v-once>{{msg}}</p>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
msg:'hello vue'
}
})
</script>
4. v-for 和 v-if 不建议作用于同一个标签上
:v-for 的优先级 高于 v-if
可以使用 template 内置组件标签 : 页面渲染时 template 是不会被渲染的
<ul>
<template v-for='(item, index) in list'>
<li v-if='!item.status' :key=''>
<input type="checkbox" @click='changeStatus(index)'> <span>{{item.title}}</span>
<del @click='remove(index)'>删除进行中</del>
</li>
</template>
</ul>
5.计算属性 computed
<!DOCTYPE html>
<html lang="zh-CN">
<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>
<!--
vue : 数据存储 data 属性中,
计算属性 computed: 针对 data中的数据派生出新的数据
任何复杂的逻辑 都需要使用计算属性搞定
计算属性的用法: 怎么使用data中的数据,就怎么使用计算属性
计算属性的本质是函数: 需要通过返回值 返回数据; 计算属性的名字 不能和 data中的数据的名字相同
-->
<div id="app">
<!-- <p>{{msg.split('').reverse().join('')}}</p>
<p> 计算属性: {{str}}</p>
<p>{{count}}</p> -->
<hr>
<hr>
<p>进行中的个数: {{todoNum}}</p>
<p>完成的个数: {{doneNum}}</p>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
msg:'hello vue',
list: [
{title:'123', status:false},
{title:'1456', status:false},
{title:'14234256', status:false},
{title:'1423dfgz256', status:false},
{title:'1423====256', status:false},
{title:'1567456', status:true},
{title:'hello', status:true}
],
},
// 计算属性
computed:{
str(){
// return 100;
return this.msg.split('').reverse().join('')
},
count(){
return 100;
},
todoNum(){
return this.list.filter(item => item.status === false).length
},
doneNum(){
return this.list.filter(item => item.status === true).length
}
}
})
// let str = 'hello vue';
// // euv olleh
// let res = str.split('').reverse().join('')
// console.log(res);
// let arr = [12, 234, 34, 56, 764];
// // > 50
// let res = arr.filter(item => {
// console.log(item);
// return item > 50
// })
// console.log(res.length);
</script>
</body>
</html>
计算属性中的setter,getter 属性
<!DOCTYPE html>
<html lang="zh-CN">
<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>
<!--
计算属性:默认只有getter属性(获取/读取),不能设置数据
计算属性 是对data 中的数据进行处理并派生出新的数据: 计算属性依赖于data中的数据,
只要data中的数据发生变化,
计算属性会自动更新 ; 也就是使用计算属性的set属性时,必须要在data中设置值,
通过这个值去动态修改set内的值
计算属性的特点: 计算属性存在缓存, 数据没有变化,则直接从缓存中获取
方法 : 方法每次都要被调用, 相当于重新解析,
在computed中写的函数实际上是一种简写的形式,get省略掉了,set直接用函数表示了,
所以写出来的函数必须要有返回值
-->
<div id="app">
<p>{{msg}}</p>
<p>{{count}}</p>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
msg:'hello',
// num 作为计算属性的setter 方法的桥梁
num:0
},
computed:{
// count(){
// return 100;
// }
// 计算属性的 getter 和 setter 属性
count:{
get(){
return this.num;
},
set(value){
// 参数value 就是要设置的值
this.num = value
}
}
}
})
</script>
</body>
</html>
6.filter 过滤器/自定义过滤器
<!DOCTYPE html>
<html lang="zh-CN">
<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>自定过滤器</title>
<style type="text/css">
.wp {
width: 200px;
height: 200px;
border: 1px #f00 solid;
}
</style>
</head>
<body>
<div id="app">
<div>{{str|aa('徐志摩')}}</div>
<input type="text" v-model="key" />
<div>{{key|fir}}</div>
</div>
</body>
</html>
<script src="./vue.js"></script>
<script>
// 关键字是filter,第一个参数是过滤器的名字,第二个参数是过滤器的回调函数,
// 该回调函数又有两个参数,第一个参数要过滤的内容,第二个参数是具体的过滤器参数,
// 每一个回调函数都必须有一个return,这样才能保证视图的渲染
Vue.filter('aa', (value, arg) => {
console.log(value);
return value + '换行' + arg;
});
let vm = new Vue({
el: '#app',
data: {
str: '民国万税,天下太贫',
key: 123
},
methods: {
},
filters: {
fir(value) {
console.log(value);
return value;
}
}
});
</script>
7.watch数据监听
<!DOCTYPE html>
<html lang="zh-CN">
<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>watch数据监听</title>
</head>
<body>
<div id="app">
<h2>{{title}}</h2>
<input type="text" v-model="title" />
<div>{{name}}</div>
<input type="text" v-model="name" />
</div>
</body>
</html>
<script src="./js/vue.js"></script>
<script type="text/javascript">
let vm = new Vue({
el:'#app',
data:{
title:'怕得鱼惊不应人',
name:'乔娜'
},
//如果需要根据数据变化来进行异步操作,需要使用watch监听
//watch 监听的时候,数据名为方法名,第一个参数是新值,第二个参数是旧值
watch:{
title(newVal,oldVal){
console.log('新='+newVal,'旧='+oldVal);
}
}
});
//也可以在外部进行修改
vm.$watch('name',(newVal,oldVal)=>{
console.log('新='+newVal,'旧='+oldVal);
});
//1.watch擅长处理的场景:一个数据影响多个数据
//例如单位换算,https://www.runoob.com/vue2/vue-watch.html
//2.computed擅长处理的场景:一个数据受多个数据影响
//3.相比于watch/computed,methods不处理数据逻辑关系,只提供可调用的函数
</script>
8.数组和对象的检测
//Vue.set(‘数组/对象名’,数组下标/key,‘更新内容’)
<!DOCTYPE html>
<html lang="zh-CN">
<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>数组和对象的检测</title>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item,index) in arr">{{item}}</li>
</ul>
<button @click="change">点击改变</button>
<div v-for="(item,index) in obj">{{index}}:{{item}}</div>
<button @click="changeObj">点击改变obj</button>
</div>
</body>
</html>
<script src="./js/vue.js"></script>
<script>
let vm = new Vue({
el:'#app',
data:{
arr:['聂小倩','乔娜','香莲','小熙'],
obj:{
name:'李瓶儿',
age:15
}
},
/*
1.数组的更新检测
数组能够引起视图发生变化的操作
push pop unshift(添加) shift splice sort reverse
不能引起视图变化的操作
filter map slice concat 等 它们返回的是新数组,因此不会引起视图的更新
this.arr[i] = '' 也不会引起视图的更新
使用
Vue.set(实例中的数组元素,下标/key,修改后的内容);
vm.$set(实例中的数组元素,下标/key,修改后的内容);
2.对象的更新检测
对象的更新,如果需要更新value,可以通过obj.key = val直接更新,但是如果对
已有对象增加或者删除键值对,不能够引起视图的修改(页面 重新渲染)
Vue.set(obj,key,内容);
通过es6的方式,重新设置对象
*/
methods: {
change(){
// this.arr.push('金莲');
// this.arr[3] = '陆判';
// Vue.set(this.arr,3,'陆判');
vm.$set(this.arr,3,'陆判');
console.log(this.arr);
},
changeObj(){
// Vue.set(this.obj,'job','种地');
// this.obj = {job:'种地',...this.obj};
this.obj=Object.assign({},this.obj,{job:'种地'});
}
},
});
</script>
9.自定义指令directive
<!DOCTYPE html>
<html lang="zh-CN">
<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>自定义指令</title>
<style type="text/css">
.wp {
width:200px;
height:200px;
border:1px #f00 solid;
}
</style>
</head>
<body>
<div id="app">
<div class="wp" v-change-color="color" v-txt="val"></div>
<input type="color" v-model="color" />
<input type="text" v-model="val" />
<div class="wp" v-shows></div>
</div>
</body>
</html>
<script src="./js/vue.js"></script>
<script>
/*
当需对普通dom元素进行底层操作的时候,可以考虑使用自定义指令来实现
//见解:适用于同时添加多个事件(原生的addeventlistener)
Vue.directive('指令名',指令的操作对象)
一个指令定义对象可以提供如下几个钩子函数 (均为可选):bind 使用较多
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。
指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略
不必要的模板更新 。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
bind的参数
第一个 el代表的是 当前指令绑定的元素
第二个 binding代表的是指令的所有信息,其中,value属性是指令绑定的值
如果 bind 和 update 内容一致的话,也可以简写为
Vue.directive('changeColor',(el,binding)=>{
//code
})
*/
Vue.directive('changeColor',{
bind(el,binding){
el.onmouseenter = function(){
el.style.background = '#04be02';
};
el.onmouseleave = function(){
el.style.background = binding.value;
};
console.log(binding);
},
update(el,binding) {
el.onmouseenter = function(){
el.style.background = '#04be02';
};
el.onmouseleave = function(){
el.style.background = binding.value;
};
console.log('更新');
},
});
Vue.directive('txt',(el,binding)=>{
el.onmouseenter = function(){
el.innerHTML = binding.value;
};
});
let vm = new Vue({
el:'#app',
data:{
color:'#ff0',
val:'瀚海阑干百丈冰'
},
methods: {
},
//自定义指令关键字
directives:{
// 正常写法
abc:{
bind(){},
update(){}
},
// 当bind和update一致的时候的写法
shows(el,bindings){
console.log(el);
}
}
});
</script>
10.组件注册,基本使用
<!DOCTYPE html>
<html lang="zh-CN">
<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>组件的属性</title>
</head>
<body>
<div id="app">
//props数组里赋值的tag属性=安禄山,点击后触发show
<abc tag="安禄山"></abc>
<son :myobj="{name:'秦桧',age:250}"></son>
<son></son>
</div>
</body>
<template id="com">
<div>
<h1>郭子仪</h1>
<p>{{tag}}</p>
<button @click="show">点击</button>
</div>
</template>
<template id="son">
<div>
<h1>朱见深{{zhu}}</h1>
<p>{{myobj.name}}</p>
</div>
</template>
</html>
<script src="./js/vue.js"></script>
<script>
Vue.component('abc',{
template:'#com',
// 添加属性,数组的形式是添加简单属性
props:['tag'],
methods: {
show(){
this.tag = '杨玉环';
}
},
});
let vm = new Vue({
el:'#app',
data:{},
methods: {
},
//添加属性的多样性,也就是属性可以有不同的类型
components:{
son:{
template:'#son',
props:{
zhu:{
type:String,
//设置默认值
default:'韩信'
},
myobj:{
type:Object,
//对象设置默认值是一个函数
default:()=>{
return {
name:'法海',
age:200
}
}
}
}
}
}
});
</script>
11.生命周期,钩子函数
<!DOCTYPE html>
<html lang="zh-CN">
<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>vue的生命周期</title>
<style type="text/css">
.wp {
width:200px;
height:200px;
background: #04be02;
}
</style>
</head>
<body>
<div id="app">
<input type="text" v-model="name" />
<div class="wp" v-if="bol"></div>
<button @click="bol=!bol">点击</button>
<button @click="die">点击销毁</button>
</div>
</body>
</html>
<script src="./js/vue.js"></script>
<script type="text/javascript">
let vm = new Vue({
el:'#app',
data:{
name:'香莲',
bol:true
},
//template 运行的时候会替换掉挂载的元素
// template:`
// <h1 @click="aa">{{name}}</h1>
// `,
//存储方法
methods:{
aa(){
alert(this.name);
},
die(){
//点击销毁实例
vm.$destroy();
}
},
// 在实例创建之后执行
created(){
console.log('created,在实例创建之后执行');
},
// 在实例初始化之后,数据观测和事件配置之前执行
beforeCreate(){
console.log('beforeCreate');
},
// 在挂载开始之前被调用
beforeMount(){
console.log('beforeMount,在挂载开始之前被调用');
},
// 只有在mounted之后,才可以执行dom操作,也就是最好在此方法中执行一些需要加载立即执行的内容
mounted() {
console.log('mounted');
},
//当data中的数据发生变化的时候触发。当视图模板重新绘制的时候触发
beforeUpdate() {
console.log(this.name);
},
// 当data中的数据发生变化之后触发。当视图模板重新绘制之后触发
updated() {
console.log('变化后');
},
//实例销毁前调用
beforeDestroy(){
vm.aa();
console.log('beforeDestroy');
},
destroyed() {
console.log('销毁');
},
});
//函数:理解
1)在初始化data和methods时执行
之前:beforeCreate
之后:Created(重点)
2)在使用虚拟dom替换el时执行
之前:beforeMount
之后:mounted(重点)
3)当数据发生改变时,重新渲染虚拟dom时执行
之前:beforeUpdated
之后:updated
4)当调用$destory方法时执行($destory销毁vue实例)
之前:beforeDestroy
之后:destroy
</script>
12.组件父传子props,refs,$children(i)
- 子级中props属性中添加数组、或者对象,在父标签中把数据传到该数组对象的标签中,如果对象的形式,默认返回的也应是一个对象
- 直接用实例vm.属性去控制data,从而子父都可以使用该数据
- ,父级methods中设置函数,通过this.$refs.aa控制子级内容
- c h i l d r e n ( i ) 在 父 级 钩 子 函 数 m o u n t e d 中 使 用 t h i s . children(i)在父级钩子函数mounted中使用this. children(i)在父级钩子函数mounted中使用this.children(i)修改控制子内容
<!DOCTYPE html>
<html lang="zh-CN">
<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>refs</title>
</head>
<body>
<div id="app">
<son ref="aa"></son>
<button @click="change">点击</button>
<button >点击++</button>
</div>
</body>
<template id="son">
<div>
<h1>{{name}}</h1>
<p>{{num}}</p>
</div>
</template>
</html>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
/*
组件标签上设置ref属性
this.$refs 获取所有携带ref属性的元素(组件)集合
this.$refs.name 此时的name是组件上的 ref=name ,用来获取 具体的某一个组件实例
*/
let son = {
template:'#son',
data(){
return {
name:'赵丽颖',
num:0
}
}
};
let vm = new Vue({
el:'#app',
data:{},
components:{
son
},
methods: {
change(){
console.log(this.$refs.aa);
this.$refs.aa.name = "贾玲";
}
},
});
</script>
13.子向父,根传值 this. p a r e n t , t h i s . parent, this. parent,this.emit ,this.$root
- 子组件methods中 触发事件写一个自定义事件this.$emit
- 子组件mounted或者created钩子中设置this.$parent.父data数据=子组件的数据
<!DOCTYPE html>
<html lang="zh-CN">
<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>子传父</title>
</head>
<body>
<div id="app">
<p>{{aa}}</p>
<!-- $event是 emit传递的参数 -->
<son @aa="setData($event)"></son>
</div>
</body>
<template id="son">
<div>
<p>{{name}}</p>
<button @click="show()">子传父按钮</button>
<button @click="getParent">getParent</button>
</div>
</template>
</html>
<script src="./js/vue.js"></script>
<script>
/*
$emit 事件触发器
$emit('事件名',传递的参数)
*/
let son = {
template:'#son',
data(){
return {
name:'林黛玉'
}
},
methods: {
show(){
//触发aa事件
this.$emit('aa',this.name);
},
// 子组件获取父组件的方法
getParent(){
//this.$parent 获取父组件实例
//console.log(this.$parent); 子组件mounted或者created钩子中设置
//this.$parent.setData('武则天');
// this.$root 获取根组件实例,如果只有父子嵌套,则 this.$root == this.$parent
this.$root.setData('慈禧');
}
},
};
let vm = new Vue({
el:'#app',
data:{
aa:'吕雉'
},
components:{
son
},
methods: {
setData(str){
console.log('此情可待成追忆'+str);
this.aa = str;
}
},
});
</script>
14.兄弟之间传值
<!DOCTYPE html>
<html lang="zh-CN">
<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>兄弟之间传值</title>
</head>
<body>
<div id="app">
<h1>父组件</h1>
<son></son>
<child></child>
</div>
</body>
<template id="son">
<div>
<p>{{name}}</p>
</div>
</template>
<template id="child">
<div>
<button @click="pass">点击</button>
</div>
</template>
</html>
<script src="./js/vue.js"></script>
<script>
//声明一个公共的vue实例Bus,此bus只是大家约定的一个名字,
//在自己的methods方法中添加 Bus.$emit去发射事件,在兄弟的mounted钩子函数中
Bus.$on接收事件 (两者约定一个公用的事件名取得联系)
const Bus = new Vue();
let son = {
template:'#son',
data() {
return {
name:'左宗棠'
}
},
mounted(){
//监听Bus上的changename事件,args是触发的时候传递的参数
Bus.$on('changename',args=>{
this.name = args;
});
},
};
let child = {
template:'#child',
data() {
return {
name:'曾国藩'
}
},
methods: {
pass(){
// 触发Bus上的changename事件,传递this.name
Bus.$emit('changename',this.name);
}
},
};
let vm = new Vue({
el:'#app',
data:{},
components:{
son,child
}
});
</script>
15.插槽 slot
- 模板中slot 标签内容=形成组件的标签内的内容
- slot标签name属性装载组件上slot=name值得内容
<!DOCTYPE html>
<html lang="zh-CN">
<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>插槽</title>
</head>
<body>
<div id="app">
<son>
<p>两句话</p>
</son>
<son></son>
<aa>
<h1 slot="head">百家姓</h1>
<p>万俟司马,闻人东方</p>
<p>六安</p>
<h1 slot="foot">百家姓终</h1>
</aa>
</div>
</body>
<template id="son">
<div>
<p>一句话</p>
<slot>您没有嵌套内容</slot>
</div>
</template>
<template id="aa">
<div>
<slot></slot>
<slot name="foot"></slot>
<slot name="head"></slot>
<p>赵钱孙李,周吴郑王</p>
</div>
</template>
</html>
<script src="./js/vue.js"></script>
<script>
let aa = {
template:'#aa',
data() {
return {
}
},
};
/*
使用 slot 更好的完成组件的复用
slot可以把组件标签中的内容显示在指定的位置上,如果组件中没嵌套内容,则显示slot中的提示文字
设置指定的插槽内容,需使用name
具名插槽
给 slot设置name属性,在使用的时候如果需要把内容植入到slot插槽中,则必须给内容设置slot="插槽名" 这样就会把内容渲染到插槽所在的位置
如果没有设置名字,则会显示到没有设置插槽名字的slot上
*/
let vm = new Vue({
el:'#app',
data:{},
components:{
son:{
template:'#son',
data() {
return {
}
},
},
aa
}
});
</script>
16.动态组件 component:is(tab切换)
<!DOCTYPE html>
<html lang="zh-CN">
<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>动态组件</title>
<style type="text/css">
span {
display: inline-block;
height:30px;
padding:0 10px;
line-height:30px;
background:#696767;
margin-right:20px;
cursor: pointer;
}
span.on {
color:#fff;
background: #04be02;
}
</style>
</head>
<body>
<div id="app">
<span @click="ch1" :class="index==1&&'on'">茅屋为秋风所破歌</span>
<span @click="ch2" :class="index==2?'on':''">清明</span>
<span @click="ch3" :class="index==3&&'on'">所见</span>
<!-- component 组件,用来显示一个组件的内容,他的is属性,用来显示需要绑定组件的名字 -->
<keep-alive>
<component :is="com"></component>
</keep-alive>
</div>
</body>
<template id="a">
<div>
<h1>南村群童</h1>
</div>
</template>
<template id="b">
<div>
<h1>牧童遥指杏花村</h1>
</div>
</template>
<template id="c">
<div>
<h1>牧童骑黄牛</h1>
</div>
</template>
</html>
<script src="./js/vue.js"></script>
<script>
let a = {
template:'#a'
};
let b = {
template:'#b'
};
let c = {
template:'#c'
};
let vm = new Vue({
el:'#app',
data:{
index:1,
com:'a1'
},
components:{
a1:a,
b1:b,
c1:c
},
methods: {
ch1(){
this.com = 'a1';
this.index = 1;
},
ch2(){
this.com = 'b1';
this.index = 2;
},
ch3(){
this.com = 'c1';
this.index = 3;
},
},
});
</script>
17.简单路由
<!DOCTYPE html>
<html lang="zh-CN">
<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>简单路由</title>
</head>
<body>
<div id="app">
<!--
vue 路由 使用router-link组件来进行路由导航,通过它的to属性来来设置
路由导航地址,每一个router-link渲染之后在dom结构上是一个a标签
-->
<router-link to="/">首页</router-link>
<router-link to="/list">列表页</router-link>
<!--
路由对应的组件出口,也就是路由匹配之后,对应的组件内容显示在
router-view组件中
-->
<router-view></router-view>
</div>
</body>
<template id="home">
<div>
<h1>满纸荒唐言</h1>
</div>
</template>
<template id="list">
<div>
<h1>列表页</h1>
</div>
</template>
</html>
<script src="./vue.js"></script>
<script src="./router.js"></script>
<script>
/*
路由导航
*/
//组件模板
let home = {
template: '#home'
};
let list = {
template: '#list'
};
//路由配置
let routes = [{
name: 'Home', // 路由的名字
path: '/', //路由对应的路径地址
component: home //当跳转到路径地址的时候,在router-view中显示的组件
},
{
name: 'List',
path: '/list',
component: list
}
];
const router = new VueRouter({
routes //配置路由
});
let vm = new Vue({
el: '#app',
data: {},
methods: {},
router // 在实例中注册路由
});
</script>
18.具名路由
//components,name
<!DOCTYPE html>
<html lang="zh-CN">
<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>具名路由</title>
</head>
<body>
<div id="app">
<router-link to="/">首页</router-link>
<router-link to="/cont">二级页</router-link>
<router-view name="header"></router-view>
<router-view></router-view>
<router-view name="footer"></router-view>
</div>
</body>
<template id="head">
<div>
<h1>头部</h1>
</div>
</template>
<template id="foot">
<div>
<h1>底部</h1>
</div>
</template>
<template id="home">
<div>
<h1>首页内容</h1>
</div>
</template>
<template id="cont">
<div>
<h1>二级页面内容</h1>
</div>
</template>
</html>
<script src="./vue.js"></script>
<script src="./router.js"></script>
<script>
let head = {
template: '#head'
};
let foot = {
template: '#foot'
};
let home = {
template: '#home'
};
let cont = {
template: '#cont'
};
const routes = [{
name: 'Home',
path: '/',
components: {
header: head, // 设置name=header的router-view上渲染的组件内容
default: home,
footer: foot
}
},
{
name: 'Cont',
path: '/cont',
components: {
header: head,
default: cont,
footer: foot
}
}
];
const router = new VueRouter({
routes
});
new Vue({
router
}).$mount('#app');
</script>
19.路由嵌套
//children
<!DOCTYPE html>
<html lang="zh-CN">
<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>路由嵌套</title>
</head>
<body>
<div id="app">
<router-link to="/">首页</router-link>
<router-link to="/cont">其他内容</router-link>
<router-view></router-view>
</div>
</body>
<template id="home">
<div>
<h1>首页</h1>
<router-link to="/news">新闻</router-link>
<router-link to="/sport">体育</router-link>
<router-link to="/food">美食</router-link>
<router-view></router-view>
</div>
</template>
<template id="news">
<div>
<div>台湾回归,中华一统</div>
</div>
</template>
<template id="sport">
<div>
<div>中国女排险胜德国女排</div>
</div>
</template>
<template id="food">
<div>
<div>夏天到了,又到了撸串的时候</div>
</div>
</template>
<template id="cont">
<div>
<p>其他内容</p>
</div>
</template>
</html>
<script src="./vue.js"></script>
<script src="./router.js"></script>
<script>
let home = {
template: '#home'
};
let news = {
template: '#news'
};
let sport = {
template: '#sport'
};
let food = {
template: '#food'
};
let cont = {
template: '#cont'
};
const routes = [{
name: 'Cont',
path: '/cont',
component: cont
},
{
name: 'Home',
path: '/',
component: home,
redirect: '/news', //路由重定向
children: [{
name: 'News',
path: '/news',
component: news
},
{
name: 'Sport',
path: '/sport',
component: sport
},
{
name: 'Food',
path: '/food',
component: food
}
]
}
];
const router = new VueRouter({
routes
});
new Vue({
router
}).$mount('#app');
</script>
20.路由传值
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>路由传值</title>
</head>
<body>
<div id="app">
<router-link to="/home?a1=晓曼&a2=李铭">首页</router-link>
<router-link to="/list/郭德纲/45/aa/相声演员">列表</router-link>
<router-link :to="{ name:'Na',params:{name:'李哪吒',age:3} }">哪吒</router-link>
<router-view></router-view>
</div>
</body>
<template id="home">
<div>
<h1>探清水河</h1>
</div>
</template>
<template id="list">
<div>
<h1>活着</h1>
<!--
$route.params 获取路径上参数的集合,他是一个对象
-->
<h2>{{$route.params.name}}</h2>
<h2>{{$route.params.age}}</h2>
<h2>{{$route.params.aa}}</h2>
<h2>{{$route.params.job}}</h2>
</div>
</template>
<template id="na">
<div>
<h1>{{$route.params.name}}</h1>
<router-link to="/detail/1">红烧肉的做法</router-link>
<router-link to="/detail/2">杨过怎么剪指甲</router-link>
<router-link to="/detail/3">孙悟空怎么挠痒</router-link>
</div>
</template>
<template id="detail">
<div>
<p>这里是新闻详情,你访问的新闻id是{{$route.params.id}}</p>
</div>
</template>
</html>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<script src="./js/router.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
let my404 = {
template:'<h1>你访问的资源不存在</h1>'
};
let na = {
template:'#na'
};
let detail = {
template:'#detail'
};
let home = {
template:'#home',
created(){
console.log(this.$route.query);
}
};
let list = {
template:'#list',
created(){
console.log(this.$route.params);
console.log(this.$route);
}
};
const routes = [
{
name:'Na',
path:'/na',
component:na
},
{
name:'Detail',
path:'/detail/:id',
component:detail
},
{
path:'/',
redirect:'/home'
},
{
name:'Home',
path:'/home',
component:home,
alias:'/index' // 设置别名,如果访问/index,实际上也是访问/home
},
{
name:'List',
path:'/list/:name/:age/aa/:job',
component:list
},
{
path:'/aa-*', // 匹配以aa-开头的任意路径
component:{
template:'<h1>aa年轻就该醒着拼</h1>'
}
},
{
path:'*',// * 代表匹配所有的路径,该路由设置只能放在配置路由的最后一位,一般用来做404页面
component:my404
}
];
const router = new VueRouter({
routes
});
new Vue({
router
}).$mount('#app');
</script>
21.手动跳转路由
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>手动跳转路由</title>
</head>
<body>
<div id="app">
<button @click="gohome">首页</button>
<button @click="gotang">唐</button>
<button @click="gosong">宋</button>
<router-view></router-view>
</div>
</body>
<template id="home">
<div>
<h1>首页</h1>
</div>
</template>
<template id="tang">
// 注意:query可以用name或path来引入(get)
params必需要用name来引入,接收参数都是类似的,分别是:(post)
this.$route.query.name和this.$route.params.name。
<div>
<h1>白毛浮绿水{{$route.params.name}}</h1>
</div>
</template>
<template id="song">
<div>
<h1>大江东去{{$route.query.name}}</h1>
</div>
</template>
</html>
<script src="./js/vue.js" type="text/javascript" charset="utf-8"></script>
<script src="js/router.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
let home = {
template:'#home'
};
let tang = {
template:'#tang'
};
let song = {
template:'#song'
};
const routes = [
{
name:'Home',
path:'/',
component:home
},
{
name:'Tang',
path:'/tang',
component:tang
},
{
name:'Song',
path:'/song',
component:song
}
];
const router = new VueRouter({
routes
});
new Vue({
methods:{
gohome(){
// this.$router.push('/'); //使用字符串跳转
this.$router.push({path:'/'}); //对象跳转,设置path
},
gotang(){
// this.$router.push('tang');//params name跳转
this.$router.push({name:'Tang', params:{name:'骆宾王'} }); // 设置name
},
gosong(){
// this.$router.push('song'); path或name都可以
this.$router.push({path:'song', query:{name:'苏东坡'} });
}
},
router
}).$mount('#app');
</script>
22.全局(导航)前置守卫
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>全局导航守卫</title>
</head>
<body>
<div id="app">
<router-link to="/">首页</router-link>
<router-link to="/login">登录</router-link>
<router-link to="/list">列表</router-link>
<router-view></router-view>
</div>
</body>
<template id="home">
<div>
<h1>首页</h1>
</div>
</template>
<template id="login">
<div>
<h1>登录</h1>
<label>用户名</label> <input type="text" v-model="user" /><br/>
<label>密码</label> <input type="password" v-model="pas" /><br/>
<button @click="login">登录</button>
</div>
</template>
<template id="list">
<div>
<h1>列表</h1>
</div>
</template>
</html>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<script src="js/router.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
let home = {
template:'#home'
};
let login = {
template:'#login',
data(){
return {
user:'',
pas:''
}
},
methods:{
login(){
if(this.user=='123'&&this.pas=='123'){
alert('登录成功');
localStorage.setItem('user','123');
}else{
alert('用户名或密码错误');
}
}
}
};
let list = {
template:'#list'
};
const routes = [
{
name:'Home',
path:'/',
component:home
},
{
name:'Login',
path:'/login',
component:login
},
{
name:'List',
path:'/list',
component:list
}
];
const router = new VueRouter({
// mode:'history',
routes
});
//设置守卫
/*
to: 跳转到的路由
from: 从哪一个路由跳
next: 执行函数
*/
router.beforeEach((to,from,next)=>{
let user = localStorage.getItem('user')||'error';
console.log(user);
//判断是否登录或者是否跳转到登录
// if(to.name!='Login'||user=='error'){
// next({name:'Login'});
// }else{
// next();
// }
next();
console.log(to,from);
});
new Vue({
router
}).$mount('#app');
</script>
23.路由独享守卫
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>路由独享守卫</title>
</head>
<body>
<div id="app">
<router-link to="/">首页</router-link>
<router-link to="/cont/1">内容页</router-link>
<router-link to="/pay">缴费页</router-link>
<router-link to="/cont/2">内容页</router-link>
<router-link to="/cont/3">内容页</router-link>
<router-link to="/cont/4">内容页</router-link>
<router-view></router-view>
</div>
</body>
<template id="home">
<div>
<h1>首页</h1>
</div>
</template>
<template id="login">
<div>
<h1>登录</h1>
</div>
</template>
<template id="cont">
<div>
<h1>内容页面{{$route.params.id}}</h1>
</div>
</template>
<template id="pay">
<div>
<h1>缴费</h1>
</div>
</template>
</html>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<script src="js/router.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
let home = {
template:'#home'
};
let login = {
template:'#login'
};
let cont = {
template:'#cont',
beforeRouteEnter(to,from,next){
// 在此不能使用组件的实例this,因为此时组件实例还没有被创建
console.log(this); //this不是当前组件
console.log(to,from);
next();
},
//当同一个组件路由传入不同的参数的时候,触发该守卫
beforeRouteUpdate(to,from,next){
console.log(to.path);
console.log(this);//this是当前组件
next();
},
//组件离开的时候执行
beforeRouteLeave(to,from,next){
console.log(to,from);
next();
}
};
let pay = {
template:'#pay'
};
const routes = [
{
name:'Home',
path:'/',
component:home
},
{
name:'Login',
path:'/login',
component:login
},
{
name:'Cont',
path:'/cont/:id',
component:cont
},
{
name:'Pay',
path:'/pay',
component:pay,
//设置独享守卫
beforeEnter:(to,from,next)=>{
//判断是否登录,如果登录直接执行next(),否则跳到登录
let user = localStorage.getItem('user');
if(user){
next();
}else{
next('/login');
}
console.log(to,from);
}
}
];
const router = new VueRouter({
routes
});
new Vue({router}).$mount('#app');
</script>
24.路由设置实例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>路由设置实例</title>
</head>
<body>
<div id="app">
<router-link to="/">首页</router-link>
<router-link to="/login">登录</router-link>
<router-link to="/list">列表</router-link>
<router-view></router-view>
</div>
</body>
<template id="home">
<div>
<h1>主页</h1>
</div>
</template>
<template id="login">
<div>
<h1>登录</h1>
<label>用户名:</label> <input type="text" v-model.trim="user" /><br />
<label>密码:</label> <input type="password" v-model.trim="pas" /><br />
<button type="button" @click="login">登录</button>
</div>
</template>
<template id="list">
<div>
<router-link to="/detail/1">列表1</router-link>
<router-link to="/detail/2">列表2</router-link>
<router-link to="/detail/3">列表3</router-link>
<router-link to="/detail/4">列表4</router-link>
<router-view></router-view>
</div>
</template>
<template id="cont">
<div>
<p>详情页{{$route.params.id}}</p>
</div>
</template>
</html>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<script src="js/router.js" type="text/javascript" charset="utf-8"></script>
<script src="js/axios.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
let home = {
template: '#home'
};
let login = {
template: '#login',
data() {
return {
user: '',
pas: ''
}
},
methods: {
login() {
/* //get请求
let url = 'http://127.0.0.1:8080/login?name='+this.user+'&pas='+this.pas;
axios.get(url).then(res=>{
console.log(res.data);
if(res.data.code){
localStorage.setItem('aa',res.data.token);
}
alert(res.data.errMsg);
this.user='';
this.pas='';
});
*/
//把HTML页面要发送的内容添加到URLSearchParams 实例中
let params = new URLSearchParams();
params.append('name', this.user);
params.append('pas', this.pas);
axios.post('http://127.0.0.1:8080/postdata', params).then(res => {
console.log(res.data);
if (res.data.code) {
localStorage.setItem('aa', res.data.token);
}
alert(res.data.errMsg);
this.user = '';
this.pas = '';
});
}
}
};
let list = {
template: '#list'
};
let cont = {
template: '#cont'
};
const routes = [{
name: 'Home',
path: '/',
component: home
},
{
name: 'Login',
path: '/login',
component: login
},
{
name: 'List',
path: '/list',
component: list,
children: [{
name: 'Cont',
path: '/detail/:id',
component: cont
}]
}
];
const router = new VueRouter({
routes: routes
});
//设置全局守卫
router.beforeEach((to, from, next) => {
let aa = localStorage.getItem('aa');
if (to.name != 'Login' && !aa) {
next('/login');
} else {
next();
}
});
new Vue({
data: {},
router
}).$mount('#app');
</script>
server
const express = require('express');
const app = express();
const cors = require('cors');
const bodyParser = require('body-parser');
//设置token
function rand(m,n){
return Math.floor(Math.random()*(n-m+1)+m);
}
function setToken(){
let str='1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM';
let aa='';
for(let i=0;i<16;i++){
aa+= str[rand(0,str.length-1)];
}
return aa;
}
//设置跨域
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended:false
}));
app.post('/postdata',(req,res)=>{
let {name,pas} = req.body;
if(name=='123'&&pas=='123'){
let obj={
code:1,
errMsg:'登录成功',
token:setToken()
};
res.send(obj);
}else{
let obj = {
code:0,
errMsg:'用户名或密码错误'
};
res.send(obj);
}
});
app.get('/login',(req,res)=>{
let {name,pas} = req.query;
if(name=='123'&&pas=='123'){
let obj={
code:1,
errMsg:'登录成功',
token:setToken()
};
res.send(obj);
}else{
let obj = {
code:0,
errMsg:'用户名或密码错误'
};
res.send(obj);
}
});
app.listen(8080,()=>{
console.log('服务启动成功');
})
25.过渡动画
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>过渡动画</title>
<style type="text/css">
/*
在进入/离开的过渡中,会有 6 个 class 切换。
以下的 v 代表 transition的name属性值
v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
v-enter-to:2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
v-leave:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
v-leave-to:2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。
*/
* {
margin: 0;
padding: 0;
}
.wp {
width: 200px;
height: 200px;
background: #04BE02;
transition: all 2s;
}
.aa-enter,
.aa-leave-to {
/* transform: translateX(500px); */
opacity: 0;
}
.aa-enter-active,
.aa-leave-active {
background: #FF0000;
}
.aa-enter-to,
.aa-leave {
/* transform: translateX(0); */
background: #04BE02;
opacity: 1;
}
.bounce-enter-active {
animation: bounce-in .5s;
}
.bounce-leave-active {
animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
/* router-link 路由被选中的效果,模糊匹配 */
.router-link-active {
background: #ccc;
display: inline-block;
width: 200px;
height: 40px;
}
/* router-link路由被选中的效果,精准匹配,切换后复原 常用 */
.router-link-exact-active {
background: #04BE02;
color: #fff;
}
</style>
</head>
<body>
<div id="app">
<transition name="aa">
<div class="wp" v-if="bol"></div>
</transition>
<button @click="show">显示/隐藏</button>
//router-link 标签三个属性:tag 修改标签名;replace 使页面不回退;
修改默认的 .router-link-active class属性名 在vueRouter实例下添加linkActiveClass:'aa'
<router-link to="/" exact tag="li" replace>首页</router-link>
<router-link to="/search">搜索页</router-link>
<transition name="bounce">
<router-view></router-view>
</transition>
</div>
</body>
<template id="home">
<div>
<h1>首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页首页
</h1>
</div>
</template>
<template id="search">
<div>
<h1>搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页搜索页
</h1>
</div>
</template>
</html>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<script src="js/router.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
let home = {
template: '#home'
};
let search = {
template: '#search'
};
const routes = [{
name: 'Home',
path: '/',
component: home
},
{
name: 'Search',
path: '/search',
component: search
}
];
const router = new VueRouter({
routes: routes
});
new Vue({
el: '#app',
data: {
bol: true
},
methods: {
show() {
this.bol = !this.bol;
}
},
router
});
</script>
26.Vuex的state和getters
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vuex基础</title>
</head>
<body>
<div id="app">
<!-- 组件中获取getters -->
<h1>{{$store.getters.addNum}}</h1>
<h2>{{$store.getters.total(100)}}</h2>
<h2>{{$store.getters.total(100)}}</h2>
<p>{{$store.state.num}}</p>
<son></son>
</div>
</body>
<template id="son">
<div>
<h1>getters:{{total(1000)}}</h1>
<p>abc:{{abc}}</p>
<p>name:{{name}}</p>
<p>num:{{num}}</p>
<p>{{$store.state.name}}</p>
<p>{{$store.state.num}}</p>
<button type="button" @click="add">点击++</button>
</div>
</template>
</html>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<script src="js/vuex.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
/*
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式
*/
/*
怎么在组件中获取state的num
$store.state.num
在实例的methods中使用state的num
this.$store.state.num
mapState() 辅助函数,他可以映射同名的数据,到实例或者组件的计算属性中
*/
let state = {
num:0,
name:'郭德纲',
num1:0
};
// getters 相当于state的计算属性,它把state复杂的运算逻辑写入到getters中的方法里面,便于调用
// 它里面的方法中,默认传入一个state参数,这个state就是vuex的state,可以通过state参数直接
获取vuex的state数据
//mapGetters
let getters= {
addNum:state=>{
let t = state.num1+10;
return t;
},
total:state=>{
return n=>{
let t = state.num1+n;
return t;
}
},
total1:state=>n=>{
let t = state.num1+n;
return t;
}
};
// 创建一个 vuex store 实例
const aa = new Vuex.Store({
//设置数据state,它里面存放全局公共的数据
state,
//设置getters,它是state的计算属性
getters
});
new Vue({
el:'#app',
data:{},
// 把vuex实例注册到vue实例上,方便使用
store:aa,
components:{
son:{
template:'#son',
methods:{
add(){
this.$store.state.num++;
}
},
computed:{
//通过mapState把同名的state映射到计算属性中,它的含义是创建一个和
state中数据名一致的计算属性,并且值为state中数据的值
...Vuex.mapState(['num','name']),
...Vuex.mapState({
abc(state){
return state.name;
}
}),
//通过mapGetters把同名的getters映射到计算属性中,它的含义是创建一个和getters中方法名一致的计算属性,并且值为getters中的方法
...Vuex.mapGetters(['total','total1','addNum']),
}
}
}
});
</script>
27.vuex的mutations和actions
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<p>{{$store.state.num}}</p>
<!-- <button @click="add">点击++</button>
<button @click="add1">提交荷载</button> -->
<button @click="numAdd">点击++</button>
<button @click="numPas(100)">提交荷载</button>
<button @click="actionsFun(1000)">点击执行actios</button>
<div v-for="(item,index) in arr" :key="index">
{{item.name}}
</div>
<button @click="getList">仪器列表</button>
<button @click="list">仪器列表辅助函数版本</button>
</div>
</body>
</html>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<script src="js/vuex.js" type="text/javascript" charset="utf-8"></script>
<script src="js/axios.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
const state = {
num:10,
arr:[]
};
//mutations是修改state中数据的唯一入口,也就是修改数据必须使用mutations才行,在组件中执行mutations的方式是执行commit()
// mapMutations 辅助函数,映射同名方法
// mutations中的方法必须是同步方法,异步方法写在actions中
const mutations = {
numAdd(state){
return state.num++;
},
numPas(state,n){
return state.num+=n;
},
setData(state,obj){
state.arr = obj;
}
};
// actions 他执行的是 mutations中的方法,不会直接去更改state中的数据,
actios中的每一个方法接收一个 context ,他类似一个store实例对象,具有store中的任意对象和属性
// actions 可以执行异步操作
// 执行actions中的方法,使用 store.dispatch('方法名')
const actions = {
//actions中方法,也可以接收第二个参数,做为传入要执行的方法的参数
numPas(context){
context.commit('numPas',n);
},
list(context){
axios.get('http://zfvcloud.cn:8099/api/api/teacher/machine/list?pageNum=1&pageSize=10').then(res=>{
console.log(res.data);
context.commit('setData',res.data.rows);
});
}
};
const store = new Vuex.Store({
state,
//设置mutations
mutations,
//设置actions
actions
});
new Vue({
el:'#app',
data:{},
store,
computed:{
...Vuex.mapState(['arr'])
},
methods:{
/*
add(){
//使用commit方法执行 mutations中的numAdd方法
this.$store.commit('numAdd');
},
add1(){
//commit的第二参数,实际上就是numPas所提交的参数
this.$store.commit('numPas',100);
}
*/
//使用辅助函数
...Vuex.mapMutations(['numAdd','numPas']),
//actions 的辅助函数
...Vuex.mapActions(['list']),
//创建方法,执行actions
actionsFun(n){
// dispatch 方法可以接收参数,第一个参数是要执行的方法名,第二个参数是传入的参数,
传入的参数也可以是一个对象
this.$store.dispatch('numPas',n);
},
getList(){
this.$store.dispatch('list');
}
}
});
</script>
//生僻方法 $store.state
28.vuex的modules
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>modules</title>
</head>
<body>
<div id="app">
<p>{{$store.state.A.name}}</p>
<!-- 获取getters中的showName -->
<p>{{$store.getters['A/showName']}}</p>
<p>{{$store.state.B.name}}</p>
<button @click="ch()">点击</button>
<button @click="">点击map</button>
</div>
</body>
</html>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<script src="js/vuex.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
/*
vuex的模块module,它里面含有 state getter mutation action ,还可以含有子模块
以下是三个模块
如果需要访问模块A的state中的name值,则
$store.state.A.name 组件中 (A是模块名,name是访问的key)
this.$store.state.A.name 方法中
由于模块中的getters、mutations和actions 默认是全局命名空间,因此如果需要当前模块高度封装,
则可以添加命名空间
namespaced: true
添加命名空间以后
获取getters this.$store.getters['模块名/getters名字']
执行mutation this.$store.commit('模块名/方法名');
执行action this.$store.dispatch('模块名/方法名');
模块中state的写法
1.没有命名空间,为了让state满足只对当前模块起作用,必须以下写法
state:()=>({
name:'李广'
})
2.添加命名空间以后,可以直接写成对象格式 state:{}
*/
const moduleA = {
namespaced: true, //命名空间,添加命名空间,使得当前模块中的内容高度封装,
也就是所有的内容不再是全局命名空间
state: () => ({
name: '李广'
}),
getters: {
showName(state) {
return '但使龙城飞将在' + state.name;
}
},
mutations: {
change(state) {
state.name = '霍去病';
}
}
};
const moduleB = {
namespaced: true,
state: {
name: '李敢'
},
mutations: {
change(state) {
state.name = '李靖';
}
}
};
const moduleC = {
state: () => ({
name: '李凌'
})
};
const store = new Vuex.Store({
modules: {
A: moduleA,
B: moduleB,
C: moduleC
}
});
new Vue({
el: '#app',
data: {},
store,
methods: {
// ch() {
// this.$store.commit('B/change');
// },
...Vuex.mapMutations(['B/change'])
}
});
</script>
29 vuex modules的辅助函数
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>辅助函数</title>
</head>
<body>
<div id="app">
<h1>{{$store.state.A.name}}</h1>
<h1>{{name}}</h1>
<h1>{{nameLiang}}</h1>
<button @click="show">改变</button>
</div>
</body>
</html>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<script src="js/vuex.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
const A = {
namespaced: true,
state: {
name: '少年强'
},
getters: {},
mutations: {
show(state, str) {
state.name = '林徽因' + str;
}
},
actions: {}
};
const B = {
namespaced: true,
state: {
name: '梁启超'
},
getters: {},
mutations: {
mon(state) {
state.name = '梁思成';
}
},
actions: {}
};
const C = {
namespaced: true,
state: {},
getters: {},
mutations: {},
actions: {}
};
const store = new Vuex.Store({
modules: {
A,
B,
C
}
});
new Vue({
el: '#app',
data: {},
computed: {
/*
使用辅助函数,显示不同模块中的state数据
// ...Vuex.mapState(['A/name']) 不能这样设置
设置模块的mapState,有两个参数,第一个参数是模块名
第二个参数是对象,对象里面设置state数据
其中,第一个参数可以省略
*/
...Vuex.mapState('A', {
name(state) {
return state.name + '则国强';
}
}),
...Vuex.mapState({
//这里state就是一个参数,可以随便写 ,commit
nameLiang(state) {
return state.B.name + '则国强';
}
})
},
methods: {
/*
mutations 辅助函数
模块的mutations 辅助函数也具有两个参数,第一个参数是模块名,
第二个参数是 对象,对象里面设置方法,该方法默认传入一个commit参数
*/
...Vuex.mapMutations({
// 传入参数 args
show(state, args) {
//提交方法
state('A/show', '你是人间的四月天');
}
}),
...Vuex.mapMutations('B', {
mon(state) {
//提交方法
state('mon');
}
})
},
...Vuex.mapActions('C',['col']),
});
</script>
30 js原生修改 bom history
location.hash=‘aa’
history.pushState({},’’,‘aa’); 可返回
history.placeState({},’’,‘aa’) 替换
history.go()正负值,前进返回
31.vuecli
安装:
npm install -g vue
npm install -g @vue/cli
注意:第二个指令如果不成功,将npm更换为cnpm再试试。
创建
2.0 vue init webpack projectname
3.0 vue create projectname
安装淘宝镜像
npm config set registry https://registry.npm.taobao.org
32. 项目设置反向代理解决跨域问题
//项目根目录下创建vue.config.js 文件
//参考官方文档https://cli.vuejs.org/zh/config/#devserver
module.exports = {
devServer: {
// proxy: '要访问的公共基础url',
proxy: {
'/api': {
//要访问或者要跨域的端口
changeOrigin:true, //设置是否改变host的地址,这样就可以跨域了
target: 'http://localhost:3000',
pathRewrite: 这个必须写,访问的基础后缀名是/api
pathRewrite: {
//设置别名,路径重定向,表示如果接口是以/开头的,那么会重定向到http://localhost:3000
/api': '/',
},
},
},
},
};
//前端代码
show() {
axios.get('/api/abc').then((res) => {
console.log(res.data);
this.str = res.data;
});
},
32.Vue页面F5被刷新后,vuex 里的state内容全部丢失解决方案
核心
s
t
o
r
e
.
r
e
p
l
a
c
e
S
t
a
t
e
(
O
b
j
e
c
t
.
a
s
s
i
g
n
(
,
t
h
i
s
.
store.replaceState(Object.assign({}, this.
store.replaceState(Object.assign(,this.store.state,getObj(‘store’)));
主要通过监听用户离开或者刷新页面时,用storage记录store 中state的值,在vue刷新时通过created函数再获取storage中的值添加到store的state中
33.router-view 和动态路由component:is=“aa”(见16条)两者的区别
router-view
router-view是vueRouter的组件,主要是更新路由,可以不用跳转到一个新的页面,不会
更新初始化函数mounted和created,只更新<router-view></router-view>标签下所渲染
的组件。可以不用引入组件。
缺点
一个页面一个路由,跳转要写路由地址,更新tab样式要传参数
component :is=" "
component :is这个是vue的组件,is=要渲染的组件,不会更新路由,不会更新初始化函数
mounted和created。刷新页面后会跳回默认显示页面;
缺点
需要引入所有要显示的组件,并且components:{声明},
34.Vue.nextTick:msg
应用场景:需要在视图更新之后,基于新的视图进行操作
Vue 在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。
以上出现了事件循环的概念,其涉及到JS的运行机制,包括主线程的执行栈、异步队列、
异步API、事件循环的协作,此处不展开之后再总结。大致理解:主线程完成同步环境执行,
查询任务队列,提取队首的任务,放入主线程中执行;执行完毕,再重复该操作,该过程
称为事件循环。而主线程的每次读取任务队列操作,是一个事件循环的开始。异步callback
不可能处在同一事件循环中。
简单总结事件循环:
同步代码执行 -> 查找异步队列,推入执行栈,执行callback1[事件循环1] ->
查找异步队列,推入执行栈,执行callback2[事件循环2]...
即每个异步callback,最终都会形成自己独立的一个事件循环。
官方解释:
Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在
同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被
推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作
上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际
(已去重的) 工作。Vue 在内部尝试对异步队列使用原生的 Promise.then
和MessageChannel,如果执行环境不支持,会采用 setTimeout(fn, 0)代替。
35.*插槽,具名插槽、作用域插槽(更新后)
//子组件
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
//默认
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
1.具名插槽+默认插槽
//父组件
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
//默认
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
//也可以简写
<base-layout>
<template #header>
<h1>Here might be a page title</h1>
</template>
//默认
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template #footer>
<p>Here's some contact info</p>
</template>
</base-layout>
//父组件渲染出
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
//自定义
<div>啊啊</div>
</footer>
</div>
2.具名+传值(作用域插槽)(父操作子内容):原理通过在子组件绑定动态属性实现
//子级
//user为子组件中的数据
<slot name="header" :user="user"><slot>
//父级
<current-user>
<template v-slot:default="aa">
{{ aa.user.firstName }}
</template>
</current-user>
//如果一个组件中只有一个插槽,当做为默认插槽的时候,才不用写template标签,可以把要传的值直接
写在父级的标签上,其他都最好在内部写一个template标签,在其内部写子组件上的名字和要传的值
<current-user #default="aa">
{{ aa.user.firstName }}
</current-user>
<current-user>
<template #header="bb">
{{ bb.user.firstName }}
</template>
</current-user>