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
- The timing of the first creation
- Again, the global context is created only once
- 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
- Member types can only be objects
- 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
- Keys must be objects
- 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
- The next method of the iterator can be passed as an argument, which is treated as the return value of the previous yield
- for… Of does not get the data after the return expression
- 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
- provide
- 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.
- The processing begins when the tag and attribute data are saved
- Process intermediate text, saving the text data and placing the text node in the child of the current node
- 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
-
I’m going to write the key for v-for, whose key is mainly used for diff. (Check out the diff link above.)
-
Use v-show, v-if, depending on the usage scenario. To switch components frequently use V-show, otherwise v-if
-
Don’t use v-if, v-show together
3.2 From the perspective of project content
-
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
-
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
-
ICONS use CSS ICONS as far as possible, not redundant to request image resources
-
Use libraries (UI frameworks such as elementUI) to use the on-demand import form
-
Release component resources (such as bound events, etc.) in a timely manner
3.3 From the perspective of packaging
- 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.)