0%


Developing frontend applications without frameworks means building web projects without using any frontend frameworks such as Vue or React, and instead relying directly on JavaScript, HTML, and CSS. This approach allows developers to gain a deeper understanding of the underlying principles and mechanisms of frontend development, while avoiding over-reliance on frameworks.

For static projects or low-complexity applications with limited functionality, developing without a framework is often more than sufficient. Code written with frameworks must be compiled before it can be rendered in the browser, which is why tools like Webpack exist. Frameworks such as Vue were created to solve specific pain points—like two-way data binding and automatic reactive updates—but they also require developers to follow framework-specific syntax and conventions. As frameworks evolve, developers must continuously learn and adapt to new versions.

Frameworks are undoubtedly beneficial for development efficiency, but for simple requirements, avoiding a framework can actually be the wiser choice. Whether or not to use a framework should always be decided based on the actual project requirements.

When developing multiple similar websites, unbundled code can be uploaded directly to the server and modified in place. In contrast, when using a framework, only the compiled output is deployed, and any changes require local modification and rebuilding, which can be more time-consuming. After becoming proficient in pure JavaScript development, developers can even go on to build their own tools or frameworks.

How to Properly Insert <script> Code in Vue (Including Ad Code Examples)

In real-world development, we often need to integrate third-party services such as advertising platforms, analytics tools, or customer support widgets. These services usually provide a block of code that includes <script> tags.

However, if you try to place <script> tags directly inside a Vue template (<template>), they will not work—and may even cause errors.

This article explains why <script> tags cannot be directly used inside Vue templates, and how to correctly inject scripts dynamically, with a ready-to-use example (using ad code as a case study).


1. Why You Can’t Write <script> Directly in a Vue Template

Vue Single File Components (SFC) have a clear structure:

  • <template>: markup / structure
  • <script>: logic
  • <style>: styles

Placing <script> tags inside <template> causes several problems:

  1. Vue treats <script> tags as plain text or strips them out
  2. It breaks Vue’s compilation process and may throw errors
  3. Third-party scripts (ads, analytics, trackers) will not execute at all

Therefore, to make external scripts work properly, they must be dynamically injected using DOM APIs during the component lifecycle.


2. Incorrect Example: Pasting Ad Code Directly (This Will NOT Work)

Suppose an ad network provides the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
<script type="text/javascript">
atOptions = {
'key' : '1523b7ddb936e560aadef64a851d500c',
'format' : 'iframe',
'height' : 250,
'width' : 300,
'params' : {}
};
</script>
<script
type="text/javascript"
src="//www.highperformanceformat.com/1523b7ddb936e560aadef64a851d500c/invoke.js">
</script>

If you paste this code directly into a Vue <template>, the advertisement will never render.


3. The Correct Approach: Dynamically Inject Scripts in Vue

The core idea is simple:

  1. Place a container element in the template (e.g. <div id="de_v"></div>)
  2. Use the onMounted (or mounted) lifecycle hook
  3. Create <script> elements via JavaScript
  4. Append them to the DOM

4. Complete Example (Vue 3 + <script setup>)

Assume the ad should be rendered inside a container with the ID "de_v".

Template Section

1
2
3
<template>
<div id="de_v"></div>
</template>

Script Section

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<script setup>
import { onMounted } from "vue";

onMounted(() => {
const targetDiv = document.getElementById("de_v");
if (!targetDiv) return;

// Create the first script (ad configuration)
const script1 = document.createElement("script");
script1.type = "text/javascript";
script1.innerHTML = `
atOptions = {
'key' : '1523b7ddb936e560aadef64a851d500c',
'format' : 'iframe',
'height' : 250,
'width' : 300,
'params' : {}
};
`;

// Create the second script (external ad script)
const script2 = document.createElement("script");
script2.type = "text/javascript";
script2.src =
"//www.highperformanceformat.com/1523b7ddb936e560aadef64a851d500c/invoke.js";

// Append scripts to the container
targetDiv.appendChild(script1);
targetDiv.appendChild(script2);
});
</script>

5. Common Issues and Notes

1. Will the script be injected multiple times?

If the component is mounted and unmounted repeatedly, the scripts may be injected multiple times.

Solutions:

  • Check whether the script already exists before inserting
  • Or remove the scripts in onUnmounted

2. How to Handle Multiple Ad Slots?

Use different container IDs for each ad slot, for example:

1
2
<div id="ad1"></div>
<div id="ad2"></div>

Then inject scripts into the corresponding container.


3. What About SSR Frameworks (Nuxt)?

On the server side, document is not available.

You must run the script injection code on the client side only:

1
onMounted(() => { ... })

Otherwise, you will encounter:

1
document is not defined

6. Summary

  • You cannot directly place <script> tags inside Vue templates
  • The correct solution is to dynamically create and inject scripts during the component lifecycle
  • Using appendChild() ensures third-party scripts execute properly
  • This approach works for ads, analytics, tracking pixels, and external SDKs

By following the method described above, third-party scripts will run correctly in Vue-based applications.

Saving CSV Data from a Third-Party API into a Database

When data retrieved from a third-party API is returned in CSV format, a common and reliable approach is:

  1. First, save the CSV file to a local path
  2. Then use fs.createReadStream to read the file
  3. Finally, parse the CSV content and store it in a local database

Below is an example implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
const processConversations = async (dateObj) => {
const username = 'your_account_name';
const password = 'password';

const year = dateObj.format('YYYY');
const month = dateObj.format('MM');
const day = dateObj.format('DD');
const hour = dateObj.format('HH');
const dateStr = `${year}-${month}-${day}-${hour}`;
const fileName = `partial_path`;
const url = `https://api/${fileName}`;
const outputPath = `/tmp/conversations`;
const tableName = 'test';

try {
// Download CSV
const response = await axios.get(url, {
responseType: 'stream',
headers: {
Authorization: `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`
}
});

const writer = fs.createWriteStream(outputPath);
response.data.pipe(writer);
await new Promise((resolve, reject) => {
writer.on('finish', resolve);
writer.on('error', reject);
});

console.log('CSV file downloaded successfully:', fileName);

const rows = [];

await new Promise((resolve, reject) => {
fs.createReadStream(outputPath)
.pipe(csv())
.on('data', (row) => {
rows.push([
row['DATE_TIME']
? dayjs(row['DATE_TIME']).format('YYYY-MM-DD HH:mm:ss')
: null,
row['CLID'] || null,
row['CLID_SOURCE'] || null
]);
})
.on('end', resolve)
.on('error', reject);
});

if (rows.length === 0) {
console.warn(`No data found in CSV: ${fileName}`);
return;
}

const insertSQL = `
INSERT INTO \`${tableName}\` (
date_time, clid, clid_source
) VALUES ?
`;

await query(insertSQL, [rows]);
console.log(`Successfully inserted ${rows.length} records: ${fileName}`);
} catch (error) {
console.error(`Error (${fileName}):`, error.message);
}
};

The Role of Indexes

When you want to quickly find the data you need, you can use indexes.
With indexes, the database can directly locate the required data during retrieval, saving time—much like the table of contents at the front of a book, which helps you quickly find what you want to read.

If an index contains multiple columns, it follows the leftmost prefix rule, meaning it is used from left to right, starting with the leftmost column.

Types of Indexes

1. Unique Index

A unique index can be added to certain columns (such as an ID number) to ensure that no two records have the same value. By using the UNIQUE keyword, we can create a unique index.

1
ADD UNIQUE INDEX uni_sf (sf);

You can also add a unique constraint to a column without explicitly creating a unique index. In this case, the column is still required to have unique values.

1
ADD CONSTRAINT uni_name UNIQUE (name);

2. Primary Key Index

Once a primary key is created, an index is automatically created for it. This is why searching data by the primary key is faster.

A table can have only one primary key, but a primary key can consist of multiple columns, which is known as a composite (or joint) primary key.

1
ALTER TABLE table_name ADD PRIMARY KEY (column);

3. Normal (Non-unique) Index

Any column can have an index created on it. It is recommended to add indexes only to columns that are frequently queried. Avoid adding indexes to every column, as this can negatively impact performance.

1
ALTER TABLE table_name ADD INDEX index_name (column);


For testing and high UI requirements (for example, replacing the thumb with a custom image), the traditional @react-native-community/slider can no longer meet the needs. So I switched to a custom approach. After searching for a long time, I finally found a great library:
https://github.com/miblanchard/react-native-slider

Component Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
import React, { PureComponent } from 'react';
import {
Animated,
Easing,
I18nManager,
Image,
ImageSourcePropType,
LayoutChangeEvent,
PanResponder,
PanResponderInstance,
View,
ViewStyle,
} from 'react-native';
// styles
import { defaultStyles as styles } from './styles';
import type { Dimensions, SliderProps, SliderState } from './types';

type RectReturn = {
containsPoint: (nativeX: number, nativeY: number) => boolean;
height: number;
trackDistanceToPoint: (nativeX: number) => number;
width: number;
x: number;
y: number;
};

