Vue 进阶用法
特征一:模版化
插槽
内部组件做展示,但是逻辑希望在外部组件 内部为一个槽,用于接受
作用:多个组件公用一个逻辑
react 中没有,直接使用JSX
默认插槽
组件外部维护参数以及结构,内部安排位置
外部组件想要传递一个 msg 给内部组件:
// App.vue
<div id="app">
<hello-world>
<p>{{ msg }}</p>
</hello-world>
</div>
// HelloWorld
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<slot></slot>
</div>
</template>
具名插槽
以 name 标识插槽的身份,而在组件内部做到可区分
// App.vue
<template v-slot:header> {{ header}}
</template>
<template v-slot:body> {{ body}}
</template><template v-slot:footer> {{ footer}}
</template>
// HelloWorld
<slot name="header"></slot>
<slot name="body"></slot>
<slot name="footer"></slot>
作用域插槽
slot-scope (V2.6 before) v-slot(after)
外部做结构描述勾勒,内部传递参数
老版本中:
//1. 插槽起名
//2. slot-scope 传递参数
<template slot="content" slot-scope="slotProps">{{slotProps}}
</template>
内部:
// HelloWorld
<slot :slotProps="slotProps" />
// HelloWorld
data() {
return {
slotProps: 'zhuawa start'
}
}
新版本中:
// App.vue
<template v-slot:slotProps="slotProps">{{slotProps}}
</template>
模版数据的二次加工
-
watch、computed => 导致响应流过于复杂(computed中赋值)
-
方案一:函数 - 优点:独立、管道化 / 缺点:无法响应式 方案二:v-html,不仅可以做逻辑判断还可以响应式,还可以在里面写html tag 起初作用:强制模版化 例子:
<h1>{{ money }}</h1> money: 100需求:超过99块,就只显示99块
<h1 v-html="money > 99 ? 99 : money"></h1>方案三:过滤器 - 单纯做数据处理最干净的方法 - 追问:无上下文,没有this,挂载在全局的
{{ 绑定值 | 过滤器名}} {{ time | format}}<h1>{{ money | moneyFilter }}</h1>filters: { moneyFilter (money) { return money > 99 ? 99 : money } }
jsx
使用 render 函数不需要 <template>
注意返回的是单个节点,每一块都会返回一个 node
render (h) {
return <ul>
{
this.options.map(item => {
return <li>{ item.text }</li>
})
}
</ul>
}
组件传值:
render (h) {
return <ul>
{
this.options.map(item => {
return <content
item={item}
value={item.value}
key={index}
>
{ this.money }
</content>
})
}
</ul>
}
jsx 中的插槽解决方案:直接利用变量
render (h) {
const moneyNode = {
<p>{ this.money }</p>
}
return <ul>
{
this.options.map(item => {
return <content
item={item}
value={item.value}
key={index}
>
{ moneyNode }
</content>
})
}
</ul>
}
jsx 中的数据处理,直接在{}中写表达式
const moneyNode = {
<p>{ this.money > 99 ? 99 : this.money }</p>
}
jsx 中的方法: onXXX=""
JSX 问题:
-
- 为什么用map 不用 forEach?
forEach 没有返回,jsx 需要返回一个节点
-
- v-model - jsx如何实现双向绑定?**
外层bind:value, 内层onInput/onChange 监听输入
<input id="inputName" value={this.state.name} onChange={this.onInputChange} />
onInputChange = (e) => {
this.setState({
name: e.target.value
})
onChange 是失去焦点触发,onInput 是元素值发生变化就触发
-
- 写 jsx 的好处、劣势?
vue 的编译路径:template -> 将的编译路径:template 转成 render函数 -> 再将render 函数 挂载在 vm.render -> 最后执行 vm.render() vue 在template-> render 的过程中做了很多优化
特征二:基于模版化的组件化
传统模版化
Vue.component('component', {
template: '<h1>组件</h1>'
})
new Vue({
el:
'#app' })
// 全局
特点:
-
- 抽象和复用(slot更像一个透传)
-
- 精简 & 聚合(接口精简,高聚合的展现)
重量级:组件 轻量级:函数式组件 再轻量级:slot
混入mixin - 逻辑混入
// demoMixin.js
export default {
data() {
return {
msg: '我是mixin',
obj: {
title: 'mixinTitle'
}
}
}
}
// 组件内部
import demoMixin from './demoMixin'
mixins: [demoMixin]
-
- 使用场景: 抽离公共逻辑(逻辑相同,模版不同,可用mixin)
-
- 缺点:数据来源不明
-
- 合并策略 a. 递归合并,合并每一层数据 b. data 合并冲突时,以组件优先 c. 生命周期回调函数不会覆盖,回先后执行,优先级为先mixin后组件
extends 继承拓展 - 逻辑拓展
组件中:
name: 'HelloWorld',
extends: demoExtends,
// demoExtends.js
export default {
data() {
return {
msg: '我是extends',
obj: {
title: 'extendsTitle'
}
}
}
}
-
- 应用:拓展独立逻辑
-
- 与mixin 区别:以数组形式传入;extends直接传对象
-
- 合并策略 a. 同mixin 递归合并 b. 合并优先级 组件 > mixin > extends c. 回调优先级 extends > mixin
整体拓展类 extend
从预定义的配置中拓展出来一个独立的配置项,进行合并
从更顶层的方式直接从实例上拓展一个配置,并对其实例化之后再做传参
// main.js
let _baseOptions ={
data: function () {
return {
course: 'zhuawa',
session: 'vue',
teacher: 'yy'
}
},
created () {
console.log('extend base')
}
}
const BaseComponent = Vue.extend(_baseOptions)
new BaseComponent({
created() {
console.log('extend created')
}
})
//
merge options: 将当前实例下的配置(data, mixins,methods ...)进行合并,合并的过程就会有优先级
应用:某些页面功能需要,某些不需要
Vue.use - 插件
-
- 注册外部插件,作为整体实例的补充
-
- 以map形式注册,会去除重,不会重复注册
-
- 手写插件 a. 外部使用Vue.use(myPlugin, options) b. 默认调用的是内部的install 方法(实现的东西在install 里写)
和 components 同级
main.js 中
// 加载拓展插件
import MyPlugin from './plugins/myPlugin'
const _options = {
name: 'my baby plugins'
}
Vue.use(MyPlugin, _options)
myPlugin 中
export default {
install: (Vue, option) => {
Vue.globalMethods = function () {
// 主函数,全局方法
}
// 挂载自定义指令
Vue.directive('myDirective', {
bind (el, binding, vonde, oldVNode) {
// 全局资源
}
})
Vue.mixin({
created: function () {
console.log(option.name + 'created')
}
})
Vue.prototype.$myMethod = function () {}
}
}
可以封装插件
组件的高级引用
-
- 递归组件 - es6 vue-tree
-
- 动态组件 -
<component :is="name"></component>
- 动态组件 -
-
- 异步组件 - router 懒加载,用的时候再加载
问题:有没有做过页面的懒加载?
结合webpack chunk回答,SPA 会分成好几个chunk,打包出得东西可能会分成chunkA[hash].js,chunkB[hash].js,chunkC[hash].js;在router中,还有chunk name 属性,在chunkA 中不会加载BC
nextTick 跟渲染顺序有关 keep-alive