TONG-H

frontend

17.9k110Notesjs2021-11-172025-02-23

rxjs

features:

  • one-directional data flow, from the Observable to the Subscriber
  • in a push system
    • push: the Producer determines when to send data to the Consumer. like promise
    • pull: the Consumer determines when to get data. like generator or a normal function
  • multicast and unicast
    • unicast by default
      • one producer is observed by only one consumer
      • lazy execution, a producer starts to execute when a consumer is subscribed to it
    • achieve multicast by using Subjects, one producer is observed by many consumers, like EventEmitters
  • cold observable and hot observable
    • cold observable: emits values only when it is subscribed to, the consumer receives values from beginning. is unicast.
    • hot observable: like websoket, emits values regardless of whether there are any subscribers, the consumer from where the Observable currently is—not from the beginning. is multicast.

Observable, a data stream that allow to be subscribe to

Observer, an object with three callbacks to response the three notifications next error complete
Subject, open to observers for subscribing, like EventEmitters: they maintain a registry of many listeners.

  • allows convert a unicast Observable execution to multicast
  • Subject, doesn’t care about value
  • BehaviorSubject, will store the current value
  • ReplaySubject, will send old values to new subscribers
  • AsyncSubject, only the last value will send to its observers at subject.complete()

Scheduler, managing concurrency, dictates how and when the operations should be scheduled in time.

  • queueScheduler
  • asapScheduler
  • asyncScheduler
  • animationFrameScheduler

Operators allow complex asynchronous code to be easily composed

  • Creation Operators, can be called as standalone functions to create a new Observable.
  • Pipeable operators

Micro-Frontend

blog: 微前端实践:single-spa+vite

web components

  • enables to attach a DOM tree to an element, and have the internals of this tree hidden from JavaScript and CSS running in the page. therefore, natural encapsulation for both styles and behavior
  • should be great for small, reusable pieces of UI, building and managing large-scale complex UIs purely with Web Components can be cumbersome

webpack module federation

  • each micro-frontend app bundling all of its dependencies. In the browser, if any shared dependency are downloaded, the subsequent micro-frontend apps will reuse that shared dependency without re-downloading

  • nginx deploy for a spa app

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # serves the index.html for all routes.
    location / {
    try_files $uri /index.html;
    add_header Access-Control-Allow-Origin *;
    }
    # handles static assets
    # ~* means case-insensitive regular expression matching
    location ~* \.(?:ico|css|js|gif|jpe?g|png|svg|woff2?|eot|ttf|otf|webp|avif)$ {
    expires 6M; # Cache these assets for six months
    access_log off; # Disable logging for static assets
    add_header Cache-Control "public";
    }
  • export a desired lifecycle through vite-plugin-Single-Spa which requires: type: "module" explicitly exists in package.json

  • vue.esm-bundler vs vue.runtime.esm-bundler.js

    • an import named vue_runtime_esmBundler may exist after vite build

gitlab ci/cd

blog: 使用 Gitlab CI/CD 自动打包和部署微前端

Monorepos

  • a single repository containing multiple distinct projects, with well-defined relationships. like app and web projects are exist in one repo, they share same libraries, logic and config files
  • code colocation the practice of grouping related pieces of code together within your project’s directory structure.

Time Complexity

the growth rate of the runtime as the input size increases.

  • O(1): Constant time – the execution time does not change with the input size.
  • O(n): Linear time – the execution time grows linearly with the input size.
  • O(log n)

Design Patterns

method chaining

a concept, one function returns the current(refer to this in js) object and another function can use values in this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

class chaining{
Method1(v){
this.v = v
console.log(this)
return this
}
Method2(v){
this.v = v
console.log(this)
return this
}
}
const chaining_obj = new chaining()
chaining_obj.Method1(1).Method2(2)

the principles of component design

  1. single responsibility principle. to compose complex components by smaller specialized components.
  2. conceals the construction of components, and only reveals the necessary information
  3. To ensure predictability and determinacy of components, always return the same output for a given input
  4. keep loose coupling, instead of tight coupling. Components should have little or no knowledge about others.
  5. composable. Composition is a way to combine components to create a bigger component
  6. reuseable
  7. pure or almost pure function which means no side effects and doesn’t depend on the environment or the global state, such as network or global variables.
  8. isolate the impure code from the pure
  9. straightforward to test
  10. a meaningful name. If a component’s usage is highly specific, its name may contain more words, like <HeaderMenu> <Header>
  11. Open Closed principle,software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification

the levels of a component based on its usage
1. Reading name and props; best
2. Consulting documentation; good
3. Exploring the code; admissible
4. Asking the author. avoid

performance optimization

  • avid reflow and repaint
  • utilize prefetch and preload to enhance the execution order of js and prevent it from blocking HTML rendering
  • enable GZIP compression on the server
  • minimize the number of DOM elements and keep the DOM structure as simple as possible
  • lazy loading
  • virtual scroll
    • listen to scroll event, only render the items within the visible area of parent element according the scrollTop and the visible height of the parent
    • dynamic height
    • Use the estimated height initially, and then replace the position value with the actual DOM height after rendering. Add a buffer area by rendering a few items at the top and bottom of the visible area. This provides more time to render and calculate for a smooth scrolling experience.
  • vue
    • use v-if and v-show appropriately
    • use computed instead of methods. Methods are called every time the component re-renders
    • watch
    • don’t use v-if with v-for

browser rendering

the rendering process

  • browser obtains the IP address from the domain name through DNS(domain name system) analysis
  • send a http request to that IP address
  • server receives the request and responds with html content
  • the browser parses html strings and create a dom tree, and then proceeds to analyzes CSS, forming CSS rule trees
  • structure the rendering tree(only the nodes necessary for display)

GUI rendering thread and JS engine thread

GUI(graphical user interface) rendering thread and JS engine thread are mutual exclusive. The rendering process pauses during the download, analysis, and execution of JS. When the browser encounters JS during the rendering process, it halts rendering to construct the rendering tree and passes control to the JS engine .

reflow and repaint

  1. reflow: When changes in DOM geometrical properties (like width, height, or display) affect the page layout or the position of other elements, the browser must recalculate the page’s layout.
  2. repaint: when the style(like color, background) of dom are changed
    If repaint doesn’t affect the geometrical size of the DOM elements, then reflow isn’t required. However, reflow will inevitably trigger repaint.

avid reflow and repaint as much as possible

  • use transform instead of positive and top, the former performs changes in the GPU instead of causing reflow and occupying the rendering thread.
  • use visibility instead of display:none, the former incur repaint only while the latter will change layout
  • instead of making multiple small changes to the DOM, try to make all necessary changes in one go.
  • accessing properties like offsetWidth, offsetHeight, or getComputedStyle() can trigger a reflow
  • use browser developer tools to monitor performance metrics like reflows, repaints, and rendering performance.

SVG

properties

width/height: define the size in the document
viewBox: first two number are min-x and min-y used to define the position, the other two are define the scale relative to width/height

1
2
when the viewBox is same as width/height, then the dimension is 1. if it is smaller than width/height, then the dimension is larger than 1, and vice versa.
<svg viewBox="0 0 200 200" width="200" height="200" xmlns="http://www.w3.org/2000/svg"></svg>

path

M: move to

L: line to

H: a horizontal line

V: a vertical line

Z: close path, combine the start and the end

C: curve, x1 y1 x2 y2 x y, the last set x y specify where the line should end and the line start at the point where the last operation ended, the first two are control points.

1
2
3
4
 <svg className="selected-round-btm" viewBox="0 0 8 8" width="8" height="8" xmlns="http://www.w3.org/2000/svg">
<path d="M 0,8 L 8,8 L 8,0 C 8,0 8,8 0,8 " fill="#fff"></path>
<circle cx="0" cy="0" r="8" stroke="#e4e4e4" fill="none" />
</svg>

S: smooth curveto

A: an arc, rx ry x-axis-rotation large-arc-flag sweep-flag x y

  • rx ry radius x and radius y

  • x-axis-rotation

  • large-arc-flag determines that the arc should be greater than or less than 180 degrees.

  • sweep-flag determines that the drawing direction is clockwise or anticlockwise.

  • x y the ending point calculated with angle, while the arc starting point is at three o’clock of a circle, circle center + radius = the arc starting point

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // get the ending point with an angle
    function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
    const angleInRadians = (angleInDegrees * Math.PI) / 180.0 // the length of the arc
    const largeArcFlag = angleInDegrees <= 180 ? "0" : "1";

    const x = centerX + radius * Math.cos(angleInRadians)
    const y = centerY + radius * Math.sin(angleInRadians)
    return [radius, radius, largeArcFlag, 0, x, y]
    }
  • a 360-degree circle can’t be done by one arc, since the end point are same with the start point. but it consist of two arcs.

    • "M 140 80 A 60 60 0 0 0 20 80 + 60 60 0 1 0 140 80

Q: x1 y1, x y, quadratic Bézier curve, to control the slope of two sides with one point, which is the first set
T: x y, smooth quadratic Bézier curveto, auto control the slope of two sides according to the last control point

filter

in && in2, SourceGraphic | SourceAlpha | BackgroundImage | BackgroundAlpha | FillPaint | StrokePaint |
1.in is the first input, and in2 is the second input, while the placing order is same with the layer order of PS
2.if in is not existed, then the value will be the previous filter input

  • feBlend, to blend two layers ruled by a certain blending mode
    • in
    • in2
    • mode
  • feComposite, to combine two input images
    • in
    • in2
    • operator, compositing operations

animation

  • SVG animation is not css animation, it’s part of the SVG animation specification, specifically within the SMIL (Synchronized Multimedia Integration Language) animations in SVG.
    it runs on the browser’s compositor thread, reducing the load on the main JavaScript thread.

CSS

anchor position

stacking context

Special properties like z-index will cause elements to form a stacking context 层叠上下文, as those properties can change the render order of elements on the page if their values are set to something other than none

position, opacity, filter, transform, detailed list of such special properties

selector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* nth-of-type and nth-child, the former according to the order of a given type, while the latter according to the order of children*/
p:nth-of-type(2) { }
p:nth-of-type(odd) { }
p:nth-of-type(even) { }
p:nth-of-type(3n) { }

/* select p which is behind h1*/
h1 + p { }

/*select all the children of container*/
.container > * { }

/* according to element's attributes */
input[type="button"] { }

box module

standard box: only content is included in with / height
IE box: border + padding + content = with / height

box-sizing: content-box = standard
box-sizing: border-box = IE

layouts

  • adaptive design: multiple predefined layouts are created for different screen sizes

  • responsive design: use flexible grids and elements that automatically adjust and reflow based on the viewport size

  • 物理像素:计算机硬件,真实屏幕的像素点

  • css像素:属于逻辑像素的一种

  • 设备像素比(Device Pixel Ratio,DPR):物理像素与逻辑像素之比吗,比如iphone6物理像素是750 x1334,但实际逻辑像素是 375* 667,所以dpr = 2,这表示iphone6采用高清屏使用两个像素去渲染一个像素使画面更高清,iphone6 plus 甚至是dpr = 3

  • 1px 问题:设计师的视觉稿是相对于物理像素750px,实现一个1px边框,但css逻辑像素是375px,border-width:0.5px 使用transform: scale(0.5)

  • % + meta

  • vw/vh/% + px + flex layout,响应式布局

  • flex + vw/v/%h布局,局部使用px

  • 在跨设备类型的时候(pc <-> 手机 <-> 平板)使用媒体查询

  • rem ,响应式布局,动态设置 root 的 fontsize,子节点使用rem,和flexible不同的是可以根据pc、手机、平板的尺寸来设置比例使子节点计算方便

1
2
var deviceWidth = document.documentElement.clientWidth
deviceWidth = deviceWidth < 320 ? 320 : deviceWidth > 640 ? 640 : deviceWidth

flexible, self-adoption

模拟vw特性,动态的将页面宽度的1/10作为根节点的 fontsize,子节点使用rem就等于是按照页面比例计算

