百科狗-知识改变命运!
--

state - 子组件通过 $store 访问其数据 - vuex(状态管理)

是丫丫呀1年前 (2023-11-21)阅读数 29#技术干货
文章标签数组

state

单一状态树

Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源”而存在。这也意味着,每个应用将仅仅包含一个store实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。

单状态树和模块化并不冲突——在后面的章节里我们会讨论如何将状态和状态变更事件分布到各个子模块中。

存储在 Vuex 中的数据和 Vue 实例中的data遵循相同的规则,例如状态对象必须是纯粹(plain)的。。


在 Vue 组件中获得 Vuex 状态

那么我们如何在 Vue 组件中展示状态呢?由于 Vuex 的状态存储是响应式的,从store实例中读取状态最简单的方法就是在计算属性中返回某个状态:

// 创建一个 Counter 组件
const Counter = {
  template: `
{{ count }}
`, computed: { count () { return store.state.count } } }

每当store.state.count变化的时候,都会重新求取计算属性,并且触发更新相关联的 DOM。

然而,这种模式导致组件依赖全局状态单例。在模块化的构建系统中,在每个需要使用state的组件中需要频繁地导入,并且在测试组件时需要模拟状态。

Vuex 通过 Vue 的插件系统将store实例,从根组件中“注入”到所有的子组件里。且子组件能通过this.$store访问到。让我们更新下 Counter 的实现:

const Counter = {
  template: `
{{ count }}
`, computed: { count () { return this.$store.state.count } } }


mapState辅助函数

当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用mapState()辅助函数帮助我们生成计算属性,让你少按几次键:

// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // 箭头函数可使代码更简练
    count: state => state.count,

    // 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: 'count',

    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}

当映射的计算属性的名称与state 的子节点名称相同时,我们也可以给mapState传一个字符串数组。

computed: mapState([
  // 映射 this.count 为 store.state.count
  'count'
])


对象展开运算符

mapState函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给computed属性。但是自从有了对象展开运算符,我们可以极大地简化写法:

computed: {
  localComputed () { /* ... */ },
  // 使用对象展开运算符将此对象混入到外部对象中
  ...mapState({
    // ...
  })
}


组件仍然保有局部状态

使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。



扩展运算符

ES2015(ES6)新增了一种基本运算符——展开运算符,使用三个点...表示。

可以在函数调用/数组构造时,将数组表达式或者string在语法层面展开;还可以在构造字面量对象时,将对象表达式按key-value的方式展开。(译者注:字面量一般指[1, 2, 3]或者{name:"mdn"}这种简洁的构造方式)

下面我们来看看它的基本用法:

// 展开基本的数组
const arr = ['apple','orange','banana'];
console.log(...arr);  // 'apple' 'orange' 'banana'

实时上上面的语句只是在语法层面展开了数组arr,让每一个元素作为log函数的参数。


语法

函数调用:

myFunction(...iterableObj);

字面量数组构造或字符串:

[...iterableObj,'4',...'hello', 6];

构造字面量对象时,进行克隆或者属性拷贝(ECMAScript 2018规范新增特性):

let objClone ={...obj};


在函数调用时使用

等价于apply的方式。如果想将数组元素迭代为函数参数,一般使用Function.prototype.apply 的方式进行调用。

function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction.apply(null, args);


//有了展开语法,可以这样写:

function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction(...args);

所有参数都可以通过展开语法来传值,也不限制多次使用展开语法。

function myFunction(v, w, x, y, z) { }
var args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);


function push(array, ...items) {
    array.push(...items);
}

function add(x, y) {
    return x + y;
}

var numbers = [4, 38];
add(...numbers)	// 42

上面代码中,array.push(...items)和add(...numbers)这两行,都是函数的调用,它们的都使用了扩展运算符。该运算符将一个数组,变为参数序列。

扩展运算符与正常的函数参数可以结合使用,非常灵活

function f(v, w, x, y, z) { }
var args = [0, 1];
f(-1, ...args, 2, ...[3]);



在 new 表达式中应用

使用 new 关键字来调用构造函数时,不能直接使用数组+ apply 的方式(apply 执行的是调用[[Call]],而不是构造[[Construct]])。当然,有了展开语法,将数组展开为构造函数的参数就很简单了:

var dateFields = [1970, 0, 1];	// 1970年1月1日
var d = new Date(...dateFields);


构造字面量数组时使用

构造字面量数组时更给力!通常情况下构建字面量结构的数组我们会使用如pushspliceunshiftconcat等函数将现有数组作为新数组的一部分。展开运算符可以更简单快速的完成这项工作。

var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];		// ["head", "shoulders", "knees", "and", "toes"]

和参数列表的展开类似,...在构造字面量数组时,可以在任意位置多次使用。


数组拷贝(copy)

var arr = [1, 2, 3];
var arr2 = [...arr]; // like arr.slice()
arr2.push(4);

// arr2 此时变成 [1, 2, 3, 4]
// arr 不受影响

提示:实际上,展开语法和 Object.assign()行为一致,执行的都是浅拷贝(只遍历一层)。如果想对多维数组进行深拷贝,下面的示例就有些问题了。

var a = [[1], [2], [3]];
var b = [...a];
b.shift().shift(); // 1
// Now array a is affected as well: [[2], [3]]


