文章主题: 掌握 Vue 进阶概念与技术应用
模板化: 插槽 & watch & computed & jsx
组件化: 抽象复用 | 精简&聚合 | 渲染顺序 | mixin | extends | 插件 | provide & inject
一、模板化
用模板抽象出一切可能的存在。
包括 v-for / v-if / 动态化节点 / 组件 / 动态组件 / 结构体(插槽) / JSX / 计算属性 / 监听属性
插槽
默认插槽 | 具名插槽 | 作用域插槽
默认插槽
- 如果有多个节点会怎么处理? 比如多个 p 标签? => 统一节点的形式, 集成化封装的操作。
- 如果安排多个插槽会怎么样? 会渲染三遍, 聚合式渲染。都会放到 default 插槽。
- 即多个默认插槽会怎么样? => 重复"替换"渲染
- 多个插槽分开布局怎么办? => 具名插槽
插槽组件
<template>
<div class="hello">
<slot></slot>
</div>
</template>
使用组件
<template>
<HelloWorld>
<!--默认插槽-->
<p>{{ msg }}</p>
</HelloWorld>
</template>
<script>
import HelloWorld from "./components/HelloWorld"
export default {
components: { HelloWorld },
data() {
return {
msg: 'Hello world'
}
}
}
</script>
具名插槽
以 name 的形式标识插槽的身份, 从而在组件内部做到可区分。
-
为什么可以通过 name 区分插槽?
- vue 将所有没有起名字的插槽全部合在了一起,起名叫 default。而具名插槽则是自己的 name。 name 索引了上下文空间。封装包裹的作用。
-
为什么默认插槽用 template 包裹不显示?
- template编译时标签, 最终会被去掉。
-
多个具名插槽会怎么样?
- 后来者会覆盖前者, 不会重复渲染, vue 会报 warning。
-
父组件如何获取所有的子组件插槽内容?
- refs
-
请问如果具名插槽想要做参数的整合合并(参数的作用域隔离)怎么办? 作用域插槽
具名插槽组件
<template>
<div class="hello">
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
</div>
</template>
使用具名插槽组件
<template>
<HelloWorld>
<template slot="header"></template>
<p>{{ msg }}</p>
<template v-slot:footer></template>
</HelloWorld>
</template>
<script>
import HelloWorld from "./components/HelloWorld"
export default {
components: { HelloWorld },
data() {
return {
msg: 'Hello world'
}
}
}
</script>
作用域插槽
参数合并(参数作用域隔离) => 作用域插槽
外部做结构的描述勾勒, 内部做传参。
插槽组件内部数据暴露给父组件。
- 考察点: 插槽传参 - 数据传递 - 尽量保持名称的对齐
插槽内层绑定数据
父组件调用组件通过指定插槽的 slot-scope 获取子组件插槽暴露出的数据
父组件期望在插槽中使用子组件域的数据。
<template>
<div class="hello">
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
<!--插槽内层-->
<slot name="content" :slotProps="slotProps"></slot>
</div>
</template>
<script>
export default {
data(){
return {
slotProps:"slotProps"
}
}
}
</script>
<template>
<HelloWorld>
<template slot="header"></template>
<p>{{ msg }}</p>
<template v-slot:footer></template>
<!--作用域插槽-->
<template slot="content" slot-scope="{slotProps}">
{{ slotProps }}
</template>
<!--vue3-->
<template v-slot:content="slotProps">{{ slotProps }}</slot>
<!--简写#-->
<template #content="slotProps">{{ slotProps }}</slot>
</HelloWorld>
</template>
<script>
import HelloWorld from "./components/HelloWorld"
export default {
components: { HelloWorld },
data() {
return {
msg: 'Hello world'
}
}
}
</script>
模板的二次加工方案
watch & computed
watch 和 computed 的区别
- 使用写法的写法: watch 一个函数 或者对象, 监听某一个 target , 传递一个目标的处理流程, 可以没有返回值; computed 根据一个或多个变量计算出来一个结果, computed 一定会有返回。一个关注流程 , 一个关注结果
- 原理不同: watch 对劫持的数据进行观察后, 触发响应的回调。 computed 收集依赖 => 依赖劫持 => 触发重新计算
- 参数不同: watch<once,immediate,deep:true, handler>; computed
收集的依赖是什么? 依赖: 一个值的变化会影响到另一个值的变化。
其它
方案一: 函数 - {{ calcAdd(header) }} ;管道符 filters , 在 vue3 中已经取消了, filter 中 this 是 undefined, 不是当前实例(纯函数)。
方案二: v-text 和 v-html
方案三: 直给 - {{ ...三元表达式 }}
JSX
可以用来做更加灵活且JS化的方案。
<script>
import content from "content.vue"
export default {
name:"HelloWorld",
components:{content},
data(){
return {
options:[{}]
}
},
methods:{
handleClick(){}
},
// 最终返回的是一个node节点
// h => createElement
// h(tag, data, children)
render(h){
// 嵌套
const moneyNode = (<p>{this.money > 99 ? 99 : this.money}</p>)
return (
<ul>
{
this.options.map(item=>{
return (
<content item={item} onClick={this.handleClick}>
{options}
{moneyNode}
</li>
)
})
}
</ul>
)
}
}
</script>
- 如何在 jsx 中实现 v-for?
- 如何在 jsx 中实现 v-if?
- 如何在 jsx 中实现嵌套循环?
- 如何在 jsx 中绑定事件?
- 如何在 jsx 中使用组件? 如何使用插槽?
二、组件化
特点
- 抽象复用
- 精简 & 聚合
- 渲染顺序
mixin - 逻辑混入
mixin 不建议继续去使用。
-
应用: 抽离公共逻辑(逻辑相同、模板不同) 对比 extends 核心逻辑是继承(拓展)
-
合并策略: 变量补充, 多 mixin 情况下, 后者覆盖前者, 但是本体(组件内容)优先。
-
vue 组件存在同名变量时, 不会覆盖本体。
-
声明周期: 生命周期在父组件和子组件之间。
- 父组件created => mixin (多个按顺序) created => 子组件 created
-
-
extends: 不会覆盖本体; 生命周期: father 和 children 之间; 只有单个; extends 在 mixin 之前执行
-
mixin 是共用 extends 是扩展
-
extends 和 mixin 中相同状态, 取 mixin 最后一个。
demo-mixin.js
export default {
data(){
return {
msg: "I'm mixin",
obj:{
title: 'mixin title',
header: 'mixin header',
}
}
},
created(){
console.log("mixin")
}
}
vue 组件中使用 mixin
<script>
import demoMixin from 'demo-mixin.js'
export default{
mixin:['demoMixin']
}
</script>
extends
vue 组件中使用 extends: extends 的使用方式
demo-extends.js
// demo-extends.js
export default {
data(){
return {
msg: "I'm extends",
obj:{
title: 'extends title',
header: 'extends header',
}
}
},
created(){
console.log("extends")
}
}
<script>
import demoExtends from 'demo-extends.js'
export default{
extends:demoExtends
}
</script>