1
2
3
var doc = document.documentElement
var rem = doc.clientWidth / 10
doc.style.fontSize = rem + 'px'

高倍屏适配,修改了等比缩放视口的scale值,使得clientWidth变为物理像素尺寸,1物理像素等于1css像素,由原来的375=>750, media尺寸查询语句也同样需要修改

1
2
3
4
5
var metaEL = document.querySelector('meta[name="viewport"]')
var dpr = window.devicePixelRatio
var scale = 1 / dpr
metaEL.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no')
document.documentElement.style.fontSize = deviceWidth / 7.5 + 'px' // 7.5并不固定

flex and grid

Flex was designed for layout in one dimension - aligning items dynamically in a single row or column. best for a small and sample component, like a nav bar, or buttons in a row
Grid was designed for two-dimensional layout - rows, and columns at the same time. best for a full page layout, product list

centered and vertically aligned layout

  • Flex

    1
    2
    3
    4
    5
    6
    .parent {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    }
  • Grid

  • display: table and display: table-cell;vertical-align: middle;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    .parent {
    display: table;
    width: 400px;
    height: 300px
    }
    .child {
    display: table-cell;
    vertical-align: middle;
    text-align: center;
    }

  • position and transform

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    .parent {
    position: relative;
    height: 100vh;
    border: 1px solid #ccc;
    }
    .child {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    }

