This article was meant to be a summary of key front-end knowledge, more and more. To give up… A series of sudden death

A, JS

1. Key knowledge of JS

This piece of knowledge is very important, so we summarized it previously. So here is no longer repeated to sort out (with the knowledge of the rise of the feeling of writing before more embarrassing… But at least it’s not a problem.)

1.1 Scopes and closures

As the key knowledge of JS, it has been taken out and sorted out earlier, but the knowledge of closure combining call stack and scope can be understood more deeply

Dig deep into the closure

1.2 Prototype and inheritance

As the key knowledge in JS, the earlier time has been taken out to sort out

Prototype and inheritance comprehensive analysis

1.3 Asynchronous and Event-lop

As the key knowledge in JS, the earlier time has been taken out to sort out

Take down asynchronous together

Event-Loop

2. Js execution mechanism

2.1 Execution context and call stack

Execution context

Execution context: The execution environment of the code

It is divided into three categories

  • Global context
  • Function context
  • Eval Execution context (Eval is a spoofing type)
let name='gxb'
function demo(){}
Copy the code

Let’s start with a simple PIECE of JS code

When this code executes, it must first create a global execution context

In this global execution context, the global object, this, is required. And this points to the global object

Note that the name variable is undefind

Why?

Because every execution context has two phases: the creation phase and the execution phase

What you do in the creation phase is as follows:

  • Creating a global object
  • Create this and make it point globally
  • Allocate storage space for variables and functions
  • The default variable is undefined; Put the function declaration into memory
  • Create scope chains

Value assignment during execution (do you understand how variable promotion works now)

The function execution context is basically the same as the global execution context, with the following differences

  1. The timing of the first creation
  2. Again, the global context is created only once
  3. Finally, the global context starts with the global object and this and this refers to the global object; The function context creates arguments, and this will need to be determined at run time

The call stack

So what does the call stack do?

At JS runtime, a function is taken out of the heap and integrated into the function context. Where does this function context go? That’s a problem

Because we know that when a function is done, the addresses that are occupied by the data in it are going to be released, and sometimes functions are going to have to have a set of functions and the order in which they’re executed and the order in which they’re freed are going to be taken into account.

The idea is to use a stack data structure to place these function contexts, called the call stack

Recursion and closure issues are well understood here (execution context, call stack, scope)

2.2 Garbage collection mechanism

We all know that data storage in JS works like this: simple data types go into stack memory, and reference data types go into heap memory

We should be familiar with the two data structures of stack.

The access mechanism is also different for simple types and reference types. Simple types fetch data directly from the stack memory, while reference types fetch data only from the stack memory address

Let’s get down to business: garbage collection

In JS, the engine checks a variable every once in a while, and if it finds that the variable is no longer needed, it frees the memory address occupied by the variable

Js garbage collection mechanism, in fact, JS garbage collection mechanism principle is relatively simple

Reference counting method

This reference can be understood in JS as a one-to-one relationship between variables and real memory

Such as:

const gxb={}
Copy the code

To make reference counting easier to understand, do this (ignore stack memory)

That is, this address is referenced by the variable GXB, counting 1.

When do I reclaim this memory? When the reference count is zero

As shown in the figure above, the variable GXB refers to another address. If the old address is not referenced, the count is 0. The garbage collector then releases it

Reference counting has been phased out because it has a major drawback. Look at this classic chestnut

function demo{
    const a={}
    const b={}
    a.b=b
    b.a=a
}
Copy the code

After a function is added to the call stack, the data in its scope is destroyed as soon as it completes execution.

But with reference counting, that’s what happens

These two addresses are not referenced by external variables, that is, the external world can not access these two addresses, so it is useless to keep them real.

In order to address this vulnerability, tag elimination was introduced

Mark clearance

Markup cleanup is also easy to understand. The principle is that memory that you can access from the outside world doesn’t need to be reclaimed, and memory that you can’t access needs to be destroyed

For example, the above two references to each other, the outside world has no variables to reference them, so it can be directly recycled

3.ES6

3.1 modular

Review the knowledge

Its general development path is as follows: commonJS->AMD->CMD/UMD->ES6 Moudle

The focus here is on commonJS and ES6

3.1.1 Take a look at commonJS first

The primary user of commonJS is Node, where each file is a module with its own scope. That is, variables, objects, and functions defined in a file are private to the file and cannot be accessed externally

In the commonJS specification, inside each module there is a variable moudle, which represents the current module. The moudle is an object.

Let’s start by printing this moudel object

Briefly, the Module object provides a module constructor inside Node that instantiates a Module for each module created. Or all modules are instances of Module

Some of its properties explained

export

The Module object has an exports property, which is an external interface. Loading the module with require in another file is actually loading the value of this property

