目录
1.传统的网页开发弊端
2.渐进式框架
解决问题
3.引用Vue框架的方式
1.安装插件
Volar
Prettier
编辑
出现报错
2.VUE安装方法
1.CDN-下载js
2.使用Vite安装项目
4.Vue基础语法
1.声明式渲染
2.应用 & 组件实例
3.模板语法
5.Data Property 和方法
6.计算属性
1.计算属性缓存vs方法
2.计算属性 VS watch属性
3.方法 VS watch属性
4.侦听器(监听器)
深度监听
7.Class 与 Style 绑定
1.绑定内联样式
8.条件渲染
1.v-if
2.在template元素上使用v-if条件渲染分组
3.v-show
4.v-if vs v-show
9.列表渲染v-for
1.用v-for把一个数组映射为一组元素
2.在v-for里使用对象
3.维护状态
4.数组更新检测
变更方法
10.事件处理
1.监听事件
2.事件处理方法
3.内联处理器中的方法
4.多事件处理器
5.事件修饰符
6.按键修饰符
11.表单输入绑定
1.值绑定
2.修饰符
.lazy
.number
.trim
12.组件基础
1.组件的组织
2.组件数据的存放
3.通过 Prop 向子组件传递数据(父传子)
13.Props
1.Prop 验证
2.单向数据流
14.监听子组件事件
(子组件通过自定义事件向父组件传值)子传父
1.使用事件抛出一个值
15.父组件访问子组件($ref)
16.通过插槽分发内容
1.具名插槽
2.渲染作用域
3.备用内容
4.作用域插槽
17.组件之间的跨级通信Provide / Inject
1.处理响应性
18.生命周期
编写HTML,指定ID-->编写CSS-->编写JavaScript交互(获取DOM->添加事件监听->维护数据(手动添加新元素))
- 列表项1
- 列表项2
- 列表项3
顺序
2.直接引入CDN
{{num}}
{{uname}}
let app = Vue.createApp(Couter).mount('#counter') //创建应用,将配置对象传入
console.log(app)//Proxy代理对象
// app.num->修改num的值,页面上的num也会随之修改,数据的双向绑定
app.num = 4
使用npm,需要在电脑上安装node
查看node版本
npm -v
下载对应版本
# npm 7+,需要加上额外的双短横线
$ npm init vite@latest <自己项目的名称> -- --template vue
cd vue-begin
npm install
npm run dev
安装 | Vue.js
看起来跟渲染一个字符串模板非常类似,但是 Vue 在背后做了大量工作。现在数据和 DOM 已经被建立了关联,所有东西都是响应式的。
{{ num }}
{{ uname }}
介绍 | Vue.js
v-bind
attribute 被称为指令。指令带有前缀v-
,以表示它们是 Vue 提供的特殊 attribute。
组件实例(data)的所有 property,无论如何定义,都可以在组件的模板(template)中访问。
链接
data() {
return {
link: "https://zxuqian.cn",
};
},
应用 & 组件实例 | Vue.js
1.v-once
v-once:当数据改变时,插值处的内容不会更新。
data () {
return {
uname: 'zz'
}
},
methods: {
//给vue定义方法
changeUname: function () {
// this指向vue实例(data)
this.uname = 'nihao'
}
},
//点击后会更新
{{ uname }}
//点击后不会更新
{{ uname }}
2.v-html
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用v-html。
data () {
return {
msg: '标题
'
}
},
{{ msg }}
//标题
//标题
在你的站点上动态渲染任意的 HTML 是非常危险的,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要将用户提供的内容作为插值。
3.v-bind
data () {
return {
id: 'd1',
url: 'https://v3.cn.vuejs.org/logo.png'
}
},
v-bind绑定
#d1 {
color: aqua;
}
#d2 {
color: blueviolet;
}
4.使用 JavaScript 表达式
{{ num + 1 }}
//1
{{ uname.split("").reverse().join("") }}
//zz
v-bind绑定
//id="d11"
5.指令
指令 (Directives) 是带有
v-
前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for
和v-on
是例外情况)。指令的职责:当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
6.参数
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。
...
//href 是参数,告知 v-bind 指令将该元素的 href attribute 与表达式 url 的值绑定。
7.v-on
用于监听 DOM 事件。
语法糖
v-bind绑定
8.动态参数
在指令参数中使用 JavaScript 表达式,方法是用方括号[ ]括起来。
attributeName会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。
eg:组件实例有一个 data property attributeName,其值为 "
id"
,那么这个绑定将等价于v-bind:id。
data () {
return {
attributeName: 'id',
}
},
等价于:
v-bind绑定
...
组件的data选项是一个函数。Vue 会在创建新组件实例的过程中调用此函数。它应该返回一个对象,然后 Vue 会通过响应性系统将其包裹起来,并以
$data
的形式存储在组件实例中。
用
methods
选项向组件实例添加方法,它应该是一个包含所需方法的对象
Vue 自动为
methods
绑定this
,以便于它始终指向组件实例。这将确保方法在用作事件监听或回调时保持正确的this
指向。在定义methods
时应避免使用箭头函数(指向是静态的),因为这会阻止 Vue 绑定恰当的this
指向。
这些
methods
和组件实例的其它所有 property 一样可以在组件的模板中被访问。在模板中,它们通常被当做事件监听使用
Data Property 和方法 | Vue.js
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。
对于任何包含响应式数据的复杂逻辑,你都应该使用计算属性
data () {
return {
message: 'hello'
}
},
computed: {//计算属性:只要依赖值不变,就不会重新计算
reverseMsg: function () {
return this.message.split("").reverse().join("")
}
},
{{ message }}
/hello
{{ message.split("").reverse().join("") }}
//olleh
{{ reverseMsg }}
//olleh
使用computed配置项定义计算属性,每个计算属性都是一个函数,函数里可以使用this访问data或其他配置项中的值。
案例(与下方事件处理案例做对比)
data() {
return {
showAnswer: false,
};
},
computed: {
label() {
return this.showAnswer ? "隐藏答案" : "显示答案";
},
},
问:Vue 是一个什么样的框架?
答:Vue 是一套用于构建用户界面的渐进式框架。
不同:
计算属性将基于它们的响应依赖关系缓存。计算属性只会在相关响应式依赖发生改变时重新求值。这就意味着只要message还没有发生改变,多次访问 reverseMsg时计算属性会立即返回之前的计算结果,而不必再次执行函数。(提高性能)
//computed计算属性:只要依赖值不变,就不会重新计算,计算属性将基于它们的响应依赖关系缓存,提高性能。
//简写
reverseMsg: function () {
console.log('计算属性')
return this.message.split("").reverse().join("")
}
// 每一个计算属性中都有一个getter和setter
reverseMsg: {
// 计算属性默认只有 getter,不过在需要时你也可以提供一个 setter
// 计算属性默认只有 getter,不过在需要时你也可以提供一个 setter(一般没有set方法),计算属性是只读属性
set: function (newValue) {//在设置或者更改计算属性的时候调用
console.log(newValue)//改变reverseMsg
this.message = newValue//页面显示也会发生改变,显示 改变reverseMsg
},
// 完整写法
get: function () {
return this.message.split("").reverse().join("")
}
}
//methods
reverseMessage: function () {
console.log('methods')
return this.message.split("").reverse().join("")
}
{{ message.split("").reverse().join("") }}
{{ message.split("").reverse().join("") }}
{{ reverseMsg }}
{{ reverseMsg }}
{{ reverseMessage() }}
{{ reverseMessage() }}
实例
data() {
return {
showAnswer: false,
};
},
computed: {
label() {
return this.showAnswer ? "隐藏答案" : "显示答案";
},
},
methods: {
toggleAnswer() {
this.showAnswer = !this.showAnswer;
},
},
问:Vue 是一个什么样的框架?
答:Vue 是一套用于构建用户界面的渐进式框架。
添加新文章到列表中
案例:计算属性
- {{ blog }}
共({{ count }})篇博客
blogPosts: [
"Vue 3.0x 入门实战",
"Vue 3.x 完全指南",
"React 18 新特性介绍",
"JavaScript 基础语法概览",
],
computed: {
count() {
return this.blogPosts.length;
},
}
案例:watch监听器
count: 4, // 使用 watch 的方式
watch: {
blogPosts: {
handler (newVal) {
this.count = newVal.length
},
//监听数组元素的变量,要加deep
deep: true,
},
}
添加新添加的文章的标题,过两秒钟才添加到列表中
watch方式
{{ newBlog }}
newBlog: "",
watch: {
blogPosts: {
handler (newVal) {
this.count = newVal.length
},
deep: true,
},
newBlog (newVal) {
// 模拟耗时操作
setTimeout(() => {
this.blogPosts.push(newVal)
}, 2000)
},
},
计算方式
newBlogPost: "", // 使用 computed 的方式
computed: {
count () {
return this.blogPosts.length
},
newBlog: {
get () {
return this.newBlogPost
},
set (value) {
this.newBlogPost = value // 需要手动赋值,无法访问之前的值,烦琐
setTimeout(() => {
this.blogPosts.push(value) // 虽然代码中可以修改其它属性,但推荐只对它所计算的属性进行修改,并且不要进行异步的修改。
}, 2000)
},
},
},
总结:
computed | watch |
简单的业务逻辑计算 | 耗时的操作和远程API加载 |
可以直接在HTML模板中使用 | 不能在HTML模板中使用 |
响应data数据变化 | 响应数据变化 |
有返回值/getter | 没有返回值 |
也可以使用setters修改data中的数据 | 可以修改data中的数据 |
类似于计算属性和监听器的区别。
- {{ blog }}
{{ newBlog }}
blogPosts: [
"Vue 3.0x 入门实战",
"Vue 3.x 完全指南",
"React 18 新特性介绍",
"JavaScript 基础语法概览",
],
newBlog: "",
methods: {
addANewBlog() {
// 模拟耗时操作
setTimeout(() => {
this.blogPosts.push(this.newBlog);
}, 2000);
},
handleButtonClick() {
this.newBlog = "Vue 3.x 计算属性和监听器的区别";
},
},
watch: {
newBlog(newVal) {
this.addANewBlog();
},
},
methods | watch |
可以在watch中调用 | 不能调用 |
可以直接在HTML模板中使用 | 不能在HTML模板中使用 |
响应data数据变化 | 响应data数据变化 |
可以有返回值,也可以没有 | 没有返回值 |
使用watch选项允许我们执行异步操作 (访问一个 API),并设置一个执行该操作的条件。这些都是计算属性无法做到的。(定时器,请求API等耗时操作)
data属性-->watch监听器(与data属性名相同)-->修改其它data属性-->反应到模板上
data () {
return {
message: 'hello',
age: 0
}
},
watch: {//监听数据的变化
// 每当message发生变化时,就会调用这个函数;相当于一个数据影响多个数据
message: function (newValue, oldValue) {
console.log(newValue) //nihao
console.log(oldValue) //hello
// 执行异步操作,或者复杂逻辑代码
if (newValue.length < 5 || newValue.length > 10) {
console.log('输入框中的内容不能小于5或者大于10')
}
}
}
{{ message }}
message: {
immediate: true,//初始化的时候调用函数
handler: function (newValue) {
console.log(newValue)
if (newValue.length < 5 || newValue.length > 10) {
console.log('输入框中的内容不能小于5或者大于10')
}
}
}
案例:五秒后答案消失,反复点击按钮,定时器清空,重新及时
data() {
return {
showAnswer: false,
countDown: 5,
timer: null,
};
},
watch: {
showAnswer(newVal, oldVal) {
if (newVal) {//newVal=true时
this.countDown = 5;//计时器为5
if (this.timer) {//如果timer有值,先清除,可能倒计时还没有结束,用户就反复点击了
clearInterval(this.timer);
this.timer = null;
}
// 5秒后关闭答案
this.timer = setInterval(() => {
this.countDown -= 1;
if (this.countDown === 0) {//隐藏答案
this.showAnswer = false;
clearInterval(this.timer);
this.timer = null;
}
}, 1000);
}
},
},
user: {
name: 'zz',
age:11
}
watch: {
// 监听不到对象中的属性变化,需要使用到深度监听
// user: function (newValue) {
// console.log(newValue)//打印不出来
// }
"user.name": {//使用字符串的形式进行优化,只会单独监听对象中对应的属性
handler: function (newValue) {
console.log(newValue)//Proxy {name: 'change'} -> 改为字符串后( "user.name")->change
},
deep: true//表示是否深度监听,侦听器会一层层的向下遍历,给对象每个属性都加上侦听器
}
}
{{ user.name }}
计算属性和侦听器 | Vue.js
操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是 attribute,所以我们可以用
v-bind
处理:只需要通过表达式计算出字符串结果即可。字符串拼接麻烦且易错。因此,在将
v-bind
用于class
和style
时,Vue做了专门的增强。
:class
指令也可以与普通的 class
attribute 共存。 data () {
return {
message: 'helloWorld',
isActive: true,
// error: null,//无错误情况
error: {},//有错(有值)
classObj: {
active: true,
helloWorld: true
},
activeClass: 'active'
}
},
computed: {
classObjCom: function () {
return {
// 没有报错展示active这个样式
active: this.isActive && !this.error,
// 报错显示helloWorld样式
helloWorld: this.error
}
}
}
hello
hello1
hello2
hello3
hello4
hello5
hello6
hello7
.active {
color: rgb(102, 47, 164);
}
.helloWorld {
font-size: 30px;
background-color: skyblue;
}
:style
的对象语法十分直观,看着非常像 CSS,但其实是一个 JavaScript 对象。CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名。
data () {
return {
activeColor: 'red',
fontSize: '50px',
bgcColor: 'pink',
styleObj: {
color: 'red',
fontSize: '30px',
'background-color': 'pink',
}
}
},
hello
hello1
helllo2
helllo2
Class 与 Style 绑定 | Vue.js
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。
A
B
C
Not A/B/C
因为
v-if
是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个元素当做不可见的包裹元素,并在上面使用
v-if
。最终的渲染结果将不包含元素。
//年龄大于18才可以看见
Title
Paragraph 1
Paragraph 2
不同的是带有
v-show
的元素始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的display
CSS property。注意,
v-show
不支持元素,也不支持
v-else
data () {
return {
age: 19,
sex: 'woman',
isShow: true
}
},
标签内容1v-if
标签内容2v-show
v-if
是“真正”的条件渲染,因为它会确保在切换过程中,条件块内的事件监听器和子组件适当地被销毁和重建。v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。我们可以用
v-for
指令基于一个数组来渲染一个列表。v-for
指令需要使用item in items
形式的特殊语法,其中 items 是源数据数组,而item
则是被迭代的数组元素的别名。
v-for
第二个参数,即当前项的索引。你也可以用
of
替代in
作为分隔符,因为它更接近 JavaScript 迭代器的语法。
在遍历对象时,会按
Object.keys()
的结果遍历,但是不能保证它在不同 JavaScript 引擎下的结果都一致。
person: ["zz", "aa"],
personObj: {
name: 'zz',
age: 19
}
- {{ item }}
-
{{ item }}-->{{ index + 1 }}
-
{{ item }}-->{{ index }}
-
{{ item }}-->{{ key }}-->{{ index }}
todos: [
{
content: "把项目做完",
complete: true,
},
{
content: "去超市购物",
complete: false,
},
{
content: "看10分钟的书",
complete: false,
},
],
-
{{todo.content}}
列表渲染 | Vue.js
当 Vue 正在更新使用
v-for
渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一的
key
。
addPerson: function () {
this.person.unshift('woa')
}
-
{{ item }}
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。
push(),
pop(),
shift(),
unshift(),
splice(),
sort(),
reverse()。
data () {
return {
list: [1, 2, 3, 4]
}
},
changeList: function () {
// 1.vue3可以,通过索引值去修改数组,触发视图更新
this.list[5] = 7 //1 2 3 4 7
// 2.push():给数组末尾添加元素 源码:...item-->[]
this.list.push(7, 8, 9)//1 2 3 4 7 8 9
// pop():给数组删除掉数组的最末尾的元素
this.list.pop()//1 2 3
// shift():给数组的第一位进行删除
this.list.shift()//2 3 4
// unshift():给数组首位开始添加元素 都是通过扩展运算符的方式,源码:...item-->[]
this.list.unshift(0, 9)//0 9 1 2 3 4
// splice():删除元素,插入元素,替换元素
// 第一个参数,表示开始插入或者开始删除的元素的位置的下标
// 1.删除元素
// 第二个参数,表示传入要删除几个元素(如果没有传,就删除后面所有的元素)
this.list.splice(1, 2)//删除从下标为1开始,删除两个//1 4
// 2.插入元素
// 第二个元素,传入0(表示没有元素要删除),并且后面
this.list.splice(1, 0, 7, 9, 6)//1 7 9 6 2 3 4
// 3.替换元素
// 第二个元素,表示我们要替换几个元素.后面的参数表示替换前面的元素
// sort():排序
this.list.sort()//1 2 3 4
this.list.splice(1, 3, 7, 7)//1 7 7
// reverse():翻转
this.list.reverse()//4 3 2 1
}
- {{ item }}
我们可以使用
v-on
指令 (通常缩写为@
符号) 来监听 DOM 事件,并在触发事件时执行一些 JavaScript。用法为v-on:click="methodName"
或使用快捷方式@click="methodName"
然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在
v-on
指令中是不可行的。因此v-on
还可以接收一个需要调用的方法名称。
data () {
return {
counter: 0
}
},
methods: {
addCounter (number) {
this.counter += number
}
},
{{ counter }}
{{ counter }}
{{ counter }}
实例
data() {
return {
showAnswer: false,
};
}
问:Vue 是一个什么样的框架?
答:Vue 是一套用于构建用户界面的渐进式框架。
除了直接绑定到一个方法,也可以在内联 JavaScript 语句中调用方法;有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量
$event
把它传入方法。
addCounter (number, e) {
this.counter += number
console.log(e)
}
{{ counter }}
事件处理程序中可以有多个方法,这些方法由逗号运算符分隔。
counter: 0,
age: 10
addCounter (number, e) {
this.counter += number
console.log(e)
},
addAge () {
this.age++
}
{{ counter }}--{{ age }}
在事件处理程序中调用
event.preventDefault()
或event.stopPropagation()
常用。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。为了解决这个问题,Vue.js 为v-on
提供了事件修饰符。
.stop; .prevent; .capture; .self;
.once; .passive
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。用
@click.prevent.self
会阻止元素本身及其子元素的点击的默认行为,而@click.self.prevent
只会阻止对元素自身的点击的默认行为。
data () {
return {
counter: 0,
age: 10
}
},
methods: {
divClick: function () {
console.log('父元素的展示')
},
btnClick: function () {
console.log('子元素的展示')
},//子元素的展示 父元素的展示 -->取消父元素-->子元素的展示
submitClick () {
console.log(' 提交数据成功')
},
onceClick () {
console.log('.once只触发一次回调')
}
},
在监听键盘事件时,我们经常需要检查特定的按键。Vue 允许为
v-on
或者@
在监听键盘事件时添加按键修饰符。
Vue 为最常用的键提供了别名:
.enter;.tab;.delete
(捕获“删除”和”“退格键);.esc;.space;.up;.down;.left;.right
keyUp () {
console.log('键盘被按下,数据提交成功')
}
你可以用 v-model 指令在表单
、
及
元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。v
-model
本质上不过是语法糖。它负责监听用户的输入事件来更新数据,并在某种极端场景下进行一些特殊处理。
v-model
在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
value
property 和 input
事件;checked
property 和 change
事件;value
作为 prop 并将 change
作为事件。return {
msg: "hello"
}
{{ msg }}
v-model原理
msg: "hello"
changeValue (e) {
console.log(e)
this.msg = target.value
}
data () {
return {
// checked: true
checked: '',
fruits: []
}
},
methods: {
changeValue (e) {
console.log(e)
this.msg = target.value
}
},
{{ checked }}
苹果
西瓜
菠萝
哈密瓜
草莓
水果{{ fruits }}
sex: '男'
男
女
{{ sex }}
city: 'a',
//
对于单选按钮,复选框及选择框的选项,
v-model
绑定的值通常是静态字符串 (对于复选框也可以是布尔值)。但是有时我们可能想把值绑定到当前活动实例的一个动态 property 上,这时可以用v-bind
实现,此外,使用v-bind
可以将输入值绑定到非字符串。(value可能是从网络上,后台拿到的值)
.lazy
在默认情况下,
v-model
在每次input
事件触发后将输入框的值与数据进行同步 (除了输入法组织文字时)。你可以添加lazy
修饰符,从而转为在change
事件之后进行同步。
msg: 'zz'
{{ msg }}
.number
如果想自动将用户的输入值转为数值类型,可以给
v-model
添加number
修饰符:当输入类型为text
时这通常很有用。如果输入类型是number
,Vue 能够自动将原始字符串转换为数字,无需为v-model
添加.number
修饰符。如果这个值无法被parseFloat()
解析,则返回原始的值。
counter: '0',//字符串类型
{{ typeof counter }}
.trim
如果要自动过滤用户输入的首尾空白字符,可以给
v-model
添加trim
修饰符:
methods: {
downmsg () {
console.log(this.msg)
}
},
{{ msg }}
组件是带有名称的可复用实例。
因为组件是可复用的实例,所以它们与根实例接收相同的选项,例如
data
、computed
、watch
、methods
以及生命周期钩子等。你可以将组件进行任意次数的复用。
通常一个应用会以一棵嵌套的组件树的形式来组织。
你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册。
全局注册的组件可以在应用中的任何组件的模板中使用。
import Content from './components/Content.vue'
components: {
Content: Content
},
//组件时带有名称的可复用的实例,单独功能模块的封装
为什么data要return的原因:
让每一个组件对象都返回一个新的对象,不会互相影响,不会造成数据污染。
app.vue
Content.vue
content组件
{{ msg }}
data () {
return {//返回的是新的数据
msg: 'zz'//只改变点击的那一个组件
}
const obj = {//几个组件用的时同一个数据
msg: 'aa'
}
export default {
data () {
return obj//改变一个,所有都会改变
},
}
向子组件传值,拿到父组件传递过来的值
app.vue
------------------------
------------------------
Content.vue
hello
{{ message }}
{{ aaa }}
输出结果
------------------------
hello
zz
123
------------------------
以字符串数组形式列出的 prop:
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
通常你希望每个 prop 都有指定的值类型。这时,你可以以对象形式列出 prop,这些 property 的名称和值分别是 prop 各自的名称和类型:
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // 或任何其他构造函数
}
//App.vue
msg: 124
//Content.vue
// 字符串数组
props:['message','aaa']
{{ message }}
{{ aaa }}
//App.vue
msg: 124
//type check failed for prop "message". Expected String with value "124", got Number with value 124. 应该写成字符串类型,因为下面有类型的限制
//Content.vue
props: {//对象
// 1.类型的限制
message: String
}
{{ message }}
//App.vue
msg: '124'
//Missing required prop: "message" at 必须要传值
//Content.vue
// 字符串数组
props: {
// 2.除了类型限制,还可以设置默认值,必传值
message: {
type: String,
default: 'nihao',
required:true//设置为true,表示必须要传值
}
}
{{ message }}
我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个要求没有被满足,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。为了定制 prop 的验证方式,你可以为
props
中的值提供一个带有验证要求的对象,而不是一个字符串数组。
//App.vue
list: [1, 2, 3, 4]
//Content.vue
// 字符串数组
props: {
list: {//对象或数组的默认值必须从一个工厂函数返回
type: Array,
default () {
return []
}
}
}
{{ list }}
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
监听子组件,然后获取子组件信息。
组件实例提供了一个自定义事件的系统来解决这个问题:
v-on
或 @
监听子组件实例的任意事件。有的时候用一个事件来抛出一个特定的值是非常有用的。
eg.我们可能想让
组件决定它的文本要放大多少。
1.这时可以使用 $emit
的第二个参数来提供这个值:
子组件
2.然后当在父级组件监听这个事件的时候,我们可以通过 $event
访问到被抛出的这个值:
父组件
或者,如果这个事件处理函数是一个方法:
3.那么这个值将会作为第一个参数传入这个方法:
methods: {
onEnlargeText(enlargeAmount) {
this.postFontSize += enlargeAmount
}
}
Content.vue
hello
{{ msg }}
{{message}}
App.vue
拿到子组件的data数据
mounted () {
console.log(this.$refs)//Proxy {hello: Proxy, p: h2}
console.log(this.$refs.hello.msg)//zz
},
Content.vue
mounted () {
// 尽量不使用$parent这个方法拿到父组件的数据,应使用props传值拿到
console.log(this.$parent)
console.log(this.$root)
},
和 HTML 元素一样,我们经常需要向一个组件传递内容。这可以通过使用 Vue 的自定义
元素来实现。使用
作为我们想要插入内容的占位符。
//App.vue
插槽
//Content.vue
有时我们需要多个插槽。
元素有一个特殊的 attribute:name
。通过它可以为不同的插槽分配独立的 ID,也就能够以此来决定内容应该渲染到什么地方。name
的
出口会带有隐含的名字“default”。
元素上使用 v-slot
指令,并以 v-slot
的参数的形式提供其名称。
元素中的所有内容都将会被传入相应的插槽。v-slot
只能添加在
上。App.vue
data () {
return {
message: 'hello'//最后显示的是hello
}
},
插槽
Content.vue
data () {
return {
message: nihao
}
},
当你想在一个插槽中使用数据时,例如:
Delete a {{ item.name }}
(父组件)该插槽可以访问与模板其余部分相同的实例 property (即相同的“作用域”)。
插槽不能访问
的作用域。例如,尝试访问 action
将不起作用:
Clicking here will {{ action }} an item
父级模板里的所有内容都是在父级作用域中编译的(所以里面的变量data还是属于父级作用域的,访问也只能访问父级作用域的);子模板里的所有内容都是在子作用域中编译的。
有时为一个插槽指定备用 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。
子组件定义了插槽,在父组件里面没有去写所对应的内容,来替换插槽的话,就可以使用备用内容。
我们可能希望这个 内绝大多数情况下都渲染“Submit”文本。
1.为了将“Submit”作为备用内容,我们可以将它放在
标签内(子组件):
2.当我们在一个父级组件中使用
并且不提供任何插槽内容时:
3.备用内容“Submit”将会被渲染:
但是如果父级提供内容:
Save
则这个提供的内容将会被渲染从而取代备用内容:
App.vue
//父组件没有提供内容
Content.vue
有时让插槽内容能够访问子组件中才有的数据是很有用的。父组件想拿到子组件的数据。
当一个组件被用来渲染一个项目数组时,这是一个常见的情况,我们希望能够自定义每个项目的渲染方式。
item
在父级提供的插槽内容上可用,我们可以添加一个
元素并将其作为一个 attribute 绑定。slot
上。
元素上的 attribute 被称为插槽 prop。现在,在父级作用域中,我们可以使用带值的 v-slot
来定义我们提供的插槽 prop 的名字。App.vue
{{ slotProps.list }}
{{ slotProps.message }}
- {{ item }}
- {{ item }}
Content.vue
data () {
return {
message: 'nihao',
list: [1, 2, 3]
}
},
当我们需要从父组件向子组件传递数据时,我们使用 props。想象一下这样的结构:有一些深度嵌套的组件,而深层的子组件只需要父组件的部分内容。在这种情况下,如果仍然将 prop 沿着组件链逐级传递下去,可能会很麻烦。
对于这种情况,我们可以使用一对
provide
和inject
。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。这个特性有两个部分:父组件有一个provide
选项来提供数据,子组件有一个inject
选项来开始使用这些数据。
Content.vue 子组件
{{ message }}
inject: ['message']
App.vue 父组件
data () {
return {
message: 'hello'
}
},
provide: { message: 'helo' },
components: {
Content: Content
},
结果:helo
// 如果我们尝试在此处 provide 一些组件的实例 property,这将是不起作用的,
//如果要访问组件实例 property,我们需要将 provide 转换为返回对象的函数
// provide: { message: this.message },// Cannot read properties of undefined (reading 'message')
provide () {
return {
message: this.message
}
},
结果:hello
方法1.传入响应式对象
App.vue 父组件法一
App--{{ obj.message }}
Content.vue 父组件
Content--{{ obj.message }}
结果点击后,子组件和父组件都发生改变
方法2.通过函数返回响应式数据
App.vue 父组件法二
data () {
return {
message: 'hello',
}
},
provide () {
return {
message:()=>this.message//2.函数返回响应式数据
}
},
App--{{ message }}
Content.vue 父组件
Content--{{ newMsg }}
Content--{{ newMsg }}
Content--{{ newMsg }}
computed: {
newMsg: function () {
return this.message()
}
},
inject: ['message']
c App.vue
data () {
return {
message: 'hello',
isShow: true
}
},
// 开发时应该将isShow=!isShow写在函数中,进行调用
methods: {
isShows () {
this.isShow = !this.isShow
}
},
components: {
Content: Content
},
content.vue
Content
{{ counter }}
data () {
return {
counter: 0
}
},
beforeCreate () {
console.log('beforeCreate')
},
created () {
console.log('created')
},
beforeMount () {
console.log('beforeMount')
},
mounted () {
console.log('mounted')
},
// 页面展示之前,以上四个都会执行
beforeUpdate () {
console.log('beforeUpdate')
},
updated () {
console.log('updated')
},
// 当页面发生改变时,以上会执行
beforeUnmount () {
console.log('beforeUmounted')
},
unmounted () {
console.log('unmounted')
}
}