携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情
前言
Vue和React属于当下主流框架,初学者(包括我)在选择的时候可能会犯难,我给个个人建议:初学选啥都一样,看个人爱好,想UI和JS一起写的推荐React,想数据渲染简单一点的选择React,两者在编码上存在 互相借鉴 ,都推荐使用Hooks编码,原理也大差不差,会其中一个,另一个在花个一周时间可以很快上手,相当于买一送一,很实惠了。
本系列主要介绍以下模块:
- 1.基础模版语法指令
- 2.比较常用的特性
- 3.组件化开发
- 4.前后端的一些交互功能
- 5.一些路由知识
组件化的开发
全局注册和局部注册
组件基础-官方文档 ,作为Vue最强大的功能之一,学习它着实用了我不少时间。我理解的组件为:类似于页面中导航栏,回到顶部的模块功能,它把HTML内容结构,CSS外观样式,JS动画特效,封装在一起成为的一个功能,就叫组件。这样在开发项目时,哪需要直接引入即可。
下面看个 全局组件 的基本样例模版:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<!-- 2. 调用组件:【注意】这里的组件可任意次复用,也就是说每个组件相互独立互不影响的 -->
<mynav></mynav>
<mynav></mynav>
<mynav></mynav>
</div>
<script>
// 1. 通过vue类注册组件,“mynav”为自定义标签名
Vue.component("mynav", {
// 模版必须是单个根元素
template:`<div>
<button @click="sub">-</button>
<input type="text" v-model="num">
<button @click="add">+</button>
</div>`,
data(){ // 组件里面的变量数据,【注意】data返回的必须是一个函数,正因为这条规则,上述的组件才相互独立
return {
num: 0
}
},
methods:{ // 组件里面的方法
sub(){
this.num--;
},
add(){
this.num++;
}
}
});
// 创建根实例
var vm = new Vue({
el:'#app',
data:{},
})
</script>
</body>
</html>
为了能让Vue识别和在模版中使用组件,所以组件分为全局和局部,全局通过Vue.component注册,局部通过new Vue注册,这里面有很多小细节需要注意,比如上述的全局组件(data返回值,组件模版要求,甚至组件名命名使用的限制),下面再说 局部注册 :
<div id="app">
<my-component></my-component>
</div>
<script>
// 定义组件的模板
var Child = {
template: '<div>局部注册</div>'
}
new Vue({
//局部注册组件
components: {
// <my-component> 将只在父模板可用 一定要在实例上注册了才能在html文件中使用
'my-component': Child
}
})
</script>
局部注册组件只能在当前注册它的vue实例中才能使用,不过值得注意的是,局部注册的组件在其子组件中不可用 ,怎么理解这句话,就是说如果你定义了好几个局部组件,组件间是不通的,像局部函数间不能直接访问一样,要通的话得换个写法,如
var ComponentA = { /* ... */ }
var ComponentB = {
components: {
'component-a': ComponentA
},
// ...
}
各不相同的组件中有着父子,兄弟之类关系,这使得在数据交互方面得分门别类,下面将说道父组件,子组件,兄弟组件俩俩之间的传值。
vue2 组件传值
1.父子传值 props
父组件引入子组件后,在data中定义所传参数,在子组件标签直接传递,子组件通过props接收即可
<!-- 父组件 -->
<div class="hello">
<h1>父组件</h1>
<sonVue :msg="msg"></sonVue>
</div>
import sonVue from "./son.vue";
export default {
data () {
return {
msg: 11
};
},
components: {
sonVue
}
};
<!-- 子组件 -->
{{ msg }}
export default {
// 1.以数组方式接受
// props: ["msg"],
// 2.以对象形式接收
props: {
msg: {
typeof: Number,
default: 3
}
}
}
2.子父传值:$emit('事件名','数据')
在传值组件定义个自定义事件名称,接收组件通过@接收在触发即可
//传值组件
<button @click="sayLove">emit子向父传值</button>
methods: {
sayLove () {
this.$emit('sayLove', "生日快乐")
}
}
//接收组件
<sonVue :msg="msg" @sayLove="getLove"></sonVue>
getLove (e) {
console.log(e);
}
3.兄弟传值:在Vue原型上再定义个新实例,通过emit和on传递
//1.新定义个实例(好几种方法)
// 方法一
// 抽离成一个单独的 js 文件 Bus.js ,然后在需要的地方引入
import Vue from "vue"
export default new Vue()
// 方法二 在main.js直接挂载到全局
Vue.prototype.$bus = new Vue()
// 方法三 在main.js注入到 Vue 根对象上
import Vue from "vue"
new Vue({
el:"#app",
data:{
Bus: new Vue()
}
}).$mount('#app')
//2.在需要传值的组件上通过$emit事件传参
this.$bus.$emit('sayEat', "吃饭")
//3.接收组件,通过on方法接收自定义事件
created () {
this.$bus.$on('sayEat', this.getSonLove)
},
methods: {
getSonLove (ee) {
console.log(ee);
}
}
4.爷孙传值provide / inject
provide:可以让我们指定想要提供给后代组件的数据或方法
inject:在任何后代组件中接收想要添加在这个组件上的数据或方法
但是!!!
在传值组件中,provide:{ }这样使用不能获取 methods 中的方法,provide(){ return{} }使用不能获取data中的属性。
// 1.在传值组件中
export default{
// 方法一 不能获取 methods 中的方法
provide:{
name:"nanmo",
age: this.data中的属性
},
// 方法二 不能获取 data 中的属性
provide(){
return {
name:"nanmo",
someMethod:this.someMethod
//也可以将爷组件整个传入,但不推荐:father:this
}
},
methods:{
someMethod(){
console.log("传个方法")
}
}
}
// 后代接收数据组件
export default{
inject:["name","someMethod"],
mounted(){
console.log(this.name)
this.someMethod()
}
}
4.slot插槽传值了,hhhh
引入插槽后直接点出插槽数据即可
// Child.vue
<template>
<div>
<slot :user="user"></slot>
</div>
</template>
export default{
data(){
return {
user:{ name:"nanmo" }
}
}
}
// Parent.vue
<template>
<div>
<child v-slot="user">
{{ user.name }}
</child>
</div>
</template>
方法肯定不止上述这些,笔者只是写了日常中比较常用的方案,更多请移步掘金。
vue3.2 组件传值
1.父子传值,props
很重要的一个API,贴个超链,官方介绍
跟vue2差不多,只是语法变了,在 <script setup> 中必须使用 defineProps API 来声明 props,不要要额外引入。
//父组件
<template>
<Child :msg="message" />
</template>
<script setup>
import Child from './components/Child.vue'
let message = 'nanmo'
</script>
// 子组件
<template>
{{ msg }}
</template>
<script setup>
const props = defineProps({
msg: {
type: String,
default: ''
}
})
console.log(props.msg) // 在 js 里需要使用 props.xxx 的方式使用。在 html 中使用不需要 props
</script>
2.子父传值,emits
还是跟vue2差不多,以上述为例子
<div>父组件:{{ message }}</div>
<Child @changeMsg="changeMessage" />
let message = ref('nanmo')
function changeMessage(data) {
message.value = data
}
子组件:<button @click="handleClick">子组件的按钮</button>
const emit = defineEmits(['changeMsg'])
function handleClick() {
emit('changeMsg', '南漠')
}
这里可以用 v-model 语法糖实现相同效果,我觉得其本质还是使用了emits传值
<Child v-model="message" />
//子组件
<div @click="handleClick">{{modelValue}}</div>
//1.接收父组件使用 v-model 传进来的值,必须用 modelValue 这个名字来接收
const props = defineProps([
'modelValue'
])
//2.必须用 update:modelValue 这个名字来通知父组件修改值
const emit = defineEmits(['update:modelValue'])
function handleClick() {
emit('update:modelValue', '南漠')
}
//更简单的还能这样,传值过程直接卸载标签内
<div @click="$emit('update:modelValue', '南漠')">{{modelValue}}</div>
const props = defineProps([
'modelValue'
])
3.子父传值,expose / ref
这个比较简单,一个把数据暴露出去,一个ref拿就好
子组件可以通过 expose 暴露自身的方法和数据,父组件通过 ref 获取到子组件并调用其方法或访问数据。
是不是有点像vue2的 provide / inject , 哈哈
<div>父组件:拿到子组件的message数据:{{ msg }}</div>
<button @click="callChildFn">调用子组件的方法</button>
<Child ref="com" />
const com = ref(null) // 通过ref拿到子组件实力,后续.出数据来就行
const msg = ref('')
onMounted(() => {
msg.value = com.value.message
})
//子组件使用 defineExpose 向外暴露指定的数据和方法,一样defineExpose 不需要额外引入
defineExpose({
message,
changeMessage
})
4.Vue3当然可以使用插槽,用法和Vue2一样
5.provide / inject,用法和Vue2一样
6.总线Bus,用法和Vue2一样
3.5、 组件插槽
插槽-官方文档 ,缩写 "#" , 简而言之就是父组件向子组件传递内容(模版的内容):子组件会有一个插槽 <slot></slot> ,用来接收父组件标签的内容。基本用法大致为:
- 插槽在
template下装入<slot>默认内容</slot>插槽;如果有默认内容的话会直接输出到插槽上 - 要插入的标签的内容:
<alert-box>要插槽的内容</alert-box>
基本使用: 写个插槽文件,在需要的地方import 插槽名字 from 路径 导入,slot的内容会被更新。 v-slot: 可以使用#替换
<!-- MySlotCpn组件 -->
<template>
<div class="my-slot-cpn">
<h1>组件开始</h1>
<slot>默认内容</slot>
<h1>组件结束</h1>
</div>
</template>
<!-- App组件 -->
<template>
<div class="app">
<MySlotCpn>
<i>Hello Vue</i>
<div>Hello Vue</div>
</MySlotCpn>
</div>
</template>
具名插槽: 在插槽文件设定name,引用时v-slot:对应名字即可。 一个不带 name 的 slot,会带有隐含的名字 default
<!-- NavBar组件 -->
<template>
<div class="nav-bar">
<div class="left">
<slot name="left"></slot>
</div>
<div class="center">
<slot name="center"></slot>
</div>
<div class="right">
<slot name="right"></slot>
</div>
</div>
</template>
<!-- App组件 -->
<template>
<div class="app">
<NavBar>
<template v-slot:left>
<button>button</button>
</template>
<template v-slot:center>
<span>span</span>
</template>
<template v-slot:right>
<i>i</i>
</template>
</NavBar>
</div>
</template>
动态的插槽名: 给name绑定:,引用时在通过 [] 解构
<!-- 组件里的name是通过props动态绑定的 -->
<slot :name="name"></slot>
<!-- 通过[]解构 -->
<template v-slot:[name]></template>
还有个作用域插槽 大体就是,父组件想用插槽组件的数据,可以在调用的时候拿到
//插槽组件,将数据传递给调用方
<slot :user = "use.name">
//调用组件,可以解构,可以自定义接受名称,在.出来
//v-slot="{scope}" 获取子组件传数据,:list="list" 把list传给子组件
<template>
<Child v-slot="{scope}" :list="list">
{{ user.name }}
</Child>
</template>
组件化对Vue的学习来说可谓十分重要,这块还是要多磨多练。