Such as:

//main.js
const { name, getValue } = require('./test')

console.log(name)
console.log(getValue(1))


//test.js
let name = 'gxb'
function getValue(value) {
    return value + 1
}
module.exports = {
    name,
    getValue
}

Copy the code

For further simplicity, look at the exports variable

The above code could also be written like this

let name = 'gxb'

function getValue(value) {
    return value + 1
}

exports.name = name
exports.getValue = getValue
Copy the code

How can I put it?

Exports: moudle exports: moudle exports: Moudle exports: Moudle exports: Moudle exports: Moudle exports

let exports=moudle.exports
Copy the code

There’s one thing I’m sure you’ve noticed.

It cannot be assigned a value directly, as it was at the beginning of moudle.exports

exports={
     name,
    getValue
}
Copy the code

Why?

Exports =moudle.exports =moudle.exports =moudle.exports =moudle.exports =moudle.exports =moudle.exports This moudle.exports is used for loading. You changed the exports variable and its previous one became obsolete

The import

CommonJS uses the require method to load modules. This method argument is the module’s path, with the default suffix.js

Its basic function is to read and execute a JS file and then return the exports object of that module.

Remember that when you load a module using require, the execution of the module code only takes place the first time it is loaded, and everything else is fetched directly from the cache

All caches are stored in require.cache, deleted

// Delete the cache for the specified module
delete require.cache[moduleName];// The module name is the absolute path to the module

// Delete the cache for all modules
Object.keys(require.cache).forEach(function(key) {
  delete require.cache[key];
})
Copy the code
Pay attention to the loading mechanism

The loading mechanism of the CommonJS module is to import a copy of the exported value. That is, what we get outside is only a copy of the data, and the modification of the data outside does not affect the internal module

The characteristics of the CommonJS

  • All modules run only in the module scope and do not pollute the global scope
  • Modules can be loaded multiple times, but the code is only run once on the first load and then the results are cached, and then loaded directly from the cache
  • Modules are loaded in the order in which they appear in the code

3.1.2 Let’s look at the ES6 Moudle

This is a little bit easier

use

//b.js

let name = 'gxb'

function demo() {
    console.log(111);
}
export { name, demo }


//a.js
import { name, demo } from './a'
demo()
Copy the code

Or export by default, so we can specify variable names at load time

//b.js
export default function() {
    console.log(111)}//a.js
import demo from './a'
demo()
Copy the code

3.1.3 Differences between the two

  • The CommonJS module prints a copy of a value, the ES6 module prints a reference to a value (important)

  • The CommonJS module is run time loaded, and the ES6 module is compile time output interface.

3.2 var, let, const

I’ve written it before, but I’m going to skip it because it’s a little bit easier

3.3 symbol set weakSet map weakMap

symbol

Symbol was introduced to prevent duplication of object attribute names

The symbol value is generated by the symbol function and represents a unique value

const s1 = Symbol(a)const s2 = Symbol('s2')// The parameter is a description of the symbol value
const s3 = Symbol.for('s3')
Copy the code

Symbol.for() and Symbol () are used to generate a Symbol value. The difference is that using symbol.for () registers the Symbol globally. Const s3 = symbol. for(‘s3’) the code will hash the Symbol to see if there is a Symbol with s3 added to it. If there is no Symbol with s3, it will use it

This means that s3 and S4 are the same Symbol

const s3 = Symbol.for('s3')
const s4 = Symbol.for('s3')
Copy the code

Symbol () will be created and will not be registered

Note that when used as an object property, this property is not for… In, for… Of iterating over the (Object) keys, Object) getOwnPropertyNames also no etc.)

But it is not the private property of the Object by Object. GetOwnPropertySymbols get (Peflect. May ownKeys)

Because of the symbol attribute, which is not private but cannot be traversed casually, you can do a lot of things with it

Symbol. KeyFor is used to obtain the description (or key value) of a registered Symbol

const s4 = Symbol.for('s3')
console.log(Symbol.keyFor(s4))//s3
Copy the code

The set with weakset

set

It’s the set in our data structure, the members are not allowed to duplicate

const s1=new Set(a)const s2=new Set([1.2.3])// Its constructor can take arguments from other data structures that have iterator interfaces


// Related methods
//add
//delete
//has
//clear
Copy the code

chestnuts

const obj = {
    *[Symbol.iterator]() {
        yield 1
        yield 2
        yield 3}}const s2 =new  Set(obj)//Set { 1, 2, 3 }
Copy the code

Common application: array deduplication

function dedupe(arr){
   return [...new Set(arr)]
}
Copy the code

It is worth noting that NaN is equal to NaN in set

Some operations on set are identical to those on sets