const Rect = ({
height,
width,
x,
y,
}: {
height: number;
width: number;
x: number;
y: number;
}) => ({
containsPoint: (nativeX: number, nativeY: number) =>
nativeX >= x &&
nativeY >= y &&
nativeX <= x + width &&
nativeY <= y + height,
height,
trackDistanceToPoint: (nativeX: number) => {
if (nativeX < x) {
return x - nativeX;
}

if (nativeX > x + width) {
return nativeX - (x + width);
}

return 0;
},
width,
x,
y,
});

const DEFAULT_ANIMATION_CONFIGS = {
spring: {
friction: 7,
tension: 100,
},
timing: {
duration: 150,
easing: Easing.inOut(Easing.ease),
delay: 0,
},
};

const normalizeValue = (
props: SliderProps,
value?: number | Array<number>,
): Array<number> => {
if (!value || (Array.isArray(value) && value.length === 0)) {
return [0];
}

const { maximumValue, minimumValue } = props;

const getBetweenValue = (inputValue: number) =>
Math.max(Math.min(inputValue, maximumValue), minimumValue);

if (!Array.isArray(value)) {
return [getBetweenValue(value)];
}

return value.map(getBetweenValue).sort((a, b) => a - b);
};

const updateValues = ({
values,
newValues = values,
}: {
values: number | Array<number> | Animated.Value | Array<Animated.Value>;
newValues?: number | Array<number> | Animated.Value | Array<Animated.Value>;
}): Animated.Value[] => {
if (
Array.isArray(newValues) &&
Array.isArray(values) &&
newValues.length !== values.length
) {
return updateValues({ values: newValues });
}

if (Array.isArray(values) && Array.isArray(newValues)) {
return values?.map((value: number | Animated.Value, index: number) => {
let valueToSet = newValues[index];
if (value instanceof Animated.Value) {
if (valueToSet instanceof Animated.Value) {
valueToSet = valueToSet.__getValue();
}
value.setValue(valueToSet);
return value;
}

if (valueToSet instanceof Animated.Value) {
return valueToSet;
}

return new Animated.Value(valueToSet);
});
}

return [new Animated.Value(0)];
};

const indexOfLowest = (values: Array<number>): number => {
let lowestIndex = 0;
values.forEach((value, index, array) => {
if (value < array[lowestIndex]) {
lowestIndex = index;
}
});
return lowestIndex;
};

export class Slider extends PureComponent<SliderProps, SliderState> {
constructor(props: SliderProps) {
super(props);
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder:
this._handleStartShouldSetPanResponder,
onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder,
onPanResponderGrant: this._handlePanResponderGrant,
onPanResponderMove: this._handlePanResponderMove,
onPanResponderRelease: this._handlePanResponderEnd,
onPanResponderTerminationRequest:
this._handlePanResponderRequestEnd,
onPanResponderTerminate: this._handlePanResponderEnd,
});
this.state = {
allMeasured: false,
containerSize: {
width: 0,
height: 0,
},
thumbSize: {
width: 0,
height: 0,
},
trackMarksValues: updateValues({
values: normalizeValue(this.props, this.props.trackMarks),
}),
values: updateValues({
values: normalizeValue(
this.props,
this.props.value instanceof Animated.Value
? this.props.value.__getValue()
: this.props.value,
),
}),
};
}

static defaultProps = {
animationType: 'timing',
debugTouchArea: false,
trackMarks: [],
maximumTrackTintColor: '#b3b3b3',
maximumValue: 1,
minimumTrackTintColor: '#3f3f3f',
minimumValue: 0,
step: 0,
thumbTintColor: '#343434',
trackClickable: true,
value: 0,
vertical: false,
};

static getDerivedStateFromProps(props: SliderProps, state: SliderState) {
if (
props.trackMarks &&
!!state.trackMarksValues &&
state.trackMarksValues.length > 0
) {
const newTrackMarkValues = normalizeValue(props, props.trackMarks);
const statePatch = {} as SliderState;

if (state.trackMarksValues) {
statePatch.trackMarksValues = updateValues({
values: state.trackMarksValues,
newValues: newTrackMarkValues,
});
}

return statePatch;
}
}

componentDidUpdate() {
const newValues = normalizeValue(
this.props,
this.props.value instanceof Animated.Value
? this.props.value.__getValue()
: this.props.value,
);
newValues.forEach((value, i) => {
if (!this.state.values[i]) {
this._setCurrentValue(value, i);
} else if (value !== this.state.values[i].__getValue()) {
if (this.props.animateTransitions) {
this._setCurrentValueAnimated(value, i);
} else {
this._setCurrentValue(value, i);
}
}
});
}

_getRawValues(
values: Array<Animated.Value> | Array<Animated.AnimatedInterpolation>,
) {
return values.map((value) => value.__getValue());
}

_handleStartShouldSetPanResponder = (e: any): boolean =>
this._thumbHitTest(e);