连接多个数组

Array.concat 函数常用于将一个数组连接到另一个数组的后面。如果不使用展开语法, 代码可能是下面这样的:

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
var arr3 = arr1.concat(arr2);

使用展开语法:

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
var arr3 = [...arr1, ...arr2];
Array.unshift 方法常用于在数组的开头插入新元素/数组.  不使用展开语法, 示例如下:

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.unshift.apply(arr1, arr2) // arr1 现在是 [3, 4, 5, 0, 1, 2]


如果使用展开语法, 代码如下:  [请注意, 这里使用展开语法创建了一个新的 arr1 数组,  Array.unshift 方法则是修改了原本存在的 arr1 数组]:

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1 = [...arr2, ...arr1]; // arr1 现在为 [3, 4, 5, 0, 1, 2]



构造字面量对象时使用展开语法

Rest/Spread Properties for ECMAScript 提议(stage 4)对字面量对象增加了展开特性。其行为是,将已有对象的所有可枚举(enumerable)属性拷贝到新构造的对象中.

浅拷贝(Shallow-cloning,不包含 prototype)和对象合并,可以使用更简短的展开语法。而不必再使用 Object.assign()方式.

var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };

var clonedObj = { ...obj1 };	// 克隆后的对象: { foo: "bar", x: 42 }

var mergedObj = { ...obj1, ...obj2 };	// 合并后的对象: { foo: "baz", x: 42, y: 13 }

提示: Object.assign()函数会触发 setters,而展开语法则不会。

提示:不能替换或者模拟 Object.assign()函数:

var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
const merge = ( ...objects ) => ( { ...objects } );

var mergedObj = merge ( obj1, obj2);	// Object { 0: { foo: 'bar', x: 42 }, 1: { foo: 'baz', y: 13 } }

var mergedObj = merge ( {}, obj1, obj2);	// Object { 0: {}, 1: { foo: 'bar', x: 42 }, 2: { foo: 'baz', y: 13 } }

在这段代码中,展开操作符(spread operator)并没有按预期的方式执行:而是先将多个解构变为剩余参数(rest parameter),然后再将剩余参数展开为字面量对象.

只能用于可迭代对象

在数组或函数参数中使用展开语法时,该语法只能用于可迭代对象:

var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable



合并数组

扩展运算符提供了数组合并的新写法。

// ES5
[1, 2].concat(more)

// ES6
[1, 2, ...more]
var arr1 = ['a', 'b'];
var arr2 = ['c'];
var arr3 = ['d', 'e'];

// ES5 的合并数组
arr1.concat(arr2, arr3);	// [ 'a', 'b', 'c', 'd', 'e' ]

// ES6 的合并数组
[...arr1, ...arr2, ...arr3]	// [ 'a', 'b', 'c', 'd', 'e' ]


与解构赋值结合

扩展运算符可以与解构赋值结合起来,用于生成数组。

const arr1 = ['welcome','to'];
const arr2 = ['hello',...arr1,'here'];   // ["hello", "welcome", "to", "here"]
// ES5
a = list[0], rest = list.slice(1)

// ES6
[a, ...rest] = list
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]

const [first, ...rest] = [];
first // undefined
rest // []:

const [first, ...rest] = ["foo"];
first // "foo"
rest // []

如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。

const [...butLast, last] = [1, 2, 3, 4, 5];	//  报错
const [first, ...middle, last] = [1, 2, 3, 4, 5];	//  报错

state - 子组件通过 $store 访问其数据 - vuex(状态管理)


函数的返回值

JavaScript 的函数只能返回一个值,如果需要返回多个值,只能返回数组或对象。扩展运算符提供了解决这个问题的一种变通方法。

var dateFields = readDateFields(database);
var d = new Date(...dateFields);

上面代码从数据库取出一行数据,通过扩展运算符,直接将其传入构造函数Date。


字符串

扩展运算符还可以将字符串转为真正的数组。

[...'hello']	// [ "h", "e", "l", "l", "o" ]

JavaScript 会将 32 位 Unicode 字符,识别为 2 个字符,采用扩展运算符就没有这个问题


实现了 Iterator 接口的对象

任何 Iterator 接口的对象,都可以用扩展运算符转为真正的数组

var nodeList = document.querySelectorAll('div');
var array = [...nodeList];

上面代码中,querySelectorAll方法返回的是一个nodeList对象。它不是数组,而是一个类似数组的对象。这时,扩展运算符可以将其转为真正的数组,原因就在于NodeList对象实现了 Iterator 接口。


Map 和 Set

扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符,比如 Map 结构。

let map = new Map([
    [1, 'one'],
    [2, 'two'],
    [3, 'three'],
]);
let arr = [...map.keys()];	// [1, 2, 3]

鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com

免责声明:我们致力于保护作者版权,注重分享,当前被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!邮箱:344225443@qq.com)

图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

内容声明:本文中引用的各种信息及资料(包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主体(包括但不限于公司、媒体、协会等机构)的官方网站或公开发表的信息。部分内容参考包括:(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供参考使用,不准确地方联系删除处理!本站为非盈利性质站点,本着为中国教育事业出一份力,发布内容不收取任何费用也不接任何广告!)