weakset
const ws = new WeakSet(a)// Related methods
//add
//delete
//has
Copy the code

Two points different from set

  1. Member types can only be objects
  2. A weak reference

A simple weak quote, here small white I say it in vernacular. Normally, a piece of memory is not garbage collected as long as it is accessible by variables. But Weakset is different. It can’t be the master on its own. If there are no references to its internal object elements by other external variables, its contents will still be garbage collected

And because it is a weak reference, there is no guarantee that its internal elements will still exist. Therefore, it cannot be traversed

The map and weakmap

map

A map is an improvement over the traditional object structure, where the key can only be a string and the map can be of any type

const m=new Map(a)const obj={}
m.set(obj,{})
m.get(obj)/ / {}Other related operations// has
// delete
Copy the code

Or pass the constructor an argument when it is new

const m1=new Map([[1.1], [2.2]])// Make sure that each entry is two-element, as one is the key and one is the value

const obj = {
    *[Symbol.iterator]() {
        yield [1.1]
        yield [2.2]}}const m2 = new Map(obj)
Copy the code

The map structure implements the iterator interface and uses the iterator generators in map.entries().

With the iterator interface, we can use for… Of, using the extension operator (these things are already constructors)

Prove chestnuts

function* demo() {
    yield 1
    yield 2
    yield 3
}

for (const iterator of demo()) {
    console.log(iterator);/ / 1, 2, 3
}
Copy the code
weakmap
const wm=new WeakMap(a)// set...
// get...
Copy the code

The difference with Map is basically the same as weakSet

  1. Keys must be objects
  2. Key references are weakly typed

3.4 the proxy and reflect

proxy

I don’t believe there are people who don’t know about data hijacking in Vue… Probably more familiar than I am

Its function is similar to AOP

const obj=new Proxy({}, {get(target,name){
        return 'gxb'}})console.log(obj.name)//gxb
Copy the code

Let’s list some common interception methods

  • get
  • set
  • Apply: Intercepts instances as function calls
  • Construct: Intercepts the instance as a constructor call

Note that the proxy instance obj is not identical to the target object even if the proxy operation does nothing.

reflect

The purpose of this is to place Object methods that are clearly internal to the language (such as Object.defineProperty) on the Reflect Object. I’m using very little here at present, and I’ll add it later

3.5 the iterator and for… of…

Iterator provides a unified access mechanism for different data structures (arrays, sets, maps).

An iterator is essentially a pointer object that is moved by the next method and returns a node object. The node object is the information about the node to which it points, and the node object has two properties, value and done, one to hold the node value and the other to point to the end

Write an iterator generator

function demo() {
    return {
        next() {
            return { value: 0.done: false}}}}Copy the code

Generator A ready-made iterator generator function

function* demo() {
    yield 1
    yield 2
    yield 3
}
console.log(demo().next());//{ value: 1, done: false }
Copy the code

As we all know, objects have no Iterator interface. The little white has written chestnut on it

Add to it, and it can use for… Of is iterated. Why? There was an explanation, for… Of is actually an iterator to this interface

const obj = {
    *[Symbol.iterator]() {
        yield 1
        yield 2
        yield 3}}Copy the code

In addition to the for… Of is the iterator interface, and there are other things that are iterator interfaces

Such as assigning values to arrays and set structures, and extending operators to arrays

chestnuts

const arr = []
arr[Symbol.iterator] = function* () {

    yield 1
    yield 2
    yield 3

}
console.log(... arr)/ / 1, 2, 3
Copy the code

Start reading ruan yifeng teacher’s book, did not specify the scope of the extension operator. It makes me a bit annoyed because objects can also use extension operators. But objects do not implement iterator interfaces

const obj = {
    *[Symbol.iterator]() {
        yield 1
        yield 2
        yield 3
    },
    name: 'gxb'
}
constobj01 = { ... obj }consoleOutput. The log (obj01) {name: 'gxb'[Symbol(Symbol.iterator)]: [GeneratorFunction: [Symbol.iterator]]
}
Copy the code

The visible object extension syntax does something else (I don’t know what it does yet…).

3.6 Promise, Generator, async

This section is mostly used for asynchrony, which is the focus of JS. I think we’re all familiar with it, but let me repeat it a little bit

Promise should be out of the question, as generator is pretty much complete

There are two general understandings of generator functions

  • The state machine
  • Iterators generate functions

As far as I’m concerned, I’ll just focus on its stop-and-go nature.

Add a few points that haven’t been mentioned above

  1. The next method of the iterator can be passed as an argument, which is treated as the return value of the previous yield
  2. for… Of does not get the data after the return expression
  3. The yield* expression is used to execute one Generator function within another

The Generator function.

Async as a syntactic sugar for generator async is also familiar

