vue 高级使用

156 阅读4分钟

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>

模版数据的二次加工

  1. watch、computed => 导致响应流过于复杂(computed中赋值)

  2. 方案一:函数 - 优点:独立、管道化 / 缺点:无法响应式 方案二:v-html,不仅可以做逻辑判断还可以响应式,还可以在里面写html tag 起初作用:强制模版化 例子:

     <h1>{{ money }}</h1>
     money: 100
    

    需求:超过99块,就只显示99块

     <h1 v-html="money > 99 ? 99 : money"></h1>
    

    image.png

    方案三:过滤器 - 单纯做数据处理最干净的方法 - 追问:无上下文,没有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 问题:

    1. 为什么用map 不用 forEach?

forEach 没有返回,jsx 需要返回一个节点

    1. 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 是元素值发生变化就触发

    1. 写 jsx 的好处、劣势?

vue 的编译路径:template -> 将的编译路径:template 转成 render函数 -> 再将render 函数 挂载在 vm.render -> 最后执行 vm.render() vue 在template-> render 的过程中做了很多优化

特征二:基于模版化的组件化

传统模版化

  Vue.component('component', {
    template: '<h1>组件</h1>'
  })
  new Vue({
    el: 
'#app'  })
// 全局

特点:

    1. 抽象和复用(slot更像一个透传)
    1. 精简 & 聚合(接口精简,高聚合的展现)

重量级:组件 轻量级:函数式组件 再轻量级:slot

混入mixin - 逻辑混入

// demoMixin.js

export default {
  data() {
    return {
      msg: '我是mixin',
      obj: {
        title: 'mixinTitle'
      }
    }
  }
}

// 组件内部
import demoMixin from './demoMixin'
mixins: [demoMixin]
    1. 使用场景: 抽离公共逻辑(逻辑相同,模版不同,可用mixin)
    1. 缺点:数据来源不明
    1. 合并策略 a. 递归合并,合并每一层数据 b. data 合并冲突时,以组件优先 c. 生命周期回调函数不会覆盖,回先后执行,优先级为先mixin后组件

extends 继承拓展 - 逻辑拓展

组件中:

name: 'HelloWorld',
extends: demoExtends,
// demoExtends.js

export default {
  data() {
    return {
      msg: '我是extends',
      obj: {
        title: 'extendsTitle'
      }
    }
  }
}
    1. 应用:拓展独立逻辑
    1. 与mixin 区别:以数组形式传入;extends直接传对象
    1. 合并策略 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 - 插件

    1. 注册外部插件,作为整体实例的补充
    1. 以map形式注册,会去除重,不会重复注册
    1. 手写插件 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 () {}
  }
}

可以封装插件

组件的高级引用

    1. 递归组件 - es6 vue-tree
    1. 动态组件 - <component :is="name"></component>
    1. 异步组件 - router 懒加载,用的时候再加载

问题:有没有做过页面的懒加载?

结合webpack chunk回答,SPA 会分成好几个chunk,打包出得东西可能会分成chunkA[hash].js,chunkB[hash].js,chunkC[hash].js;在router中,还有chunk name 属性,在chunkA 中不会加载BC

set和响应式有关set 和响应式有关 nextTick 跟渲染顺序有关 keep-alive