or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

computed.mdeffect-scopes.mdeffects.mdindex.mdreactive-objects.mdrefs.mdutilities.mdwatchers.md

computed.mddocs/

0

# Computed Values

1

2

Computed values are cached derived values that automatically update when their dependencies change. They provide an efficient way to create values based on reactive state while avoiding unnecessary computations.

3

4

## Capabilities

5

6

### computed()

7

8

Creates a computed ref that automatically tracks its dependencies and caches the result. The computed value only re-evaluates when its dependencies change.

9

10

```typescript { .api }

11

/**

12

* Creates a readonly computed ref from a getter function

13

* @param getter - Function that computes the value

14

* @param debugOptions - Optional debug configuration

15

* @returns A readonly computed ref

16

*/

17

function computed<T>(

18

getter: ComputedGetter<T>,

19

debugOptions?: DebuggerOptions

20

): ComputedRef<T>;

21

22

/**

23

* Creates a writable computed ref with getter and setter

24

* @param options - Object with get and set functions

25

* @param debugOptions - Optional debug configuration

26

* @returns A writable computed ref

27

*/

28

function computed<T, S = T>(

29

options: WritableComputedOptions<T, S>,

30

debugOptions?: DebuggerOptions

31

): WritableComputedRef<T, S>;

32

33

type ComputedGetter<T> = (oldValue?: T) => T;

34

type ComputedSetter<T> = (newValue: T) => void;

35

```

36

37

**Usage Examples:**

38

39

```typescript

40

import { ref, computed, effect } from "@vue/reactivity";

41

42

// Basic computed value

43

const count = ref(1);

44

const doubleCount = computed(() => count.value * 2);

45

46

console.log(doubleCount.value); // 2

47

count.value = 5;

48

console.log(doubleCount.value); // 10

49

50

// Computed with multiple dependencies

51

const firstName = ref("John");

52

const lastName = ref("Doe");

53

const fullName = computed(() => `${firstName.value} ${lastName.value}`);

54

55

console.log(fullName.value); // "John Doe"

56

firstName.value = "Jane";

57

console.log(fullName.value); // "Jane Doe"

58

59

// Complex computed logic

60

const todos = ref([

61

{ id: 1, text: "Learn Vue", completed: false },

62

{ id: 2, text: "Build app", completed: true }

63

]);

64

65

const completedTodos = computed(() =>

66

todos.value.filter(todo => todo.completed)

67

);

68

69

const incompleteTodos = computed(() =>

70

todos.value.filter(todo => !todo.completed)

71

);

72

73

const todoStats = computed(() => ({

74

total: todos.value.length,

75

completed: completedTodos.value.length,

76

remaining: incompleteTodos.value.length,

77

progress: completedTodos.value.length / todos.value.length

78

}));

79

80

console.log(todoStats.value.progress); // 0.5

81

```

82

83

### Writable Computed

84

85

Create computed values that can be both read from and written to by providing getter and setter functions.

86

87

```typescript { .api }

88

interface WritableComputedOptions<T, S = T> {

89

get: ComputedGetter<T>;

90

set: ComputedSetter<S>;

91

}

92

93

interface WritableComputedRef<T, S = T> extends ComputedRef<T> {

94

set value(value: S);

95

}

96

```

97

98

**Usage Examples:**

99

100

```typescript

101

import { ref, computed } from "@vue/reactivity";

102

103

const firstName = ref("John");

104

const lastName = ref("Doe");

105

106

// Writable computed that combines and splits full name

107

const fullName = computed({

108

get: () => `${firstName.value} ${lastName.value}`,

109

set: (value: string) => {

110

const [first, last] = value.split(" ");

111

firstName.value = first || "";

112

lastName.value = last || "";

113

}

114

});

115

116

console.log(fullName.value); // "John Doe"

117

118

// Writing to computed updates the source refs

119

fullName.value = "Jane Smith";

120

console.log(firstName.value); // "Jane"

121

console.log(lastName.value); // "Smith"

122

123

// Writable computed for form data

124

const user = ref({

125

firstName: "Alice",

126

lastName: "Johnson",

127

email: "alice@example.com"

128

});

129

130

const displayName = computed({

131

get: () => `${user.value.firstName} ${user.value.lastName}`,

132

set: (name: string) => {

133

const [first, ...rest] = name.split(" ");

134

user.value.firstName = first || "";

135

user.value.lastName = rest.join(" ") || "";

136

}

137

});

138

139

displayName.value = "Bob Wilson"; // Updates user.firstName and user.lastName

140

```

141

142

### Computed with Side Effects

143

144

While computed values should generally be pure, you can use them with careful side effects for logging or debugging.

145

146

```typescript

147

import { ref, computed } from "@vue/reactivity";

148

149

const count = ref(0);

150

151

// Computed with debug logging

152

const expensiveComputation = computed(() => {

153

console.log("Computing expensive value..."); // Side effect for debugging

154

155

// Simulate expensive computation

156

let result = 0;

157

for (let i = 0; i < count.value * 1000; i++) {

158

result += Math.random();

159

}

160

161

return result;

162

});

163

164

// The computation only runs when count changes

165

console.log(expensiveComputation.value); // Runs computation

166

console.log(expensiveComputation.value); // Returns cached value

167

count.value = 2; // Invalidates cache

168

console.log(expensiveComputation.value); // Runs computation again

169

```

170

171

### Computed Error Handling

172

173

Handle errors in computed values gracefully:

174

175