3.7 Other Extensions

What about expansion operators and deconstruction

Second, the VUE

What kind of outline does the vue summary start with? This is really a headache. So go through vUE’s frequently met tests again with your own understanding (although there may be countless people have done the sorting, but they sorted it out no matter how good it is as you go through it yourself)

1. Key knowledge

1.1 Life Cycle

1.1.1 a single set of pieces

1.1.2 Parent and Child Components

The hook execution sequence from creation to mount is

Subcomponent update

There is actually a hole here, many articles are written directly like this

Child component update process: Parent beforeUpdate -> child beforeUpdate -> Child updated -> Parent updated

But to get started, you only change one piece of data in the child, and it does not trigger the update hook for the parent

However, the child component should also be part of the parent component, and its update is supposed to trigger the parent component’s update hook

What are the premises for such a conclusion?

The premise is that the child component changes, which is monitored by the parent component, and thus causes the data change in the parent component (that is, when the data change of the subcomponent in the code is reported to the parent component through emit).

The destruction of parent and child components looks like this

1.1.3 Common Problems

Which construct to manipulate the DOM in

The only thing you need to do in both of these hooks is to actually attach the compiled template to the browser, so you can retrieve the latest DOM in the Mounted hook

Which hook to call the asynchronous request from

This is actually much simpler than the time required to manipulate the DOM above. In general, asynchronous requests are sent to get data on the server side, where the main concern is data storage

At least data has been initialized

Create, beforeMount, and Mounted hooks are available

Asynchronous requests are handled in the CREATED hook for faster data retrieval

1.2 Communication between Components

1.2.1 the father to son

Method one: props

Do you need chestnuts for this?

Method 2: refs

The parent component

<template> <div id="app"> <test01 ref="test01Ref"></test01> </div> </template> <script> import Test01 from /components/test01' export default {mounted() {this.$refs. test01ref. test=' parameters'}, components: {test01}} </script>Copy the code

Child components

<template>
  <div>
    {{test}}
  </div>
</template>
<script>
export default {
  data() {
    return {
      test:''
    }
  },
}
</script>

Copy the code

Method 3: $children

Parent component changes above

 mounted() {
   this.$children[0].test='parameters'
 },
Copy the code

It is important to note that $children does not guarantee order and is not responsive

1.2.2 child to the parent

This is using emit

1.2.3 Sibling Components

If there is a common father

This is also easier. The data to be passed on to the parents

Use parent in the 1 component to get the parent component to send an event and put the data in it. In the 2 component, the parent component listens for the event it just triggered and takes the data back

1 component

 this.$parent.$emit('demo', data)Copy the code

2 components

this.$parent.$on('demo'.data= > {
      console.log(data)
    })
Copy the code
No common parent

Using the Event Bus

class Bus {
    constructor() {
        this.callbacks = {}
    }
    on(name, fn) {

        this.callbacks[name] = fn

    }
    emit(name, args) {
        if (this.callbacks[name]) {
            this.callbacks[name](args);
        }
    }
}
Vue.prototype.$bus = new Bus()
Copy the code

1.2.4 Generational Components

So we’re using these two things

  1. provide
  2. inject
Ancestor component: Add a provide option, which can be an object or a function that returns an objectprovide(){
    return{
      test:"Parameters"}}, descendant component: js adds a inject option, which can be an array of strings or an objectinject: ['test']
Copy the code

1.2.5 vuex

Vuex related knowledge, has been sorted out

Others are playing source code, you are still struggling to use vuex…

1.3 Differences between Computed and Watch

computed

With caching, recalculation occurs only when the dependent data changes

watch

Whenever the listening properties change, the following callback is performed

1.4 Similarities and Differences between V-IF and V-show

V-if will selectively render components, v-show just show and hide

1.5 Six advanced features of VUE

Things like nextTick, slot, interfuse, keep-alive, etc., have been summarized previously

Link: A summary of the six advanced features of VUE — and a brief introduction to the principles of nextTick and Keep-Alive

1.6 Application and principle of VUex and VUE-Router

Others are playing source code, you are still entangled in vue-router use…

People are playing source code, you are still struggling to use vuex…

2. Knowledge of main principles

2.1 The responsivity principle of VUE

Vue2. X main API Object.defineProperty

Getter for dependency collection, setter for trigger update

Here as an important knowledge point, has been sorted out in front

Portal: Vue responsive implementation & Vue and React Diff algorithm (PS: too much code, plus a few mistakes not changed, such as watcher asynchronous updates should use microtasks, I use macro tasks…)

Here is sorting out the main idea, the main flow as small white place drawing below

Dep mainly collects Watch objects and notifies updates

let id = 0

class Dep {
    constructor() {
        this.subs = []
        this.id=id++
    }