_handleMoveShouldSetPanResponder(): boolean {
return false;
}

_handlePanResponderGrant = (e: { nativeEvent: any }) => {
const { thumbSize } = this.state;
const { nativeEvent } = e;
this._previousLeft = this.props.trackClickable
? nativeEvent.locationX - thumbSize.width
: this._getThumbLeft(this._getCurrentValue(this._activeThumbIndex));

this.props?.onSlidingStart?.(this._getRawValues(this.state.values));
};

_handlePanResponderMove = (_e: any, gestureState: any) => {
if (this.props.disabled) {
return;
}

this._setCurrentValue(
this._getValue(gestureState),
this._activeThumbIndex,
() => {
this.props?.onValueChange?.(
this._getRawValues(this.state.values),
);
},
);
};

_handlePanResponderRequestEnd = () => {
return false;
};

_handlePanResponderEnd = (_e: any, gestureState: any) => {
if (this.props.disabled) {
return;
}

this._setCurrentValue(
this._getValue(gestureState),
this._activeThumbIndex,
() => {
if (this.props.trackClickable) {
this.props?.onValueChange?.(
this._getRawValues(this.state.values),
);
}

this.props?.onSlidingComplete?.(
this._getRawValues(this.state.values),
);
},
);

this._activeThumbIndex = 0;
};

_measureContainer = (e: LayoutChangeEvent) => {
this._handleMeasure('_containerSize', e);
};

_measureTrack = (e: LayoutChangeEvent) => {
this._handleMeasure('_trackSize', e);
};

_measureThumb = (e: LayoutChangeEvent) => {
this._handleMeasure('_thumbSize', e);
};

_handleMeasure = (
name: '_containerSize' | '_trackSize' | '_thumbSize',
e: LayoutChangeEvent,
) => {
const { width, height } = e.nativeEvent.layout;
const size = { width, height };

const currentSize = this[name];

if (
currentSize &&
width === currentSize.width &&
height === currentSize.height
) {
return;
}

this[name] = size;

if (this._containerSize && this._thumbSize) {
this.setState({
containerSize: this._containerSize,
thumbSize: this._thumbSize,
allMeasured: true,
});
}
};

_getRatio = (value: number) => {
const { maximumValue, minimumValue } = this.props;
return (value - minimumValue) / (maximumValue - minimumValue);
};

_getThumbLeft = (value: number) => {
const { containerSize, thumbSize } = this.state;
const { vertical } = this.props;

const standardRatio = this._getRatio(value);
const ratio = I18nManager.isRTL ? 1 - standardRatio : standardRatio;

return (
ratio *
((vertical ? containerSize.height : containerSize.width) -
thumbSize.width)
);
};

_getValue = (gestureState: { dx: number; dy: number }) => {
const { containerSize, thumbSize, values } = this.state;
const { maximumValue, minimumValue, step, vertical } = this.props;

const length = containerSize.width - thumbSize.width;
const thumbLeft = vertical
? this._previousLeft + gestureState.dy * -1
: this._previousLeft + gestureState.dx;

const nonRtlRatio = thumbLeft / length;
const ratio = I18nManager.isRTL ? 1 - nonRtlRatio : nonRtlRatio;

let minValue = minimumValue;
let maxValue = maximumValue;

const rawValues = this._getRawValues(values);
const buffer = step ? step : 0.1;

if (values.length === 2) {
if (this._activeThumbIndex === 1) {
minValue = rawValues[0] + buffer;
} else {
maxValue = rawValues[1] - buffer;
}
}

if (step) {
return Math.max(
minValue,
Math.min(
maxValue,
minimumValue +
Math.round(
(ratio * (maximumValue - minimumValue)) / step,
) *
step,
),
);
}

return Math.max(
minValue,
Math.min(
maxValue,
ratio * (maximumValue - minimumValue) + minimumValue,
),
);
};

_getCurrentValue = (thumbIndex: number = 0) =>
this.state.values[thumbIndex].__getValue();

_setCurrentValue = (
value: number,
thumbIndex: number | null | undefined,
callback?: () => void,
) => {
const safeIndex = thumbIndex ?? 0;
const animatedValue = this.state.values[safeIndex];

if (animatedValue) {
animatedValue.setValue(value);
callback?.();
} else {
this.setState((prevState: SliderState) => {
const newValues = [...prevState.values];
newValues[safeIndex] = new Animated.Value(value);
return { values: newValues };
}, callback);
}
};

_setCurrentValueAnimated = (value: number, thumbIndex: number = 0) => {
const { animationType } = this.props;
const animationConfig = {
...DEFAULT_ANIMATION_CONFIGS[animationType],
...this.props.animationConfig,
toValue: value,
useNativeDriver: false,
};
Animated[animationType](this.state.values[thumbIndex], animationConfig).start();
};

