前言
副作用并不是绝对不好的,因为它们在某些情况下可能会有用,比如在 setTimeout()
函数中。但我们需要努力确保我们知道这些副作用。
什么是副作用
副作用 (Side Effect) 是指当一个函数改变或修改不在其作用域内的数据时。一个简单的例子是一个接收一个数组作为参数的函数,在函数内部修改了该数组的数据。
const numberList = [1, 2, 3, 4, 5, 6];
function addItem(item) {
return item.push(item.length - 1 + 1);
}
addItem(numberList);
console.log(numberList);
在上面的例子中,我们发现在调用 addItem
时 numberList
被修改了。
Vue 中的副作用
例子1:计算属性中的副作用,一个例子如下:
<template>
<div>
Unsorted Data: {{ languages }}
</div>
<div>
Sorted Data: {{ sortLanguages }}
</div>
</template>
<script>
export default {
name: ‘languages-list’,
data() {
return {
languages: [
{
name: "React",
},
{
name: "Vue",
},
{
name: "Angular",
},
{
name: "Svelte",
}
]
},
},
computed: {
sortLanguages() {
return this.languages.sort( ( a, b ) => {
if ( a.name < b.name ) { return -1; }
if ( a.name > b.name ) { return 1; }
return 0;
} )
}
}
}
</script>
从上面的例子中,我们可以看到未排序的数据和排序后的数据是相同的。当引用 sortedLanguages
时,languages
数据发生了变化。如果不正确处理,这是一个可能导致出现意外行为的副作用。
例子2:访问 getter 时的副作用
试着在访问和分配 getter 数据时创建一个副作用的例子。
state: {
languages: [
{
name: "React",
},
{
name: "Vue",
},
{
name: "Angular",
},
{
name: "Svelte",
}
]
},
getters: {
getLanguages: function (state) {
return state.languages;
}
},
actions: {
sortLanguages: function (context) {
const languages = JSON.parse(JSON.stringify(context.getters.getLanguages()));
return languages.sort((a, b) => {
if (a.name < b.name) { return -1; }
if (a.name > b.name) { return 1; }
return 0;
});
}
}
在这个例子中,我们可以看到当调用(dispatch)sortLanguages
动作时,存储会得到更新。这是一个副作用,可能导致应用程序出现意外行为,因为languages
状态被改变。
如何解决这些副作用呢?
- 浅拷贝
computed: {
sortLanguages() {
const languagesClone = this.languages.slice();
return languagesClone.sort( ( a, b ) => {
if ( a.name < b.name ) { return -1; }
if ( a.name > b.name ) { return 1; }
return 0;
} )
}
}
当我们使用.slice()
方法对语言数组进行浅克隆时,它会返回一个原数组部分的浅拷贝,生成一个新的数组对象。我们这样做是为了防止对数据的修改。
- 深拷贝
computed: {
sortLanguages() {
const languagesClone = json.parse( json.stringify( this.languages ) );
return languagesClone.sort( ( a, b ) => {
if ( a.name < b.name ) { return -1; }
if ( a.name > b.name ) { return 1; }
return 0;
} )
}
}
要进行深拷贝,我们可以简单地使用 JSON.parse(JSON.stringify())
来操作我们的数据。
JSON.stringify
将数据转换为字符串,从而消除了数据中的引用。JSON.parse
将字符串转换回对象数组。
在 Vue.js 中,修改存储中的响应式属性的副作用可能会隐藏起来,导致难以追踪和调试的意外错误。