    / / subscribe
    addSub(watcher) {
        this.subs.push(watcher)
    }

    / / release
    notify() {
        this.subs.forEach(watcher= > {
            watcher.update()
        })
    }
    
    // Implement the association with watcher
    depend() {
        if (Dep.target) {
            Dep.target.addDep(this)}}}Copy the code

Dep.depend () is called when collecting dependencies.

So go

Why have such a complicated process of relying on collection?

Think back to reactive data in VUE. Whenever reactive data in vUE changes, it is updated wherever it is used

In addition, only getter is used for dependency collection, so we defined data in the component data but did not use it in the view, that is, we did not fire the getter, so changing this data will not update the view (performance optimization)

Let’s use the big guy’s diagram

The responsivity principle of VUE3

The responsivity principle and dependency correlation of VUE3 are shown below

2.2 Diff algorithm logic

Here as an important knowledge point, has been sorted out in front

Portal: VUE responsive implementation & Vue and React diff algorithm

React and Vue diff are both involved here. React diff is simpler than Vue diff. There was a little bit of a diagram in the arrangement that I’m not going to separate out here

2.3 Principles of Template Compilation

This piece of knowledge point I fell in front, now simply fill its implementation

The main process is relatively simple, as we all know that the end result of template compilation is to generate a render function, which then generates a VNode and diff it to the page

So the main action here is: template — > render function

There are a lot of things to deal with in templates, like instructions, {{}}, etc. These things need to be found and operated from inside. How to manipulate some data in a piece of source code? If you’ve seen the handwritten section behind webpack that I summarized earlier, or if you’ve seen abstract syntax trees, this is the first place you’ll be answering. Yes, here is the need to convert the template into an abstract syntax tree, so that we can structurally operate in the form of nodes in this section of template source data

I did not know much about the optimization after making AST, so it will not be involved here. My writing focus on the template ->AST->render

Render function: render function: render function: render function: render function: render function: render function: render function: render function Focus on what’s important

Generate AST

The template

 <div id="app" style="color:blueviolet; font-size: 30px;">I am a {{name}}<span style="color: rgb(150, 70, 16)">{{age}}</span>
        <p class="demo" style="color:black;">How to play Vue?</p>

    </div>
Copy the code

It’s easier to rewrite webpack require, the AST generation can be done by a third party, but the Vue template has to be written by ourselves.

So the following is mainly string and re related operations

Let’s start by looking at what the generated AST looks like

The basic structure

Tag: tag attrs: {attributes... }children:{children... }parent: parent type: node typeCopy the code

Here comes the main point: the main idea for generating an AST

Again, it’s all about regex

Re’s in several VUE, focusing on annotated ones

