做正直的人不易 發表於 2020-11-4 13:49:00

Vue3.x 从零开始(五)—— Router + Vuex + TypeScript 实战演练(上)

<p>前面的几篇文章已经大致介绍了 Vue 3 的常用 API,现在综合起来做一个实战演练</p>
<p>配合完整代码食用更香哦,项目地址:https://github.com/wisewrong/bolg-demo-app/tree/main/test-vue3-demo<br></p>
<p>&nbsp;</p>
<p><strong>一、初始化</strong></p>
<p>首先通过 Vue-CLI 创建一个 Vue 3 项目,详细流程可以参考《Vue3.x 从零开始(一)》</p>
<div class="cnblogs_code">
<pre>vue create test-vue3-demo</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201019142133957-2079321059.png" alt="" width="374" height="74" loading="lazy"></p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201019145806702-1721223625.png" alt="" width="378" height="199" loading="lazy"></p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201019142400230-1106612681.png" alt="" width="520" height="54" loading="lazy"></p>
<p>勾选 TypeScript、Router、Vuex,版本选用 Vue 3.x,其他的选项可以自行选择,拿不准就直接回车选择默认</p>
<p>初始化完成后的项目是这样的:</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201019144412590-2031807216.png" alt="" width="272" height="400" loading="lazy"></p>
<p>store 目录用来维护基于 Vuex 开发的状态仓库</p>
<p>router 目录维护基于 vue-router 开发的路由配置</p>
<p>main.ts 是项目的入口文件,在这里将 Router 和 Vuex 载入项目中:</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201019170400324-518261938.png" alt="" width="608" height="177" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong>二、头部导航<strong>( Router )</strong></strong></p>
<p>首先需要创建头部导航 &lt;header&gt; 组件,header 属于公共组件,可以放到 components 目录下</p>
<p>header 上有 Home 和 About 两个页面的导航入口,点击导航可以跳转到对应的页面</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201020161027678-2091486835.gif" alt="" loading="lazy"></p>
<p>这个功能可以通过 vue-router 提供的<span style="color: rgba(0, 128, 128, 1)"><strong> &lt;router-link&gt;</strong> </span>来实现</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201019163628114-1279160772.png" alt="" width="573" height="276" loading="lazy"></p>
<p>&lt;router-link&gt; 是经过封装的 &lt;a&gt; 标签,它需要接收一个<strong><span style="color: rgba(128, 0, 0, 1)">路由地址</span></strong> <span style="color: rgba(128, 0, 0, 1)"><span style="color: rgba(0, 128, 128, 1)"><strong>to</strong></span><span style="color: rgba(0, 0, 0, 1)">,类似于 &lt;a&gt; 标签的 href</span></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">router-link </span><span style="color: rgba(255, 0, 0, 1)">to</span><span style="color: rgba(0, 0, 255, 1)">="/home"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>Home<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">router-link</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p>如果目标路由地址配置了组件,就能在父组件的<strong><span style="color: rgba(0, 128, 128, 1)">&nbsp;&lt;router-view&gt;</span> </strong>中渲染对应组件</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201019164910634-323905608.png" alt="" width="496" height="182" loading="lazy">&nbsp;</p>
<p>路由的配置文件是&nbsp;<span class="cnblogs_code">src/router/index.ts</span>&nbsp;,可以配置路由信息,包括路由地址和对应的组件</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201019165349721-1091894461.png" alt="" width="499" height="169" loading="lazy"></p>
<p>不过 &lt;router-link&gt; 只适合导航菜单这种【<span style="color: rgba(128, 0, 0, 1)"><strong>只需要跳转页面,不需要做其他操作</strong></span>】的场景</p>
<p>更多的时候我们需要<span style="color: rgba(128, 0, 0, 1)"><strong>在函数中进行路由跳转</strong></span>,这时候可以使用&nbsp;&nbsp;<span class="cnblogs_code"><span style="color: rgba(0, 0, 255, 1)">this</span>.$router.push()</span>&nbsp;</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201019175015758-1660143012.png" alt="" width="363" height="134" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong>三、登录框弹窗(&nbsp;teleport + slot )</strong></p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201020165958513-495193705.gif" alt="" width="642" height="479" loading="lazy"></p>
<p>首先来完成弹窗组件 &lt;modal&gt;</p>
<p>从业务上来讲,这个弹窗组件是在 &lt;header&gt; 上打开的,也就是说 &lt;header&gt; 会是 &lt;modal&gt; 的父组件</p>
<p>如果按照传统开发组件的方式,&lt;modal&gt; 会渲染到父组件 &lt;header&gt; 的 DOM 节点下</p>
<p>但从交互的层面来说,弹窗是一个全局性的强交互,组件应该渲染到外部,比如 body 标签</p>
<p>在 Vue 3 中提供了一个新的解决方案 teleport</p>
<p>在组件中用 &lt;teleport&gt; 组件包裹需要渲染到外部的模板,然后通过 <span style="color: rgba(128, 0, 0, 1)"><strong>to</strong></span> 指定渲染的 DOM 节点</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">teleport </span><span style="color: rgba(255, 0, 0, 1)">to</span><span style="color: rgba(0, 0, 255, 1)">="body"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 组件内容</span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">teleport</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p><span style="color: rgba(128, 0, 0, 1)"><strong>to</strong></span> 接收一个可以被 querySelector 识别的字符串参数,用于查找目标 DOM 节点,该 DOM 节点必须在组件外部</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201020170941352-418780880.png" alt="" width="595" height="447" loading="lazy"></p>
<p>这里将 modal 组件渲染到了body 标签</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201020175229333-1078559296.gif" alt="" width="628" height="485" loading="lazy">&nbsp;</p>
<p>上面的代码还用到了插槽 &lt;slot&gt;&nbsp;</p>
<p>这个标签允许父组件向子组件插入自定义的模板内容,在 &lt;modal&gt; 组件中可以让父组件编辑弹窗的内容</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201020175942825-1187937959.png" alt="" width="397" height="160" loading="lazy"></p>
<p>如果组件中需要配置多个 &lt;slot&gt; 标签,还可以用 name 来给 &lt;slot&gt; 命名</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="modal-header"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">slot </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="header"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 这里是 slot-header 的默认模板 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">span </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="modal-title"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>{{title}}<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">span</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">button </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="modal-close"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">button</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">slot</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span> </pre>
</div>
<p>然后在父组件中通过&nbsp;<span style="color: rgba(0, 128, 128, 1)"><strong>&lt;template v-slot:name&gt; </strong><span style="color: rgba(0, 0, 0, 1)">向指定的 slot 插入内容</span></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">template </span><span style="color: rgba(255, 0, 0, 1)">v-slot:header</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span><span style="color: rgba(0, 0, 0, 1)">
    这里是 slot-header 的内容
