ref的使用
一般用ref来拿到某一个DOM元素,虽然Vue不推荐来操作DOM,但是复杂情况除外。 如下例:子组件复用,通过点击子组件来修改父组件中的值,用到的知识点有子组件向父组件传值,和获取子组件的数据。
refs不是响应式的,所以避免在计算属性或者模板中去使用$refs.
<div id="app">
<couter @change="handleChange" ref="one"></couter>
<couter @change="handleChange" ref="two"></couter>
<div>{{total}}</div>
</div>
Vue.component('couter', {
template: '<div @click="handleClick">{{number}}</div>',
data() {
return {
number: 0
}
},
methods: {
handleClick() {
// this.number++ 控制的是子组件数据
this.number++;
// 数据改变的同时,触发`change`事件,父组件就会监测到这个事件,就会执行对应的方法
this.$emit('change')
}
}
})
var vm = new Vue({
el: '#app',
data: {
total: 0
},
methods: {
handleChange() {
// 由于子组件通过触发change事件来执行了这个函数,所以子组件每点击一次,父组件都要重新计算total
this.total = this.$refs.one.number + this.$refs.two.number
// this.$refs.one 就是这个Vue实例组件
}
}
})
子组件想改变父组件传过来的值
思路:父组件传过来的值,子组件不能修改(避免数据造成混乱),可以将父组件传过来的值,copy一份,然后进行修改。
<div id="app">
<couter number="0"></couter>
</div>
var couter = {
template: '<div @click = "handleClick">{{couterNumber}}</div>',
props: ['number'],
data() {
return {
couterNumber: this.number
}
},
methods: {
handleClick() {
this.couterNumber++
// 不直接修改父组件传过来的值,应当将传过来的值赋值一份,操作复制的那份
}
}
}
var vm = new Vue({
el: "#app",
components: {
couter
}
})
给组件绑定原生事件
思路:在子组件里面的事件添加.native
事件修饰符,在父组件中的子组件上写click事件不会起作用。
<div id="app">
// 在子组件标签上绑定点击事件,不会起作用。
<couter @click.native="handleClick"></couter>
</div>
跨组件之间的传值:发布与订阅
思路:兄弟组件想要相互控制,相当于相互通信。设置一个公共bus,该bus包含所有vue实例的方法,因为涉及到$emit
来触发事件和$on
方法来监听事件。
<div id="app">
<Child content="hello"></Child>
<Child content="world"></Child>
</div>
Vue.prototype.bus = new Vue()
// 每一个Vue实例都会有这个bus属性,相当于每一个组件(包括子组件)都会有这个属性
Vue.component('Child', {
template: '<div @click="handleClickChild">{{myContent}}</div>',
props: ['content'],
data() {
return {
myContent: this.content
}
},
mounted() {
// this指向当前组件
var _this = this
// 用$on来监测这个change事件,只要一触发就会执行
this.bus.$on('change', function (val) {
// 也要注意一下这个this的指向,是把当前组件的值更改,所以可以提前存一下this值。
_this.myContent = val
})
},
methods: {
handleClickChild() {
// 因为每个组件都会有bus这个属性,而这个属性又是Vue的实例,所以都有$emit方法,触发change事件并传值
// 注意bus是公共的。
this.bus.$emit('change', this.myContent)
}
},
})
var vm = new Vue({
el: "#app",
})
beforecCreate
生命周期函数中,所调用的函数都是未声明的,在created()
中,声明了,要想调用函数,要在created()
里面,否则函数会有is not undefined
报错。
组件中的细节点
- 在标签中利用子组件渲染的时候,注意一些未知的bug,可以考虑一下is属性
- 子组件中的data必须是一个函数,而不是对象。是为了让每一个子组件都有一个独立的数据存储,不会出现多个子组件互相影响的情况。
- 通过
$refs.ref
来获取DOM节点,也可以在子组件上绑定ref来获取这个子组件的引用,里面的数据可以直接写this.$refs.ref.number
(number是组件中data中的数据) - 事件名一般采用动宾的形式,程序中出现的常量,可以单独写在文件前面,以大写表示。
- props参数校验,可配置哪些属性:(注意一下自定义校验器)
插槽的使用
插槽的使用场景用于:父组件通过子组件,向子组件中传递DOM标签,而子组件中通过<slot></slot>
标签来接收传过来的内容。
具名插槽,在外围包含一个template标签,用v-slot来什么插槽名称.注意 v-slot 只能添加在 <template>
上 (只有一种例外情况),
<div id="app">
<Child>
<template v-slot:"header">
<p slot="header">
<span>
返回
</span>
</p>
</template>
<template v-slot:"footer">
<p slot="footer">
尾部
</p>
</template>
</Child>
</div>
Vue.component('Child', {
template: `<div>
<div @click="handleClick">
<slot name='header'>默认值header</slot>
</div>
<p>hello world</p>
<slot name='footer'>默认值footer</slot>
</div>`,
methods:{
// 注意 插槽不能绑定方法 可以在slot标签外放一个div,事件绑在div上
handleClick(){
console.log('返回')
}
}
})
// slot标签中可以设置默认值,不传标签过来的时候,以默认显示的内容来占位,传了DOM元素过来,直接替换,不管里面是啥
var vm = new Vue({
el:'#app'
})
动态组件
顾名思义,就是动态的去渲染哪个组件<component :is=type></component>
,其中的type为对应的组件名称,可以通过js来获得.可实现两个组建的切换显示
<button @click="change('Child')">点击切换Child</button>
<button @click="change('myfooter')">点击切换myfooter</button>
<component :is="componentId"></component>
import Child from './childOne'
import myfooter from './childTwo'
methods:{
change(val){
this.componentId = val
// componentId写对应的组件名
}
}
组件的刷新
<div @click="refresh">点击刷新组件</div>
<myfooter v-if="isShow"></myfooter >
import myfooter from './myfooter'
export default {
data(){
isShow: true
},
methods:{
refresh(){
this.isShow = false;
this.$nextTick(() => {
//DOM更新之后,重新加载组件
this.isShow = true
})
}
},
componets:{
myfooter
},
}
v-once指令
可以更好地展示一些静态内容,就是将一些静态内容只展示一次,再放在内存中,再次渲染的时候,直接在内存中获取.可以很好地提高性能.
computed 和 watch
html模板里面最好不要写逻辑性的代码,可以写在computed里面,在computed里面可以写get()和set()方法,来获取属性,或者设置属性。 watch侦听器可以侦听data里面的属性,属性一旦改变,就会立即执行。 比如:
watch:{
letter () {
console.log(this.letter) //这个letter是当前的这个data里面的当前值
}
}
-
computed
- 支持缓存,只有依赖数据发生改变,才会重新进行计算,计算属性可用于快速计算视图(View)中显示的属性。这些计算将被缓存,并且只在需要时更新。computed是计算属性的; 它会根据所依赖的数据动态显示新的计算结果, 该计算结果会被缓存起来。computed的值在getter执行后是会被缓存的。如果所依赖的数据发生改变时候, 就会重新调用getter来计算最新的结果。
- 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
- 如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
- computed和methods:计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。响应式依赖:data和props里面的数据?
computed: { fullName: { // getter get: function () { return this.firstName + ' ' + this.lastName }, // setter set: function (newValue) { var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } }
获取属性,默认调用
get
方法。 -
watch
当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
大部分的使用场景是,在输入框绑定
v-model
的时候,监测里面的值,在回调中会传入newVal和oldVal两个参数。可以参考官网的例子,可以在watch
里面发异步请求,而computed
不可以。<div> <van-field @focus="focus('num_s',800,350,'up')" @blur="blur('up')" placeholder="现金值" v-model="begin_cash" maxlength="7" type="number" input-align="center" /> </div>
watch: { begin_cash(newVal, oldVal) { if (newVal < 0) { this.$toast('输入金额不能为负数!'); this.begin_cash = ''; } if (newVal.toString().indexOf('.') === 0) { this.begin_cash = ''; } if (newVal === '' && oldVal.toString().indexOf('.') > 0) { this.begin_cash = oldVal; return; } if (newVal) { newVal = newVal.toString(); var pointIndex = newVal.indexOf('.'); if (pointIndex > 0 && (newVal.length - pointIndex) > 3) { this.begin_cash = oldVal; return; } } }
作者:夏末_阳光依然耀眼
链接:juejin.cn/post/699992… 来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
vue中数组更新时的注意点
比如,要改变data中的数据,如果这个数据是数组格式,数据本身可能已经更新了,但是视图不会更新。
<div id="app">
<p v-for="item in arr">{{item}}</p>
<!-- 还是打印出 1 2 3 -->
</div>
let vm = new Vue({
el: '#app',
data: {
arr: [1, 2, 3]
},
created() {
setTimeout(() => {
this.arr[0] = 100
console.log(this.arr) // [100,2,3],但是视图不会更新
}, 2000)
}
})
vue钩子函数
-
beforeCreate
(实例创建前)实例组件刚开始创建,元素dom和数据都还没有初始化
应用场景:可以在这加个loading事件;可以混入公共逻辑,
Vue.mixin()
-
created
(实例创建后) 数据data
已经初始化完成,方法也已经可以调用,但是dom
未渲染,在这个周期里面如果进行请求是可以改变数据并渲染,由于dom
未挂载,请求过多或者占用时间过长会导致页面线上空白。应用场景:在这结束loading,还做一些初始化,实现函数自执行
-
beforeMoute
(元素挂载前) dom未完成挂载,数据初始化完成,但是数据的双向绑定还是{{}},这是因为vue采用了虚拟dom技术。 -
render
函数执行 创建虚拟DOM,将它进行挂载。 -
mouted
(元素挂载后) 数据和dom都完成挂载,在上一个周期占位的数据把值渲染进去,一般请求会放在这个地方,因为这边请求改变数据之后刚好能渲染。 -
beforeUpdate
(实例更新前) 只要是页面数据改变了都会触发,数据更新之前,页面数据还是原来的数据,当你请求赋值一个数据的时候就会执行这个周期,如果没有数据改变不执行。 -
updated
(实例更新后) 第一次加载数据也会执行,只要是页面数据改变了都会触发,数据更新完毕,页面的数据是更新完成的,beforeUpdated
和updated
要谨慎使用,因为页面更新数据的时候都会触发,在这里操作数据很影响性能和死循环。 注意:beforeUpdate
和updated
用的都不多,可以用watch
来监测。 -
beforeDestory
(实例销毁前) 实例销毁之前调用,在这一步,实例仍然完全可用。 应用场景:可以做自定义事件的解绑,去除DOM
事件的绑定,以及定时器的清理。 -
destory
(实例销毁后)vue
实例销毁后调用,调用后,Vue
实例指示的所有内容都会解除绑定,所有的事件监听器都会被移除,所有的子实例也会被销毁。 注意:切换路由,组件会自动执行beforeDestory
和destory
钩子函数。如果切换的很频繁,可以用<keep-alive>
来做缓存。
父子组件执行的顺序
同步和异步组件,父子组件执行的钩子函数顺序不同。
-
同步组件:
import Page from '@/components/page'
生命周期钩子函数执行顺序: 父组件的
beforeCreate
、created
、beforeMount
--> 所有子组件的beforeCreate
、created
、beforeMount
--> 所有子组件的mounted
--> 父组件的mounted
-
异步组件:
1. const Page = () => import('@/components/page')
2. const Page = resolve => require(['@/components/page'], page)
生命周期钩子函数执行顺序: 父组件的
beforeCreate、created、beforeMount、mounted
--> 子组件的beforeCreate、created、beforeMount、mounted
Vue.mixin()
混入逻辑:是将各个钩子函数,或者data数据组成一个数组,依次执行。抽离公共方法和编写插件
[beforeCreate,beforeCreate]
,Vue.mixin()中可以增加一些钩子函数。
Vue.mixin({
beforeCreate(){
console.log('1111111111') // 最先执行
}
})
缺陷:导致数据来源不明,Vue3用compositionApi来解决这个问题。
<transition>
动画:切换显示与隐藏效果
<transiton></transition>
标签里面包裹要切换的元素
<transition name="fade">
<!-- transition把要切换动画的元素包裹起来 -->
<div
style="width:100px;height:100px"
class="box"
v-show="isShow"
>
</div>
</transition>
<button
style="width:100px;height:40px"
@click="change"
>点我切换
</button>
.box{
background red
}
// 动画一开始的一瞬间,比如显现的一瞬间
.fade-enter{
background green
}
// 持续时间为2秒
.fade-enter-active{
transition all 2s linear
}
// 最后的颜色为蓝色
.fade-enter-to{
background blue
}
// 总体效果就是:green => blue(持续2s),最后变成红色
// 动画结束之后,会移除所有样式
.fade-leave{
// 看不出效果
}
.fade-leave-active{
transition all 2s linear
}
.fade-leave-to{
background pink
}
// // 总体效果就是:red => pink(持续2s),最后隐藏
// 结束之后直接隐藏
注意:v-指令可能会重名,如果动画很多的情况下,所以可以取名字。
组件化开发
-
为什么组件化开发?
- 实现组件复用
- 可以方便维护代码
- 只用组件级别更新,会给每一个组件给一个
watcher
,不用所有组件都去看,哪个组件出现问题,就去查找该组件。 - 能抽离的就尽早抽离,可以减少更新。
-
组件实例化过程
会将当前传入的对象,创建出一个Vue实例来。 将当前组件的html,css,js放在一起。
Vue.component('Child', { template: '<div @click="handleClickChild">{{myContent}}</div>', data() { return { myContent: this.content } },
在
Vue.component
传入一个对象,会默认调用Vue.extend()
Vue.extend( { template: '<div @click="handleClickChild">{{myContent}}</div>', data() { return { myContent: this.content } } )
data
为什么要是一个函数? 函数可以返回一个对象,在每一个组件中,返回的值不同,可以避免data
中的数据冲突。
父子组件传值(单向数据流)
-
Props的大小写
在父组件中以短横线来传递
<!-- 在 HTML 中是 kebab-case 的 --> <blog-post post-title="hello!"></blog-post>
在子组件中用驼峰命名法来接收
Vue.component('blog-post', { // 在 JavaScript 中是 camelCase 的 props: ['postTitle'], template: '<h3>{{ postTitle }}</h3>' })
注意: 任何类新的数据都可以传给prop
-
想把整个对象都传给prop
post: { id: 1, title: 'My Journey with Vue' } <blog-post v-bind="post"></blog-post> // 相当于 <blog-post v-bind:id="post.id" v-bind:title="post.title"> </blog-post>
注意:父子传递,默认是单向数据流,子组件更改,父组件是不会更改的,如果传递给prop是对象或者数组,则传递的是引用(地址),在子组件中更改数据,(不推荐),则会影响父组件。
-
也可以传function给子组件,子组件直接使用,像props属性一样,直接使用。并且传对应的值给父组件 在parent父组件中:
<div> parent <son1 :money="mny" :change-money="changeMoney"></son1> </div>
data(){ return { mny: 100 } }, methods:{ changeMoney(val){ console.log(val) } }, components: { son1 }
在子组件son1中:
<div> {{money}} <button @click="changeMoney(500)">点我</button> <!-- 点击直接调用父组件传过来的方法 --> </div>
props: { money: { type: Number }, changeMoney: { type: Function, // 注意这里是大写 default: () => {} } }
父子组件传值(同步数据)
若想子组件通过$emit把值传给父组件,父组件又可以同步给子组件,可以这样:
在父组件中:
<!-- 父组件把值改了,顺便又传给了子组件,就实现了父子组件的同步更新 -->
<ShopList :value="mny" @input="val => mny = val"></ShopList>
<!-- 这里注意一下,input是绑定给子组件的,绑定的对应时间是啥,是父组件的,所以下面的$emit方法触发的,也是自己组件本身的方法。-->
data() {
return {
mny: 100
};
},
在子组件中:
<button @click="go">点击按钮</button>
{{value}}
props:{
value:Number
},
methods: {
go(){
// 将值传给父组件
this.$emit('input',500)
}
}
可以直接优化为:
<ShopList v-model="mny"></ShopList>
这个value
和input
为固定搭配,v-model
是他俩的语法糖,这里的500
会替换掉v-model
中的mny
属性,同时更新到了子组件中。
-自定义v-model
属性和事件名
有时候不想使用value+input
组合,可以自己更改名字,如
<HomeList v-model="mny"></HomeList>
model:{
prop: 'mny', // 自定义属性名
event: 'handleClick' // 自定义事件名
},
props:{
mny:{
type: Number // 接收数据名
}
},
methods:{
goto(){
this.$emit('handleClick',300)
}
}
又有一种简洁的语法:
<HomeList :money.sync="mny"></HomeList>
// 等价于
<HomeList :money="mny" @update:money="val => mny = val"></HomeList>
// 在子组件的$emit中,也以update:money为事件名把值传给父组件。
注意:这个
.sync
语法糖在子组件中接收数据和事件的时候有具体的格式,注意。
这个详细案例可以看vue.js
官网,在这里我自己认为在子组件上绑定v-model
的含义的,想让父子组件数据同步,一般子组件传数据给父组件就无了,现在子组件也想保持数据同步,就可以想到用v-model
.
父孙组件传值
- 父 => 子 => 孙,props传值,性能差,速度慢
- 利用
provide
和inject
,父组件中使用provide
将当前父组件实例暴露出去,孙级组件用inject
来接收。注意不要在子组件中直接更改父组件数据!可以通过调用方法来改变数据。通常是在自己写的库中进行使用。-
provide
和inject
的使用示例这里有三级组件,parent,son2,grandson
parent.vue:
<div> parentdata属性: {{mny}} <son2></son2> </div>
import son2 from './son2.vue' export default { provide() { return { parent: this // 将当前实例暴露出去 } }, data() { return { mny: 100 } }, }
son2.vue:
<div> 部分代码省略 son2引入的孙组件: <grandson></grandson> </div>
grandson.vue:
<div> 孙级组件直接用parent属性: {{parent.mny}} </div>
export default { inject: ['parent'] // 注入parent实例 }
-
$parent
,$children
,获取当前组件的父组件和子组件,可以直接触发儿子的事件或者父亲的事件。尽量少用,不知道父级是谁,子是谁-
$parent
,$children
使用示例在父组件parent.vue中给son2绑定方法,准备在孙级组件grandson中去触发
parent.vue:
<div> <son2 @eat="eat()"></son2> // 给son2绑定eat方法 </div>
eat(){ console.log('触发了son2上的eat方法') }
grandson.vue:
<button @click="$parent.$emit('eat')"> 点击触发父组件的eat方法,当前的父组件是son2,相当于触发son2上的eat方法。 </button>
-
- 可以使用
$dispatch
,一层一层的找父级元素,直到该父级有这个事件。可以说是$parent
的一种改良方案。
- 改写组件原型上的
$dispatch
方法
// 向上派发这个事件,只要组件上绑定过这个事件,就会触发
// 想要某个特定组件触发,传入一个参数,这个参数是组件名称
Vue.prototype.$dispatch = function(eventName,componentName, value){
let parent = this.$parent
while(parent){
if(parent.$options.name === componentName){
parent.$emit(eventName, value) // 触发某一组件的eventName事件。
return
}
parent = parent.$parent
}
}
<HomeList v-model="ischecked" @eat="eat"></HomeList>
这个是给HomeList
组件绑定了一个eat
事件。
5. 通过父组件,给具体子组件派发事件$broadcast
// 向下通知某个触发某个事件,可能会有多个子组件,所以会涉及到循环
Vue.prototype.$broadcast = function (eventName, componentName, value) {
let children = this.$children // 获取的是个数组,直接子元素,不包含孙级
// 递归函数,一层一层地找
function broadcast(children) {
for (let i = 0; i < children.length; i++) {
if (children[i].$options.name === componentName) {
children[i].$emit(eventName, value)
return
} else {
if (children[i].$children) {
broadcast(children[i].$children)
}
}
}
}
broadcast(children)
}
// 在父级的父级组件里,想要找绑定了drink事件的子组件
<button @click="$broadcast('drink','grandson',300)">
点击触发孙级组件的broadcast
</button>
// 在父级组件里
<grand-son @drink="drink"></grand-son>
drink(val){
console.log('=====================',val)
}
// =====================,300
- 用
$attrs
和$listeners
来传递所有属性和方法,传递属性:v-bind
,传递方法:v-on
在App.vue里面,给parent组件传了属性和方法
<div style="height:30px">
<parent @sing="sing" a="111"></parent>
</div>
在parent组件中,可以直接使用这些属性和方法,也可以把属性和方法传给下一个孙级组件son1.
<div>
属性:{{$attrs}} // {"a":"111"}
<son1 v-bind="$attrs"></son1>
<son1 v-on="$listeners"></son1>
</div>
mounted(){
console.log(this.$listeners) // 有sing方法
},
在son1组件中
mounted(){
console.log('son1',this.$listeners) // 有sing方法
},
这样就实现了父 => 子 => 孙,数据及方法传递
- 关于
$attrs
和$listeners
来传递所有属性和方法注意的地方$attrs
和props
声明的属性是互斥的,相当于你在props
声明的属性是不会出现在$attrs
对象里面的。$attrs
是响应式的props
没有声明的属性,但是父级组件又传过来了,这些属性会默认渲染到传给这个组件的最外层的div标签上,如果不想要添加到DOM上,可以直接在组件中声明:inheritAttrs: false,
$listeners
在html模板中看不到,可以在钩子函数中看到,$attrs
可以在html和钩子中都可以看到。$attrs
和$listeners
传递的是所有的属性和方法。
-
ref直接引用组件的值和方法
比如一个弹框组件,里面暴露了一些属性和方法,供外界使用,局限性就在于你要向外提供一些方法。
兄弟组件传值
两种方法:
- 找到共同的父亲
eventBus
,发布订阅模式,在任何组件中去订阅事件($on()
),也可以在任何组件触发($emit()
) 注意 :$on()
和$emit()
是要对一个Vue
实例而言的,相当于操作的是同一个this
。$on()
和$emit()
触发和监听的事件名要相同。- 缺陷: 如果项目庞大,事件名可能会重名,比如一个
$emit()
,可能会触发多个$on()
,一呼百应。只适合小规模通信。
案例:子组件如何监听父组件中的mounted?
就可以在子组件中去监听事件,等父亲搞完了,在去$emit()
这个事件
在main.js
中:
Vue.prototype.$bus = new Vue() // 创建一个公共实例
在子组件中:
mounted() {
this.$bus.$on('事件名', function(params){
console.log('监听到了',params)
})
}
在父组件中:
mounted() {
// ...
this.$bus.$emit('事件名', 111)
}
//子组件感应到触发了事件,立即执行回调函数
// 打印:监听到了',111
扩展:
$off
:移除事件监听,一般是放在beforeDestory()钩子函数中
beforeDestory() {
this.$bus.$off('事件名',function(){...})
}
在子组件中,如果需要等父组件mounted
同步任务执行完,再去执行子组件mounted
,可以在子组件中增加$nextTick()
函数,在同步组件中,一般是要把子组件中的mounted
执行完,再去执行父组件中的mounted
的,用这个方法,可以解决,但是情况得是同步任务,还有一个解决方案是,把父组件中的mounted
方法移到created
里面,这肯定就先执行了。
插槽slot
在一个组件中放slot标签,感觉是占了一个位置,而这个位置上的样式,可以在外面套一个div,先把样式写死,传过来的是啥,由父组件决定传啥过来。
在myDialog组件中:
<template>
<div>
<!-- 定义了两个slot样式,提前写死了样式 -->
<div class="header">
<slot name="header"></slot>
</div>
我是弹框组件
<div class="footer">
<slot name="footer"></slot>
</div>
</div>
</template>
在父组件中引用myDialog组件,以下为老版本写法,部分代码省略 老版本:
<template>
<div>
<myDialog>
<div slot="header">header</div>
<div slot="footer">footer</div>
</myDialog>
</div>
</template>
新版本:
<myDialog>
// 注意这里header和footer不加引号
// 这里的mny是当前组件的数据,也会带到插槽里面去的。
<template v-slot:header>header{{mny}}</template>
<template v-slot:footer>footer</template>
</myDialog>
这里相当于一个数组:
[header:div,footer:div]
,在子组件中去查找对应的名字,也就是header
和footer
,找到了,就把对应的slot
替换成了div
.
作用域插槽
相当于是想把子组件的数据带到父组件中,利用插槽来传递,这个需要子组件提供一些属性和方法给父组件使用。
在父组件中:
<myDialog>
// 父组件把属性传个子组件
<template v-slot:header>header{{mny}}</template>
<template v-slot:footer="slotProps">{{slotProps.myobj}}</template>
</myDialog>
在myDialog组件中:
<template>
<div>
<!-- 定义了两个slot样式 -->
<div class="header">
<slot name="header"></slot>
</div>
我是弹框组件
<div class="footer">
// 子组件把属性传给父组件
<slot name="footer" :myobj="obj"></slot>
// 这里的obj是子组件的属性
</div>
</div>
</template>
export default {
data () {
return{
obj:{
a: 'a',
b: 'b',
c: 111,
d: true
}
}
}
}
slotProps
相当于是传过来的属性的名字对象,属性都放在这个对象里面,这样通过作用域插槽,就实现了父子组件通信。
Vue.use(xxx)
use是vue的一个全局的api,会自动调用xxx插件的install方法,这个install方法是内部封装的必须要export default出来的。
Vuex
import store from './store.js'
new Vue({
store // 在vue初始化过程中,注入了一个store属性,内部会将这个store属性注入到每一个vue组件的$store中
render: h => h(App)
}).$mount('#app')
先创建new Vue,再创建App action
actions: {
// {commit},解构,也可以写直接写store
asyncChange({commit}, payload){
setTimeout(() => {
commit('changeValue', payload)
}, 1000); //最后也是通过提交mutation来修改数据
}
},
扩展:
strict: true
// 表示严格模式
注意
- mutation中,不要写异步,包括(setTimeOut),mutation更改状态,只能在同步下更改。
$nextTick
在修改数据之后,使用$nextTick可以获取更新后的DOM,数据可能变了,但是DOM还没有变。
使用场景: 一般轮播图数据是接口返回的,数据有了之后,需要DOM更新之后才会去定义new Swiper('xxx')
$attrs
是父组件给子组件传值,但是又不想用props来传,可以在子组件中,通过$attrs来获得,该属性是一个对象,并且这个传的值会放到子组件的HTML根元素上。在子组件上,可以使用inheritAttrs: false来进行控制,不需要放到组件的根元素上。
子组件又可以把attrs"来传给子组件的内部组件。
label标签
label标签把input框包裹,可以点击文字的时候也可以选择input框
Array.prototype.splice()
arr.splice()会影响原数组
标签缓存组件
场景:想实现tabs切换,缓存数据的那种效果,首先看要不要求数据的实时性。 用法1:缓存整个组件,需要切换路由,从而切换组件
用法2:配合is属性,切换组件
子组件中的mounted钩子函数中去触发$emit写法改造
<template>
<List @hook:mounted="listenMounted" />
</template>