```typescript

176

import { ref, computed } from "@vue/reactivity";

177

178

const jsonString = ref('{"name": "Alice"}');

179

180

const parsedData = computed(() => {

181

try {

182

return JSON.parse(jsonString.value);

183

} catch (error) {

184

console.warn("Invalid JSON:", error);

185

return null;

186

}

187

});

188

189

console.log(parsedData.value); // { name: "Alice" }

190

191

jsonString.value = "invalid json";

192

console.log(parsedData.value); // null (with warning)

193

```

194

195

### Debugging Computed Values

196

197

Use debug options to track computed value behavior:

198

199

```typescript

200

import { ref, computed } from "@vue/reactivity";

201

202

const count = ref(0);

203

204

const doubleCount = computed(

205

() => count.value * 2,

206

{

207

onTrack: (event) => {

208

console.log("Computed tracked:", event);

209

},

210

onTrigger: (event) => {

211

console.log("Computed triggered:", event);

212

}

213

}

214

);

215

216

// Access to track dependencies

217

console.log(doubleCount.value); // Logs tracking

218

219

// Change to trigger re-computation

220

count.value = 5; // Logs trigger

221

console.log(doubleCount.value); // New computed value

222

```

223

224

## Types

225

226

```typescript { .api }

227

// Core computed interfaces

228

interface ComputedRef<T = any> extends BaseComputedRef<T> {

229

readonly value: T;

230

}

231

232

interface WritableComputedRef<T, S = T> extends BaseComputedRef<T, S> {

233

set value(value: S);

234

[WritableComputedRefSymbol]: true;

235

}

236

237

interface BaseComputedRef<T, S = T> extends Ref<T, S> {

238

[ComputedRefSymbol]: true;

239

effect: ReactiveEffect<T>;

240

}

241

242

// Function types

243

type ComputedGetter<T> = (oldValue?: T) => T;

244

type ComputedSetter<T> = (newValue: T) => void;

245

246

// Options interface

247

interface WritableComputedOptions<T, S = T> {

248

get: ComputedGetter<T>;

249

set: ComputedSetter<S>;

250

}

251

252

// Debug options

253

interface DebuggerOptions {

254

onTrack?: (event: DebuggerEvent) => void;

255

onTrigger?: (event: DebuggerEvent) => void;

256

}

257

258

interface DebuggerEvent {

259

effect: ReactiveEffect;

260

target: object;

261

type: TrackOpTypes | TriggerOpTypes;

262

key: any;

263

newValue?: any;

264

oldValue?: any;

265

oldTarget?: Map<any, any> | Set<any>;

266

}

267

268

// Internal implementation (exported but marked as internal)

269

class ComputedRefImpl<T, S = T> implements WritableComputedRef<T, S> {

270

constructor(

271

getter: ComputedGetter<T>,

272

setter?: ComputedSetter<S>,

273

isReadonly?: boolean,

274

debugOptions?: DebuggerOptions

275

);

276

277

get value(): T;

278

set value(newValue: S);

279

280

readonly effect: ReactiveEffect<T>;

281

readonly [ComputedRefSymbol]: true;

282

readonly [RefSymbol]: true;

283

}

284

```

285

286

## Performance Considerations

287

288

### Lazy Evaluation

289

290

Computed values are lazy - they only compute when accessed:

291

292

```typescript

293

import { ref, computed } from "@vue/reactivity";

294

295

const count = ref(0);

296

297

// This computed is created but not evaluated yet

298

const expensiveValue = computed(() => {

299

console.log("Computing..."); // Won't run until accessed

300

return count.value * 1000;

301

});

302

303

// Now the computation runs

304

console.log(expensiveValue.value); // Logs "Computing..." then 0

305

```

306

307

### Caching Behavior

308

309

Computed values cache their results and only re-compute when dependencies change:

310

311

```typescript

312

import { ref, computed } from "@vue/reactivity";

313

314

const count = ref(1);

315

let computationCount = 0;

316

317

const doubled = computed(() => {

318

computationCount++;

319

console.log(`Computation #${computationCount}`);

320

return count.value * 2;

321

});

322

323

// First access computes the value

324

console.log(doubled.value); // Logs "Computation #1", returns 2

325

326

// Second access returns cached value

327

console.log(doubled.value); // Returns 2 (no computation log)

328

329

// Changing dependency invalidates cache

330

count.value = 3;

331

332

// Next access re-computes

333

console.log(doubled.value); // Logs "Computation #2", returns 6

334

```

335

336

### Avoiding Unnecessary Computations

337

338

Structure computed values to minimize unnecessary work:

339

340

```typescript

341

import { ref, computed } from "@vue/reactivity";

342

343

const users = ref([

344

{ id: 1, name: "Alice", active: true },

345

{ id: 2, name: "Bob", active: false },

346

{ id: 3, name: "Charlie", active: true }

347

]);

348

349

// Good: Computed values that can be independently cached

350

const activeUsers = computed(() =>

351

users.value.filter(user => user.active)

352

);

353

354

const userCount = computed(() => users.value.length);

355

const activeUserCount = computed(() => activeUsers.value.length);

356

357

// Less optimal: Single computed doing multiple calculations

358

const userStats = computed(() => ({

359

total: users.value.length,

360

active: users.value.filter(user => user.active).length, // Filters twice

361

inactive: users.value.filter(user => !user.active).length

362

}));

363

364

// Better: Use other computed values

365

const optimizedUserStats = computed(() => ({

366

total: userCount.value,

367

active: activeUserCount.value,

368

inactive: userCount.value - activeUserCount.value

369

}));

370

```