组件的三大组成部分 - 注意点说明
①结构 < template >:只能有一个根元素
②样式 < style >:
全局样式(默认):影响所有组件
局部样式:scoped
样式下,只作用于当前组件
③逻辑 < script >:el根实例独有,data是一个函数,其他配置项一致
(下面将详细的逐一地学一下②③部分)
默认情况:写在组件中的样式会 全局生效→ 因此很容易造成多个组件之间的样式冲突问题。
scoped原理:
//BaseOne.vue
<template>
<div>BaseOnediv>
template>
<script>
export default{
}
script>
<style>
/* 默认的style样式,会作用于全局 */
div {
border: 3px solid blue;
margin: 30px;
}
style>
//BaseTwo.vue
<template>
<div>BaseTwodiv>
template>
<script>
export default{
}
script>
<style>
style>
//App.vue
<template>
<div id="app">
<BaseOne>BaseOne>
<BaseTwo>BaseTwo>
div>
template>
<script>
import BaseOne from './components/BaseOne.vue';
import BaseTwo from './components/BaseTwo.vue'
export default{
name:'App',
components:{
BaseOne:BaseOne,
BaseTwo:BaseTwo
}
}
script>
<style>
style>
效果图如下
加上scoped进行修改
<style scoped>
/* 1.默认的style样式,会作用于全局
2.加上scoped属性的style样式,只会作用于当前组件局部样式
组件应该有着自己独立的样式,推荐加上scoped
*/
div {
border: 3px solid blue;
margin: 30px;
}
style>
效果图如下
①一个组件如果要提供数据,也是通过data
②一个组件的data
选项必须是一个函数
。 保证每一个组件实例,维护独立
的一份数据对象。
每次创建新的组件实例,都会新执行一次data函数,得到一个新的对象
<template>
<div class="base-count">
<button @click="count--">-button>
<span>{{ count }}span>
<button @click="count++">+button>
div>
template>
<script>
export default{
// data必须是一个函数 → 保证每个组件实例
data (){
return{
count: 999
}
}
}
script>
<style scoped>
.base-count{
margin: 20px;
}
style>
组件通信,就是指组件与组件之间的数据传递
思考:1.组件之间有哪些关系? 2.对应的组件通信方案有哪几类?
<template>
<div style="border:3px solid #000;margin: 10px;">
我是App组件
<Son :title="myTitle">Son>
div>
template>
<script>
import BaseSon from './components/BaseSon.vue'
export default{
data () {
return {
myTitle:'黑马程序员'
}
},
components:{
BaseSon:BaseSon,
}
}
script>
<style>
style>
<template>
<div style="border:3px solid #000;margin: 10px;">
我是组件Son {{ title }}
div>
template>
<script>
export default{
// 2.通过props进行接受,且props里面的名字要和老爹里面要传过来的东西同名
props:['title']//3.这边接收完毕,页面中就可以直接渲染使用了
}
script>
<style>
style>
<template>
<div style="border:3px solid #000;margin: 10px;">
我是App组件
<Son :title="myTitle" @changeTitle="handelChange">Son>
div>
template>
<script>
import BaseSon from './components/BaseSon.vue'
export default{
data () {
return {
myTitle:'黑马程序员'
}
},
components:{
BaseSon:BaseSon,
},
methods:{
//3.提供处理函数,提供逻辑
handelChange (newTitle) {
this.myTitle = newTitle
}
}
}
script>
<style>
style>
<template>
<div style="border:3px solid #000;margin: 10px;">
我是组件Son {{ title }}
<button @click="changeFn">修改titilebutton>
div>
template>
<script>
export default{
props:['title'],
methods:{
changeFn () {
//1.通过$emit,向父组件发送消息通知
this.$emit('changeTitle','传智教育')
}
}
}
script>
<style>
style>
什么是prop
Prop定义:组件上注册一些自定义属性
prop作用:向子组件传递数据
特点:
- 可以传递任意数量的
prop
- 可以传递任意类型的
prop
写法:1.在父组件中提供一些数据(字符串,对象,布尔值等)
2.然后通过给当前这个组件以添加属性的方式传值
3.传了之后,在子组件内部,就可以通过props
接收
4.最终一面当中就可以做渲染了
App.vue
<template>
<div class="app">
<UserInfo
:username="username"
:age="age"
:isSingle="isSingle"
:car="car"
:hobby="hobby"
>UserInfo>
div>
template>
<script>
import UserInfo from './components/UserInfo.vue'
export default {
data() {
return {
username: '小帅',
age: 28,
isSingle: true,
car: {
brand: '宝马',
},
hobby: ['篮球', '足球', '羽毛球'],
}
},
components: {
UserInfo,
},
}
script>
<style>
style>
UserInfo.vue
<template>
<div class="userinfo">
<h3>我是个人信息组件h3>
<div>姓名:{{ username }}div>
<div>年龄:{{ age }}div>
<div>是否单身:{{ isSingle ? 'yes' : 'no' }}div>
<div>座驾:{{ car.brand }}div>
<div>兴趣爱好:{{ hobby.join('、') }}div>
div>
template>
<script>
export default {
props:['username','age','isSingle','car','hobby']
}
script>
<style>
.userinfo {
width: 300px;
border: 3px solid #000;
padding: 20px;
}
.userinfo > div {
margin: 20px 10px;
}
style>
思考:组件的prop可以乱传吗?(不可以,比如一个百分比数字不可以给它传布尔值)
作用:为组件的prop指定验证要求,不符合要求,控制台就会有错误提示 → 帮助开发者快速发现错误
语法:
①类型校验
②非空校验
③默认值
④自定义校验
需要将原来props接收数据的写法改成对象,对象里面就可以写键值对了。值就是对应的类型要求:比如传进来的是Number类型,就写number
App.vue
<template>
<div class="app">
<BaseProgress :w="width">BaseProgress>
div>
template>
<script>
import BaseProgress from './components/BaseProgress.vue'
export default {
data() {
return {
width: 'abc',
}
},
components: {
BaseProgress,
},
}
script>
<style>
style>
BaseProgress.vue
<template>
<div class="base-progress">
<div class="inner" :style="{ width: w + '%' }">
<span>{{ w }}%span>
div>
div>
template>
<script>
export default {
// props: ["w"],
props:{
w:Number//Number String Boolean Array Object Function
}
// 1.基础写法(类型校验)
// 2.完整写法(类型、是否必填、默认值、自定义校验)
}
script>
<style scoped>
省略
style>
<script>
export default {
// props: ["w"]
//1.基础校验(类型校验)
// props:{
// w:Number//Number String Boolean Array Object Function
// }
// 2.完整写法(类型、是否必填、默认值、自定义校验)
props:{
w:{
//对类型有要求
type:Number,
required:true,//需要非空
default:0,//如果希望有一个默认值。而不是必填
validator (value){//写这样一个方法,这个方法的形参,是可以拿到
//你传递过来的prop传值的,拿到传值,就可以对他进行判断
if (value >=0 && value <=100) {
return true
}
return false
}
}
}
}
script>
共同点:都可以给组件提供数据
区别:
App.vue
<template>
<div class="app">
<BaseCount>BaseCount>
div>
template>
<script>
import BaseCount from './components/BaseCount.vue'
export default {
components:{
BaseCount
},
data(){
return {
count:100
}
},
methods:{
}
}
script>
<style>
style>
erzi
<template>
<div class="base-count">
<button @click="count--">-button>
<span>{{ count }}span>
<button @click="count++">+button>
div>
template>
<script>
export default {
// 1.自己的数据随便修改 (谁的数据 谁负责)
// 意思就是,打开网页,想加就加,想减就减
// data () {
// return {
// count: 100,
// }
// },
// 2.外部传过来的数据(由父组件提供) 不能随便修改
props:{
count:Number
}
}
script>
<style>
.base-count {
margin: 20px;
}
style>
上面的代码,数据由父组件提供,一保存会直接报错(外部数据不能直接改)
需要提供对应的函数
app.vue
<template>
<div class="app">、
<BaseCount
@changeCount="handleChange"
>BaseCount>
div>
template>
<script>
import BaseCount from './components/BaseCount.vue'
export default {
components:{
BaseCount
},
data(){
return {
count:666
}
},
methods:{
handleChange (newCount) {
this.count = newCount
}
}
}
script>
<style>
style>
~.vue
<template>
<div class="base-count">
<button @click="handleSub">-button>
<span>{{ count }}span>
<button @click="handleAdd">+button>
div>
template>
<script>
export default {
// 1.自己的数据随便修改 (谁的数据 谁负责)
// 意思就是,打开网页,想加就加,想减就减
// data () {
// return {
// count: 100,
// }
// },
// 2.外部传过来的数据(由父组件提供) 不能随便修改
//单项数据流:父组件的prop更新,会单向的向下流动,影响到子组件
props:{
count:Number
},
methods:{
handleAdd(){
//通过子传父 this.$emit(事件名,参数)
//告诉老爹改数字,然后在老爹里面需要监听一下
this.$emit('changeCount',this.count + 1)
},
handleSub(){
this.$emit('changeCount',this.count - 1)
}
}
}
script>
<style>
.base-count {
margin: 20px;
}
style>
作用:非父子组件之间,进行简易消息传递。(复杂场景 → Vuex)
1.创建一个都能访问到的事件总线(空 Vue实例)→ utils/EventBus.js
impprt Vue for 'vue'
const Bus = new Vue()
export default Bus
2.A组件(接收方),监听Bus实例的事件监听事件(订阅消息)
created () {
Bus.$on('sendMsg',(msg) =>{
this.msg = msg
})
3.B组件(发送方),触发Bus实例的事件
Bus.$emit('sendMsg','这是一个消息')
provide & inject 作用:跨层级共享数据
1.父组件`provide``提供数据
(provide写成一个函数,在里面return,return就是共享的数据,里面可以写多个数据【简单(string)/复杂(对象)类型】)
2.你在父组件中共享了数据,在孙组件中想要接收,直接写inject,想接收谁就写谁的名字,一旦接收好了,就可以在孙组件的上方直接渲染
3.共享数据的时候,通常会把它包成的一个对象,一复杂类型(响应式)的方式向下共享
export default {
provide () {
return {
//普通类型【非响应式】
colour:this.color,
//复杂类型【响应式】
userInfo:this.userInto,
}
}
}
子/孙组件(无论多少层)
inject
取值使用(直接写你要接收的属性名就可以了,将来也可以通过this
直接访问)
export default {
inject: ['color','userInfo'],
created () {
console,log(this.color,this.userInfo)
}
}
注:也可以接收多个数据 {{ xxx }} {{ xxx }} {{ xxx }}
App.vue
<template>
<div class="app">
我是APP组件
<button @click="change">修改数据button>
<SonA>SonA>
<SonB>SonB>
div>
template>
<script>
import SonA from './components/SonA.vue'
import SonB from './components/SonB.vue'
export default {
provide() {
return {
// 简单类型 是非响应式的
color: this.color,
// 复杂类型 是响应式的
userInfo: this.userInfo,
}
},
data() {
return {
color: 'pink',
userInfo: {
name: 'zs',
age: 18,
},
}
},
methods: {
change() {
this.color = 'red'
this.userInfo.name = 'ls'
},
},
components: {
SonA,
SonB,
},
}
script>
<style>
lue
style>
~.vue
<template>
<div class="grandSon">
我是GrandSon
{{ color }} -{{ userInfo.name }} -{{ userInfo.age }}
div>
template>
<script>
export default {
inject: ['color', 'userInfo'],
}
script>
<style>
lue
style>
原理:v-model本质上是一个语法糖。例如应用在输入框上,就是value属性
和input事件
的合写
作用:提供数据的双向绑定
①数据变,视图跟着变:value
②视图变,数据跟着变@input
注意:$event
用于在模板中,获取事件的形参
=:value="msg" +
@input="msg = $event.target.value"
type=“text”>
<template>
<div class="app">
<input v-model="msg1" type="text" />
<br />
<input :value="msg2" @input="msg2 = $event.target.value" type="text" > <br><br>
div>
template>
<script>
export default {
data() {
return {
msg1: '',
msg2:''
}
},
}
script>
1.表单类组件封装 → 实现 子组件 和 父组件数据的 双向绑定
①父传子:数据应该是父组件props传递过来的,v-model拆解 绑定数据
②子传父:监听输入,子传父值给父组件修改
App.vue
<template>
<div class="app">
<BaseSelect
:cityId="selectId"
@changeId="selectId= $event"
>
BaseSelect>
div>
template>
<script>
import BaseSelect from './components/BaseSelect.vue'
export default {
data() {
return {
//数据应该由老爹提供
selectId: '102',
}
},
components: {
BaseSelect,
},
methods: {
handleChange(e) {
console.log(e);
this.selectId = e
}
}
}
script>
<style>
style>
~vue
<template>
<div>
<select :value="cityId" @change="handleChange">
<option value="101">北京option>
<option value="102">上海option>
<option value="103">武汉option>
<option value="104">广州option>
<option value="105">深圳option>
select>
div>
template>
<script>
export default {
// 在子组件中用props接收
props:{
cityId:String
},
// 写处理函数
methods:{
handleChange (e) {
this.$emit('changeId',e.target.value)
}
}
}
script>
<style>
style>
2.父组件v-model 简化代码,实现 子组件 和 父组件数据 双向绑定
核心步骤
①子组件中:props通过value接收,事件触发 input
②父组件中:v-model给组件直接绑数据(:value+@input
)
通过value进行接收,然后设置给上面的select,通过value往下传递,监听下拉菜单的改变,然后触发事件提交数据时,触发事件提交数据的是`input``
App.vue
<template>
<div class="app">
<BaseSelect
v-model="selectId"
>
BaseSelect>
div>
template>
<script>
import BaseSelect from './components/BaseSelect.vue'
export default {
data() {
return {
//数据应该由老爹提供
selectId: '102',
}
},
components: {
BaseSelect,
},
}
script>
~.vue
<template>
<div>
<select :value="value" @change="handleChange">
<option value="101">北京option>
<option value="102">上海option>
<option value="103">武汉option>
<option value="104">广州option>
<option value="105">深圳option>
select>
div>
template>
<script>
export default {
// 在子组件中用props接收
props:{
value:String
},
// 写处理函数
methods:{
handleChange (e) {
this.$emit('input',e.target.value)
}
}
}
script>
作用:可以实现 子组件 与 父组件数据 的双向绑定,简化代码
特点:prop属性名,可以自定义,非固定为value
场景:封装弹框类的基础组件,visible属性
true
显示 false
隐藏
//father
<BaseDialong :visible.sync = "isShow">
<BaseDialog
:visible="isShow"
@updata:visible="isShow = $event"
>
//son
<script>
props:{
visible:Boolean
},
this.$emit('updata:visible',false)
script>
作用:利用ref和¥refs可以用于获取dom元素,或组件实例
特点:查找范围 → 当前组件内(更精确稳定)
①获取dom:1.目标标签-添加ref属性
②恰当时机,通过this.$refs.xxx
,获取目标标签
mounted () {
console.log(this.$refs.chartRef)
},
App.vue
<template>
<div class="app">
<div class="base-chart-box">
这是一个捣乱的盒子
div>
<BaseChart>BaseChart>
div>
template>
~vue
<template>
<div ref="mychart" class="base-chart-box">子组件div>
template>
<script>
import * as echarts from 'echarts'
export default {
mounted() {
//const myChart = echarts.init{document.querySelect'base-chart-box'}//这个会找到父组件中的盒子
//但我们想找当前组件范围内的盒子,用ref
// 基于准备好的dom,初始化echarts实例
const myChart = echarts.init(this.$ref.mychart)
// 绘制图表
}
script>
1.目标组件 - 添加ref属性
2.恰当时机,通过this.$refs.xxx
,获取目标组件,就可以调用组件对象里面的方法
this.$refs.baseFrom.组件方法()
App.vue
<template>
<div class="app">
<BaseForm ref="baseFrom">BaseForm>
<button @click="handleGet">获取数据button>
<button @click="handleReset">重置数据button>
div>
template>
<script>
import BaseForm from './components/BaseForm.vue'
export default {
data (){
return {
}
},
components: {
BaseForm,
},
methods: {
handleGet () {
console.log(this.$refs.baseForm.getValue())
},
handleReset () {
this.$refs.baseForm.resetValues()
}
}
}
script>
~。vue
<template>
<div class="app">
<div>
账号: <input v-model="username" type="text">
div>
<div>
密码: <input v-model="password" type="text">
div>
<div>
<button @click="getFormData">获取数据button>
<button @click="resetFormData">重置数据button>
div>
div>
template>
<script>
export default {
data() {
return {
account: '',
password: '',
}
},
methods: {
//方法1:收集表单数据,返回一个对象
getValues() {
retyrn () {
account: this.account,
password:this.password
}
},
//方法2:重置表单
resetValues() {
this.account = ''
this.password = ''
console.log('重置表单数据成功');
},
}
}
script>
需求:编辑标题,编辑框自动聚焦
1.点击编辑,显示编辑框
2.让编辑框,立刻获取焦点
this.isShowEdit = true //显示输入框
this.$refs.inp.focus() //获取焦点
问题:“显示之后”,立刻获取jiajiao点不能成功显示
原因:Vue是 异步更新DOM(提升性能)
<template>
<div class="app">
<div v-if="isShowEdit">
<input v-model="editValue" type="text" ref="inp" />
<button>确认button>
div>
<div v-else>
<span>{{ title }}span>
<button @click="handleEdit">编辑button>
div>
div>
template>
<script>
export default {
data() {
return {
title: '大标题',
isShowEdit: false,
editValue: '',
}
},
methods: {
handleEdit(){
//显示输入框(异步dom更新)
this.isShowEdit = true
//让输入框获取焦点
this.$nextTick(()=>{
console.log(this.$refs.inp)
this.$refs.inp.focus()
})
// setTimeout(() => {
// this.$refs.inp.focus()
// },1000)
}
}
}
script>
<style>
style>