按键修饰符
和事件修饰符同理,v-on能绑定的不仅仅是click这种点击事件,还包括了对键盘上的键盘进行监听,键盘修饰符则是为键盘事件被触发是增加的一些修饰:
注意
另外修饰符是可以连续使用的,比如:
上面的keyup事件必须是alt和c键同时松开时才会触发clear函数
条件渲染
v-if
v-if指令用户条件性的将绑定DOM渲染出来,比如
//只有当awesome的值为true的时候,h1标签才会被渲染
//如果awesome的值为false,那么这个h1将不会被渲染进DOM树
Vue is awesome!
于js相似,既然有了if,那么必定有else-if和else,所以对应的指令为v-else-if和****v-else
上例中的v-if例子就是一个最完整的v-if条件渲染,如果type的值是A,那么就会渲染A这个div,之后的B,C,Not A/B/C这个3个div将不会被渲染,会被vue忽略掉,type的值是B,C或其他则是同理,这4个div只会根据条件渲染其中的一个,剩下的3个将不会被渲染进DOM树
v-show
使用方式和v-if一致,后面的值是一个布尔值,区别在于v-show如果是false那么就相当于在css中将display属性设置成none,虽然在页面上不可见,但是实际上是存在DOM树里面的,如果值是true则会显示在页面上
//如果ok的值是true,那么h1标签会被显示,否则则会隐藏
Hello!
v-if和v-show
一般来说,v-if 有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
列表渲染
列表渲染,也就是指令v-for,该指令基于一个数组或对象来渲染一个列表,具体例子如下:
假设现在有一个长度为100的数组,数组中的每一项都是一个对象,对象上有一个属性message,其值是一个string类型,现在我们在ul中生成100个li,每个li中的文字对应message的值
使用v-for的写法如下
{{ item.message }}
-
items:是我们要遍历的目标数组;
-
item:循环时的数组中的每一个对象;
-
index:当前遍历对象的索引;
-
item.message:是对象上的属性值;
注意:这边:key是vue为了更好的进行内部排序而绑定的标志,绑定的值的要求是必须本次循环中是唯一的,不能有重复,不然如果一旦li的顺序有所变动,vue在内部就不知道哪个对应哪个
如果遍历的目标是一个对象,那么例子如下:
-
{{ name }}: {{ value }}
new Vue({
el: ‘#v-for-object’,
data: {
object: {
title: ‘How to do lists in Vue’,
author: ‘Jane Doe’,
publishedAt: ‘2016-04-10’
}
}
})
结果:
-
title:How to do lists in Vue’
-
author:Jane Doe
-
publishedAt:2016-04-10
遍历对象时,上例中有两个参数,第一个属性也就是value对应对象的值,第二属性name对应对象的键值;
当然,如果遍历对象时,还有第三个参数index,其值是当前遍历对象的索引
v-model
双向数据绑定,可以用 v-model 指令在表单 、 及 等元素上创建双向数据绑定。绑定后,如果元素上手动输入会直接影响data中的值,而data中的值发生变化,那么元素上的显示也会发生变化
比如:
Message is: {{ message }}
new Vue({
el: ‘#v-for-object’,
data: {
message:‘’
}
})
现在给input绑定了一个v-model指令,其值是在data中定义的message变量,此时,如果我们在input中输入:你好,那么在data中的message属性的值将同步被更新为“你好”,只会因为p元素中也绑定了message,所以p元素中的message位置也将同步修改为“你好”;
如果,这时有一个按钮,按钮的作用是将触发一个函数,函数会将message的值清空,点击按钮后,message的值被清空了,这是input输入框中的值将会被清空,p元素message的位置也会被清空;
v-model本质上是一个语法糖,在Vue中是通过给dom元素同时绑定v-bind:value和v-on:input事件达到最终的效果的;它在内部对不同的表单组件做了识别,针对不同的表单组件实现不同的方案;
示例:现在自定义了一个输入框组件,其内部包含了一个input和一个label,在其他组件引用后,在组件上绑定了v-model,那么在组件内部的input如何接收v-model上的内容呢?
//自定义组件使用
//组件内部
<input @input=“ e m i t ( ′ i n p u t ′ , emit('input', emit(′input′,event.target.value)” :value=“value”>
**
input和和textarea
在这两个元素上v-model的用法差不多,就是和上面的那个例子一样,其绑定后,在元素内的输入会同步修改data中的属性值,而data中的属性值一旦发生便会,也会同步修改元素中输入框内的值,这是一个双向的影响
复选框
单个复选框,绑定到布尔值:
那么选中时,值为true,未选中时,值为false;
多个复选框,绑定到同一个数组:
Checked names: {{ checkedNames }}
new Vue({
el: ‘#example-3’,
data: {
checkedNames: []
}
})
那么,选中的复选框的value值将被加入数组,未选中时其value值将被从数组移除;
单选按钮
Picked: {{ picked }}
new Vue({
el: ‘#example-4’,
data: {
picked: ‘’
}
})
v-model将会把选中的单选框的value值赋值给picked,换句话说picked的值就是当前被选中的单选框的value值
选择框
单选时和单选按钮相似,其绑定的变量的值就是当前选择框选中的value值;多选时绑定到一个数组和复选框相似,其数组内的值就是当前全部被选中的项的value值
首先明确一点,计算属性是一个属性,而不是方法,因此调用的时候不能有括号,在vue中存在一个函数,叫做computed,也叫做计算属性,这个属性专门用于复杂逻辑的处理,比如,从服务器上获取了一堆属性值,这堆值没办法直接使用,需要通过一系列的处理才能使用(具体例子如:请求得到的数据中用户的名字,联系方式,地址,邮箱都是不同的键值对,而在html里需要合并成一个完整的字符串显示),那么就可以在计算属性中处理完了,在放入html中
另外,当计算属性中的某个值发生变化时,计算属性的结果将重新计算,并且页面将被重新渲染,比如现在定一个一个计算属性name,而这个name中有两个值:姓和名,一旦其中某一个发生了变化,那么计算属性name将会被重新执行,然后所有使用到name的地方也将被连带这重新执行渲染
注意:如果某个计算属性在页面中没有被使用到,那么此时计算属性的变更并不会引起页面的渲染
var vm = new Vue({
el: ‘#example’,
data: {
message: ‘Hello’
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
//
this
指向 vm 实例return this.message.split(‘’).reverse().join(‘’)
}
}
})
console.log(vm.reversedMessage) // => ‘olleH’
vm.message = ‘Goodbye’
console.log(vm.reversedMessage) // => ‘eybdooG’
例子中,reversedMessage的值依赖于message,那么当message的值发生变化时,计算属性reversedMessage将会重新计算执行;
注意
计算属性是有缓存的,也就是说,如果其中的值并没有发生变化,那么即使多次被访问,其值并不会重新运行计算,它会将之前计算好的结果给返回出去,只有在其值发生变化的时候,才会重新运行计算结果;
因此注意,下例这个值是不会更新的,其值就是首次创建时返回的值
computed: {
now: function () {
return Date.now()
}
}
侦听属性,watch,该属性用于监测某些属性,比如需要监测某个值是否发生了变化,因为一旦某个值发生了变化,那么带来的结果就会有一系列的响应,侦听属性的函数名,就是属性名,其函数体内的方法就是一旦侦听的属性发生了变化,那么函数体内的代码就会执行;
ar vm = new Vue({
el: ‘#demo’,
data: {
firstName: ‘Foo’,
lastName: ‘Bar’,
fullName: ‘Foo Bar’
},
watch: {
//这里的firstName,就是data中的firstyName,代表侦听的属性就是firstyName
firstName: function (val) {
this.fullName = val + ’ ’ + this.lastName
},
//这里的lastName,就是data中的lastName,代表侦听的属性就是lastName
lastName: function (val) {
this.fullName = this.firstName + ’ ’ + val
}
}
})
这里,watch中里设定了监听了data中的两个属性:firstName和lastName,一旦data中的fistName或者lastName的值发生变化,那么对应在watch中的对应的方法就会被执行;
到这里,是不是觉得计算属性computed和侦听属性watch有点像,区别在于:
-
计算属性是:其函数体内的某个属性的值发生变化,那么计算属性就会重新执行,有点多对一的意思;
-
侦听属性是:监测的属性一旦发生变化会随之改变其他的属性,有点一对多的意思;
另外侦听属性很容易被滥用,比如上例,其实可以用计算属性重写
var vm = new Vue({
el: ‘#demo’,
data: {
firstName: ‘Foo’,
lastName: ‘Bar’
},
computed: {
fullName: function () {
return this.firstName + ’ ’ + this.lastName
}
}
})
每个Vue实例在被创建的过程中都会经过一系列的初始化过程,在这些过程中Vue会进行数据监听,编译模版,实例挂载到DOM等等一系列操作,在这个过程中,Vue为用户在不同阶段都预留了一个函数接口,这些接口函数会在不同的阶段被执行,这些接口也叫做生命周期钩子函数;
注意:钩子函数可以用es6的写法,也可以用es5的写法,但是****唯独不可以用箭头函数,因为生命周期的钩子函数中的this指向的是vue的实例,而箭头函数是没有自己的this的;
new Vue({
data: {
a: 1
},
created: function () {
//
this
指向 vm 实例console.log('a is: ’ + this.a)
}
})
//最终会打印:a is 1
图解
beforeCreate()
该阶段运行在实例创建前的状态 ,**此时的el和data尚未被创建,因此值都是undefined;**这个阶段可以除了整个系统的Loading
el就是我们挂载的根节点元素,比如下例,创建的vue实例就是挂载在id为app的DOM元素上的,当然也可以将#app改为.app,那么就代表挂载的对象是类名为app的DOM元素;
var app = new Vue({
el: ‘#app’,
data: {
message: ‘Hello Vue!’
}
})
Created()
该阶段在实例被创建后就立即调用,此时的data中已经有数据了,并且可以运行方法,使用计算数据和侦听属性之类的了,但是该阶段挂载还没有开始,因此el是undefined;
通常在这个阶段,通过axios等将数据请求下来,存储至vuex或者data中;
beforeMount()
该阶段在实例被挂载开始前调用;不过通常该阶段能做的事在created()中也能做;这个钩子函数其实不常用;
mounted()
该阶段在实例挂载后被调用,这是el被创建的(虚拟DOM)替换了,注意的是mounted不会保证所有的自组件也都被一起挂载,如果你希望等到整个视图都渲染完毕,可以在mounted的内部调用vm.$nextTick
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
}
在mounted()之前,页面上所有的数据都是通过占位符占位的,而mounnted之后占位符的内容发生更新,数据被填充到页面上;
beforeUpdate()
该钩子函数会在页面上数据更新前调用,在这个阶段适合在更新前访问现有的DOM;
updated()
该钩子函数会在页面上数据更新之后调用,这个阶段DOM已经更新,适合处理依赖新DOM的操作,然而在绝大多数情况下,根据DOM或者说根据数据需要改变现有状态,通常使用计算属性或者侦听属性比较好;
同样,updated不会保证所所有的子组件页都被一起重绘,如果希望等到所有的视图都重绘完毕,那么可以在updataed里使用vm.$nextTick
updated: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been re-rendered
})
}
beforeDestroy()
实例销毁前调用,在这一步,实例仍然完全可用,可以在这个钩子函数内清理事件,计时器或者取消订阅操作等;
destroyed()
实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
比如现在在该组件上有一个定时器,希望离开组件时定时器被清除,那么就可以在beforeDestroy()或者destroyed()这两个钩子函数中的一个设置清除定时器,因为当执行beforeDestroy()或者destroyed()这个两个钩子函数时,就代表着离开当前组件了;
组件是Vue中最为强大的功能之一,它可以将HTML元素进行封装重用,这样整个页面其实就被组件化了,页面上类似的元素部分可以进行剥离,然后组件化,这样既可以减少代码量,也方便对相同的部分进行统一管理,一旦需要修改那么只需要修改一处便可以将多处进行同时修改;
简单示例
// 定义一个名为 button-counter 的新组件
Vue.component(‘button-counter’, {
data: function () {
return {
count: 0
}
},
template: ‘You clicked me {{ count }} times.’
})
上例是在Vue实例上添加(也可以说是注册)了一个名为button-counter的组件,注册之后,那么在Vue上的任意地方可以进行任意次数的使用,并且因为data是通过函数返回的形式定义的数据,因此多个组件之间的数据并不相互影响,而且每个组件都有自己的data,computed,watch,methods以及生命周期,具体使用例子如下:
组件创建
通过template标签创建,比如
这是template构建的组件注意:所有元素都必须包含在一个元素中,或者说组件的根组件有且只有有个;
组件注册
通过上例,相信对组件有了一个简单的了解,组件的注册分为两种:全局注册和局部注册;
这两者的区别在于,全局组件一旦注册,那么就可以在根实例下任意一个地方使用,但是这种注册往往是不够理想的,设想下,如果你全局注册了一个组件,在通过像webpack这样的构建系统时,即使你全局注册的组件没有被使用到,webpack仍然会将组件进行打包,这样最终的打包结果代码量就会无意义的增大;
局部注册则不同,当制作完局部组件之后,那么如果哪个组件需要使用,就通过components进行局部注册,这样组件可以在当前组件中被使用了;
全局注册
通过Vue.component()这个方法给全局注册一个组件
Vue.component(‘my-component-name’, { /* … */ })
第一个参数是一个字符串,代表注册组件的名字,第二个参数是组件代码;
全局组件通常只用来注册一些非常常用的基础组件,比如:自定义的按钮,自定义的输入框,自定义的icon等等;
局部注册
局部注册则是需要在当前组件中注册
import ComponentA from ‘./ComponentA.vue’
export default {
components: {
ComponentA
}
}
首先通过import将组件引入,之后在当前的components中注册,这样注册后就可以在当前组件中使用了;
通过vue-cli搭建的项目,更多的使用的是局部注册的方式引入组件,使用组件;
注意
因为HTML对大小写不敏感,因此强烈建议组件的命名遵循W3C的规范,也就是:字母全部小写且必须包含一个连字符;
可以这样注册
import ComponentA from ‘./ComponentA.vue’
export default {
components: {
ComponentA
}
}
但是使用的适合则写成
组件通讯
父组件向子组件传递
父组件上定义一个属性,子组件通过prop接收属性,具体如下
父组件示例
//静态属性
如果是静态属性,那么直接在组件上写上属性名和属性值,但实际情况除了UI库,很少会有静态属性直接写在组件上,更多是动态值传递给子组件,这个时候就用到了v-bind指令
//动态属性
//:name是v-bind:name的缩写
通过v-bind指令给name绑定了一个变量username
注意:假如组件上只有绑定了属性,没有绑定属性值,比如下例,只有绑定了name,没有为name绑定任何值,那么在子组件中获得的值默认为boolean类型,且值为true
子组件示例
props: {
// 基础的类型检查 (
null
和undefined
会通过任何类型验证)propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: ‘hello’ }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return [‘success’, ‘warning’, ‘danger’].indexOf(value) !== -1
}
}
}
-
type:类型检查,value为对应类型的构造函数,如果是null或者undefined,则会通过任何类型的验证,如果是多种类型则可以通过传入数组[String,Array]
-
required:是否是必填的
-
default:默认值,默认值为对象或者数据的默认值必须从一个工程函数获取;
-
validator:自定义娇艳函数,范围true为通过,false为不通过;
在子组件中通过一个对象props接收父组件传递过来的属性,该对方的键名就是父组件上的属性名
值的注意的是,值的传递是单向数据流,也就是说,父组件值的变化会实时更新到子组件的props中,但是子组件的变化不会影响到父组件,这意味着不应该在子组件去修改props中的值,如果在子组件中需要监听props的值,并且当值改变时需要有一系列的操作,那么这是应该使用计算属性等方式重新计算组件,例如下面两种示例
1.props传递可一个初始值,子组件希望将这个值保存到本地
props: [‘initialCounter’],
data: function () {
return {
counter: this.initialCounter
}
}
2.props传递了一个原始值,子组件希望通过这个原始值计算出了一个新的值供组件使用
props: [‘size’],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
子组件向父组件传递
在子组件中,以某些事件为契机,触发后通过**$emit()**方法将消息和数据传递到父组件,在父组件中,通过v-on将事件接收,比如
子组件
传递
methods:{
updata(){
this.$emit(‘msg’,data)
}
}
在子组件中有一个点击事件,点击后,出发了$emit(),第一个参数是定义的名字,父组件中通过这个名字接收数据,第二个参数就是需要传递的数据;
父组件
methods:{
getData(msg){
console.log(msg)
}
}
getData中的形参msg就是子组件传递上来的数据data;
动态组件
通过标签动态加载,动态组件可以更大的帮助用户去复用代码,而不是重新构造
computed:{
currentComponent(){
return this.ab?LifeCycleA : FOR;
}
}
通过判断this.ab的值选择当前的的位置是加载组件LifeCycleA还是组件FOR
另外通过这种方式动态加载组件会重新卸载,加载组件,如果在组件内部有什么修改,切换后修改是不会被保存的,因此,如果需要保存,Vue提供了一种内置的解决办法:,将这个标签包裹component标签,例如
包裹后,会去缓存当前的组件实例,因此如果当前的组件再一次进入页面,那么Vue将不会去重新创建渲染进页面,而是从缓存中将该页面取出来,插入页面中;
vue上面的原文是:Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 元素作为承载分发内容的出口。
换句话说,也就是定义的内容会被自动加载替换到slot这个元素中,拿个实际场景为例,很多后台管理页面,注册,登录,密码找回使用的是同一套背景模版,区别在于上面的输入框不同,那么实际上开发的时候背景模版只需要单独封装成一个组件,并且预留一个作为接口,之后分别在登录,注册,密码找回中引入,实际上登录注册密码找回的代码就仅仅是输入部分的编写设计
例子:
navigation-link是一个基础组件,标签内部的"Your Profile"将会替换组件内部的标签
Your Profile
navigation-link组件内部
<a
v-bind:href=“url”
class=“nav-link”
同样上例中"Your Profile"部分可以是文字,也可以是HTML代码,如果是HTML代码,HTML代码将整体替换组件内部的部分
另外标签内部是可以有内容的,它仅仅只会在插槽没有被使用到的时候才会被渲染,成为**“后备内容”**,
Submit
如果标签内部没有写入内容,那么button上就会有默认文字"Submit",如果有文字,那么会显示文字内容;
具名插槽
如果一个组件内需要有多个插槽,那么就需要给不同的插槽进行命名,这样,指定的内容可以通过指令插入到指定的插槽内,另外如果没有命名实际上vue会给他一个默认添加一个名字default,例如
//虽然没有添加name,但是实际上vue会给它添加一个name='default’属性,默认插槽有且只有一个
通过给插槽添加属性name来确定为具名插槽,在向具名插槽提供内容的时候,我们可以在一个 元素上使用 v-slot 指令(缩写#),并以 v-slot 的参数的形式提供其名称:
//v-sort:header 等同于 #header
Here might be a page title
A paragraph for the main content.
And another one.
Here's some contact info
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
最后
前端CSS面试题文档,JavaScript面试题文档,Vue面试题文档,大厂面试题文档,需要的读者可以戳这里免费领取!
如果标签内部没有写入内容,那么button上就会有默认文字"Submit",如果有文字,那么会显示文字内容;
具名插槽
如果一个组件内需要有多个插槽,那么就需要给不同的插槽进行命名,这样,指定的内容可以通过指令插入到指定的插槽内,另外如果没有命名实际上vue会给他一个默认添加一个名字default,例如
//虽然没有添加name,但是实际上vue会给它添加一个name='default’属性,默认插槽有且只有一个
通过给插槽添加属性name来确定为具名插槽,在向具名插槽提供内容的时候,我们可以在一个 元素上使用 v-slot 指令(缩写#),并以 v-slot 的参数的形式提供其名称:
//v-sort:header 等同于 #header
Here might be a page title
A paragraph for the main content.
And another one.
Here's some contact info
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-dbwfttEU-1713610171510)]
[外链图片转存中…(img-ugxmfDrt-1713610171511)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
[外链图片转存中…(img-dYwalgWp-1713610171511)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
[外链图片转存中…(img-OpTXVyi6-1713610171511)]
最后
前端CSS面试题文档,JavaScript面试题文档,Vue面试题文档,大厂面试题文档,需要的读者可以戳这里免费领取!
[外链图片转存中…(img-MLAoLlbw-1713610171511)]
[外链图片转存中…(img-QmULYqlg-1713610171512)]
-