📝尤雨溪对逻辑复用的讲解 - 从 Mixin、高阶组件到 Hooks

本文记录《B站·跟尤雨溪一起解读Vue3源码》中,尤雨溪对于逻辑复用演化过程的讲解

假设我们有这么一个组件,[x, y]这一坐标会根据鼠标移动而变化,x,y是响应式的。 我们想把这段逻辑抽取出来,让它可以复用,有什么方法?

js 复制代码
const App = {
  template: `{{ x }} {{ y }}`,
  data() {
    return { x: 0, y: 0 }
  },
  methods: {
    update(e) {
      this.x = e.pageX
      this.y = e.pageY
    }
  },
  mouted() {
    window.addEventListener('mousemove', this.update)
  },
  unmounted() {
    window.removeEventListener('mousemove', this.update)
  }
}

1. mixin

第一种方法,使用mixin。提取mixin很容易,但是如果你有很多个mixin,会出现以下问题:

  • 注入源不明:不知道属性、方法来自哪个mixin
  • 命名冲突
js 复制代码
const MouseMixin = {
  data() {
    return { x: 0, y: 0 }
  },
  methods: {
    update(e) {
      this.x = e.pageX
      this.y = e.pageY
    }
  },
  mouted() {
    window.addEventListener('mousemove', this.update)
  },
  unmounted() {
    window.removeEventListener('mousemove', this.update)
  }
}

const App = {
  mixins: [MouseMixin, mixinA, mixinB, mixnC], // 存在多个mixin时,会很混乱
  template: `{{ x }} {{ y }}`,
}

2. 高阶组件

因为mixin的缺点很明显,React在很早的时候就移除了Mixin。 但是面对逻辑复用,又没有很好的解决方法,于是提出来了高阶组件。 高阶组件的用法:高阶组件包含了需用逻辑复用的变量和方法,并包裹住 Inner 组件,同时还要把x,y传递给 Inner 组件。

js 复制代码
function withMouse(Inner) {
  data() {
    return { x: 0, y: 0 }
  },
  methods: {
    update(e) {
      this.x = e.pageX
      this.y = e.pageY
    }
  },
  mouted() {
    window.addEventListener('mousemove', this.update)
  },
  unmounted() {
    window.removeEventListener('mousemove', this.update)
  },
  render() {
    return h(Inner, {
      x: this.x, 
      y: this.y
    })
  }
}

const App = withMouse({
  props: ['x', 'y'],
  template: `{{ x }} {{ y }}`,
})

相比于mixin,高阶组件有了自己的命名空间,不用担心命名冲突的问题。x, y的来源也指向了withMouse 但依然不能改变所有问题,假如有多个高阶组件包裹。我们依然会得到很多props,同样也无法知道哪个props来自哪个高阶组件

js 复制代码
const App = withFoo(withAnother(withMouse({
  props: ["x", "y", "foo", "bar"]
})))

3. slot

在vue中,跟高阶组件相似的是slot

js 复制代码
function withMouse(Inner) {
  data() {
    return { x: 0, y: 0 }
  },
  methods: {
    update(e) {
      this.x = e.pageX
      this.y = e.pageY
    }
  },
  mouted() {
    window.addEventListener('mousemove', this.update)
  },
  unmounted() {
    window.removeEventListener('mousemove', this.update)
  },
  // template: `<slot :x="x" :y="y" />`,
  render() {
    return this.$slots.default && this.$slots.default({
      x: this.x,
      y: this.y
    })
  }
}

const App = {
  template: `<Mouse v-slot="{x, y}">{{ x }} {{ y }}</Mouse>`
}

存在多个slot时会是,比高阶函数要好的是,我们知道x,y的来源是<Mouse />。但slot额外产生了组件,增加了性能开销

js 复制代码
const App = {
  component: { Mouse, Foo },
  template: `
    <Mouse v-slot="{ x, y }">
      <Foo v-slot="{ foo }">
        {{ x }} {{ y }} {{ foo }}
      </Foo>
    </Mouse>
  `
}

4. Hooks

React Class Componentvue2 option,把data, method都绑定在一起,把它们挪到组件外是一个代价很大的行为。 意识到高阶组件也不是什么灵丹妙药后,React 提出用 Hooks 彻底取代 Class Component,开启了组件逻辑表达和逻辑复用的新范式,完美解决了上面的问题

js 复制代码
function useFeatureA() {
  const foo = ref(0)
  const plusone = computed(() => foo.value + 1)
  return { foo, plusone }
}

function useFeatureB() {
  const bar = ref(1)
  return { bar }
}

export default {
  template: `{{ event.count }}`,
  props: ["id"],
  setup(props) {
    const { foo, plusone } = useFeatureA()
    const { bar } = useFeatureB()
  }
}

参考资料

相关推荐
卤蛋fg610 分钟前
vxe-table 自定义复制粘贴逻辑:精确控制单元格数据转换
vue.js
张人玉2 小时前
基于 Vue 3 + ECharts + Express + SQLite 构建的新能源汽车销量数据分析与可视化平台——新能源汽车销量数据分析系统
数据库·vue.js·sqlite·echarts
清风programmer4 小时前
H5与小程序通信
前端·vue.js·uni-app
Cobyte5 小时前
通过 Vite 运行手写的模板编译器
前端·javascript·vue.js
张人玉6 小时前
基于 Vue 3 + ECharts + Express + SQLite 构—— LDA 模型的新闻舆论主题识别与情感分析系统
vue.js·echarts·express
无人生还6 小时前
从 Vue3 到 React · 快速上手系列第 4 篇:条件渲染与列表渲染
前端·vue.js·react.js
咩咩啃树皮6 小时前
第46篇:Vue3 Router工程化进阶——懒加载+嵌套路由+全局守卫+权限控制
javascript·vue.js·ecmascript
咩咩啃树皮6 小时前
第44篇:Vue3侦听器完全精讲——watch与watchEffect区别、深度监听、异步业务落地
前端·javascript·vue.js
晓得迷路了7 小时前
栗子前端技术周刊第 139 期 - Nuxt 4.5、Vue 3.6 RC、Angular 发布节奏...
前端·javascript·vue.js
程序软件分享21 小时前
多语言DAPP综合盘交易所系统源码搭建剖析
vue.js·金融·开源