others

  • text linear-gradient

    1
    2
    3
    4
    5
    6
    {
    color: transparent;
    -webkit-background-clip: text;
    background-clip: text;
    background-image: linear-gradient(130deg,red,black);
    }
  • browsers (like chrome) can set a minimum fontsize which can’t be overthrow, but transform: scale can be a workaround

    1
    2
    3
    4
    5
    6
    7
    8
      // set an unseen element for getting the minimum size
    const minimumSize = Number.parseInt(window.getComputedStyle(document.getElementById("minimumSize"), null).getPropertyValue("font-size").replace(/px/, ""))
    // generate different scales based on the minimumSize
    //the app has been set to fit `window.devicePixelRatio` in this situation. so the fontsize takes rem as unit, and item * fontSize is purposed to get the original size.
    const fontSizes = [1.2, 1.3, 1.4, 1.6, 2, 2.8].map((item) => {
    const _s = item * fontSize < minimumSize ? item * fontSize / minimumSize : 1
    return `--fontSize${item.toString().replace(/\./, "")}:${_s};`
    }).join("")
  • transform doesn’t work on inline elements

  • use position: sticky with flex layout. the sticked elements may still moving when user scrolling.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <!-- set flex's width to the sum total of all the cell widths 250vw to fix the problem -->
    <div class="container" style="overflow: auto">
    <div class="flex"style="display: flex">
    <div class="cell" style="width: 25vw;flex-shrink: 0;background: green; position: sticky; left: 0">cell</div>
    <div class="cell" style="width: 25vw;flex-shrink: 0;background: green;position: sticky; left: 25vw">cell</div>
    <div class="cell" style="width: 25vw;flex-shrink: 0;background: red">cell</div>
    <div class="cell" style="width: 25vw;flex-shrink: 0;background: red">cell</div>
    <div class="cell" style="width: 25vw;flex-shrink: 0;background: red">cell</div>
    <div class="cell" style="width: 25vw;flex-shrink: 0;background: red">cell</div>
    <div class="cell" style="width: 25vw;flex-shrink: 0;background: red">cell</div>
    <div class="cell" style="width: 25vw;flex-shrink: 0;background: red">cell</div>
    <div class="cell" style="width: 25vw;flex-shrink: 0;background: red">cell</div>
    <div class="cell" style="width: 25vw;flex-shrink: 0;background: red">cell</div>
    </div>
    </div>
  • text ellipsis

    • text-overflow: ellipsis only applies to single line

      1
      2
      3
      4
      5
      .single {
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
      }
    • multiple lines

      • use after pseudo class to display “…” after the text
      • cut off the text through js
  • attr()

    1
    2
    3
    .test::before {
    content: '我的内容是' attr(text) '颜色是' attr(data-color);
    }
    1
    <div class="test" data-text="TEXT" data-color="red"></div>
  • shape-outside make the adjoining inline-elements around itself

  • pseudo-elements, don’t work on img/select/input

    • ::before and ::after can add elements, and clean float.
    • ::first-line, ::first-letter, ::selection
  • pseudo-class

    • :enabled and `:disabled
    • :checked
    • :nth-child(n), :nth-of-type(n)
  • element hiding

    • transform: scale(0,0) will not respond to event listeners
    • visibility:visible can display children whose parent is hidden by visibility:hidden
  • Browsers render empty characters between inline elements as whitespace.

Storage

  • cookie, 4kb, can set the expire time, httpOnly = true means this cookie is set by server and can only be changed by server

  • localStorage sessionStorage, maximum size 5MB

    1
    2
    3
    4
    localStorage.setItem('chooseId',JSON.stringify(this.$route.params))
    localStorage.removeItem('chooseId')
    localStorage.clear()
    localStorage.key(index)
  • indexedDB no size limits, it can be used like a database

  • caches, an object which include cache instances, each cache instance can viewed as a storage region. the return type of all the methods of caches or cache instance is promise.

    • methods which are mounted on caches are used for managing cache instances.

      • caches: keys open delete has match
      • differ between has and match: has return boolean while match give you the data
    • if we want to store something, then we need to use open to get a cache instance where is our data stay

      • cache instance: add addAll keys delete match matchAll put

        1
        2
        3
        4
        5
        6
        7
        8
        // differ between `add` and `put`: add can `fetch` and `store` the data automatically, but also because of automatically then we can't make changes on key and value to be stored
        // so fetch + put = add
        cache.add(request).then(function() {
        // request has been added to the cache
        });
        fetch(url).then(function(response) {
        return cache.put(url, response);
        })
      • when use cache with service worker, the data we stored is the raw response returned by http

RegExp (regular expression)

1
2
var re = new RegExp(“a”, "img")
var re = /a/img;

modifier

  • i ignore uppercase and lowercase of letters
  • g global matching, to search from the start to the end whatever how much
  • m matching on multiple lines
  • es6 u turn on Unicode mode to deal with Unicode characters
  • es6 y similar with g, but it’s match work started form the beginning of the string
    • it’s effect is equal to add ^ at the beginning of a regexp, so it could regarded as a complement for that g couldn’t add ^
    • sticky, new property of RegExp, to know the regexp is added y or not

Properties

  • es6 regexp.flags, get all the modifiers

  • es6 regexp.source, get the regexp context

  • regexp.lastIndex, allow you to define and get the index from where the next match work started

    1
    2
    3
    4
    5
    const REGEX = /a/g;
    REGEX.lastIndex = 2; // to define the index
    const match = REGEX.exec('match a string');
    match.index // 6
    REGEX.lastIndex // 7, to get the index

test(), is matching or not

1
regExp.test(str) // boolean

search()

1
string.search(regExp) // return an index or -1

replace(), es6 replaceAll

1
string.replace(regExp, replaceText) // return the replaced string

exec()

1
regExp.exec(string) // return an array of information which include lastIndex, allow you to match along the last position.
1
2
3
4
5
6
// use with while
var regExp = /t(e)(st(\d?))/g;
var string = 'test1test2test3';
while (match = regExp.exec(string)) {
console.log(match)
}

with named capture groups(具名组匹配), you can get the data of each group which declared by ()

1
2
const regExp = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
const matchObj = regExp.exec('1999-12-31');

match(), es6 matchAll

1
'For more information, see Chapter 3.4.5.1'.match( /see (chapter \d+(\.\d)*)/i) // return an array of information

symbols

PS Safari does not support ?!=n ?<=n ?<!=n ?:n

key usage
() Parentheses, to group regular expressions
[] match any character within the brackets
^ $ match from the start or the end of the string
[^a] match string which no ”a“
{n,m} the display times > n and < m
{n,} the display times > n
{,m} the display times < m
* equal to {0,}
+ equal to {1,}
? equal to {,1}, /.*?(\d)/, when ? is used with greed mode, it means to exclude a specific format
| a|b === a or b
. equal to {1,}
.* greedy mode
.*? the regex engine will, at every step it matches text into the “.” attempt to match whatever make come after the “.*?”
\d equal to [0-9]
\D equal to [^0-9]
\w equal to [a-zA-Z_]
\W equal to [^/w]
\s equal to [ \f\n\r\t\v](换页,enter,空格,tab,垂直制表符) , matching all the empty characters
\S equal to [^/s]
/S/s all the characters
(.|\n)* to match any characters across multiple line
?=n match string but don’t get, the string after which is n
?!=n match string but don’t get, the string after which isn’t n
?<=n (incompatible with safari) match string but don’t get, the string before which is n
?<!=n match string but don’t get, the string before which isn’t n
?:n match string but don’t get

examples

to match every character except ,;

1
/[^,;]+/

to match a string, stop matching until a specific format is encountered

1
2
3
 // ?(?=hello) match string but don't get
// ?(hello) match string and get
'"/public/images/index/组 4099.png","textAlign":"'.match(/public\/images\/(.*?(?="))/img)

verify float number, two standards judged by |

1
/^(([0-9]+\.?[0-9]+)|([0-9]*))$/.test("5.")

get an array of date number arranged by [year, month, day, hour, minute, second]

1
new Date().toLocaleString("zh").match(/[0-9]*/g).filter((el) => el).map(el => (el.length < 2 ? "0" : "") + el)
  • extract the type in the string returned by Object.prototype.toString.call
1
/(?<=\[object ).*(?=])/.exec(Object.prototype.toString.call({}))

Javascript

symbol and keyword

keyword function
=~ 按位非var num1=~25;//-26 值反一次再减一
6%4 取余, c%2 !== 0
~~ 取整 ~~(6 / 4) // 1 ~~(9 / 4) // 2
yield next yield* to stop or resume a generate function, yield* 加入另一个可迭代的对象
es6 ?. optional chain, stop running when null or undefined is encountered
es6 ?? null ?? 4; 4 ?? 5 the right value will be returned when the left value is null or undefined
es6 &&= a &&= 1 === a && (a=1) // if a is existed then assign 1 to it
es6 ??= a ??= 1 === a ?? (a=1) // if a is null or undefined then assign 1 to it
es6 ** 指数运算符 2 ** 3 // 8 b **= 3; // equal to b = b * b * b;
es6 symbol data type: designed to prevent conflicts in property naming
  • delete obj.a delete an object’s own property without affecting properties that are part of the object’s prototype chain

  • in: to get know whether a property or an item exists

    1
    2
    var arr = [1]; console.log(0 in arr); // true
    var arr = { a: 4 }; console.log('a' in arr); // true
  • es6 … spread syntax to expand an iterable such as an array expression or string

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // express rest arguments
    const bar = (...args) => args

    // to concat or insert items
    { ...obj1, ...obj2 }
    [...iterableObj, '4', 'five', 6]

    // Applying with new operator
    new Date(...[1970, 0, 1])
  • while, the expression inside the brackets can be an assigning expression

    1
    2
    3
    4
    5
    6
     var bb = 1
    while (aa = bb) { // assigning the value of bb to aa, and judge aa is false or not
    console.log(aa)
    bb++
    if (bb === 5) bb = null // bb = null, then aa = null
    }
  • break continue return, continue is used to break one layer of the loop

    1
    2
    3
    4
    5
    6
    7
    8
     for (let i = 0; i < 5; i++) {
    if (i === 3) break
    console.log(i) // 0 1 2
    }
    for (let i = 0; i < 5; i++) {
    if (i === 3) continue
    console.log(i) // 0 1 2 4
    }
  • es6 set similar to Array, but each item in set is unique

    1
    2
    3
    const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
    items.size // the length is 5, and the repeated value aren't be counted
    items.add(6) // add item
  • es6 map similar to set, but key: value structure like an object. the key of an object is string, while the key of a map can be any type.

    1
    2
    3
    4
    5
    const m = new Map();
    const o = {p: 'Hello World'};
    m.set(o, 'content')
    m.get(o)
    m.size // 1
  • es6 WeakMap && WeakSet, all the properties of the two are shallow copied(weak reference), if an object are referenced only by WeakSet, then this object will be deleted by Garbage-Collection

  • export && import && export default

Strict Mode

  • ReferenceError: Assigning a value to an undeclared variable. in Non-Strict Mode, automatically creates a global variable
  • TypeError: assign a value to a read-only property.
  • SyntaxError: duplicate parameter names in a function
  • undefined this: the value of this is automatically coerced to a global object in Non-Strict Mode. in Strict Mode, this is undefined in functions that are not called with a specific context.
  • SyntaxError: use reserved keywords as name of variables
  • Function declarations within blocks
1
2
3
4
5
6
7
8
{
if (true) {
function foo() {
return "hello";
}
}
console.log(foo()); // Strict: ReferenceError
}

variable declaration

var

  • if a var variable is not declared in a function scope, then it is a global object

  • can be re-declared in a same scope

    1
    2
    var b = 1
    var b = 1
  • var can be used before declare, var declaration will be raising to the first line when engine is running, even the variable is declared in a block scope. this is called hoisting

    1
    2
    3
    4
    console.log(a) // warning
    let a = 1
    console.log(b) // undefined
    var b = 1

let, const

  • let declares a variable.

  • const declares a constant with simultaneous assignment and declaration

    • its value cannot be reassigned
    • For reference type data, the value maintains the memory location unchanged.
  • no variable increase

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    if (true) {
    let m = 'block scope';
    const n = 'block scope 2';
    var k = 'global scope';
    console.log(m); // block scope
    console.log(n); // block scope 2
    console.log(k); // global scope
    }
    console.log(m); // Reference Error
    console.log(n); // Reference Error
    console.log(k); // global scope

    function a() {
    var a = 1
    if (a == 1) let a = 2
    }
    function a() {
    let a = 1
    if (a == 1) var a = 2 // warning
    }
  • a variable cannot be declared with the same name as a function’s arguments.

    1
    2
    (function func(arg) { var arg })()
    (function func(arg) { let arg })() // SyntaxError

es6 shorthand

1
2
3
4
5
6
const a = {
'first word': 'hello',
[lastWord]: 'world'
}

const baz = {foo}

iterate

  • for...in... iterates object’s key, and array’s index
  • forEach can’t jump out by return, forEach for...of... iterate value and can’t be used on Object
1
2
3
4
5
Object.keys(obj) // without Symbol
Object.getOwnPropertyNames(obj) // without Symbol
Object.getOwnPropertySymbols(obj) // only Symbol
Reflect.ownKeys(obj) // all the attribute names no except
for (let i in obj) console.log(obj[i])

Instance’s Properties

1
2
3
4
5
6
// Object.create(null) will create an object which does not inherit from Object.prototype, so the methods of Object are inaccessible.
Object.create(null).hasOwnProperty("foo") // error

// the following two are worked same
Object.hasOwn({a:0},"a")
Object.prototype.hasOwnProperty.call({a:0}, "a")

Object’s Properties

  • es6 Object.values({}), Object.keys({}), es6 Object.assign()

  • es6 Object.entries({}) Object.fromEntries([])

  • es6 Object.is(value1, value2) compares objects according they reference. almost same as ===, but there has two differences:

    1
    2
    3
    4
    5
    +0 === -0 //true
    NaN === NaN // false

    Object.is(+0, -0) // false
    Object.is(NaN, NaN) // true
  • Object.prototype.toString.call() get data’s type

  • es6 Object.getOwnPropertyDescriptors

  • es6 Object.setPrototypeOf() Object.getPrototypeOf() set and get the prototype of an object

JSON

  • JSON.parse(string)

  • JSON.stringify(obj)

    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
    JSON.stringify(object: any, replacer: (key: string, value: any) => any, space: string)
    const foo = {
    foundation: "Mozilla",
    model: "box",
    week: 45,
    transport: "car",
    month: 7,
    obj: {
    a: 2,
    b:[7,8]
    }
    };

    // if replacer is a function, replacer will be called on each value to-string process
    // ps: the value of the first call is the target object
    JSON.stringify(foo, (key, value) => {
    console.log(key, value)
    if (typeof value === "number") {
    return "number"
    }
    return value
    }) // '{"foundation":"Mozilla","model":"box","week":"number","transport":"car"}'

    // if replacer is an array containing only string, the output will retain ony the {key: value} pairs where the key is listed in the replacer
    JSON.stringify(foo, ["month", "obj", "a"]); //'{"month":7,"obj":{"a":2}}'

Array

  • includes(item)

  • lastIndexOf, indexOf

  • at(index), index can be negative

    1
    2
    3
    const arr = [1, 2, 3, 4, 5];
    arr.at(2) // 2
    arr.at(-2) // 4
  • forEach, cannot break the loop
    if an item is deleted during the loop, the items after it will be skipped

  • every all items return true => return true, any one return false => return false and break the loop

  • some any item return true => true, no one return true => false

  • find findLast | findIndex | findLastIndex any item return true => return the item or index(findLast return the last one)

  • filter return an array constituted by items which is returned true

  • map return an array constituted by the return data of the function

  • reduce reduceRight, get an accumulation from an array, the latter is executed by the order of right to left

    1
    [1,2,3].reduce((accumulator, currentValue, currentIndex, Array) => accumulator + currentValue)
  • reverse reverse the order of an array

  • join, concat(the items of new array are shallow copied)

  • fill(value, start, end)

  • pop / shift remove the first / last item of an array, and return the removed item

  • unshift / push add item to the start / end of an array, and return the current length of an array

  • splice slice, three parameters: start index, delete count, items which need to add. return the removed items
    splice changes the original array, while slice create new one

  • sort

  • flat

    1
    2
    3
    4
    // 数组降维,递归地将数组展平到指定的深度,默认为1	const array = [1, [2, [3]]];
    array.flat();  // → [1, 2, [3]]
    array.flat(Infinity);   // → [1, 2, 3]
    [234].flatMap((x) => [x, x * 2]);  // → [2, 4, 3, 6, 4, 8]
  • set

    • has(value) return boolean
    • add(value) return the array itself
    • delete(value) return boolean
    • clear() no return, clean all items
    1
    2
    [...new Set(array)]	// repeated items will be deleted
    Array.from(new Set(array)) // set to array

number precision issues

JS uses floating-point format the IEEE 754 64-bit format.
computers use the binary (base-2) number system, which cannot precisely represent many decimal (base-10) fractions. some floating-point numbers, such as 0.1 or 0.2 get stored as approximate values.

IEEE 754 stores numbers in computer memory and allocates a 64-bit storage space for floating-point numbers.

  • 1-bit for a sign(express negative or positive)
  • 11-bit for exponent
  • 52 -bit for fraction
1
2
3
// decimal to binary in computer
0.1 (decimal) = 0.0001100110011001100110011001100... (binary)
0.1.toString(2) // 0.0001100110011001100110011001100110011001100110011001101

workarounds

  • a tolerance for comparison

    1
    2
    const epsilon = 1e-10;
    console.log(Math.abs((0.1 + 0.2) - 0.3) < epsilon);
  • string conversion

  • scale numbers to integers for avoiding fractions.

  • packages like Big.js or Decimal.js

scope

scope types

  • global scope

    • defined at the external of the outmost function
    • declared without keywords like var, let const
    • mount on window
  • ES6 Module scope

  • function or local scope, a variable is declared within a function

  • block scope, if / switch / while / for create a block scope. a variable defined using let / const stays in the block scope

  • static scope, when looking a variable, the engine will go to the scope in which the variable was created

    1
    2
    3
    4
    5
    6
    7
    var x = 10
    function fn() { console.log(x); }
    function show(f) {
    var x = 20;
    fn() //10
    }
    show(fn);
  • nested scope, a scope can be nested inside another scope.

scope chain

when the JS engine tries to find a variable, it first looks in the current scope, if it doesn’t find then it moves to the parent’s scope. finding layer by layer, this process is called scope chain.

1
2
3
4
5
6
7
8
9
var a = 100
function F1() {
var b = 200;
(function () {
var c = 300
console.log(a, b, c)
})()
}
F1()

Memory Management

life cycle

  • Allocate the memory

    • JS automatically allocate memory whenever a value is declared

    • function is considered as an object

    • arguments needs memory allocation

      1
      2
      3
      const s = "azerty";
      // [0,3] range needs stored
      const s2 = s.substr(0, 3);
  • Use the allocated memory (reading and writing)

  • Release the allocated memory by Garbage-Collection

Garbage-Collection

an object has a reference to its prototype (implicit reference 隐式引用) and to its properties values (explicit reference 显示引用).

Reference-counting Garbage-Collection 引用计数垃圾收集

  • An object is said to be garbage, if there are zero references pointing to it.

  • circular references easily results in memory leak.

    • two objects are reference to each other in a function scope. They will go out of the function scope after the function call has completed. they are useless after the function is called, but they will not be considered garbage since they still have at least one reference.
    1
    2
    3
    4
    5
    6
    7
    8
    function f() {
    const x = {};
    const y = {};
    x.a = y; // x references y
    y.a = x; // y references x
    return
    }
    f()

mark and sweep algorithm 标记-清除算法

  • An object is said to be garbage, if an object is unreachable.
  • the garbage collector starts from the root(the global object), and inspects all the children recursively. thus, the garbage collector will find all reachable objects and mark them as active. Any memory not marked as active can now be considered garbage.
  • with this algorithm, circular references is no longer a problem since the objects is unreachable after the function is completely.

weakly reference

if an object is only held by weakly reference, then the object will be considered as unreachable.
The keys of WeakMap and WeakSet can be garbage-collected (for WeakMap objects, the values would then be eligible for Garbage-Collection as well) as long as nothing else in the program is referencing the key

FinalizationRegistry

listen to the garbage-collect event

1
2
3
4
5
const registry = new FinalizationRegistry(heldValue => {
console.log(heldValue)
});
let obj = { a: 0 }
registry.register(obj, "some value"); // target and holdings cannot be same

memory leak

  • A memory leak occurs when a program fails to release memory that is no longer needed, leading to increasing memory usage over time

  • Out of DOM references. a DOM node that was once kept in an object, even after it’s removed from the DOM tree, still persists in that object.

  • timers or callbacks. Most of libraries provide observers and other facilities that take callbacks.

  • accidental global variables. cause mark and sweep algorithm, a variable which is mounted on window or this will not be considered garbage

    • add ‘use strict’ can prevent this issue
    1
    2
    3
    4
    5
    function foo() {
    this.variable = "potential accidental global";
    }
    // Foo called on its own, this points to the global object (window)
    foo();
  • closure. A closure retains access to variables from its outer function’s scope, even after the outer function has completed.

    1
    2
    3
    4
    5
    6
    7
    8
    function createClosure() {
    let largeArray = new Array(1000000).fill("data"); // Large memory allocation
    return function () {
    console.log(largeArray.length); // Closure keeps reference to largeArray
    };
    }

    const leakyFunction = createClosure(); // `largeArray` remains in memory

closure

  • closure is a combination created by binding a function to the lexical environment.
  • allows to access the outside scope from the inner of a function.

advantages

  • data privacy. it means that you can’t access the data of the inner scope from the outside.

  • privileged methods. the methods exposed by the function are privileged since they can access data in both the inner scope and the outer scope.

  • stateful function. a state is a snapshot of the current environment, every change will take a picture of the current environment.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var func = () => {
    let a = 3
    return (b) => {
    a = ++ a
    return b * a
    }
    }
    let closure = func()
    closure(4) // 16
  • partial application refers to the process of fixing a number of arguments to a function, producing another function that takes fewer arguments than the original function. it takes advantage of closure scope in order to fix arguments.

    1
    2
    3
    4
    5
    const partialApply = (fn, ...fixedArgs) => {
    return function (...remainingArgs) {
    return fn.apply(this, fixedArgs.concat(remainingArgs))
    }
    }
  • Function Factories, to generate functions with pre-configured behavior or to create multiple functions with similar functionality. like debounce

  • Memoization, to cache the results of expensive function calls

Stale closure

a stale closure captures a variable whose value is outdated. like useMemo

  • createIncrement function returns increment and log. The former responsible for value increasing. the latter logs a const, whose value is fixed since it defined, then every time call the log fun, it gets message which has a outdated value.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function createIncrement(incBy) {
    let value = 0;
    function increment() {
    value += incBy;
    console.log(value);
    }
    const message = `Current value is ${value}`;
    function log() {
    console.log(message);
    }
    return [increment, log];
    }
    const [increment, log] = createIncrement(1);
    increment(); // 1
    increment(); // 2
    log(); // Current value is 0
  • When using setTimeout and setInterval, the function delays execution, but any variables saved within the function retain their values as they were at the time the function was defined. This means that if variables change during the delay time, the function only accesses the old values of those variables.

prototype

  • prototype, object inherit properties and methods from it

  • an instance inherits prototype from constructor, which is stored on the __proto__

  • a constructor has a prototype which includes two properties constructor and proto

    • even all the built-in functions such as Object Function Array RegExp Date Boolean Number String , the prototype.proto of them point to the Object.prototype, that means they inherited are from Object, and Object.prototype.proto refer to null.
    1
    2
    3
    Person.prototype.constructor === Person // Person is a constructor, Person.prototype.constructor point to the "constructor" function self
    person01.__proto__ == Person.prototype // person01 is an instance, person01.__proto__ point to a place same as Person.prototype
    Array.prototype.__proto__ == Object.prototype == Object.prototype.__proto__ = null

property chain

when we access an object‘s property, first the engine will try to find the property within the object itself. if it doesn’t find, it then look at the object.__proto__. if it still doesn’t find , it continues up the __proto__ chain, checking each subsequent __proto__. until it find Object.prototype, in which the prototype is null.

constructor vs normal function

  • a constructor usually doesn’t use return keyword, because this of a constructor will be used as the result of new keyword.

  • when a constructor returns an object, that object will be used as the result returned by the new keyword. otherwise, if the result is a primitive type, then this will be returned by the new keyword.

    1
    2
    3
    4
    5
    6
    7
    function Person(name) {
    this.name = name;
    return "Hello"; // a primitive value
    }

    const person1 = new Person("Alice");
    console.log(person1); // Output: Person { name: "Alice" }
  • by the first rule, this and new are used in a constructor but not in a normal function

  • what does NEW do?

    • create an Object
    • bind this of the current scope to that object
    • copy the prototype from the constructor, and store it in the object._proto_
    • execute the code inside the constructor, and return the object

Inheritance

  • create a child object classes which will inherit features from another class
  • OO => object oriented programming
  • favor object composition over inheritance
  • concatenative inheritance, inherit features from another object by copying the source objects properties
    • Object.assign()
    • ... spread syntax
    • bind, call and apply(can receive an array). binding “this” to a function
1
2
3
4
5
6
7
8
9
10
11
12
let beBound = { a: 1 }
let beCalled = function (...arg) { console.log(this, arg )}
// the first argument will be bound with the function, the others used as rest arguments
// return a function which are created by binding a copy of the first argument to the called function
let newOneFixedArgument = beCalled.bind(beBound, "newOne", "fixedArgument")
newOneFixedArgument()
let newOneFreeArgument = beCalled.bind(beBound)
newOneFreeArgument("newOne", "freeArgument")

// call and apply will execute the function immediately, and doesn't change the called function
beCalled.call(beBound, "call", "individual arguments")
beCalled.apply(beBound, ["apply", "array argument"]) // accept an array

Inheritance Chain

every object has a __proto__ (or prototype) that points to another object. This chain continues up until it reaches Object.prototype, which is the root of all objects.

prototype delegation

  • new constructor
    • functional inheritance / factory function. It is not a constructor or class. Instead, it operates by generating an object from a factory and extending the produced object by assigning properties to it through concatenative inheritance.
  • class extend / sub-classing.
    inherit everything from a class, can’t choose what you wanted
    this initialization by calling the parent’s constructor with super() and can pass arguments to the parent, while in the constructor function that the new keyword does the initialization automatically.

deep and shallow copy

  • basic data types: data are stored in stack

  • reference data types: store data in heap, object’s reference in stack

  • shallow copy: copy the reference of object

  • deep copy: copy data

  • the following methods perform deep copy only the first layer of an object.

    1
    2
    3
    4
    Object.assign({}, state)
    {...state}
    Array.prototype.concat()
    Array.prototype.slice()

completely deep copy

  • JSON.parse(JSON.stringify()), some complex types will be translated into string:

    • Dates, functions, undefined, Infinity, RegExps, Maps, Sets, Blobs, FileLists, ImageData, sparse Arrays, Typed Arrays

    • undefined will be ignored

    • an object with circular references(properties are refer to each other) can’t be converted

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      let obj = {
      a: 1,
      b: {
      c: 2,
      d: 3,
      },
      f: undefined,
      }
      obj.c = obj.b
      obj.e = obj.a
      obj.b.c = obj.c
      obj.b.d = obj.b
  • structuredClone() creates a deep clone of a given value(include circular references). able to clone complex types DOM element File, Map, Set that JSON.stringify() cannot

  • MessageChannel, data that is delivered MessageChannel are deep copied. but delivering a function will cause error

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     function deepCopy(obj) {
    return new Promise((resolve) => {
    const { port1, port2 } = new MessageChannel()
    port2.onmessage = (ev) => resolve(ev.data)
    port1.postMessage(obj)
    })
    }

    deepCopy(obj).then((copy) => {
    console.log(copy);
    })

Single-threaded

a thread is a single unit of execution. one or more threads can be exist in a process. a Single-threaded programming language which has a single-threaded runtime engine that runs tasks in a program sequentially.

manipulating dom in the web worker is not allowed
web worker is controlled by main-thread
JS as a browser script, interacts with user and manipulates dom. two threads will bring synchronize problem, as if one thread changes a node, and another thread removes that node.

Event Loop

  • Heap and Stack?

    • two areas in memory where data is stored.
    • Heap(dynamic memory allocation) stores objects which mutable and have a variable size.
    • Stack(Static memory allocation) stores Primitive values which is immutable and have a fixed size.
  • Call Stack
    Call Stack is in the Main-Thread and is used to store functions for execution

  • synchronous and asynchronous

    • two types of js tasks.
    • synchronous tasks are stored in the Call Stack, waiting for execution
    • asynchronous tasks like fetch or setTimeout are handled by other process in the browser. JS is Single-threaded, but browser is Multi-process
  • Task Queue(Callback Queue)

    • following FIFO(first in first out)
    • asynchronous tasks are related with callbacks. if a asynchronous task is finished by browser, then it’s corresponding callback will push into the Task Queue
  • Event Loop

    • if all the tasks in Call Stack are completed, then JS engine will continuously scans Task Queue to find tasks and push them into Call Stack. this process where JS engine constantly scans for tasks is called as Event Loop.
    • through Event Loop, JS engine can handle asynchronous tasks, and meanwhile keeps the responsive of the page
  • Call Frame

    • every call of function will leave a Call Frame(调用帧), which keeps the inner information about function including variables and the location where the function called. those information will be deleted after the function is completed.
    • following LIFO(last in first out)
    1
    2
    3
    4
    5
    // put C into stack => B in => A in => A out => B out => C out
    function funA() { console.log('funA') };
    function funB() { funA() }
    function funC() { funB() }
    funC()
  • two types of tasks in Task Queue, Macro and Micro

    • Macro tasks include setImmediate(Deprecated) / setTimeout / setInterval / MessageChannel /requestAnimationFrame / UI render/ I/O in node.js

    • Micro tasks include callbacks onerror then.

    • setImmediate -> MessageChannel -> setTimeout 0

    • the engine checks Macro Task first and the Micro Task second.

    • initialize and resolve of Promise are synchronous.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      setTimeout(function () {
      console.log(1)
      }, 0)
      var p1 = new Promise(function (resolve, reject) {
      console.log(2)
      setTimeout(function () {
      console.log(4)
      resolve(1)
      }, 0)
      })
      setTimeout(function () {
      console.log(3)
      }, 0)
      for (var i = 0; i < 5; i++) {
      ;(function (j) {
      p1.then(function (value) {
      console.log("promise then - " + j)
      })
      })(i)
      }
  • async await, look at the following example:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    async function async1(){
    console.log('async1 start')
    await async2()
    console.log('async1 end')
    }
    async function async2(){
    console.log('async2')
    }
    console.log('script start')
    setTimeout(function(){
    console.log('setTimeout')
    },0)
    async1();
    new Promise(function(resolve){
    console.log('promise1')
    resolve();
    }).then(function(){
    console.log('promise2')
    })
    console.log('script end')

    async1() doesn’t have await prefix, async1 will be translated by v8 like:

    1
    2
    3
    4
    5
    6
    7
    8
     // `async2` is Promise initialization, so it‘s a synchronous
    // the code after `await` will be put in `then` callback
    function async1(){
    console.log('async1 start')
    Promise.resolve(async2).then(()=>{
    console.log('async1 end')
    })
    }
  • Tail Call 尾调用 refers to calling another function “at the end” of a function’s execution.

    1
    function a(x) { return b(x) }
    • b is a Tail Call. given Call Stack, when a function is called, it creates a Call Frame that holds internal information until the call is completed. while Tail Call is the final action, the Call Frame will be kept until Tail Call is finished.
    • optimize Tail Call to reduce stack space usage, is called Tail call optimization.
  • Tail Recursion

    • due tof call frame, Recursion often consumes a significant amount of memory and results stack overflow

    • in languages that support Tail call optimization, Tail Recursion can be used to avoid such issues

      1
      2
      3
      4
      5
      6
      7
      8
      9

      // the recursive call is the last operation performed by the function
      // the call frame associated with the recursive call can be discarded or reused.
      function factorial(n, acc = 1) {
      if (n === 0) return acc;
      return factorial(n - 1, n * acc); // Tail recursion
      }
      console.log(factorial(5)); // Output: 120

debounce and throttle

  • debounce. a function can be executed when it is not called during a specific time

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // execute debounce => got a function which bound with the debounce scope
    // every time the instance executed that will clean and reassign the timer
    const debounce = (func, delay) => {
        let timer
        return function ({
            const context = this, args = arguments
            clearTimeout(timer) // to clean before reassign
            timer = setTimeout(() => func.apply(context, args), delay)
        }
    }
    const instance = debounce(function (console.log("executing") }, 3000)
  • throttle. a function can be executed only when the time is up

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
      // execute throttle => got a function which bound with the throttle scope
    //when the isExpired is true, that means that the function can be executed and when it executed, then change the isExpired's state to false and set a time to change back
      const throttle = (func, delay) => {
            let isExpired = true
            return function ({
                const context = this
                const args = arguments
                if (isExpired === falsereturn
                func.apply(context, args)
                isExpired = false
                setTimeout(() => isExpired = true, delay)
            }
        }
        const instance = throttle(function (console.log("executing") }, 3000)

breakpoint resume

web: fileReader + slice + FormData
server: createWriteStream + createReadStream

messageChannel

  • to communicate between two iframes / web workers / js modules
  • deep copy

generator

use generator to simulate async function

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
function run(fn: any) {
const gen = fn()
function thunk(...argu: any[]) {
const res = gen.next(...argu)
if (res.done) return
if (res.value instanceof Promise) {
res.value.then((res: any) => thunk(res))
}
}
thunk()
}

const gen = function* (): Generator<any, any> {
const a = yield get({ api: "manifest.json", params: { tenant: "devices" }, options: { sync: false } })
console.log("a", a)

const b = yield get({
api: "manifest.json",
params: { tenant: "devices" },
options: {
sync: false,
},
})
console.log(b)

}

run(gen)

vue vs react

both of them are component-based architecture

Vue

  • considered easier to learn for beginners due to its simpler syntax and clear documentation.
  • use a Single File Component (SFC) which contains a template section for HTML markup, a script section for JS logic, and a style section for CSS.
  • has a growing community, most libraries are developed by the official team.
  • has a powerful two-way data binding system, keeping data and the UI in sync.
1
2
<!-- The input value is bound to the 'msg' data property -->
<input type="text" v-model="msg" />

React

  • has a steeper learning curve due to its extensive ecosystem and more complex concepts.
  • uses JSX (JS XML), this allows to write HTML-like code in js files. JSX may be more powerful and expressive.
  • has a larger ecosystem and community support, with many third-party libraries and tools available.
  • follows a one-way data flow, explicitly manage state and re-render components when needed.
  • data flows in a one-way direction, from outside to inside, and from top to bottom. this ensures a simple and predictable data flow

the differences between class component and function component can considered as the difference between vue and react. since vue uses class component

  • no this issues in function components, you don’t have to do scope bonding
  • code lesser with function component, but worse maintaining
  • doesn’t need to manage state and life cycle

architectural patterns

MVC

  • Model - View - Controller
  • model contains data model, and describes how the data can be changed and manipulated.
  • view is UI components. it receives data from the model (via the controller) and display it to the user.
  • controller sits between model and view, processing the data from the model and passing back to the view for rendering.
  • view and model can interact with each other directly, so it’s coupled.

MVVM

  • Model – View – ViewModel
  • view and model are same as in MVC
  • ViewModel sits below the UI layer, acts as a bridge between the View and Model. Unlike the Controller in MVC, it doesn’t handle user interactions directly. it binds data between the View and Model, and ensures that the View reflects the state of the Model and vice versa.
  • view and model can’t interact with each other, it’s decoupled.
  • vue and react can be vm or mvvm, early Angular is mvc.

active view and passive view

  • the later only outputs UI and doesn’t accept user’s input, contains zero logic
  • the former contains events, data binging, and behaviors

Typescript

references

setting tsconfig for specific file

diff with js

pro

  • object-orient language static type checking. it can find low level bugs, like spelling mistake, incorrect or instanceof variable properties
  • type infer.
  • optional parameters
  • enum support
  • better support in IDE, like intelliSense of editor, auto-supplement
  • the type files can increase code reading ability, and make the project maintaining easer
  • .d.ts can be viewed as specifications
  • to identify incompatible interfaces when the field name changed

con

  • compiling step is required, and it may take a long time.
  • abstract class is not support
  • definition files for a third library is required
  • pollutes the code with type gymnastics

type judgement

  • Generics

  • keyof

  • typeof

  • indexed access: User["name]

  • map types, use - or + to remove readonly or optional modifier

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    type OptionsFlags<Type> = {
    [Property in keyof Type]: Type[Property]
    }
    // change all readonly or optional properties to normal
    type CreateMutable<Type> = {
    -readonly [Property in keyof Type]: Type[Property];
    };
    type Concrete<Type> = {
    [Property in keyof Type]-?: Type[Property];
    };

Diff of null and undefined

  • null means an absence of value
  • undefined means not initialized

Diff of .ts and .d.ts

  • .d.ts file is a declaration file, and would not be complied as a js file
    • in a .d.ts file, if we want it act as a global declaration, then we have to use /// Triple-Slash Directives to import dependencies
    • /// <reference types="node" /> is used for importing an outside module, while /// <reference types="./global.d.ts" /> is for importing a local declaration file

Diff of type and interface

  • type is used to declare a type alias, act like a variable. while interface introduce a named object type.

    1
    2
    3
    // get the type of a variable to deduct (a part) of a payment from (the total)
    let div = document.createElement("div")
    type B = typeof div
  • interface, can be declared repeatedly, but a type cannot

    1
    2
    3
    4
    5
    6
    7
    8
    interface User {
    name: string;
    age: number;
    }
    interface User {
    sex: string;
    }
    /* { name: string; age: number; sex: string } */

features

  • satisfies

is used to ensure that an expression meets certain constraints without altering the type of the expression.

  • circular references

    1
    2
    interface LocalRes<T> { [x: string]: T }
    type LocalResPackage = LocalRes<LocalResPackage | string>
  • new describe the shape of a constructor

  • abstract to mark a class with abstract, means that the class is only meant to be extended from, can’t be used directly

    1
    2
    3
    4
    5
    6
    7
    8
    abstract class Shape {
    abstract getArea(): number;
    }

    new Shape(); // error Cannot create an instance of an abstract class.

    class Square extends Shape {}
    new Square(3) // right
  • extends conditional types
    SomeType extends OtherType ? TrueType : FalseType;

    1
    2
    3
    // if Dog extends from Animal, then Example = number, otherwise Example = string
    type Example = Dog extends Animal ? number : string;

  • infer
    can be used to unpack type, to get the item’s type of an array, the return value’s type or the parameter’s type of a function

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // if T is a function ? the return value of the function : any
    type Unpack<T> = T extends (...args: any[]) => infer R ? R : any;

    // if T is an Array ? R : T
    type Unpack<T> = T extends Array<infer R> ? R : T

    // the item type of array
    typeof UserChosen[number]

    // if R is Reducer ? Reducer's first parameter S : never
    type ReducerState<R extends Reducer<any, any>> = R extends Reducer<infer S, any> ? S : never;
    type Reducer<S, A> = (prevState: S, action: A) => S;

    // get the parameter type of a constructor
    abstract class AbstractClass {
    constructor(a: string, b: number) { }
    }
    export type InferAbstract<T> = T extends abstract new (...args: infer Args) => infer _ ? Args : never; // new a function which return a value, infer the parameter as Args, mark the value as abstract class, if T is a abstract class ? Args : never
    type Result = InferAbstract<typeof AbstractClass> // [a: string, b: number]

  • decorators, use the form @expression, where the expression must infer to a function.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     function first() {
    console.log("first(): factory evaluated");
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("first(): called");
    };
    }

    function second() {
    console.log("second(): factory evaluated");
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("second(): called");
    };
    }

    class ExampleClass {
    @first()
    @second()
    method() {}
    }
  • Record

    1
    2
    3
    4
    5
    6
     type AnyObject = Record<string, any>
    type Coord = Record<"x" | "y", number>
    type Coord = {
    x: number,
    y: number,e
    }
  • Readonly Partial Required: all the properties are Readonly or selectable or required

    1
    2
    3
    4
    5
    type Coord = Partial<Record<"x" | "y", number>>
    type Coord = {
    x?: number,
    y?: number,
    }
  • Pick omit: to pick or omit some properties from a given type

    1
    2
    3
    type Coord = Record<"x" | "y", number>
    type CoordX = Pick<Coord, "x">
    type CoordX = Omit<Coord, "x">
  • ReturnType Parameters, the former is used for getting the output of a function, while the later is used for a function’s input

    1
    2
    let timer: ReturnType<typeof setTimeout>
    Parameters<(props: Event) => void>
  • typeof

    1
    2
    const zed: Hero = { name: "影流之主", skill: "影子" };
    type LOL = typeof zed; // type LOL = Hero
  • keyof collecting key

    1
    2
    3
    4
    interface Person { name: string; age: number;  location: string; }
    type K1 = keyof Person; // "name" | "age" | "location"
    type K2 = keyof Person[]; // number | "length" | "push"|"concat" | ...
    type K3 = keyof { [x: string]: Person }; // string | number
  • enums

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    enum Themes {
    default = "_default",
    thinkuem = "_thinkuem",
    safeuem = "_safeuem",
    }
    // collect all the values of an enum, but the types of all the values will be inferred as string, even some values are number or string
    type ThemesVal = `${Themes}`
    // generate a interface
    type ThemesVal = Record<Themes, string>
    // collect all the keys of a enum
    type ThemesVal = keyof typeof Themes
  • template literal type,a set of every possible string literal that could be represented by each union member

    1
    2
    3
    type a = 1 | 2 | 3
    type b = 4 | 5 | 6
    type ab = `${a}_${b}` // "1_4" | "1_5" | "1_6" | "2_4" | "2_5" | "2_6" | "3_4" | "3_5" | "3_6"
  • template literal has four convenient features Uppercase Lowercase Capitalize Uncapitalize

    1
    2
    3
    type PropEventSource<Type> = {
    on(eventName: `${string & keyof Type}Changed`, callback: (newValue: any) => void): void;
    };
  • type assertion, as or <> syntax

    1
    2
    3
    4
    5
    let value: unknown = "Foo";
    let len: number = (value as string).length;
    let len: number = (<string>value).length;
    // response is changed in the interceptor, so the first type parameter <unknown> indicates that the response type is not known or specified.
    client.post<unknown, EquipmentUptime>(api.insert, data);
  • is

    1
    2
    3
    export function foo(arg: string): arg is MyType {
    return ...
    }
  • in

    1
    console.log('model' in car);  // boolean
  • this

    1
    2
    3
    4
    5
    6
    7
     class Box<T> {
    value?: T;

    hasValue(): this is { value: T } {
    return this.value !== undefined;
    }
    }
  • as const
    to infer in an explicitly way, infer 1 to 1 instead of number, infer false to false instead of boolean

    1
    2
    3
    4
    5
    6
    let x = [0, 1] as const; // let x: [0,1]
    // another write pattern: let x = <const>[0, 1];
    let x = [0,1] // let x: number[]

    let y = [10, 20] as const; // Type 'readonly [10, 20]'
    let z = { text: "hello" } as const; // Type '{ readonly text: "hello" }'
  • any extends object instead of any when reference to an object

  • void is used when a function’s output is null or undefined, if a fun does not use return keyword then its value is undefined

  • unknown is a type-safe of any type. you can narrow down unknow to any type by type assertion

  • never is used when a function can’t end which means it can’t return a value even an undefined. to express an empty object

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function get<T extends object, K extends keyof T>(o: T, name: K): T[K] {
    return o[name]
    }
    let foo: unknown = "hello";
    let bar: string = foo as string;

    function login(data: LoginForm): void {}

    function login(data: LoginForm): never {
    while (true) {}
    }
    function login(data: LoginForm): never {
    throw new Error("error");
    }

    type EmptyObject = Record<string, never>
  • map types, to generate a type According to a map object, and can be a generics

    1
    2
    3
     type OptionsFlags<Type> = {
    [Property in keyof Type]: boolean;
    };
  • index access for looking up a specific property of a type

    1
    type Age = Person["age"]
  • generics generate a type with a specific params

    1
    2
    3
    4
     function identity<T>(arg: T): T {
    return arg
    }
    let myIdentity: <Type>(arg: Type) => Type = identity
  • implements, to check that whether a class is satisfied the contract specified by one or above interfaces

    1
    2
    3
    4
    5
    6
    7
    8
    9
     interface Runnable {
    run(): void;
    }

    class Job implements Runnable {
    run() {
    console.log("running the scheduled job!");
    }
    }
  • triple slash directives, to introduce other files in the compiling process

    1
    /// <reference path="..." />
  • class modifiers

    • public
    • protected: All the members of the class and its child classes can access them, But not the instance of the class.
    • private: Only the members of the class can access them.

useful tricks

  • reduce method needs to provide the initial type and the return type when the return type is not same with the type of array item

    1
    reduce< [string, Lan], { [k: string]: Lan }>((a, c) => {})
  • refer to React component

    1
    2
    new() => React.Component<any, any>
    typeof React.Component
  • promise type, the argument’s type of resolve is number, while the argument’s type of reject is infer to any

    1
    2
    3
    4
    5
    function test(arg: string): Promise<number> {
    return new Promise<number>((resolve, reject) => {
    arg === "a" ? resolve(1) : reject("1");
    });
    }
  • the resolve type of promise

    1
    type resolve = Awaited<ReturnType<typeof drawSlogan>>
  • to override a property of a interface extended

    1
    2
    3
    interface ColorPickerProps extends Omit<BasicProps<string>, "change"> {
    change: import("react-color").ColorChangeHandler
    }
  • use map and literal to create new property names

    1
    2
    3
    type Getters<Type> = {
    [Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
    };
  • to extend html attributes

    1
    2
    3
    4
    5
    declare module "react" {
    interface DOMAttributes<T> {
    name?: string
    }
    }

React

life cycle

  • Mount Phase

    • initialize

      • class: constructor
      • fun: inside the function
    • before render

      • class: getDerivedStateFromProps, React will call it right before calling render, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.

      • fun: compare the state and then return the appropriate result within the function

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        function ScrollView({row}) {
        const [isScrollingDown, setIsScrollingDown] = useState(false);
        const [prevRow, setPrevRow] = useState(null);

        if (row !== prevRow) {
        setIsScrollingDown(prevRow !== null && row > prevRow);
        setPrevRow(row);
        }

        return `Scrolling down: ${isScrollingDown}`;
        }
    • rendering

    • after render

      • class: componentDidMount
      • fun: useEffect() with an empty dependency array
  • Update Phase

    • before update

      • class: getDerivedStateFromProps => shouldComponentUpdate

      • fun: useMemo for shallowly comparing

        1
        2
        3
        4
        5
        // MyComponent will only re-render if the value prop changes
        const MyComponent = React.memo(({ value }) => {
        // Component logic
        return <div>{value}</div>;
        });
    • rendering

      • class: getSnapshotBeforeUpdate(prevProps, prevState) it could return a value as the third argument of componentDidUpdate
      • fun: use useRef to get the old and new state
      • class: componentDidUpdate
      • fun: useEffect() with watched data
  • unmount stage

    • class: componentWillUnmount
    • fun: the returned function of useEffect()
  • Error Handling

    • getDerivedStateFromError(error): allow the component to capture errors in its children when they occur during rendering
    • componentDidCatch(error, info): It can log error information and display a fallback UI when an error is thrown by a child component.

view updating

props changing
In React, data flows in a one-way direction, from outside to inside, and from top to bottom. To ensure this simple and predictable data flow, props are kept read-only

state changing:useState(), useReducer(), redux state
the data will not update immediately when we call setState, multiple updates from different components will be merged into a single re-render automatically. it’s called batching update.

  • the process setState
    setState(partialState) => merges partialState into the current state
    => if isBatchingUpdates === true(is react in the batch updating status), then push this update into _pendingStateQueue(the queue of pending updates) , else set isBatchingUpdates = true and re-call the previous step
    => call waper() to iterate _pendingStateQueue to execute update
    => componentWillReceiveProps
    => to merge the states to get the new state
    => componentShouldUpdate, and then determines whether to continue the update process
    => componentWillUpdate
    => render
    => componentDidUpdate

  • even an empty object like this.setState({}) will also trigger a new update cycle

  • the updated data can be achieved in callback

  • setTimeout or native events can step over React asynchronous behavior and achieve a sync response

    1
    2
    3
    4
    5
    6
    7
    8
    9
    componentDidMount(){
    setTimeout(() => {
    this.setState({ number: 3 })
    console.log(this.state.number) // 3
    }, 0)
    }
    componentDidMount() {
    document.body.addEventListener('click', this.changeVal, false)
    }
  • ReactDOM.flushSync() in v18, this method is used when you don’t want to apply automatic batching. updates in this method will be update immediately, and it’s a sync method, since we know that setState is a async method.

1
2
3
4
5
6
7
 import { flushSync } from 'react-dom'; // Note: react-dom, not react

function handleClick() {
flushSync(() => {
setCounter(c => c + 1);
});
}

context, this is shallowly compare
forceUpdate(), only support in class component, and shouldComponentUpdate() will be skipped
dom changing through ref or native dom handlers

components conversation

  • parent to children: props
  • children to parent: callbacks inside the parent props, event bubbling(eventually, events will bubble up to the parent)
  • between brother: find the common parent node, and then through the above methods
  • context, other global state management tools
  • ref
  • importing a module and interacting within module scope

JSX

  • every file should import react, as long as jsx code is contained
    • cause jsx will be translated by babel to React.createElement()
  • a custom component name should begin with a capital letter
    • if the first letter is lowercase, the component will be treated as a native html element. this rule helps to differentiate custom components from standard HTML tags.
  • a component should only return one element
    • virtual dom is a tree structure, only one root is allowed
    • return multiple elements: return an array of elements, <React.Fragment></React.Fragment>(<></> is a syntax sugar)

high order component

  • HOC is a function, which accepts a component and additional arguments, then returns a new component that maintains a similar interface to the original one.
  • HOC’s name begins with with, such as withButton
1
type HOC = (component: React.component, props) => React.component

features

  • It allows for the addition or modification of the props of a component, enabling us to handle complex or dirty business logic without affecting the original component.
  • unifying the data source. such as connect in redux.
  • to renders different components based on data. e.g., rendering after login can help avoid setting up multiple routers.
  • If multiple components have the same dependencies, instead of importing the function into each component separately, we can apply the function to different components using HOC
  • container and component. The former handles logic, while the latter receives props from container and is only responsible for rendering.

Render Props

  • it is a mode, a component with a callback which accepts component’s data and return React elements
  • tips: try not use Render Props with pureComponent, because this callback will return a new value after parent re-render. while pureComponent is shadow compare for reduce the times of children re-render, but with Render Props the result of compare will always be false.
1
2
3
4
5
6
7
<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)}/>

const DataProvider = (props) =>{
return <div>{this.props.render(this.state)}</div>
}
  • render prop is kind of a simply HOC
1
<Mouse render={mouse => <Component {...this.props} mouse={mouse} />}/>

ref

  • ref is a way for accessing dom
  • create ref by React.createRef() or useRef()
  • it is not support to access ref directly on a function component which does not have ref instance.

callback ref

  • differ from React.createRef(), pass a function to components is also allowed
  • a better way is use with useCallback() hook, then it wouldn’t be executed every time after component updated
1
2
3
4
5
6
7
8
const inputRef = (node) => {
// argument is a dom element during mount
// argument is null when unmount
}
const inputRef = useCallback(node => {
console.log(node);
}, []);
<input type="text" ref={inputRef}/>

ref forwarding

  • Ref forwarding lets components opt into exposing any child component’s ref as their own.
  • we can customize the data exposed to the outside using useImperativeHandle().
  • to access child node from a parent component for triggering focus or measuring the size or position of a child dom.
  • it is useful in HOC, like assigning ref to input’s ref in a input component
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function FancyInput(props, ref) {
const inputRef = useRef();
// assign focus to inputRef
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <div><input ref={inputRef}/></div>
}
FancyInput = forwardRef(FancyInput);

// this ref will be the input's ref and can use the focus method
const ref = React.createRef();
<FancyInput ref={ref} />;

hooks

  • each component has an initial list of “memory cells”, when we call a hook like useState(), it reads the current cell or initializes it during the first render.
  • hook is a function which lets you to “hook into” React state and lifecycle features from function components.
  • use pure function as much as possible
  • useState() keeps the local state in a function component, when we update a state variable, react replaces the state instead of merging it like this.state() in class component.

    1
    2
    3
    4
    // fun will only execute once when the component initialize
    const [goodGet, goodSet] = React.useState(() => fun(0))
    // fun will execute every time the component re-render
    const [badGet, badSet] = React.useState(fun(1))
  • useReducer(), store a complicated state, act like a small redux in a component.

  • useRef(), store data, and the value returned remains valid throughout the entire life cycle

  • useEffect() / useLayoutEffect()

    • one is called after the data is updated, the another is called after the DOM is updated.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // empty dependency array
    useEffect(() => {
    // mounted = componentDidMount and componentDidUpdate
    return fun() // unmount = componentWillUnmount
    }, [])
    // specific dependency array
    useEffect(() => {
    // updated
    return fun()// componentWillUnmount and componentWillUpdate
    }, [id])
  • useContext() accept a context object created by createContext() and returns the current context value.. it’s used when components of different nesting levels need to access the same data.

    1
    const value = useContext(MyContext)
    • createContext() accept a default value and returned a context object which has three properties. context will shallowly compare the properties of value to decide whether it should re-render.

      1
      const MyContext = React.createContext(defaultValue)
    • use useContext() with useReducer() allows passing callbacks to children, avoiding the need to pass callbacks layer by layer.
      create a context object for passing dispatch to children, so any child within the component can get the dispatch by useContext.

    • provider: a react component, that allows to consume components to subscribe to context changes.

    • consumer: a react component, that allows components to subscribe to context value

    • displayName: can be modified directly, and is showed in DevTools

    • contextType: a property on a class component can be assigned a Context object

    1
    2
    3
    4
    5
    6
    7
    < MyContext.Provider value = {/* some value */ } />

    <MyContext.Consumer>
    {value => /*you can return a component and assign the value to it*/}
    </MyContext.Consumer>

    MyContext.displayName = 'MyDisplayName'
  • useCallback() / useMemo() accept a function and an array of dependencies

    • the former memorizes the received function, while the latter memorizes the value returned by the received function
    • useCallback() allows to keep same callback after re-render, and is recommend to use as callback ref to get dom node after mound
    1
    2
    3
    4
    const measuredRef = useCallback(node => {
    console.log(node);
    }, []);
    <h1 ref={measuredRef}>Hello, world</h1>
  • custom hook

    • It allows for the sharing of stateful logic, rather than the state itself. Its purpose is to encapsulate reusable logic.
  • useTransition

  • useDeferredValue()

    • Using the staled value allows de-prioritizing the updating of a part of the UI. Deferring the re-rendering of that part serves as a performance optimization to prevent it from blocking the rest of the UI. this effect may similar to debounce
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // in this case, SlowList is running slowly and should update in response to user input. This indicates that SlowList is blocking user input and the rendering of this component.
    // With the help of useDeferredValue, the dependencies are delayed to update, consequently causing SlowList to be delayed.
    function App() {
    const [text, setText] = useState('');
    const deferredText = useDeferredValue(text);
    // the re-call of SlowList depends on the deferredText
    const SlowList = useMemo(function SlowList({ text }) {}, [deferredText]);
    console.log(deferredText, text)
    return (
    <>
    <input value={text} onChange={e => setText(e.target.value)} />
    <SlowList/>
    </>
    );
    }
    • what’s happened?
      • useDeferredValue is realized by useTransition and useEffect under the react hood
      • input 1 => setText executes, text updates to 1 => render(text = 1, deferredText = “”) => the callback of useEffect executes => Update deferredText to match text => render(text = 1, deferredText = “”)

difference between class and hook

  • class component is hard to reconstruct and decompose

  • compilation of class component will generate a lot of auxiliary functions

  • class component require this when passing a function to a component

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // by a arrow-function. when the parent refresh, a new arrow function will be generated and the offspring will refresh, even the received props haven't change
    <button onClick={() => this.handleClick1()}> btn3 </button>

    // by bind. the side effect of this is same with the last one
    <button onClick={this.handleClick1.bind(this)}> btn2 </button>

    // to declare in the constructor, but it cannot pass parameters
    constructor(props) { this.handleClick1 = this.handleClick1.bind(this) }

    // by the static property of class, same with the last one
    handleClick3 = () => { }
  • no this issues in function components, which helps to avoid problems with scope

  • code lesser with function component, but worse maintaining

  • doesn’t need to manage state and life cycle

key

when an array re-rendering, React will check each list item according to the last render. if an inexistent item of the last render is found in the current render, then react will create an new element. if an item of the last render is altered in the current render, then react will delete the old one and create a new one.

  • The index of an item in an array is Lack of Stability, since add, remove, and reorder are frequent operations.

shouldComponentUpdate VS pureComponent

  • the former is in the life cycle of class components, by return a boolean to decide whether to continue update
  • the later can shallowly compare props and states
  • both of them are helpful for avoiding needless re-render.

fiber

stack Reconciliation(differ process)

  • current tree and WIP(working-in-progress) tree react creates WIP tree(working-in-progress) based on current tree which marks the current view. WIP tree is used to perform updates under the hood.
  • React uses DFS(depth first search) to traverse two virtual dom tree, comparing the new and old props, as well as the current tree with the WIP tree, to identify changed nodes and determine which needs to be updated, this process called Reconciliation. The result of Reconciliation will be applied to the current tree.
  • during Reconciliation, the traversal process can cause performance problems as the DOM tree grows larger, and it cannot be stopped once it’s started. React occupies the resources of the browser until it’s finished, so when handling complex views, it can lead to blocking in the main thread, resulting in unresponsiveness in the page.

what is fiber

  • React saves references to the return(parent), sibling, and child nodes, the data structure is similar to linked list. This more powerful representation of the virtual DOM tree is known as Fiber.
  • Fiber‘s references enable React to memorize the work in progress, and then pause and resume the Reconciliation process.

Fiber structure

  • React slices the process of Reconciliation into small pieces, this is called time slicing. It gives control to the browser to respond to user interactions and then takes back control to continue it’s work slice by slice(react => browser => react => browser ….). this mechanism of handling control is known as Fiber structure. in the Fiber structure, react gives control to browser to respond to user interactions at the right time, creating the perception of faster responsiveness.

fiber Reconciliation(differ process)

  • In the Fiber structure, during Reconciliation, React saves the nodes that has effect(require changes) in the fiber‘s’ property effectTag, and uses nextEffect to link those nodes in linked list structure.
  • After Reconciliation, React steps into the commit phase. During the commit phase, React applies all the effects to WIP tree in one go, while this process cannot be interrupted.

Concurrent Mode

  • with time slicing, react ables to bounce back and forth between multiple tasks, and pause one task while other more urgent task are seen to. once the more urgent one are done, then jump back the less urgent one, bringing with the updated information from the more urgent one.
  • useTransition, this hook is used to wraps a less urgent update.

DFS(depth first search) 深度优先遍历 a search algorithm used for traversing or searching tree or graph data structures.
linked list 链表, a common data structure used to store a collection of elements, where each element is connected to the next one through a node. Each node contains two parts: data and a reference (or pointer) to the next node.

controlled and uncontrolled

  • they are two different approaches to managing form inputs and components

  • controlled is driven by props or state, following one-direction data flow. uncontrolled is driven by dom itself, and retrieves value by ref.

    1
    2
    3
    4
    5
     // uncontrolled
    <input type="text" ref={inputRef} />

    // controlled
    <input value={state.value} onChange={handleChange} />
  • controlled components offer more control and are preferred in most cases, while uncontrolled components can be useful in certain scenarios. In many cases, a mix of controlled and uncontrolled components may be the best approach

    • for form inputs where real-time validation, formatting or data manipulation is not required. uncontrolled components can minimize state management overhead and improve performance.
    • when integrating with external libraries like date pickers or rich text editors, using controlled components allows these libraries to manage their own state

Built-in React Components

Fragment

1
2
3
4
5
6
7
// explicitly writing form  is required when pass key to a `Fragment`
<Fragment key={yourKey}>...</Fragment>.

<>
<OneChild />
<AnotherChild />
</>

Profiler

1
2
3
4
// measure rendering performance of a React tree. but adds some additional overhead, so it is disabled in the production build by default
<Profiler id="Sidebar" onRender={(id, phase, actualDuration, baseDuration, startTime, commitTime) => ({})}>
<Sidebar />
</Profiler>

Suspense + dynamic import + lazy loading

  • lazy loading should always be declared at the top level of modules.
  • with webpack code splitting, each module loaded by import() will be split to separate files.
  • Data fetching, Lazy-loading by lazy
  • fallback will be used if any of the children suspend while waiting for data. nesting multiple Suspense components can create a loading sequence for revealing a component as it loads.
1
2
3
4
5
6
7
8
9
10
11
const MarkdownPreview = lazy(() => delayForDemo(import('./MarkdownPreview.js')));

export default function MarkdownEditor() {
return (
<>
<Suspense fallback={<Loading />}>
<h2>Preview</h2>
<MarkdownPreview/>
</Suspense>
</>
);

Portal

to render a component within other nodes. it can be used for dialog or message components

1
ReactDOM.createPortal(child,container)

StrictMode

  • enables additional checks and warnings for its descendants. it‘s used in the DEV mode only.
  • under StrictMode, setState updaters run twice.

redux

concept

A(change) –> B(send an action to store) –> C(store dispatch the action to a reducer) –> D(the reducer return a new state to store) –> E(state changed) –> G(execute listeners)

  • store a container created by createStore(), where data is changed and saved.
  • state data, an object
  • action An object include a required type property and other business related properties. The different values of the type determine the number of actions that a store can perform.
  • reducer a function, receive an action ,and return a new state. it automatically triggered by store.dispatch()
    Reducers should be as the parameter pass to the createStore() when initialize the store.

Redux Methods

  • createStore(), reducers as parameter is required, an initial state, the third argument is enhancer like middleware

  • provider allow the redux hooks and connect to components

  • store.getState() get the present state

  • store.dispatch() sending an action to reducer

  • store.replaceReducer() Replaces the reducer currently used by the store.

  • combineReducers() organize your reducers to manage their own slices of state. accept an object whose values are different reducing functions and turn it into a single reducing function you can pass to createStore.

    1
    combineReducers({ todo: myTodoReducer, counter: myCounterReducer })
  • bindActionCreators is wrap action into dispatch

    1
    2
    3
    type actionCreator = (payLoad: any) => {...{type: string }, ...payload}
    type dispatchAction = (payLoad: any) => dispatch(actionCreator(payLoad))
    (actionCreators: actionCreator | actionCreator[], dispatch: Function) => dispatchAction | dispatchAction[]
  • store.subscribe() receives an listeners function and return a function in which you can disengage the listener.

  • connect return a function which like a HOC accepts a component and wrap it to subscribe store’s updates and able to dispatch actions.

    • how it works? with Provider component provided by redux we can wrap a component and get the component’s context and children, then any nested components can access store with Hooks and connect
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    (mapStateToProps: mapStateToProps, mapDispatchToProps: mapDispatchToProps, mergeProps: mergeProps, options) => ((component) => connectedComponent)

    // combine state and component's props and pass to the component, if this argument is passed to the components, then mapStateToProps will be executed as every time the store and own-props are updated.
    type mapStateToProps = (state: object, ownProps?: object) => stateProps
    // wrap actions as a part of props pass to the component
    type mapDispatchToProps = (dispatch: Function, props?: object) => dispatchProps
    // return an object as a result of mapStateToProps and mapDispatchToProps
    type mergeProps = (stateProps, dispatchProps, ownProps) => mapStateToProps & mapDispatchToProps

    type stateProps = any

    interface dispatchProps {
    dispatchPlainObject: () => dispatch(action)
    }
  • middleware is used to inject a third-party in the redux processing, allow you to do sth (like logging, crash reporting, talking to an asynchronous API, routing, and more) after an action is dispatched and before a reducer update the state.

    1
    (getState: store.getState, action, next: (action) => void) => void
    • action => middleware => reducer => state
    • next is used to pass action to the next middleware. you can pass the current action to continue the middleware chain or pass another action.
    • middleware chin: an action can have an array of middleware that is linked by next.
      • tips: if next is used in a middleware for passing another action, then that action will be directly to the reducer and will not go through its own middleware chain
    • use dispatch inside a middleware will stop current middleware chain and start another middleware chin.
    • it is not different, either use dispatch, or next, or do nothing to stop middleware chain.

redux-toolkit

  • core api
    • createAction,createReducer
    • createSlice:including createAction and createReducer
    • PayloadAction:ts 泛型,用以声明 action.payload

createAsyncThunk

accepts a Redux action type string and a callback function that should return a promise. return a standard Redux thunk action creator

V19

  • useActionState

  • useFormStatus

    • only returns status information for a parent <form>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      function Submit() {
      const { pending } = useFormStatus()
      return (
      <button type="submit" disabled={pending}> {pending ? "Submitting..." : "Submit"} </button>
      )
      }
      function Username() {
      const { pending, data } = useFormStatus()
      console.log("Username", data?.getAll("username"))
      return <input type="text" name="username" disabled={pending} />
      }
      function Form() {
      const { pending } = useFormStatus()
      console.log("Form", pending) // always false
      return (
      <form action={submitForm}>
      <Username />
      <Submit />
      </form>
      )
      }
  • useOptimistic

Vue

2x: Object.defineProperty
3x: Proxy, setter and getter
defineProps is used with destructuring, applying a compile-time transform

reactive-vs-ref

reactive for complex object and ref for simple primitives

  • reactive
    • only works for object, not js primitives(string, number…), but can work for a group of primitives
    • reactive CANNOT be reassigned
  • ref
    • calls reactive under the hood
    • works for primitives

lifecycle

  • (replaced by setup in vue3) before / after component instance create
  • before / after dom mount
  • before / after state update
  • before component instance destroy > onBeforeUnmount > component instance destroyed > unmounted
  • errorCaptured
  • onActivated && deActivated, when the component is include in keep-alive

differ between 2.0 and 3.0

how reactivity works

vue.esm-bundler-vs-vue.runtime.esm-bundler.js

vue.esm-bundler

  • contains both the runtime(the core features of vue) and the template compiler(compile templates into render functions).
  • often used in scenarios where the templates are not pre-compiled during the build phase and user want Vue to handle the compilation of templates on the fly. like ssr, CDN

vue.runtime.esm-bundler.js

  • contains only the runtime
  • designed for bundled environments where templates are pre-compiled during the build process using tools like Vite

vue-css-features

  • scoped css, achieved by using PostCSS, only apply to the current component

    • :deep(), to affect child components

    • :slotted, to affect contents rendered by <slot/>, contents in slot are owned by the parent component

    • use class rather than tag selector

      • tag selector with scoped will generate HTML for tags to include data-v-* attribute.
      • attribute selectors(like p[data-v-123abc]) are more computationally expensive because Browsers need to inspect each element’s attributes
      1
      2
      3
      4
      5
      6
      7
      8
      <template>
      <p>Hello, World!</p>
      // after transform
      <p data-v-123abc>Hello, World!</p>
      </template>
      <style scoped>
      p { color: red; }
      </style>
    • :global, to apply one rule globally

  • css modules, compiled as CSS Modules

  • v-bind, color: v-bind('theme.color'); linking CSS values to dynamic component state

computed

  • similar to useMemo(), the function will re-evaluate when some of its reactive dependencies have changed

  • a computed variable can be re-written by a setter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const fullName = computed({
    // getter
    get() {
    return firstName.value + ' ' + lastName.value
    },
    // setter
    set(newValue) {
    // Note: we are using destructuring assignment syntax here.
    [firstName.value, lastName.value] = newValue.split(' ')
    }
    })

watch

similar to useEffect(() => {}, [data]), listen to data change

nextTick()

a callback after a state change to wait for the DOM updates to complete.

comparison-api

provides logic composition capabilities, like React Hooks
composition faq
composables

compare to options api

  • less organized, but more flexible and logic reuse ability. with Options API, everything has its place based on the option it falls under, but poses serious limitations when a single component’s logic complexity grows
  • better type inference
  • more efficient and minification-friendly.
    • with options api, reference a property in the template {{ message }}—Vue accesses it via an instance proxy (i.e., through this.message). This proxy is responsible for managing reactivity and ensuring that updates trigger re-renders.
    • with comparison-api, the template is compiled as a function inlined in the same scope of the <script setup> code, so it access variables declared inside <script setup> without an instance proxy in between
    • this difference leads more efficient and better minification because all the variable names can be safely shortened.

compare to react hooks

  • Invokes setup() code only once, while React Hooks are invoked repeatedly every time a component updates
  • in react, functions are re-created on every render. function reference changes cause unnecessary child updates by default, and require explicit useCallback as an optimization. while vue reactivity system intelligently tracks dependencies – It only re-renders components that actually need updates.
  • no need to manually declare dependencies. Vue's runtime reactivity system automatically collects reactive dependencies used in computed properties and watchers

Vue Reactivity System, the foundation of how Vue tracks and updates data changes
Vue’s runtime reactivity system, refers specifically to the runtime behavior of Vue’s reactivity system, focuses on how Vue handles reactivity while the application is running, e.g. Detecting changes to reactive data, Managing the dependency graph

vue-components

register

A Vue component *needs to be registered so that Vue knows where to locate its implementation when it is encountered in a template.

  • Global, app.component()
  • local, through import in <script setup>, components option in non-<script setup>
  • can be load by defineAsyncComponent, similar to react import + lazy + suspense

communication

  • parent -> child, pass props and functions with v-bind and v-on
    • in child
      • defineModel, exclusively for v-model
      • defineProps(), get props
      • slot, get the children or a named child(named slots)
  • child -> parent
    • emits, like callbacks in props
    • v-module, creates a two-way data binding
  • despite layer
    • ref
    • provide / inject, similar to context in react
    • global state management
    • event bubble

bundlers

tree shaking

  • after checking import and export, tree shaking will remove the modules which is imported but doesn’t be used. from this point, what method do we use to import file is critical, and we can know tree shaking only supports ES module.

  • to import the specific modules instead of the whole library

    1
    2
    3
    4
    5
    6
    // Import everything (NOT TREE-SHAKABLE)
    import _ from 'lodash';

    // CAN BE TREE SHAKEN
    import { debounce } from 'lodash';
    import debounce from 'lodash/lib/debounce';
  • side effect refer to a function will effect the variables or scope outside the function

  • however, the border between used and not used is not clean, some modules are started to work when it is imported. since object is a reference type, which means that if a function’s arguments are object, then any changes on we made on it will be easily effect the data outside the function. so there has a property sideEffects in the package.json to tell bundler whether should be tree shaking.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
      // all the modules have/haven't side effects, and then can't/can be tree shaking
    {
    "sideEffects": true/false
    }
    // give webpack an array with side effects
    {
    "sideEffects": [
    "./src/file1.js",
    "./src/file2.js"
    ]
    }

optimization

  • tree shaking
  • compress code by some plugins, like terser, css minimize
  • code splitting, use import() to split code into multiple chunk and lazy-loading them. import() can cooperate with suspense feature in react.

vite

based on rollup, and provides a development server or HMR out of the box

turpopack

currently support nextjs only

webpack

module building, browser compatibility, code compressing, code splitting, lazy loading

building process

  1. generate a config from the webpack.config.js and the command line.
  2. use compiler module to create a compilation instance which has access to all modules and their dependencies. execute the compiler.run() to start working. compiler and compilation are the heart of webpack, both of them extend from tapable.
  3. analysis process is started from the entry file. use acron parser to generates AST(Abstract Syntax Tree / 抽象语法树) for the entry file.
  4. traverse through entire AST to create a dependency graph. at this stage, if webpack find any matched rules for loaders, the loaders will convert those codes(e.g. css files) into js, and then Parser parses them into AST.
  5. this process will be repeatedly executed for the each dependency, to recursive traverse.
  6. bundle all the files.
  7. in the above process
    1. webpack compiles the different types of file to js file by loaders
      • loader is responsible for file converting. every file type can have more than one loader, those loaders will be invoked with method chaining in order. so the output of a loader will be the arguments of the next loader.
    2. compiler will throw some hooks when webpack starts/stops, emit assets, watch mode etc. those events can be listened by plugin and allows plugin to intervene the result of output.
      • plugin can extends features. listen events sended by compiler allows a plugin to effect the result.

boost build time

  • webpack cache strategies
  • get precise time by speed-measure-webpack-plugin, like how long the engine spends in each plugin / loader
  • save time from each plugin or loader
    • ts-loader, use happyPackMode or transpileOnly to turn off type checking. the former will implicitly sets transpileOnly: true and will not reports syntactic errors. it is usually used with happypack.
    • fork-ts-checker-webpack-plugin works for ts type checking in a separate process which runs in parallel with webpack

module federation

to expose some of our components to the outside, and therefor which can be used in other projects.
this feature is considered as an idea of micro-frontend

HTTP

http: HTTP > TCP > IP > MAC
https: HTTP(application) > SSL/TSL(application) > TCP(transport) > IP(internet) > MAC(link)
http2: HTTP > HPack and Stream> SSL/TSL > TCP > IP > MAC

  • http vs https

    • http requests are sent in plaintext. HTTPS adds SSL/TLS protocol between TCP and HTTP for encrypting data
    • http uses 80 port as default, while https uses 403 as default
    • friendly to SEO. Search engines like Google prefer HTTPS websites
    • HTTPS is seen as a trust signal which will be indicates by browser
  • three-way handshake is used to establish TCP connection, to ensure that the service and the client both can send and receive data

    • The client sends a SYN packet (SYN = synchronize) to the server, and specifying its initial sequence number. (client can send and server can receive).
    • the server responds with a packet containing both SYN and ACK(Acknowledgement Number) flags. (server can send)
    • the client sends back an ACK packet. (client can receive).
    • what it’s used for
      • prevent historical connection
  • 127.0.0.1, localhost, 0.0.0.0

    • localhost is domain, equal to 127.0.0.1 which is loopback address(回环地址)
  • URL

    • Reserved characters : : / ; ? #
    • encodeURI decodeURI, don’t encode reserved characters
    • encodeURIComponent decodeURIComponent encode reserved characters

CORS

same origin policy, requires same protocol, domain and port

http versions

  • http1.0
  • http1.1
    • connection:keep-alive feature allows for long-lasting connections
    • supports pipeline transport(but it’s not allowed in browsers)
    • It compresses only body, excluding header.
    • the server responds to requests in the order they were received, a delay in one request can cause subsequent requests to be blocked. This issue is known as head-of-line blocking(队头阻塞)
  • http2.0
    • Compress both the header and body. Both the client and the server maintain a dynamic table of header information. Every header field is stored in this table and assigned an index number. For headers with the same name, only the index number will be sent. this called HPACK
    • uses binary format, reducing the step of transforming requests into binary form
    • introduces Stream concept, allowing multiple requests and responses to be sent and received concurrently within a single TCP connection.
      • multiple streams exist in one tcp connection, and own a stream-id as an identifier. a full request (request and correspondent response) can be split in frames and then sent with the same stream identifier.
      • a stream is initiated when client or server sends bytes.
    • server push. since the server can create stream
    • Head-of-line blocking still exists in the TCP layer. TCP is a byte-oriented protocol, and data is only sent to the application layer when the received bytes are complete. Packet loss triggers TCP to resend the missing packets, which can block all the requests.
  • http3.0

methods

  • post | put | delete contain request body for saving data.
  • get: retrieve data, safe and can be cached
  • post: creating new resources, submitting form data, or sending large amounts of data that cannot be sent in a URL query string.
  • put: updating existing resources

headers

  • Content-Disposition: a response header which is indicating if the content is expected to be displayed inline in the browser,or as an attachment to downloaded and saved locally.

    1
    2
    3
    // inline or attachment
    res.set('Content-Disposition', 'attachment; filename=foo.fdf');
    res.send(_res)

http cache

memory cache, disk cache

  • cache use priority: Service Worker -> Memory Cache -> Disk Cache -> Push Cache
  • memory cache: fast but limited in time; cache is deleted when the browser is closed.
  • disk cache: need I/O operations during read and write

strong cache

  • headers, priority: pragma <= cache-control <= expires
    • Expires, an absolute date which is the expire date of the current resource
    • Cache-Control
      • max-age:5000, Specifies the maximum amount of time in seconds
      • no-store, strong cache is not allowed
      • no-cache, submit a request to the server for validation before using strong cache
      • private
      • public
    • Pragma: no-cache, for compatibility with older HTTP/1.0 caches.

negotiated cache

  • headers
    • Last-Modified / Last-Modified-Since, ETag / If-None-Match
      • Last-Modified set by the server, represents the last modified date of the current resources
      • ETag, set by the server, a hash code represents the current resource
      • Last-Modified-Since / if-none-match sent by the client with the value of Last-Modified / ETag from the previous response. The server compares the values in the request with the current values. If they match, the server responds with 304 Not Modified.

client scenarios

  • press f5 to refresh: sets Cache-Control:max-age:0;, means the cache is invalid
  • ctrl + f5 force to refresh, sets Cache-Control:no-cache

code

  • 1XX- Informational
  • 2XX- Success
  • 3XX - Redirection
    • 301 vs 302, 301 is permanent redirection, while 302 is temporary redirection. For permanent redirects, the browser caches the redirection location, so the cached location is used the next time.
    • 302 process
      • send a request using ajax
      • => server responds with 302, Note that the name for the 302 response is “Found” – what it means is “The content you were looking for exists, but in a different location login.”
      • => browser hears that the stuff it was looking for is actually at login instead. So it sends another request to login through ajax. In other words, it sends another asynchronous request in the background.
      • => server responds with a 200, and tells the contents of login. at the end, we can catch this 200 response and a responseURL of 302
  • 4XX - Client Error, 400 Bad Request, 401 Unauthorized, 404 Not Found
  • 5XX - Server Error, 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable

https optimization

  • reduce the number of request
    • use http cache
    • combine small and light requests, like small images
  • use compress as needed
    • lossless compression, Accept-Encoding: gzip, deflate Content-Encoding: gzip
    • lossy compression, Accept: audio/*; q=0.2, audio/basic, q refer to the number of quality

thread and process

real-time communication

SSE

http long-lasting connection
plain text only
one-way communication, server to client
standard HTTP connection

Websocket

plain text and binary format
bidirectional communication, server to client and client to server
use HTTP handshake, and then upgrade to ws protocol

long-polling

extending the time of timeout, and the server holds the request open until it has new data to send to the client or until a timeout occurs

package mangagers

1
2
3
4
5
npm version patch // v1.0.1
npm version prepatch // v1.0.2-0
npm version minor // v1.1.0
npm version major // v2.0.0
npm publish --access public
  • control the files of a package
    • define the file field in the package.json
    • .npmignore, is same as gitignore
  • rules
    • ^, install the latest minor or patch versions that are >=3.2.0 and <4.0.0

Corepack

  • Corepack is included with all official Node.js releases starting from Node.js 14.19 / 16.9. It’s however opt-in for the duration of the experimental stage, so you’ll need to run corepack enable before it’s active.
  • packageManager in package.json is used to set a certain package manager with version
  • environment issue: corepack will going to install yarn or pnpm after enabled, but corepack doesn’t share the same mirror source configuration as npm

peer dependencies

peer dependencies are a special type of dependency that are expected to be provided by the consumer of the package. A package specifies its peer dependencies when it requires another package but doesn’t want to install it itself, instead relying on the host application to provide it.
a package like react-router-dom might have a peer dependency on react.

ghost dependencies

Ghost dependencies refer to dependencies that are listed in a project’s package.json or node_modules, but are not actually used in the project.

reasons

  • Accidental Installation,
  • if a direct dependency is removed, but its sub-dependencies can be remained.
  • a package is removed but not tracked by the package manager

resolve

  • Each manager has its own way of cleaning ghost dependencies. commands like npm prune or yarn autoclean, or settings yarn’s packageExtensions
  • tools like depcheck

Hoisting

spreads common packages as far up the tree as possible so other packages can share them as well

yarn

  • Yarn PnP storing everything in a global directory, a .pnp.cjs that contains symlinks remaind in the node_modules folder.
  • offline mirror
    • setting enableGlobalCache to false
    • save the package cache into a folder local to the project that can then be added to Git.
  • zero-install can be achived through offline mirror and Yarn PnP

pnpm

Vscode

short key function
Ctrl+Shift+[ 折叠
Ctrl+Shift+] 展开
ctrl+d 选中单词
alt+shift+鼠标点选 多个位置
alt +鼠标点选 选中
alt + z word break
ctrl + shift + L pitch on the same words simultaneously
ctrl + shift + p show command menu
Setting json C:\Users\tong\AppData\Roaming\Code\User
Line break “files.eol”: “\n”
ctrl + g go to a specif line