</span><span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">template</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong>四、完成弹窗表单( $refs + Vuex )</strong></p>
<p>接下来开发登录窗的表单组件 &lt;sign-in-form&gt;</p>
<p>组件的内容十分简单,就是两个输入框 &lt;input /&gt;,不多介绍,重点在于获取表单数据</p>
<p>由于这个 &lt;form&gt; 组件是 &lt;header&gt; 的子组件,所以我们需要在 &lt;header&gt; 获取 &lt;form&gt; 的数据并提交</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201020192417712-1586260159.png" alt="" width="687" height="377" loading="lazy"></p>
<p>Vue 中可以通过 ref 属性获取自定义组件的实例</p>
<p>比如上面的代码就在 &lt;sign-in-form&gt; 组件上指定了 ref="signInForm"</p>
<p>然后就能在 header 组件中通过 this.$refs.signinForm 获取到表单组件的实例,并直接使用它的 methods 或者 data</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 没有找到适合 $ref 的类型断言,只好用 any</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> data = (<span style="color: rgba(0, 0, 255, 1)">this</span>.$refs.signInForm <span style="color: rgba(0, 0, 255, 1)">as</span> any).getValue();<span style="color: rgba(0, 128, 0, 1)"> // getValue 是 signInForm 组件中的 methods</span></pre>
</div>
<p>现在获取到了登录信息,正常来说需要用登录信息请求登录接口,如果用户名和密码正确,接口会返回用户信息</p>
<p>这里我们就跳过请求接口的过程,直接把登录信息当做用户信息</p>
<p>用户信息对于整个项目来说是一个共用信息,我们可以选择暂存在&nbsp;localStorage 或 sessionStorage 中,也可以使用 Vuex 来管理</p>
<p>如果在使用 Vue-CLI 创建项目时勾选了 Vuex,就能在&nbsp;<span class="cnblogs_code">src/store/index.ts</span>&nbsp;中维护公共变量和方法</p>
<p>然后在组件中通过 <span style="color: rgba(0, 128, 128, 1)"><strong>this.$store</strong></span> 来使用 Vuex 提供的 API</p>
<hr>
<p>Vuex 中有&nbsp;State、Getter、Mutation、Action、Module 五个核心属性</p>
<p>其中 <strong><span style="color: rgba(0, 128, 128, 1)">State</span></strong> 就像是 Vue 组件中的相应数据 data,<span style="color: rgba(0, 128, 128, 1)"><strong>Getter</strong></span>&nbsp;类似于计算属性 computed</p>
<p>然后 Mutation 和&nbsp;Action 都可以看做&nbsp;methods,区别在于:</p>
<p>Mutation 是同步函数,用来更新 state(在严格模式下只能通过&nbsp;mutation 来更新 state)</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Vuex</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> state =<span style="color: rgba(0, 0, 0, 1)"> {
user: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">wise wrong</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
};

