框架是什么?
帮助我们构建与业务(就是项目中的功能)无关的事情
例子:
在没有学习框架的时候 隔行变色(代码最少在10行)
在学习了框架之后 jquery
重点记忆
vue是什么?
vue的特点
MVVM是什么?请分别简述
多个vue实例可以吗?怎么用?
{{}}是什么?
vue的延迟挂载是什么?$mount是什么?
data的写法分别是什么?
什么是指令?
v-model 作用
双向绑定是什么与原理
v-show是什么?
vue.js
vue是什么?
vue就是一个当下最为主流的前端框架,用于构建用户界面的渐进式javascript框架
渐进式:先完成最基本的功能 开发应用 但是如果想开发复杂功能 那么就可以引入各种vue插件来完成(vue是一个自底向上逐层开发的一个框架)
谁开发的
尤雨溪
vue特点
1、组件化。实现了封装和重用,且组件间可以相互嵌套;2、轻量级。相对于其他框架,Vue学习成本低,简单易上手;3、虚拟 DOM。虚拟 dom 中存在 diff算法,是 cpu 密集型运算,占用内存较少,可以提高运行效率,并压缩运行时体积;4、Vue是一个MVVM框架,数据绑定。数据单项绑定(v-bind),单向指data中的值绑定到视图中,但视图中修改不会影响到data数据;双向绑定(v-model),数据发生变化会驱动视图的更新,视图的更新也会驱动数据的变化;5、单页面应用(SPA)。用户体验好,内容改变时不需要重新加载整个页面,不会进行多个 html 页面间的切换;服务器压力小等。
MVVM
MVVM是Model-View-ViewModel的简写
M --- model 模型==数据==变量 (只要今后听见了M 或者是模型 就直接想象成 data数据·) data数据·
V --- view 视图==页面==用户可以看见的界面 模板
VM --- viewModel 视图模型==用来关联数据与视图之前的桥梁 vue实例
Data Bindings 数据绑定 (按照上图描述 就是把model数据 ----》数据绑定 ----》绑定到页面上)
DOM Listeners dom监听 (按照上图描述 就是view页面的数据改变-----》dom监听------》反应到数据上)
HelloWord
淘宝镜像
安装淘宝镜像( npm服务器在国外可能会导致网速很慢导致下载失败,可以使用淘宝镜像 当然不是必须要安装 ---- 如果安装了今后所有使用npm 在下载内容的时候 就要把npm 变成 cnpm 剩下都一样)
npm install cnpm -g --registry=https://registry.npm.taobao.org
检查是否安装成功
cnpm -v
安装成功显示如图下:
vue初体验
1.下载vue库文件 npm init -y (初始化) npm install --save vue@2 简写 : npm i -S vue@2(下载)
2.新建html页面先把vue引用进来
3.编写如下代码
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<metahttp-equiv="X-UA-Compatible"content="IE=edge">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 1.先引用 -->
<scriptsrc="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<!-- M --- model 模型==数据==变量
V --- view 视图==页面==用户可以看见的界面
VM --- viewModel 视图模型==用来关联数据与视图之前的桥梁 -->
<!-- 2创建视图层 V vue的根容器 根节点 今后把你的vue内容都写在这个根节点中-->
<divid="demodiv">
<h1>{{text}}</h1>
<h1>{{num}}</h1>
<h1>{{obj.name}}</h1>
<h1>{{arr[4]}}</h1>
</div>
<script>
// 3.创建vm层 ---》 就是vue实例
newVue({
el:"#demodiv",//关联视图
data:{//4 m层 模型数据
text:"我是字符串",
num:18,
bool:true,
obj:{
name:"xixi",
age:18
},
arr:[1111,22222,33333,4444,5555]
}
})
</script>
</body>
</html>
多个vue实例
多个vue实例生效的就是对应的 视图
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<metahttp-equiv="X-UA-Compatible"content="IE=edge">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<title>Document</title>
<scriptsrc="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<divid="demodiv">
<h1>{{text}}</h1>
</div>
<divid="demodivb">
<h1>{{text}}</h1>
</div>
<script>
newVue({
el:"#demodiv",
data:{
text:"我是第一个",
}
})
newVue({
el:"#demodivb",
data:{
text:"我是第2个",
}
})
</script>
</body>
</html>
{{}}--模板语法--双大括号赋值法
在vue中{{}}被称之为模板语法 双花括号赋值法 vue数据插值 。。。。 作用:就是可以在双大括号中写入表达式 并且展示在页面中
语法:在你想展示数据的任何位置 {{表达式}} (表达式 通过计算可以返回结果的公式)
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<metahttp-equiv="X-UA-Compatible"content="IE=edge">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<title>Document</title>
<scriptsrc="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<divid="demodiv">
<h1>{{num}}</h1>
<!-- 因为{{表达式}}s所以运算符都可以写 -->
<h1>{{num*2}}</h1>
<h1>{{bool?"你好":"你坏"}}</h1>
<!-- 建议不要在{{}}写太复杂的内容 -->
<h1>{{text.toUpperCase().slice(1,5)}}</h1>
</div>
<script>
newVue({
el:"#demodiv",
data:{
num:666,
bool:false,
text:"abcdefghiaslkdjaskldhalk"
}
})
</script>
</body>
</html>
el与data的两种写法
$mount 手动挂载
Vue 的$mount()为手动挂载,在项目中可用于延时挂载(比如vue是一个渐进式的 在我们使用一些负载功能的时候 我们需要依赖很多后面会学到的vue插件 来完成 但是使用这些插件 我们必须先要把这些插件先挂载之后 在加载视图的关联 所以在这个时候我们会用到延迟健在 ),之后要手动挂载上。new Vue时,el和$mount并没有本质上的不同。
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<metahttp-equiv="X-UA-Compatible"content="IE=edge">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<title>Document</title>
<scriptsrc="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<divid="demodiv">
<h1>两种挂载方式</h1>
<h1>{{text}}</h1>
</div>
<script>
letv=newVue({
// el:"#demodiv",
data:{
text:"我是一个变量"
}
})
console.log(v);
v.$mount("#demodiv")
</script>
</body>
</html>
data函数式
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<metahttp-equiv="X-UA-Compatible"content="IE=edge">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<title>Document</title>
<scriptsrc="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<divid="demodiv">
<h1>两种挂载方式</h1>
<h1>{{text}}</h1>
</div>
<script>
letv=newVue({
//对象式写法
// data:{
// text:"我是一个变量"
// }
// 函数式写法
data(){
return {
text:"我是函数式变量"
}
}
})
console.log(v);
v.$mount("#demodiv")
</script>
</body>
</html>
指令
什么是html标签的属性?
通过写在html开标签中的 使用属性="属性值" 的这些东西 可以扩展标签的功能
什么是指令?
就是在vue中给html标签添加的带有v-前缀的特殊属性(在vue中 给html标签添加个一些特殊性功能属性)
v-model
作用 : 就是给表单元素进行数据的双向绑定
双向绑定
视图改变模型也会改变
模型变视图也会随之改变
语法: <标签 v-model=""值/>
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<metahttp-equiv="X-UA-Compatible"content="IE=edge">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<title>Document</title>
<scriptsrc="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<divid="demodiv">
<h1>v-model</h1>
<inputtype="text"v-model="inputval"/>
<h1>{{inputval}}</h1>
</div>
<script>
newVue({
el:"#demodiv",
data:{
inputval:""
}
})
</script>
</body>
</html>
双向绑定的原理
在vue中基于数据劫持--数据代理与发布者订阅者模式完成的
数据劫持:数据拦截 就是对data中的数据在初始化的时候监听起来(Object.defineProperty 来进行监听/代理) 当数据改变setter之后 vm就会知道 在视图改变getter 他就会通知模型你要修改了 模型改变了也会通知视图改变
发布者订阅者模式:就是一个一对多的关系 发布者就是数据提供者 订阅者就是页面展示的 一个发布者可以对应无数个订阅者 但是发布者改变了 所有订阅者也会改变
v-show
作用:控制dom元素的显示或者隐藏 v-show的显示和隐藏是通过css的dispaly方式来控制的
语法 v-show="布尔值" true显示 false隐藏
<!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>
<script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<input type="checkbox" v-model="bool"/>{{bool?"勾选了":"没勾选"}}
<h1 v-show="bool">我是站位的</h1>
</div>
<script>
new Vue({
el:"#demodiv",
data:{
bool:false
}
})
</script>
</body>
</html>
v-on
作用:就是给vue的dom绑定事件的
语法: onclick v-on:click onchange v-on:change
传统写法(写法稍微麻烦点): v-on:你的事件不加on="函数" v-on:click=”函数“
简写写法: @你的事件不加on="函数" @click="函数"
那今后工作用哪个 简写的函数 传统的 (想用那个用哪个)
注意:函数需要写在 与el data同级位置使用methods来进行包裹
<!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>
<script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<button v-on:click="fun()">点我触发函数</button>
<button @click="fun()">点我触发函数简写写法</button>
</div>
<script>
new Vue({
el:"#demodiv",
data:{
},
// 函数需要写在与el data同级的位置使用methods来进行包裹
methods:{
fun(){
console.log("你好")
},
}
})
</script>
</body>
</html>
注意 函数中怎么使用data数据 使用this.变量名(this指向的就是vue实例)
methods:{
fun(){
// console.log("你好")
console.log(this.text)
console.log(this._data.text);
},
}
v-for
概念: 用来遍历数据 生成页面内容
语法: v-for="(遍历的值,遍历的下标) in 你要遍历的数据"
<!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>
<script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<!-- 语法: v-for="(遍历的值,遍历的下标) in 你要遍历的数据" -->
<ul>
<li v-for="(v,i) in arr">
{{v}}------{{i}}
</li>
</ul>
</div>
<script>
new Vue({
el:"#demodiv",
data:{
arr:["ez","Vn","MF","noc"]
},
methods:{
}
})
</script>
</body>
</html>
遍历复杂数据
<!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>
<script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<!-- 语法: v-for="(遍历的值,遍历的下标) in 你要遍历的数据" -->
<ul>
<li v-for="(v,i) in arr">
{{v}}------{{i}}
</li>
</ul>
<hr/>
<table border="1">
<tr v-for="(v,i) in obj">
<td>{{v.name}}</td>
<td>{{v.age}}</td>
</tr>
</table>
</div>
<script>
new Vue({
el:"#demodiv",
data:{
arr:["ez","Vn","MF","noc"],
obj:[
{name:"xixi1",age:181},
{name:"xixi2",age:182},
{name:"xixi3",age:183},
{name:"xixi4",age:184},
{name:"xixi5",age:185},
{name:"xixi6",age:186}
]
},
methods:{
}
})
</script>
</body>
</html>
key属性
可以在是用v-for的时候搭配使用 他的作用就是给便利出来的dom 起个唯一的名字 相当于我们的身份证号 通过添加了这个key属性可以增加我们在便利展示的时候生成dom元素的效率 (key里面是唯一的不建议使用我们便利出来的下标 因为如果一个页面有两次便利的话key的值就有可能相同)
v-bind 这个是初学者最容易忘记的 但是很重要
概念: 就是html的属性插入变量
语法:
传统写法: v-bind:html的属性="值"
简写写法: :html的属性="值"
<!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>
<script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<a v-bind:href="ahref">{{text}}</a>
<a :href="ahref">{{text}}</a>
</div>
<script>
new Vue({
el:"#demodiv",
data:{
text:"点我去百度",
ahref:"http://www.baidu.com"
}
})
</script>
</body>
</html>
v-if全家桶
v-if
作用:就是对dom元素进行移除和添加
语法: 写在开标签中 true添加 false移除
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>v-if</h1>
<input type="checkbox" v-model="bool"/>{{bool}}
<h1 v-show="bool">我是v-show的标签</h1>
<h1 v-if="bool">我是v-if的标签</h1>
</div>
<script>
new Vue({
el:"#demoDiv",
data:{
bool:true
},
methods:{
}
})
</script>
</body>
</html>
v-if与v-show的区别
1.v-show是使用css的方式对dom元素进行显示和隐藏的 在频繁切换的时候效率更高 在初始化的时候对性能的损耗比较高
2.v-if是直接把这个dom元素移除或者是添加 在频繁切换的时候效率比较低 在初始化的时候对性能的损耗比较低
v-else
语法: 必须配合v-if使用 不能单独使用
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>v-if</h1>
<input type="checkbox" v-model="bool"/>{{bool}}
<h1 v-if="bool">请您登录</h1>
<h1 v-else>欢迎您尊敬的vip</h1>
</div>
<script>
new Vue({
el:"#demoDiv",
data:{
bool:true
},
methods:{
}
})
</script>
</body>
</html>
v-else-if
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>v-else-if</h1>
<select v-model="text">
<option value="吃饭">吃饭</option>
<option value="睡觉">睡觉</option>
<option value="在吃饭">在吃饭</option>
<option value="在睡觉">在睡觉</option>
</select>
<h1 v-if="text=='吃饭'">我是吃饭的dom</h1>
<p v-else-if="text=='睡觉'">我是睡觉的dom</p>
<em v-else-if="text=='在吃饭'">我是在吃饭的dom</em>
<h2 v-else-if="text=='在睡觉'">我是在睡觉的dom</h2>
<b v-else>我什么都没有干</b>
</div>
<script>
new Vue({
el:"#demoDiv",
data:{
text:""
},
methods:{
}
})
</script>
</body>
</html>
v-if与v-for能同时使用吗
当 v-if 与 v-for 不推荐同时使用,v-for 具有比 v-if 更高的优先级,这意味着 v-if 将分别重复运行于每个 v-for 循环中,影响速度
如果必须要使用的话 变成一个嵌套关系
解决方式
1.使用computed处理数据在便利
2.使用v-show替代v-if
v-html
作用:就是把字符串标签插入到页面中
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>v-html</h1>
{{text}}
<div v-html="text">
</div>
</div>
<script>
new Vue({
el:"#demoDiv",
data:{
text:"<h1>我是一个h1</h1>"
},
methods:{
}
})
</script>
</body>
</html>
v-once
作用: 一次性插值 数据插入到页面中就不会被改变了
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>v-once</h1>
<input type="text" v-model="text">
<h1>{{text}}</h1>
<h1 v-once>{{text}}</h1>
<h1>{{text}}</h1>
<h1>{{text}}</h1>
<h1>{{text}}</h1>
<h1>{{text}}</h1>
<h1>{{text}}</h1>
<h1>{{text}}</h1>
<h1>{{text}}</h1>
</div>
<script>
new Vue({
el:"#demoDiv",
data:{
text:"我是默认值"
},
methods:{
}
})
</script>
</body>
</html>
v-text
作用:向dom中插入文本内容 和{{}}作用一样
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>v-text</h1>
<h1>{{num}}</h1>
<h1 v-text="num"></h1>
</div>
<script>
new Vue({
el:"#demoDiv",
data:{
num:"你好么么哒!!!!^_!"
},
methods:{
}
})
</script>
</body>
</html>
屏幕闪烁
当用户的设备和网络比较慢的时候可能就会在页面中吧{{}}全部展现出来 当网络恢复正常之后 有突然间显示ok
使用{{}}模板语法的话就会出现这个问题
解决方式
1.使用v-text (全部在页面中使用v-text指令很麻烦)
2.使用v-cloak指令 等待vue实例渲染完成之后在进行(这个指令是防止页面加载时出现 vuejs 的变量名而设计)
<!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>
<!-- <script src="./node_modules/vue/dist/vue.js"></script> -->
<style>
[v-cloak]{
display: none;
}
</style>
</head>
<body>
<div id="demoDiv" v-cloak>
<h1>v-text</h1>
<h1>{{num}}</h1>
<h1 v-text="num"></h1>
</div>
<script>
new Vue({
el:"#demoDiv",
data:{
num:"你好么么哒!!!!^_!"
},
methods:{
}
})
</script>
</body>
</html>
事件对象
事件对象--$event
谁触发这个事件事件对象就指向谁 (事件对象中包含触发这个事件的元素所有信息)类似于拖拽
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demoDiv">
<!-- 事件对象 $event -->
<input type="text" @keydown="fun($event)">
</div>
<script>
let vm = new Vue({
data: {
},
methods: {
fun(e) {
console.log(e)
if (e.keyCode == 90) {
console.log("z被按下了")
}
}
}
})
vm.$mount("#demoDiv")
</script>
</body>
</html>
修饰符
通过vue提供的修饰符可以来处理dom事件的一些细节性的内容
v-on:事件.修饰符="函数()"
按键修饰符
优化我们对于键盘事件的相关处理
.up .down .left .right .ctrl .space .enter .tab .delete .shift .esc
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demoDiv">
<input type="text" @keydown.space="fun()">
</div>
<script>
let vm = new Vue({
data: {
},
methods: {
fun() {
console.log("空格被按下了")
}
}
})
vm.$mount("#demoDiv")
</script>
</body>
</html>
事件修饰符
.stop修饰符 阻止事件传播
.prevent修饰符 阻止事件默认行为
.captrue修饰符 设置捕获
.self 修饰符 只会触发自己范围内的事件 不包含子元素
.once修饰符 只触发当前事件一次
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
<style>
.fu {
width: 600px;
height: 600px;
background-color: pink;
}
.zi {
width: 300px;
height: 300px;
background-color: goldenrod;
}
</style>
</head>
<body>
<div id="demoDiv">
<div class="fu" @click="fufun()">
<div class="zi" @click.stop="zifun()"></div>
</div>
</div>
<script>
let vm = new Vue({
data: {
},
methods: {
fufun() {
console.log("fufufuffufufu")
},
zifun() {
console.log("zizizizziizzi")
},
}
})
vm.$mount("#demoDiv")
</script>
</body>
</html>
v-model修饰符
lazy修饰符
用户使用v-model之后,用户每次修改输入内容,都会将数据同时绑定,为了避免这种情况的发生,使用lazy修饰符来进行限定。只有当用户的input中失去焦点或者用户点击回车按钮时,才会将的数据进行修改。
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demoDiv">
<input type="text" v-model.lazy="inputval">
<h1>{{inputval}}</h1>
</div>
<script>
let vm = new Vue({
data: {
inputval:"我是默认值"
}
})
vm.$mount("#demoDiv")
</script>
</body>
</html>
number修饰符
当用户在input中输入数字时,浏览器会默认将输入的数字转化为string类型,使用number修饰符来将输入的数字转为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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demoDiv">
没有使用number
<input type="text" v-model="inputval">
<hr>
使用了number
<input type="text" v-model.number="inputval">
<hr>
<button @click="fun()">点我查看类型</button>
</div>
<script>
let vm = new Vue({
data: {
inputval:0
},
methods:{
fun(){
console.log(typeof(this.inputval))
}
}
})
vm.$mount("#demoDiv")
</script>
</body>
</html>
trim修饰符
使用trim修饰符来去掉字符串首部或者尾部的所有空格
计算属性--computed
引子
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demodiv">
<!-- 因为view区域是展示数据的 我们在这个里面处理数据 不合适 会导致页面的可读性比较差 -->
<h1>基本展示:{{text}}</h1>
<h1>大写:{{text.toUpperCase()}}</h1>
<h1>大写截取:{{text.toUpperCase().slice(1,4)}}</h1>
</div>
<script>
let vm=new Vue({
el:"#demodiv",
data:{
text:"abcdefghijk"
},
})
</script>
</body>
</html>
上面的代码没有问题但是后期可能会导致代码的可读性非常差
所以在我们遇到了一条数据 在不同位置 展示出不同形态的时候 我们可以使用计算属性
基本概念
在弄懂什么是计算属性之前 思考一个问题 什么是属性
什么是计算属性?
就是把属性通过加工通过计算返回一个新的结果那么这个就是计算属性
那么计算属性写在哪里 并且怎么写呢?
传统写法
写在与el data methods 同级位置 使用 computed
其中有两个方法 get与set
get方法:必须要写,该函数不接受参数,当初次读取计算属性或者计算属性所依赖的数据发生变化时被调用,getter函数有一个返回值,该返回值就是计算属性的值
computed:{
计算出存储结果的名字:{
//必须要写,该函数不接受参数
//什么时候被调用?:当初次读取计算属性或者计算属性所依赖的数据发生变化时被调用,getter函数有一个返回值,该返回值就是计算属性的值
get(){
return 你的计算逻辑
},
//可选项 接受一个可选参数(计算属性被修改之后的值)
//什么时候被调用?: 当计算属性被修改时被调用
set(value){
}
}
}
读取
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demodiv">
<h1>读取data中的数据--{{text}}</h1>
<h1>读取计算属性的数据--{{newtext}}</h1>
</div>
<script>
let vm=new Vue({
el:"#demodiv",
data:{
text:"abcdefghijk"
},
// 计算属性
computed:{
newtext:{
get(){
console.log("我是计算属性的get方法")
return this.text.toUpperCase()
}
}
},
})
</script>
</body>
</html>
修改
set方法:可选项 接受一个可选参数(计算属性被修改之后的值)当计算属性的值被修改时被调用
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demodiv">
<h1>读取data中的数据--{{text}}</h1>
<input type="text" v-model="newtext">
<h1>读取计算属性的数据--{{newtext}}</h1>
</div>
<script>
let vm=new Vue({
el:"#demodiv",
data:{
text:"abcdefghijk"
},
// 计算属性
computed:{
newtext:{
get(){
console.log("我是计算属性的get方法")
return this.text.toUpperCase()
},
set(value){//当newtext被修改的时候set就会触发 value就是修改之后的数据
console.log("我是计算属性的set方法",value)
}
}
},
})
</script>
</body>
</html>
简写写法推荐使用
通常我们的计算属性都是把计算出来的结果展示到页面上 set这种修改使用的非常少 所以在计算属性中也给我们提供了一种读取的简写语法
computed:{
你处理好的变量(){
return 你的处理逻辑
}
}
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demodiv">
<h1>读取data中的数据--{{text}}</h1>
<h1>读取计算属性的数据--{{newtext}}</h1>
</div>
<script>
let vm=new Vue({
el:"#demodiv",
data:{
text:"abcdefghijk"
},
// 计算属性简写
computed:{
newtext(){
return this.text.toUpperCase()
}
}
})
</script>
</body>
</html>
计算属性是依赖data数据的
计算属性是依赖data里面的属性的 当计算属性依赖的data数据改变了 那么计算属性也会收到通知 做出相关的计算返回新的结果
计算属性与方法methods的区别
计算属性处理的数据 如果被多次调用的时候 计算属性只执行一次(因为计算属性在第一次处理好数据之后 就会把数据放到缓存中 之后的每次读取都是从缓存中读取的 所以 多次调用他只执行一次)总结一下就是计算属性是依赖缓存的
方法 他是一个憨憨 只要被调用就会执行 那么相对于计算属性依赖缓存而言 他更消耗内存
watch监听/侦听属性
watch是vue实例的一个属性 他的作用就是用来监听data中的数据 当数据变了watch就会触发 从而调用函数处理一些逻辑
简写写法
语法:写在与el data methods 同级位置
watch:{
你要监听的data数据(newval,oldval){
}
}
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>watch</h1>
<input type="text" v-model="text">
<h1>{{text}}</h1>
</div>
<script>
new Vue({
el:"#demoDiv",
data:{
text:""
},
methods:{
},
// watch监听数据
watch:{
text(newval,oldval){
console.log(newval+"---"+oldval)
}
}
})
</script>
</body>
</html>
watch在初始化能触发吗?
不会触发
想再初始化触发watch怎么办?
watch 的一个特点是,最初绑定的时候是不会执行的,要等到 监听的数据 改变时才执行监听。那我们想要一开始就让他最初绑定的时候就执行改怎么办呢?
watch 传统写法之属性与方法
handler方法
data监听的数据改变触发的回调函数
//text变量改变的时候。handler方法就会触发
watch:{
text:{
handler(newval,oldval){
console.log("aaaa");
},
},
}
immediate属性
watch默认绑定,页面首次加载时,是不会执行的。只有值发生改变才会执行。
设置immediate为true后,监听会在被监听值初始化的时候就开始,也就页面上的数据还未变化的时候。
watch:{
text:{
handler(){
console.log("aaaa");
},
immediate:true //true就表示会立即执行
},
}
deep 属性
属性 deep,默认值是 false,代表是否深度监听
观察下面的代码
当我们在在输入框中输入数据视图改变obj.name的值时,我们发现是无效的。
watch在监听对象的时候受现代 JavaScript 的限制. Vue 不能检测到对象属性的添加或删除。
如果我们需要监听obj里的属性name的值呢?
<template>
<div>
<input type="text" v-model="obj.name">
<h1>{{obj.name}}</h1>
</div>
</template>
<script>
export default {
data(){
return {
obj:{
name:"xixi"
}
}
},
watch:{
obj:{
handler(){
console.log("aaaa");
},
immediate:true //true就表示会立即执行
},
}
}
</script>
<style>
</style>
这时候deep属性就派上用场了。
deep的意思就是深入观察,监听器会一层层的往下遍历,给对象的所有属性都加上这个监听器,但是这样性能开销就会非常大了,任何修改obj里面任何一个属性都会触发这个监听器里的 handler。
<template>
<div>
<input type="text" v-model="obj.name">
<h1>{{obj.name}}</h1>
</div>
</template>
<script>
export default {
data(){
return {
obj:{
name:"xixi"
}
}
},
watch:{
obj:{
handler(){
console.log("aaaa");
},
immediate:true, //true就表示会立即执行
deep:true
},
}
}
</script>
<style>
</style>
使用了deep之后 他会把对象中的所有属性都监听到不管你用不用 反正都会给你监听 那么这样性能开销就会非常大了
deep优化
优化,我们可以是使用字符串形式监听
<template>
<div>
<input type="text" v-model="obj.name">
<h1>{{obj.name}}</h1>
</div>
</template>
<script>
export default {
data(){
return {
obj:{
name:"xixi"
}
}
},
watch:{
// 字符串方式
"obj.name":{
handler(){
console.log("aaaa");
},
immediate:true
},
}
}
</script>
<style>
</style>
总结
如果进行被问到了watch任何知识点?
首先watch的作用就是监听data数据 当data的数据改变了 watch就会触发一个回调函数完成对应的逻辑操作。
其次watch有两种写法 第一种简写 使用监听的数据对应一个函数 里面有两个形参newval和oldval
第二种就是传统写法 监听的数据里面 有一个handler方法 这个方法就是被监听的数据被触发之后 调用的回调函数 当然里面也有两个形参newval和oldval 其次里面还有一个属性immediate属性 他的作用是watch'首次加载不会触发 如果想让watch首次加载/初始化监听 那么就可以设置这个属性为true 开启初始化监听 还有就是 vue不能检测到对象的属性操作 所以我们如果要监听对象的属性 那么必须使用deep开启深度监听 但是开启deep之后还有问题 就是deep会把监听的对象所有属性都监听上 不管用不用都监听 浪费资源 所以我们也可以使用字符串的方式来监听对象的属性 从而优化性能
扩展--计算属性与watch的区别
计算属性 : 计算属性是依赖data的数据 当data的数据改变之后 计算属性会重新计算返回一个新的结果
watch: watch是监听data的数据 当data的数据改变之后 watch会调用一个回调函数完成一些特定的逻辑
计算属性是同步的 watch是异步的
计算属性(computed) | 属性检测(watch) |
首次运行 | 首次不运行 |
默认深度依赖 | 默认浅度观测 |
适合做筛选,不可异步 | 适合做执行异步或开销较大的操作 |
自定义过滤器
在不改变原始数据的情况下格式化展示内容
我们今后的数据都是从后台请求过来的--但是有的时候后台给我们的数据网王和我们想展示的结果不太一样
过滤器的地方有很多,比如单位转换、数字打点 59,999、文本格式化 斯大林格日勒保卫战 大于5个字 截取并且在后面加...、时间格式化 2022825 2022-8-25
内置过滤器
在vue2x中 vue已经取消了内置过滤器 在vue1x中有内置和自定义过滤器
在面试的时候有很多挖坑是提问 很可能人家说 你给我简述下vue的内置过滤器?
你的想法是老师那货没有教我 咋办呀?
你的回答是 您刚才提到的是vue的内置过滤器 在我所知 vue在1x中是有内置过滤器的 但是我学习vue的时候1x已经淘汰了 我是从2x开始学习的 所以这个内置过滤器没有太多的了解 但是如果贵公司需要 我也可以第一时间掌握 因为就是一个小小的过滤没有什么复杂的
自定义过滤器
全局过滤器--filter
在所有vue实例组件中都可以使用
定义
使用filter()来定义全局过滤器
写在main.js
语法:
Vue.filter("过滤器的名字",(你要过滤的数据)=>{return 逻辑})
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>{{text|xiaoming}}</h1>
<h1>{{textb|xiaoming}}</h1>
</div>
<script>
// 全局过滤器
Vue.filter("xiaoming",(val)=>{
if(val.length>5){
return val.substr(0,5)+"..."
}else{
return val
}
})
let vm = new Vue({
data:{
text:"斯大林格日勒保卫战",
textb:"大话西游"
}
})
vm.$mount("#demoDiv")
</script>
</body>
</html>
使用
在想使用的地方 使用| 来完成
<h1>{{变量|过滤器的名字}}</h1>
局部过滤器--filters
仅仅只能在指定的实例组件中使用
定义
写在el data等同级位置
filters:{
过滤器的名字(你要过滤的数据){
return 你的逻辑
}
}
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>{{text|xiaoming}}</h1>
<h1>{{textb|xiaoming}}</h1>
</div>
<script>
let vm = new Vue({
data: {
text: "斯大林格日勒保卫战",
textb: "大话西游"
},
filters: {
xiaoming(val) {
if (val.length > 5) {
return val.substr(0, 5) + "..."
} else {
return val
}
}
}
})
vm.$mount("#demoDiv")
</script>
</body>
</html>
使用
在想使用的地方 使用| 来完成
<h1>{{变量|过滤器的名字}}</h1>
注意
全局过滤器和局部过滤器的优先级
注意:当全局过滤器和局部过滤器重名时,会采用局部过滤器
钩子函数
在vue中可以自动执行的函数叫做钩子函数
自定义指令
在现有内置指令不够用的时候 我们可以自己定义指令来进行使用
自定义指令的钩子函数
bind 指令绑定到元素之上的时候执行 但是只执行一次
unbind 指令被移除的时候执行 只执行一次
update 所有组件节点更新的时候执行调用
componentUpdate 指令所在节点以及所有的子节点都更新完成的时候调用
inserted 绑定指令的元素在页面展示的时候调用
全局自定义指令--directive
在所有位置都能生效
语法
Vue.directive("自定义指令名字",{
自定义指令钩子函数(el你绑定自定义指令的dom){
你的逻辑
}
})
使用
指令 就是在vue中带有v-前缀的html特殊属性
在你想使用的位置 v-你的自定义指令名
局部自定义指令--directives
只能在局部范围内生效
语法:写在与data methods watch computed 同级的位置
directives:{
自定义指令的名字:{
自定义指令的钩子函数(el代表的就是指定放在那个dom上形参就是谁){
你的逻辑
}
},
自定义指令的名字2:{
},
}
使用 v-自定指令的名字
<template>
<div>
<input type="text" v-xiaoming/>
</div>
</template>
<script>
export default {
// 创建
directives:{
xiaoming:{
inserted(el){
el.focus()
}
}
}
}
</script>
<style>
</style>
vue/cli 脚手架--5x
脚手架就是项目的开发环境 在脚手架中已经把我们需要开发的一切环境已经配置好了 我们只需要直接写业务代码即可
脚手架创建
1全局安装脚手架 npm install -g @vue/cli
2查看版本 vue --version
上面的两部 除非你重新装系统或者 重新装node了 否则不需要重复
3。创建项目
(3-1)把你的cmd切换到你要创建项目的文件夹中
(3-2)vue create 你的项目名不要中文空格特殊符号
4 cd到项目名下
5启动项目 npm run serve
6 根据提示打开浏览器访问
拿到空项目怎么办?
1.删除src文件夹下的components文件夹下的helloword.vue文件
2.在components文件夹中创建属于你自己的文件xxxx.vue(名字必须使用大驼峰命名法 首字母大写其后单词也要大写)
3.在vscode下载 vetur插件 安装完成 在你新建的文件中 使用<vue这个快捷键
参考视频
扩展----项目怎么启动?
1.cd项目下 npm install 下载依赖
2.启动项目 找到项目下的package.json文件 的scripts节点去查看启动命令
注意 所有单词都是npm run 你的配置 唯独 start不一样 因为start可以不加run
"scripts": {
"diaodeyipi": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
扩展----自动开启浏览器与端口修改
1.找到vue。config.js文件写入如下内容
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
// 设置浏览器自动开启
devServer:{
open:true,//设置自动开启
port:8888,//修改端口
host:"localhost"
}
})
组件化
组件的基本概念
组件的本质就是自定义标签
组件其实就是把我们的页面差分成一个个的小模块 分开编写 增加开发效率 降低维度难度 复用性更高
组件的创建
在vue中组件使用.vue文件来进行表示 .vue文件叫做单文件组件
在创建组件的时候 我们是在components文件夹中进行创建的
.vue文件基本页面内容
一个.vue文件中 有三个部分
template -------》写html
script -------》写js逻辑
style -------》写css
<template>
<div>
我是轮播图
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
组件的分类
全局组件---component
有的时候 一个组件在很多个地方都要被重复使用 那么默认情况下 我们使用局部组件的引用调用使用 每次在这样写很麻烦
全局组件 只需要配置一次main.js 那么就可以在当前项目的任意位置直接使用(全局组件慎用 因为全组件可能会造成组件命名污染)
import Vue from 'vue'
import App from './App.vue'
// 1.引用
import AllCom from "./components/AllCom.vue"
// 2.配置全局组件
// Vue.component(“给你这个全局组件起个名字”,你所要对应使用的组件)
Vue.component("AllCom",AllCom)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
局部组件---components
局部组件 只能在特定区域使用的组件 谁引用的 谁才能用
1.引用
<template>
<div></div>
</template>
<script>
// 1.引用 组件的名字要大写
import Bottombar from "./components/bottombar/index.vue"
import List from "./components/list/index.vue"
import Slider from "./components/slider/index.vue"
export default {
name: 'App',
}
</script>
<style lang="scss">
</style>
2.调用
<template>
<div></div>
</template>
<script>
// 1.引用 组件的名字要大写
import Bottombar from "./components/bottombar/index.vue"
import List from "./components/list/index.vue"
import Slider from "./components/slider/index.vue"
export default {
name: 'App',//命名空间 给这个组件起个名字
// 2。调用
components:{
// 名字:你引用的组件
Bottombar,
List,
Slider
}
}
</script>
<style lang="scss">
</style>
3.使用
<template>
<div>
<!-- 1.使用 -->
<Bottombar></Bottombar>
<List></List>
<Slider></Slider>
</div>
</template>
<script>
// 1.引用 组件的名字要大写
import Bottombar from "./components/BottomBar/BottomBar.vue"
import List from "./components/ListCom/ListCom.vue"
import Slider from "./components/SliderCom/SliderCom.vue"
export default {
name: 'App',//命名空间 给这个组件起个名字
// 2。调用
components:{
// 名字:你引用的组件
Bottombar,
List,
Slider
}
}
</script>
<style lang="scss">
</style>
组件样式隔离--scoped
使用scoped属性可以让 当前样式仅对当前组件生效
<style scoped>
div{
color:green;
}
</style>
之前学的内容怎么用
之前学过 data methods watch 还有一堆堆指令
除了data以外 剩下的都一样
<template>
<div>
你是轮播图--{{text}}
<button @click="fun()">点我</button>
</div>
</template>
<script>
export default {
// 在组件中data是一个函数return一个对象
data(){
return {
text:"我是字符串",
num:666,
bool:true,
obj:{name:"xixi"}
}
},
methods:{
fun(){
console.log("么么哒")
}
}
}
</script>
<style scoped>
div{
color:green;
}
</style>
vue组件的data为什么是一个函数?
数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。
父子组件
组件和组件之间相互嵌套
父组件
<template>
<div>
fufufuffufufufufufu
<Zicom></Zicom>
</div>
</template>
<script>
import Zicom from "./ZiCom"
export default {
components:{
Zicom
}
}
</script>
<style>
</style>
子组件
<template>
<div>
zizizizizzizizizi
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
app.vue
<template>
<div>
<FuCom/>
</div>
</template>
<script>
import FuCom from "./components/NobCom/FuCom.vue"
export default {
name: 'App',
components:{
FuCom
}
}
</script>
<style lang="scss">
</style>
组件传值
父组件的数据 子组件不能直接使用
子组件的数据 父组件也不能直接使用
组件与组件之间是一个完整地 独立地个体 他们之间的数据 默认是不能相互使用的
正向传值---父组件给子组件数据---props
props是vue实例的一个属性 他的作用是用来让组件接受外部传递进来的数据
语法:
写在data methods watch 的同级
props:[接收参数1,接受参数2,........n]
基本props使用
1.在子组件中 使用props来定义接收参数
<template>
<div>
<!-- 2.使用 -->
zizizizizizizzi--{{xiaoming}}---{{xiaohong}}
</div>
</template>
<script>
export default {
// 1.定义接收参数
props:["xiaoming","xiaohong"]
}
</script>
<style>
</style>
2父组件 开始给子组件传递参数
<template>
<div>
fuffufufufufufufuf
<!-- 在子组件被调用的地方 把props的参数当成属性进行传值 -->
<Zicom :xiaoming="text" :xiaohong="num"/>
</div>
</template>
<script>
import Zicom from "./ZiCom.vue"
export default {
components:{
Zicom
},
data(){
return {
text:"我是字符串",
num:18
}
}
}
</script>
<style>
</style>
props验证语法
在上面的例子中 大家会发现 我们给子组件传递任意数据类型都可以 但是如果我们想限制父组件给子组件传递的数据类型时候 那么就要使用props验证
props验证可以对传递给子组件的数据 进行约束(数据类型 默认值 等)
props验证 仅仅只在浏览器的控制台中打印出警告 但是不会对页面的展示造成影响(props验证只是给我们开发人员一个数据类型不匹配的 提示)
语法:
props:{
接受参数:{
type:数据类型,//验证类型
default:"默认值"
},
接受参数2:{
type:数据类型
},
}
子组件
<template>
<div>
<!-- 2.使用 -->
zizizizizizizzi--{{xiaoming}}---{{xiaohong}}
</div>
</template>
<script>
export default {
// 1.定义接收参数
// props:["xiaoming","xiaohong"]
// props验证
props:{
xiaoming:{
type:String
},
xiaohong:{
type:Boolean
}
}
}
</script>
<style>
</style>
逆向传值--- 子组件给父组件数据
$emit自定义事件
逆向传值默认是不被允许的 我们需要使用一些歪门邪道
需要使用自定义事件来完成 $emit()
注意:很多同学后期在被问到$emit是什么的时候 总会回到他是逆向传值 这个回答是错的 因为$emit是自定义事件 而逆向传值只是他能完成的一个小功能
1.在子组件中必须必须通过事件来触发自定义事件的抛出
<template>
<div>
zzizizizizizizizi
<!-- 1.通过事件调用一个自定义事件的创建 -->
<button @click="fun()">点我逆向传值</button>
</div>
</template>
<script>
export default {
data(){
return {
zitext:"我是子组件的数据么么哒!!!!!"
}
},
methods:{
fun(){
// 2.在函数中使用$emit来创建一个自定义事件
// this.$emit("自定义事件的名字",你要传递的数据)
this.$emit("zipao",this.zitext)
}
}
}
</script>
<style>
</style>
2.父组件接收
<template>
<div>
fufufufufufufuf
<!-- 在子组件被调用的时候 得到刚才的自定义事件 函数不加()-->
<!-- 在子组件被调用的时候 得到刚才的自定义事件 函数不加()-->
<!-- 在子组件被调用的时候 得到刚才的自定义事件 函数不加()-->
<!-- 在子组件被调用的时候 得到刚才的自定义事件 函数不加()-->
<!-- 在子组件被调用的时候 得到刚才的自定义事件 函数不加()-->
<!-- 在子组件被调用的时候 得到刚才的自定义事件 函数不加()-->
<!-- 在子组件被调用的时候 得到刚才的自定义事件 函数不加()-->
<Zicom @zipao="fun"/>
</div>
</template>
<script>
import Zicom from "./ZiCom.vue"
export default {
components:{
Zicom
},
methods:{
// 有个形参 这个形参接收的就是刚才自定义事件的第二个参数
fun(val){
console.log(val)
}
}
}
</script>
<style>
</style>
ref
把ref绑定到子组件身上 那么就可以得到当前这个子组件的所有信息 包含他的data数据 从而完成了逆向传值
同胞传值--- 兄弟组件(有一个共同的父组件)传值
跨层级传值 --- 爷爷组件给孙子组件---vuex
因为vue中是单向数据流(组件与组件之间的数据传递只能是单向一层一层的进行传值)那么在多层级传值的时候如果我们一层一层的传值 非常麻烦
vuex是什么?
vuex就是在vue中的统一状态(数据)管理工具
vuex中 命名空间 namespaced :在项目中,如果需要用到Vuex,可以将store分割成多个模块(module),每个模块拥有自己的state、mutation、action、getter,这样你的模块具有更高的封装度和复用性,此时就用到了命名空间这个概念。( 默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的,这样使得多个模块能够对同一 mutation 或 action 作出响应。)
如何使模块成为一个命名空间模块?
你可以在单个模块中通过添加namespaced:true的方式使其成为带命名空间的模块。
export default {
namespaced:true,
state,
getters,
actions,
mutations
}
1234567
vuex创建
在创建的时候选择vuex即可
vuex5大属性---state---数据源
state属性的作用 就是在vuex中存放数据的 我们今后在vuex的所有数据都写在state中
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {//就是数据源 存放数据的地方
text:"我是字符串",
num:18,
bool:true,
arr:[1111,2222,3333],
obj:{
name:"xixi",
age:18
}
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
}
})
使用state的数据
因为vuex是统一状态管理 所以在项目下的任何数据 都可以直接使用state的数据
语法: this.$store.state.xxx
方式1 直接读取
<template>
<div class="about">
<h1>This is an about page---{{this.$store.state.text}}</h1>
</div>
</template>
方式2 使用计算属性间接读取
<template>
<div class="about">
<h1>This is an about page---{{this.$store.state.text}}</h1>
<h1>计算属性方式-- {{newbool}}</h1>
</div>
</template>
<script>
export default{
computed:{
newbool(){
return this.$store.state.bool
}
}
}
</script>
vuex5大属性---module---模块
随着项目的体积逐渐增大 那么变量与今后的其他操纵就会在vuex的文件中 增多 导致这个文件中的内容原来越冗余 后期也几乎无法维护
为了避免上述情况 所以我们可以使用vuex的模块把内容进行拆分
1.在store文件夹下创建文件夹用来存放我们拆分的模块
2.创建模块文件 写入如下内容
可以直接从store下的index里面复制过来
let aboutm={
state: {//就是数据源 存放数据的地方
text:"我是字符串",
bool:true,
},
getters: {
},
mutations: {
},
actions: {
},
}
// 必须暴露
export default aboutm
3.在store下的index.js 中关联并使用我们创建的模块
import Vue from 'vue'
import Vuex from 'vuex'
// 1.引用模块
import homem from "./modules/homem.js"
import aboutm from "./modules/aboutm.js"
Vue.use(Vuex)
export default new Vuex.Store({
modules: {//2配置模块
homem,
aboutm
}
})
4,如果vuex被拆成了模块的话 那么我们要使用数据 必须使用
this.$store.state.模块名.xxxx
<template>
<div class="about">
<h1>This is an about page---{{this.$store.state.aboutm.text}}</h1>
<h1>计算属性方式-- {{newbool}}</h1>
<h1>{{this.$store.state.homem.num}}</h1>
</div>
</template>
<script>
export default{
computed:{
newbool(){
return this.$store.state.aboutm.bool
}
}
}
</script>
vuex5大属性---mutations---修改数据的
mutations的作用就是在vuex中修改state的 如果想修改state的数据必须使用mutations来修改
mutations是一个属性 这个属性中包含的是一个个修改的函数 mutaitons想使用 那么我们需要在组件中使用commit()来进行调用
let aboutm={
state: {//就是数据源 存放数据的地方
text:"我是字符串",
bool:true,
},
getters: {
},
mutations: {
我是修改函数1(){
},
我是修改函数1(){
},
我是修改函数1(){
},
我是修改函数1(){
},
我是修改函数1(){
},
},
actions: {
},
}
// 必须暴露
export default aboutm
组件内使用commit调用
methods:{
add(){
// 调用vuex的修改
// this.$store.commit("你调用的mutations名字随便写",你想给mutations的数据 可选)
this.$store.commit("NUM_LIST_ADD_DATA")
},
del(){
this.$store.commit("NUM_LIST_DEL_DATA")
}
},
创建mutations中的内容
let homem={
state: {//就是数据源 存放数据的地方
num:666
},
getters: {
},
mutations: {
// state是一个形参可以随便写但是建议写state
// 这个形参的作用就是代表上面的数据源
NUM_LIST_ADD_DATA(state){
state.num++
},
NUM_LIST_DEL_DATA(state){
state.num--
},
},
actions: {
},
}
// 必须暴露
export default homem
mutations的payload
在我们使用commit()的时候 第一个参数是你要调用的修改动作名 第二个参数是你要给mutations传递的数据
methods:{
add(){
// 调用vuex的修改
// this.$store.commit("你调用的mutations名字随便写",你想给mutations的数据 可选)
this.$store.commit("NUM_LIST_ADD_DATA",{inputval:this.inputval})
},
del(){
this.$store.commit("NUM_LIST_DEL_DATA",{inputval:this.inputval})
}
},
在mutations中可以读取这个第二个参数
mutations: {
// state是一个形参可以随便写但是建议写state
// 这个形参的作用就是代表上面的数据源
// 第二个参数就是payload (载荷) payload就是接受commit的第二个参数
NUM_LIST_ADD_DATA(state,payload){
state.num=state.num+payload.inputval
},
NUM_LIST_DEL_DATA(state,payload){
state.num=state.num-payload.inputval
},
},
扩展---vuex数据修改刷新丢失
监听页面刷新 如果刷新 那么就把vuex的数据存储到本地存储中 然后当页面在此加载得的时候 把本地存储存的原始vuex的数据那出来 替换当前的state数据
created () {
//判断是否有store这个本地存储的数据
if (sessionStorage.getItem("store") ) {
// 如果有 那么把vuex的数据替换 把当前的state 和上次刷新存储的state合并起来
this.$store.replaceState(Object.assign({}, this.$store.state,JSON.parse(sessionStorage.getItem("store"))))
sessionStorage.removeItem("store")
}
//在页面监听绑定一个beforeunload事件(页面刷新事件)
// 当页面刷新的时候使用本地存储存一个store的数据 把vuex的数据全部取出来 转成字符串存起来
window.addEventListener("beforeunload",()=>{
sessionStorage.setItem("store",JSON.stringify(this.$store.state))
})
}
vuex5大属性---actions---异步触发器
actions是vuex的一个属性 他的作用就是在vuex中进行异步操作的触发(很多同学后期总爱说actions是异步请求 但是要注意他不是异步请求 他是进行异步操纵的触发 异步请求只是它触发的众多异步操纵的其中一种)
语法:要触发actions使用dispatch (dispath触发actions进行异步触发 把请求来的数据 通过commit交给mutations修改state 在页面读取展示)
1.在组件内使用dispatch()触发vuex的actions进行异步请求的发送
<template>
<div>
<button @click="fun()">点我使用vuex发送请求</button>
</div>
</template>
<script>
export default {
methods:{
fun(){
// 如果我们要使用vuex进行数据的发送
// this.$store.dispatch("你触发的actions的名字",{参数的key:参数的val})
this.$store.dispatch("AXIOS_CESHI",{url:"/data/user"})
}
}
}
</script>
<style>
</style>
2。需要在对应的actions创建你要触发的异步触发器
actions: {
// actions中也是一个个的方法 每个方法就是一个异步触发器
// context代表的就是vuex store对象
AXIOS_CESHI(context,payload){
// 在actions中就可以写请求
$http({
url:payload.url,
method:"get"
}).then((ok)=>{
console.log(ok.data)
})
}
},
3.把请求来的数据通过context.commit()触发修改
actions: {
// actions中也是一个个的方法 每个方法就是一个异步触发器
// context代表的就是vuex store对象
AXIOS_CESHI(context,payload){
// 在actions中就可以写请求
$http({
url:payload.url,
method:"get"
}).then((ok)=>{
console.log(ok.data)
context.commit("AXIOSDATA",{data:ok.data})//把请求来的数据通过commit触发mutations
})
}
},
4创建对应mutations修改state
mutations: {
AXIOSDATA(state,payload){
state.arr=payload.data
}
},
vuex5大属性---getters---vuex的计算属性
vue的计算属性是computed 对data的数据进行依赖 处理之后返回新的计算之后的结果
vuex的getters也是计算属性 只是他和上面的computed最大的区别就是 他处理的数据可以在任何组件直接使用 而vue的computed 处理的数据只能在当前组件使用
getters: {
// state就是上面的数据源
newtext(state){
return state.text.toUpperCase()
}
},
使用: this.$store.getters.xxx
slot槽口/插槽
引子
组件的本质是自定义标签 标签分为双标签和单标签(就是需要在页面中展示标签所蕴含内容的时候用双标签 单标签就是在页面中表现一个行为的时候 )
组件单标签不考虑了 但是双标签我在其中插入内容是否可以
<组件>
<p>xxxxxxxxx</p>
</组件>
如果在组件的开关标签中插入内容 默认是不显示的(组件是一个完整的独立的个体 如果没有特殊设置 那么没有办法向组件中插入内容)
用来混合父组件与子组件自己的模板
slot其实就是让组件接受一个外部插入进来的dom元素 并且进行显示 通过slot就可以扩展组件的复用性
引子
组件的本质 自定义标签 标签可以是双标签也可以是单标签 那么我们能不能向组件的开关标签内插入dom内容?
默认情况下 给组件的开关标签中写入dom页面不显示原因是因为 组件是一个完整地独立地个体 外部的内容默认插入不进来
<template>
<div>
fufufufufuffu
<!-- 默认情况下 给组件的开关标签中写入dom页面不显示
原因是因为 组件是一个完整地独立地个体 外部的内容
默认插入不进来 -->
<Zicom>
<h1>你好么么哒</h1>
<h1>你好么么哒</h1>
<h1>你好么么哒</h1>
<h1>你好么么哒</h1>
<h1>你好么么哒</h1>
<h1>你好么么哒</h1>
</Zicom>
</div>
</template>
<script>
import Zicom from "./ZiCom.vue"
export default {
components:{
Zicom
}
}
</script>
<style>
</style>
基本slot
在组件中想接受外部插入的dom位置 直接写slot标签 即可接受外部的dom
什么时候使用?
props 接收的是组件外部传递进来的数据变量
slot接收的是 组件外部传递进来的dom
<template>
<div>
<!-- 定义基本的插槽 -->
<slot></slot>
zizizizizizzizizi
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
具名槽口--带有名字的槽口
语法
在定义slot的时候 使用name属性起个名字
<template>
<div>
<!-- 定义基本的插槽 -->
<!-- <slot></slot> -->
<!-- 定义具名槽口 -->
<slot name="xiaoming"></slot>
zizizizizizzizizi
<slot name="xiaobai"></slot>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
在使用的时候使用slot属性 指定那个槽口
<template>
<div>
fufufufufuffu
<!-- 默认情况下 给组件的开关标签中写入dom页面不显示
原因是因为 组件是一个完整地独立地个体 外部的内容
默认插入不进来 -->
<Zicom>
<h1 slot="xiaoming">你好么么哒1</h1>
<h1>你好么么哒2</h1>
<h1>你好么么哒3</h1>
<h1>你好么么哒4</h1>
<h1 slot="xiaobai">你好么么哒5</h1>
<h1>你好么么哒6</h1>
</Zicom>
</div>
</template>
<script>
import Zicom from "./ZiCom.vue"
export default {
components:{
Zicom
}
}
</script>
<style>
</style>
生命周期的钩子函数
vue实例从创建到销毁的过程中被自动执行的函数
写在与 data methods watch computed directives 同级的位置
作用
就是给程序提供一个自动执行逻辑的场所
8大钩子
实例创建
实例创建之前-----beforeCreate
实例创建之后-----created
模板渲染
模板渲染之前-----beforeMount
模板渲染之后-----mounted
数据更新
数据更新之前-----beforeUpdate
数据更新之后-----updated
实例销毁
实例销毁之前-----beforeDestory
实例销毁之后-----destoryed
<template>
<div>
<input type="text" v-model="text">
</div>
</template>
<script>
export default {
data(){
return {
text:"我是默认值"
}
},
methods:{
},
computed:{
},
watch:{
},
directives:{
},
// 生命周期的钩子函数
beforeCreate(){
console.log("实例创建之前")
},
created(){
console.log("实例创建之后")
},
beforeMount(){
console.log("模板渲染之前")
},
mounted(){
console.log("模板渲染之后")
},
beforeUpdate(){
console.log("数据更新之前")
},
updated(){
console.log("数据更新之后")
},
beforeDestory(){
console.log("实例销毁之前")
},
destoryed(){
console.log("实例销毁之后")
},
}
</script>
<style>
</style>
什么是生命周期的钩子函数?
vue实例从创建到销毁的过程中被自动执行的函数
生命周期第一次执行那些?
实例创建之前-----beforeCreate
实例创建之后-----created
模板渲染之前-----beforeMount
模板渲染之后-----mounted
生命周期几个阶段
4大阶段 8个钩子
第一次页面加载触发那些
实例创建前后 模板渲染前后
dom在那个阶段渲染完毕?
mounted
请您介绍一下生命周期的每个钩子?(请您给我说一下vue实例创建的流程与原理)
实例创建
实例创建之前-----beforeCreate 数据的观测与事件的初始化 属性的创建 还没有进行
实例创建之后-----created 在此时vue实例已经创建完毕 所以 数据的观测 属性 方法等内容都已经创建完毕(el属性还没有挂载)
模板渲染
模板渲染之前-----beforeMount 在页面挂载前调用的 所以在此阶段 页面还没有进行渲染与模板的编译 程序在此时会把数据绑定到页面上 但是页面并没有显示
模板渲染之后-----mounted 页面已经渲染出来了 html的内容会在dom中进行加载展示
数据更新
数据更新之前-----beforeUpdate 在此时数据会不停的在dom中进行修改
数据更新之后-----updated 把修改之后的dom内容已经在页面成功的展示了
实例销毁
实例销毁之前-----beforeDestory 此时vue实例还能用
实例销毁之后-----destoryed 什么都没有了 vue实例等内容都没了
父子组件的生周期顺序是什么?
前后台交互
什么是前台什么是后台 什么是前端什么事后端?
前端指的是数据展示
后端指的是数据处理
分类
fetch VS axios VS ajax区别
1.传统的ajax 就是值使用XMLHttpRequest方法实现的数据请求 他隶属于原生的js 核心就是XMLHttpRequest对象 如果多个请求有先后顺序的话 那么容易造成回调地狱问题
jqueryajax 就是对原生XMLHttpRequest封装
2.axios 是基于promise封装的 本质上还是XMLHttpRequest的封装 只不过他是基于最新的语法进行封装的
3.fetch 就是原生js最新标准 和XMLHttpRequest没有半点关系
1.原生ajax
2.jqueryAjax 对上面的XHR对象进行了封装 方便使用
(1)下载jquery npm install --save jquery
(2)引用jquery
<template>
<div>
<h1>jqueryajax请求数据</h1>
</div>
</template>
<script>
// 引用jquery
import $ from "jquery"
export default {
}
</script>
<style>
</style>
(3)使用jquery
<template>
<div>
<h1>jqueryajax请求数据</h1>
<h1>{{data}}</h1>
</div>
</template>
<script>
// 引用jquery
import $ from "jquery"
export default {
data(){
return {
data:{}
}
},
mounted(){
$.ajax({
url:"/user_list/ceshidemo",
type:"GET",
dataType:"json",
success:(ok)=>{
console.log(ok)
this.data=ok
}
})
}
}
</script>
<style>
</style>
3.axios 也是对XHR对象进行封装 当时它是使用符合当下的promise来进行的封装
(1)下载 cnpm install --save axios
(2)引用
// 引用axios
import axios from "axios"
(3)使用
axios({
// 地址
url:"/movie/list/data_list",
// 方式 有的同学写的时候写成methods了 有s也可以 原因是因为默认get 他不认识你带s的这个属性所以执行默认了
method:"get"
}).then((ok)=>{
console.log(ok.data.subjects)
// 把请求来的数据赋值给arr
this.arr=ok.data.subjects
}).catch((err)=>{
console.log(err)
})
4.fetch fetch和上面三个都不一样 因为他没有使用XHRajax对象 而是es最新的请求标准 但是既然是最新的 那么兼容性有很大问题
axios
axios 是目前最优秀的 HTTP 请求库之一,虽然 axios 已经封装的非常好了,我们可以直接拿过来用。但是在实际的项目中,我们可能还需要对 axios 在封装一下,以便我们更好的管理项目和各个接口。
axios常见api
axios.request
该方法是axios项目的核心处理方法,实现用户自定义配置、应用拦截器、发送请求核心功能
axios其他api
就是发送相关请求 比如get请求 delete请求等
get方式--params发送参数
// axios.get("请求地址",{params:{发送数据key:发送的val}}).then((ok)=>{
// // 成功回调
// }).catch((err)=>{
// // 失败回调
// })
axios.get("/api/userlist/get",{params:{name:"xixi"}}).then((ok)=>{
console.log(ok)
}).catch((err)=>{
console.log(err)
})
}
post方式--data发送参数
// axios.post("请求地址",{data:{发送的key:发送的val}}).then((ok)=>{
// // 成功回调
// }).catch((err)=>{
// // 失败回调
// })
axios.post("/api/userlist/post",{data:{name:"xixi"}}).then((ok)=>{
console.log(ok)
}).catch((err)=>{
console.log(err)
})
但是大家会发现后台接收不到我们发送的数据
原因是因为:
在发送post的时候Content-type(表示请求和响应中的媒体类型信息。它用来告诉服务端如何处理请求的数据,以及告诉客户端(一般是浏览器)如何解析响应的数据)常见有三种形式:
Content-Type: application/json 就是给后台的数据是一个json对象
Content-Type: application/x-www-form-urlencoded 表单数据编码为键值对,&分隔 如:name=java&age = 23
Content-Type: multipart/form-data 通常文件上传
现在最主流的是application/json形式axios默认就是这种方式 就像上面我们写的post代码 直接把后该要的参数放到data中就可以了
如图可以发现我们的请求方式
但是有时候后端要求Content-Type必须以application/x-www-form-urlencoded形式,那么通过上面application/json传递的参数,后端是收不到的,我们必须对参数数据进行所谓的序列化处理才行,让它以普通表单形式(键值对)发送到后端,而不是json形式
用qs模块来序列化参数
我们也能通过第三方依赖来序列化参数,就更加方便简洁,下载qs模块。
1.下载 npm install --save qs
2.引用 import qs from “qs”
3.在传递数据的时候使用qs序列化
let key=qs.stringify({
key:val
})
// 引用qs
import qs from 'qs';
// 序列化数据
let key=qs.stringify({
name:"xixi"
})
// 传递
axios.post("/api/userlist/post",key).then((ok)=>{
console.log(ok)
}).catch((err)=>{
console.log(err)
})
}
delete put 等方式
delete 同get
axios.delete("/api/userlist/delete",{params:{name:"xixi"}}).then((ok)=>{
console.log(ok)
}).catch((err)=>{
console.log(err)
})
put方式 同post
import axios from "axios"
// 引用qs
import qs from 'qs';
// 序列化数据
let key=qs.stringify({
name:"xixi"
})
// 传递
axios.put("/api/userlist/put",key).then((ok)=>{
console.log(ok)
}).catch((err)=>{
console.log(err)
})
}
跨域
因为浏览器的安全机制 同源策略 不同端口不同域名不同协议 就会造成跨域
jsonp
面试的时候千万不要先说jsonp 太low
面试的时候千万不要先说jsonp 太low
面试的时候千万不要先说jsonp 太low
面试的时候千万不要先说jsonp 太low
代理跨域
代理:造成跨域的问题是浏览器的安全机制 因为有了这个安全机制我们才要解决跨域
我现在不让浏览器帮我发送请求了 而是让我项目的服务器帮助我绕开浏览器发送请求
nginx反向代理
devServer代理跨域
devServer就是vue脚手架中那个内置的微型开发小服务器--注意 上线之后 该种方式就会失效
1.在项目的根路径下 创建一个vue.config.js
2.写入如下内容
module.exports={
devServer: {
proxy: { //配置跨域
'/api': {
target: 'http://www.weather.com.cn/', //需要解决跨域的地址
pathRewrite: {
'^/api': ''
}
},
}
},
}
3.修改请求路径
<template>
<div>
<h1>大纲要修写法</h1>
<button @click="fun()">点我请求数据</button>
</div>
</template>
<script>
import $http from "axios"
export default {
methods:{
fun(){
$http({
// 修改名字
url:"/api/data/cityinfo/101320101.html",
method:"get",//没有s
}).then((ok)=>{
console.log(ok)
}).catch((err)=>{
console.log(err)
})
}
}
}
</script>
<style>
</style>
4.千万不要忘了重启
4.千万不要忘了重启
4.千万不要忘了重启
4.千万不要忘了重启
4.千万不要忘了重启
4.千万不要忘了重启
4.千万不要忘了重启
cors
后端解决跨域----在工作的时候 后端解决跨域 大概率都是在项目上线的时候 才会解决
json-server模拟数据
json-server就是为我们创建模拟数据的一个技术 因为在开发的时候 我们不可能写一个功能之前后端都给我们把对应的接口写好 所以有的时候我们需要有模拟数据 来给我们提供页面展示内容
1.安装
npm i json-server -g
2.在项目中新建一个mock文件夹 并且创建json文件用来容纳模拟数据文件
3.cd到mock文件夹下 json-server --watch xxx.json --port 端口号
https://blog.csdn.net/weixin_48813932/article/details/124624103
axios封装
在大型项目中 http请求会有很多 而且我们需要区分 所以我们可以把数据请求拆分出来 单独管理这样一来 就可以增强项目的可维护性与可扩展性
大体分为如下内容
api 请求集中式管理
实现请求拦截
实现响应拦截
常见错误信息处理
请求头设置
api请求集中管理
1.初始化axios实例 编写util文件夹 在编写index.js来容纳封装内容
虽然axios中已经给我们封装好了一些常见的api如上面的axios.get axios.post等
但是我们为了更好的全局控制所有请求的相关配置,所以我们使用 axios.create()创建实例的方法来进行相关配置,这也是封装 axios 的精髓所在。
通过 create() 方法我们得到了一个 axios 的实例,该实例上有很多方法,比如拦截器等等。我们创建实例的时候可以配置一些基础设置,比如基础请求地址,请求超时等等。
import axios from "axios"
// 创建 axios 请求实例
const serviceAxios = axios.create({
baseURL: "", // 基础请求地址
timeout: 10000, // 请求超时设置
});
export default serviceAxios
2.新建api文件夹 并且编写js文件用来容纳 api请求集中管理
import request from "../util/index.js"
export let funget=()=>{
return request({
url:"/api/userlist/get",
method:"GET",
params:{
name:"xixi"
}
})
}
3.在组件中进行使用我们封装的 api请求集中管理
// 引用
import {funget} from "../api/index.js"
// 使用
funget().then((ok)=>{
console.log(ok)
})
}
我们就简单的划分出 API 管理层了,这样我们每次新增加一个 API,只需要找到对应模块的 API 文件去添加即可,然后再到具体页面导入使用就行啦。
axios拦截器(不是所有公司都用)
拦截器
每次发送请求或者请求相应的时候 都会经过拦截器 才会进入到我们的程序(就是对我们的请求和相应进行发送前或者获取前的一个拦截 )
请求拦截
有的时候我们在发送请求的时候后端需要每次我们携带一些数据(比如token)但是添加在每个请求非常麻烦 所以我们可以添加在请求头中 那么这个配置我们可以在拦截器中进行设置
相应拦截
响应拦截器的作用是在接收到响应后进行一些操作
响应拦截器也是一样如此,就是在请求结果返回后,先不直接显示错误,而是先对响应码等等进行处理,处理好后再导出给页面一个错误提醒
添加请求 响应拦截
import axios from "axios"
// 创建 axios 请求实例
const serviceAxios = axios.create({
baseURL: "", // 基础请求地址
timeout: 10000, // 请求超时设置
});
// 添加请求拦截器
serviceAxios.interceptors.request.use(function (config) {
//处理token 携带token
let token=sessionStorage.getItem("token");
if(token){
if(config.headers){
config.headers={
...config.headers,
token:token
}
}
}
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
serviceAxios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
export default serviceAxios
响应拦截设置HTTP状态码
得到错误http状态码
// 添加响应拦截器
serviceAxios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
// 得到错误http状态码
console.log("响应",error.response.status)
return Promise.reject(error);
});
// 添加响应拦截器
service.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
console.log("error", error.response.status)
switch (error.response.status) {
case 302: alert('接口重定向了!'); break;
case 400: alert('参数不正确!'); break;
case 401: alert('您未登录,或者登录已经超时,请先登录!'); break;
case 403: alert('您没有权限操作!'); break;
case 404: alert('请求地址出错'); break; // 在正确域名下
case 408: alert('请求超时!'); break;
case 409: alert('系统已存在相同数据!'); break;
case 500: alert('服务器内部错误!'); break;
case 501: alert('服务未实现!'); break;
case 502: alert('网关错误!'); break;
case 503: alert('服务不可用!'); break;
case 504: alert('服务暂时无法访问,请稍后再试!'); break;
case 505: alert('HTTP版本不受支持!'); break;
default: alert('异常问题,请联系管理员!'); break
}
// 对响应错误做点什么
return Promise.reject(error);
});
设置请求拦截请求头信息
你把用户的token以请求头的方式给后台
地址:/userlist/routertoken
方式 get
参数: 头信息是usertoken
返回值??
vue环境部署与baseurl配置
环境变量
在开发的时候一般会有是三个环境:开发环境 测试环境 线上(生产)环境
vue 中有个概念就是模式,默认先vue cli 有三个模式
development开发环境模式用于 vue-cli-service serve
production生产环境模式用于 vue-cli-service build 和 vue-cli-service test:e2e
test测试环境模式用于 vue-cli-service test:unit
但是往往开发的时候可能不止有三种:
本地环境(local)
开发环境(development)
测试环境(devtest)
预发布环境(beta)
生产环境(production)
创建不同环境变量文件
通过为.env文件增加后缀来设置某个模式下特有的环境变量。
1.在项目根路径下设置 新建对应文件 .env.development(开发环境文件) .env.production(生产环境文件).env.devtest(测试环境文件)
2.在每个文件写入如下内容(VUE_APP_随便写)
VUE_APP_XIAOMING = "开发模式"
package.json环境对应的执行语句
"scripts": {
"serve": "vue-cli-service serve",//开发模式
"build": "vue-cli-service build",//生产模式
"dev_test_build": "vue-cli-service build --mode development_test",//测试模式
"lint": "vue-cli-service lint"
},
使用变量process.env.你的内容即可得到
mounted()=>{
console.log(process.env.VUE_APP_XIAOMING);
}
打包生产环境
1.npm run build 会生成一个dist文件夹 我们点开之后像运行html一样运行项目
2.配置生产环境 (1)在vue.config.js中设置 publicPath:"./" (2)把路由模式设置为hash
3.重新build
默认请求地址baseurl
因为我们可以配置不同的环境变量 那么我们就可以在设置请求的时候 根据不同的环境来设置不同的请求地址
1.在开发环境文件中配置我们的请求(今后开发的时候可以在其他配置文件中配置你的请求)
VUE_APP_XIAOMING = "开发模式"
VUE_APP_API = "http://localhost:8888"
2.在封装的拦截器文件中配置baseURL
let axiosurl = ""
// 如果为开发模式的话执行url
if(process.env.NODE_ENV === 'development' ){
axiosurl=process.env.VUE_APP_API
}
else{
// 否则设置成其他的模式(这里今后有很多个判断)
axiosurl=process.env.VUE_APP_API
}
// 创建axios 赋值给常量service
const service = axios.create({
baseURL: axiosurl
});
3.在今后的请求中直接写请求的路由地址就行
// 发送请求 url中请求地址前缀已经在baseURL中配置了
link("/one").then((ok)=>{
console.log(ok)
})
路由
路由是什么?
就是可以让我们完成一个SPA单页面应用 传统的页面在跳转切换的时候会造成页面加载白屏 这样一来用户体验非常的差 但是 我们通过spa应用 可以达到类似于原生app的切换效果 切换没有白屏 丝滑切换 用户体验更高
路由的本质 就是根据url的不同来渲染不同的组件页面
路由基本创建
方式1 脚手架自动创建
在创建项目的时候 选中router项即可在项目中集成路由
拿到路由项目之后怎么办?
1.删除掉views文件夹中的内容
2.删除掉components下的helloword.vue
3.在app。vue中删除内容 但是router-view千万千万不要删
方式2 手工创建方式
稍后在说
路由模式
hash
是vue路由的默认路由模式 如果你不指定 那么就是hash模式
url带#号 上线之后刷新不会丢失 浏览器兼容性好
history
url不带#号 上线之后刷新会丢失(需要让你们后端服务器人员给你配置服务器的重定向) 浏览器兼容性一般
一级路由创建
1.在views文件夹中创建对应的路由页面组件
2.配置路由 在router下index.js中进行配置
(2-1)先把你要使用的组件页面引用
import Vue from 'vue'
import VueRouter from 'vue-router'
// 1.把你要使用的路由页面引用
import Fenlei from '../views/fenlei.vue'
import Gouwuche from '../views/gouwuche.vue'
import Home from '../views/home.vue'
import Jingxi from '../views/jingxi.vue'
import Wode from '../views/wode.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',//url路径
name: 'home',//给这个路由规则起个名字
component: HomeView //引用组件
},
// 下面是后面要学的
// {
// path: '/about',
// name: 'about',
// component: () => import('../views/AboutView.vue')
// }
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
(2-2)
配置路由规则
import Vue from 'vue'
import VueRouter from 'vue-router'
// 1.把你要使用的路由页面引用
import Fenlei from '../views/fenlei.vue'
import Gouwuche from '../views/gouwuche.vue'
import Home from '../views/home.vue'
import Jingxi from '../views/jingxi.vue'
import Wode from '../views/wode.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/fenlei',//url路径
name: 'fenlei',//给这个路由规则起个名字
component: Fenlei //引用组件
},
{
path: '/gouwuche',//url路径
name: 'gouwuche',//给这个路由规则起个名字
component: Gouwuche //引用组件
},
{
path: '/home',//url路径
name: 'home',//给这个路由规则起个名字
component: Home //引用组件
},
{
path: '/jingxi',//url路径
name: 'Jingxi',//给这个路由规则起个名字
component: Jingxi //引用组件
},
{
path: '/wode',//url路径
name: 'Wode',//给这个路由规则起个名字
component: Wode //引用组件
},
// 下面是后面要学的
// {
// path: '/about',
// name: 'about',
// component: () => import('../views/AboutView.vue')
// }
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
3.在app.vue中设置router-view路由出口
路由导航
路由导航就是在页面中的一些连接通过点击之后完成页面的跳转
标签的方式---声明式导航
不能使用a标签
router-link这个标签来完成页面之间的跳转 其中有一个to属性就是写你的路径
<template>
<div>
<router-link to="/home">首页</router-link>
<router-link to="/fenlei">分类</router-link>
<router-link to="/jingxi">惊喜</router-link>
<router-link to="/gouwuche">购物车</router-link>
<router-link to="/wode">我的</router-link>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
扩展--动态类名
在当前路由页面下 vuerouter会给对应的声明式导航添加一个类名 通过这个类名可以设置当前的样式
<template>
<div>
<router-link to="/home">首页</router-link>
<router-link to="/fenlei">分类</router-link>
<router-link to="/jingxi">惊喜</router-link>
<router-link to="/gouwuche">购物车</router-link>
<router-link to="/wode">我的</router-link>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.router-link-exact-active{
background-color: red;
}
</style>
js的方式---编程式导航
this.$router.push("/你要去的路径") push跳转的页面可以回退回来
<script>
export default {
methods:{
fun(){
this.$router.push("/jingxi")
}
}
}
</script>
this.$router.replace('/替换路径') replace是替换 跳转之后不能回退
this.$router.go()正数前进 负数后退
二级或者多级路由创建
二级路由或者多级路由在创建的时候 使用children关键字来进行规则的配置
1.路由页面创建 views文件夹
2.配置二级路由规则
(2-1)在router文件夹下的index.js中先引用二级路由页面
(2-2)配置路由规则 必须在对应的一级路由规则中使用children关键字来进行配置
{
path: '/home',//url路径
name: 'home',//给这个路由规则起个名字
component: Home, //引用组件
children:[//配置二级路由规则
{
path: '/era',
name: 'era',
component: Era
},
{
path: '/erc',
name: 'erc',
component: Erc
},
{
path: '/erd',
name: 'erd',
component: Erd
},
]
},
3.注意
3.注意
3.注意
3.注意
3.注意 必须设置二级路由的路由出口 router-view (写在对应一级路由的页面中)
扩展---二级路由path设置
在上面的笔记中 会发现 我们在配置二级路由的时候 path路径为 /二级 在路由导航的时候 我们在to中直接就写/二级
{
path: '/home',//url路径
name: 'home',//给这个路由规则起个名字
component: Home, //引用组件
children:[//配置二级路由规则
{
path: '/era',
name: 'era',
component: Era
},
{
path: '/erc', path路径 是直接/二级
name: 'erc',
component: Erc
},
{
path: '/erd',
name: 'erd',
component: Erd
},
]
},
那么路由导航
<router-link to="/da">火锅</router-link> 这里直接/二级的path
注意
注意
注意
注意
注意
注意 我们在写二级路由的时候 path 也可以写成 不加/的方式
{
path: '/meishi',
name: 'meishi',
component: Meishi,
children:[
{
path: 'da',//这里二级路由的path没有加/
name: 'da',
component: Da
},
{
path: 'db',
name: 'db',
component: Db
},
{
path: 'dc',
name: 'dc',
component: Dc
},
]
},
在路由导航的时候 必须写成 /一级/二级
<router-link to="/meishi/da">火锅</router-link>
<router-link to="/meishi/db">甜品</router-link>
<router-link to="/meishi/dc">自助餐</router-link>
路由重定向---redirect
重(重新)定(定位)向(方向)
import Vue from 'vue'
import VueRouter from 'vue-router'
import Meishi from '../views/MeiShi.vue'
import Shouye from '../views/ShouYe.vue'
import Da from "@/views/MeiShichil/DemoA.vue"
import Db from "@/views/MeiShichil/DemoB.vue"
import Dc from "@/views/MeiShichil/DemoC.vue"
Vue.use(VueRouter)
const routes = [
{
path: '/meishi',
name: 'meishi',
component: Meishi,
children:[
{
path: 'da',//这里二级路由的path没有加/
name: 'da',
component: Da
},
{
path: 'db',
name: 'db',
component: Db
},
{
path: 'dc',
name: 'dc',
component: Dc
},
]
},
{
path: '/shouye',
name: 'shouye',
component: Shouye
},
// 重定向
{
path:"/",
redirect:"/shouye"
}
// {
// path: '/about',
// name: 'about',
// // route level code-splitting
// // this generates a separate chunk (about.[hash].js) for this route
// // which is lazy-loaded when the route is visited.
// component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
// }
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
404页面
就是给用户一个页面错误提示的作用
import Vue from 'vue'
import VueRouter from 'vue-router'
import Meishi from '../views/MeiShi.vue'
import Shouye from '../views/ShouYe.vue'
import No from '../views/NoView.vue'
import Da from "@/views/MeiShichil/DemoA.vue"
import Db from "@/views/MeiShichil/DemoB.vue"
import Dc from "@/views/MeiShichil/DemoC.vue"
Vue.use(VueRouter)
const routes = [
{
path: '/meishi',
name: 'meishi',
component: Meishi,
children:[
{
path: 'da',//这里二级路由的path没有加/
name: 'da',
component: Da
},
{
path: 'db',
name: 'db',
component: Db
},
{
path: 'dc',
name: 'dc',
component: Dc
},
]
},
{
path: '/shouye',
name: 'shouye',
component: Shouye
},
// 重定向
{
path:"/",
redirect:"/shouye"
},
// 404页面必须在所有规则的最下面
{
path: '*',
name: 'no',
component: No
}
// {
// path: '/about',
// name: 'about',
// // route level code-splitting
// // this generates a separate chunk (about.[hash].js) for this route
// // which is lazy-loaded when the route is visited.
// component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
// }
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
路由传参 动态路由匹配
就是把数据从一个路由页面传递到另外一个路由页面中 (新闻列表页面 用户点击之后 会跳转到新闻详情页 但是新闻详情页展示的内容应该是用点击的那一条新闻 所以如何把数据从一个页面传递到另外一个页面)
params方式
1.在需要接受数据的路由页面规则上 设置接受参数
{
path: '/all/:xiaoming', //设置接收参数
name: 'All',
component: All
},
2.发送
声明式
<router-link v-bind:to='{name:"All",params:{xiaoming:"111我是phone传递的数据"}}'>点我使用声明式把数据传递给all页面</router-link>
编程式
<template>
<div>
<Link/>
phone
<h1 v-for="(v,i) in arr" :key="i" @click="fun()">{{v.title}}</h1>
</div>
</template>
<script>
export default {
methods:{
fun(){
// 编程式导航
// this.$router.push({name:"你要去的路由规则的名字",params:{你在规则上配置的参数:你要传递的数据}});
this.$router.push({name:"All",params:{xiaoming:"我是phone传递的数据"}});
}
},
data(){
return {
arr:[
{
title:'我是辩题11',content:"我是内容11",
},
{
title:'我是辩题12',content:"我是内容12",
},
{
title:'我是辩题13',content:"我是内容14",
},
{
title:'我是辩题14',content:"我是内容15",
},
]
}
}
}
</script>
<style>
</style>
3.接受
在接受的页面使用 this.$route.params.xxx 注意单词
<template>
<div>
<h1 @click="fun()"><</h1>
<h1>新闻详情页---{{this.$route.params.xiaoming}}</h1>
</div>
</template>
<script>
export default {
methods:{
fun(){
// 路由回退
this.$router.go(-1);
}
}
}
</script>
<style>
</style>
query方式
1.发送
声明式
<template>
<div>
<Link/>
home
<!-- <router-link :to="{name:'你要去的页面规则的name',query:{key:val}}">使用query方式把数据传递到购物页面</router-link> -->
<router-link :to="{name:'Shop',query:{xiaohong:'我是query数据'}}">使用query方式把数据传递到购物页面</router-link>
<!-- query方式发送参数的时候 不但可以写name 还可以写成path -->
<router-link :to="{path:'/shop',query:{xiaohong:'1111我是query数据'}}">使用query方式把数据传递到购物页面</router-link>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
编程式
课下自行尝试
2.接受
在想使用数据的页面中 使用 this.$route.query.xxxx
总结路由传参/动态路由匹配
路由传参或者是动态路由匹配 他是vue-router 给我们提供的一种 把数据从一个路由页面传递到另外一个路由页面的技术
他有两种方式
第一种params 这种方式在传递参数的时候共3步 1在接收的路由页面规则中配置接收参数 2发送 我是使用name作为跳转地址 使用params来设置发送参数 3接收 能想起来就说想不起来就不说 this.$route.params.xxxx
第二种方式 query 他在传递数据的时候共两部 发送和接收 和params方式基本相同 唯独在发送数据的时候 他不但可以使用name作为跳转地址 还可以使用path 在接收的时候 语法也略有不同 他使用this.$route.query.xxxx
query方式与params方式传参区别
1.url展示形态
params方式 传递参数的时候相对安全一些 因为不显示传递的数据key
query方式 在传递的时候会显示数据key和val 相对来说没有parmas安全
2.刷新丢失
parmas方式 刷新会丢失 (上线之后)
query方式 刷新不会丢失
3.语法区别
$router与$route的区别
$router代表的是 路由对象 他所涉及的范围是全部项目页面
$route代表 当前路由页面对象
扩展----路由懒加载
在没有使用路由懒加载的时候 第一次加载会把所有的路由页面全部加载完 在显示页面 有的时候 用户的设备网络不好的时候 导致渲染时间过长 用户就会在第一次进入我们项目的时候 有白屏问题(用户体验不好)
异步组件方式
component: (resolve) => require(['你引用组件页面的路径'], resolve)
import导入方式
component: () => import('../views/About.vue')
路由钩子/路由守卫/导航守卫/路由卫士
在路由跳转的特定时期自动触发的一些函数 (这些钩子函数通常是在页面跳转的时候 对项目中的用户权限进行设置的)
全局
所有页面在跳转的时候都会触发
全局前置守卫---beforeEach()
在所有路由跳转之前 触发的钩子函数(此时路由还没有跳转完成)
全局后置守卫--- afterEach(()
所有路由跳转之后 触发的钩子函数 (此时已经进入到了跳转之后的新页面)
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/home',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue')
},
{
path: '/phone',
name: 'phone',
component: () => import('../views/phone.vue')
},
{
path: '/shop',
name: 'shop',
component: () => import('../views/shop.vue')
},
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
// 全局前置
// to 去哪里
// from 从哪个路由来的
// next 下一步(必须要写 不写的话 就会卡在这个钩子中不想下进行了)
router.beforeEach((to,from,next)=>{
console.log(to);
console.log(from);
if(to.path=="/phone"||to.path=="/shop"){
next()
}else{
alert("您没有登录请您登录后再访问")
next("/phone")
}
})
// 全局后置
router.afterEach((to,from)=>{
console.log("全局后置守卫")
})
export default router
路由独享
指定页面在跳转的时候触发
路由独享---beforeEnter
只会对一个路由规则生效(路由独享写在那个规则之上 就对哪一个生效)
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue'),
// 路由独享守卫
beforeEnter(to,from,next){
console.log(to);
console.log(from);
next()
}
},
组件内
仅仅对某些组件在路由跳转的时候生效
进入组件时候---beforeRouteEnter
离开组件的时候---beforeRouteLeave
// 进入组件
beforeRouteEnter(to,from,next){
console.log("我进来了")
console.log(to)
console.log(from)
next()
},
beforeRouteLeave(to,from,next){
console.log(to)
console.log(from)
if(confirm("您确定离开吗?")){
next()
}else{
next(false)
}
},
路由懒加载
因为使用传统的路由来进行开发的时候可能会出现首页加载白屏问题( 因为如果我们有200个页面 那么在第一次加载的时候 会把这200个页面都先加载出来然后再把指定的哪一个显示到页面上 ) 白屏问题对于用户体验很不好
所以为了解决首页加载白屏 那么我们可以使用路由懒加载方式
懒(按需)加载
实现1 import方式
{
path: '/about',
name: 'about',
component: () => import('你的路由页面路径')
}
实现2 异步组件方式
component: (resolve) => require(['你的路由页面路径'], resolve),
路由模式
1.hash模式 默认模式 带#号 hash模式上线正常 hash兼容性好
2.history模式 必须手工指定 不带#号 history上线之后刷新会丢失页面(让后端给你们配置服务器的重定向) 是H5新增 所以兼容性比较差
修改 使用mode属性来进行修改
ui库--vantui
mixin混入
就是vue组件中的一个封装技巧 他的作用就是对组件中重复出现的数据 方法 生命周期等内容进行封装方便其他组件复用 在组件中引用了混入内容之后 就可以直接在当前组件中进行使用。
全局混入---mixin
全局混入 能不用尽量不要用 因为会造成全局的污染
1.新建一个文件夹 mixin 用来存储混入的代码
let demo={
methods:{
fun(){
console.log("你好")
}
}
}
export default demo
2.main.JS中进行配置
import Vue from 'vue'
import App from './App.vue'
import router from './router'
// 全局混入
import demo from "./mixin/demo.js"
// 全局混入
Vue.mixin(demo)
// 全局过滤器
// Vue.filter("xiaoming",(val)=>{
// if(val.length>6){
// return val.slice(0,5)+"..."
// }else{
// return val
// }
// })
// 全局过滤器
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
局部混入---mixins
1.新建一个文件夹 mixin 用来存储混入的代码
let demo={
methods:{
fun(){
console.log("你好")
}
}
}
export default demo
2.在你想使用的组件中 引用 使用
<template>
<div>
<!-- 3.你就可以在组件内容任意的使用混入的内容 -->
aaaaaaaa-- <button @click="fun()">点我--{{newnum}}</button>
</div>
</template>
<script>
// 1.引用
import demo from "@/mixin/demo.js"
export default {
// 2.使用mixins调用
// 与data methods同级使用
mixins:[demo]
}
</script>
<style>
</style>
ref
ref就是对页面的dom进行操作
使用场景1--绑定到DOM元素身上
进行基本的页面dom操作。
1.在标签的dom之上使用ref=“随便起个名字”
2.this.$refs.你的那个名字即可找到指定元素
<template>
<div>
<!-- 1.绑定 -->
<h1 ref="wangcai">找到我</h1>
<button @click="fun()">点我修改上面的内容</button>
</div>
</template>
<script>
export default {
methods:{
fun(){
// 2.找到他
this.$refs.wangcai.style.color="red";
}
}
}
</script>
<style>
</style>
使用场景2--绑定到组件身上
因为组件的核心就是自定义标签 上面的ref可以绑定到标签之上 我们的自定义标签能不能使用ref 如果可以使用那么得到的是什么?
可以得到绑定的那个组件所有的信息(得到了vue组件对象)
<template>
<div>
<h1>我是home</h1>
<!-- 1.把ref绑定到组件身上 -->
<Rc ref="com"/>
<button @click="fun()">点我</button>
</div>
</template>
<script>
import Rc from "@/components/refcom.vue"
export default {
components:{
Rc
},
methods:{
fun(){
// 2.把ref绑定到组件身上父组件就可以得到子组件的所有属性和方法
console.log(this.$refs.com)
}
}
}
</script>
<style>
</style>
1.ref可以完成逆向传值
2.父组件如何触发子组件的方法?
使用ref绑定到组件即可访问
$set
在vue中 数据改变试图不变怎么解决?
在vue数据改变试图有的时候是不会更新的如下代码
<template>
<div>
<h2>数据变试图不会改变</h2>
<h3>{{obj.age}}</h3>
<button @click="funb()">点我</button>
</div>
</template>
<script>
export default {
methods:{
funb(){
this.obj.age=18
console.log(this.obj.age)
}
},
data(){
return {
obj:{
name:"xixi"
}
}
}
}
</script>
<style>
</style>
在vue2.0中 数据的双向绑定 是基于数据劫持与发布者订阅者模式的
其中数据劫持是通过Object.defineProperty()这个方法来拦截劫持data中的数据的 因为有了这个方法
所以数据改变试图也会更新
但是 Object.defineProperty()有个问题 他是会监听初始化的数据 如果中途给数组或者对象
添加新属性的时候 Object.defineProperty() 就不会监听到 不会监听到就没有数据劫持 没有数据劫持就没有双向绑定 没有双向绑定就没有数据变试图变
如果我就是想在程序运行的时候 给vue的data中对象或者数组添加新属性 并且让试图改变怎么办?
$set() 就是在程序运行的时候给对象或者数组添加新属性并且让试图改变
语法:
This.$set("你要给谁添加","你要添加的key","你要添加的val")
<template>
<div>
<h2>数据变试图不会改变</h2>
<h3>{{obj.age}}</h3>
<button @click="funb()">点我</button>
</div>
</template>
<script>
// 在vue2.0中 数据的双向绑定 是基于数据劫持与发布者订阅者模式的
// 其中数据劫持是通过Object.defineProperty()这个方法来拦截劫持data中的数据的 因为有了这个方法
// 所以数据改变试图也会更新
// 但是 Object.defineProperty()有个问题 他是会监听初始化的数据 如果中途给数组或者对象
// 添加新属性的时候 Object.defineProperty() 就不会监听到 不会监听到就没有数据劫持 没有
// 数据劫持就没有双向绑定 没有双向绑定就没有数据变试图变
export default {
methods:{
funb(){
// this.obj.age=18
// console.log(this.obj.age)
this.$set(this.obj,"age",18)
}
},
data(){
return {
obj:{
name:"xixi"
}
}
}
}
</script>
<style>
</style>
动态组件
多个组件 使用同一个挂载点 并且动态切换
1.需要有多个组件
2.设置挂载点
<component :is="你要显示组件的变量名"></component>
<h1>动态组件</h1>
<component :is="Com"></component>
3.动态切换 修改挂载点上绑定的变量即可
<template>
<div>
<h1>动态组件</h1>
<button @click="fun('Da')">显示a</button>
<button @click="fun('Db')">显示b</button>
<button @click="fun('Dc')">显示c</button>
<component :is="Com"></component>
</div>
</template>
<script>
import Da from "@/components/demoa.vue"
import Db from "@/components/demob.vue"
import Dc from "@/components/democ.vue"
export default {
methods:{
fun(val){
this.Com=val
}
},
data(){
return {
Com:"Dc"
}
},
components:{
Da,Db,Dc
}
}
</script>
<style>
</style>
keep-alive--保存组件的状态
在路由或者动态组件中 页面用户填写的数据可能会随着页面的切换 而丢失(原因是因为 在我们每次切换的时候 vue都会创建一个新的组件实例 )
如果我就是想在切换的时候保存之前的页面内容呢?
keep-alive 用来保存组件切换的时候页面状态内容 使用keep-alive包裹的组件 不会随着页面的切换 而数据丢失 因为当前组件在切换的时候会被缓存起来 那么这样一来 在组件切换的时候能减低性能上的损耗
使用
动态组件
包裹挂载点即可
<keep-alive>
<component :is="Com"></component>
</keep-alive>
路由
包裹路由出口即可
<keep-alive>
<router-view/>
</keep-alive>
keep-alive 属性与钩子
属性
include 你要缓存谁
exclude 你不想缓存谁
如果 我把两个都写了 excloud的优先级大于incloude
<keep-alive exclude="Db">
<!-- 设置动态组件的挂载点 -->
<component :is="com"></component>
</keep-alive>
钩子函数
activated 在进入被kepp-alive管理的组件时候触发
deactivated 在离开被kepp-alive管理的组件时候触发
这两个钩子应该写在被keep-alive管理的组件中 与data等属性同级
<template>
<div>
aaaaaaaa
<input type="text" />
</div>
</template>
<script>
export default {
activated() {
console.log("进入到了被keep-alive管理的组件中了");
},
deactivated() {
console.log("离开到了被keep-alive管理的组件中了");
},
};
</script>
<style>
</style>
$nexttick
等待dom加载完毕后立即执行的一个操作
观察下面得代码
分析上面的代码发现:Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新
简单来说,Vue 在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。原理:1.首先修改数据,这是同步任务。同一事件循环的所有的同步任务都在主线程上执行,形成一个执行栈,此时还未涉及DOM。
2.Vue开启一个异步队列,并缓冲在此事件循环中发生的所有数据变化。
3.同步任务执行完毕,开始执行异步队列的任务,更新DOM
什么时候用
为了在数据变化之后等待 Vue 完成更新 DOM 可以在数据变化之后立即使用Vue.nextTick(callback)。这样回调函数在 DOM 更新完成后就会调用。
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
<template>
<div>
<h1>nexttick</h1>
<h1 ref="demoh">{{ text }}</h1>
</div>
</template>
<script>
export default {
created() {
// 在这个实例创建完毕的钩子函数中 我如果想获取到h1里面的显示内容可以吗?
// 但是我就是想获取怎么办?
this.$nextTick(()=>{
console.log(this.$refs.demoh.innerText)
})
},
data() {
return {
text: "你好",
};
},
};
</script>
<style>
</style>
PNPM
pnpm是 Node.js 的替代包管理器。它是 npm 的直接替代品,但速度更快、效率更高。
为什么效率更高 ? 当您安装软件包时, 我们会将其保存在您机器上的全局存储中,然后我们会从中创建一个硬链接(pnpm 通过硬链接的方式保证了相同的包不会被重复下载,比如说我们已经在 A 中下载过一次 xxx@4.17.1 版本,那我们后续在 B 中安装 xxx@4.17.1 的时候是会被复用的,具体就是 A 中的 xxx 中的文件和 B 中的 xx 中的文件指向的是同一个 )
PNPM不是进行复制。对于模块的每个版本,磁盘上只保留一个副本。例如,当使用 npm 或 yarn 时,如果您有 100 个使用的包,则磁盘上将有 100 个 lodash 副本。pnpm 可让您节省数 GB 的磁盘空间!
安装
npm install -g pnpm
vite
原来咱们学习v3的时候 使用的是vue/cli脚手架(实质上是帮助我们封装好了webpack来帮助我们构建与编译项目)
Vite 是 vue 的作者尤雨溪在开发 vue3.0的时候开发的一个 web 开发构建工具。其本人在后来对 vue3 的宣传中对自己的新作品 Vite 赞不绝口,并表示自己 ”再也回不去 webpack 了“ 。那么 Vite 究竟有什么魅力
Vite 和 Webpack 区别
Vite 优势:
1.vite 开发服务器启动速度比 webpack 快webpack 会先打包,然后启动开发服务器,请求服务器时直接给予打包结果。vite 在启动开发服务器时不需要打包,也就意味着不需要分析模块的依赖、不需要编译,因此启动速度非常快。当浏览器请求某个模块时,再根据需要对模块内容进行编译。这种按需动态编译的方式,极大的缩减了编译时间,项目越复杂、模块越多,vite的优势越明显。
2.vite 热更新比 webpack 快当改动了一个模块后,vite仅需让浏览器重新请求该模块即可,不像webpack那样需要把该模块的相关依赖模块全部编译一次,效率更高。
3.vite 构建速度快,比 webpack 的 nodejs,快 10-100 倍。
Vite 劣势:
1.生态不及webpack,加载器、插件不够丰富
2.项目的开发浏览器要支持 ES Module,而且不能识别 CommonJS 语法
1、搭建第一个Vite项目
兼容性注意
Vite 需要 Node.js 版本 >= 12.0.0。
pnpm create vite
运行完的结果如下:
felixlu 2206 $ npm init vite
Need to install the following packages:
create-vite
Ok to proceed? (y) y
✔ Project name: … vue3-basics
? Select a framework: › - Use arrow-keys. Return to submit.
? Select a framework: › - Use arrow-keys. Return to submit.
? Select a framework: › - Use arrow-keys. Return to submit.
? Select a framework: › - Use arrow-keys. Return to submit.
? Select a framework: › - Use arrow-keys. Return to submit.
✔ Select a framework: › vue
✔ Select a variant: › vue
Scaffolding project in /Users/felixlu/Desktop/workspace/st-test/2206/vue3-basics...
Done. Now run:
cd vue3-basics
npm install
npm run dev
vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': resolve(__dirname, './src')
}
}
})
路由使用
1.创建文件夹用来容纳 页面views与路由规则router
2. 新建路由页面
3.配置路由规则--与下载路由
pnpm install --save vue-router@4
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/home',
name: 'home',
component: () => import('../views/HomeView.vue')
},
{
path: '/shop',
name: 'shop',
component: () => import('../views/ShopView.vue')
},
{
path: '/user',
name: 'user',
component: () => import('../views/UserView.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
4 设置路由出口
在app.vue中设置 router-view
5 注入路由
在main.js中引用使用路由
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
vuex
1.下载
pnpm install --save vuex
2,创建对应容纳文件夹 store
3.编写vuex相关内容
在store/index.js中创建
import { createStore } from 'vuex'
export default createStore({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
}
})
4.在项目中配置vuex
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App).use(store).use(router).mount('#app')
pinia
pinia是一个用于vue的状态管理库,类似于vuex,是vue的另一种状态管理工具
Pinia意为菠萝,表示与菠萝一样,由很多小块组成。在pinia中,每个store都是单独存在,一同进行状态管理。 很多人也将pinia称为vuex5,因为pinia将作为vue3推荐的状态管理库,而vuex将不再迭代。
与vuex有什么不同
与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持。
1.mutations 不再存在。
2.无需创建自定义复杂包装器来支持 TypeScript,所有内容都是类型化的,并且 API 的设计方式尽可能利用 TS 类型推断。
3.不再需要注入、导入函数、调用函数、享受自动完成功能!
4.无需动态添加 Store,默认情况下它们都是动态的
5.不再有 modules 的嵌套结构
安装(仅限于vue3)
通过你喜欢的包管理器安装
pnpm install pinia yarn add pinia npm install --save pinia
pinia基本使用
1.创建pinia实例并挂载
在main.ts中
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
// 引用
import {createPinia } from 'pinia'
// 创建Pinia实例
const Pinia = createPinia()
// 挂载pinia
createApp(App).use(Pinia).mount('#app')
2.在src目录下新建一个stroe文件夹,在文件夹中新建一个index.ts
3.通过import将下载好的pinia引入到index.ts中并导出 并且使用defineStore创建store对象
import { defineStore } from 'pinia'
//
// 第一个参数是应用程序中 store 的唯一 id
export const useStore = defineStore('main', {
})
pinia--state
state 是 store 的核心部分。 我们通常从定义应用程序的状态开始。 在 Pinia 中,状态被定义为返回初始状态的函数。 Pinia 在服务器端和客户端都可以工作。
1.创建state
import { defineStore } from 'pinia'
// useStore 可以是 useUser、useCart 之类的任何东西
// 第一个参数是应用程序中 store 的唯一 id
export const useStore = defineStore('main', {
state:()=>{//创建状态
return {
text:"我是pinia的一个状态变量"
}
}
})
2.组件中使用
<template>
<div>
<!-- 3.使用 -->
我是测试pinia的组件--{{store.text}}
</div>
</template>
<script lang="ts" setup>
// 1.引用
import {useStore} from "../store/index"
// 2.创建
let store=useStore()
</script>
<style>
</style>
pinia--修改--action
修改方式1
1.组件内创建触发
调用修改方法 store.你在pinia中actions中创建的修改函数
<template>
<div>
我是测试pinia的组件--{{store.text}}
<button @click="update()">点我修改</button>
</div>
</template>
<script lang="ts" setup>
import {useStore} from "../store/index"
let store=useStore()
let update=()=>{
// 调用修改方法 store.你在pinia中actions中创建的修改函数
store.updatetext()
}
</script>
<style>
</style>
2.pinia中创建修改方法
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
state:()=>{
return {
text:"我是pinia的一个状态变量"
}
},
actions:{
updatetext(){//修改方法
this.text="我变了"
}
}
})
修改方式2--$patch
$patch是pinia的内置方法 通过该方法可以直接修改state的数据
方式1
在组件内调用$parch 便可直接修改数据
let update=()=>{
store.$patch({
//你要修改state的数据: 你要修改的value值
text:"我是$patch直接修改"
})
}
方式2
let update=()=>{
store.$patch((state)=>{
console.log(state)
state.text="你又被改了"
})
}
pinia--修改常见问题
我们正常在取值的时候是使用如下方法
<template>
<div>
<!-- 3.使用 -->
我是测试pinia的组件--{{store.text}}
</div>
</template>
<script lang="ts" setup>
// 1.引用
import {useStore} from "../store/index"
// 2.创建
let store=useStore()
</script>
<style>
</style>
那么大家发现store能点出数据 那么store是一个对象 那么我们可能想到了如果数据很多 每次store点xxx就很麻烦 那么我们使用解构来试试
<template>
<div>
我是测试pinia的组件--{{text}}
</div>
</template>
<script lang="ts" setup>
import {useStore} from "../store/index"
let store=useStore()
// 结构取值
let {text}=store
</script>
<style>
</style>
大家会发现可以正常读取 那么修改下数据试试
<template>
<div>
我是测试pinia的组件--{{text}}
<button @click="update()">点我修改</button>
</div>
</template>
<script lang="ts" setup>
import {useStore} from "../store/index"
let store=useStore()
// 结构取值
let {text}=store
let update=()=>{
// 调用修改方法 store.你在pinia中actions中创建的修改函数
store.updatetext()
}
</script>
<style>
</style>
发现数据并没有修改 原因是因为使用解构之后 数据就失去了响应式 所以我们使用
<template>
<div>
我是测试pinia的组件--{{text}}
<button @click="update()">点我修改</button>
</div>
</template>
<script lang="ts" setup>
import {useStore} from "../store/index"
import {storeToRefs} from "pinia"
let store=useStore()
// 结构取值
// let {text}=store
// storeToRefs 让数据快去取出 并且不丢失响应式
let {text}=storeToRefs(store)
let update=()=>{
// 调用修改方法 store.你在pinia中actions中创建的修改函数
store.updatetext()
}
</script>
<style>
</style>
pinia--重置数据--$reset
顾名思义就是让数据恢复成state的初始值
<template>
<div>
我是测试pinia的组件--{{store.text}}
<button @click="update()">点我修改</button>
<button @click="reset()">点我恢复初始值</button>
</div>
</template>
<script lang="ts" setup>
import {useStore} from "../store/index"
let store=useStore()
let update=()=>{
store.updatetext()
}
let reset=()=>{
// $reset
store.$reset()
}
</script>
<style>
</style>
pinia--监听数据修改--$subscribe
$subscribe 监听store数据修改 当数据改变了 那么subscribe也会触发
<template>
<div>
我是测试pinia的组件--{{store.text}}
<button @click="update()">点我修改</button>
</div>
</template>
<script lang="ts" setup>
import {useStore} from "../store/index"
let store=useStore()
let update=()=>{
store.updatetext()
}
// 监听store数据修改
let sub=store.$subscribe((params,state)=>{
console.log("params",params);
console.log("state",state);
})
</script>
<style>
</style>
pinia--处理异步操作--action
在pinia中acition不但能处理同步操作 同样也可以处理异步操作
使用方式和之前一致
<template>
<div>
我是测试pinia的组件--{{store.text}}
<button @click="demoAxios()">点我获取异步数据</button>
</div>
</template>
<script lang="ts" setup>
import {useStore} from "../store/index"
let store=useStore()
let demoAxios=()=>{
store.link()
}
</script>
<style>
</style>
import { defineStore } from 'pinia'
import $http from "axios"
export const useStore = defineStore('main', {
state:()=>{
return {
text:"我是pinia的一个状态变量"
}
},
actions:{
link(),then((ok)=>{
console.log(ok)
})
async link(){
try {
let data= await $http({
url:"http://localhost:8888/userlist/demoget",
method:"get"
})
console.log(data.data.data.name)
this.text=data.data.data.name
} catch (error) {
console.log("失败")
}
}
}
})
pinia--getters
import { defineStore } from 'pinia'
export const useStoreb = defineStore('mainb', {
state:()=>{
return {
text:"abcdefg"
}
},
actions:{
},
getters:{
newtext(){
return this.text.toUpperCase()
}
}
})
pinia--模块化
pinia中定义模块 只需要定义多个store即可 因为pinia没有单一数据源这个概念 在其中可以定义多个store对象
<template>
<div>
我是测试pinia的组件--{{storeone.text}}---{{storetwo.text}}
</div>
</template>
<script lang="ts" setup>
// 引用第一个store
import {useStore} from "../store/index"
// 引用第2个store
import {useStoreb} from "../store/indexb"
let storeone=useStore()
let storetwo=useStoreb()
</script>
<style>
</style>
asyncawait
async是用于声明一个function是异步的,await用于等待一个异步方法执行完成
,async返回一个promise对象。
步骤:1.定义一个需要进行异步等待的方法,在前面加上 async 关键字
2.在这个内部有异步请求的方法中用awiat等待相应的请求完成
3.在这个异步请求的方法中返回一个promise对象,并封装resolve和reject方法