什么是vue
vue是动态构建用户界面的渐进式javaScript框架 ,与其他框架不同的是,vue被设计为可以自低向上逐层的应用
怎么理解渐进式
vue使用起来比较灵活
在一个已有的项目中,可以单独使用vue.js本身
也可以使用vue以及vue的生态实现一些复杂的应用
vue的作者
尤雨溪开发的vue
vue全家桶
vue全家桶通常是指构建一个大型的vue项目必须掌握核心技能 , vue全家桶通常由以下核心技能组成
- Vue:渐进式的javaScript框架
- VueX:专门为vue.js应用程序开发的状态管理库
- Vue Router:专为vue.js应用程序开发的路由管理器
- Vue Cli : 专为Vue.js应用程序开发的脚手架工具
vue特征
- vue 是一个双向绑定的框架。
- react 是一个单向绑定的框架。 必须主动调用 setState方法。
vue实例对象和容器的关系
一个实力化对象只能对应一个容器
一个容器只能对应一个实例化对象
vue表达式 {{双花括号}}
数据类型
- 在表达式中显式的数据类型
- string
- number
- boolean
- object
- array
- 在表达式中不显示的数据类型
- undefined
- null
- 函数方法:方法的返回值必须是 string 、number、boolean、object 、array
- 不能写语句 如:
- if else
- switch
- 循环 for 、 while 、do..while 、 forin 、 forof、forEach
- try Catch
- 实例化对象vm上的属性,原型对象属性展示
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>3.vue表达式</title>
<!-- 引入Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="app">
<h1>{{ str }}</h1>
<h1>{{ num }}</h1>
<h1>{{ num + 10 }}</h1>
<h1>{{ Math.PI }}</h1>
<h1>{{ Infinity }}</h1>
<h1>{{ bol }}</h1>
<h1>{{ un }}</h1>
<h1>{{ nu }}</h1>
<h1>{{ obj }}</h1>
<h1>{{ arr }}</h1>
<h1>{{ str.slice(0,3) }}</h1>
<h1>{{ num > 10 ? "ok" : "no" }}</h1>
</div>
</body>
<script>
new Vue({
el:"#app",
data:{
str:"功名万里外,心事一杯中",
num:10,
bol:false,
un:undefined,
nu:null,
obj:{
uname:"张三",
age:18
},
arr:[1,2,3,4,5]
}
})
</script>
</html>
MVC
M Model 数据
V View 试图
C Controller 控制器
MVVM
M 模型 (数据)data
V 视图 view html+css 容器
VM 试图模型 【虚拟dom】vue实例
vue语法
- 1、插值语法:{{}}
- 作用:呈现标签体中的文本内容
- 2、指令语法:
- 概念:vue提供的一些 特定的属性
- 特点:v-
- 作用:呈现动态的标签的属性值或者标签的文本内容
el 和 data的几种形式
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>8.el和data的几种写法</title>
<!-- 引入Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="app">
<h1>{{str}}</h1>
</div>
</body>
<script>
// 1. el 第一种形式
new Vue({
el:"#app",
data:{
str:"学习vm"
}
})
// 2. el 第二种形式
new Vue({
el:document.getElementById('app'),
data:{
str:"学习vm"
}
})
// 3. el 第三种形式
let vm = new Vue({
data:{
str:"学习vm"
}
})
console.log(vm)
vm.$mount("#app");
//mount 挂载
// 4. data 的第一种形式 值为对象
new Vue({
el:"#app",
data:{
str:"学习vm"
}
})
// 5. data 的第二种形式 值为函数,函数必须是普通函数
new Vue({
el:"#app",
data(){
// console.log(this);
return {
str:'我是一个函数'
}
}
})
</script>
</html>
vue的配置项目
el 绑定静态页面
- 作用:
- 实例化对象绑定容器的方法
data 存储状态(数据)
- 作用:
- 存储状态 (数据)
- 特点:
- data中的数据都是响应式数据(数据的双向绑定)
methods 方法 (书写普通函数)
- 作用:
- 存放所有的普通方法
- 特点
- vue将methods中的所有的方法直接放到了vm身上 ,并没有走数据代理。
- methods中的方法必须是普通函数【this指向vm】,不能是箭头函数【箭头函数 this指向window】
- methods中的方法没有缓存,调用几次就执行几次
- 注意:
- 不能将普通方法放在data配置中,因为不需要走数据代理
computed 计算属性
- 计算属性的作用:
- 通过data中已经存在的数据 , 去计算出一个新的数据
- 计算属性的特点:
- 使用data中的一个或者多个数据 , 可以计算出一个新结果【多对一 / 一对一】
- computed 值为对象 。内部有一个get和set方法
- get :当有 人调用时,会自动执行get方法 ,且get方法的返回值就是计算属性的值
- set :当有人修改的时候 , 会自动调用set方法 ,会一个value参数表示是需要修改的值,在内部跟新对应的数据。
- computed值为函数 ,函数内部代码默认就是get内部的代码
- computed计算属性,会代理到vm身上
- computed计算属性,有缓存。支持缓存 ,只有当computed计算属性是计算发生改变,才会重新计算,否则走缓存
- computed计算属性,不支持异步 , 因为computed计算属性的值是需要返回返回值的,如果内部只写l异步的话,默认返回undefined ,返回后(return)代码不执行。
watch 监听属性
- watch监听的作用:
- 监听存在的状态数据。只能监听【data / computd】中存在的数据
- watch监听的特点:
- 监听一个数据得到一个多个结果【一对多 / 一对一】
- 不支持缓存 , 监听的数据发生变化就会立即执行
- 支持异步 , watch不需要返回值
- computed可以做的监听一定可以做 , 监听可以做的computed不一定可以做
- watch监听的配置项:
- handler : 监听的数据发生变化时, 自动调用
- immediate:true 立即监听
- deep:true 深度监听
vue组件中data为什么必须是一个函数
因为:组件是可以复用的,而当组件复用时,data如果是对象,那么复用组件中的data指向同一个地址,一个组件数据更改,其他组件数据也会更改,data是函数,return返回一个对象 , 就会开辟新的空间,不会影响到其他组件中的数据。
computed 计算属性和 watch监视的区别
- 1、watch监视已有的数据 , computed计算新的数据
- 2、watch没有缓存 , computed有缓存
- 3、watch一对多 , computed多对一
- 4、watch支持异步 , computed 不支持异步
vue 监听数据
原理 : Object.defineProperty( )
原因 :重新渲染试图
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>8.vue监视数据</title>
<!-- 引入Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="app">
<h1>今天天气怎么样? {{isHot}} </h1>
<button @click="change">切换</button>
<h1>{{str}}</h1>
<h1>{{userInfo.uname}}</h1>
<h1>{{userInfo.age}}</h1>
<button @click="userInfo.uname = '李四'">改名</button>
<button @click="userInfo.age++">长了一岁</button>
<button @click="userInfo = '123456'">改userInfo</button>
</div>
</body>
<script>
let vm = new Vue({
el:"#app",
data:{ // 状态
weather:false,
str:"",
userInfo:{
uname:"张三",
age:18
}
},
methods:{ //普通方法
change(){
this.weather = !this.weather;
}
},
computed:{ //计算属性
isHot(){
return this.weather ? "炎热" : "凉爽"
}
},
watch:{ //监听属性 监视属性
weather:{
immediate:true, //立即监听
handler(newValue,oldValue){ // 当监听的数据发生变化时,自动调用
console.log(" 当监听的数据发生变化时,自动调用");
// console.log(newValue,oldValue)
this.str = newValue ? "喝点冷饮" : "多喝热水";
}
},
/* "userInfo.uname":{
handler(){
alert("你咋又改名了!");
}
},
"userInfo.age":{
handler(){
alert("恭喜你成年了!");
}
}, */
userInfo:{
handler(){
alert("userInfo改了!");
},
deep:true //深度监视
},
weather(newValue){
//这段代码相当于 在 handler 写
this.str = newValue ? "喝点冷饮" : "多喝热水";
}
}
})
// console.log(vm)
/* vm.$watch("weather",{
handler(newValue){
// console.log(this);
this.str = newValue ? "喝点冷饮" : "多喝热水";
}
}); */
</script>
</html>
vue的指令
v-bind
- 作用:
- 动态解析标签属性值
- 简写形式:
- :
- 特点:
- 单向数据绑定,数据可以驱动试图。但是试图不能驱动数据
v-model
- 特点:
- 双向数据绑定,数据可以驱动试图 , 试图也可以驱动数据
- 注意点:
- v-model指令只能用在表单 , input 、select 、 textarea
- 本质:
- 默认渲染value属性值
- 简写形式
- <input type=”email” v-model=”str” >
- v-model的修饰符
- .lazy ——取代 input 监听 change 事件
- .number —— 输入字符串转为有效的数字
- .trim —— 输入首尾空格过滤
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>5.v-model</title>
<!-- 引入Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="app">
v-bind单向数据绑定:<input type="text" v-bind:value="str" > <br><br><br>
v-model 双向数据绑定: <input type="text" v-model:value="str"><br><br><br>
<!-- 错误代码 -->
<!-- <h1 a="123" b="456" v-model:c="str">{{ str }}</h1> -->
v-model简写形式: <input type="email" v-model="str" ><br><br>
v-model修饰符number: <input type="number" v-model.number="num" >
<h1>{{ 1 + num}}</h1>
v-model修饰符lazy: <input type="number" v-model.lazy.number="num" >
<h1>{{num}}</h1>
v-model修饰符trim: <input type="text" v-model.trim="num" >
<h1>{{num}}</h1>
</div>
</body>
<script>
new Vue({
el:"#app",
data:{
str:"学习vue2基础",
num:10
}
})
</script>
</html>
v-on
- 作用:
- 绑定事件
- 简写形式:
- @
- 事件修饰符
- 阻止冒泡:.stop 调用 event.stopPropagation()。
- 阻止默认行为
.prevent
– 调用event.preventDefault()
。 - 点击本身才触发(冒泡不触发)
.self
– 只当事件是从侦听器绑定的元素本身触发时才触发回调。 - 键盘事件修饰符
.{keyCode | keyAlias}
– 只当事件是从特定键触发时才触发回调。 - 只触发一次回调
.once
– 只触发一次回调 ,触发后解绑事件 .left
– (2.2.0) 只当点击鼠标左键时触发。.right
– (2.2.0) 只当点击鼠标右键时触发。.middle
– (2.2.0) 只当点击鼠标中键时触发。
v-for
数组循环 、对象循环、字符串循环、数字循环
v-show
- 作用:
- 条件渲染
- 值 : boolean
- 如果值为true 正常显式代码 。 如果为false ,会动态添加 display:none 代码隐藏
- 使用场景 :
- 频繁切换视图时,使用 v-show (用空间换取时间)
v-if
- 作用:
- 条件渲染
- 值 : boolean
- 如果值为true 正常显式代码 。 如果为 false 代码不加载 ,不显式,结构上也不存在
- 使用场景 :
- 不频繁切换视图时,使用 v-if (用时间换取空间)
v-else
- 作用:条件渲染
v-else-if
- 作用:条件渲染
v-text
- 作用:更新元素的 textcontent
v-html
- 作用:更新元素的 innerhtml 可以转换标签
自定义指令
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>12.自定义指令</title>
<!-- 引入Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="app">
<input type="text" v-focus /> <br><br>
<!-- <input type="text" v-focus /> <br><br> -->
</div>
</body>
<script>
// 指令名称 要求 : 不能带 v- , 必须全小写
// 自定义 全局 指令 directive("指令名称",{ 配置对象 })
Vue.directive("focus",{
inserted(el){ //为指令添加功能 el : 使用他的真实dom元素 (标签) , bindings 真实dom元素的上下文 ,带不带哪些东西
el.focus();
}
})
new Vue({
el:"#app",
data:{},
methods:{},
computed:{},
watch:{},
directives:{ //自定义局部指令
focus:{
inserted(el){
el.focus();
}
}
}
})
</script>
</html>
v-if 和 v-show
v-if 和 v-show都是条件渲染,
但是v-if如果值是 true 就会正常渲染到页面上,如果值是false 就不会执行代码,页面不会渲染,结构中也不会显式(时间换取空间)
但是v-show如果值是true 就会正常渲染到页面,如果值是false也会执行代码,只是不会渲染到页面,结构也会体现,会发现只是隐藏起来了,(空间换取时间)
如果频繁切换,需要使v-show 。如果不频繁切换,需要使用v-if
v-for和 v-if 优先级
在 同一个标签中 v-for的优先级 高于 v-if v-for和v-if不能同时出现在同一个标签上。先循环,在判断,效率低。 解决方法:利用计算属性,计算出新的结果,然后循环遍历新的结果。不需要再次判断.
Object.defineProperty
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let num = 10;
let obj = {
uname:'张三',
sex:"男"
}
// console.log(obj)
/* obj.age = 18;
console.log(obj) */
/* for (const key in obj) {
console.log(obj[key]);
}
*/
//为 对象添加 高度定制化的属性
Object.defineProperty(obj,"age",{
/* value:18 , // 默认值
configurable:true , // configurable 属性是否可删除,默认值为false
enumerable:true , // enumerable 属性是否参与枚举【遍历循环】,默认值为false
writable:true // writable 属性是否可修改,默认值为 false */
// 当有人 读取 age 的值时,会自动调用get方法,且get方法的返回值就是age 的值。
get(){
console.log("当有人 读取 age 的值时,会自动调用get方法,且get方法的返回值就是age 的值")
return num;
},
// 当有人 更新 age 的值时,会自动调用set方法,更新对应的数据
set(value){
console.log("当有人 更新 age 的值时,会自动调用set方法,更新对应的数据")
// console.log("@@@",value);
num = value;
}
});
console.log(obj)
/* for (const key in obj) {
console.log(obj[key]);
} */
</script>
</body>
</html>
数据代理
一个对象代理 对另一个对象属性 的操作(读取 / 更改)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//数据代理: 一个对象(obj1)代理对另外一个对象(obj2)属性(b)的操作【读/写】
let obj1 = {
a:1
}
let obj2 = {
b:2
}
Object.defineProperty(obj1,"b",{
get(){
return obj2.b
},
set(value){
obj2.b = value;
}
})
</script>
</body>
</html>
vue数据代理
优点 :简化程序员写代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>11.vue数据代理</title>
<!-- 引入Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="app">
<h1>{{_data.uname}}</h1>
<h1>{{_data.age}}</h1>
</div>
</body>
<script>
let vm = new Vue({
el:"#app",
data:{
uname:"张三",
age:18
}
})
//配置对象中的data === 实例对象vm _data;
console.log(vm);
// console.log(vm._data === data);
//数据代理: 一个对象(vm)代理对另外一个对象(_data)属性的操作【读/写】
/*
Object.defineProperty(vm,_data.uname,{
get(){
return _data.uname;
},
set(value){
_data.uname=value;
}
})
Object.defineProperty(vm,_data.age,{
get(){
return _data.age;
},
set(value){
_data.age=value;
}
}) */
</script>
</html>
vue 数据代理和数据劫持
数据代理
- 目的: 简化视图中程序员的代码。 _data.uname uname
- 原理: Object.defineProperty()
- 过程: 当 视图读取 数据时,会调用 vm 上的 proxyGetter(),进而读取 _data 中的数据。 当 视图更新 数据时,会调用 vm 上的 proxySetter(),进而更新 _data 中的数据。
- 数据代理就是将_data里面的数据代理到vm实例身上。vm代理了_data的数据 通过object.defineProperty()里面的get方法获取_data中是数据读取 , 通过object.defineProperty()里面的set方法更改_data中是数据 ,
数据劫持
- 目的: 监听_data 中的 数据的变化,然后重新渲染视图。
- 原理: Object.defineProperty()
- 过程: 当 读取 _data 数据时,会调用 _data 上的 reactiveGetter()读取到最终数据。 当 更新 _data 数据时,会调用 _data 上的 reactiveSetter()更新最终数据,然后重新渲染视图。
vue事件源
vue事件源 定义为:$event
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>2.vue事件源</title>
<!-- 引入Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="app">
<h1 v-on:click="getValue($event)">一切都将逝去,只有死神永生。</h1>
</div>
</body>
<script>
new Vue({
el:"#app",
methods:{
getValue(e){
alert(e.target.innerHTML);
}
}
})
</script>
</html>
vue 事件传参
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>3.vue事件传参</title>
<!-- 引入Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="app">
<button v-on:click="demo1(111111111111111)">点击1</button>
<button v-on:click="demo2(22222,'传递两个参数')">点击2</button>
<button v-on:click="demo3(str,num,$event)">点击3</button>
<h1 @click="demo4">{{str}}</h1>
</div>
</body>
<script>
new Vue({
el:"#app",
data:{
str:'学习vue事件传参',
num:10
},
methods:{
demo1(value){
alert(value);
},
demo2(a,b){
alert(a+b);
},
demo3(value,value1,e){
// console.log(e);
alert(value+"-----"+value1+"---"+e.target.innerHTML);
},
demo4(){
// console.log(this);
this.str = "这是更新后的数据!";
this.num = 1123;
}
}
})
</script>
</html>
diff算法
概念: diffing 比较算法
过程: 通过比较新旧虚拟dom,然后更新对应的dom结构,更加高效的更新dom(比较新旧虚拟Dom ,数据不同在渲染更新 ,相同就不渲染更新 , 实现高效更新dom)
key:虚拟dom唯一的标识符 。 index不严谨 , 一般使用id,
监视数组变化的7种
尾插:push() 尾删: pop() 头删:shift() 头插:unshift() 增删改: splice() 排序: sort() 翻转: reverse()
vue生命周期
什么是生命周期
实例【vm】/ 组件【vc】,从创建到销毁的过程 , 叫做生命周期钩子
在这个过程中,会自动调用一些函数,而这些函数叫做生命周期钩子函数
生命周期钩子主要学的有11个分为五大阶段
1、创建阶段 【beforeCreate】【created】
创建前 :beforeCreate :数据代理还未开始, 现在拿不到data数据和methods中方法
创建后:created : 完成了数据代理和数据劫持,可以拿到data,和methods中的方法
2、挂载阶段【beforeMount】【mounted】
挂载前:beforeMount: 解析模板 解析模板结束后调用 render 方法 准备生成虚拟dom vue生成了虚拟dom,但是还没有替换真实dom,没有渲染到页面
挂载后:mounted: 生成虚拟dom 并且将虚拟dom转换为真实dom 页面渲染以及完成 ,可以操作真实dom节点
3、页面更新阶段【beforeUpdate】【updated】
页面更新前:beforeUpdate: 数据发生变化 ,需要渲染页面(但还没有渲染页面) , 会生成虚拟dom , 新的虚拟dom 和 原来的 虚拟dom进行对比(这里使用dif算法)有改变直接更改真实dom 。这个过程叫做 render 数据更新成功,但是页面还没有更新
页面更新后:updated:数据更新成功,页面新数据渲染成功
4、销毁阶段【beforeDestroy】【destroyed】
销毁前:beforeDestroy: Vue实例进入销毁阶段 , 在此阶段做一些善后工作,如:解绑事件,清除定时器
销毁后:destroyed: 切断联系 销毁完成(组件已经全部销毁)
5、缓存生命周期【activated】【deactivated】
activated : 被keep-alive缓存的组件激活时调用
deactivated:被keep-alive缓存的组件失活时调用
6、父子组件中的生命周期: 每一个阶段完成的最后一步一定是父组件结尾。每个阶段完成前一步一定是先清空子组件该阶段的生命周期,然后父组件执行生命周期最后一步
生命周期常见问题:
- 哪个钩子中不能访问 data 数据 和 methods 方法? —- beforeCreate
- 最早在哪个钩子中可以给vm对象添加属性? —– beforeCreate
- 哪个钩子的data数据和页面是不同步的? — beforeUpdate
- data中的数据和methods中的方法最早可以在哪个钩子中获取? — created
- 最早可以在哪个钩子中发送ajax请求? —- created
关于销毁
- 销毁的是 vm 和视图的联系,并没有销毁掉vm实例对象。
- 数据data, methods方法都存在,只是数据不是响应式。
- 销毁了 自定义事件,并不会销毁js内置的事件。
vue 组件
组件是什么/什么是组件:具有特定功能效果的集合,里面封装了 html + css + js ,组件的复用。高内聚,低耦合。 组件以标签的形式使用
- 非单文件组件 .html
- 单文件组件 .vue
- 创建组件
- 注册组件
- 使用组件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>6.注册全局组件</title>
<!-- 引入Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="app"></div>
</body>
<script>
//先创建 hello 组件
const Hello = Vue.extend({
template:`<h1 style='color:pink'> hello world </h1>`
})
// 注册全局组件
Vue.component("Hello",Hello);
// console.dir(Vue);
//创建组件
const Student = Vue.extend({
template:`
<div>
<Hello></Hello>
<h1>{{uname}}</h1>
<h1 style='color:blue'>{{age}}</h1>
<h1>{{sex}}</h1>
<button @click="add">点击+1</button>
</div>
`,
data(){
return{
uname:"张三",
age:18,
sex:"男"
}
},
methods:{
add(){
console.log(this);
this.age++;
}
}
})
//创建书籍组件
const Book = Vue.extend({
template:`
<div>
<Hello></Hello>
<h1 style='color:red'>{{title}}</h1>
<h1>{{author}}</h1>
<h1>{{price}}</h1>
<button @click="add">点击增加价格</button>
</div>
`,
data(){
return {
title:"《vue从入门到放弃》",
author:"尤雨溪",
price:100
}
},
methods:{
add(){
console.log(this);
this.price += 100;
}
}
})
// 创建 App 根组件
const App = Vue.extend({
template:`
<div>
<Hello></Hello>
<Student></Student>
<Book></Book>
</div>`,
components:{
Student,
Book
}
})
//创建实例对象vm
new Vue({
el:"#app",
components:{
App
},
template:`<div><App/>
<Hello></Hello>
</div>`
})
</script>
</html>
vue组件和vue实例对象区别
- el 配置项只能使用在 vue实例,不能在 组件上。
- 实例对象 中的data 可以是对象/函数,组件中的data 必须是 函数,返回一个对象,函数存在作用域,当前组件的数据只能在当前组件使用。
- 在 非单文件组件文件中 ,组件的使用形式 最好是 <Student></Student>
- vue 实例 相当于 vue组件,vue实例拥有的配置项 vue组件基本也有。
- vue 组件和组件之间的作用域【data/methods/watch/computed/8个生命周期钩子】不互通。 vue 实例对象【vm】和组件之间作用域不互通。
- vue 组件最终的使用形式是标签。
vue组件特点
- vue 组件 的 本质是 一个名为 VueComponent() 的构造函数。
- VueComponent() 构造函数 是通过 Vue.extend() 返回的。且返回的每次都是独一无二的,唯一的VueComponent();
- 当 在 vue中 使用组件标签时,vue 会自动执行 new VueComponent();得到实例对象,这个实例对象就叫 组件对象【vc】
VueComponent 和 Vue
VueComponent.prototype.__proto__ === Vue.prototype
- vue实例对象vm
- (1) 导入 vue,得到一个Vue 构造函数
- (2) 实例化对象 new Vue();
- (3) 得到的对象就是实例对象 vm。
- vue组件对象 vc
- (1) Vue.extend() 创建组件,返回一个独一无二 VueComponent();
- (2) 当我们 使用 组件标签时<TodoList>,会自动 new VueComponent()得到一个组件对象。
- (3) 得到一个组件对象 vc
mixin 混入
把各个组件当中相同的代码进行封装
是一个对象 , 抽离 组件中的公共的逻辑部分。 可以写组件的配置项。
- mixin 混入 和 vm、vc 基本一致。
- vm 和 vc 所拥有的 属性【data/methods/computed/watch/八个生命周期钩子函数】 mixin也有
- mixin 混入的 data 数据 也会走代理和劫持。
- 组件中的 data/methods/computed/watch 优先级高于 mixin 混入的 data/methods/computed/watch。
- 组件会合并 mixin 的生命周期,组件和mixin的生命周期都会执行。且mixin 的生命周期优先级高于组件的生命周期。
//混入 mixin 抽离导航部分的数据及方法
export default {
data(){
return {
i:0,
str:"这是mixin中的数据"
}
},
methods:{
change(value){
this.i = value;
}
},
// mounted(){
// console.log("这是mixin中的 mounted");
// }
}
// 导入 mixin
import nav from "../mixins/nav"
export default {
name:"TopNav",
data(){
return{
arr:["首页","html","css","js","vue"], //数据
str:"这是top组件中的数据"
}
},
methods:{
change(){
console.log(this);
}
},
mixins:[nav], // 混入
}
vue过滤器
过滤数据:在不改变原数据的情况下过程数据
管道符 |
Vue.filter(“过滤器名称”,function(){});
//截取字符串长度
Vue.filter("sub",function(value,end=10){
return value.length > end ? value.slice(0,end)+"..." : value;
})
Vue.filter("atguigu",function(value,str="尚硅谷"){
return str+value;
})
// ·中使用
<span> {{ todo.title | sub(20) | atguigu }}</span>
axios
安装
npm i axios
在插件中配置 导入
//4. 为 Vue 添加一些全局的属性和方法
// Vue.prototype.a = "12345678";
Vue.prototype.$axios = axios;
vue配置代理服务器
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave:false, //关闭语法检查
devServer:{ //配置代理服务器
proxy:{
"/api":{ // 跨越api接口的前缀
target:"https://i.maoyan.com", // 目标服务器的域名,要请求的服务器
ws:true, // 支持 webscoket https
changeOrigin:true, //是否开始跨域。
pathRewrite:{"^/api":""} //重写api地址,去掉跨越前缀标识符。
}
}
}
})
// 更新配置文件,必须重启服务器
// 更新配置文件,必须重启服务器
// 更新配置文件,必须重启服务器
前端数据来源
- 前端表单收集
- 请求后台api
组件中的数据来源
- 自带数据
- 父组件传递的数据
vue组件间通讯
props 验证
- type 验证数据类型: String Number Boolean Array Object Function
- required : true 必传
- default :默认数据 (1) 默认数据值 为 String Number Boolean 正常写 (2)默认数据值 为Array Object , 值必须为函数,返回一个 Array Object 。
- validator : 自定义验证。
传递方法有三种 : 1、数组 2、对象简单写法 3、对象复杂写法
1、props :【‘data’】
2、props:{data :Number}
3、props:{
data:{
type:Number,// 类型
default : 0,// 默认值 (和必传项互斥)
required : true, //必传项 (和默认值互斥)
validator ( value ) { //自定义判断 返回 true为符合 , 返回false 为不符合
return value > 0 && value <= 100
}
}
}
<template>
<ul class="middle">
<!-- <h1>{{ num }}</h1> -->
<!-- <h1>{{ str }}</h1> -->
<!-- <h1>{{ arr }}</h1> -->
<li @click="change(index)" v-for="(item,index) in arr" :key="index">
<h3>{{ item.title }}</h3>
<h4 v-show="item.isShow" >{{ item.position }}</h4>
</li>
</ul>
</template>
<script>
export default {
name:"Middle",
// 1. 接受外部组件传递的数据
// props:["arr","change"],
// 2. 接受外部组件传递的数据
props:{
arr:{
type:Array, //验证数据类型
required:true // 必须
// default(){
// return [1,2,3,4];
// }
},
num:{
type:Number,
required:true,
// default:666666
validator(value){ //自定义验证
return value >= 18;
}
},
str:{
type:String,
// required:true,
default:"这是我的默认数据"
},
change:{
type:Function,
required:true
}
},
/* mounted(){
console.log(this);
} */
}
</script>
<style scoped>
.middle {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
}
.middle li{
width: 260px;
height: 200px;
border: 1px solid rgb(136, 210, 233);
margin: 20px;
text-align: center;
line-height: 100px;
cursor: pointer;
}
.middle li:hover{
background: gainsboro;
}
</style>
props 和 data 的 优先级
props 接受的外部数据 优先级 高于 组件内部的 data
计算属性的优先级最高。根据已有的数据计算出最终计算的结果。
props
props 接受父组件数据,接受到的数据是只读的,不能更新 在 数据源的组件中更新对应的数据.
ref
1、ref 获取真实dom对象
<template>
<div>
<input type="text" ref="input" />
<button @click="getValue">获取数据</button>
<div class="con" ref="con"></div>
</div>
</template>
<script>
export default {
name:"Ref",
data(){
return{
}
},
methods:{
getValue(){
// console.log(this);
// 获取数据
let value = this.$refs.input.value;
//展示数据
this.$refs.con.innerHTML = value;
//清空数据
this.$refs.input.value = "";
}
}
}
</script>
<style scoped>
div.con{
width: 200px;
height: 200px;
border: 1px solid red;
}
</style>
2、ref 获取组件对象
<template>
<div>
<input type="text" ref="input" />
<button @click="getValue">获取数据</button>
<div class="con" ref="con"></div>
<hr>
<One ref="one"/>
<button @click="getOne">点击获取one组件数据</button>
<button @click="updateOne">在ref中更新one数据</button>
</div>
</template>
<script>
import One from "../components/Ref/One"
export default {
name:"Ref",
data(){
return {
str:"这是【ref】组件中的数据"
}
},
methods:{
getValue(){
// console.log(this);
// 获取数据
let value = this.$refs.input.value;
//展示数据
this.$refs.con.innerHTML = value;
//清空数据
this.$refs.input.value = "";
},
getOne(){
console.log(this.$refs.one.str);
},
updateOne(){
// console.log(this.$refs.one);
this.$refs.one.changeOne(this.str);
}
},
components:{
One
},
/* mounted(){
console.log(this);
} */
}
</script>
<style scoped>
div.con{
width: 200px;
height: 200px;
border: 1px solid red;
}
button{
width: 200px;
height: 50px;
}
</style>
组件数据传递
props
- props 主要做父子之间数据通讯
- 如果传递的是非函数的数据 , 其实是父组件传数据到子组件
- 如果传递的是函数数据 , 其实是子组件传数据到父组件
- 传递数据方式有三种
- 数组接收数据 (不可以限定数据)
- 对象简单方式接收 (只能限定数据类型)
- 对象复杂方式接收 (都可以限定、可以限定类型、设置默认参数 default、必须传递required、自定义限制方法validator)
自定义事件 【子传父】
ref 获取组件对象。
中央【全局】事件总线 EventBus
$emit(“自定义事件名”,要传递的数据) 触发自定义事件
$off(“要解绑的事件名”) 解绑自定义事件
$on() 创建自定义事件
$once() 创建自定义事件
$children 获取所有的子组件对象,值为一个数组
$parent 获取父组件对象
$root 获取 实例对象 vm
$attrs 隔代组件数据传递
$listeners接收事件
v-model
(1) 绑定dom,input select textarea 双向数据绑定;
(2) 绑定组件,双向数据传递
插槽 slot v-slot 【匿名(默认)插槽/具名插槽/作用域插槽】
路由
vuex
pubsub-js
vuex的使用
state 数据源存放地
getters 相当于store的计算属性
Mutations 为什么不能写异步 :因为页面数据更新了,但是vuex中的mutations数据还是之前的没有更新
actions 异步操作数据
自定义插件
(目的就是添加功能 , 只有是官方插件 , 都要去use)
插件分为两大类 , 对象和函数
Vue.use( ) , 声明使用插件 , 本质是调用函数
对象式插件 : 对象当中必须有一个install方法
Vue.use(调用对象式插件) 本质是调用 install 方法
函数式插件: 就是一个函数
Vue.use(调用该函数) 本质是调用定义的函数插件
暴露方法
1、默认暴露
export default [1,2,3]
import { default as XXX } from './xxx' 全写 因为default是关键字
import xxx from './xxx' 简写
2、分别暴露
export const a = { a:1,b:2 }
export const b = { c:3,d:4 }
import { a , b} from './xxx'
3、统一暴露
export {
const a = { a:1,b:2 },
const b = { c:3,d:4 }
}
import { a , b} from './xxx'
4、* 引入
import * as xxx from './xxx'
( * ) 是 './xxx' 页面暴露出来的对象
5、引入并分别暴露 ( 语法不可以简写 )
export { default as xxx} from ‘ ./xxx ‘
总结 :无论什么暴露方式 , 出文件都是对象 , 只是形成的方式不同
默认暴露形成的方式: 以default 为属性,以default后面的值为值,形成的对象 { default : [1,2,3] }
分别暴露形成的方式: 花括号的形成方式是系统自动携带
统一暴露形成的方式: 花括号是自己写的花括号 (统一暴露是分别暴露的简写 , 可以省略很多 export)
权限控制
任意路由 在vue2中注册必须写最后
在vue3中可以写任意地方 , 因为配置的时候要用pathMatch 注册
1、权限包含几部分
权限数据管理 (用户 角色 权限(菜单) 多对多关系) 增删改查和之前做的spu道理是一样的,而且简单
权限控制(菜单权限控制和按钮权限控制,写代码写逻辑控制用户的操作权限的)
2、权限控制当中的菜单权限(路由权限)控制实现的结果是什么
不同的用户登录进来看到的菜单是不同的
3、权限控制只能在前端做吗?
后端可以做
什么时候是前端什么时候是后端主要是看用户的动态路由表在哪生成
4、菜单权限控制流程
1、菜单的不同是路由的不同导致的,路由的不同又是由登录的用户不同导致的
2、用户的信息数据当中包含了一个routes,它就是不同用户登录所携带的菜单权限信息数据,本质是一个数组,数组里面是字符串
3、目前我们没有控制,我们的路由都是放在静态路由当中,所有的用户登录看到的菜单是一样的
4、我们不能所有的路由都在静态路由当中,需要把路由拆分为3大类
1、静态路由(常量路由)
所有用户都能访问的路由,决定了所有的这些路由菜单都能看到
2、动态路由(异步路由)
allAsyncRoutes代表参与权限控制的菜单所有路由,并不是某个用户的动态路由,后期某个用户登录,需要从这个路由当中去筛选过滤用户自己的动态路由
3、任意路由
anyRoute 最终重定向到404,vue2当中追加路由的时候必须注册在最后
5、在获取用户信息的时候,我们除了name和avatar以外还有routes、roles、buttons,routes数组其实是用户有权访问的路由name值,我们需要用这个name值数组,从所有的动态路由当中去筛选用户自己的动态路由
6、需要把过滤的用户自己的动态路由,动态追加到路由器当中进行注册 router.addRoutes,它的作用是后期你点击菜单,可以正常跳转
7、显示菜单,菜单是便利路由器当中的路由,动态产生的,我们需要把路由器当中的路由数据拿到,v-for让菜单动态生成
8、白板(next(to))
9、深拷贝(不能再原始数据身上进行过滤修改)
5、按钮权限控制
按钮权限依赖菜单权限,如果没有菜单权限是不可能有按钮权限
用户登录返回信息当中还有一个buttons,内部存储的是用户所拥有的按钮权限信息数据
如果用户携带了相应的按钮权限信息数据,对应的按钮就在页面上显示,否则删除
vue3当中自定义一个插件,在插件当中再去自定义指令,每个按钮身上都要使用这个指令
vue2当中完全可以直接定义函数,挂载vue的原型身上去调用即可,也可以使用vue3当中的做法