文章目录
- 1.简介
- 2.Vue的使用(Scirpt版本)
- 3.HelloWord小案例
- 4.Vue注意点
- 5.模板语法
- 6.MVVM模型
- 7.数据代理
- 8.el和data的两种写法
- 9.计算属性
- 10.监听属性
- 11.computed和watch的区别
- 12.绑定样式
- 13.数据监测原理
- 14.收集表单数据:
- 15.过滤器(Vue3.0已移除)
- 16.生命周期
- 17.组件
- 18.Vue脚手架
- 19ref属性
- 20props属性
- 21.mixin(混入)
- 22.插件(plug)
- 23.scoped样式
- 24.webStroage
- 25组件的自定义事件
- 26.全局事件总线
- 27.消息订阅与发布
- 28.$nextTick
- 29.Vue封装的过度与动画
- 30.Axios
- 31.插槽
- 32.Vuex
- 33.路由
- 34.UI组件库
- 35.Vue3
- 35.1创建Vue3项目
- 35.2main.js介绍
- 35.3setup函数
- 35.4ref函数
- 35.5reactive函数
- 35.6响应式原理
- 35.7reactive与ref的对比
- 35.8计算属性的使用
- 35.9watch函数
- 35.10watchEffect函数
- 35.11生命周期
- 35.12hook函数
- 35.13toRef
- 35.14shallReactive和shallowRef
- 35.15readonly和shallowReadonly
- 35.16toRaw和markRaw
- 35.17customRef
- 35.18provide和inject
- 35.19响应式数据的判断
- 35.20Fragment标签
- 35.21Teleport
- 35.22Suspense
- 35.23Vue3的其他调整
- 常用配置:
1.简介
作为国内使用较多的前端开发框架,vue是渐进式框架,能够是我们减去繁杂的代码工作,简易了前端开发。
2.Vue的使用(Scirpt版本)
- 1.下载vue.js,地址如下:
https://v2.cn.vuejs.org/v2/guide/installation.html#%E7%9B%B4%E6%8E%A5%E7%94%A8-lt-script-gt-%E5%BC%95%E5%85%A5
- 2.引入到html文件中
<script src="../js/vue.js" type="text/javascript"></script>
3.HelloWord小案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../js/vue.js" type="text/javascript"></script>
</head>
<body>
<div class="root">
<!--插值表达式-->
Hello,{{name}}
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el: ".root", //el代表的是当前这个Vue对象服务的元素,el的值可以通常用元素选择器,或者也可用bom获取,比如el: document.getElementById("#root")
data: { //data用于存储数据,用于el所指定的对象使用
name: "尚硅谷"
}
});
</script>
</body>
</html>
4.Vue注意点
- 一个标签元素只能绑定一个Vue对象
- 一个Vue对象只能绑定一个标签
- 插值表达式需要些Js语法,同时不能使用Vue对象中未定义的数据变量
- Vue对象中,可以定义两个相同名的变量,但是后者会把前者覆盖。
5.模板语法
5.1插值语法
语法:vue{{js表达式}}
用于解析标签体中的内容,括号内是JS表达式,可以直接绑定data中的所有内容。
5.2指令语法
5.2.1v-bind
单向数据绑定,用于绑定标签属性,同时可以简写如:v-bind:href="xxx"可以简写为:href=“xxx”.
5.2.2v-model
双向绑定,用于绑定标签中的value属性,因为绑定的是value属性,因此v-model支持表单中的标签,如input和selete。用于其他标签,如h1等,都会报错。语法如下:
<input v-model:value="data"/>
同时也有简写语法,个人表示,语法糖,yyds
<input v-model="data"/>
5.2.2.1v-mode的修饰符
- .lazy:失去焦点在收集数据
- .number:输入字符串转为有效的数字
- .trim:输入首位空格过滤
5.2.3v-on
事件绑定,绑定Vm对象中methods中的方法,由于可以直接用方法名调用,因此在vm对象中做了声明,所以在methods中定义的函数,可以直接用函数名调用。但是可以使用method或者method(arg1,arg2…)。只是用前者未传递参数,后者传递了参数。
- 简写:v-on:click -> @click
- 传递事件event对象:@click=“add($event)”
5.2.3.1事件修饰符
- 1.prevent:阻止默认事件
- 2.stop:阻止事件冒泡
- 3.once:事件只触发一次
- 4.capture:使用时间的捕获模式
- 5.self:只有event.target是当前操作的元素是才触发事件
- 6.passive:时间的默认行为,无需等待事件回调执行完毕。
注意:
- 可以同时使事件使用多次修饰符,@事件.修饰符1.修饰符2…,那个修饰符在前面,那个先被使用。
5.2.3.2键盘事件
- @keyup
键盘松开
- @keydown
键盘按下
注意:
- Vue中常用的按键别名(使用方法为:@keyup.别名)
- 回车 => enter
- 删除 => delete(捕获删除和退格键)
- 退出 => esc
- 空格 => space
- 换行 => tab
- 上 =
5.2.4v-show
用来隐藏当前样式,但是不会破坏BOM结构,也displace:none相同
5.2.5v-if,v-else-if,v-else
- 写法:
v-if="表达式"
v-else-if="表达式"
v-else="表达式"
不符号条件的DOM元素将直接被移除。同时v-if和v-else-if,v-else一起使用,但要求结构不能被打断。使用v-if的时,元素可能无法获得,而使用v-show一定能获得
5.2.5.1template
当我们想要用一个条件判断几个元素,通常的做法就是用div包裹。但是使用div破坏了BOM结构。因此template标签的出现解决了这个问题。template可以包裹元素,但是不会改变bom结构,就是不会增加template标签。但是template只能配合v-if使用
5.2.6v-for
用来遍历数据,可以遍历数组,对象,字符串以及次数。同时可以获取索引值
语法:
v-for="item in 数组/对象/字符串/次数"
v-for="(item,index) in 数组/对象/字符串/次数"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div class="root">
<!--遍历数组 i in forArr是只取出每一项的数据,(i, index) in forArr 是遍历每一项和索引值(从0开始)-->
<ul>
<li v-for="i in forArr">
{{i.name}}--{{i.age}}
</li>
</ul>
<!--遍历对象,value在前,key在后-->
<ul>
<li v-for="(value, key) in forObj">
{{value}} -- {{key}}
</li>
</ul>
<!--遍历字符串,同样也可以通过(i,index) in forStr 获取索引值-->
<ul>
<li v-for="i in forStr">{{i}}</li>
</ul>
<!--遍历次数-->
<ul>
<li v-for="item in forNum">{{item}}</li>
</ul>
</div>
</body>
<script>
new Vue({
el: ".root",
data() {
return {
forArr: [
{name: "mick", age:12},
{name: "anna", age:13}
],
forObj: {
name: "mick",
age: "18"
},
forStr: "abadfaf",
forNum: 3
}
}
})
</script>
</html>
5.2.6.1key的原理和介绍
- key的作用
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM,随后Vue进行新虚拟DOM与旧虚拟DOM的差异比较。比较规则如下:
- key选择index带来的问题
5.2.7v-text
将标签体中的内容替换为v-text绑定的属性,不能解析标签。
5.2.8v-html
将标签体中的内容替换为v-html绑定的属性,能解析标签。但是存在安全性问题,会产生XSS攻击
5.2.9v-cloak
- 本质上是一个特殊的语法,没有属性值,Vue接管容器后,会删除这个属性
- 可以配合css可以解决网速慢页面展示出{{xxx}}的问题
5.2.10v-once
- 所在节点在初次动态渲染后,就视为静态属性
- 以后数据的改变不会引起v-once所有结构的改变,可以用于优化性能
5.2.11v-pre
标记v-pre的元素标签不会受vue的编辑接管,即写出的是什么,表示的就是什么
5.2.12自定义标签
5.2.12.1自定义语法
5.2.12.1.1局部指令
new Vue({
directives:{指令名:配置对象}
})
或者
new Vue({
directives{指令名:回调函数}
})
5.2.12.1.2全局指令
Vue.directive(指令名,配置对象)
或者
Vue.directive(指令名,回调函数)
5.2.12.2常用的三种回调
- bind:指令与元素成功绑定时调用
- inserted:元素被插入页面时调用
- update:指令所在模板结构被重新解析时调用
5.2.12.3注意:
- 指令定义时不加v-,但是用要加v-
- 指令名如果是多个单词,要使用kebab-case,不要用camelCase命名
6.MVVM模型
- M:模型:data中的数据
- V:试图:DOM代码
- VM:视图模型,也就是Vue实例
通过定义到data中的所有属性,最后都出现在vm上了,vm上的所有属性,都可以通过插值语法和模板语法使用。
7.数据代理
7.1Object.defineProperty方法介绍
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div class="box"></div>
<script>
let person = {
name: "mick",
sex: "man"
}
let num = 20;
Object.defineProperties(person, 'age', {
//给age赋值的值
value: 19,
//可枚举,默认值为false
enumerable: true,
//可修改,默认为false
writable: true,
//可删除,默认为false
configurable: true,
//当读取person的age时,get方法会被调用
get() {
return num;
},
//当修改person的age时,set方法会被调用
set(value) {
num = value;
}
})
new Vue({
el: ".box",
data() {
return {}
}
});
</script>
</body>
</html>
7.2定义
通过一个对象可以对另一个对象进行读/写
7.3Vue中的应用
Vue将我们定义的data对象存放在了vm的_data属性中,同时通过defineProperty方法来实现data对象与vm对象的数据绑定。而之所以将_data的属性有放到外面一份,是为了操作方便。不至于每次开发人员都要用{{_data.属性}}来获取属性。
8.el和data的两种写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div class="div">
你好,{{name}}
</div>
<script>
const v = new Vue({
//el的第一种写法,在vue对象中直接定义
el: ".div",
// data的第一种写法,对象式,以后使用组件会报错。
data: {
name: "世界"
},
//data的第二种写法,函数式
data: function() {
return {
name: "世界"
}
},
//当然也可以用es6的语法糖做一个简化
data() {
return {
name: "世界"
}
}
});
//el的第二种写法,使用$mount挂载,不一样的是,这种方法比较方法,可以实现懒加载
v.$mount(".div");
</script>
</body>
</html>
9.计算属性
9.1定义
定义在computed中,是通过现有属性计算获得的属性(data中的数据),底层调用了Object.defineproperty方法提供的get和set方法
9.2.get方法的执行时机
- 在第一读取计算属性的时候,会调用get方法
- 当计算属性依赖的属性发生变化时,会调用get方法
9.3注意
- 计算属性会出现在vm上,直接读取即可
- 如果计算属性要被修改,那必须定义set函数,否则将会报错
9.4简写
computed: {
//属性名
fullName() {
//get方法的函数体
return this.firstName + "-" + this.secondName;
}
}
10.监听属性
定义在watch中的属性,这些属性会监听属性的变化,然后调用回调函数(handle),实现数据的监听
10.1语法
watch: {
//属性名,默认是“属性名”,但是如果只是监听属性,而不是data中对象的属性,则不需要添加引号
flag: {
//是否立即执行,即加载页面的时候就执行,false代表只有flag发生变化的时候调用
immediate: true,
//默认watch是不能够监听data中对象的属性(增加效率),使用deep可以监听
deep: true,
//属性发生变化时,执行的函数。
handler(newValue, oldValue) {
console.log("newValue", newValue, "oldValue", oldValue);
}
}
}
当然Vue也给我们提供了另一种方式来定义watch
vm.$watch(”监听属性", 回调函数);
10.2简写
如果不用定义watch的及时性和深度监听,我们可以将上述代码简写如下
watch: {
flag(newValue, oldValue) {
console.log(newValue);
console.log(oldValue);
}
}
11.computed和watch的区别
- computed能完成的,watch都能完成,watch能完成的,computed不一定能完成,比如异步任务
- 所有被Vue管理的函数都写成普通函数,不被Vue管理的函数,如:ajax的回调函数,Promise的回调函数,定时器的回调函数,都写成箭头函数
12.绑定样式
12.1绑定class样式
可以利用:class属性绑定data中的变量,变量的形式可以为字符串,数组,对象。:class绑定的class样式,vue会将此数据与原来class样式绑定。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="../js/vue.js"></script>
<style type="text/css">
.a {
background-color: yellowgreen;
}
.b {
border: 1px solid black;
}
.c {
color: red;
}
.d {
font-size: 10px;
}
</style>
</head>
<body>
<div class="root">
<!--样式绑定为属性,用来绑定一个class样式,且样式未知-->
<div class="d" :class="classAttr">测试</div>
<button @click="click1">点击1</button>
<br/>
<br/>
<!--样式绑定为数组,用来绑定多个class样式,且样式未知-->
<div class="d" :class="classArr">测试</div>
<button @click="click2">点击2</button>
<br/>
<br/>
<!--样式绑定为对象,用来绑定范围确定的样式-->
<div class="d" :class="classObj">测试</div>
<button @click="click3">点击3</button>
</div>
</body>
<script>
new Vue({
el: ".root",
data() {
return {
classAttr: "",
classArr: [],
classObj: {
"a": false,
"b": false
}
}
},
methods: {
click1() {
this.classAttr = "a"
},
click2() {
this.classArr = ["a", "b", "c"];
},
click3() {
this.classObj.a = true;
}
}
})
</script>
</html>
12.2绑定style样式
Vue提供了两种解决行内样式的方式,样式对象和数组,通常使用样式对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div class="root">
<!--样式对象,注意间隔线要用下划线表示-->
<span :style="styleObj">测试1</span>
<!--数组形式,数组捏包含的是样式对象,不常见-->
<span :style="styleArr">测试2</span>
</div>
</body>
<script>
new Vue({
el: ".root",
data() {
return {
styleObj: {
fontSize: "50px",
color: "red"
},
styleArr: [
{
fontSize: "50px"
},
{
color: "red"
}
]
}
}
})
</script>
</html>
13.数据监测原理
13.1Vue.set()和vm.$set()
当我们想要在现有数据(也就是data中的数据,不能是data)中新增一个属性,可以使用set方法,具体的使用方式有两种,如下:
语法:
Vue.set(data中的数据,属性名,属性值)
vm.$set(data中的数据,属性名,属性值)
13.2如何检测数组中的数据
在Vue中,vm对push、pop()、shift()、unshift()、splice()、sort()、reverse()进行了封装,只要使用了这7个方法,Vue都会更新数组,当然也可以用ser方法来改变数组元素,但是set方法不能给vm或者vm的跟对象添加属性。。
14.收集表单数据:
- 1.如果表单类型为非text类型,如radio,checkbox,select那么需用自己定义value
- 2.checkbox为多选时,绑定的为数组,单个checkbox,绑定的为boolean,并且不需要定义value
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div class="root">
<form action="www.baidu.com" @submit.prevent="save">
<label>
<span>账号:</span>
<input type="text" v-model="info.account">
</label>
<br/>
<label>
<span>密码:</span>
<input type="password" v-model="info.password">
</label>
<br/>
<span>性别:</span>
<label>
<span>男</span>
<input type="radio" v-model="info.sex" value="man">
</label>
<label>
<span>女</span>
<input type="radio" v-model="info.sex" value="woman">
</label>
<br/>
<span>爱好:</span>
<label>
<span>抽烟</span>
<input type="checkbox" v-model="info.hobby" value="smoke">
</label>
<label>
<span>喝酒</span>
<input type="checkbox" v-model="info.hobby" value="drink">
</label>
<label>
<span>烫头</span>
<input type="checkbox" v-model="info.hobby" value="head">
</label>
<br/>
<label>
<span>所属校区:</span>
<select v-model="info.school">
<option>请选择校区</option>
<option value="bj">北京</option>
<option value="sh">上海</option>
<option value="nj">南京</option>
</select>
</label>
<br/>
<label>
<span>其他信息:</span>
<textarea v-model="info.other"></textarea>
</label>
<br/>
<label>
<input type="checkbox" v-model="info.accept">
<span>
阅读并接受
<a href="www.baidu.com">《用户协议》</a>
</span>
</label>
<br/>
<button type="submit">提交</button>
</form>
</div>
</body>
<script>
new Vue({
el: ".root",
data() {
return {
info: {
account: "",
password: "",
sex: "",
hobby: [],
school: "",
other: "",
accept: false
}
}
},
methods: {
save() {
console.log("信息", this.info)
}
}
})
</script>
</html>
15.过滤器(Vue3.0已移除)
15.1语法
<h1>{{time | 过滤属性}}</h1>
new Vue({
filters: {
过滤属性() {
}
}
}
16.生命周期
16.1定义
一个组件从创建->更新->销毁的阶段,而在此期间,Vue给我们提供了一些函数,这些函数会在生命周期特定的时间段被VUE调用。而这些函数被称为钩子函数。
16.2介绍
17.组件
17.1定义
用来实现局部功能效果的代码集合。
17.2多文件组件的使用
组件的使用过程分为三个步骤:创建,注册,使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div class="root">
<!--第三步:组件使用-->
<school></school>
<br/>
<student></student>
</div>
</body>
<script>
//第一步:组件创建
const school = Vue.extend({
template: `
<div>
<h2>学校名字:{{schoolName}}</h2>
<h2>学校地址:{{schoolAddr}}</h2>
</div>`,
data() {
return {
schoolName: "尚硅谷",
schoolAddr: "北京"
}
}
});
const student = Vue.extend({
template: `
<div>
<h2>学生名字:{{studentName}}</h2>
<h2>学生地址:{{studentAddr}}</h2>
</div>`,
data() {
return {
studentName: "Mick",
studentAddr: "上海"
}
}
});
new Vue({
el: ".root",
data() {
return {}
},
//第二步:组件注册,这种方式即变量名即为组件名,还有一种写法是:student:student
components: {
student,
school
}
})
</script>
</html>
17.2.1全局注册
//全局注册
Vue.component({
template: `
<div>
<h2>学生名字:{{studentName}}</h2>
<h2>学生地址:{{studentAddr}}</h2>
</div>`,
data() {
return {
studentName: "Mick",
studentAddr: "上海"
}
}
})
注意
- 在组件的使用过程中,data不可以使用变量的形式。因为如果使用变量的形式,多个组件实际是直接操作的是同一个变量,一个组件修改了,就会影响另一个组件。
- 可以在组件中定义一个属性name,这个属性对应的值,可以影响开发者工具中显示的标签。
17.2.3简写
const student = {
template: `
<div>
<h2>学生名字:{{studentName}}</h2>
<h2>学生地址:{{studentAddr}}</h2>
</div>`,
data() {
return {
studentName: "Mick",
studentAddr: "上海"
}
}
};
17.2.4VueCompoent
- 组件本质是一个VueComponent,当执行Vue.extend后,由Vue创建的
- 每次调用一次,返回的都是全新的VueComponent
- 组件中的this是VueComponent
- VueComponent简称vm
17.2.5内置关系
VueComponent.prototype.__proto__ === Vue.prototype
17.3单文件组件的使用
Vue将单文件组件分为了三个部分,分别为template,script,style
<template>
DOM结构
</template>
<script>
以默认方式暴露当前文件,可使用import引入
export default {
vue中的方法,比如data,methods,component,watch,computeds,钩子函数等
}
</script>
<style scoped>
样式
</style>
注意
- 用import引入组件
import 组件名 from 相对地址
- 组件名通常首字母大写
18.Vue脚手架
18.1创建脚手架
- 1.首先在命令中输入vue命令,查看vue是否安装,如果没有安装,安装vue
- 2.创建vue
vue create 项目名
18.2脚手架结构
.文件目录
├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ └── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json: 应用包版本控制文件
└── vue.config.js:定制化脚手架的一些功能,比如入口文件的路径等
18.2.1入口文件介绍(main.js)
//引入APP
import Vue from 'vue'
import App from './App.vue'
//关闭开发提示
Vue.config.productionTip = false
//创建实例
new Vue({
//由于Vue在脚手架中引入的并不是完整版的Vue.js,而是vue.runtime.js,虽然较少了vue.js的体积大小,
// 但是在这个版本中没有模板解析器,所以Vue官方提供了render函数,由render函数接收到createElement函数去指定具体内容。
render: h => h(App),
}).$mount('#app') //将vue实例挂载到app元素上
19ref属性
用来给元素或子组件注册引用信息,也就是id的替代者,可以获取组件实例对象(vc)
使用方式
this.$refs.值
20props属性
20.1定义:
props主要用于组件的传值,他的工作就是为了接收外面传过来的数据,与data、methods、computed是一个级别的配置项。
20.2语法:
- 定义props:
props: {
属性名:{
type: 属性类型,
required: 是否必须,
default: 默认值
}
}
例子:
props: {
name: {
type: String,
required: true
},
age: {
type: Number,
required: true
},
sex: {
type: String,
required: true
}
}
- 定义组件
<person-name sex="男" :age="18" name="Mick"/>
20.3定义简写
props: ['属性名'...]
注意:
- props中的属性是不能修改的,如果想要修改,可以在data中创建一个中间变量,将props中的变量赋值给中间变量,通过修改中间变量修改数据,
<template>
<div>
<h1>{{msg}}</h1>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{tempAge}}</h2>
<h2>性别:{{sex}}</h2>
<button @click="changeAge">按钮</button>
</div>
</template>
<script>
export default {
name: "PersonName",
data() {
return {
msg: "欢迎学习尚硅谷!",
tempAge: this.age
}
},
props: {
name: {
type: String,
required: true
},
age: {
type: Number,
required: true
},
sex: {
type: String,
required: true
}
},
methods: {
changeAge() {
this.tempAge ++;
}
}
}
</script>
<style scoped>
</style>
21.mixin(混入)
21.1定义
混入是可以单独抽出重复配置,这些配置可以是Vue中的一些属性如:methods,并通过引入mixin的形式,解决重复配置的问题
21.2语法:
- 定义一个混入
//a代表的是混合的名
export const a = {
methods: {
sayHello() {
console.log("你好啊");
}
}
}
- 引入一个混入
<script>
import {a} from "@/mixins/sayHello";
export default {
name: "CustomerName",
props: {
name: {
type: String,
required: true
},
address: {
type: String,
required: true
}
},
mixins: [a]
}
</script>
22.插件(plug)
22.1定义:
用于增强Vue,包含install方法的一个对象,install的第一个参数是Vue,第二个及以后的函数是使用者传递的函数
22.2语法:
- 定义插件:
export default {
install(Vue) {
}
}
- 使用插件
Vue.use();
23.scoped样式
在单文件组件中写的样式最终会汇总到一起来,这时候就可能会造成class名重复,导致后引入的样式覆盖原来的样式,如何解决这个问题呢?引出了scoped
23.1作用
让样式在局部内生效
23.2语法
<style scoped>
</style>
24.webStroage
存储器大小一般支持5MB左右(不同浏览器还不一样),分为loclaStorage和sessionStorage
24.1localStorage
浏览器关闭,依然存在,保存在本地磁盘上,用户清空缓存,就会消失。
- 增加
localStorage.setItem("name", "anna");
- 查询
localStorage.getItem("name")
- 删除
localStorage.removeItem("name");
- 清空
localStorage.clear();
24.2sessionStorage
浏览器关闭,存储的信息就会丢失。
- 增加
sessionStorage.setItem("name", "anna");
- 查询
sessionStorage.getItem("name")
- 删除
sessionStorage.removeItem("name");
- 清空
sessionStorage.clear();
25组件的自定义事件
一种组件间通信的方式,适用于:子组件=>父组件,需要提前准备父组件的回调函数,函数的声明放在子组件中。
25.1绑定自定义事件
- 1.在父组件中使用
@事件名="回调函数"
或者v-on:事件名
- 2.在父组件中使用
this.$refs.refName.$on("事件名'', 回调函数)
注意:使用这种方式,回调函数中的this为子组件的vm
- 3.子组件使用this.$emit(自定义事件名,参数0
25.2组件中绑定原生组件
@事件名.native
25.3解除绑定
this.$off("事件名")
26.全局事件总线
一种组件间通信的方式,适用于任意组件间的通信
26.1安装全局事件总线
new Vue() {
.....
beforeCreate() {
Vue.$propertype.$bus = this;
}
}
26.2使用
1.接受数据
methods() {
demo(data) {
.....
}
}
this.$bus.$on('xxxx', this.demo)
2.发送数据
this.$bus.$emit('xxxx', 数据)
注意
最好在beforeDestory中,用 o f f 去解绑当前组件所有的事件,因为如果 off去解绑当前组件所有的事件,因为如果 off去解绑当前组件所有的事件,因为如果bus不会被销毁
27.消息订阅与发布
一种组件间通信的方式,适用于任意组件间通信
27.1使用步骤
- 1.安装:pubsub:npm i pubsub-js
- 2.引入:import pubsub from ‘pubsub-js’
- 3.接受数据:
methods() {
dome(data) {
....
}
}
mounted() {
this.pid = pubsub.subscribe('xxx',this.demo);
}
- 4.提供数据:```pubsub.publish(‘xxx’,数据)
- 5.在beforeDestory中取消订阅:
pubsub.unsubscribe(this.pid)
28.$nextTick
当数据渲染之后,执行此API内的回调函数,用于一些特殊场合,比如,当我们想要在数据渲染后获取焦点,而不是数据渲染前执行,示例代码如下
changeName(id, event) {
let name = event.target.value;
if (!name.trim()) {
return alert("修改不能为空");
}
this.$bus.$emit("changeName", id, name);
this.$nextTick(function() {
this.$refs.inputTitle.focus();
})
}
29.Vue封装的过度与动画
再插入、更新或移除DOM元素时,再合适的时候给元素添加样式元素
29.1基本语法
- 元素进入时的样式
- v-enter:进入的起点
- v-enter-active:进入的过程中
- v-enter-to:进入的起点
- 元素离开时的样式
- v-leave:离开时的起点
- v-leave-active:离开过程中
- v-leave-to:离开的终点
注意
- 使用时,要用transition包裹使用动画的元素
- 如果多个元素需要过度,则需要使用transition-group,且每个元素要制定key值
30.Axios
30.1使用
- 安装
npm install axios;
- 引入
import axios from 'axios'
- 使用
axios("url",回调函数)
30.2解决跨域
30.2.1单个地址
- vue.config.js配置代理地址:
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
proxy: 'http://localhost:5000'
}
})
- 请求地址改为项目启动地址:
axios.get("http://localhost:8080/students", (data) => {
console.log(data);
})
30.2.2多个地址
devServer: {
proxy: {
'/api1': { //url匹配规则
target: 'http://localhost:5000', //转发地址
pathRewrite: {'^/api1' : ''}, //重写规则,第一个参数写正则表达式
ws: true, //是否支持webStock请求
changeOrigin: true //是否显示正确地址
},
'/api2': {
target: 'http://localhost:5001',
ws: true,
pathRewrite: {'^/api2' : ''},
changeOrigin: true
}
}
}
31.插槽
父组件向子组件传递带数据的标签,当一个组件有不确定的结构时, 就需要使用slot 技术,注意:插槽内容是在父组件中编译后, 再传递给子组件的。
31.1分类
31.1.1默认插槽
31.1.2具名插槽
31.1.3作用域插槽
数据在组件的自身,但根据数据生成的结构需要组建的使用者决定
32.Vuex
32.1概念
在Vue中实现集中式管理的一个Vue插件,对Vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件。
32.2原理图
- state:Vuex使用单一状态树,我的理解是就是单一的数据源,类似于java中的单例模式,在vue中是全局唯一的。
如何使用state:每一个compoent中都会有一个
$store
对象,stroe对象中含有state对象,可以通过this.$store.state.状态名
访问Vuex状态对象。
- getter:类似于computed,我们如果想要从state中根据属性派生出来一些属性,就需要用到getter
const getters = { bigSum(State) { return state.sum * 10; } } export default new Vue.store( getters )
- 组件中读取getters
$store.getters.bigSum
- mutations:更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,类似于事件,需要注意的是,mutation的名称建议为大写,并且采用
_
作为分隔符,并且第一个参数为state,且mutations中必须使用同步方法,因为mutations作为devtool监控的入口,如果采用异步方法,devtool就无法准确的监控对象的变化,语法如下:
mutations: {
SET_TOKEN: (state, token) => {
state.token = token
}
}
- action:Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。语法如下:
actions: {
Login({ commit }, userInfo) {
const username = userInfo.username.trim()
return new Promise((resolve, reject) => {
login(username, userInfo.password).then(response => {
const data = response.result
console.log(data)
setToken(data.token)
commit('SET_TOKEN', data.token)
resolve()
}).catch(error => {
reject(error)
})
})
}
}
- module: 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
32.3搭建Vuex环境
- 创建store/index.js文件
import Vuex from "vuex"
import Vue from 'vue'
Vue.use(Vuex)
const actions = {};
const mutations = {};
const state = {};
export default new Vuex.Store({
actions,
mutations,
state
});
- 在main.js引入store
import Vue from 'vue'
import App from './App.vue'
import store from "./store"
Vue.config.productionTip = false
const vm = new Vue({
render: h => h(App),
store
}).$mount('#app');
注意:
不能再main.js中引入Vuex
32.4.mapState和mapGetter(放在计算属性中)
由于在组件中获取状态对象需要使用
this.$store.state.状态名
的方法,当我们在组件中需要获取多个state对象时,就会显得代码很繁杂,所以vue官方引出了mapState和mapGetter,并结合对象解析符号...
,可以轻松的获取装填对象。
32.4.1使用
- 引入
import {mapState, mapGetters} from "vuex";
- 定义
computed:
...mapState(["sum"]),
...mapGetters(["bigSum"])
}
- 两种写法
对象写法:
...mapState({
sum : "sum"
}),
...mapGetters({
bigSum : "bigSum"
}),
数组写法:
...mapState(["sum"]),
...mapGetters(["bigSum"])
32.5.mapActions和mapMutations(放在methos中)
32.5.1使用
- 引入
import {mapState, mapGetters, mapActions, mapMutations} from "vuex";
- 使用
methods: {
...mapActions({
incrementWait: "incrementWait",
incrementOdd: "incrementOdd",
}),
...mapMutations({
INCREMENT: "INCREMENT",
DECREMENT: "DECREMENT"
})
}
- 两种写法
对象写法
...mapActions({
incrementWait: "incrementWait",
incrementOdd: "incrementOdd",
}),
...mapMutations({
INCREMENT: "INCREMENT",
DECREMENT: "DECREMENT"
})
数组写法
...mapActions(["incrementWait", "incrementOdd"]),
...mapMutations(["INCREMENT", "DECREMENT"])
32.6模块化编码+ 命名空间
33.路由
路由就是一组映射关系(key-value),key为路径,value可能为function和component
33.1分类
- 后端路由(了解)
value是function,用于处理客户端提交的请求,指的是服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据。
- 前端路由
value就是component,用于展示页面内容,当浏览器的类路径改变时,对应的组件就会显示。
33.2使用
- 1.下载插件
npm install vue-router@3
- 2.脚手架使用
main.js
import Vue from 'vue'
import App from './App.vue'
import router from "@/router";
import VueRouter from "vue-router";
Vue.use(VueRouter);
Vue.config.productionTip = false
new Vue({
render: h => h(App),
router
}).$mount('#app')
App.vue
<template>
<div id="app">
<div>
<div class="row">
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header"><h2>Vue Router Demo</h2></div>
</div>
</div>
<div class="row">
<div class="col-xs-2 col-xs-offset-2">
<div class="list-group">
<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
</div>
</div>
<div class="col-xs-6">
<div class="panel">
<div class="panel-body">
<router-view/>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
</style>
- 3.在router目录下创建index.js中注册路由
import VueRouter from "vue-router";
import About from "@/components/About.vue";
import Home from "@/components/Home.vue";
export default new VueRouter({
routes: [{
path: "/about",
component: About
},{
path: "/home",
component: Home
}]
})
33.3注意点
- 1.路由组件通常存放在pages文件夹,一般组件通常放在component文件夹
- 2.通过切换,“隐藏“了的路由组件,默认是被销毁的,需要的时候再被挂载
- 3.每个组件都有自己的$route属性,里面存储着自己的路由信息
- 4.整个应用只有一个router,可以通过组件的$router属性获取
33.4多级路由
- 1.配置路由规则
import VueRouter from "vue-router";
import About from "@/pages/About.vue";
import Home from "@/pages/Home.vue";
import HomeMsg from "@/pages/HomeMsg.vue";
import HomeNew from "@/pages/HomeNew.vue";
export default new VueRouter({
routes: [{
path: "/about",
component: About
},{
path: "/home",
component: Home,
children: [
{
path: "homeMsg", //不要加‘/’
component: HomeMsg
},
{
path: "homeNew",
component: HomeNew
}
]
}]
})
注意:使用的时候要把路径补全
<router-link to="/home/homeMsg" class="list-group-item">Message</router-link>
33.5路由传递参数
33.5.1query方式
- 路径方式
<template>
<div>
<ul>
<li v-for="item in msgList" :key="item.id">
<!-- query形式 -->
<router-link :to="`/home/homeMsg/details?id=${item.id}&title=${item.title}`"> {{item.title}}{{item.id}}</router-link>
</li>
</ul>
<router-view/>
</div>
</template>
<script>
export default {
name: "HomeMsg",
data() {
return {
msgList: [
{id: "001", title: "消息"},
{id: "002", title: "消息"},
{id: "003", title: "消息"}
]
}
}
}
</script>
<style scoped>
</style>
注意
- 与get请求方式相同
- to必须使用v-bind绑定,并配合${}使用
- 对象方式
<template>
<div>
<ul>
<li v-for="item in msgList" :key="item.id">
<!-- 对象形式 -->
<router-link :to="{
<!-- 请求路径 -->
path: '/home/homeMsg/details',
<!-- 请求参数 -->
query: {
id: item.id,
title: item.title
},
<!-- 路由名,如果太长可以用路由名代替路径 -->
name: 'name’
}">{{item.title}}{{item.id}}</router-link>
</li>
</ul>
<router-view/>
</div>
</template>
<script>
export default {
name: "HomeMsg",
data() {
return {
msgList: [
{id: "001", title: "消息"},
{id: "002", title: "消息"},
{id: "003", title: "消息"}
]
}
}
}
</script>
<style scoped>
</style>
- 使用
<template>
<div>
<ul>
<li>
消息编号:{{$route.query.id}}
</li>
<li>
消息标题:{{$route.query.title}}
</li>
</ul>
</div>
</template>
33.5.2params方式(不能使用path,只能用name.另外,一定要再路由中配置占位符(:id,:title))
- 路径方式
<template>
<div>
<ul>
<li v-for="item in msgList" :key="item.id">
<!-- 对象形式 -->
<router-link :to="`/home/homeMsg/details/${item.id}/${item.title}`">
{{item.title}}{{item.id}}
</router-link>
</li>
</ul>
<router-view/>
</div>
</template>
<script>
export default {
name: "HomeMsg",
data() {
return {
msgList: [
{id: "001", title: "消息"},
{id: "002", title: "消息"},
{id: "003", title: "消息"}
]
}
}
}
</script>
<style scoped>
</style>
{
path: "homeMsg", //不要加‘/’
component: HomeMsg,
children: [
{
path: "details/:id/:title", //:代表占位
component: MsgDetails
}
]
}
- 参数方式
<template>
<div>
<ul>
<li v-for="item in msgList" :key="item.id">
<!-- 对象形式 -->
<router-link :to="{
name: 'details',
params: {
id: item.id,
title: item.title
}
}">
{{item.title}}{{item.id}}
</router-link>
</li>
</ul>
<router-view/>
</div>
</template>
import VueRouter from "vue-router";
import About from "@/pages/About.vue";
import Home from "@/pages/Home.vue";
import HomeMsg from "@/pages/HomeMsg.vue";
import HomeNew from "@/pages/HomeNew.vue";
import MsgDetails from "@/pages/MsgDetails.vue";
export default new VueRouter({
routes: [{
path: "/about",
component: About
}, {
path: "/home",
component: Home,
children: [
{
path: "homeMsg", //不要加‘/’
component: HomeMsg,
children: [
{
name: "details",
path: "details",
component: MsgDetails
}
]
},
{
path: "homeNew",
component: HomeNew
}
]
}]
})
33.5props属性
让路由组件更方便的收到参数
33.6的repalce属性
控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史操作记录有两种方式,分别为push和replace,push是追加历史记录,replace是替换当前记录,路由跳转默认为push
- 如何开始
replace
模式:router-link replace .....>News</router-link>
33.7编程式路由导航
/**
* push跳转
*/
queryOfPush(item) {
this.$router.push({
name: 'details',
params: {
id: item.id,
title: item.title
}
})
},
/**
* replace跳转
*/
queryOfReplace(item) {
this.$router.replace({
name: 'details',
params: {
id: item.id,
title: item.title
}
})
}
33.6.1常用的一些方法
- 路由后退:
jsthis.$router.back();
- 路由前进:
jsthis.$router.forward();
- 路由指定前进几步:
jsthis.$router.go(指定步数);
33.7缓存路由组件
作用:让不展示的路由组件保持挂载,不被销毁
实现
<keep-alive include="组件名,name要与组件名相同,不然不行,我觉得这边可能是组件的name值">
<router-view/>
</keep-alive>
也可以使用组件::include="数组"
33.8路由的特有钩子
路由组件所独有的两个钩子,用于捕获路由组件的激活状态
- activated:路由组件被激活时触发
- deactivated:路由组件失活时触发
33.9路由守卫
/**
* 前置路由守卫
*/
vueRouter.beforeEach((to, from, next) => {
if (to.path.includes("about")) {
if (to.meta.isAuth) {
next();
}
} else {
next();
}
});
/**
* 后置路由守卫
*/
vueRouter.afterEach((to, from) => {
document.title = to.meta.title;
})
//独享路由守卫
beforeEnter(to, from, next) {
next();
}
//组件内守卫(进入)
beforeRouterEnter(to,from,next) {
}
//组件内守卫(离开)
beforeRouterLeave(to,from,next) {
}
33.10history模式和hash模式
- hash值就是#及其后面的内容,并且hash值不会包含在http请求中,即:hash值不会带给服务器。
- hash模式
- 地址中永远带着#,不美观
- 若以后地址通过第三方手机app分享,若app校验严格,则地址会标记为不合符
- 兼容性好
- history模式
- 地址干净,美观
- 兼容性比hash差
- 应用上线需要后端人员支持,解决404问题。
34.UI组件库
34.1常用的UI库
34.2引入elementui
import Vue from 'vue'
import App from './App.vue'
import router from "@/router";
import VueRouter from "vue-router";
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(VueRouter);
Vue.config.productionTip = false
Vue.use(ElementUI);
new Vue({
render: h => h(App),
router
}).$mount('#app')
35.Vue3
35.1创建Vue3项目
vue create <project-name>
npm init vite-app <project-name>
注意点:
- 如果是用vite创建的项目,是不会自动下载依赖,所以需要手动下载一下依赖
35.2main.js介绍
//引入vm工厂
import { createApp } from 'vue'
import App from './App.vue'
//createApp:vm工厂,用来生成vm实例,相比于Vue2中通过构造函数创建vm,使用工厂创建vm,减轻了vm的体积,轻量化
createApp(App).mount('#app')
35.3setup函数
- 组件中所有到的:数据、方法等等,均要配置在setup中
- setup的返回值:
- 若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用(重点)
- 若返回一个渲染函数,则可以自定义渲染内容。(了解)
注意点:
- 不要与Vue2昏庸
- setup不能是一个async函数
- setup是在beforeCreate之前执行一次,this是undefined
- setup有两个参数,props就是父组件传递给子组件的参数,context是上下文对象,包括
attrs(相当于this.$attrs),slots(相当于this.$slots),emit:(相当于this.$emit)
35.4ref函数
- 作用:定义一个响应式数据
- 语法:const xxx = ref(initValue),JS操作数据:xxx.value,模板中读取数据:不需要.value
注意点:
- 接受的数据可以是:基本类型,也可以是对象类型,基本类型响应式通过Object.defineProperty()函数的get和set实现的,对象类型的数据,内部使用了reactive函数
35.5reactive函数
- 作用:定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
- 语法:const 代理对象 = reactive(原对象),返回一个代理对象(Proxy的实例对象,简称proxy对象)
- reactive定义的响应式数据是深层次的
- 内部基于ES6的proxy实现,通过代理对象操作原对象内部数据进行操作
35.6响应式原理
- 实现过程:
- 通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等。
- 通过Reflect(反射):对源对象的属性进行操作
- 代码
new Proxy(data, {
get(target, prop) {
return Reflect.get(target, prop);
},
set(target, prop, value) {
return Reflect.set(target, prop, value);
},
deleteProperty(target, p) {
return Reflect.deleteProperty(target, p);
}
})
35.7reactive与ref的对比
- 从定义调度
ref是用来定义基础类型的,reactive使用来定义引用类型的,当然ref也可以定义引用类型,内部时会自动使用reactive的
- 从原理角度
ref是通过Object.defineProperty()的set、get方法实现的
reactive是通过Proxy实现的
- 从使用角度对比
ref定义的数据,操作的时候需要.value,而reactive定义的数据就不需要
35.8计算属性的使用
<script>
import {reactive, computed} from 'vue'
export default {
name: 'HelloWorld',
props: {
msg: String
},
setup() {
let person = reactive({
firstName: "tom",
lastName: "li"
});
//计算属性,简写
person.fullName = computed(() => {
return person.firstName + "-" + person.lastName;
});
//计算属性,全写
person.fullName2 = computed({
get() {
return person.firstName + "-" + person.lastName
},
set(value) {
const nameArr = value.split("-");
person.firstName = nameArr[0];
person.lastName = nameArr[1];
}
})
return {person}
}
}
</script>
35.9watch函数
- 与Vue2中watch的功能一致
注意点:
- 监视reactive定义的响应式数据时:oldValue无法正确获取,强制开启了深度监视(deep配置失效)
- 监视reactive定义的响应式数据的某个属性时,deep配置失效
35.10watchEffect函数
- watch的思路是:既要指明监视的属性,也要指明监视的回调
- watchEffect的套路是:不用知名监视哪个属性,坚实的回调中用到那个属性,那就监视那个属性
- watchEffect类似于computed,只不过computed注重计算的计算的结果,watch注重计算的过程
35.11生命周期
注意事项:
- vue3用beforeUnMounte和mouted代替了beforeDestory和Destory
- 如果要将生命周期钩子写到组合式API中,created和beforeCreate都又setup代替,需要在每个生命周期函数上加个on
35.12hook函数
- hook函数就是把setup中的Composition Api进行了封装(命名以use开头,例如:useMessage)
- 类似于vue2.x中的mixin
- 使用方法:
- 将setup中复用代码封装到一个js文件中,在vue文件中引用即可
35.13toRef
- 作用:创建一个ref对象,其value值指向另一个对象中的某个属性
- 语法:
const name = toRef(person, 'name')
- 应用:要将响应式对象中的某个属性单独提供给外部使用
- 扩展:toRefs与toRef功能一致,但可以批量创建多个ref对象,语法:```toRefs(person)``
35.14shallReactive和shallowRef
- shallowReactive:只处理最外层属性的响应式
- shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理
- 如果有一个对象数据,结构比较深,但变化只是做外层属性变化用shallowReactive
- 如果有个一对象数据,后续功能不会修改该属性就用shallowRef
35.15readonly和shallowReadonly
readonly是禁止对一个对象进行修改,shallowReadonly是禁止对一个对象的第一层进行修改
35.16toRaw和markRaw
toRaw是将一个ractive函数处理的变量失去reactive的处理,markRaw是标记一个对象,使其永远不会成为响应式对象
35.17customRef
- 作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制。
35.18provide和inject
- 作用:组件与后代组件间的通信方式,一般不用再父子组件上,父子组件上还是用props
- 语法:
provide('car', car);
let cat = inject('car')
35.19响应式数据的判断
- isRef:检查一个对象是否为ref对象
- isReactive:检查一个对象是否由reactive创建的响应式对象
- isReadonly:检查一个对象是否由readonly创建的只读代理
- isProxy:检查一个对象是否由reactive或者readonly创建的对象
35.20Fragment标签
在Vue2中,每个组件中必须有个一个根标签,而在vue3中,将由Fragment标签代替这个根标签,当然Fragment标签和template标签相似,都是个虚拟标签,不会产生真实的bom结构
35.21Teleport
- 作用:将标签可以瞬移到某个位置
- 代码
#当显示的时候,将h1标签放到body下
<teleport v-if='isShow' to='body'>
<h1>{{person.name}}</h1>
</telepor>
35.22Suspense
- 等待异步组件时渲染一些额外内容,让应用有更好的体验
35.23Vue3的其他调整
常用配置:
1.关闭esline检查
module.exports = defineConfig({
lintOnSave: false
})