数据代理
1.Vue中的数据代理:
通过vm对象来代理data对象中属性的操作 (读/写)
2.Vue中数据代理的好处:
更加方便的操作data中的数据
3.基本原理:
通过object.defineProperty()把data对象中所有属性添加到vm上为每一个添加到vm上的属性,都指定一个getter/setter。在getter/setter内部去操作 (读/写) data中对应的属性。
事件处理
事件的基本使用
1.使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;
2.事件的回调需要配置在methods对象中,最终会在vm上;
3.methods中配置的函数,不要用箭头函数!否则this就不是vm了;
4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
5.@click="demo”和 @click="demo($event)”效果一致,但后者可以传参;
<div id="root">
<button @click="showInfo">点我提示信息1</button>
<button @click="showInfo2($event,66)">点我提示信息2</button>
</div>
methods: {
showInfo(e){
alert('你好!' + e.target.innerText)
},
showInfo2(e,number){
alert('你好!' + number)
},
},
事件修饰符
- prevent:阻止默认事件 (常用) ;
- stop: 阻止事件冒泡(常用) ;
- once: 事件只触发一次(常用) ;
- capture:使用事件的捕获模式;
- self:只有event.target是当前操作的元素是才触发事件;
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕
条件渲染
1.v-if
写法:
- v-if="表达式"
- v-else-if="表达式"
- v-else="表达式"
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意:v-if可以和v-else-if、v-else一起使用,但要求结构不能被“打断”
2.v-show
写法: v-show="表达式"
适用于:切换频率较高的场景
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
备注: 使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
计算属性
定义:要用的属性不存在,要通过已有属性计算得来。
原理: 底层借助了objcet.defineproperty方法提供的getter和setter。
get函数什么时候执行?
1.初次读取时会执行一次。
2.当依赖的数据发生改变时会被再次调用。
优势: 与methods实现相比,内部有缓存机制(复用),效率更高,调试方便
备注:
1.计算属性最终会出现在vm上,直接读取使用即可。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
<div id="root">
姓:<input type="text" v-model="firstName"/><br/>
名:<input type="text" v-model="lastName"/><br/>
姓名:<span>{{fullName}}</span>
</div>
data() {
return {
firstName:'张',
lastName:'三'
}
},
computed:{
// 完整写法
fullName:{
// get有什么作用? 当有人读取fulIName时,get就会被调用,且返回值就作为fulIName的值
// get什么时候调用? 1.初次读取fullName时。2.所依赖的数据发生变化时。
get(){
return this.firstName + '-' + this.lastName
},
// set什么时候调用? 值被修改的时候
set(val){
const arr = val.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
// 简写
fullName(){
return this.firstName + '-' + this.lastName
}
}
监视属性watch
1.当被监视的属性变化时,回调函数自动调用,进行相关操作
2.监视的属性必须存在,才能进行监视!!
3.监视的两种写法:
(1)new Vue时传入watch配置
data() {
return {
isHot:true
}
},
watch:{
isHot:{
handler( newValue,oldValue){
console.log('isHot被修改了,修改前值为:' + oldValue + ';修改后的值为:' + newValue)
}
}
(2)通过vm.$watch监视
vm.$watch('isHot',{
handler(newValue,oldValue){
console.log('c被修改了')
}
})
深度监视
- Vue中的watch默认不监测对象内部值的改变 (一层)
- 配置deep:true可以监测对象内部值改变 (多层)。
备注:
- Vue自身可以监测对象内部值的改变,但vue提供的watch默认不可以!
- 使用watch时根据数据的具体结构,决定是否采用深度监视。
data() {
return {
numbers:{
a: 1,
},
}
},
watch:{
// 监视多层级结构某个值的变化
'numbers.a':{
handler(newValue,oldValue){
console.log('numbers.a被修改了')
}
},
// 监视多层级结构所有值的变化
numbers:{
deep:true, // 深度监视
immediate:true,// 初始化时让handler调用一下
handler(newValue,oldValue){
console.log('numburs被修改了')
}
},
}
computed和watch之间的区别
- computed能完成的功能,watch都可以完成。
- watch能完成的功能,computed不一定能完成,例如: watch可以进行异步操作。
关于this的小点
两个重要的小原则:
- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
- 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等),最好写成箭头函数.
这样this的指向才是vm 或 组件实例对象。
绑定样式
1.class样式
写法: class="xxx”xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
2.style样式
:style="{fontsize: xxx}"其中xxx是动态值。
:style="[a,b]"其中a、b是样式对象。
条件渲染
1.v-if
写法:
(1).v-if="表达式"
(2).v-else-if="表达式"
(3).v-else="表达式"适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意: v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”
2.v-show
写法: v-show="表达式"
适用于:切换频率较高的场景。
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
列表渲染
v-for指令:
1.用于展示列表数据
2.语法: v-for="(item, index) in xxx” :key="yyy"
3.可遍历: 数组、对象、字符串 (用的很少)、指定次数(用的很少)
key有什么作用? (key的内部原理)
1.虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据[新数据]生成[新的虚拟DOM]随后Vue进行[新虚拟DOM] 与[旧虚拟DOM] 的差异比较,比较规则如下:
2.对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
①.若虚拟DOM中内容没变,直接使用之前的真实DOM!
②.若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM.
(2).旧虚拟DOM中未找到与新虚拟DOM相同的key创建新的真实DOM,随后渲染到到页面。
3.用index作为key可能会引发的问题:
(1).若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题,但效率低
(2).如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题
4.开发中如何选择key?
(1).最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
(2).如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
列表的过滤 filter()
定义和用法
filter用于对数组进行过滤。
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
语法
array.filter((currentValue,index,arr)=>{
return xxx
})
currentValue:当前元素的值
index:当前元素的索引值
arr:当前元素的数组对象
实例1. 返回数组nums中所有大于5的元素。
let nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let res = nums.filter((num) => {
return num > 5;
});
console.log(res); // [6, 7, 8, 9, 10]
实例2. 对数组进行过滤,筛选出年龄大于 18岁的数据
const arr = [
{
name: 'tom1',
age: 23
},
{
name: 'tom2',
age: 42
},
{
name: 'tom3',
age: 17
},
{
name: 'tom4',
age: 13
},
]
const res = arr.filter(item => item.age > 18);
console.log(res); //[{name: 'tom1',age: 23},{name: 'tom2',age: 42}]
console.log(arr);
列表的排序 sort()
定义和用法
sort() 方法用于对数组的元素进行排序。返回值是对数组的引用。数组在原数组上进行排序,不生成副本。
let arr = [1,5,1,6,6,6,8,7,7,1];
arr.sort( (a,b) => {
return b - a;//从大到小
// return a - b;//从小到大
})
内置指令
v-bind:单向绑定解析表达式,可简写为 :xxx
v-model:双向数据绑定
v-for:遍历数组/对象/字符串
v-on:绑定事件监听,可简写为@
v-if:条件渲染(动态控制节点是否存存在)
v-else:条件渲染(动态控制节点是否存存在)
v-show:条件渲染 (动态控制节点是否展示)
v-text:向其所在的节点中渲染文本内容
v-html:向指定节点中渲染包含htm结构的内容
v-cloak(没有值):
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
2.使用css配合v-cloak可以解决网速慢时页面展示出未编译的模板问题。
v-once指令:
1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
v-pre指令:
1.跳过其所在节点的编译过程。
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
自定义指令
定义语法
1.局部定义
directives:{
fbind:{ // 指令名称
bind(el,binding){
console.log('bind')
},
inserted(el,binding){
console.log('inserted')
},
update(el,binding){
console.log('update')
}
}
}
}
2.全局定义
Vue.directive('指令名',(el,binding)=>{
el.innerText = binding.value * 3
})
Vue.directive('指令名',{
bind(el,binding){
console.log('bind')
},
inserted(el,binding){
console.log('inserted')
},
update(el,binding){
console.log('update')
}
})
配置对象中常用的3个回调:
(1).bind:指令与元素成功绑定时调用。
(2).inserted: 指令所在元素被插入页面时调用。
(3).update:指令所在模板结构被重新解析时调用。
备注:
1.指令定义时不加v-,但使用时要加v-;
2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名
非单文件组件
Vue中使用组件的三大步骤:
1、定义组件(创建组件)
2.注册组件
3.使用组件(写组件标签)
如何定义一个组件?
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别:
1.el不要写,为什么?- 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
2.data必须写成函数,为什么? - 避免组件被复用时,数据存在引用关系。
备注:使用template可以配置组件结构。
如何注册组件?
1.局部注册:靠new Vue的时候传入components选项
2.全局注册: 靠Vue.component("组件名 ,组件)
编写组件标签:
<school></school>
ref属性
1.被用来给元素或子组件注册引用信息 (id的替代者)
2.应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
3.使用方式:
打标识: <h1 ref="xxx">.....</h1> 或 <School ref="xxx"x/School>
获取: this.$refs.xxx
props配置项
1.功能:让组件接收外部传过来的数据
2.传递数据: <Demo name="xxx"/>
3.接收数据:
1.第一种方式(只接收) : props:['name']
2.第二种方式(限制类型): props:{ name:String }
3.第三种方式(限制类型、限制必要性、指定默认值) :
props:{
name:{
type:String,//类型
required;true,//必要性
default;老王 //默认值
}
}
备注: props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
props适用于
父组件 ==> 子组件 通信
子组件 ==> 父组件 通信(父组件先传递一个函数给子组件,子组件通过调用函数来传值)
mixin(混入)
-
功能:可以把多个组件共用的配置提取成一个混入对象
-
使用方式:
第一步定义混合:
mixin.js
export const mixin ={
data(){....},
methods:{....}
....
}
路由
理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。
前端路由:key是路径,value是组件。
1.基本使用
-
安装vue-router,命令:
npm i vue-router
-
应用插件:
Vue.use(VueRouter)
- 编写router配置项:
//引入VueRouter import VueRouter from 'vue-router' //引入Luyou 组件 import About from '../components/About' import Home from '../components/Home' //创建router实例对象,去管理一组一组的路由规则 const router = new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home } ] }) //暴露router export default router
- 实现切换(active-class可配置高亮样式)
<router-link active-class="active" to="/about">About</router-link>
5. 指定展示位置
<router-view></router-view>
2.几个注意点
-
路由组件通常存放在
views
文件夹,一般组件通常存放在components
文件夹。 -
通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
-
每个组件都有自己的
$route
属性,里面存储着自己的路由信息。 -
整个应用只有一个router,可以通过组件的
$router
属性获取到。
3.嵌套路由(多级路由)
1.配置路由规则,使用children配置项:
routes:[
{
path:'/about',
component:About,
},
{
path:'/home',
component:Home,
children:[ //通过children配置子级路由
{
path:'news', //此处一定不要写:/news
component:News
},
{
path:'message',//此处一定不要写:/message
component:Message
}
]
}
]
2.跳转(要写完整路径):
<router-link to="/home/news">News</router-link>
4.路由的query参数
1.传递参数
<!-- 跳转并携带query参数,to的字符串写法 -->
<router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
<!-- 跳转并携带query参数,to的对象写法 -->
<router-link
:to="{
path:'/home/message/detail',
query:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
2.接收参数:
$route.query.id $route.query.title
5.命名路由
1.作用:可以简化路由的跳转。
2.如何使用
1.给路由命名:
{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello' //给路由命名
path:'welcome',
component:Hello,
}
]
}
]
}
2.简化跳转:
<!--简化前,需要写完整的路径 -->
<router-link to="/demo/test/welcome">跳转</router-link>
<!--简化后,直接通过名字跳转 -->
<router-link :to="{name:'hello'}">跳转</router-link>
<!--简化写法配合传递参数 -->
<router-link
:to="{
name:'hello',
query:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
6.路由的params参数
1.配置路由,声明接收params参数
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
component:Message,
children:[
{
name:'xiangqing',
path:'detail/:id/:title', //使用占位符声明接收params参数
component:Detail
}
]
}
]
}
2.传递参数
<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="/home/message/detail/666/你好">跳转</router-link>
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link
:to="{
name:'xiangqing',
params:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
3.接收参数:
$route.params.id
$route.params.title