发布时间:2023-12-27 18:30
重点说明:当前笔记内容侧重点为各个知识点的应用及需要注意的地方,而不是每个知识点的概念。概念相关请查看官网:https://cn.vuejs.org/v2/guide/installation.html vue入门教程。
代码及xmind源文件Gitee:https://gitee.com/zhiyaoyun/vue-project
代码下载地址:https://gitee.com/zhiyaoyun/vue-project.git
目的
处理模板中需要复杂逻辑计算的属性,简化模板中的表达式。
优点
a. 减少模板中的计算逻辑
b. 能够进行数据缓存,提高性能【和普通函数相比】
c. 响应式数据
使用
<h2>计算属性h2>
<p v-if=\"show\">{{fullName}}p>
<h2>函数获取h2>
<p v-if=\"show\">{{getFullName()}}p>
<script>
el: \"#app\",
data: {
firstName: \'yun\',
lastName: \'Zhiyao\',
show: true
},
computed: {
fullName() {
console.log(\"计算属性\")
return this.firstName + this.lastName;
}
},
methods: {
getFullName() {
console.log(\"函数获取\")
return this.firstName + this.lastName;
}
}
})
script>
目的: 执行一些复杂的逻辑,包括异步或者开销比较大的计算
和计算属性相比:
a. 更加的灵活、通用
b. 计算属性执行不了的逻辑可以在侦听器中进行执行,反之则不可
使用
<h2>侦听器h2>
<p>当前年龄:<input type=\"text\" v-model=\"age\">p>
<p>应该做的事情:{{doSomeThing}}p>
<p>应该做的事情:{{doSomeThing2}}p>
<script>
let vm = new Vue({
el: \"#app\",
data: {
age: 0,
doSomeThing: \'\',
},
computed: {
doSomeThing2(){
// 无效,不可以在计算属性中使用异步操作
setTimeout(()=>{
return this.age + \'岁的事情\'
},1000)
}
},
watch: {
age(newAge){
setTimeout(()=>{
this.doSomeThing = newAge + \'岁的事情\'
},1000)
}
}
})
script>
实现原理:为不同的元素监听的相对应的事件
a. input / textarea : value属性 + input事件
b. checkbox / radio:checked属性 + change 事件
c. select :value属性 + change事件
应用
<h1>使用v-modelh1>
<h3>message:{{message}}h3>
<input type=\"text\" v-model=\"message\">
<h1>v-model的实现原理h1>
<h3>message2:{{message2}} ; checkbox:{{checkbox}}h3>
<input type=\"text\" :value=\"message2\" @input=\"handleInput($event)\">
<input type=\"checkbox\" @change=\"handleChange($event)\">
<script>
const vm = new Vue({
el: \"#app\",
data: {
message: \"你好\",
message2:\'hello\',
checkbox:true,
},
methods:{
handleInput(event){
this.message2 = event.target.value
},
handleChange(event){
console.log(event)
this.checkbox = event.target.checked
}
}
})
script>
<h1>修饰符h1>
<input type=\"text\" v-model.lazy=\"message\">
<input type=\"text\" v-model.number=\"numberData\">
<input type=\"text\" v-model.trim=\"message\">
<p>{{total}}p>
<my-component v-model=\"total\">my-component>
<my-component @input=\"handleInputCom\">my-component>
Vue.component(\'my-component\', {
template: `<button @click=\"handleClick\">+1button>`,
data() {
return {
count: 0,
}
},
methods: {
handleClick() {
this.count++;
this.$emit(\"input\", this.count)
}
}
})
<h1>自定义组件使用-model2h1>
{{checkbox}}
<base-checkbox v-model=\"checkbox\">base-checkbox>
Vue.component(\'base-checkbox\', {
model: {
prop: \'checked\',
event: \'change\'
},
props: {
checked: Boolean
},
template: `
<input
type=\"checkbox\"
:checked=\"checked\"
@change=\"$emit(\'change\', $event.target.checked)\"
>`
})
组件在Vue中是一个非常重要的基础,主要有以下几点注意:
注册组件时,需要注意组件的命名方式,组件的命名有两种方式:
Vue.component(\'组件名称\',{组件配置项})
import MyComponent from \'./components/MyComponent\'
// 使用数组的方式接受props
props: [\'text\',\'mystyle\'],
// 使用对象的方式接受props
props:{
text:{
type:String
},
mystyle: {
type:Object
}
},
(a)prop只作为初始值,后续可能会在组件内部发生改变,但不会影响到父元素。这种情况下可以定义一个data属性接收prop传递过来的值,后续的改变在data属性上处理。
props:{
text:{
type:String
},
mystyle: {
type:Object
}
},
data(){
return {
buttonText:this.text
}
},
template:``,
methods:{
changeText(){
this.buttonText = \'自定义文字\'
}
}
(b)prop作为依赖源,当prop发生改变时,可能会对prop进行处理后响应显示。
// 接受到的prop属性
props:{
text:{
type:String
},
},
// 使用计算属性处理prop值
computed:{
buttonText2(){
return this.text.split(\'\').reverse().join(\'\')
}
},
// 验证类型
propA:Number,
// 可能会传递过来多种类型
propB:[String,Number],
// 验证是否为自定义类型的实例
propC:Person,
// 使用type属性验证
propD:{
type:Number
},
// 使用默认值,当没有传值过来时使用该值
propE:{
type:String,
default:\"默认值\"
},
// 验证必填项
propF:{
type:Number,
required:true
},
// 自定义验证函数,validatoe函数返回一个布尔值
propG:{
type:String,
validator(value){
return [\'success\', \'warning\', \'danger\'].indexOf(value) !== -1
}
},
// 子组件中定义的模板
template:`<div><button :style=\"mystyle\" @click=\"changeText\">{{buttonText2}}button>div>`
props:{
text:{
type:String
},
mystyle: {
type:[String , Number]
},
}
// 在调用子组件时
<base-button :text=\"text\" :mystyle=\"mystyle\" disabled data-name=\"button\"/>
// 渲染结果
<div disabled=\"disabled\" data-name=\"button\">
<button style=\"background: red;\">钮按击点button>
div>
(a)使用属性:inheritAttrs:false 先禁止根节点继承属性,使用该属性后,没有被接受的属性值则直接被忽略
// 子组件中定义的模板
template:`<div><button :style=\"mystyle\" @click=\"changeText\">{{buttonText2}}button>div>`
props:{
text:{
type:String
},
mystyle: {
type:[String , Number]
},
}
inheritAttrs:false
// 渲染结果
<div>
<button style=\"background: red;\">钮按击点button>
div>
(b)指定元素继承未被接受的组件:使用 v-bind = “$attrs”
template:`<div><button v-bind=\"$attrs\" :style=\"mystyle\" @click=\"changeText\">{{buttonText2}}button>div>`,
// 渲染结果
<div>
<button disabled=\"disabled\" data-name=\"button\" style=\"background: red;\">钮按击点button>
div>
props
具体使用方法同 3-2 组件传值
$emit / $on: $emit用于子组件向父组件传值
// 子组件 Header.vue
<template>
<div>
<h1 @click=\"changeTitle\">{{ title }}h1> //绑定一个点击事件
div>
template>
<script>
export default {
name: \'header\',
data() {
return {
title:\"hellow\"
}
},
methods:{
changeTitle() {
// 通过自定义事件,将自己的参数值传递给父组件
this.$emit(\"titleChanged\",\"子向父组件传值\");
}
}
}
script>
// 父组件接收值
<template>
<div id=\"app\">
// 与子组件titleChanged 自定义事件保持一致
// updateTitle($event)接受传递过来的参数值
<header @titleChanged=\"updateTitle\">header>
<h2>{{ title }}h2>
div>
template>
<script>
import Header from \"./components/Header\"
export default {
name: \'Parent\',
data(){
return{
title:\"传递的是一个值\"
}
},
methods:{
//声明这个函数
updateTitle(e){
this.title = e;
}
},
components:{
\"app-header\":Header,
}
}
script>
缺点:无法跨层级使用。$refs 只在组件渲染完成之后可以使用,而且是非响应式的。
说明:在日常开发过程中,应该避免直接使用这几个方法,这种方法使得组件之间的耦合性比较强。且数据流向不明确,代码不好维护。
// 模板
<div id=\"app\">
<h1>子组件1h1>
<child ref=\"child1\">child>
<h1>子组件2h1>
<child ref=\"child2\">child>
<h1>子组件3h1>
<child ref=\"child3\">child>
div>
<script>
Vue.component(\'Child\',{
template:`{{message}}{{parentData}}`,
data(){
return {
message:\'子组件\',
}
},
mounted(){
let parentMessage = this.$parent.parentMessage;
console.log(\"从父组件实例中获取数据\")
console.log(parentMessage);
},
computed:{
parentData(){
return this.$parent.parentMessage
}
}
})
const vm = new Vue({
el:\"#app\",
data:{
parentMessage:\'父组件\'
},
mounted() {
let childMessage = this.$children[0].message;
console.log(\"从子组件实例中获取数据\")
console.log(childMessage);
let childHref = this.$refs;
console.log(childHref)
},
})
script>
作用:允许一个祖先组件向其所有子孙后代注入一个依赖,不 论 组件层次有多深,并在起上下游关系成立的时间里始终生效。祖先组件中通过 provider 来 提供变量,然后在子孙组件中通过 inject 来注入变量
优点:主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系
// 基本的使用:这种情况下数据是不响应的
//父组件.vue
export default {
provide: {
color: \'red\'
}
// 子组件.vue
export default {
inject: [\'color\'],
mounted () {
console.log(this.color) //输出red
}
}
// 响应式方法1:直接传递整个父组件的实例过去,这样当父组件的值改变时,子组件接受到的数据也会改变
//父组件.vue
export default {
provide(){
return parent:this
}
}
// 子组件.vue
export default {
inject: [\'parent\'],
mounted () {
console.log(this.parent.color) //输出red
}
}
// 响应方法2:使用2.6+版本以后的 Vue.observable
//父组件.vue
export default {
provide(){
this.theme = Vue.observable({
color:\'red\'
})
return {
theme:this.theme
}
}
}
// 子组件.vue
export default {
inject: [\'theme\'],
mounted () {
console.log(this.theme.color) //输出red
}
}
** a t t r s : ∗ ∗ 包 含 了 父 作 用 域 中 不 被 p r o p 所 识 别 ( 且 获 取 ) 的 特 性 绑 定 ( c l a s s 和 s t y l e 除 外 ) 。 当 一 个 组 件 没 有 声 明 任 何 p r o p 时 , 这 里 会 包 含 所 有 父 作 用 域 的 绑 定 ( c l a s s 和 s t y l e 除 外 ) , 并 且 可 以 通 过 v − b i n d = \" attrs:** 包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个 组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=\" attrs:∗∗包含了父作用域中不被prop所识别(且获取)的特性绑定(class和style除外)。当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(class和style除外),并且可以通过v−bind=\"attrs\" 传入内部组件。通常配合 interitAttrs 选项一起使用。
**KaTeX parse error: Unexpected character: \'\' at position 60: …n 事件监听器。它可以通过 v̲on=\"listeners\" 传入内部组件
具体使用方式可查看3-2 prop传值中
slot:插槽,需要动态的向组件中传递内容时,可以使用插槽传递。根据业务使用场景,插槽主要分为以下几类:
- 默认插槽
- 具名插槽
- 作用域插槽:作用域插槽编译时作用域和组件调用同级,所以不能访问组件内部的作用域。
const vm = new Vue({
el:\"#app\",
data:{
message:\'这是一个父组件\',
},
})
<!--在子组件中使用slot标签占位-->
Vue.component(\'ChildMiddle\',{
template:`
{{message}}
`,
data(){
return {
message:\'默认插槽\',
}
},
})
// 使用子组件时
<h1>父组件信息</h1>
<p>{{message}}</p>
// 直接插入信息
<child-middle>
你好,默认插槽
</child-middle>
v-slot:footer,v-slot的缩写 #
/具名插槽
Vue.component(\'Child2\',{
template:`
{{message}}
组件内容
组件内容2
`,
data(){
return {
message:\'具名插槽\',
}
},
})
// 调用组件时
<child2>
<template v-slot:header>
<div>header</div>
</template>
<template v-slot:default>
<div>default</div>
</template>
<template #footer>
<div>使用缩写footer</div>
</template>
</child2>
// 作用域插槽,传递了info数据和user数据
Vue.component(\'Child3\',{
template:`
{{message}}
组件内容
组件内容2
`,
data(){
return {
message:\'作用域插槽\',
info:\'info属性子组件信息\',
user:{
name:\'slot\',
version:2.6
}
}
},
})
// 调用子组件时,接收到的是props对象,可以单独一个命名,也可以自己把需要的字段解构出来
<child3>
<template #header=\"{info}\">
<div>{{info}}</div>
</template>
<p>default</p>
<template #footer=\"{user}\">
<div>{{user.name}} - {{user.version}}</div>
</template>
</child3>
Vue.component(\'ChildCom\', {
name: \'child-com\',
template: `
{{ message }}+{{count}}
`,
props:[\'count\'],
data() {
return {
message: \'递归组件\',
}
},
})
const vm = new Vue({
el:\"#app\",
data:{
message:\'这是一个父组件\',
count:0
},
})
// 定义的内联模板组件
Vue.component(\'ChildCom2\', {
name: \'child-com2\',
data() {
return {
msg: \'内联模板\',
}
},
})
// 使用内联模板组件
<child-com2 inline-template>
<div>
<div>里面内容使用内联模板渲染</div>
<p>{{msg}}</p>
</div>
</child-com2>
// 定义多个组件
Vue.component(\'comA\', {
template: `组件A`,
})
Vue.component(\'comB\', {
template: `组件B`,
})
Vue.component(\'comC\', {
template: `组件C`,
})
// 定义父组件
const vm = new Vue({
el: \"#app\",
data: {
currentView:\'comA\',
},
methods:{
changeCom(comName){
this.currentView = comName;
}
}
})
// 动态展示各个组件
<component :is=\"currentView\"></component>
<button @click=\"changeCom(\'comA\')\">comA</button>
<button @click=\"changeCom(\'comB\')\">comB</button>
<button @click=\"changeCom(\'comC\')\">comC</button>
<button @click=\"changeCom(\'comD\')\">comD</button>
<keep-alive>
<component :is=\"currentView\">component>
keep-alive>
// 定义一个异步组件
Vue.component(\'comE\',function (resolve,reject){
window.setTimeout(function (){
resolve( {
template: `{{message}}`,
data(){
return {
message:\'异步组件,5000后渲染\'
}
}
})
},5000)
})
// 使用异步组件
<com-e></com-e>