// Match attributes such as id="app" id='app' id=app
const attribute = /^\s*([^\s"'<>\/=]+)(? :\s*(=)\s*(? :"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))? /;
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`;
const qnameCapture = ` ((? :${ncname}\ \ :)?${ncname}) `;
// Match tags start like 
const startTagOpen = new RegExp(` ^ <${qnameCapture}`);
// Match the tag as shown in > />
const   startTagClose = /^\s*(\/?) >/;
// Match 
const endTag = new RegExp(` ^ < \ \ /${qnameCapture}[^ >] * > `);
Copy the code

Starting with <, the method that matches the start of the tag and saves the tag and attributes matches the first start tag

As shown here, you can grab the tag and then go back and match the attribute (note that the matching rule is cut off as soon as the match is done).

The figure matches these attribute data and encapsulates them

Note that the tag is completed only if it matches , so all tags inside it can be used as child nodes

Let’s look at the main code here

function parseStartTag(template) {
    // Get the label
    const start = template.match(startTagOpen)
    console.log(start)
    let end, attr

    // Make a splice
    if (start) {
        const match = {
            tagName: start[1].attrs: []
        }
        template = advance(template, start[0].length)

        // See if there are attributes
        while(! (end = template.match(startTagClose)) && (attr = template.match(attribute))) {/ / properties
            match.attrs.push({
                name: attr[1].value: attr[3] || attr[4] || attr[5]})/ / cutting
            template = advance(template, attr[0].length)
        }

        / / over
        if (end) {
            template = advance(template, end[0].length)
            return { template, match }
        }
    }
}
Copy the code

And then you go down and you do this, and you do this again and again

Again, it’s code based, but look at the main function below, which deals with three cases.

  1. The processing begins when the tag and attribute data are saved
  2. Process intermediate text, saving the text data and placing the text node in the child of the current node
  3. To deal with end
// Convert the template to an AST
export function parseHtml(template) {
    const typeMsg = {
        text: undefined.root: undefined.currentParent: undefined.stack: []}while (template) {
        let testEnd = template.indexOf('<')
        const endTagMatch = template.match(endTag)

        if (endTagMatch) {
            template = advance(template, endTagMatch[0].length)

            end(endTagMatch[1], typeMsg)
        } else if (testEnd === 0) {
            const { template: newTemplate, match } = parseStartTag(template)

            // The head has been cut and collected
            template = newTemplate
            if (match) {
                start(match.tagName, match.attrs, typeMsg)
            }
        } else {
            typeMsg.text = template.substring(0, testEnd)
            template = advance(template, typeMsg.text.length)
            chars(typeMsg)
        }

    }

    return typeMsg.root
}
Copy the code

Advance method for cutting

function advance(template, n) {
    return template = template.substring(n)
}
Copy the code

Three start the middle end of the concrete operation function, these three methods are mainly tree structure construction

function createAST(tagName, attrs) {
    return {
        tag: tagName,
        type: 1.children: [],
        attrs,
        parent
    }
}
Copy the code
// Handle the header
function start(tagName, attrs, typeMsg) {
    const element = createAST(tagName, attrs)

    if(! typeMsg.root) { typeMsg.root = element } typeMsg.currentParent = element typeMsg.stack.push(element) }// Handle the end
function end(tagName, typeMsg) {
    const element = typeMsg.stack.pop()
    typeMsg.currentParent = typeMsg.stack[typeMsg.stack.length - 1]

    if (typeMsg.currentParent) {
        element.parent = typeMsg.currentParent
        typeMsg.currentParent.children.push(element)
    }
}

// Process intermediate text
function chars(typeMsg) {
    typeMsg.text = typeMsg.text.trim()

    if (typeMsg.text.length > 0) {
        typeMsg.currentParent.children.push({
            type: 3.text: typeMsg.text
        })
    }
}
Copy the code

To render

This step is also string concatenation, that is, you need to rearrange the AST from above into something like this

_c('div'),
    { id: "app".style: { "color": "blueviolet"."font-size": " 30px" } }
    , _v("I am" + _s(name)), _c('span'),
    { style: { "color": " rgb(150, 70, 16)" } }
    , _v(_s(age))
    , _c('p'),
    { class: "demo".style: { "color": "black" } }
    , _v("How do you play Vue?")
Copy the code

_c: Creates the element node

_v: Creates a text node

_s: Processes the data inside {{}}

The main function


export function generate(node) {
    let code = `_c('${node.tag}'),
    ${node.attrs.length > 0
            ? `${formatProps(node.attrs)}`
            : undefined}
           ${node.children ? `,${formatChild(node.children)}` : ' '}
            `
    return code

}
Copy the code

The above as the main function also handles the spelling of _c(‘div’)

Here are the concrete concatenation functions

{id: “app”, style: {“color”: “blueViolet “, “font-size:” 30px”}

// Concatenate attributes
function formatProps(attrs) {
    let attrStr = ' '

    for (let i = 0; i < attrs.length; i++) {
        let attr = attrs[i]

        if (attr.name === 'style') {
            let styleAttrs = {}

            attr.value.split('; ').map(item= > {
                let [key, value] = item.split(':')
                styleAttrs[key] = value
            })
            attr.value = styleAttrs
        }

        attrStr += `${attr.name}:The ${JSON.stringify(attr.value)}, `
    }

    return ` {${attrStr.slice(0, -1)}} `

}
Copy the code

Concatenate child nodes, which are mainly text nodes

It can be divided into the case with or without {{}}, without directly splicing _v()

If so, it’s a little bit more complicated

Take the following paragraph for example

I'm {{name}} and this is the text {{age}}Copy the code

Here we mainly use the regular multiple match exec method

First look at /\{\{((? :.|\r? \n)+?) \}\}/g.exec(‘ I am {{name}} this is the text {{age}}’)

Return value: [” {{name}} “, “name”, the index: 2, input: “my name is {{name}} this is text {{age}}”, groups: undefined]

The return value of this re can be used to get the first index subscript, the content of the face of {{}}, and the length of the first item of the return value array

The following operation is somewhat similar to the one above to generate the AST

So first match to

Push me into a temporary array container, then concatenate the contents of {{}} into _s(name), and then push me into the array

The next time the circular pointer moves, the operation is again pushed into the temporary container as above. I’m going to push this text in, and I’m going to put the age together and push it in

So the data of the last temporary container is zero

['I am'.'_s(name)'.'_s(age)']
Copy the code

Of course it could be: I’m {{name}} and this is the text {{age}} and the mosquito

Then you just need to go out of the loop to make a judgment, and finally concatenate the container data into a string

// Splice child nodes

/** ** There are two types of children * 1. Element nodes * 2. The text node * * element node is left to the generate function, which handles text */
/ / match {{}}
const defaultTagRE = / \ {\ {((? :.|\r? \n)+?) \}\}/g
function formatChild(children) {
    return children.map(child= > generateChild(child)).join(', ')}function generateChild(child) {

    switch (child.type) {
        case 1:

            return generate(child)

        // Main logic: processing text
        case 3:
            let text = child.text

            // There is no {{}}
            if(! defaultTagRE.test(text)) {return `_v(The ${JSON.stringify(text)}) `;
            }

            {{}}} {{}}
            let match,
                index,
                lastIndex = defaultTagRE.lastIndex = 0,
                textArr = []
            while (match = defaultTagRE.exec(text)) {

                index = match.index

                // Cut off the text before {{}}
                if (index > lastIndex) {
                    textArr.push(JSON.stringify(text.slice(lastIndex, index)))
                }

                / / stitching {{}}
                textArr.push(`_s(${match[1].trim()}) `)

                // Move pointer after {{}} to match {{}} again
                lastIndex = index + match[0].length
            }

            // When exiting the loop, lastIndex is less than the length of the text. That is, there is a text fragment without {{}}, collect it
            if (lastIndex < text.length) {
                textArr.push(JSON.stringify(text.slice(lastIndex)));
            }

            // All the collated data is stored in the textArr container and converted to a string
            _v(" hello, "+_s(name)),
            return `_v(${textArr.join('+')}) `}}Copy the code

3. Optimization of VUE project

Because of their lack of project experience, so can only write some of their own or heard and more important optimization plan

3.1 From a code perspective

  1. I’m going to write the key for v-for, whose key is mainly used for diff. (Check out the diff link above.)

  2. Use v-show, v-if, depending on the usage scenario. To switch components frequently use V-show, otherwise v-if

  3. Don’t use v-if, v-show together

3.2 From the perspective of project content

  1. Image lazy loading, route lazy loading components. That is, not all requests at one time can speed up the return of resources; At the same time, some resources may not be used and request waste is avoided

  2. The keep-alive cache component can be properly used. For some frequently used components, it can be cached, avoiding the repeated creation and destruction of this commonly used component

  3. ICONS use CSS ICONS as far as possible, not redundant to request image resources

  4. Use libraries (UI frameworks such as elementUI) to use the on-demand import form

  5. Release component resources (such as bound events, etc.) in a timely manner

3.3 From the perspective of packaging

  1. Plugins such as Axios, images (large) can be imported using CDN (small images can be base64, which adds a bit more packing volume but saves one request)

3.4 Common diseases of SPA

The common problem of single page is the first screen, adding a loading (it doesn’t matter if it is small).

3.5 Other optimizations can be made using the Webpack configuration

Link: Webpack — from basic use to manual implementation

4. First taste of vuE3 knowledge

4.1 Composition API Experience

4.2 Write reactive and simply implement its responsiveness

Vue3 data hijacking is mainly the use of proxy, note that this is the simplest. Some set operations on arrays, such as multiple proxies, are not handled (e.g. array push goes through set twice).

Reactive, the core of reactive, is the agent. For example, intercept GET and set.

Vue3’s data hijacking is also recursive, such as our reactive- a deep object. It’s just not as brainless recursive as vue2. X

function reactive(target) {
      return createReativeObject(target)
  }



  function createReativeObject(target) {
      // Determine if it is an object
      if (isObject(target)) {

          let observed = new Proxy(target, {
              get(target, key, receiver) {

                  console.log('get')
                  let res = Reflect.get(target, key, receiver)



                  return isObject(res) ? reactive(res) : res
              },
              set(target, key, value, receiver) {
                  console.log('set')
                  let res = Reflect.set(target, key, value, receiver)
               
                  return res
              },
              
          })


          return observed
      }
      return target
  }
Copy the code

Now comes the reactive core, which relies on collecting and distributing updates

Effect is the core here in VUe3, and is invoked in mountComponent, doWatch, Reactive, and computed

such

const obj = reactive({ name: 'gxb' })
effect(() = > {
    console.log(obj.name)
})
Copy the code

After we create a reactive proxy object with Reactive, we execute a side effect. The callback in this side effect is called first and then again when the data in obj.name has changed. Now you can think of the callbacks in the side effects as a view that first renders and then updates when the dependent data changes.

To implement the effect function, which is a bit verbose. Again, I’ll write it in simplified form. And it’s annoying that variables in the source code always have the same name as the effect function…

const stack = []

function effect(fn) {
    const effect = createReativeEffect(fn)
    effect()
}

function createReativeEffect(fn) {
    const effect = function() {
        return run(effect, fn)
    }
    return effect
}

function run(effect, fn) {

    stack.push(effect)
    fn()
    stack.pop()
}
Copy the code

After simplifying the source code, createReativeEffect is called in the effect function, and createReativeEffect returns a function, that is, the effect variable in the effect function

The main job here is to put an effect on the prepared stack

So what’s going to be pushed in?

This thing right here

ƒ () {
        return run(effect, fn)
    }
Copy the code

And then we have a property and the data may correspond to multiple of these things, and the property changes and what you do is you send out updates by calling this thing

Our main current is that the callback in the original side effect is executed again after the property has changed. This corresponds to the fact that the execution of the callback is inside run.

As for why there is a stack to store this thing, it is similar to the Watch stack in vue2. X.

Next, the FN callback executes. Be sure to get methods that trigger data. Even here it is the same as vue2 2.x. Dependencies are collected during get and updates are distributed during SET

function createReativeObject(target) {
      if (isObject(target)) {

          let observed = new Proxy(target, {
              get(target, key, receiver) {

                  console.log('get')
                  let res = Reflect.get(target, key, receiver)

                  // Rely on collection
                  track(target, key)


                  return isObject(res) ? reactive(res) : res
              },
              set(target, key, value, receiver) {
                  console.log('set')
                  let res = Reflect.set(target, key, value, receiver)
                  // Send updates
                  trigger(target, key)
                  return res
              },
              deleteProperty(){}})return observed
      }
      return target
  }
Copy the code

Use track to rely on collection and use track to distribute updates

Let’s implement track first

Let’s take a look at what the data structure for saving dependencies looks like

{
     target1: {key: [effect, effect]},target2: {key:[effect,effect]
   	}
 
}
Copy the code

A three-tier data structure is required

Because target is an object and for memory purposes. Therefore, a weakMap is used in the outermost layer

In the layer. Keys can also be objects, so use a map instead

At the lowest level, let’s use set for possible undo

The following operation is much easier to open the whole

export function track(target, key) {effect first from effect stackconst effect = stack[stack.length - 1]
    if (effect) {
        // Create a structure and plug it in
        let depsMap = targetMap.get(target)

        if(! depsMap) { targetMap.set(target, depsMap =new Map)}let deps = depsMap.get(key)
        if(! deps) { depsMap.set(key, deps =new Set()}if(! deps.has(effect)) { deps.add(effect) } } }Copy the code

Reactive ({name: ‘GXB’})

So it’s preserved like this

{reactive({ name: 'gxb'}) : {name:{effect}
}}
Copy the code

When the update is triggered, the name property data changes. Take out the corresponding effect and execute it, that is, the callback in run is still executed.

export function trigger(target, key) {
    const depsMap = targetMap.get(target)
    if (depsMap) {
        const deps = depsMap.get(key)
        if (deps) {
            deps.forEach(effect= > {
                console.log(effect)
                effect()
            })
        }
    }
}
Copy the code

3. Browser (Supplementary knowledge)

Browser stuff, I didn’t put it into the current stage of intensive reading… So just the simple ones

3.1 Rendering Engine work (Webkit)

Summarize the main process

Main process, as shown in figure

More official

I draw the process is the beginning of a xiu yan master’s summary of the text, the steps of different articles have thick and thin flow. How to change the final stage to grasp the most important, right

The most simplified process introduction

First, the browser encounters the HTML and parses it (generating a DOM tree). Parsing CSS when encountering CSS (generating CSSOM tree)

DOM trees are merged with CSSOM trees to generate render trees

It is important to note that this render tree will have a different node structure from the original DOM tree. The render tree will only contain the visual DOM nodes, and the computational styles will also be applied at this point

Again, note that rendering the tree does not include node location and size information, so this is what the layout phase does

The final drawing stage is to display it on the browser page!

One final note: this step does not mean just doing HTML, then CSS, and regenerating a render tree. They are synchronous, meaning they are rendered as they are parsed

The main common optimization problem here is rearrangement versus redraw

What is rearrangement and redraw

Rearrangement (reflux) : The operation causes a change in the geometry of the DOM

Redraw: The action only causes style changes

Why do these two things affect rendering performance?

Well, knowing what’s up there, it makes a lot of sense that this triggers rearrangement, redrawing and first CSSOM needs to be updated. Redrawing is fine, but the style has changed

It just needs to draw directly with the new CSSOM tree, update the render tree

But rearrangement is different, because the geometry of a node changes not only its size, but also its environment.

So the rearrangement needs to go through the process again, starting with updating the CSSOM tree

Therefore, reducing unnecessary rearrangement redraw (mainly rearrangement) will help optimize rendering speed

Note that there are also operations that cause rearrangements, node operations, fetching values that need to be computed in real time (offsetTop, etc.)