关于vue.js:React-有ProviderConstmerHook模式Vue-30-也要

9次阅读

共计 25331 个字符,预计需要花费 64 分钟才能阅读完成。

<section id=”nice” data-tool=”mdnice 编辑器 ” data-website=”https://www.mdnice.com” style=”padding: 0 10px; line-height: 1.6; word-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, ‘PingFang SC’, Cambria, Cochin, Georgia, Times, ‘Times New Roman’, serif; font-size: 15px; letter-spacing: 0.05em; color: #595959;”><h1 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; margin-bottom: 15px; font-size: 24px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #773098;”><span class=”prefix” style=”display: none;”></span><span class=”content”>React 有 Provider-Constmer+Hook 模式,Vue 3.0 也要!</span><span class=”suffix”></span></h1>
<figure data-tool=”mdnice 编辑器 ” style=”margin: 0; margin-top: 10px; margin-bottom: 10px; display: flex; flex-direction: column; justify-content: center; align-items: center;”></figure>
<h2 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; min-height: 32px; line-height: 28px; border-bottom: solid 1px #000000; color: #773098; display: inline-block; border-bottom-width: 1px; border-bottom-style: solid; border-color: #773098; padding-top: 5px; padding-right: 0.5em; padding-left: 0.5em; margin-bottom: -3px; font-size: 22px; margin: 1em auto; padding: 0.5em 0; text-align: center; width: 85%; font-weight: bold; display: flex; flex-direction: column; justify-content: center;”><span class=”prefix” style=”display: none;”></span><span class=”content”> 前言 </span><span class=”suffix”></span></h2>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 相熟 React 的小伙伴都晓得,随着 React hook 的推出,React 呈现了一种新型的状态治理形式 Provider-Consumer 模式 ,能够说是真香正告。</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 随着 Vue3.0 的公布,提供了咱们属于 Vue 的 Hook+provider-inject 的新型状态管理工具。 本篇文章将以 概念 + 案例 联合的模式为大家解说该模式的用法和长处。</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 文章目录:</p>
<ol data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: decimal;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 引出问题,这种模式解决了什么问题?(长处)</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 如何应用,代码解构,我的项目案例。(以代码为主)</section></li></ol>
<h2 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; min-height: 32px; line-height: 28px; border-bottom: solid 1px #000000; color: #773098; display: inline-block; border-bottom-width: 1px; border-bottom-style: solid; border-color: #773098; padding-top: 5px; padding-right: 0.5em; padding-left: 0.5em; margin-bottom: -3px; font-size: 22px; margin: 1em auto; padding: 0.5em 0; text-align: center; width: 85%; font-weight: bold; display: flex; flex-direction: column; justify-content: center;”><span class=”prefix” style=”display: none;”></span><span class=”content”>1. 引出问题 </span><span class=”suffix”></span></h2>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 首先我以自问自答的形式来引出一些问题和以往的解决方案。带着问题学习是一种很好的学习办法吧。(我要我感觉)</p>
<ol data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: decimal;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”>在 vue 中,兄弟组件、祖孙组件如何通信?</section></li></ol>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 看到第一个问你,你是不是嘴角微微上扬,45 度角俯视天空,微撩刘海(如果你不是秃头的话????),感觉这很 easy</p>
<ul data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”><p style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>Vuex; 不论多简单的组件通信,Vuex 好像一键搞定。然而,Vuex 作为一个状态治理的重型利器,在面对大量的须要多组件共享的数据,显然是杀鸡用???????? 啊。况且在一个小型的 Vue 我的项目中,根本都不必 Vuex,太重了不是么?</p>
</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”><p style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>EventBus;这种办法罕用于兄弟级别的组件通信。弊病也是非常明显;</p>
</section></li></ul>
<ol data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: decimal;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”>EventBus 不会随着组件的销毁而主动销毁,须要咱们手销毁;</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 挂在到全局;</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 留神:肯定要记得销毁,否则会呈现 bug;</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 事件产生者只能单向播送,无奈取得接收者对事件的处理结果;</section></li></ol>
<ul data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 其余(attrs,provide-inject(vue2.x 的, 本地缓存,url 传参)。这些办法都不太罕用,尽管能勉强解决跨组件通信,但都是有种本末倒置的感觉。(我要我感觉????);</section></li></ul>
<ol start=”2″ data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: decimal;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 在 Vue2.x 中,如何进行逻辑复用?</section></li></ol>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 举个栗子:在后盾治理我的项目中,多个菜单(组件)同时用到一堆数据,进行花色出现,有列表展现、有饼图占比、有折线图趋势、有热力求阐明频次等等,这些组件应用的是雷同的一些数据和数据处理逻辑(增删改查);如果在每个组件只都写一套这类的解决逻辑,显然是不合乎程序员的终极目标(偷懒????);
以往的解决方案:</p>
<ul data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”>Mixins</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 高阶组件 (Higher-order Components, aka HOCs)</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”>Renderless Components (基于 scoped slots / 作用域插槽封装逻辑的组件)</section></li></ul>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 其实下面在写业务代码的时候,也就 mixin 罕用。高阶组件和 Renderless Components 更多的是封装组件的时候用到的。它们存在的毛病:</p>
<ul data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 模版中的数据起源不清晰 </section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 命名空间抵触。</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 须要额定的组件实例嵌套来封装逻辑 (性能问题);</section></li></ul>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 那么 Vue 将如何解决以上这些问题呢。</p>
<h2 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; min-height: 32px; line-height: 28px; border-bottom: solid 1px #000000; color: #773098; display: inline-block; border-bottom-width: 1px; border-bottom-style: solid; border-color: #773098; padding-top: 5px; padding-right: 0.5em; padding-left: 0.5em; margin-bottom: -3px; font-size: 22px; margin: 1em auto; padding: 0.5em 0; text-align: center; width: 85%; font-weight: bold; display: flex; flex-direction: column; justify-content: center;”><span class=”prefix” style=”display: none;”></span><span class=”content”> 概念 </span><span class=”suffix”></span></h2>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>context</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 咱们要利用 provide+inject+hook 创立一个上下文对象,而且对外裸露 供应者(通常在组件树中上层的地位和消费者(inject)),只有在下层组件应用 provide, 在上下文以内的所有子组件,通过 inject 都可能接见这个上下文环境以内的数据,而且不用经由过程 props。也就是提供了一个汇合治理并具备响应式的对象,并限度了这个对象可接见的局限,在局限内的子组件都能猎取到她外部的值。</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>provide+inject</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 在下层组件通过 provide 提供一些变量,在子组件中能够通过 inject 来拿到 </p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>provide 用法:</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>provide 承受两个参数,第一个参数是 provide 惟一名称,最好用 Symbol, 防止反复。第二个参数是你要裸露的数据 </p>
<pre class=”custom” data-tool=”mdnice 编辑器 ” style=”margin-top: 10px; margin-bottom: 10px;”> provide(ThemeSymbol, data)
</pre>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>inject 用法:</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>inject 接管两个参数,第一个参数是 provide 名称, 也就是 ThemeSymbol,第二个参数是 provide 裸露进去的数据,也就是 data。</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>Hook</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>Vue3.0 的composition api 和 setup 提供了 Vue 具备 Hook 的能力。而 Hook 的存在能够让你在组件之外封装公共办法,达到逻辑复用。所以,咱们能够在.vue 组件外自定义 Hook,创立 context;</p>
<h2 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; min-height: 32px; line-height: 28px; border-bottom: solid 1px #000000; color: #773098; display: inline-block; border-bottom-width: 1px; border-bottom-style: solid; border-color: #773098; padding-top: 5px; padding-right: 0.5em; padding-left: 0.5em; margin-bottom: -3px; font-size: 22px; margin: 1em auto; padding: 0.5em 0; text-align: center; width: 85%; font-weight: bold; display: flex; flex-direction: column; justify-content: center;”><span class=”prefix” style=”display: none;”></span><span class=”content”> 文件目录解构 </span><span class=”suffix”></span></h2>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 依据下面的观点和功能,分析一下要实现的步骤:</p>
<ul data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 建设一个公共 context,封装自定义 hook</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”>provide 供给数据 </section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”>inject 注入数据 </section></li></ul>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 这里的文件结构:</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>context</p>
<pre class=”custom” data-tool=”mdnice 编辑器 ” style=”margin-top: 10px; margin-bottom: 10px;”>├─context      //   context 的文件夹
  ├─index.js   // context 根组件,对立引入各个模块的 context, 并对立裸露
  │─home      // 以模块名称命名,一个模块一个 context
    │─index.js   
 
</pre>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>View</p>
<pre class=”custom” data-tool=”mdnice 编辑器 ” style=”margin-top: 10px; margin-bottom: 10px;”> ├─View
   ├─Home
     ├─index.vue  // 根组件,在这层注入 inject
     ├─xxx.vue // 其余组件  消费者地点的层级
</pre>
<h2 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; min-height: 32px; line-height: 28px; border-bottom: solid 1px #000000; color: #773098; display: inline-block; border-bottom-width: 1px; border-bottom-style: solid; border-color: #773098; padding-top: 5px; padding-right: 0.5em; padding-left: 0.5em; margin-bottom: -3px; font-size: 22px; margin: 1em auto; padding: 0.5em 0; text-align: center; width: 85%; font-weight: bold; display: flex; flex-direction: column; justify-content: center;”><span class=”prefix” style=”display: none;”></span><span class=”content”> 用法 </span><span class=”suffix”></span></h2>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 为了不便解读,我这里以 Todolist 我的项目为例;</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 源码:https://github.com/961998264/…</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 我的项目介绍:</p>
<ol data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: decimal;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 新增待办事件 </section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 编辑待办事件 </section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 删除待办事件 </section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;”> 查看待办事件 </section></li></ol>
<h3 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #773098;”><span class=”prefix” style=”display: none;”></span><span class=”content”>1. 建设一个上下文 context</span><span class=”suffix” style=”display: none;”></span></h3>
<pre class=”custom” data-tool=”mdnice 编辑器 ” style=”margin-top: 10px; margin-bottom: 10px;”>// 引入 composition api
import {provide, ref, Ref, inject, computed,} from <span class="hljs-string" style="color: #D69D85; line-height: 26px;">'vue'</span>

// provide 命名
<span class="hljs-built_in" style="color: #4EC9B0; line-height: 26px;">export</span> const listymbol = Symbol()

// 自定义 hook,借用 React hook 命名规范,以 use 结尾

<span class="hljs-built_in" style="color: #4EC9B0; line-height: 26px;">export</span> const useProvide = () => { 

  const list = ref([])
  // 获取 list
  const getList = async <span class="hljs-function" style="color: #DCDCDC; line-height: 26px;"><span class="hljs-title" style="color: #DCDCDC; line-height: 26px;">function</span></span> () { ...}
  // 新增 list
  const addList = (item) => {...}
  // 删除事件
  const delList = (id) => {...},
  ...
  
  // provide 裸露数据
  provide(listymbol, {
  list,
  getList,  
  addList,
  delList
})
}

</pre>
<h3 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #773098;”><span class=”prefix” style=”display: none;”></span><span class=”content”>2. inject 注入并应用 </span><span class=”suffix” style=”display: none;”></span></h3>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 在根组件 index.vue 中注入 </p>
<pre class=”custom” data-tool=”mdnice 编辑器 ” style=”margin-top: 10px; margin-bottom: 10px;”>import {inject} from <span class="hljs-string" style="color: #D69D85; line-height: 26px;">'vue'</span>
import {listymbol} from <span class="hljs-string" style="color: #D69D85; line-height: 26px;">'@/context/home/index'</span>

 <span class="hljs-function" style="color: #DCDCDC; line-height: 26px;"><span class="hljs-title" style="color: #DCDCDC; line-height: 26px;">setup</span></span> () {
   const listContext = inject(listymbol)
   console.log(<span class="hljs-string" style="color: #D69D85; line-height: 26px;">"setup -> listContext"</span>, listContext)
 }
</pre>
<figure data-tool=”mdnice 编辑器 ” style=”margin: 0; margin-top: 10px; margin-bottom: 10px; display: flex; flex-direction: column; justify-content: center; align-items: center;”></figure>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 如果你这样应用未尝不可,然而后面咱们说过了,在大的我的项目中,每个模块都可能有本人的 context, 为了方便管理,咱们最好是建设一个对立治理这类‘useProvide’的核心。</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 咱们再编辑新增一些逻辑 </p>
<pre class=”custom” data-tool=”mdnice 编辑器 ” style=”margin-top: 10px; margin-bottom: 10px;”>// 引入 composition api
import {provide, ref, Ref, inject, computed,} from <span class="hljs-string" style="color: #D69D85; line-height: 26px;">'vue'</span>

// provide 命名
<span class="hljs-built_in" style="color: #4EC9B0; line-height: 26px;">export</span> const listymbol = Symbol()

// 自定义 hook,借用 React hook 命名规范,以 use 结尾

<span class="hljs-built_in" style="color: #4EC9B0; line-height: 26px;">export</span> const useProvide = () => { 

  const list = ref([])
  // 获取 list
  const getList = async <span class="hljs-function" style="color: #DCDCDC; line-height: 26px;"><span class="hljs-title" style="color: #DCDCDC; line-height: 26px;">function</span></span> () { ...}
  // 新增 list
  const addList = (item) => {...}
  // 删除事件
  const delList = (id) => {...},
  ...
  
  // provide 裸露数据
  provide(listymbol, {
  list,
  getList,  
  addList,
  delList
})

// 新增的逻辑
<span class="hljs-built_in" style="color: #4EC9B0; line-height: 26px;">export</span> const useInject = () => {
  const listContext = inject(listymbol);
  <span class="hljs-keyword" style="color: #569CD6; line-height: 26px;">if</span> (!listContext) {
    throw new Error(useListInject&nbsp;must&nbsp;be&nbsp;used&nbsp;after&nbsp;useListProvide);
  }
  <span class="hljs-built_in" style="color: #4EC9B0; line-height: 26px;">return</span> listContext
};
}

</pre>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>index.js</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 对立引入并到处 </p>
<pre class=”custom” data-tool=”mdnice 编辑器 ” style=”margin-top: 10px; margin-bottom: 10px;”><span class="hljs-built_in" style="color: #4EC9B0; line-height: 26px;">export</span> {useProvide, useInject} from <span class="hljs-string" style="color: #D69D85; line-height: 26px;">'./home/index'</span>
</pre>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>index.vue</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 通过 useProvide 提供数据 </p>
<pre class=”custom” data-tool=”mdnice 编辑器 ” style=”margin-top: 10px; margin-bottom: 10px;”>import {useProvide} from <span class="hljs-string" style="color: #D69D85; line-height: 26px;">'@/context/index.js'</span>
<span class="hljs-function" style="color: #DCDCDC; line-height: 26px;"><span class="hljs-title" style="color: #DCDCDC; line-height: 26px;">setup</span></span>(){
  // 在顶层组件提供数据
  useProvide()
}
</pre>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”>inject 应用</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 通过 inject 注入数据 </p>
<pre class=”custom” data-tool=”mdnice 编辑器 ” style=”margin-top: 10px; margin-bottom: 10px;”>import {useInject}  from <span class="hljs-string" style="color: #D69D85; line-height: 26px;">'@/context/index.js'</span>

<span class="hljs-function" style="color: #DCDCDC; line-height: 26px;"><span class="hljs-title" style="color: #DCDCDC; line-height: 26px;">setup</span></span>(){
  // 在顶层组件提供数据 
  const {list, getList ...}  = useInject()
  <span class="hljs-built_in" style="color: #4EC9B0; line-height: 26px;">return</span> {
  list,getList
  }
}
</pre>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 最终,home 模块的 context 就实现了。能够看到,在 home 模块下任何组件,都能够共享数据 (list), 以及复用解决 list 的公共办法。而这样做不仅避开了 Vue2.x 跨组件通信的坑,而且在前期的保护当中,逻辑聚合将使咱们更容易找到该数据的所有相干逻辑,而不是依照组件划分。</p>
<h2 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; min-height: 32px; line-height: 28px; border-bottom: solid 1px #000000; color: #773098; display: inline-block; border-bottom-width: 1px; border-bottom-style: solid; border-color: #773098; padding-top: 5px; padding-right: 0.5em; padding-left: 0.5em; margin-bottom: -3px; font-size: 22px; margin: 1em auto; padding: 0.5em 0; text-align: center; width: 85%; font-weight: bold; display: flex; flex-direction: column; justify-content: center;”><span class=”prefix” style=”display: none;”></span><span class=”content”> 最初 </span><span class=”suffix”></span></h2>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 如果有不对的中央,欢送留言斧正。</p>
<p data-tool=”mdnice 编辑器 ” style=”font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;”> 如果该文章对你有帮忙,欢送点赞。
扫码_搜寻联结流传款式 - 红色版.png</p>

</section>React 有 Provider-Constmer+Hook 模式,Vue 3.0 也要!

前言

相熟 React 的小伙伴都晓得,随着 React hook 的推出,React 呈现了一种新型的状态治理形式 Provider-Consumer 模式,能够说是真香正告。

随着 Vue3.0 的公布,提供了咱们属于 Vue 的 Hook+provider-inject 的新型状态管理工具。 本篇文章将以 概念 + 案例 联合的模式为大家解说该模式的用法和长处。

文章目录:

  1. 引出问题,这种模式解决了什么问题?(长处)
  2. 如何应用,代码解构,我的项目案例。(以代码为主)
  3. 引出问题

首先我以自问自答的形式来引出一些问题和以往的解决方案。带着问题学习是一种很好的学习办法吧。(我要我感觉)

  1. 在 vue 中,兄弟组件、祖孙组件如何通信?

看到第一个问你,你是不是嘴角微微上扬,45 度角俯视天空,微撩刘海(如果你不是秃头的话????),感觉这很 easy

  • Vuex; 不论多简单的组件通信,Vuex 好像一键搞定。然而,Vuex 作为一个状态治理的重型利器,在面对大量的须要多组件共享的数据,显然是杀鸡用???????? 啊。况且在一个小型的 Vue 我的项目中,根本都不必 Vuex,太重了不是么?
  • EventBus;这种办法罕用于兄弟级别的组件通信。弊病也是非常明显;
  1. EventBus 不会随着组件的销毁而主动销毁,须要咱们手销毁;
  2. 挂在到全局;
  3. 留神:肯定要记得销毁,否则会呈现 bug;
  4. 事件产生者只能单向播送,无奈取得接收者对事件的处理结果;
  • 其余(attrs,provide-inject(vue2.x 的, 本地缓存,url 传参)。这些办法都不太罕用,尽管能勉强解决跨组件通信,但都是有种本末倒置的感觉。(我要我感觉????);
  1. 在 Vue2.x 中,如何进行逻辑复用?

举个栗子:在后盾治理我的项目中,多个菜单(组件)同时用到一堆数据,进行花色出现,有列表展现、有饼图占比、有折线图趋势、有热力求阐明频次等等,这些组件应用的是雷同的一些数据和数据处理逻辑(增删改查);如果在每个组件只都写一套这类的解决逻辑,显然是不合乎程序员的终极目标(偷懒????);以往的解决方案:

  • Mixins
  • 高阶组件 (Higher-order Components, aka HOCs)
  • Renderless Components (基于 scoped slots / 作用域插槽封装逻辑的组件)

其实下面在写业务代码的时候,也就 mixin 罕用。高阶组件和 Renderless Components 更多的是封装组件的时候用到的。它们存在的毛病:

  • 模版中的数据起源不清晰
  • 命名空间抵触。
  • 须要额定的组件实例嵌套来封装逻辑(性能问题);

那么 Vue 将如何解决以上这些问题呢。

概念

context

咱们要利用 provide+inject+hook 创立一个上下文对象,而且对外裸露 供应者(通常在组件树中上层的地位和消费者(inject)),只有在下层组件应用 provide, 在上下文以内的所有子组件,通过 inject 都可能接见这个上下文环境以内的数据,而且不用经由过程 props。也就是提供了一个汇合治理并具备响应式的对象,并限度了这个对象可接见的局限,在局限内的子组件都能猎取到她外部的值。

provide+inject

在下层组件通过 provide 提供一些变量,在子组件中能够通过 inject 来拿到

provide 用法:

provide 承受两个参数,第一个参数是 provide 惟一名称,最好用 Symbol, 防止反复。第二个参数是你要裸露的数据

 provide(ThemeSymbol, data)

inject 用法:

inject 接管两个参数,第一个参数是provide 名称, 也就是 ThemeSymbol,第二个参数是 provide 裸露进去的数据,也就是 data。

Hook

Vue3.0 的 composition api 和 setup 提供了 Vue 具备 Hook 的能力。而 Hook 的存在能够让你在组件之外封装公共办法,达到逻辑复用。所以,咱们能够在.vue 组件外自定义 Hook,创立 context;

文件目录解构

依据下面的观点和功能,分析一下要实现的步骤:

  • 建设一个公共 context,封装自定义 hook
  • provide 供给数据
  • inject 注入数据

这里的文件结构:

context

├─context      //   context 的文件夹
  ├─index.js   // context 根组件,对立引入各个模块的 context, 并对立裸露
  │─home      // 以模块名称命名,一个模块一个 context
    │─index.js 

View

 ├─View
   ├─Home
     ├─index.vue  // 根组件,在这层注入 inject
     ├─xxx.vue // 其余组件  消费者地点的层级

用法

为了不便解读,我这里以 Todolist 我的项目为例;

源码:https://github.com/961998264/todolist-vue-3.0

我的项目介绍:

  1. 新增待办事件
  2. 编辑待办事件
  3. 删除待办事件
  4. 查看待办事件

1. 建设一个上下文 context

// 引入 composition api
import {provide, ref, Ref, inject, computed,} from 'vue'
// provide 命名
export const listymbol = Symbol()
// 自定义 hook,借用 React hook 命名规范,以 use 结尾
export const useProvide = () => {const list = ref([])
  // 获取 list
  const getList = async function () { ...}
  // 新增 list
  const addList = (item) => {...}
  // 删除事件
  const delList = (id) => {...},
  ...
  
  // provide 裸露数据
  provide(listymbol, {
  list,
  getList,  
  addList,
  delList
})
}

2. inject 注入并应用

在根组件 index.vue 中注入

import {inject} from 'vue'
import {listymbol} from '@/context/home/index'
 setup () {const listContext = inject(listymbol)
   console.log("setup -> listContext", listContext)
 }

如果你这样应用未尝不可,然而后面咱们说过了,在大的我的项目中,每个模块都可能有本人的 context, 为了方便管理,咱们最好是建设一个对立治理这类‘useProvide’的核心。

咱们再编辑新增一些逻辑

// 引入 composition api
import {provide, ref, Ref, inject, computed,} from 'vue'
// provide 命名
export const listymbol = Symbol()
// 自定义 hook,借用 React hook 命名规范,以 use 结尾
export const useProvide = () => {const list = ref([])
  // 获取 list
  const getList = async function () { ...}
  // 新增 list
  const addList = (item) => {...}
  // 删除事件
  const delList = (id) => {...},
  ...
  
  // provide 裸露数据
  provide(listymbol, {
  list,
  getList,  
  addList,
  delList
})
// 新增的逻辑
export const useInject = () => {const listContext = inject(listymbol);
  if (!listContext) {throw new Error(`useListInject must be used after useListProvide`);
  }
  return listContext
};
}

index.js

对立引入并到处

export {useProvide, useInject} from './home/index'

index.vue

通过 useProvide 提供数据

import {useProvide} from '@/context/index.js'
setup(){
  // 在顶层组件提供数据
  useProvide()}

inject 应用

通过 inject 注入数据

import {useInject}  from '@/context/index.js'
setup(){
  // 在顶层组件提供数据 
  const {list, getList ...}  = useInject()
  return {list,getList}
}

最终,home 模块的 context 就实现了。能够看到,在 home 模块下任何组件,都能够共享数据(list), 以及复用解决 list 的公共办法。而这样做不仅避开了 Vue2.x 跨组件通信的坑,而且在前期的保护当中,逻辑聚合将使咱们更容易找到该数据的所有相干逻辑,而不是依照组件划分。

最初

如果有不对的中央,欢送留言斧正。

如果该文章对你有帮忙,欢送点赞。扫码_搜寻联结流传款式 - 红色版.png

正文完
 0