_getTouchOverflowSize = (): { width: number; height: number } => {
const { allMeasured, containerSize, thumbSize } = this.state;
const { thumbTouchSize } = this.props;

const size = { width: 40, height: 40 };

if (allMeasured) {
size.width = Math.max(0, (thumbTouchSize?.width || 0) - thumbSize.width);
size.height = Math.max(0, (thumbTouchSize?.height || 0) - containerSize.height);
}

return size;
};

_getTouchOverflowStyle = () => {
const { width, height } = this._getTouchOverflowSize();
const touchOverflowStyle = {} as ViewStyle;

if (width !== undefined && height !== undefined) {
const verticalMargin = -height / 2;
touchOverflowStyle.marginTop = verticalMargin;
touchOverflowStyle.marginBottom = verticalMargin;

const horizontalMargin = -width / 2;
touchOverflowStyle.marginLeft = horizontalMargin;
touchOverflowStyle.marginRight = horizontalMargin;
}

if (this.props.debugTouchArea === true) {
touchOverflowStyle.backgroundColor = 'orange';
touchOverflowStyle.opacity = 0.5;
}

return touchOverflowStyle;
};

_thumbHitTest = (e: { nativeEvent: any }) => {
const { nativeEvent } = e;
const { trackClickable } = this.props;
const { values } = this.state;

const hitThumb = values.find((_, i) => {
const thumbTouchRect = this._getThumbTouchRect(i);
const containsPoint = thumbTouchRect.containsPoint(
nativeEvent.locationX,
nativeEvent.locationY,
);

if (containsPoint) {
this._activeThumbIndex = i;
}

return containsPoint;
});

if (hitThumb) {
return true;
}

if (trackClickable) {
if (values.length === 1) {
this._activeThumbIndex = 0;
} else {
const thumbDistances = values.map((_value, index) => {
const thumbTouchRect = this._getThumbTouchRect(index);
return thumbTouchRect.trackDistanceToPoint(nativeEvent.locationX);
});
this._activeThumbIndex = indexOfLowest(thumbDistances);
}

return true;
}

return false;
};

_getThumbTouchRect = (thumbIndex: number = 0): RectReturn => {
const { containerSize, thumbSize } = this.state;
const { thumbTouchSize } = this.props;
const { height, width } = thumbTouchSize || { height: 40, width: 40 };

const touchOverflowSize = this._getTouchOverflowSize();

return Rect({
height,
width,
x:
touchOverflowSize.width / 2 +
this._getThumbLeft(this._getCurrentValue(thumbIndex)) +
(thumbSize.width - width) / 2,
y:
touchOverflowSize.height / 2 +
(containerSize.height - height) / 2,
});
};

_activeThumbIndex: number = 0;
_containerSize: Dimensions | null | undefined;
_panResponder: PanResponderInstance;
_previousLeft: number = 0;
_thumbSize: Dimensions | null | undefined;
_trackSize: Dimensions | null | undefined;

_renderDebugThumbTouchRect = (
thumbLeft: Animated.AnimatedInterpolation,
index: number,
) => {
const { height, y, width } = this._getThumbTouchRect() || {};
const positionStyle = { height, left: thumbLeft, top: y, width };
return (
<Animated.View
key={`debug-thumb-${index}`}
pointerEvents="none"
style={[styles.debugThumbTouchArea, positionStyle]}
/>
);
};

_renderThumbImage = (thumbIndex: number = 0) => {
const { thumbImage } = this.props;
if (!thumbImage) return null;

return (
<Image
source={
(Array.isArray(thumbImage)
? thumbImage[thumbIndex]
: thumbImage) as ImageSourcePropType
}
/>
);
};

