VUE常用功能
一、安装项目
可以使用vue UI 工具创建一个项目,具体过程看以往文章: 使用VUE UI 创建前端项目,这里我使用的版本是vue3
运行项目
npm run serve
运行启动成功,点击控制台地址
项目初始版本启动完成
二、父子组件通讯
1.Props:父组件向子组件传递数据,子组件通过props属性接收数据
测试案例:添加一个测试页面,并在index.js,App.vue中添加路由
index.js中添加
{
path: '/test',
name: 'test',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../components/ChildParentTset.vue')
}
App.vue中添加
<template>
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<router-link to="/test">测试页面</router-link>
</nav>
<router-view/>
</template>
编写一个子组件,并接受来自父组件的数据
<template>
<div>
<h1>子组件</h1>
<p>子组件接收到的数据:{{ parentData }}</p>
</div>
</template>
<script>
export default {
props: {
parentData: String
}
}
</script>
引入子组件
<template>
<div>
<h1>引入子组件</h1>
<child-component :parentData="data"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
data () {
return {
data: '父组件传递的数据'
}
}
}
</script>
运作第一个案例,刷新页面查看
或者你可以这样传递数据
<!-- 根据一个变量的值动态传入 -->
<BlogPost :title="post.title" />
<!-- 根据一个更复杂表达式的值动态传入 -->
<BlogPost :title="post.title + ' by ' + post.author.name" />
传递不同类型的数据
export default {
props: {
title: String,
likes: Number
}
}
对于以对象形式声明的每个属性,key 是 prop 的名称,而值则是该 prop 预期类型的构造函数。比如,如果要求一个 prop 的值是 number 类型,则可使用 Number 构造函数作为其声明的值。
对象形式的 props 声明不仅可以一定程度上作为组件的文档,而且如果其他开发者在使用你的组件时传递了错误的类型,也会在浏览器控制台中抛出警告。
传递不同的值类型
在上述的两个例子中,我们只传入了字符串值,但实际上任何类型的值都可以作为 props 的值被传递。
Number
<!-- 虽然 `42` 是个常量,我们还是需要使用 v-bind -->
<!-- 因为这是一个 JavaScript 表达式而不是一个字符串 -->
<BlogPost :likes="42" />
<!-- 根据一个变量的值动态传入 -->
<BlogPost :likes="post.likes" />
Boolean
<!-- 仅写上 prop 但不传值,会隐式转换为 `true` -->
<BlogPost is-published />
<!-- 虽然 `false` 是静态的值,我们还是需要使用 v-bind -->
<!-- 因为这是一个 JavaScript 表达式而不是一个字符串 -->
<BlogPost :is-published="false" />
<!-- 根据一个变量的值动态传入 -->
<BlogPost :is-published="post.isPublished" />
Array
<!-- 虽然这个数组是个常量,我们还是需要使用 v-bind -->
<!-- 因为这是一个 JavaScript 表达式而不是一个字符串 -->
<BlogPost :comment-ids="[234, 266, 273]" />
<!-- 根据一个变量的值动态传入 -->
<BlogPost :comment-ids="post.commentIds" />
Object
<!-- 虽然这个对象字面量是个常量,我们还是需要使用 v-bind -->
<!-- 因为这是一个 JavaScript 表达式而不是一个字符串 -->
<BlogPost
:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
/>
<!-- 根据一个变量的值动态传入 -->
<BlogPost :author="post.author" />
使用一个对象绑定多个 prop
如果你想要将一个对象的所有属性都当作 props 传入,你可以使用没有参数的 v-bind,即只使用 v-bind 而非 :prop-name。例如,这里有一个 post 对象:
const post = {
id: 1,
title: 'My Journey with Vue'
}
以及下面的模版
<BlogPost v-bind="post" />
而实际上等价于
<BlogPost :id="post.id" :title="post.title" />
单向数据流
所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。如果子组件执意要修改则会警告
const props = defineProps(['foo'])
// ❌ 警告!prop 是只读的!
props.foo = 'bar'
prop的校验
defineProps({
// 基础类型检查
// (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
propA: Number,
// 多种可能的类型
propB: [String, Number],
// 必传,且为 String 类型
propC: {
type: String,
required: true
},
// 必传但可为空的字符串
propD: {
type: [String, null],
required: true
},
// Number 类型的默认值
propE: {
type: Number,
default: 100
},
// 对象类型的默认值
propF: {
type: Object,
// 对象或数组的默认值
// 必须从一个工厂函数返回。
// 该函数接收组件所接收到的原始 prop 作为参数。
default(rawProps) {
return { message: 'hello' }
}
},
// 自定义类型校验函数
// 在 3.4+ 中完整的 props 作为第二个参数传入
propG: {
validator(value, props) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
// 函数类型的默认值
propH: {
type: Function,
// 不像对象或数组的默认,这不是一个
// 工厂函数。这会是一个用来作为默认值的函数
default() {
return 'Default function'
}
}
})
2. $emit 和$on:子组件向父组件传递数据,子组件通过$emit触发事件,父组件通过$on监听事件并接收数据。
$emit():在当前组件触发一个自定义事件。任何额外的参数都会传递给事件监听器的回调函数。
子组件定义事件名,及传递参数
<template>
<div>
<button @click="handleClick">点击发送数据给父组件</button>
</div>
</template>
<script>
export default {
methods: {
handleClick () {
this.$emit('childEvent', '子组件传递的数据')
}
}
}
</script>
父组件声明事件,并接受参数数据的传递
<template>
<div>
<child-component @childEvent="handleChild"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
methods: {
handleChild (data) {
alert('父组件接收到的数据:' + data);
}
}
}
</script>
运行查看
传递多个参数
示例:
export default {
created() {
// 仅触发事件
this.$emit('foo')
// 带有额外的参数
this.$emit('bar', 1, 2, 3)
}
}
3.使用$refs.调用组件实例来传递
父组件
<template>
<div>
<button @click="handleClick">向子组件传递数据</button>
<child-component ref="child"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
methods: {
handleClick () {
this.$refs.child.handleData('父组件传递的数据')
}
}
}
</script>
子组件
<template>
<div>
子组件
</div>
</template>
<script>
export default {
methods: {
handleData (data) {
console.log('子组件接收到的数据:' + data)
}
}
}
</script>
刷新查看
4.依赖注入:Provide (提供),nject (注入)
Prop 逐级透传问题
通常情况下,当我们需要从父组件向子组件传递数据时,会使用 props。想象一下这样的结构:有一些多层级嵌套的组件,形成了一棵巨大的组件树,而某个深层的子组件需要一个较远的祖先组件中的部分数据。在这种情况下,如果仅使用 props 则必须将其沿着组件链逐级传递下去,这会非常麻烦:
provide 和 inject 可以帮助我们解决这一问题
provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
provide() 函数接收两个参数。第一个参数被称为注入名,可以是一个字符串或是一个 Symbol。后代组件会用注入名来查找期望注入的值。一个组件可以多次调用 provide(),使用不同的注入名,注入不同的依赖值。第二个参数是提供的值,值可以是任意类型,包括响应式的状态
父组件
<script setup>
import { ref, provide } from 'vue'
import GrandChild from './ChildComponent.vue'
const message = ref('hello')
provide('message', message)
</script>
<template>
<input v-model="message">
<GrandChild />
</template>
子组件
<script setup>
import { inject } from 'vue'
const message = inject('message')
</script>
<template>
<p>
Message to grand child: {{ message }}
</p>
</template>