首先
组件时
vue.js
最强大的功能之一,而组件实例的作用的域是相互独立的,这就意味着不同组件之间的数据无法相互引用。一般来说,组件可以有以下几种关系
如图所示
A是父级、B是A的子级C和D的父级,C和D是兄弟关系,A和C、D是隔代关系。
针对于不同的场景,我们可以使用不同的组件方式。
第一种:父子组件传值
1、父传子
- 在父组件的子组件标签上绑定一个属性,挂载要传输的变量
- 在子组件中通过
props
来接收数据,props
可以是数组也可以是对象,接受的数据可以直接是props:['属性名']
或props:{属性名:数据类型}
- 在子组件中通过
// 父组件
<template>
<div id = 'app'>
<Child :msg='msg'></mycom>
</div>
</template>
<script>
import Child from "./components/child"
export default {
name: 'App',
data(){
return{
msg:" 2 "
}
},
components:{
Child
}
}
// 子组件
<template>
<div id = 'child'> </div>
</template>
<script>
export default {
name: 'child',
// 第一种
props:['msg']
// 第二种
props:{
type:String,
default:function(){
return :""
}
}
data(){
return:{}
},
}
2、子传父
- 在父组件的子组件标签上自定义一个事件,然后调用需要的方法
- 在子组件的方法中通过
this.$emit("事件")
来触发在父组件中定义的事件,数据是以参数的形式来进行传递的
// 父组件
<template>
<div id = 'app'>
// 自定义一个事件
<Child :msg='msg' @reception='add'></mycom>
</div>
</template>
<script>
import Child from "./components/child"
export default {
name: 'App',
data(){
return{
msg:" 2 "
}
},
components:{
Child
}
methods:{
// 调用方法
add(data){
console.log(data)
}
}
}
// 子组件
<template>
<div id = 'child' @click='add()'> </div>
</template
<script>
export default {
name: 'child',
props:['msg']
data(){
return{
child:"Son"
}
},
methods:{
add(){
this.$emit('reception',this.child)
}
}
}
第二种:兄弟组件传参
注意:
兄弟之间传值和子传父的方式类似,都是通过
$emit
事件发射的形式,但是兄弟之间要找一个Vue
实例作为媒介,也就是通过一个eventBus
事件总线
兄弟组件传参也有多种方式
第一种:
- 在
src
中新建一个Bus.js
文件,然后导出一个空的vue
实例- 在传输数据的一方引入
Bus.js
,然后通过Bus.$emit("事件名","参数")
来派发事件,数据是以$emit()
的参数来进行传递的- 在接受的数据的一方,引入
Bus.js
,然后通过Bus.$on("事件名",(data)=>{data是接受的数据})
创建一个BUS.js
文件
// 1、创建事件中心
const eventBus = new Vue();
//2、把创建出来的空的Vue对象挂载到Vue的原型上
Vue.prototype.eventBus = eventBus //eventBus【属性名】
在组件中引入刚才创建的js
import '@/eventBus.js'
在methods
中定义一个函数
// 在传输数据的组件中
methods:{
changesize(){
eventBus.$emit('add',this.arg)
}
}
/*
--------------------------------------------------------------
*/
// 在接收数据的组件中
created(){
eventBus.$on('add',(message)=>{
//一些操作,message就是从top组件传过来的值
console.log(message)
})
}
第二种
在main.js
中也可以创建事件中心,创建成功后,在组件中,直接使用,不用导入
代码就不贴了,简单,太累,
第三种:vuex
传参
Vuex
实现了一个单向数据流,在全局拥有一个 State 存放数据,当组件要更改 State 中的数据时,必须通过 Mutation 进行,Mutation 同时提供了订阅者模式供外部插件调用获取 State 数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走 Action,但 Action 也是无法直接修改 State 的,还是需要通过 Mutation 来修改 State 的数据。最后,根据 State 的变化,渲染到视图上。
两种写法:一种是直接操作数据,一种是先赋值
在store.js
中
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state:{
name:jing,
age:18,
look:beautiful
},
mutations:{},
actions:{}
})
在组件中
组件一
<div>
name:{{username}}
<button @click="add"> 确认</button>
</div>
data(){
return(){
//注意 写在data中,因为先赋值给了username,那么store值如有改变,这里将不能发生改变
// username:this.$store.state.name
}
},
computed:{
username(){
return this.$store.state.name
}
}
methods:{
add(){
this.$store.state.name = 'jing+1'
}
}
组件二
<div>
name02:{{$store.state.name}}
</div>
如果sore中有许多的值需要获取的话
在组件中引入mapState
import {mapState} from 'vuex'
//1
computed:mapState(['name','age','look']),
//2
computed:mapState({
storeName:state => state.name,
storeAge:state => state.age,
storeLook:state => state.look
})
//但computed也有自己的运算函数,就不能写1、2这样了
computed:{
...mapState({
storeName:state => state.name,
storeAge:state => state.age,
storeLook:state => state.look
})
}
第四种:ref/$refs
如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
在父组件中
<template>
<div>
<login ref="loginRef" />
</div>
</template>
<script>
import Login from '@/components/login.vue'
export default {
components:{
Login
}
data() {
return {};
},
};
</script>
在子组件中methods
中定义一个方法
methods:{
hider() {
this.show = true;
},
}
在父组件中methods
中调用
methods:{
regis() {
this.$refs.register.shower();
},
}
注意
在父组件中是通过this.$refs
来调用自组件中的方法,自组件中的方法不用在标签上定义事件,只需要在methods
中直接定义一个事件的方法。
第五种:本地存储
根据项目需求,了解localStorage
和sessionStorage
两种本地存储的特性
localStorage
:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据sessionStorage
:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;作用域不同
,sessionStorage
不在不同的浏览器窗口中共享,即使是同一个页面;localStorage
在所有同源窗口中都是共享的
通过本地存储实现数据的传输
// 保存数据到 sessionStorage
sessionStorage.setItem('key', 'value');
// 从 sessionStorage 获取数据
let data = sessionStorage.getItem('key');
// 从 sessionStorage 删除保存的数据
sessionStorage.removeItem('key');
// 从 sessionStorage 删除所有保存的数据
sessionStorage.clear();
注意:
存的是键值对,只能是字符串类型,如果要存对象的话,需要使用
JSON.stringify(obj)
转义字符串然后再存储使用的时候
JSON.parse(objStr)
解析为对象。
推荐一个库good-storage
,它封装了sessionStorage
,可以直接用它的API
存对象
//localStorage
storage.set(key,val)
storage.get(key, def)
//sessionStorage
storage.session.set(key, val)
storage.session.get(key, val)
拓展:
推荐使用seeionStorage
,随着浏览器的关闭而清除,不会影响下次操作
第六种:路由传值
通过路由传参依旧是有多种方式
出现场景
<div class="examine" @click="details(2)">查看详情</div>
第一种路由拼接
- 页面刷新数据不丢失
methods:{
details(id) {
//直接调用$router.push 实现携带参数的跳转
this.$router.push({
path: `/particulars/${id}`,
})
}
router.js
中相应配置。
{
path: '/particulars/:id',
name: 'particulars',
component: particulars
}
需要在
path
中添加/:id
来对应$router.push
中path
携带的参数。在子组件中可以使用来获取传递的参数值
在页面内个获取参数
this.$route.params.id
第二种:params
页面刷新数据会丢失
通过路由属性中的name
来确定匹配的路由,通过params
来传递参数
methods:{
insurance(id) {
this.$router.push({
name: 'particulars',
params: {
id: id
}
})
}
router.js
{
path: '/particulars',
name: 'particulars',
component: particulars
}
在子组件中
this.$route.params.id
注意
这里不能使用:/id来传递参数了,因为组件中,已经使用params
来携带参数了
第三种:query
传参
使用
path
来匹配路由,然后通过query
来传递参数这种情况下,query
传递的参数会显示在url
后面?id=?
methods:{
insurance(id) {
this.$router.push({
path: '/particulars',
query: {
id: id
}
})
}
router.js
中
{
path: '/particulars',
name: 'particulars',
component: particulars
}
对应子组件中
created(){
this.$route.query.id
}
第七中:全局变量
在
src
目录中新建一个Cloble.js
文件,导入到main.js
全局文件中,并进行在原型上的一个挂载
main.js中
import Globle from './Globle'
Vue.prototype.$arr = Globle
Goble.js
中
exports.install = function (Vue) {
// 每次创建vue实例
Vue.prototype.$arr = {}
// 组件中
// this.$target.arr = arr;
}
相应的组件中
传输数据的一方
methods: {
more() {
let arr = [];
this.phone.forEach((item) => {
if (!arr.includes(item.category_id)) {
arr.push(item.category_id);
}
});
this.$arr.arr = arr;
this.$router.push({ path: "/goods", query: { categoryID: arr } });
},
接收数据的一方
activated() {
/*
浏览更多
*/
if (this.$arr.arr) {
this.categoryID = this.$arr.arr;
this.getlist();
} else {
this.getlist("list");
}
},
全局变量:定义的变量通过挂载可以在全局所有组件中使用,比较方便
存在缺点:页面刷新后,数据会丢失
总结不易,感谢留下脚印
第八中:$root,$parent,$children
$root,$parent
都能够实现访问父组件的属性的方法,两者的区别在于,如果存在多级子组件,通过$parent
访问得到的是它最近一级的父组件,通过root
访问得到的是根组件
1、$children
<body>
<div id="app">
<mxc></mxc>
<mxc></mxc>
<mxc></mxc>
<button @click="btnClick">颤抖吧</button>
</div>
<template id="mxc">
<div>我是子组件啊</div>
</template>
<script>
const app=new Vue({
el:'#app',
data:{
message:'你好'
},
methods:{
btnClick(){
console.log(this.$children)
}
},
components:{
mxc:{
template:'#mxc',
methods:{
showMessage(){
console.log('mxcnb')
}
}
}
}
})
</script>
</body>
通过循环拿到所以的子组件数据
methods:{
btnClick(){
console.log(this.$children)
for(let c of this.$children){
console.log(c.name)
}
}
},
注意:
因为是数组,所以我们可以通过比如:this.$children[2]
来拿到第三个子组件的数据。
但是这么做有一个问题:比如开发时突然在这三个子组件中又插入了一个子组件(可能相同,也可能不同),这时候【2】就不再是我们需要的了。。。
那么我们就可以用到前面说的的:$refs
2、子访问父组件:$parent
$parent()
方法是在子组件中可以直接访问该组件的父实例或组件;
<body>
<div id="app">
<mxc></mxc>
</div>
<template id="mxc">
<div>我是子组件啊</div>
<button @click="btnClick">更加颤抖的child</button>
</template>
<script>
const app=new Vue({
el:'#app',
data:{
message:'你好'
},
components:{
mxc:{
template:'#mxc',
methods:{
btnClick(){
console.log(this.$parent)
}
}
}
}
})
</script>
</body>
注意:
在实际中
$parent
用的非常少,主要是考虑到耦合度的原因
3、子组件访问根组件:$root
<body>
<div id="app">
<mxc></mxc>
</div>
<template id="mxc">
<div>
<div>我是mxc组件</div>
<cdn></cdn>
</div>
</template>
<template id="mxca">
<div>
<div>我是子子组件啊</div>
<button @click="btnClick">巨颤祖child</button>
</div>
</template>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好'
},
components: {
mxc: {
template: '#mxc',
data() {
return {
name: '我是中间子组件的name'
}
},
components: {
cdn: {
template: '#mxca',
methods: {
btnClick() {
console.log(this.$parent.name)
console.log(this.$root.message)
}
}
}
}
}
}
})
</script>
</body>
码字不易,请留下脚印