render() {
const {
containerStyle,
debugTouchArea,
maximumTrackTintColor,
maximumValue,
minimumTrackTintColor,
minimumValue,
renderAboveThumbComponent,
renderTrackMarkComponent,
renderThumbComponent,
thumbStyle,
thumbTintColor,
trackStyle,
vertical,
...other
} = this.props;

const {
allMeasured,
containerSize,
thumbSize,
trackMarksValues,
values,
} = this.state;

const interpolatedThumbValues = values.map((value) =>
value.interpolate({
inputRange: [minimumValue, maximumValue],
outputRange: I18nManager.isRTL
? [0, -(containerSize.width - thumbSize.width)]
: [0, containerSize.width - thumbSize.width],
}),
);

const interpolatedTrackValues = values.map((value) =>
value.interpolate({
inputRange: [minimumValue, maximumValue],
outputRange: [0, containerSize.width - thumbSize.width],
}),
);

const interpolatedTrackMarksValues =
trackMarksValues &&
trackMarksValues.map((v) =>
v.interpolate({
inputRange: [minimumValue, maximumValue],
outputRange: I18nManager.isRTL
? [0, -(containerSize.width - thumbSize.width)]
: [0, containerSize.width - thumbSize.width],
}),
);

const valueVisibleStyle = {} as ViewStyle;
if (!allMeasured) valueVisibleStyle.opacity = 0;

const interpolatedRawValues = this._getRawValues(interpolatedTrackValues);
const minThumbValue = new Animated.Value(Math.min(...interpolatedRawValues));
const maxThumbValue = new Animated.Value(Math.max(...interpolatedRawValues));

const minimumTrackStyle = {
position: 'absolute',
left:
interpolatedTrackValues.length === 1
? new Animated.Value(0)
: Animated.add(minThumbValue, thumbSize.width / 2),
width:
interpolatedTrackValues.length === 1
? Animated.add(interpolatedTrackValues[0], thumbSize.width / 2)
: Animated.add(Animated.multiply(minThumbValue, -1), maxThumbValue),
backgroundColor: minimumTrackTintColor,
...valueVisibleStyle,
} as ViewStyle;

const touchOverflowStyle = this._getTouchOverflowStyle();

return (
<>
{renderAboveThumbComponent && (
<View style={styles.aboveThumbComponentsContainer}>
{interpolatedThumbValues.map((value, i) => (
<Animated.View
key={`slider-above-thumb-${i}`}
style={[
styles.renderThumbComponent,
{
bottom: 0,
transform: [
{ translateX: value },
{ translateY: 0 },
],
...valueVisibleStyle,
},
]}>
{renderAboveThumbComponent(i)}
</Animated.View>
))}
</View>
)}

<View
{...other}
style={[
styles.container,
vertical ? { transform: [{ rotate: '-90deg' }] } : {},
containerStyle,
]}
onLayout={this._measureContainer}>

<View
renderToHardwareTextureAndroid
style={[
styles.track,
{ backgroundColor: maximumTrackTintColor },
trackStyle,
]}
onLayout={this._measureTrack}
/>

<Animated.View
renderToHardwareTextureAndroid
style={[styles.track, trackStyle, minimumTrackStyle]}
/>

{renderTrackMarkComponent &&
interpolatedTrackMarksValues &&
interpolatedTrackMarksValues.map((value, i) => (
<Animated.View
key={`track-mark-${i}`}
style={[
styles.renderThumbComponent,
{
transform: [
{ translateX: value },
{ translateY: 0 },
],
...valueVisibleStyle,
},
]}>
{renderTrackMarkComponent(i)}
</Animated.View>
))}

{interpolatedThumbValues.map((value, i) => (
<Animated.View
key={`slider-thumb-${i}`}
style={[
renderThumbComponent
? styles.renderThumbComponent
: styles.thumb,
renderThumbComponent
? {}
: { backgroundColor: thumbTintColor, ...thumbStyle },
{
transform: [
{ translateX: value },
{ translateY: 0 },
],
...valueVisibleStyle,
},
]}
onLayout={this._measureThumb}>
{renderThumbComponent ? renderThumbComponent() : this._renderThumbImage(i)}
</Animated.View>
))}

<View
style={[styles.touchArea, touchOverflowStyle]}
{...this._panResponder.panHandlers}>
{!!debugTouchArea &&
interpolatedThumbValues.map((value, i) =>
this._renderDebugThumbTouchRect(value, i),
)}
</View>
</View>
</>
);
}
}

How to Use (Import)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { Slider } from 'path-to-the-component-above';

<Slider
minimumValue={1}
maximumValue={7} // sliding range: 1 to 7
minimumTrackTintColor="#0A79C3"
maximumTrackTintColor="#F3F3F3"
step={1} // step size
value={targetDay} // default value, e.g. 4
onValueChange={targetDays =>
dispatch({
type: 'SetupWeekTarget/updateState',
payload: { targetDay: targetDays }
})
} // triggered when sliding
trackStyle={{ height: 10, borderRadius: 12 }}
containerStyle={{ width: SCREEN_WIDTH - 90 }}
thumbTintColor="transparent"
thumbImage={require('path-to-your-image')}
/>

More Parameters

For the full list of available props and detailed documentation, please refer to:
https://github.com/miblanchard/react-native-slider


What Is Serialization

Data transmitted over a TCP channel can only be in binary form. So converting a data structure or an object into binary form is called serialization. The reverse process is called deserialization. The rules that define how they convert back and forth are called a protocol.


