随着组件的细化,就会遇到多组件状态共享的情况,Vuex当然可以解决这类问题,不过就像Vuex官方文档所说的,如果应用不够大,为避免代码繁琐冗余,最好不要使用它,今天我们介绍的是vue.js 2.6新增加的,通过使用这个api我们可以应对一些简单的跨组件数据状态共享的情况。
如下这个例子,我们将在组件外创建一个store,然后在App.vue组件里面使用store.js提供的store和mutation方法,同理其它组件也可以这样使用,从而实现多个组件共享数据状态。
首先创建一个store.js,包含一个store和一个mutations,分别用来指向数据和处理方法。
import Vue from "vue"; export const store = Vue.observable({ count: 0 }); export const mutations = { setCount(count) { store.count = count; } };
然后在App.vue里面引入这个store.js,在组件里面使用引入的数据和方法
<template> <div> <img src="https://www.jb51.net/article/assets/logo.png"> <p>count:{{count}}</p> <button @click="setCount(count+1)">+1</button> <button @click="setCount(count-1)">-1</button> </div> </template> <script> import { store, mutations } from "./store"; export default { name: "App", computed: { count() { return store.count; } }, methods: { setCount: mutations.setCount } }; </script> <style>
你可以点击在线DEMO查看最终效果
2 长列表性能优化
我们应该都知道vue会通过object.defineProperty对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要vue来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止vue劫持我们的数据呢?可以通过object.freeze方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。
export default { data: () => ({ users: {} }), async created() { const users = await axios.get("/api/users"); this.users = Object.freeze(users); } };
另外需要说明的是,这里只是冻结了users的值,引用不会被冻结,当我们需要reactive数据的时候,我们可以重新给users赋值。
export default { data: () => ({ users: [] }), async created() { const users = await axios.get("/api/users"); this.users = Object.freeze(users); }, methods:{ // 改变值不会触发视图响应 this.users[0] = newValue // 改变引用依然会触发视图响应 this.users = newArray } };
3 去除多余的样式
随着项目越来越大,书写的不注意,不自然的就会产生一些多余的css,小项目还好,一旦项目大了以后,多余的css会越来越多,导致包越来越大,从而影响项目运行性能,所以有必要在正式环境去除掉这些多余的css,这里推荐一个库purgecss,支持CLI、JavascriptApi、Webpack等多种方式使用,通过这个库,我们可以很容易的去除掉多余的css。
我做了一个测试,在线DEMO
<h1>Hello Vanilla!</h1> <div> We use Parcel to bundle this sandbox, you can find more info about Parcel <a href="https://parceljs.org" target="_blank">here</a>. </div> body { font-family: sans-serif; } a { color: red; } ul { li { list-style: none; } } import Purgecss from "purgecss"; const purgecss = new Purgecss({ content: ["**/*.html"], css: ["**/*.css"] }); const purgecssResult = purgecss.purge();
最终产生的purgecssResult结果如下,可以看到多余的a和ul标签的样式都没了
4 作用域插槽
利用好作用域插槽可以做一些很有意思的事情,比如定义一个基础布局组件A,只负责布局,不管数据逻辑,然后另外定义一个组件B负责数据处理,布局组件A需要数据的时候就去B里面去取。假设,某一天我们的布局变了,我们只需要去修改组件A就行,而不用去修改组件B,从而就能充分复用组件B的数据处理逻辑,关于这块我之前写过一篇实际案例,可以点击这里查看。
这里涉及到的一个最重要的点就是父组件要去获取子组件里面的数据,之前是利用slot-scope,自vue 2.6.0起,提供了更好的支持 slot 和 slot-scope 特性的 API 替代方案。
比如,我们定一个名为current-user的组件:
<span> <slot>{{ user.lastName }}</slot> </span>
父组件引用current-user的组件,但想用名替代姓(老外名字第一个单词是名,后一个单词是姓):
<current-user> {{ user.firstName }} </current-user>
这种方式不会生效,因为user对象是子组件的数据,在父组件里面我们获取不到,这个时候我们就可以通过v-slot
来实现。
首先在子组件里面,将user作为一个<slot>元素的特性绑定上去:
<span> <slot v-bind:user="user"> {{ user.lastName }} </slot> </span>
之后,我们就可以在父组件引用的时候,给v-slot带一个值来定义我们提供的插槽 prop 的名字:
<current-user> <template v-slot:default="slotProps"> {{ slotProps.user.firstName }} </template> </current-user>
这种方式还有缩写语法,可以查看独占默认插槽的缩写语法,最终我们引用的方式如下:
<current-user v-slot="slotProps"> {{ slotProps.user.firstName }} </current-user>
相比之前slot-scope代码更清晰,更好理解。
5 属性事件传递