JAVA入门基础_Vue2~3的入门使用
- Vue核心
- 使用Vue配合Vue脚手架使用
- 创建一个Vue2的脚手架项目
- ref属性
- props属性,父给子传递数据(3种写法)
- mixin混入
- 插件
- scoped的style作用域
- 浏览器本地存储
- 组件的自定义事件(2种创建方式),可以让子给父传递数据
- 全局事件总线
- 消息订阅与发布
- nextTick钩子的使用
- Vue中的过度与动画
- 使用axios获取数据并解决跨域问题
- 插槽的使用
- Vuex
- Vue-Router
- router-link的replace属性
- 编程式路由导航$router(push、replace、back、forward、go)
- 缓存路由组件(部分或全部)
- 路由组件的2个生命周期钩子(一般用于开启和关闭资源,比如定时器)
- 路由组件
- history和hash模式的区别
- 使用element-ui组件库
- Vue3技术
Vue核心
初识Vue(掌握Vue的引入以及JS表达式与JS语句的区别)
- HTML
<body>
<div id="app">
<!-- 这里面写的是(JS表达式) -->
{{msg}}
</div>
</body>
- javascript脚本
<script type="text/javascript" src="../js/vue.js"></script>(写在head标签中)
<script>
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
msg: "这是一条信息,hello vue~"
}
},
mounted() {
console.log("hello")
}
})
</script>
Vue的模板语法(2大类)
-
插值语法:写JS表达式,用于解析标签体内容
-
指令语法:写JS表达式,用于解析标签(包括属性、内容、事件)
Vue数据绑定的2种方式(只要data中的数据被改变,Vue就会重新渲染模板)
-
单项数据绑定:数据只能从data流向页面。
-
双向数据绑定:数据不仅能从data流向页面,还可以从页面流向data。
-
html代码
<div id="app">
<!-- 数据单向绑定:只能从data将数据流向页面 -->
<input v-bind:value="msg"></input>
<input :value="msg"></input>
<hr>
<!-- 数据双向绑定:能从data将数据流向页面,也能将页面的数据流向data,适用于有value的选项 -->
<input v-model:value="msg"></input>
<input v-model="msg"></input>
</div>
- javascript代码
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
msg: "这是一条信息,hello vue~"
}
}
})
</script>
MVVM模型,还有VueComponent是什么?
-
M: Model模型,指的是Vue中的data数据
-
V: View视图,指的是页面中的模板
-
VM:ViewModel模型视图,指的是Vue的实例
-
VueComponent: Vue的组件。VM可以使用el、而VC不能使用el。并且VC中的data只能用函数写法
事件处理
事件的基本绑定使用(注意默认传参)
- HTML代码
<!-- 1、绑定点击事件 -->
<button v-on:click="showMsg">点我提示一个信息</button>
<button @click="showMsg">点我提示一个信息</button>
<!-- 2、绑定修改事件 -->
<input v-on:change="changeInput($event)" value="一个input"></input>
<input @change="changeInput" value="一个input"></input>
- javascript代码
<script>
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
msg: "这是一条信息,hello vue~"
}
},
methods: {
showMsg(event) {
console.log("当前的事件信息为:" + event)
alert(this.msg)
},
changeInput(event) {
console.log(event)
alert("当前的input已经被修改")
}
}
})
</script>
事件修饰符及组合使用(6个)
-
prevent:阻止默认事件(常用);
-
stop:阻止事件冒泡(常用);
-
once:事件只触发一次(常用);
-
capture:使用事件的捕获模式;
-
self:只有event.target是当前操作的元素时才触发事件;
-
passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
- HTML代码
<div id="app">
<!-- 1、阻止事件冒泡,不要出现点第二个Div导致第一个Div的事件触发 -->
<div @click="showMsg" style="width: 200px;height: 200px;background-color: azure">
我是第一个Div
<div @click.stop="showMsg" style="width: 100px;height: 100px;background-color: salmon;">
我是第二个Div
</div>
</div>
<hr>
<!-- 2、阻止标签的默认行为,例如a标签、button标签 -->
<div>
<a @click.prevent="showMsg" href="http://www.baidu.com">点我调用方法,我是a标签</a>
<form action="http://www.baidu.com">
<button @click.prevent="showMsg" type="submit">点我调用方法,我是按钮</button>
</form>
</div>
<hr>
<!-- 3、绑定的事件只能被调用一次时使用 -->
<div>
<button @click.once="showMsg">点我调用方法,我是一个按钮标签</button>
</div>
</div>
- javascript
<script>
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
msg: "这是一条信息,hello vue~"
}
},
methods: {
showMsg() {
alert(this.msg)
}
}
})
</script>
键盘事件
-
Vue中常用的按键别名:
- 回车 => enter
- 删除 => delete (捕获“删除”和“退格”键)
- 退出 => esc
- 空格 => space
- 换行 => tab (特殊,必须配合keydown去使用)
- 上 => up
- 下 => down
- 左 => left
- 右 => right
-
演示HTML代码
<div id="app">
<!-- 键盘事件 -->
请输入你的姓名:<input type="text" @keydown.enter="showName" />
</div>
计算属性
完整版写法
- HTML
<div id="app">
<div>
<h2>计算属性</h2>
<h3>firstName: {{firstName}}</h3>
<h3>lastName: {{lastName}}</h3>
<h3>fullName: {{fullName}}</h3>
</div>
</div>
- javascript
<script type="text/javascript">
// 通过控制台:vm.fullName = '王五',试着修改看看效果
const vm = new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
firstName: "张",
lastName: "三"
}
},
computed: {
fullName: {
// value为修改后的值
set(value) {
console.log("啥?你竟然修改了计算属性的值,修改后的值为:" + value)
},
get(){
return this.firstName + this.lastName
}
}
}
})
</script>
简写
-
当一个计算属性不需要使用set()时,也就是只使用get()时,可以使用简写形式
-
HTML
<div id="app">
<div>
<h2>计算属性</h2>
<h3>firstName: <input type="text" v-model="firstName"></h3>
<h3>lastName: {{lastName}}</h3>
<h3>fullName: {{fullName}}</h3>
</div>
</div>
- javascript(这里只放了简写后的形式)
computed: {
fullName(){
return this.firstName + this.lastName
}
}
监视属性
监视属性完整写法
-
监视属性的3个参数
(1)immediate:默认为false,如果调整为true,则在模板渲染后就立即执行一次handler方法
(2)deep:默认为false,监视对象时只能监视对象的引用被改变,调整为true则开启深度监视
(3)handler:回调函数,当监视的属性被修改时调用,有2个参数,分别为修改后的值与修改前的值 -
HTML
<div id="app">
<div>
<h2>监视属性</h2>
<!-- 这里使用双向绑定以便演示效果 -->
<h3>你的姓名: <input type="text" v-model="person.name"></h3>
<!-- 若不开启深度监视,无法监视到该属性的变换 -->
<h3>你的爱好: <input type="text" v-model="person.hobby.game"></h3>
</div>
</div>
- javascript
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
person: {
name:'张三',
age: 15,
hobby:{study:'看书', game: '亚索'}
}
}
},
watch:{
// 1、可以监视一个对象,但是oldValue与newValue没有任何区别
person: {
immediate: true,
deep: true,
handler(newValue, oldValue) {
console.log("当前的person发生了改变")
console.log(newValue)
console.log(oldValue)
}
}
}
})
</script>
监视属性简写形式
-
当一个监视属性只需要使用handle方法时可以使用简写形式
-
HTML
<div id="app">
<div>
<h2>监视属性</h2>
<!-- 这里使用双向绑定以便演示效果 -->
<h3>你的姓名: <input type="text" v-model="msg"></h3>
</div>
</div>
- javascript
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
msg: "这是一条信息"
}
},
watch:{
msg(newValue, oldValue) {
console.log("当前的msg发生了改变")
console.log(newValue)
console.log(oldValue)
}
}
})
</script>
绑定样式
绑定Class样式
-
绑定Class样式的三种方式
(1):class="myClass"
,字符串写法,适用于类名不确定,需要动态获取
(2):class="arrClass"
,数组写法,适用于类名不确定,类名数量也不确定
(3):class="objClass"
,对象写法,适用于类名确定、类名数量也确定,但是不确定使用还是不使用 -
编写的一点点CSS样式
<style>
.class1 {
color: red;
}
.class2 {
font-size: 30px;
}
.class3 {
font-family: 隶书;
}
</style>
- HTML
<div id="app">
<div>
<p :class="myClass">我是使用字符串方式绑定的class</p>
<hr>
<p :class="arrClass">我是使用数组方式绑定的class</p>
<hr>
<p :class="objClass">我是使用对象方式绑定的class</p>
</div>
</div>
- javascript
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
myClass: "class1",
arrClass: ['class1', 'class2', 'class3'],
objClass: {class1: true, class2: true, class3: false}
}
}
})
</script>
绑定CSS样式
-
绑定CSS样式的3种方式
(1):style={color: strStyle}
,单个对象写法,其中的strStyle为data中的数据
(2):style="objStyle"
,对象写法(常用)
(3):style="arrStyle"
,数组写法 -
HTML
<div id="app">
<div>
<!-- 1、通过对象的方式,其中第二个参数是动态指定的 -->
<p :style="{color: myStyle}">我是使用字符串方式绑定的style</p>
<hr>
<!-- 2、通过数组的方式 -->
<p :style="arrStyle">我是使用数组方式绑定的style</p>
<hr>
<!-- 3、通过对象的方式(常用) -->
<p :style="objStyle">我是使用对象方式绑定的style</p>
</div>
</div>
- javascript
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
// 单对象方式
myStyle: 'red',
// 数组方式
arrStyle: [
{color: 'red', fontSize: '30px'},
{fontFamily: '隶书'}
],
// 对象方式
objStyle: {color: 'red', fontSize: '30px', fontFamily: '隶书'}
}
}
})
</script>
条件渲染
-
v-if 与 v-show的区别在于v-show是控制元素的display属性,而v-if是直接移除掉DOM元素
-
v-if、v-else-if、else 配合使用时,中间不能被其他标签打断(也就是中间不能穿插其他标签)
-
HTML
<div id="app">
<div>
<!-- 1、v-if 与 v-show的使用 -->
<h3 v-if="flag">hello,小伙伴,我使用了v-if</h3>
<h3 v-show="flag">hello,小伙伴,我使用了v-show</h3>
<button @click="flag = !flag">点我切换flag</button>
<hr><hr><hr>
<!-- 2、v-if 、v-else-if、else -->
<h3 v-if="num % 3 == 1">hello,小伙伴,我是v-if</h3>
<h3 v-else-if="num % 3 == 2">hello,小伙伴,我是v-else-if</h3>
<h3 v-else>hello,小伙伴,我是v-else</h3>
<button @click="num++">点我num+1</button>
</div>
</div>
- javascript
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
flag: true,
num: 1
}
}
})
</script>
列表渲染
-
key:非常重要,务必不要重复,因为Vue底层使用diff算法对虚拟DOM进行比较时,就是先判断的key值
-
index,从0开始
-
HTML
<div id="app">
<ul>
<!-- 1、遍历数组(最常用) -->
<li v-for="(person,index) in personList" :key="id">
{{index}} -- {{person.id}} -- {{person.name}} -- {{person.age}}
</li>
</ul>
<!-- 2、遍历对象 -->
<ul>
<li v-for="(value,key,index) in student">
索引:{{index}} --- 键:{{key}} -- 值:{{value}}
</li>
</ul>
</div>
- javascript
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
personList: [
{id: '001', name: '张三', age: 15},
{id: '002', name: '李四', age: 19},
{id: '003', name: '王五', age: 55}
],
student: {name:'张三',age: 11}
}
}
})
</script>
收集表单数据(text、radio、checkbox[1或多]、select)
- HTML
<div id="app">
<form action="">
用户名:<input type="text" v-model="username"><br>
密码:<input type="password" v-model="password"><br>
性别:<input type="radio" v-model="sex" name="sex" value="0"> 男
<input type="radio" v-model="sex" name="sex" value="1"> 女<br>
爱好:<input type="checkbox" v-model="hobby" value="0"> 篮球
<input type="checkbox" v-model="hobby" value="1"> 足球
<input type="checkbox" v-model="hobby" value="2"> 学习<br>
籍贯:<select v-model="fromAddr">
<option selected>--请选择--</option>
<option value="beijing">北京</option>
<option value="shagnhai">上海</option>
<option value="shenzhen">深圳</option>
</select>
简介:<textarea v-model="intro"></textarea><br>
<input v-model="isAgree" type="checkbox" >是否同意用户协议?
<button>点击注册!</button>
</form>
</div>
- javascript
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
username:'',
password: '',
sex: '',
hobby: [],
fromAddr: '',
intro: '',
isAgree: false
}
}
})
</script>
- 数据收集效果
过滤器(Vue3中已弃用)
- HTML
<div id="app">
<!-- 1、请求使用过滤器只获取到前3个字符 -->
<h3>{{msg | strSpilt3}}</h3>
</div>
- javascript
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
msg: '你好呀adsfasdfzcxv'
}
},
filters: {
strSpilt3(value) {
return value.substr(0,3)
}
}
})
</script>
其他内置指令与自定义指令
v-text
-
作用:替换标签体中的内容
-
HTML
<div id="app">
<h3 v-text="myText"></h3>
</div>
- javascript
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
myText: '你好呀,我也很好,谢谢您'
}
}
})
</script>
v-html
-
作用:替换标签体中的内容,但是可以解析html中的标签(千万不要在用户输入的功能上使用v-html)
-
HTMl
<div id="app">
<h3 v-html="myHtml"></h3>
</div>
- javascript
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
myHtml: '<span style="color: red; font-size: 30px; font-family: 隶书">这是一段内容</span>'
}
}
})
</script>
v-cloak 属性
-
作用:** Vue在渲染模板时,会将v-cloak这个标签属性移除**,因此可以配合css样式达到当界面未渲染完毕时,不展示模板内容的效果。
-
CSS
<style>
[v-cloak]{
display: none;
}
</style>
- HTML
<div id="app" v-cloak>
<h3>{{msg}}</h3>
</div>
- javascript
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
msg: '这是一个消息'
}
},
beforeMount() {
console.log("挂载之前")
debugger;
}
})
</script>
v-once 属性
-
添加了该标签属性后,该标签中只会渲染一次Vue中的数据
-
HTML
<div id="app">
可以修改msg的值:<input type="text" v-model="msg"> <br>
<h3 v-once v-text="msg"></h3>
</div>
- javascript
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
msg: '这是一个消息'
}
}
})
</script>
v-pre属性
-
作用:被v-pre标志的DOM节点中的所有元素均不会被Vue解析
-
HTML
<div id="app">
<div v-pre>
我是添加了v-pre属性的一个div,我的内容不会被Vue解析:{{msg}}
</div>
<div>
我是没有添加v-pre属性的一个div,我的内容会被Vue解析:{{msg}}
</div>
</div>
- javascript
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
msg: '这是一个消息'
}
}
})
</script>
自定义指令
-
在实现v-bind指令的效果前提下,实现获取焦点的功能,取名为f-bind
-
自定义指令的属性
(0)element:当前的元素节点, binding:当前的指令状态
(1)bind(element,binding):指令与元素成功进行绑定时
(2)inserted(element,binding):指令所在的元素被插入页面时
(3)update(element, binding):当模板被重新渲染时 -
HTML
<div id="app">
一个文本输入框,姓名:<input type="text" v-fbind:value="name"><br>
<button @click="name+='!!'">点我名字发生变化</button>
</div>
- javascript
<script type="text/javascript">
new Vue({
el: "#app", // 挂载一个容器
data() { // 绑定数据
return {
name: '张三'
}
},
directives: {
// 1、简易写法,相当于 bind与update的结合
// fbind(element, binding){
// element.value = binding.value;
// element.focus();
// }
// 2、完整写法
fbind: {
bind(element, binding) {
element.value = binding.value
},
inserted(element, binding) {
element.focus();
},
update(element, binding) {
element.value = binding.value
console.log("你好呀,模板被重新解析了")
}
}
}
})
</script>
共计11个生命周期钩子
-
beforeCreate() : 数据代理与数据监测之前(无法通过vm访问data中的数据或methods方法等)
-
created():数据代理与数据监测之后(可以通过vm访问数据及方法了)
-
beforeMount() : 数据挂载之前(此时页面还是未经Vue编译的DOM,在该钩子中对DOM进行的更改均不生效)
-
mountd() : 数据挂载之后(页面已经是被Vue编译后的真实DOM)
-
beforeUpdate():数据更新之前
-
updated(): 数据更新之后
-
beforeDestroy():Vue实例销毁之前
-
destroyed:Vue实例销毁之后
-
$nextTick:页面渲染完毕后执行该方法的回调函数
-
activated:组件通过路由路径被访问时触发(可以绑定一些定时器之类的)
-
deactivated:组件通过路由路径离开时触发(可以关闭一些定时器之类的)
非单文件组件(了解组件的使用3步骤)
-
组件使用3步骤
(1)创建组件(2种方式)
(2)注册组件(2种方式)
(3)使用组件 -
HTML
<body>
<div id="app">
<!-- 3、使用组件 -->
<School></School>
<Student></Student>
</div>
</body>
- javascript
<script type="text/javascript">
// 1、创建组件的2种方式
const SchollComponent = Vue.extend({
template: `<h2>你好,这是我的学校</h2>`
})
const StudentComponent = {
template: `<h2>你好,我是一个学生,张三</h2>`
}
// 2、注册组件的2种方式,全局注册
Vue.component('School', SchollComponent);
// 2、局部注册
new Vue({
el: "#app",
components: {
Student: StudentComponent,
}
})
</script>
单文件组件的使用思路
-
(1)创建一个一般组件(非路由组件),后缀为.vue,并将组件使用export进行导出(暴露)
-
(2)创建一个组件,取名为App.vue,用于统一管理所有的Vue组件
-
(3)创建一个main.js,用于创建Vue实例,在Vue实例中注册App.vue这个组件,并且完成挂载
-
(4)既然需要挂载,所以创建一个index.html来作为容器使用
使用Vue配合Vue脚手架使用
创建一个Vue2的脚手架项目
下载并安装node.js来以便使用npm包管理器的命令
为npm配置淘宝镜像
npm config set registry https://registry.npm.taobao.org
安装Vue脚手架
npm install -g @vue/cli
进入到一个目录,打开cmd命令行来创建一个Vue项目、项目结构分析
# 创建一个vue_cli项目并按照提示选择vue2的版本
vue create my_study_vue2_cli
npm包管理器的运行、打包等命令
-
npm run serve
运行 -
npm run build
打包项目
ref属性
-
作用:给元素或子组件进行一个唯一的标识,可以通过当前实例的$refs属性中获取。
-
App.vue文件(引入了一个Student组件)
<template>
<div id="app">
<span ref="sp">我是一个span标签</span>
<Student ref="stu"></Student>
</div>
</template>
<script>
import Student from './components/Student.vue'
export default {
name: 'App',
components: {
Student
},
mounted() {
// 获取到ref指定的元素
console.log(this.$refs.sp);
// 获取到ref指定的组件
console.log(this.$refs.stu);
}
}
</script>
- 运行结果
props属性,父给子传递数据(3种写法)
-
props的三种使用方式
- 数组方式
props: ['studentList']
- 对象方式(限制类型)
props: { studentList: Number, name: Array, }
- 对象嵌套对象方式:
props: { name: { // 类型 type: Number, // 默认值,与require互斥 default: '小王同学', // 是否必须 required: true } }
- 数组方式
-
父组件
<template>
<div id="app">
<Student :studentList="studentList"></Student>
</div>
</template>
<script>
import Student from './components/Student.vue'
export default {
name: 'App',
components: {
Student
},
data() {
return {
studentList: [
{id:'001', name:'张三',age: 55},
{id:'002', name:'李四',age: 22},
{id:'003', name:'王五',age: 54}
]
}
}
}
</script>
- 子组件
<template>
<div>
<h2>学生列表</h2>
<ul>
<li v-for="(student,index) in studentList" :key="student.id">
{{student.id}} --- {{student.name}} --- {{student.age}}
</li>
</ul>
</div>
</template>
<script>
export default {
name: "Student",
props: ['studentList']
}
</script>
mixin混入
- 作用:相当于抽取公共的配置项,让其他组件可以通过导入使用(局部混入),或者使用Vue.mixin注册为全局混入
创建一个混入mixin.js,放在与main.js平级,并且将混入暴露
const testMixin = {
data(){
return {
testMsg: "我是testMixin混入的信息"
}
},
methods: {
test() {
alert("我是testMixin混入的test方法")
}
}
}
const demoMixin = {
data(){
return {
demoMsg: "我是demoMixin混入的信息"
}
},
methods: {
demo() {
alert("我是demoMixin混入的demo方法")
}
}
}
export {testMixin, demoMixin}
使用局部注册的方式使用
<template>
<div>
<h2>学生列表</h2>
<h2>demoMixin中的msg: {{demoMsg}}</h2>
<h2>testMixin中的msg: {{testMsg}}</h2>
<button @click="demo">点我使用demoMixin的demo()方法</button>
<button @click="test">点我使用testMixin的test()方法</button>
</div>
</template>
<script>
import {demoMixin} from "@/mixin";
export default {
name: "Student",
mixins:[demoMixin]
}
</script>
<style scoped>
</style>
使用全局引入的方式main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
// 引入一个混入并且进行全局注册
import {testMixin} from '@/mixin'
Vue.mixin(testMixin)
new Vue({
render: h => h(App),
}).$mount('#app')
插件
-
作用:扩展当前项目的功能。
-
使用步骤
(1)创建一个插件
(2)在main.js中进行全局注册
(3)各个组件就都可以使用了 -
创建一个插件
export default {
// 导入该插件后,Vue就会自动帮我们调用该方法,并且会传递VM给我们
install(Vue) {
// 注册一个全局指令
Vue.directive('fbind',(element,binding)=> {
element.innerText = binding.value;
})
// 注册一个全局组件
Vue.component('Demo',{
template: `<h2>我是Demo组件,嘿嘿</h2>`
})
// ...还可以定义混入、插件等等,因此插件可以做很多很多事
}
}
- 在main.js中进行全局注册
import myPlugin from "@/plugins/myPlugin";
Vue.use(myPlugin)
scoped的style作用域
-
作用:在组件中的style上标识了scoped属性后,则该组件中的样式只对当前演示有效
-
演示
<style scoped>
.title{
color: red;
}
</style>
浏览器本地存储
localStorage
<script type="text/javascript">
// 1、添加本地存储
localStorage.setItem('username','张三')
localStorage.setItem('password','123456')
// 1.1 存储一个对象
let person = {id:'001',name:'王五',age:55}
let personJson = JSON.stringify(person)
localStorage.setItem('person',personJson)
// 2、获取本地存储
let username = localStorage.getItem('username')
console.log(username)
// 2.1 获取一个Json字符串并转换为对象
let personStr = localStorage.getItem('person')
let newPerson = JSON.parse(personStr)
console.log(newPerson)
// 3、删除本地存储
localStorage.removeItem('username')
// 4、修改本地存储(覆盖)
localStorage.setItem('password','666666')
// 5、清空本地存储
localStorage.clear()
</script>
sessionStorage
<script type="text/javascript">
// 1、添加本地存储
sessionStorage.setItem('username','张三')
sessionStorage.setItem('password','123456')
// 1.1 存储一个对象
let person = {id:'001',name:'王五',age:55}
let personJson = JSON.stringify(person)
sessionStorage.setItem('person',personJson)
// 2、获取本地存储
let username = sessionStorage.getItem('username')
console.log(username)
// 2.1 获取一个Json字符串并转换为对象
let personStr = sessionStorage.getItem('person')
let newPerson = JSON.parse(personStr)
console.log(newPerson)
// 3、删除本地存储
sessionStorage.removeItem('username')
// 4、修改本地存储(覆盖)
sessionStorage.setItem('password','666666')
// 5、清空本地存储
sessionStorage.clear()
</script>
组件的自定义事件(2种创建方式),可以让子给父传递数据
标签属性创建自定义事件
- 父组件
<template>
<div id="app">
<Student @hello="sayHello" ></Student>
</div>
</template>
<script>
import Student from './components/Student.vue'
export default {
name: 'App',
components: {
Student
},
methods: {
sayHello(name){
alert(name + ",你好呀~")
}
}
}
</script>
- 子组件
<template>
<div>
<button @click="emitHello">点我触发当前组件上的hello自定义事件</button>
</div>
</template>
<script>
export default {
name: "Student",
methods: {
emitHello() {
const name = '张三'
this.$emit('hello',name)
}
}
}
</script>
绑定ref组件后使用$on
- 父组件
<template>
<div id="app">
<Student ref="stu" ></Student>
</div>
</template>
<script>
import Student from './components/Student.vue'
export default {
name: 'App',
components: {
Student
},
mounted() {
// 注意这里需要写箭头函数,不然this将会是调用者的vc
this.$refs.stu.$on('hello',(name)=> {
alert(name + ",你好呀!!!")
})
}
}
</script>
- 子组件
<template>
<div>
<button @click="emitHello">点我触发当前组件上的hello自定义事件</button>
</div>
</template>
<script>
export default {
name: "Student",
methods: {
emitHello() {
const name = '张三'
this.$emit('hello',name)
}
}
}
</script>
全局事件总线
-
作用:实现所有组件之间的数据传输
-
原理:创建一个所有人都能够访问到的VC,每个需要数据的组件都可以向该组件身上使用\(on**的方式绑定数据,每个发送数据的组件都可以通过向**该组件身上使用\)emit的方式调用方法来发送数据。
-
实现步骤
(1)在main.js中注册一个所有组件都能访问到的VC
(2)在需要接收数据的组件中使用该VC来绑定事件
(3)在发送数据的组件中使用该VC来调用事件发送数据 -
main.js(创建全局事件总线)
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
beforeCreate() {
// 1、创建全局事件总线
Vue.prototype.$bus = this
}
}).$mount('#app')
- 需要数据的组件向$bus绑定事件
<template>
<div id="app">
<Student></Student>
</div>
</template>
<script>
import Student from './components/Student.vue'
export default {
name: 'App',
components: {
Student
},
mounted() {
// 注意这里需要写箭头函数,不然this将会是调用者的vc
this.$bus.$on('hello',(name)=> {
alert(name + ",你好呀!!!")
})
}
}
</script>
- 需要发送数据的组件向$bus发送事件
<template>
<div>
<button @click="emitHello">点我触发当前组件上的hello自定义事件</button>
</div>
</template>
<script>
export default {
name: "Student",
methods: {
emitHello() {
const name = '张三'
this.$bus.$emit('hello',name)
}
}
}
</script>
消息订阅与发布
-
作用:可以完成不同组件之间数据的传递
-
使用步骤
(1)使用npm安装第三方库npm install --save pubsub-js
(2)引入插件import PubSub from 'pubsub-js'
(3)需要数据的就调用订阅api,PubSub.subscribe
(4)发送数据的就调用发布api,PubSub.publish
-
订阅者
<template>
<div>
<button @click="emitHello">点我触发当前组件上的hello自定义事件</button>
</div>
</template>
<script>
import PubSub from 'pubsub-js'
export default {
name: "Student",
methods: {
emitHello() {
const name = '张三'
PubSub.publish('hello',name)
}
}
}
</script>
- 发布者
<template>
<div id="app">
<Student></Student>
</div>
</template>
<script>
import Student from './components/Student.vue'
import PubSub from 'pubsub-js'
export default {
name: 'App',
components: {
Student
},
mounted() {
PubSub.subscribe('hello',(name)=> {
alert(name + ",你好呀!!!")
})
}
}
</script>
nextTick钩子的使用
-
作用:在页面渲染后再执行$nextTick()该方法的回调函数
-
编写一个一段文字,旁边放一个按钮,实现点击编辑的时候将文字变成一个输入框,并且获取input的焦点
- 发现的问题:Vue是在方法执行完毕后,才会去重新解析模板(总不能执行一条一句就解析一次模板吧)
- 而本案例中的写法是原本input框是隐藏的,此时执行input.focus()并没有效果。然后方法执行完毕,Vue才去解析模板,将input框显示出来。所以这就是如果不使用nextTick()就会出现bug的原因。执行时机的问题
-
测试代码(存在bug的)
<template>
<div>
<input v-show="isEdit" ref="oneInput" v-model="msg" type="text">
<span v-show="!isEdit">{{msg}}</span>
<button @click="getInputFocus">点我获取文本框的焦点</button>
</div>
</template>
<script>
export default {
name: "Student",
data() {
return {
msg: '一条信息',
isEdit: false
}
},
methods: {
getInputFocus() {
// 展示文本输入框,并隐藏原本的span标签
this.isEdit = true
this.$refs.oneInput.focus()
}
}
}
</script>
- 测试代码(使用nextTick后)
<template>
<div>
<input v-show="isEdit" ref="oneInput" v-model="msg" type="text">
<span v-show="!isEdit">{{msg}}</span>
<button @click="getInputFocus">点我获取文本框的焦点</button>
</div>
</template>
<script>
export default {
name: "Student",
data() {
return {
msg: '一条信息',
isEdit: false
}
},
methods: {
getInputFocus() {
// 展示文本输入框,并隐藏原本的span标签
this.isEdit = true
// 将会在DOM解析完毕后调用该方法
this.$nextTick(()=> {
this.$refs.oneInput.focus()
})
}
}
}
</script>
Vue中的过度与动画
-
Vue为我们提供了2个组件,只需要为其指定class则会为我们完成动画效果
-
使用animate.css这个样式库来完成动画效果
-
动画效果完成思路
(1)引入第三方样式库npm i animate.css
(1.1)可以进入官网看看有哪些效果:https://animate.style
(2)在需要使用动画的组件中使用transition
或者transition-group
来完成动画效果 -
transition 与 transition-group的相关属性
(1)appear:第一次出现时就展示动画效果
(2)name:需要引入的类样式的前缀(别漏了)
(3)enter-active-class: 设置进入的样式
(4)leave-active-class:设置离开的样式 -
使用示例
<template>
<div>
<!-- 单个标签的动画 -->
<transition
appear
name="animate__animated animate__bounce"
enter-active-class="animate__backInDown"
leave-active-class="animate__backOutDown">
<p v-show="isShowP">我想要拥有动画效果,我是一个p标签</p>
</transition>
<hr><hr>
<!-- 多个标签的动画 -->
<transition-group
appear
name="animate__animated animate__bounce"
enter-active-class="animate__backInDown"
leave-active-class="animate__backOutDown">
<p v-show="isShowP" key="1">我们是多个p标签,我们也想要动画效果</p>
<p v-show="isShowP" key="2">我们是多个p标签,我们也想要动画效果</p>
<p v-show="!isShowP" key="3">我们是多个p标签,我们也想要动画效果</p>
</transition-group>
<button @click="isShowP=!isShowP">点我可以切换p标签是否显示</button>
</div>
</template>
<script>
import 'animate.css'
export default {
name: "Student",
data() {
return {
isShowP: true
}
}
}
</script>
使用axios获取数据并解决跨域问题
安装axios并发送请求获取数据
-
使用
npm i axios
安装axios -
发送网络请求向服务器请求数据
<template>
<div>
<h2>学生列表</h2>
<ul>
<li v-for="student in studentList" :key="student.id">
{{student.id}} -- {{student.name}} --{{student.age}}
</li>
</ul>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: "Student",
data() {
return {
studentList: []
}
},
mounted() {
axios.get('http://localhost:5000/students')
.then((response)=>{
console.log(response)
})
.catch((err)=>{
console.log("出错了,错误信息为:" + err)
})
}
}
</script>
- 出现了如下的错误信息
Access to XMLHttpRequest at 'http://localhost:5000/students' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
为什么会出现跨域问题以及解决思路
-
跨域问题只会存在于服务器与浏览器之间。
-
并不是服务器不响应数据,而是浏览器会判断当前的 协议、域名、端口号 与访问的服务器是否相匹配,如果不匹配则会出现跨域问题,浏览器不接收数据。
-
既然知道了跨域问题仅存在于服务器与浏览器之间,那么我们有2种解决思路。
-(1)在服务器端进行修改,返回数据的时候携带一些特殊的响应头信息等解决
-(2)前端使用代理服务器来获取信息,再将代理服务器获取到的信息返回给前端
使用VueCli配置代理服务器解决跨域问题(想想端口号该怎么写?)
- 配置代理服务器的第一种方式,vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false,
devServer: {
proxy: 'http://localhost:5000'
}
})
- 再次发送请求时,需要访问本机的端口号,然后由代理服务器去访问数据服务器
axios.get('http://localhost:8080/students')
.then((response)=>{
this.studentList = response.data
})
.catch((err)=>{
console.log("出错了,错误信息为:" + err)
})
- 配置代理服务器的第二种方式
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false,
devServer: {
proxy: {
// 如果路径是 /students为前缀时,访问该服务器
'/students': {
// 访问的服务器地址
target: 'http://localhost:5000',
// 用于支持websocket
ws: true,
// 意思是请求代理服务器时,将students前缀去掉
// pathRewrite:{'^/students':''},
// 用于控制请求头中的域名及端口号,
// 若为true,则请求时会与target服务器的域名、端口号一致
// 若为false,则请求时,自己是啥域名和端口号,请求头中的域名和端口号就是啥
changeOrigin: true
},
// 如果路径是 /foo为前缀时,访问该服务器
'/foo': {
target: 'http://localhost:6000'
}
}
}
})
插槽的使用
-
作用:主要用于占位,当一个组件中存在一些不确定内容时,可以使用插槽。由使用者来决定具体展示的内容
-
默认插槽:
<slot></slot>
-
具名插槽:
<slot name="slot1"></slot>
-
作用域插槽:
<slot msg="这是一条信息"></slot>
默认插槽
- 子组件定义插槽
<template>
<div>
<h2>学生列表</h2>
<slot>我是一个插槽,如果你使用我这个组件不传标签的话,则显示这句话</slot>
</div>
</template>
<script>
export default {
name: "Student"
}
</script>
- 父组件使用插槽
<template>
<div id="app">
<Student>
<h2>我们要成为那个插座,我是一个h2标签</h2>
<h2>我们要成为那个插座,我是一个h2标签</h2>
<h2>我们要成为那个插座,我是一个h2标签</h2>
</Student>
</div>
</template>
<script>
import Student from './components/Student.vue'
export default {
name: 'App',
components: {
Student
}
}
</script>
具名插槽
- 子组件定义插槽
<template>
<div>
<h2>学生列表</h2>
<slot name="slot1">我是一个插槽,如果你使用我这个组件不传标签的话,则显示这句话</slot>
</div>
</template>
<script>
export default {
name: "Student"
}
</script>
- 父组件使用插槽
<template>
<div id="app">
<Student>
<h2 slot="slot1">我们要成为那个插座,我是一个h2标签</h2>
<h2>我们要成为那个插座,我是一个h2标签</h2>
<h2>我们要成为那个插座,我是一个h2标签</h2>
</Student>
</div>
</template>
<script>
import Student from './components/Student.vue'
export default {
name: 'App',
components: {
Student
}
}
</script>
作用域插槽(父组件调用子组件时,使用子组件传递的数据,注意必须使用template)
- 子组件定义插槽
<template>
<div>
<h2>学生列表</h2>
<slot name="slot1" :stu="student">我是一个插槽,如果你使用我这个组件不传标签的话,则显示这句话</slot>
</div>
</template>
<script>
export default {
name: "Student",
data() {
return {
student: {id: '001', name: '张三', age: 51}
}
}
}
</script>
- 父组件使用插槽
<template>
<div id="app">
<Student>
<!-- 采用普通语法 -->
<!--<template slot="slot1" scope="childData">
{{childData.stu.id}} -- {{childData.stu.name}} -- {{childData.stu.age}}
</template>-->
<!-- 采用解构语法 -->
<template slot="slot1" scope="{stu}">
{{stu.id}} -- {{stu.name}} -- {{stu.age}}
</template>
</Student>
</div>
</template>
<script>
import Student from './components/Student.vue'
export default {
name: 'App',
components: {
Student
}
}
</script>
Vuex
-
作用:Vuex可以实现所有组件中对数据的共享,让组件中的通信变得没有障碍。一般需要被所有组件所访问的数据,可以存放在Vuex中。Vuex的4大组成为: actions、mutations、state、store(用于管理前面3个)
-
使用步骤
(1)使用包管理器安装Vuex插件npm i vuex@3
,这里之所以写3,是因为Vuex4的版本不支持Vue2版本,而默认是安装Vuex4,因此需要注意版本
(2)在src目录下创建一个store目录用于表示仓库,在store目录下创建一个index.js
(3)在index.js中编写Vuex所需要的4个组件,并且最后将store给导出
Vuex的原理图
Vuex的基本使用(非模块化)
Store的配置以及如何使用state、getters、actions、mutations
-
下载安装Vuex3
npm i vuex@3
-
创建store目录和index.js,修改index.js的代码如下
import Vue from 'vue'
import Vuex from 'vuex'
// 1、使用vuex插件
Vue.use(Vuex)
// 2、配置actions,可以用来处理一些复杂的业务逻辑,可以理解为业务层
const actions = {
addNum(context, value) {
context.commit('ADDNUM',value)
}
}
// 3、配置mutations,直接与数据打交道,进行修改数据,理解为DAO层
const mutations = {
ADDNUM(state, value) {
state.num = value
}
}
// 4、配置getters,用于加工数据
const getters = {
// 获取当前计数大于10倍的信息
getBigNum(state) {
return state.num * 10
}
}
// 4、用于存储数据,理解为数据库
const state = {
// 1、供Count组件使用
num: 0,
// 2、供Person组件使用
personList: [
{id: '001', name: '张三', age: 15},
{id: '002', name: '李四', age: 44},
{id: '003', name: '王五', age: 52}
]
}
export default new Vuex.Store({
actions,
mutations,
getters,
state
})
- 页面中的使用
<template>
<div>
<h2>计数功能</h2>
<h3>当前的计数是: {{$store.state.num}}</h3>
<h3>当前的计数乘以10倍是: {{$store.getters.getBigNum}}</h3>
<button @click="addNum">点我数量+1</button>
</div>
</template>
<script>
export default {
name: "Count",
methods: {
addNum() {
const addOneNum = this.$store.state.num + 1
this.$store.dispatch('addNum', addOneNum)
}
}
}
</script>
简化获取vuex中数据与方法的写法
- store的代码
import Vue from 'vue'
import Vuex from 'vuex'
// 1、使用vuex插件
Vue.use(Vuex)
// 2、配置actions,可以用来处理一些复杂的业务逻辑,可以理解为业务层
const actions = {
addNum(context, value) {
context.commit('ADDNUM',value)
}
}
// 3、配置mutations,直接与数据打交道,进行修改数据,理解为DAO层
const mutations = {
ADDNUM(state, value) {
state.num += value
},
ADDNUM2(state, value) {
state.num += value
}
}
// 4、配置getters,用于加工数据
const getters = {
// 获取当前计数大于10倍的信息
getBigNum(state) {
return state.num * 10
}
}
// 4、用于存储数据,理解为数据库
const state = {
// 1、供Count组件使用
num: 0,
// 2、供Person组件使用
personList: [
{id: '001', name: '张三', age: 15},
{id: '002', name: '李四', age: 44},
{id: '003', name: '王五', age: 52}
]
}
export default new Vuex.Store({
actions,
mutations,
getters,
state
})
- Vue组件中的代码
<template>
<div>
<h2>计数功能</h2>
<h3>当前的计数是: {{num}}</h3>
<h3>当前的计数乘以10倍是:{{bigNum}} </h3>
<button @click="addNum(1)">点我数量+1(通过actions)</button>
<button @click="addNum2(2)">点我数量+2(使用mutations)</button>
</div>
</template>
<script>
import {mapState, mapGetters, mapActions, mapMutations} from 'vuex'
export default {
name: "Count",
computed: {
// 获取state中的数据,对象写法:左边为当前computed的键,右边为对应state中的键
// ...mapState({'num':'num'})
// 获取state中的数据,数组写法:若是需要当前computed的键,与state中的键一致,则可以使用该写法
...mapState(['num']),
// 获取getters中的数据,对象写法,还有一个数组写法,与mapState一致
...mapGetters({'bigNum':'getBigNum'})
},
methods: {
// 获取actions中的方法
...mapActions({'addNum':'addNum'}),
// 获取mutations中的方法
...mapMutations({'addNum2':'ADDNUM2'})
}
}
</script>
vuex的模块化开发
将针对不同组件的数据与方法拆分,拆分后放在不同的js文件中(别忘了namespaced)
- person.js
export default {
namespaced:true,
actions: {
addPerson(context, value) {
context.commit('ADDPERSON',value)
}
},
mutations: {
ADDPERSON(state, value) {
state.personList.unshift(value)
}
},
getters: {
// 获取所有人数的平均年龄
personAgeAvg(state) {
return state.personList.map(p => p.age).reduce((age,count) => age + count, 0) / state.personList.length
}
},
state: {
// 2、供Person组件使用
personList: [
{id: '001', name: '张三', age: 15},
{id: '002', name: '李四', age: 44},
{id: '003', name: '王五', age: 52}
]
}
}
- count.js
export default {
namespaced:true,
actions: {
addNum(context, value) {
context.commit('ADDNUM',value)
}
},
mutations: {
ADDNUM(state, value) {
state.num += value
},
ADDNUM2(state, value) {
state.num += value
}
},
getters: {
// 获取当前计数大于10倍的信息
getBigNum(state) {
return state.num * 10
}
},
state: {
// 1、供Count组件使用
num: 0
}
}
在store目录下的index.js中引入不同的模块
import Vue from 'vue'
import Vuex from 'vuex'
// 1、使用vuex插件
Vue.use(Vuex)
// 2、引入不同的模块
import countOptions from '@/store/count'
import personOptions from '@/store/person'
// 3、创建仓库
export default new Vuex.Store({
modules: {
countAbout: countOptions,
personAbout: personOptions
}
})
获取不同模块中的数据(原始方式)
<template>
<div>
<h2>人员列表</h2>
<ul>
<li v-for="(person,index) in personList" :key="person.id">
{{person.id}} --- {{person.name}} --- {{person.age}}
</li>
</ul>
<h3>员工的平均年龄为:{{personAgeAvg}}</h3>
<div>
员工id:<input type="text" v-model="id"><br>
员工姓名:<input type="text" v-model="name"><br>
员工年龄:<input type="number" v-model.number="age"><br>
</div>
<button @click="addPersonOfActions">点我添加一个员工(通过actions的方式)</button>
<button @click="addPersonOfMutations">点我添加一个员工(通过mutations的方式)</button>
</div>
</template>
<script>
export default {
name: "Person",
data() {
return {
id: '',
name: '',
age: 0
}
},
computed: {
personList() {
return this.$store.state.personAbout.personList
},
personAgeAvg() {
return this.$store.getters["personAbout/personAgeAvg"]
}
},
methods: {
addPersonOfActions() {
const person = {id: this.id, name: this.name, age: this.age}
this.$store.dispatch("personAbout/addPerson", person)
},
addPersonOfMutations() {
const person = {id: this.id, name: this.name, age: this.age}
this.$store.commit("personAbout/ADDPERSON", person)
}
}
}
</script>
获取不同模块中的数据(通过简写方式)
<template>
<div>
<h2>计数功能</h2>
<h3>当前的计数是: {{num}}</h3>
<h3>当前的计数乘以10倍是:{{bigNum}} </h3>
<button @click="addNum(1)">点我数量+1(通过actions)</button>
<button @click="addNum2(2)">点我数量+2(使用mutations)</button>
</div>
</template>
<script>
import {mapState, mapGetters, mapActions, mapMutations} from 'vuex'
export default {
name: "Count",
computed: {
// 获取state中的数据,对象写法:左边为当前computed的键,右边为对应state中的键
...mapState('countAbout',{'num':'num'}),
// 获取state中的数据,数组写法:若是需要当前computed的键,与state中的键一致,则可以使用该写法
...mapState('countAbout', ['num']),
// 获取getters中的数据,对象写法,还有一个数组写法,与mapState一致
...mapGetters('countAbout', {'bigNum':'getBigNum'})
},
methods: {
// 获取actions中的方法
...mapActions('countAbout', {'addNum':'addNum'}),
// 获取mutations中的方法
...mapMutations('countAbout', {'addNum2':'ADDNUM2'})
}
}
</script>
Vue-Router
-
作用: 就相当于是一个路由器,它管理着一个个路由,通过配置规则,使一个个路由通过不同的访问路径展示不同的组件,达到页面局部刷新的效果,是单页面应用程序(SPA--------single page application)开发时的必备选项。
-
使用步骤
(1)安装Vue-Router插件,由于我这使用的是vue2,所以安装vue-router3的版本。npm i vue-router@3
(2)在src目录下创建2个目录,分别为pages(用于存放路由组件)和router(用于存放路由相关配置)
(3)在router目录下创建一个index.js,用于编写路由规则
(4)而后页面通过 router-link标签以及 router-view标签实现路由的跳转
路由的基本使用
- 创建index.js配置路由规则
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件
import Home from '@/pages/Home'
import About from '@/pages/About'
// 1、配置路由规则
const routes = [
{
path: '/home',
component: Home
},
{
path: '/about',
component: About
}
]
// 2、创建一个VueRouter
export default new VueRouter({
routes
})
- 在main.js中配置
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
// 引入VueRouter插件并使用
import VueRouter from "vue-router";
Vue.use(VueRouter)
// 引入路由规则
import router from "@/router";
new Vue({
render: h => h(App),
router: router
}).$mount('#app')
- 使用router-link 以及 router-view配合实现路由跳转
<template>
<div id="app">
<router-link to="/home">我的小屋</router-link>
<br>
<router-link to="/about">关于我的信息</router-link>
<hr>
<!-- 路由视图,用于展示不同的路由组件 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
- 2个路由组件
// Home
<template>
<div>
<h2>欢迎来到你的小屋</h2>
</div>
</template>
<script>
export default {
name: "Home"
}
</script>
// About
<template>
<div>
<h2>欢迎来到关于你的信息</h2>
</div>
</template>
<script>
export default {
name: "About"
}
</script>
多级路由的使用(配合children属性,还要注意path路径)
- 在router/index.js中配置多级路由规则
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件
import Home from '@/pages/Home'
import HomeBasic from "@/pages/HomeBasic";
import HomeDetail from "@/pages/HomeDetail";
import About from '@/pages/About'
// 1、配置路由规则
const routes = [
{
path: '/home',
component: Home,
// 嵌套组件的必备属性
children: [
{
// 注意这里的path不要再带 / 斜杠了
path: 'basic',
component: HomeBasic
},
{
path: 'detail',
component: HomeDetail
},
]
},
{
path: '/about',
component: About
}
]
// 2、创建一个VueRouter
export default new VueRouter({
routes
})
- 修改需要进行路由嵌套的组件
<template>
<div>
<h2>欢迎来到你的小屋</h2>
<router-link to="/home/basic">点我即可查看小屋的基本信息</router-link><br>
<router-link to="/home/detail">点我即可查看小屋的详细信息</router-link>
<hr><hr>
<!-- 用于展示不同的路由组件 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "Home"
}
</script>
- 新增2个路由组件
// HomeBasic
<template>
<div>
<h3>我是小屋的基本信息</h3>
</div>
</template>
<script>
export default {
name: "HomeBasic"
}
</script>
// HomeDetail
<template>
<div>
<h3>我是小屋的详细信息</h3>
</div>
</template>
<script>
export default {
name: "HomeDetail"
}
</script>
路由的query参数传递(给路由组件传)
通过字符串拼接的方式传递query参数
- 修改router-link标签,采用字符串拼接
<router-link :to="`/home/basic?msg=${msg}&status=${status}`">
点我即可查看小屋的基本信息
</router-link><br>
- 路由组件通过$route.query来获取其中的参数
<div>
<h3>我是小屋的基本信息</h3>
<h5>我是小屋,我接收到了query参数:其中msg为:{{$route.query.msg}}</h5>
<h5>我是小屋,我接收到了query参数:其中status为:{{$route.query.status}}</h5>
</div>
通过router-link中to的对象写法来传递
- 修改router-link标签,采用to的对象写法
<router-link
:to="{
path: '/home/basic',
query: {
msg: msg,
status: status
}
}">
点我即可查看小屋的基本信息
</router-link><br>
- 路由组件还是使用一样的方式接收即可
<div>
<h3>我是小屋的基本信息</h3>
<h5>我是小屋,我接收到了query参数:其中msg为:{{$route.query.msg}}</h5>
<h5>我是小屋,我接收到了query参数:其中status为:{{$route.query.status}}</h5>
</div>
命名路由(当路由的嵌套层数过多时可以使用)
-
作用: 给路由取一个名字。如果想要通过路由的名字实现路由的跳转,则router-link中的to必须为对象写法,将path变更为name,再写上路由名即可。
-
为一个路由取名
// 1、配置路由规则
const routes = [
{
// 注意这里取了一个名字!!!!!!!!!!
name: 'myHome',
path: '/home',
component: Home,
// 嵌套组件的必备属性
children: [
{
// 注意这里的path不要再带 / 斜杠了
path: 'basic',
component: HomeBasic
},
{
path: 'detail',
component: HomeDetail
},
]
},
{
path: '/about',
component: About
}
]
- 通过路由的名称完成路由的跳转
<router-link :to="{name:'myHome'}">我的小屋</router-link>
路由的params参数传递
to采用对象写法时需要命名路由
- 如果传递的params采用对象写法,则必须配置为命名路由,并且不再指定path占位符
{
name: 'xiangqing',
path: 'detail',
component: HomeDetail
}
- 传递参数(对象写法)
<router-link :to="{
name: 'xiangqing',
params: {
id: '001',
msg: '详情信息:我只是一条单纯的详细信息'
}
}">
点我即可查看小屋的详细信息
</router-link>
- 接收参数
<div>
<h3>我是小屋的详细信息</h3>
<h4>Home路由传递过来的消息id为: {{$route.params.id}}</h4>
<h4>Home路由传递过来的详细消息为: {{$route.params.msg}}</h4>
</div>
to采用字符串写法时,必须配置path占位符
- 如果传递的params采用字符串写法,则需要配置路由的path占位符
{
// name: 'xiangqing',
path: 'detail/:id/:msg',
component: HomeDetail
}
- 传递参数
<router-link :to="`/home/detail/111/${detailMsg}`">
点我即可查看小屋的详细信息
</router-link>
注意: 这里的detailMsg已经在data中配置了。
- 接收参数
<div>
<h3>我是小屋的详细信息</h3>
<h4>Home路由传递过来的消息id为: {{$route.params.id}}</h4>
<h4>Home路由传递过来的详细消息为: {{$route.params.msg}}</h4>
</div>
路由的props配置(解决总采用$route.的方式获取参数)
第一种配置方式(对象形式)
- 第一种写法,props的值为对象,路由组件可以通过 props属性来获取对应的值
{
path: 'basic',
component: AboutBasic,
props: {
name: '张三',
age: 15
}
}
- 路由组件获取参数
<template>
<div>
<h4>我是基础的个人信息</h4>
<h5>姓名: {{name}}</h5>
<h5>年龄: {{age}}</h5>
</div>
</template>
<script>
export default {
name: "AboutBasic",
props: ['name', 'age']
}
</script>
布尔值方式(也必须遵循params传递参数的规则)
- 第二种方式,返回一个布尔值,此时会将所有的params参数通过props参数传递给路由组件
{
name: 'basicAbout', // 因为也必须遵循params传递参数的规则
path: 'basic',
component: AboutBasic,
props: true
}
- router-link的写法
<router-link :to="{
name: 'basicAbout',
params: {
name: '李四',
age: 15
}
}">
点我查看当前的基础个人信息
</router-link><br>
- 路由组件接收参数
<template>
<div>
<h4>我是基础的个人信息</h4>
<h5>姓名: {{name}}</h5>
<h5>年龄: {{age}}</h5>
</div>
</template>
<script>
export default {
name: "AboutBasic",
props: ['name', 'age']
}
</script>
函数方式(非常灵活,可以拿到$route,不过依然要遵循params参数的获取规则)
- 第三种方式:函数形式,该函数可以拿到$route,最后返回一个对象,非常灵活
{
name: 'detailAbout',
path: 'detail',
component: AboutDetail,
props: function (route) {
const allParam = {
id: route.query.id,
name: route.query.name,
age: route.query.age,
hobby: route.params.hobby,
height: route.params.height
}
return allParam
}
}
- router-link的写法,传递了多种类型的参数
<router-link
:to="{
name: 'detailAbout',
query: {id:'001',name:'张三', age: 55},
params: {hobby:'学习',height: '180厘米'}
}">
点我查看当前的详细个人信息
</router-link>
- 路由组件使用props接收参数
<template>
<div>
<h4>我是详细的个人信息</h4>
<h5>身份编号为: {{id}}</h5>
<h5>姓名为: {{name}}</h5>
<h5>年龄为: {{age}}</h5>
<h5>爱好为: {{hobby}}</h5>
<h5>身高为: {{height}}</h5>
</div>
</template>
<script>
export default {
name: "AboutDetail",
props: ['id','name','age','hobby','height']
}
</script>
router-link的replace属性
-
一旦给router-link标签添加上replace属性,那么就会覆盖默认的push规则
-
使用方式,添加个属性即可
<router-link replace></router-link>
编程式路由导航$router(push、replace、back、forward、go)
-
作用:当我们无法使用router-link标签时,可以采用编程式路由导航完成路由的跳转
-
使用实例
<template>
<div>
<h2>欢迎来到你的小屋</h2>
<button @click="resetHomeBasic">点我即可查看小屋的基本信息,我只是个button</button>
<button @click="resetHomeDetail">点我即可查看小屋的详细信息,我只是个button</button>
<hr><hr>
<!-- 用于展示不同的路由组件 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "Home",
methods: {
resetHomeBasic() {
this.$router.push({
path: '/home/basic',
query: {id: '001', name: '茅草屋'},
})
},
resetHomeDetail() {
this.$router.replace({
path: '/home/detail',
query: {id: '002', name: '精装房'}
})
}
}
}
</script>
- 额外3个与历史记录有关的api
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进也可后退
缓存路由组件(部分或全部)
-
作用: 当一个路由组件不希望被销毁时,可以缓存路由组件,不然每次切换路由时,路由组件都会被执行销毁。
-
在router-view外包一层
keep-alive
,该标签还有个inlude属性,可以指定需要被缓存的路由组件(注意:这里写的是组件名) -
缓存全部路由组件
<template>
<div>
<h2>欢迎来到你的小屋</h2>
<router-link to="/home/basic">点我即可查看小屋的基本信息</router-link><br>
<router-link to="/home/detail">点我即可查看小屋的详细信息</router-link>
<hr><hr>
<!-- 缓存所有被包裹的路由组件 -->
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
name: "Home"
}
</script>
- 缓存部分路由组件
<template>
<div>
<h2>欢迎来到你的小屋</h2>
<router-link to="/home/basic">点我即可查看小屋的基本信息</router-link><br>
<router-link to="/home/detail">点我即可查看小屋的详细信息</router-link>
<hr><hr>
<!-- 缓存部分路由组件,多个组件之间用逗号分割 -->
<keep-alive include="HomeBasic,HomeDetail">
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
name: "Home"
}
</script>
路由组件的2个生命周期钩子(一般用于开启和关闭资源,比如定时器)
-
作用:可以监视路由组件在通过路由的方式被访问时以及离开时。
-
钩子函数:
activated 和 deactivated
<template>
<div>
<h3 :style="{opacity: opacity}">我是小屋的基本信息</h3>
我是一个输入框,我需要被缓存:<input type="text" value="">
</div>
</template>
<script>
export default {
name: "HomeBasic",
data() {
return {
opacity: 1
}
},
activated() {
console.log("我是HomeBasic,我被访问了,进入了活动状态")
// 开启定时器
this.timer = setInterval(() => {
console.log("###")
this.opacity = this.opacity - 0.02
if(this.opacity <= 0) {
this.opacity = 1
}
},15)
},
deactivated() {
console.log("我是HomeBasic,路由从我这离开了,失活了")
// 关闭定时器
clearInterval(this.timer)
}
}
</script>
路由组件
作用: 用于对路由进行权限控制
全局路由组件(包含路由的元数据定义)
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件
import Home from '@/pages/Home'
import HomeBasic from "@/pages/HomeBasic";
import HomeDetail from "@/pages/HomeDetail";
import About from '@/pages/About'
// 1、配置路由规则
const routes = [
{
// 注意这里取了一个名字!!!!!!!!!!
name: 'myHome',
path: '/home',
component: Home,
// 配置路由的元数据
meta: {
title: '我的小屋'
},
// 嵌套组件的必备属性
children: [
{
path: 'basic',
component: HomeBasic
},
{
path: 'detail',
component: HomeDetail
},
]
},
{
path: '/about',
component: About,
// 配置路由的元数据
meta: {
title: '关于~~'
}
}
]
// 2、创建一个VueRouter
const router = new VueRouter({
routes
})
// 3、 配置前置全局路由守卫
router.beforeEach((to, from , next) => {
// console.log(to)
// console.log(next)
// to:要到哪个路由去
// from: 从哪个路由而来
// next:这是一个函数,调用后即可放行
// 这里可以做一些权限判断,例如从localStrong中取一个username,如果为zhangsan,则放行,否则不放行
const username = localStorage.getItem("username")
if(username === 'zhangsan') {
next()
}
})
// 4、配置后置路由守卫(可以用来修改标题之类的)
router.afterEach((to, from) => {
// 如果该前往的路由的原信息中有title,则将当前网页的标题修改
if(to.meta.title) {
document.title = to.meta.title
}else {
document.title = '学习vue的测试网页'
}
})
export default router
独享路由组件
{
path: '/about',
component: About,
// 独享路由守卫,只有前置,没有后置
beforeEnter: function (to, from, next) {
// 这里可以做一些权限判断之类的
// 放行
next()
}
}
组件内路由守卫(通过路由规则进入和离开时生效)
<template>
<div>
<h2>欢迎来到关于你的信息</h2>
</div>
</template>
<script>
export default {
name: "About",
beforeRouteEnter(to, from, next) {
// 通过路由规则进入该组件时生效
},
beforeRouteLeave(to, from, next) {
// 通过路由规则离开该组件时生效
}
}
</script>
history和hash模式的区别
-
history的网址路径看起来比较好看,但是兼容性较差
-
hash模式的网址路径中会携带一个#,看起来不好看,但是兼容性好
-
使用过程中的区别是:hash模式不会将#后面的内容发送给服务器,但是history模式会
-
因此需要history模式时,需要后端的配合
使用element-ui组件库
- 使用步骤
(1)安装element-ui组件库,npm i element-ui
(2)在main.js中引入相对应的组件,使其全局可用(注意此时引入了全部的组件,非常大)
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
(3)接下来页面想用就直接拿来用即可
如果想要按需引入
-
安装如下插件
npm install babel-plugin-component -D
-
修改
babel.config.js
,添加上如下内容
{
"presets": [["@babel/preset-env", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
- 接下来就可以进行按需引入了,在main.js中
import Vue from 'vue';
import { Button, Select } from 'element-ui';
import App from './App.vue';
Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
Vue3技术
使用Vue-Cli创建Vue3的项目
-
需要建立在已经安装好了node.js,拥有了npm命令
-
并且已经为npm配置了淘宝的镜像时使用
-
创建Vue3项目
vue create 需要的项目名
,而后选择vue3,点击创建
分析Vue3项目结构中的main.js
// 引入createApp函数,用于创建实例对象-app
// 类似于vue2中的vm,不过这个app实例对象比vm更轻
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
常用的Compsitions API
初识setup,感受一下vue2到vue3的变化
<template>
<h2>第一个Vue3项目</h2>
<h3>{{msg}}</h3>
<!-- 1、目前发现数据并不是响应式的 -->
<input type="text" v-model="title"/>
<!-- 2、即便修改了值,页面也不会发生变化 -->
<button @click="updateTitle">点我修改title的值为张三</button>
<button @click="showTitle">点我查看当前title的值</button>
</template>
<script>
export default {
name: 'App',
setup: function() {
// 1、定义2个变量
const msg = "我是一条信息"
let title = "我是标题"
// 2、定义2个方法
function updateTitle() {
title = '张三'
}
function showTitle() {
alert("当前title的值为: " + title)
}
// 3、返回一个对象,模板中可以直接使用
return {msg, title, updateTitle,showTitle}
}
}
</script>
注意:Vue3可以兼容vue2的写法,但是vue2的写法无法读取Vue3的数据
ref函数,用于使数据成为响应式的(基础类型、对象|数组类型,使用方式、修改方式)
-
使用步骤
(1)先引入ref函数
(2)赋值时,将需要进行响应式的数据用函数包裹即可
(3)js语法操作数据时需要 xxx.value,而在模板中d读取数据时,不需要.value
(4)ref函数可以包裹基本数据类型,内部使用Object.defineProperty()的get、set完成的响应式
(5)也可以包裹对象或者数组数据类型,内部求助了Vue3的新函数reactive()将类型转换为Proxy -
使用演示
<template>
<h2>第一个Vue3项目</h2>
<h3>当前的标题是:{{title}}</h3>
<input type="text" v-model="title"/>
<button @click="addTitleHi">点我给标题最后加一个感叹号</button>
</template>
<script>
import {ref} from 'vue'
export default {
name: 'App',
setup: function() {
let title = ref("我是标题")
function addTitleHi() {
console.log(title)
title.value = title.value += '!'
}
return {title, addTitleHi}
}
}
</script>
reactive函数
-
使用步骤
(1)先引入reactive函数
(2)用于包裹对象数据类型,使其成为响应式,内部通过Proxy完成响应式(数据劫持),通过Reflect操作源对象内部的数据。
(3)修改时不需要像RefImpl对象一样xxx.value了,以前对象如何修改,现在就如何修改 -
使用示例
<template>
<h2>第一个Vue3项目</h2>
<h3>id: {{person.id}}</h3>
<h3>姓名: {{person.name}}</h3>
<button @click="updatePersonName">点我修改person的姓名为赵六</button>
</template>
<script>
import {reactive} from 'vue'
export default {
name: 'App',
setup: function() {
let person = reactive({id:'001',name:'李四'})
function updatePersonName() {
person.name = '赵六'
}
return {person, updatePersonName}
}
}
</script>
setup的两个注意点
执行时机与次数
- setup在beforeCreate之前执行一次,并且只执行一次
setup的两个参数
-
props : 用于接收组件从外部传递过来,并且在props中声明接收了的属性
-
context:上下文对象
- attrs : 外部传递的数据中,如果props中没有声明接收该属性,则会将其放到这里
- solts:收到的未被当前组件使用的插槽数据
- emit:访问当前VC实例的自定义事件函数
计算属性与监视
计算属性(函数,一个参数)
-
使用步骤
(1)引入计算属性这个函数
(2)使用时,参数可以是一个对象(用于写set和get方法),也可以直接是一个函数,相当于get() -
使用示例
<template>
<h2>计算属性的使用</h2>
<h3>当前的数字为: {{num}}</h3>
<h3>当前的数字 * 10倍后为: {{bigNum}}</h3>
可以通过该input对num进行修改:<input type="number" v-model.number="num">
</template>
<script>
import {computed, ref} from 'vue'
export default {
name: 'App',
setup: function() {
let num = ref(10);
// 1、完整写法
/*let bigNum = computed({
get() {
return num.value * 10
},
set() {
}
})*/
// 2、简写形式
let bigNum = computed(() => {
return num.value * 10
})
return {num, bigNum}
}
}
</script>
watch属性监视(监视对象、数组时的小坑)
-
使用步骤
(1)引入watch函数
(2)该函数有3个参数
参数1:需要监视的属性
参数2:回调函数(相当于vue2时的handler函数)
参数3:对象形式,可以指定watch的额外参数,例如deep和immediate属性
(3)监视对象|数组属性时,其newValue与oldValue均为修改后的值(注意) -
使用示例
<template>
<h2>监视属性的使用</h2>
<h3>当前的数字为: {{num}}</h3>
可以通过该input对num进行修改:<input type="number" v-model.number="num">
<hr>
<hr>
<h3>当前的Person信息</h3>
<h4>{{person.name}} -- {{person.age}} -- {{person.job.study}} -- {{person.job.play}} -- </h4>
<button @click="person.name = '王老五'">点我即可将person.name修改为王老五</button>
<button @click="person.job.play = '英雄联盟'">点我即可将person.job.play修改为英雄联盟</button>
<hr>
<hr>
<h3>当前的爱好为</h3>
<ul>
<li v-for="(h,index) in hobby" :key="index">
{{h}}
</li>
</ul>
<button @click="hobby[0] = '看电视'">点我修改第一个爱好为:看电视</button>
</template>
<script>
import {watch, ref, reactive} from 'vue'
export default {
name: 'App',
setup: function () {
// 准备数据
let num = ref(10);
let num2 = ref(20);
let person = reactive({name: '张三', age: '李四', job: {study: '看书', play: '羽毛球'}})
let hobby = reactive(['羽毛球','篮球','足球'])
// 1、监视单个ref属性
watch(num, (newValue, oldValue) => {
console.log("修改后的num值为:" + newValue)
console.log("修改前的num值为:" + oldValue)
}, {immediate: false, deep: true})
// 2、监视多个ref属性,此时得到的newValue和oldValue将会是一个数组
watch([num, num2], (newValue, oldValue) => {
console.log("num或num2中任意一个值发生了变化,变化前:" + oldValue)
console.log("num或num2中任意一个值发生了变化,变化后:" + newValue)
}, {immediate: false, deep: true})
// 3、监视reactive定义的响应式数据,默认就强制开启深度监视,此时的deep属性无效,其实相当于是对一个对象的第一层进行了监视
watch(person, (newValue, oldValue) => {
console.log("person被进行了修改,变化前:")
console.log(oldValue)
console.log("person被进行了修改,变化后:")
console.log(newValue)
}, {immediate: false, deep: false})
// 4、监视reactive定义的响应式数据中的某个属性中的属性变化,此时开启deep深度监视有效
watch(() => person.job, (newValue, oldValue) => {
console.log("person中的job的play属性被进行了修改,变化前:")
console.log(oldValue)
console.log("person中的job的play属性被进行了修改,变化后:")
console.log(newValue)
}, {immediate: false, deep: true})
// 5、监视多个reactive定义的响应式数据中的某个属性中的属性变化,此时开启deep深度监视有效(在这也变成了一个数组,数组中为Proxy对象)
watch([() => person.job, ()=> person.job.study], (newValue, oldValue) => {
console.log("person中的job的play或者study属性被进行了修改,变化前:")
console.log(oldValue)
console.log("person中的job的play或者study属性被进行了修改,变化后:")
console.log(newValue)
}, {immediate: false, deep: true})
// 6、监视一个数组,
watch(hobby, (newValue, oldValue) => {
console.log("hobby被进行了修改,变化前:")
console.log(oldValue)
console.log("hobby被进行了修改,变化后:")
console.log(newValue)
}, {immediate: false, deep: false})
return {num, num2, person, hobby}
}
}
</script>
watchEffect
-
watch的套路是:既要指明监视的属性,也要指明监视的回调。
-
watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
-
watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
Vue3的生命周期钩子
-
beforeCreate
===>setup()
-
created
=======>setup()
-
beforeMount
===>onBeforeMount
-
mounted
=======>onMounted
-
beforeUpdate
===>onBeforeUpdate
-
updated
=======>onUpdated
-
beforeUnmount
==>onBeforeUnmount
-
unmounted
=====>onUnmounted
-
使用示例
<template>
<h2>生命周期钩子的使用示例</h2>
<h4>当前num为:{{num}}</h4>
<button @click.once="num++">点我num + 1,只能点击一次</button>
</template>
<script>
import {ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted} from 'vue'
export default {
name: 'App',
setup() {
let num = ref(10)
onBeforeMount(() => {
console.log("挂载之前")
})
onMounted(() => {
console.log("挂载完毕")
})
onBeforeUpdate(() => {
console.log("数据更新之前")
})
onUpdated(() => {
console.log("数据更新之后")
})
onBeforeUnmount(() => {
console.log("解除挂载之前")
})
onUnmounted(() => {
console.log("解除挂载之后")
})
return {num}
}
}
</script>
自定义hook函数(一般hook文件以use开头,而且默认导出为一个函数)
-
作用: 相当于vue2的mixin混入,本质的作用是将代码进行封装,使代码更加清晰易懂。
-
为什么默认导出为一个函数? 因为需要保证每一个人使用到的都是不同的数据,不能出现数据错乱
-
使用步骤
(1)在src目录下创建一个hooks目录
(2)在该目录下创建一个js文件,用于实现某个公用的功能,需要返回核心的数据
(3)需要使用的地方直接引入即可 -
创建一个点击屏幕则显示坐标功能的hook,名为usePoint.js
import {reactive,onMounted,onBeforeUnmount} from 'vue'
export default function() {
let point = reactive({
x: 0,
y: 0
})
function savePoint(event) {
point.x = event.pageX
point.y = event.pageY
}
onMounted(() => {
window.addEventListener('mousemove', savePoint)
})
onBeforeUnmount(() => {
window.removeEventListener('mousemove', savePoint)
})
return point
}
- 接下来需要使用到该功能的组件直接引入即可使用
<template>
<h2>hook的使用示例</h2>
<h4>当前x轴为:{{point.x}}, y轴为: {{point.y}}</h4>
<h4>小提示:只要对着屏幕移动鼠标,即可看到对应的坐标位置</h4>
</template>
<script>
import {reactive} from 'vue'
import usePoint from '@/hooks/usePoint'
export default {
name: 'App',
setup() {
let point = reactive(usePoint())
return {point}
}
}
</script>
toRef与toRefs
-
作用:可以获取对象中的一个属性的引用,并且将指向传递给一个变量,使页面代码更加简洁
-
toRef是一个个属性的处理,而toRefs是将整个对象一起处理
-
使用示例(toRef)
<template>
<h2>hook的使用示例</h2>
<h4>当前用户的id为: {{id}}</h4>
<h4>当前用户的name为: {{name}}</h4>
<h4>当前用户的age为: {{age}}</h4>
<h4>当前用户的birthday为: {{birthday}}</h4>
<h4>当前用户的job为: {{job}}</h4>
</template>
<script>
import {toRef, reactive} from 'vue'
export default {
name: 'App',
setup() {
let person = reactive({id: '001', name: '张三', age: 15, birthday: '1111-10-21',job: '摸鱼'})
let id = toRef(person, 'id')
let name = toRef(person, 'name')
let age = toRef(person, 'age')
let birthday = toRef(person, 'birthday')
let job = toRef(person, 'job')
return {id, name, age, birthday, job}
}
}
</script>
- 示例toRefs
<template>
<h2>hook的使用示例</h2>
<h4>当前用户的id为: {{id}}</h4>
<h4>当前用户的name为: {{name}}</h4>
<h4>当前用户的age为: {{age}}</h4>
<h4>当前用户的birthday为: {{birthday}}</h4>
<h4>当前用户的job为: {{job}}</h4>
</template>
<script>
import {toRefs, reactive} from 'vue'
export default {
name: 'App',
setup() {
let person = reactive({id: '001', name: '张三', age: 15, birthday: '1111-10-21',job: '摸鱼'})
return { ...toRefs(person) }
}
}
</script>
其他Composition API(组合式API)
shallowReactive 与 shallowRef
-
shallowReactive的作用:用于对象数据,仅监视对象第一层的变化(浅层次),第二层及其以下的对象数据无法被监视到
-
shallowRef的作用:用于基本数据类型 或 对象类型。
- 当应用于基本数据类型时,就跟ref一样
- 当应用于对象类型时,该对象将成为一个普通的对象(没有响应式)
readonly 与 shallowReadonly
-
readonly:深只读,被该函数处理后的数据,无法被进行任何修改
-
shallowReadonly:浅只读,被该函数处理后的数据的第一层,无法被进行修改,但是第二层及其之后的层数都可以被修改
toRaw与markRaw(一个是需要普通对象,一个是让其永远失去响应式)
-
toRaw:
- 作用:将一个由
reactive
生成的响应式对象转为普通对象。 - 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
- 作用:将一个由
-
markRaw:
- 作用:标记一个对象,使其永远不会再成为响应式对象。
- 应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类库等。
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
customRef自定义Ref
-
作用: 创建一个自定义的Ref,对其依赖项更新触发进行显示控制
-
customRef: 是一个函数,有2个参数,分别为track追踪函数,trigger告诉Vue渲染模板函数
-
使用示例:实现用户修改的数据在500毫秒后再重新渲染,实现防抖效果
<template>
<h2>customRef使用示例</h2>
<hr><hr><hr>
<h4>当前的信息为: {{info}}</h4>
<h4>可以通过该输入框修改info信息:<input type="text" v-model="info"></h4>
</template>
<script>
import {customRef} from 'vue'
export default {
name: 'App',
setup() {
function myRef(value, delay) {
let timer
return customRef(((track, trigger) => {
return {
get(){
// 属性追踪
track()
return value
},
set(newValue){
// 实现防抖效果
clearTimeout(timer)
// 添加一个计时器
timer = setTimeout(() => {
value = newValue
// 告知Vue渲染模板
trigger()
},delay)
}
}
}))
}
let info = myRef('我是初始化信息', 500)
return { info }
}
}
</script>
provide 与 inject(祖先组件给其后代组件传递数据)
-
作用: 祖先组件给后代组件提供数据,后代组件可以直接获取到。值得注意的是:仅仅是存在于祖先组件给后代组件提供数据,或者父组件给后代组件传递数据。
- 兄弟组件无法通过这种方式获取数据
- 子组件提供的数据,父组件同样无法获取。
- 如果想要数据是响应式的,要么祖先先使其变成响应式再提供,要么后代组件自行处理。(注意:无论是哪种方式,都不会改变数据提供者的数据,亲测)
-
祖先组件
<template>
<Student></Student>
<Person></Person>
</template>
<script>
import Student from "@/components/Student";
import Person from "@/components/Person";
import {provide} from 'vue'
export default {
name: 'App',
components: {
Student,Person
},
setup() {
provide('appData', '这是App组件提供的一条数据')
}
}
</script>
- 孙子组件
<template>
<h2>我是一个小学生,位于第三层</h2>
<h4>我是小学生,这是祖先传递过来的数据: {{appData}}</h4>
<h4>我是小学生,这是Student正常大小学生传递过来的数据: {{studentData}}</h4>
<button @click="appData = appData += '!!'">点我修改appData</button>
</template>
<script>
import {ref, inject} from 'vue'
export default {
name: "SmallStudent",
setup() {
// 接收祖传递过来的数据,若要实现响应式,还得用上ref处理下
let appData = ref(inject('appData'))
let studentData = inject('studentData')
return {appData, studentData}
}
}
</script>
响应式数据的判断(4个)
-
isRef 判断是否为一个RefImpl对象
-
isReactive 判断是否为一个由reactive()创建的对象
-
isReadOnly 判断是否为一个深只读对象
-
isProxy 判断一个对象是否为reactive() 或者 readonly() 创建的代理
新的组件
Fragment
-
在Vue2中: 组件必须有一个根标签
-
在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
-
好处: 减少标签层级, 减小内存占用(因此Vue3的template中不需要再嵌套一个div)
Teleport
-
Teleport
是一种能够将我们的组件html结构移动到指定位置的技术。 -
实例代码
<teleport to="移动位置">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<button @click="isShow = false">关闭弹窗</button>
</div>
</div>
</teleport>
Suspense异步组件
-
作用: 等待异步组件时渲染一些额外内容,让应用有更好的用户体验
-
使用步骤
(1)引入异步组件定义功能,使用异步组件来引入组件
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
(2)使用Suspense
包裹组件,并配置好default
与 fallback
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>加载中.....</h3>
</template>
</Suspense>
</div>
</template>