What Is RPC

RPC stands for Remote Procedure Call. Most RPC systems are based on TCP. The general implementation flow is as follows:

  1. The frontend calls the corresponding interface through a Proxy. The Proxy converts information such as the RPC service name, method name, and parameters into an RPC Request object and passes it to the RPC framework.
  2. The RPC framework serializes the RPC Request object into binary form according to the RPC protocol.
  3. The RPC framework sends the binary data to the backend through a TCP channel.
  4. The backend deserializes the binary data back into an RPC Request object.
  5. The backend maps the deserialized data to the actual method it implements, passes in the parameters, obtains the result, and then packages it into an RPC Response object for the RPC framework.
  6. The RPC framework serializes the RPC Response object into binary form according to the RPC protocol and sends it back to the frontend through the TCP channel.
  7. After receiving the binary data, the frontend deserializes it into an RPC Response object, and the Proxy returns the result to the business code.

Notes on Encountering the Unknown Self

What is the “Unknown”?

The “self” refers to the “true self.” From the question “Who am I?” to the concept of “from existence to non-existence,” the entire book is structured around dialogues between Ruoling and a wise elder, revealing many profound life concepts.


Core Concepts

  • There is no one outside—only yourself.
    All external situations are actually projections of your inner world.

  • Golden Moments to Talk with the Subconscious
    The best times to imagine you’ve already achieved your goals are the moments between waking and sleeping—early morning just before fully waking up, or late at night before falling asleep. These times are when we are closest to our subconscious.

  • Key Practices: Awareness, Surrender, Overcoming the Peptide Self (Ego)

    • Awareness: Focus on your present self. Observe your emotions and reactions without judgment.
    • Surrender: Accept reality instead of resisting it.
    • Peptide Self (Ego): Represents inner voices of fear, comparison, and anxiety. Learn to recognize and transcend them.
  • Meditation, Gratitude, Giving

    • During meditation, visualize yourself already living your desired life.
    • Be grateful even before your goals are achieved.
    • Maintain a selfless, giving mindset.
  • Care Less About Others, Care More About Yourself

    • Bring your attention back to yourself and your inner world.
    • When emotions arise, be aware of which part of your body feels uncomfortable and understand its message.

Conclusion

This book is not only about discovering “who you are,” but also about reminding you to “become that true self.” Awakening inner strength requires continuous awareness, practice, and transcending the ego.

What I Talk About When I Talk About Running

It was through this book that I realized there could be another version of myself out there, doing something I love. Before reading this book, I had never read anything by Murakami. I knew he was popular and had many famous books, but I hadn’t really paid attention. But when it comes to running, I felt there could be a connection.

If I keep running, will I eventually reach the moon? Running isn’t about living longer, but about making life feel easier and lighter within the time we have.

I was surprised by Murakami’s imagination while running—so free and boundless. I really enjoy that. I’ve imagined many things too, like suddenly recalling a lyric while running—something like “speeding up to chase happiness,” blabla… Long-distance running requires endurance, but also imagination. Of course, the premise is loving to run. If someone doesn’t like running, no matter how much you say, it won’t matter. But if someone does love it, they’ll even want to meet another version of themselves during a break in the rain. Humans are inherently lonely, but the spirit can resonate with others.

I plan to keep running no matter how the world changes. Once, I walked 30 kilometers in one day. Despite the sunburn, I loved the spiritual fulfillment more. If you haven’t experienced it, you have no voice. Maybe I’m like Chen Kun—a pessimistic optimist. I live actively in this world, but I also see through its illusions. Neither joyful because of things, nor sorrowful because of myself.

My pulse has dropped to just over 50 beats per minute. Seems like I’m built for jogging. After all, it’s about interest. As long as my freedom isn’t restricted, anything is acceptable.

I hope next year I’ll join a marathon, with people cheering me on. That would be so happy.

“I’m not a person, but a pure machine. So I don’t need to feel anything, just keep running forward.”

What’s most similar is that we both love to run non-stop. Not the type who walks when tired or stops to enjoy the scenery. Maybe people have different views, but I think the true king is the one who can keep running without stopping.

When reaching the finish line, I’d feel so cool, a rush of pride, like winning the lottery or getting incredibly lucky. It’s a principle—and also my pride.

Oh, right—just like Brother Shang waiting at the finish line, and I’d smile proudly.

Aunt Tai

Aunt Tai is strong-willed and resilient. Her optimistic attitude is something worth learning from. The greatest thing my father ever did was marrying my mother—a truly great woman. A real woman should face everything with positivity and build the future she desires with her own hands.