</span><span style="color: rgba(0, 0, 255, 1)">const</span> mutations =<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 所有 mutation 的第一个参数都是 state,后面的参数在调用时传入</span>
<span style="color: rgba(0, 0, 0, 1)">updateUser(state, payload) {
    state.user </span>=<span style="color: rgba(0, 0, 0, 1)"> payload;
}
};

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 组件中通过 commit 来调用 mutations</span>
export <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ...</span>
<span style="color: rgba(0, 0, 0, 1)">methods: {
    handler() {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.$store.commit(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">updateUser</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">new user</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
    }
},
};</span></pre>
</div>
<p>而 Action 可以看做&nbsp;Mutation 的父级,用来提交&nbsp;Mutation,而且可以包含异步函数</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Vuex</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> actions =<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> action 的第一个参数是 context,其中包含 commit,用来调用 mutation</span>
<span style="color: rgba(0, 0, 0, 1)">fetchUser(context, payload) {
    fetch(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">/api</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, payload)
      .then((res) </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 调用 mutation</span>
      context.commit(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">updateUser</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, res.data);
      })
      .</span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)">()
}
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 组件中通过 dispatch 来调用 action</span>
export <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ...</span>
<span style="color: rgba(0, 0, 0, 1)">methods: {
    handler() {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.$store.dispatch(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">fetchUser</span><span style="color: rgba(128, 0, 0, 1)">'</span>, {id: <span style="color: rgba(128, 0, 128, 1)">123</span><span style="color: rgba(0, 0, 0, 1)">});
    }
},
};</span></pre>
</div>
<p>&nbsp;</p>
<p>回到我们的项目上来,由于我们用的是 TypeScript,所以需要提前定义 state 的类型</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 用户信息</span>
export <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> UserState {
user: </span><span style="color: rgba(0, 0, 255, 1)">string;</span><span style="color: rgba(0, 0, 0, 1)">
password: </span><span style="color: rgba(0, 0, 255, 1)">string;</span><span style="color: rgba(0, 0, 0, 1)">
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> state 的根类型</span>
export <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> RootState {
userInfo: UserState;
}</span></pre>
</div>
<p>然后创建 state.ts 和 mutations.ts</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201021145854195-352026809.png" alt="" width="395" height="245" loading="lazy"></p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201021145809261-672047195.png" alt="" width="511" height="178" loading="lazy"></p>
<p>然后在组件中通过 commit 调用 mutation 以更新用户信息:</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201021151844365-1906983744.png" alt="" width="543" height="109" loading="lazy"></p>
<p>由于<span style="color: rgba(128, 0, 0, 1)"><strong>在 Vuex 4 中删除了对全局属性 $store 的类型支持</strong></span>,所以上面的截图中 $store 被标红,代码也无法运行</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201021152113037-930218172.png" alt="" width="846" height="86" loading="lazy"></p>
<p>为解决该问题,可以在 src 目录下创建一个&nbsp;<span class="cnblogs_code">shims-vuex.d.ts</span>&nbsp;文件,手动声明 $store</p>
<div class="cnblogs_code">
<pre>import { Store } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">vuex</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;

declare module </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">@vue/runtime-core</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> ComponentCustomProperties {
    $store: Store;
}
}</span></pre>
</div>
<p>但我更<strong>推荐使用 Vue 提供的辅助函数 map</strong></p>
<p>对于 state,我们可以在 computed 中使用 <strong><span style="color: rgba(0, 128, 128, 1)">mapState</span></strong> 将需要的 state 映射到当前组件</p>
<div class="cnblogs_code">
<pre>import { mapState } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">vuex</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ...</span>
<span style="color: rgba(0, 0, 0, 1)">computed: {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 组件本身的计算属性</span>
    localComputed () { <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> ... </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> },

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用对象展开运算符将 state 混入当前组件</span>
<span style="color: rgba(0, 0, 0, 1)">    ...mapState([
      </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">userInfo</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 以数组的形式传入 state 的键名</span>
<span style="color: rgba(0, 0, 0, 1)">    ])
},

methods: {
    test() {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 以计算属性的形式使用 state</span>
      console.log(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.user);
    }
}
}</span></pre>
</div>
<p>同样的,对于 mutations 可以通过&nbsp;<span style="color: rgba(0, 128, 128, 1)"><strong>mapMutations</strong></span> 混入 methods 中</p>
<div class="cnblogs_code">
<pre>import { mapMutations } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">vuex</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ...</span>
<span style="color: rgba(0, 0, 0, 1)">methods: {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 组件本身的方法</span>
<span style="color: rgba(0, 0, 0, 1)">    test() {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 以 methods 的形式调用 mutation</span>
      <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.updateUserInfo();
    },
    ...mapMutations([
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 混入名为 updateUserInfo 的 mutation</span>
      <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">updateUserInfo</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
    ])
}
}</span></pre>
</div>
<p>上面 this.$store.commit 的调用方式就可以替换成:</p>
<p><img src="https://img2020.cnblogs.com/blog/1059788/202010/1059788-20201021155333341-29963805.png" alt="" width="594" height="229" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-size: 14px">到这里为止已经具备了一个简单的 Vue 项目的雏形</span></p>
<p><span style="font-size: 14px">接下来会打造一个综合性的 Todo List,并真正用上 Composition API</span></p><br><br>
来源:https://www.cnblogs.com/wisewrong/p/13839396.html
頁: [1]
查看完整版本: Vue3.x 从零开始(五)—— Router + Vuex + TypeScript 实战演练(上)