vue 中比较重要或有用的部分 MVVM模式 创建可复用组件需要注意 .sync修饰符 slot 和 scoped-slot (已经废弃,用v-slot代替,elementUI依然使用着) v-bind & v-on $attrs & $listeners scoped style

这些都是我消化理解之后的东西,我不能保证我的理解都是对的,仅供参考。

MVVM最早由微软提出来,它借鉴了桌面应用程序的MVC思想,在前端页面中,把Model用纯JavaScript对象表示,View负责显示,两者做到了最大限度的分离。

把Model和View关联起来的就是ViewModel。ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model。

创建可复用组件需要注意

  1. 用name属性指定组件的名字,建议使用帕斯卡命名法
  2. 用props属性指定需要父组件传过来的属性(用在组件的标签上)vue官网 props
  3. 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。父组件传递到子组件的prop的改变会反映到子组件中,但是子组件prop的改变不会反映到父组件中。需要用$emit()触发父组件的某个事件,修改父组件的值。

.sync修饰符

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。所以我们用$emit()触发父组件的属性更新事件来通知父组件修改指定属性。

比如:父组件将自己的 visible 属性传给子组件的 shown 属性( :shown="visible" ),子组件想修改 shown 的值,就通过 $emit("update:shown",value) 通知父组件(触发父组件的 update:shown 事件),父组件通过 @update:shown 的回调函数将 visible 的值修改为 value@update:shown="value => this.visible = value" )。综上,父组件包含 :shown="visible" @update:shown="value => this.visible = value" ,这就可以简写为 :shown.sync="visible"。但是,这种带 .sync 修饰符的属性,不能用表达式,只能直接获取属性,比如: :shown.sync="!visible" 无效,:shown.sync="ui.visible" 是可以的。

参考彻底明白VUE修饰符sync-简书

slot 和 scoped-slot (已经废弃,用v-slot代替,elementUI依然使用着)

  1. Slot 意思是插槽,就是组件xxx可以有一个带slot属性的子组件作为插件,elementUI会自动根据slot的值使插件发挥相应的作用。
  2. Scoped Slot 意思是带自定义域的slot,除了声明slot属性之外,还需要通过slot-scope属性,指定一个变量,在插件中就可以使用该变量暴露的属性。
<!-- 定义一个插槽,name是该插槽的名字,不写默认为default -->
<slot name="xxx" ></slot>

<!-- 使用插槽,通过给标签定义slot属性,声明该标签替换哪个slot,不写默认是default插槽 -->
<div slot="xxx"></div>

<!-- 定义一个带域插槽(域可以理解为一个对象),通过在标签上定义属性,声明域中的变量(对象中的属性),当然可以通过:value动态绑定value变量值 -->
<slot name="xxx" text="这是一个带域插槽" value="10"></slot>

<!-- 使用带域插槽,通过slot属性声明替换的插槽,通过slot-scope声明域的名字(对象名),然后通过域名调用域中的属性。 -->
<div slot="xxx" slot-scope="scope">{{scope.text}},值为{{scope.value}}</div>

v-bind & v-on

v-bind 用来动态绑定属性,v-bind:name="fullName":将vm中的fullName属性绑定到标签的name属性上。

  1. v-bind 可以省略,属性名前直接加:可以达到同样的效果,上面简写为:name="fullName"
  2. v-bind 可以解构赋值,v-bind="obj",vue会将obj中所有属性绑定到标签上。注意优先级↓↓↓。
<!-- 将attr1和attr2绑定到div标签上 -->
<div v-bind="{attr1:value1,attr2:value2}"></div>

<!-- 直接通过属性名绑定的优先级更高,下面这种情况,attr1的实际值为value -->
<div :attr1="value" v-bind="{attr1:value1,attr2:value2}"></div>

v-on 用来绑定事件监听,v-on:click="func":将vm中的func方法绑定到标签的click事件上。

  1. v-on 也可以省略为@,上面简写为@click="func"
  2. v-on 也可以解构赋值,v-on="obj",vue会将obj中所有属性绑定为标签的事件。注意兼容性↓↓↓。
<!-- 将event1和event2绑定到div标签上 -->
<div v-on="{event1:func1,event2:func2}"></div>

<!-- 这两种绑定方法,绑定的函数会同时存在,因此event事件触发的时候func和func1都会执行。 -->
<div @event="func" v-on="{event1:func1,event2:func2}"></div>

$attrs & $listeners

通常创建一个组件,设置props属性定义需要父组件传入的参数,然后就可以在组件标签上声明相应的属性将数据传到组件中。

如果在该组件标签上声明了一个没有在props中定义的属性vue会怎么办?当然除了class和style这两个纯天然的属性。

vue会把这些属性存放在this.$attrs中。它是一个对象({attr1:value1,attr2:value2...}),因此你可以利用 v-bind 指令将它绑定到子组件上。但是,注意一下优先级。

同理,你在组件标签上定义的事件监听,除了.native修饰的之外,都会放到this.$listeners中,也可以通过 v-on 指令将它绑定到子组件。但是,同一个事件可能会被绑定两次。

scoped style

需求:去掉elementUI的textarea的边框。

<style scoped>
.el-textarea > textarea{
  border: 0;
}
</style>
<!-- ↓↓↓↓↓↓↓↓↓↓↓ vue编译之后 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ -->
<style type="text/css">
.el-textarea > textarea[data-v-1dcb4f26]{
  border: 0;
}
</style>

vue组件中的 scooped style 编译之后为了使样式只对当前组件生效,会自动再追加一个属性选择器,然后给当前组件中的标签都添加该属性。

因为,textarea是在el-textarea子组件中的,所以属性选择器并没有添加到textarea上,编译之后的html代码如下。

<div data-v-1dcb4f26 class="el-textarea">
  <textarea autocomplete="off" rows="7" class="el-textarea__inner" style="min-height: 33px">
  </textarea><!---->
</div>

所以,因为vue的‘隔离’措施,上面的style样式并不会生效。

如果,编译之后代码如下就可以了。

<style type="text/css">
.el-textarea[data-v-1dcb4f26] textarea{
  border: 0;
}
</style>

vue允许我们指定添加属性选择器的位置,通过 >>> 操作符(深度作用选择器)实现,如下。

<style scoped>
.el-textarea >>> textarea{
  border: 0;
}
</style>
<!-- ↓↓↓↓↓↓↓↓↓↓↓ vue编译之后 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ -->
<style type="text/css">
.el-textarea[data-v-1dcb4f26] textarea{
  border: 0;
}
</style>

这里还有一些需要注意的。