Although she resorted to stealing, it was driven by hardship—survival and family. Setting aside pride to do things against her will is, in a way, a form of strength. It’s not about crying and making a scene, but staying proactive in adversity.

Thinking about it, the author’s achievements are inseparable from his great mother.

Ah, that lovely mother! To preserve her husband’s dignity and happiness, she insisted on completing the house. Isn’t that a form of strength too? To follow her heart courageously is truly admirable.

In fact, the person who understands my father the most is my mother. She knows his pride. Isn’t this what love is? You don’t need to say it—I understand. I understand you, and you understand me, and that’s why we’ve been together for so long.

Zhang Meili

Get up from where you fell. Zhang Meili seems like someone who pursued true love and worked hard for what she desired. Though she was eventually forced to liberate herself sexually, I still respect her for everything she once did.

Public opinion is powerful, but Zhang Meili’s inner strength is even more powerful.

The book does contain a lot of his own “biases,” but I find him authentic and humorous. His language is simple and understandable, and each subtitle essentially tells us what we should do.

Understanding Life

Life is never about what you want it to be. It’s good to discover beauty and to view things from others’ perspectives. At the same time, one should have self-awareness.

Understanding Love

Sheldon once said: “The pursuit of another human to spend life with is something I’ve never understood. Maybe I’m too interesting to need company. So I wish you the same joy with each other as I have with myself.”
One can never escape loneliness through another person; only through passion for life. Managing a relationship requires rationality. Freedom is inalienable. I once checked my ex’s phone—I realized it was wrong and won’t do it again.

Understanding Strategy

This section mainly introduces the strategies of courting and how to maintain a good relationship—true wisdom. Never sacrifice your career for family. Don’t live with in-laws (though I think it depends). Have your own hobbies.

Understanding Marriage

Endure and stay loyal. As for non-marriage beliefs, there’s nothing more to say.

Understanding Setbacks

Every breakup is just making way for true love. Don’t force it. I love you, so I wish you well, even if I’m not the one by your side.
Do what you want regardless of others’ opinions. Only care about those who matter to you.

Understanding Reading

What, Why, How. Keep reading. Don’t chase speed, but depth.

Understanding Romance

He shares sweet daily stories with his wife. If you don’t break up, you won’t discover how many better people there are out there. If you find one—lucky. If not—you can still live happily on your own.
Live with emotional intelligence and the world becomes beautiful.

Understanding Socializing

Associate with decent people. Different people require different approaches. Rejecting others is an art. If you don’t have the guts to ask for your money back, don’t lend it.

Understanding Human Nature

I tend to be skeptical about human nature. You can’t control others, so just manage yourself. Don’t be vulgar.

Understanding Good and Evil

Some people enter your life just to teach you a lesson. Good or evil—I prefer believing in others, as trust is given before it’s earned.
Even if deceived, it’s a lesson learned—don’t fall into the same trap again. But don’t stop trusting altogether; kind people still exist.
God made the right hand a right hand as a reward; same for kind people—kindness is its own reward.
I recall being in a restroom without tissue once. I was going to ask a friend, but a stranger overheard and handed me tissues. Such kindness moved me deeply.
Kindness must come with principles—otherwise it may hurt others or yourself.

Understanding Wealth

The poor stay poor because of narrow thinking. To get rich, don’t just invest in your body—think bigger. See the essence of problems.
Time is limited—how to get rich fast? Find a subject you love, apply it, think broadly, and work in fields closely tied to society.

Understanding Society

Have ambition. Take one step at a time. Build an independent personality. Be accountable for your actions. Learn to transform your ability to feel happiness—then change yourself.

Understanding Life and Death

Death studies include concepts like: No death, no life; face death to live; understand death to be reborn. Interesting and thought-provoking.
Knowing that we will die is what pushes us to live fully. When the time comes, I think I’ll embrace death.

What the world lacks isn’t perfect people, but justice, sincerity, courage, and compassion from the heart. — My biggest takeaway after watching Forever Young today.
We all know cause and effect—but causes and effects come from relationships. Someday, I’ll read Schopenhauer and Nietzsche properly.

Understanding Travel

This part recounts Mr. Zhuomo’s experiences in Tibet. Many dream of going there—maybe because it helps people truly understand life’s struggles.
The US is less disciplined than the UK. Japan has great food culture and says “sorry” a lot—avoiding bothering others.

Understanding Career

First learn to endure hardship. Then understand yourself and your talents. Begin with the end in mind and stay focused.

Understanding Education

Every child is born pure. Without proper education, they won’t become pillars of the nation.

Understanding Entertainment & Freedom

Life has two paths: one for career ambition—keep passion; the other for living warmly—keep humanity.
Open your eyes each day and tell yourself: Oh yeah!