After writing this article, I think that in the future, I should categorize all the common knowledge of this foundation into this "Don't ask me XX questions again" and form a series of content. I hope that after everyone reads it, if someone asks you these questions again, you will secretly feel happy and say, "Hey hey, it's time to showcase true technology
1、 Don't ask me any more questions about the direction of this
The word 'cross domain' is like a piece of dog skin ointment stuck to every front-end developer, and you will inevitably encounter this problem in your work or interview. In order to cope with the interview, I always memorize a few plans casually, and I don't know why I have to do this. After finishing the pros and cons, I can throw them away. I don't think I will use so many messy plans in my work. When it comes to real work, the development environment is handled by webpack dev server, and even the big shots on the server end will be ready. I don't care what is configured, as it won't cross domains anyway. The days have just passed, and one day, I feel like I can't continue to mess around like this anymore. I must thoroughly understand this thing! So there is this article.
To master cross domain, the first thing to know is why the problem of cross domain arises
Indeed, as brick workers like us, we are just trying to make a living. Just set up a good interface and tell me that we have crossed domains. This kind of thing that hinders us from easily moving bricks is really disgusting! Why cross domain? Who is causing the problem? To find the culprit of this problem, please click on the browser's homologous strategy.
It's really difficult to understand such an official thing, it's okay, at least you know, because the browser's homologous strategy leads to cross domain, which is the browser doing things.
So, why are browsers doing things? Just don't want to give us a good life? For such questioning, the browser flipped the pot and said, "The same source policy restricts how documents or scripts loaded from the same source interact with resources from another source. This is an important security mechanism used to isolate potential malicious files
This official script is really difficult to understand. It's okay, at least you know, it seems to be a security mechanism.
So, why on earth is such a security mechanism needed? What problem has this security mechanism solved? Don't worry, let's continue our research.
Two major dangerous scenarios without homologous policy restrictions
As far as I know, browsers implement this same origin strategy from two aspects: one is for interface requests, and the other is for Dom queries. Imagine the danger of these two actions without such restrictions.
Interface requests without homology policy restrictions
There is a small thing called a cookie that everyone should know, which is usually used to handle login and other scenarios, with the purpose of letting the server know who made this request. If you request an interface to log in, the server will add a Set Cookie field to the response header after verification. Then, the next time you send a request, the browser will automatically attach a cookie to the HTTP request header field Cookie, and the server will know that the user has already logged in. After learning this, let's take a look at the scene:

  1. You are about to clear your shopping cart, so you open the shopping website www.maimaimai.com and log in successfully. Upon seeing that there are so few items in the shopping cart, you still need to buy more.
  2. While you were looking for something to buy, your good friend sent you a link to www.nidongde.com and said with a smile, "You understand," and you didn't hesitate to open it.
  3. You are browsing www.nidongde.com with great interest, but to your surprise, this website has secretly done something indescribable! Due to the lack of restrictions from the same source policy, it initiated a request to www.maimaimai.com! Clever of you, you must have thought of the above sentence: "After the server validates, it will add a Set Cookie field to the response header. Then, the next time the request is sent, the browser will automatically attach the cookie to the HTTP request header field Cookie." In this way, this illegal website is equivalent to logging into your account and doing whatever you want! If this is not a buy buy account, but your bank account, then
    This is the legendary CSRF attack. Talking about CSRF attack methods.
    After watching this wave of CSRF attacks, I was thinking that even with the restriction of the same origin policy, cookies are in plain text and cannot still be taken down. So I read some articles related to cookies and talked about the mechanisms and security of cookies, cookies, and sessions. I learned that the server can set httpOnly so that the front-end cannot operate cookies. If there is no such setting, XSS attacks can obtain XSS for cookie web security testing; Set secure to ensure transmission in encrypted communication over HTTPS to prevent interception.
    Dom queries without homologous policy restrictions
    One day, you just woke up and received an email saying that your bank account was at risk. Quickly click on www.yinghang.com to change your password. You're scared to pee. Hurry up and click on the familiar bank login interface. Enter your account password and log in to see if the money is missing.
  4. With dim sleep, you didn't see clearly. The bank website you usually visit is www.yinhang.com, but now you are visiting www.yinhang.com. What does this phishing website do?

//HTML

//JS
//Due to the lack of homologous policy restrictions, phishing websites can directly obtain Doms from other websites
Const iframe=window. frames ['yinhang ']
Const node=iframe. document. getElementById ('Input for the account password you entered ')
Console.log (I have obtained this ${node}, but can't I still obtain the account password you just entered?)
From this, we know that the same origin policy can indeed avoid some dangers. It does not mean that having the same origin policy is safe, but rather that the same origin policy is the most basic security mechanism for browsers, after all, it can increase the cost of attacks a bit. Actually, there is no shield that cannot be pierced, but the cost of an attack is not directly proportional to the benefits obtained after a successful attack.
Correct opening method across domains
After understanding the same origin strategy, we should eliminate misunderstandings about browsers. The same origin strategy is a good thing that browsers do and is used to defend against attacks from evil sources. However, we should not turn everyone out in order to prevent bad people from entering. That's right, as long as we open it correctly, we gentlemen should be able to cross domains.
Below, we will demonstrate the correct opening method one by one, but before that, there are some preparatory work to be done. In order to demonstrate cross domain locally, we need to:

  1. Run a copy of the front-end code randomly (the following front-end is a Vue that runs randomly), with the address http://localhost:9099 .
  2. Run a copy of the backend code randomly (the following backend is the node koa2 that runs randomly), and the address is http://localhost:9971 .
    The Correct Opening Method of Interface Requests under the Restriction of Same Origin Policy
  3. JSONP
    In HTML tags, some tags such as script and img for accessing resources do not have cross domain restrictions. Taking advantage of this, we can do the following:
    Write a small interface on the backend
    //Tool for handling successful and failed return formats
    const {successBody} = require('../utli')
    class CrossDomain {
    static async jsonp (ctx) {
    //The parameters passed from the front-end
    const query = ctx.request.query
    // Set a cookie
    ctx.cookies.set('tokenId', '1')
    // Query.cb is a method name agreed upon between the front and back ends, which actually means that the backend returns a directly executed method to the front-end. Since the front-end initiates a request using a script tag, returning this method is equivalent to executing immediately, and the data to be returned is placed in the parameters of the method.
    ctx.body = ${query.cb}(${JSON.stringify(successBody({msg: query.msg}, 'success'))})
    }
    }
    module.exports = CrossDomain
    Simple version front-end

<!DOCTYPE html>

<meta charset="utf-8">


<script type='text/javascript'>
  // The backend returns a method for direct execution, which is equivalent to executing this method. Since the backend places the returned data in the parameters of the method, res can be obtained here.
  window.jsonpCb = function (res) {
    console.log(res)
  }
</script>
<script src='http://localhost:9871/api/jsonp?msg=helloJsonp&cb=jsonpCb' type='text/javascript'></script>



Simply encapsulate the front-end routine
/**
*JSONP Request Tool
*Address requested by @ param URL
*Parameters for @ param data request
*@ returns {Promise}
*/
const request = ({url, data}) => {
return new Promise((resolve, reject) => {

//Process parameter transfer in the form of xx=yy&aa=bb
const handleData = (data) => {
  const keys = Object.keys(data)
  const keysLen = keys.length
  return keys.reduce((pre, cur, index) => {
    const value = data[cur]
    const flag = index !== keysLen - 1 ? '&' : ''
    return `${pre}${cur}=${value}${flag}`
  }, '')
}
  //Dynamically creating script tags
   Const script=document. createElement ('script ')
   //Obtaining data returned by the interface
window.jsonpCb = (res) => {
  document.body.removeChild(script)
  delete window.jsonpCb
  resolve(res)
}
script.src = `${url}?${handleData(data)}&cb=jsonpCb`
document.body.appendChild(script)

})
}
// usage
request({
url: 'http://localhost:9871/api/jsonp',
data: {

// Chuanshen
msg: 'helloJsonp'

}
}).then(res => {
console.log(res)
})
2.Empty iframe with form
Careful friends may find that JSONP can only send GET requests, because loading resources with scripts is essentially a GET. So what if a POST request needs to be sent?
Write a small interface on the backend
//Tool for handling successful and failed return formats
const {successBody} = require('../utli')
class CrossDomain {
static async iframePost (ctx) {

let postData = ctx.request.body
console.log(postData)
ctx.body = successBody({postData: postData}, 'success')

}
}
module.exports = CrossDomain
front end
Const requestPost=({url, data})=>{
//First, create an iframe for sending data
Const iframe=document. createElement ('iframe ')
Iframe. name='iframePost'
Iframe. style. display='none '
Document. body. appendChild (iframe)
Const form=document. createElement ('form ')
Const node=document. createElement ('input ')
//Register the load event handler for iframe, if you need to perform some actions on response returns
Iframe. addEventListener ('load ', function()){
Console. log ('post success')
})
Form. action=URL
//Execute the form in the specified iframe
Form. target=iframe. name
Form. method='post'
For (let name in data){
Node. name=name
Node. value=data [name]. toString()
Form. appendChild (node. loneNode())
}
//Form elements need to be added to the main document
Form. style. display='none'
Document. body. appendChild (form)
Form. submit()
//After the form is submitted, it can be deleted without affecting the next data transmission
Document. body. removeChild (form)
}
//Usage
requestPost({
url: 'http://localhost:9871/api/iframePost',
data: {

msg: 'helloIframePost'

}
})

  1. CORS
    CORS is a W3C standard, also known as Cross origin resource sharing. You can tell by name that this is the standard practice for dealing with cross domain issues. CORS has two types of requests, simple and non simple.
    Here is a reference to the article by Teacher Ruan Yifeng from the link above to explain simple and non simple requests.
    The browser divides CORS requests into two categories: simple requests and not so simple requests.
    As long as both of the following conditions are met, it is considered a simple request.
    (1) The request method is one of the following three methods:
    HEAD
    GET
    POST
    (2) The header information of HTTP does not exceed the following fields:
    Accept
    Accept Language
    Content Language
    Last Event ID
    Content Type: limited to three values: application/x-www form urlencoded, multipart/form data, text/plain
  2. Simple request
    back-end
    //Tool for handling successful and failed return formats
    const {successBody} = require('../utli')
    class CrossDomain {
    static async cors (ctx) {
    const query = ctx.request.query
    // *Cookies will not be included in HTTP requests
    ctx.set('Access-Control-Allow-Origin', '*')
    ctx.cookies.set('tokenId', '2')
    ctx.body = successBody({msg: query.msg}, 'success')
    }
    }
    module.exports = CrossDomain
    The front-end doesn't need to do anything, just send requests normally. If cookies are needed, both the front-end and back-end should be set up, as shown in the non simple request example below.

fetch(http://localhost:9871/api/cors?msg=helloCors).then(res => {
console.log(res)
})

  1. Non simple request
    A non simple request will issue a pre detection request with a return code of 204. Only after the pre detection is passed will the request be truly issued, which returns 200. Here, an additional header is added when the front-end sends a request to trigger non simple requests.
    Clipboard.png
    back-end
    //Tool for handling successful and failed return formats
    Const {successBody}=require ('../utli')
    Class CrossDomain{
    Static asynchronous cors (ctx){
    Const query=ctx. request. query
    //If cookies need to be included in HTTP requests, credentials need to be set on both the front and back ends, and the specified origin needs to be set on the backend
    Ctx. set ('Access Control Allow Origin ',' http://localhost:9099 ')
    Ctx. set ('Access Control Allow Credentials', true)
    //CORS requests that are not simple requests will have an HTTP query request added before formal communication, called a "preflight" request
    //In this case, in addition to setting the origin, it is also necessary to set the Access Control Request Method and Access Control Request Headers
    Ctx. set ('Access Control Request Method ',' PUT, POST, GET, DELETE, OPTIONS')
    Ctx. set ('Access Control Allow Headers', 'Origin, X-Requested With, Content Type, Accept, t')
    Ctx. cookies. set ('tokenId ',' 2 ')
    Ctx. body=successBody ({msg: query. msg}, 'success')
    }
    }
    Module. exports=CrossDomain
    There is so much code to write for an interface, what more elegant way is there to handle all interfaces uniformly? See koa2 cors below.
    Const path=require ('path ')
    Const Koa=require ('koa ')
    Const koaStatic=require ('koa static ')
    Const bodyParser=require ('koa bodyParser ')
    Const router=require ('./router')
    Const cors=require ('koa2 cors')
    Const app=new Koa()
    Const port=9871
    App. use (bodyParser())
    //Processing static resources here is the directory after the front-end build is completed
    App. use (koaStatic(
    Path. resolve (__dirname, '../dist')
    ))
    //Process Cors
    App. use (cors){
    Origin: function (ctx){
    Return ' http://localhost:9099 '
    },
    Credentials: true,
    AllowMethods: ['GET ',' POST ',' DELETE '],
    AllowHeaders: ['t ',' Content Type ']
    })
    //Routing
    App. use (router. routes()). use (router. allowedMethods())
    //Listening port
    App. listen (9871)
    Console. log ( [demo] start quick is starting at port ${port} )
    front end
    Fetch ( http://localhost:9871/api/cors?msg=helloCors {
    //Need to bring a cookie
    Credentials: 'include',
    //Add additional headers here to trigger non simple requests
    Headers:{
    't ':' extra headers'
    }
    }Then (res=>{
    Console. log (res)
    })
  2. Agency
    Think about it, if we still use the front-end domain name when requesting, and there is something to help us forward this request to the real back-end domain name, wouldn't it avoid cross domain? At this point, Nginx appeared.
    Nginx configuration
    Server{

    Listening to port 9099

    Listen 9099;

    The domain name is localhost

    Server_ Name localhost;

    Anything like localhost: 9099/API will be forwarded to the true server address http://localhost:9871

    Location ^~/apis{
    Proxy_ Pass http://localhost:9871 ;
    }
    }
    There's no need for the front-end to do anything, except write interfaces, and there's no need for the back-end to do anything
    //Directly use the domain name on the front-end side when requesting http://localhost:9099 This will not cross domains, and then Nginx listens to everything like localhost: 9099/apis and forwards it to the true server address http://localhost:9871
    Fetch http://localhost:9099/api/iframePost '{
    Method: 'POST',
    Headers:{
    'Accept': 'application/json',
    'Content Type': 'application/json'
    },
    Body: JSON. stringify ({
    Msg: 'helloIframePost'
    })
    })
    The Nginx forwarding method seems very convenient! But this usage also depends on the scenario. If the backend interface is a public API, such as some public services that obtain weather information, the front-end call should not have the operation and maintenance personnel configure Nginx. If compatibility is not a problem (IE 10 or above), CROS is the more common approach.
    The Correct Opening Method of Dom Queries under the Restriction of Same Origin Policy

  3. PostMessage
    Window. postMessage () is an interface in HTML5 that focuses on cross domain communication between different windows and pages.
    For the convenience of the demonstration, we will change the hosts to 127.0.0.1 crossDomain.com. Now accessing the domain name crossDomain.com is equivalent to accessing 127.0.0.1.
    This is http://localhost:9099/#/crossDomain From

This is http://crossdomain.com:9099 Received by

The results can be seen as follows:
Clipboard.png

  1. document.domain
    This method is only suitable for cross domain iframes with the same primary domain but different sub domains.
    For example, the main domain name is http://crossdomain.com:9099 The sub domain name is http://child.crossdomain.com:9099 In this case, specifying document. domain for both pages, i.e. document. domain=crossdomain. com, allows access to their respective window objects.
  2. Cross domain problem of canvas operation images
    This should be a relatively unpopular cross domain issue. Zhang Dashen has already written about it, so I will no longer try to solve the canvas image getImageData, toDataURL cross domain problem
    last
    I hope that after reading this article, if someone asks me cross domain questions again, you can raise your mouth slightly and sneer, "Don't ask me cross domain questions anymore
    Soaring away.

Look at the interview questions, just to fill in any gaps and see what aspects you still don't understand. Remember not to think that memorizing interview questions means everything is fine. It's best to understand the underlying principles so that you can have a smooth conversation during the interview. Otherwise, a slightly skilled interviewer can tell at a glance whether they have real talent and knowledge or have just memorized this interview question.
(These are all basic vue interview questions, so there's no need to waste time looking down.)
1、 What is your understanding of MVVM?
MVVM is the abbreviation for Model View View Model.
A Model represents a data model, and business logic for data modification and operation can also be defined in the Model.
View represents the UI component, which is responsible for transforming the data model into a UI for presentation.
ViewModel listens to changes in model data, controls view behavior, and handles user interaction. Simply understood, it is an object that synchronizes View and Model, connecting Model and View.
In the MVVM architecture, there is no direct connection between View and Model, but rather interaction through ViewModel. The interaction between Model and ViewModel is bidirectional, so changes in View data are synchronized to the Model, and changes in Model data are immediately reflected on the View.
ViewModel connects the View and Model layers through bidirectional data binding, and the synchronization between View and Model is completely automatic without human intervention. Therefore, developers only need to focus on business logic, do not need to manually operate DOM, and do not need to pay attention to data state synchronization issues. Complex data state maintenance is completely managed by MVVM.
Bg2015020110.png
2、 Vue's lifecycle
Before creating, data observation and initialization events have not yet started
Created (after creation) completes data observation, property and method operations, initializes events, and the $el attribute has not yet been displayed
Before Mount is called before the start of the mount, and the relevant render function is called for the first time. The instance has completed the following configuration: compile the template and generate HTML from the data and template in the data. Please note that the HTML has not been attached to the page at this time.
Mounted (after loading) is a newly created VM in EL$ El, and mount it to the instance. The instance has completed the following configuration: replace the DOM object pointed to by the el attribute with the compiled HTML content above. Complete the HTML rendering in the template to the HTML page. Perform ajax interaction during this process.
BeforeUpdate is called before the data is updated, which occurs before the virtual DOM is re rendered and patched. You can further change the state in this hook without triggering additional re rendering processes.
Updated is called after the virtual DOM is re rendered and patched due to data changes. When called, the component DOM has been updated, so DOM dependent operations can be performed. However, in most cases, changing the state during this period should be avoided as it may result in an infinite loop of updates. This hook is not called during server-side rendering.
BeforeDestroy is called before the instance is destroyed. The instance is still fully available.
Destroyed is called after the instance is destroyed. After the call, all event listeners will be removed and all child instances will also be destroyed. This hook is not called during server-side rendering.
What is the Vue lifecycle?
Answer: The process from creation to destruction of Vue instances is the lifecycle. The lifecycle of Vue is a series of processes, including creating, initializing data, compiling templates, mounting Doms, rendering, updating, rendering, and destroying.
What is the role of the Vue lifecycle?
Answer: It has multiple event hooks in its lifecycle, making it easier for us to form good logic when controlling the entire Vue instance process.
How many stages are there in the Vue lifecycle?
Answer: It can be divided into a total of 8 stages: before/after creation, before/after loading, before/after updating, and before/after destruction.
Which hooks will trigger the first page load?
Answer: The following beforeCreate, created, beforeMount, and mounted will be triggered.
In which cycle has DOM rendering been completed?
Answer: The DOM rendering is already completed in mounted.
3、 The principle of Vue implementing bidirectional data binding: Object. defineProperty()
The main way to achieve bidirectional data binding in Vue is to use a combination of data hijacking and publisher subscriber mode, using Object. defineProperty() to hijack the setters and getters of various properties, and publish messages to subscribers when data changes, triggering corresponding listening callbacks. When passing a regular Javascript object to a Vue instance as its data option, Vue will traverse its properties and convert them to getters/setters using Object. defineProperty. Users cannot see getters/setters, but internally they allow Vue to track dependencies and notify changes when attributes are accessed and modified.
The bidirectional data binding of Vue uses MVVM as the entry point for data binding, integrating Observer, Compile, and Watcher. Through Observer, it listens for data changes in its own model, parses compilation template instructions through Compile (used in Vue to parse {{}}), and finally uses Watcher to build a communication bridge between Observer and Compile, achieving data changes ->view updates; View interaction change (input) ->bidirectional binding effect of data model change.
Implementing Simple Bidirectional Binding with JavaScript

<div id="app">
<input type="text" id="txt">
<p id="show"></p>


4、 Parameter transfer between Vue components

  1. Parent component and child component passing values
    The parent component passes to the child component: the child component receives data through the props method;
    Child component passing to parent component: $exit method passing parameters
  2. Data transfer between non parent-child components, and value transfer between sibling components
    EventBus is the creation of an event center, equivalent to a transit station, that can be used to transmit and receive events. The project is relatively small, so this is more suitable. (Although many people recommend using VUEX directly, it depends on the requirements. Technology is just a means, achieving the goal is the king's way.)
    5、 Route Implementation of Vue: Hash Mode and History Mode
    Hash mode: In the browser, the symbol '#', followed by the characters' # ', is called a hash, which is read using window. location. hash;
    Feature: Although hash is in the URL, it is not included in HTTP requests; Used to guide browser actions, useless for server security, hash will not reload the page.
    In hash mode, only the content before the hash symbol will be included in the request, such as http://www.xxx.com Therefore, for the backend, even if full coverage of the route is not achieved, a 404 error will not be returned.
    History mode: History adopts the new features of HTML5; And two new methods are provided: pushState (), replaceState () to modify the browser history stack, and to listen for state changes in popState events.
    In the history mode, the front-end URL must be consistent with the actual URL that initiates the request from the backend, such as http://www.xxx.com/items/id . If the backend lacks routing processing for/items/id, it will return a 404 error. In the official website of Vue Router, it is described as follows: "However, to play this mode well, backend configuration support is also required... So, you need to add a candidate resource on the server that covers all situations: if the URL cannot match any static resources, you should return to the same index. html page, which is the page that your app depends on
    6、 What is the difference between Vue, Angular, and React?
    (The versions are constantly updated, and the following differences may not be very accurate. I only use Vue in my work and am not very familiar with Angular and React.)
  3. Differences from AngularJS
    Similarities:
    Both support instructions: built-in instructions and custom instructions; Both support filters: built-in filters and custom filters; Both support bidirectional data binding; None of them support low-end browsers.
    Differences:
    AngularJS has a high learning cost, such as adding the Dependency Injection feature, while Vue.js itself provides a relatively simple and intuitive API; In terms of performance, AngularJS relies on dirty checking of data, so the more Watchers, the slower; Vue.js uses dependency tracking based observation and asynchronous queue updates, and all data is independently triggered.
  4. Differences from React
    Similarities:
    React adopts a special JSX syntax, and Vue. js also advocates writing. Vue special file formats in component development, with some conventions for file content, both of which need to be compiled and used; The central idea is the same: everything is a component, and component instances can be nested; Provide reasonable hook functions that allow developers to customize requirements; They do not have built-in column count AJAX, Route, and other functions in the core package, but are loaded as plugins; Mixins feature is supported in component development.
    Differences:
    The Virtual DOM used by React will perform dirty checks on the rendered results; Vue.js provides instructions, filters, and more in the template, making it very convenient and fast to operate the Virtual DOM.
    7、 Hook function for Vue routing
    The homepage can control navigation jumps, beforeEach, afterEach, etc., and is generally used for modifying page titles. Some redirect functions that require login to adjust the page.
    Before Each mainly has three parameters to, from, and next:
    To: The target routing object that the route is about to enter,
    From: route The current navigation is about to leave the route
    Next: Function must call the method resolve hook. The execution effect depends on the calling parameters of the next method. Can control webpage redirection.
    8、 What is vuex? How to use it? Which functional scenario uses it?
    The states that are only used for reading are concentrated in the store; The way to change the state is to submit mutations, which is a synchronized thing; Asynchronous logic should be encapsulated in actions.
    Introduce the store in main.js and inject it. Created a new directory store Export.
    Scenarios include: in a single page application, the status between components, music playback, login status, and adding to the shopping cart
    Picture description
    State
    Vuex uses a single state tree, meaning that each application will only contain one store instance, but a single state tree and modularity do not conflict. The stored data status cannot be directly modified.
    Mutations
    The methods defined by mutations dynamically modify the state or data in Vuex's store.
    Getters
    Computational attributes similar to Vue are mainly used to filter some data.
    Action
    Actions can be understood as transforming the data processing methods in mutations into asynchronous data processing methods, which in simple terms are asynchronous operations on data. The view layer distributes actions through store.dispath.

const store = new Vuex.Store({//Store instance

  state: {
     count: 0
         },
  mutations: {                
     increment (state) {
      state.count++
     }
      },
  actions: { 
     increment (context) {
      context.commit('increment')

}
}
})
modules
When the project is particularly complex, each module can have its own state, mutation, action, and getters, making the structure very clear and easy to manage.

const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}

const store = new Vuex.Store({
modules: {

a: moduleA,
b: moduleB

})
九、How can Vue cli add custom instructions?

  1. Create local instructions

var app = new Vue({

el: '#app',
data: {    
},
// Create instructions (can have multiple)
directives: {
    // Instruction Name
    dir1: {
        inserted(el) {
            // The first parameter in the instruction is the DOM of the currently used instruction
            console.log(el);
            console.log(arguments);
            // Operate on DOM
            el.style.width = '200px';
            el.style.height = '200px';
            el.style.background = '#000';
        }
    }
}

})
2Global Instructions

Vue.directive('dir2', {

inserted(el) {
    console.log(el);
}

})
3.Use of instructions

十、How can Vue customize a filter?
HTML code:

{{msg| capitalize }}

JS code:

var vm=new Vue({

el:"#app",
data:{
    msg:''
},
filters: {
  capitalize: function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}

})
Global Definition Filter

Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
The filter receives the value of the expression (msg) as the first parameter. The capitalization filter will receive the value of msg as the first parameter.
11、 What is your understanding of keep alive?
Keep alive is a built-in component of Vue that can preserve the state of the included components or avoid re rendering.
After version 2.1.0 of Vue, keep alive added two new attributes: include (cache of included components) and exclude (exclude components are not cached and have a higher priority than include).
Usage method


<!-- Whether this component is cached depends on the include and exclude attributes -->



Parameter Interpretation
Include - String or regular expression, only components with matching names will be cached
Exclude - Strings or regular expressions, any components with matching names will not be cached
The properties of include and exclude allow components to cache conditionally. Both can use "," to separate strings, regular expressions, and arrays. When using regexes or arrays, remember to use v-bind.
Usage examples
<-- Comma separated string, only components a and b are cached. -->
Keep alive include="a, b">


<-- Regular expression (requires the use of v-bind, all matching rules will be cached) -->
Keep alive: include="/a | b/">


<-- Array (requires the use of v-bind, everything contained will be cached) -->
Keep alive: include="['a ','b']">


12、 An interview question that can be answered in just one sentence

  1. CSS only works on the current component
    Answer: Write scoped in the style tag, for example:
  2. Differences between v-if and v-show
    Answer: V-if is rendered according to the conditions, and V-show is the block or none of display;
  3. Differences between $route and $router
    Answer: $route is the "routing information object", including path, params, hash, query, fullPath, matched, name, and other routing information parameters. And $router is the "routing instance" object, which includes the jump method, hook function, and so on of the route.
    What are the two cores of vue.js?
    Answer: Data driven, component system
  4. Several Common Instructions for Vue
    Answer: V-for, V-if, V-bind, V-on, V-show, V-else
  5. What are the commonly used modifiers for Vue?
    Answer:. prevent: Submitting an event will no longer overload the page Stop: Prevent click events from bubbling Self: Triggered when an event occurs on the element itself rather than a child element Capture: Event listening, which will be called when an event occurs
    Can V-on bind multiple methods?
    Answer: Yes
    What is the role of key values in Vue?
    Answer: When Vue.js uses v-for to update the rendered element list, it defaults to using the "in place reuse" strategy. If the order of the data items is changed, Vue will not move DOM elements to match the order of the data items, but will simply reuse each element here and ensure that it displays each element that has been rendered under a specific index. The main function of key is to efficiently update the virtual DOM.
  6. What are the computational properties of Vue?
    Answer: Placing too much logic in a template can make it too heavy and difficult to maintain. In situations where complex data processing is required and may be used multiple times, it is recommended to use the method of calculating attributes as much as possible. Benefits: ① Clear data processing structure; ② Depends on data, updates data, and automatically updates processing results; ③ This points to the VM instance within the calculation attribute; ④ When calling the template, simply write the calculation property name; ⑤ The commonly used method is the getter method, which can obtain data or use the set method to change the data; ⑥ Compared to methods, regardless of whether the dependent data remains unchanged, methods will be recalculated. However, when the dependent data remains unchanged, computed is obtained from the cache and will not be recalculated.
  7. Single page applications such as Vue and their advantages and disadvantages
    Answer: Advantages: Vue's goal is to achieve responsive data binding and composite view components through the simplest possible API, with a responsive data binding system at its core. MVVM, data-driven, componentized, lightweight, concise, efficient, fast, and modular friendly.
    Disadvantage: It does not support lower version browsers, and only supports IE9 at the minimum; Not conducive to SEO optimization (if you want to support SEO, it is recommended to use the server to render components); The first time loading the homepage takes a relatively long time; You cannot use the browser's navigation buttons and need to implement forward and backward actions yourself.
  8. How to define dynamic routing for vue router? How to obtain the value passed in
    Answer: In the index.js file in the router directory, add/: id to the path attribute and use the params. id of the router object to obtain it.

preface
Limited insights. If there are any inappropriate descriptions, please help to point them out in a timely manner. If there are any errors, they will be corrected in a timely manner.
----------Long text and multi image warning require a lot of time----------
If, after reading this article, you are still confused about process threads and the differences between browser multiprocessing, browser kernel multithreading, JS single threading, and JS running mechanism. So please reply to me, it must be that my writing is not clear enough. I will make the necessary changes...
----------Start of main text----------
Recently, I have found many articles introducing the JS single thread running mechanism, but I have found that many of them only introduce a certain part of the knowledge, and the explanations in various places are not uniform, which can easily cause confusion.
Therefore, we are preparing to sort out this knowledge point, combine it with existing knowledge, and based on a large number of reference materials online,
From browser multiprocessing to JS single threading, systematically review the running mechanism of the JS engine.
Presentation form: As it belongs to the systematic sorting type, it does not go from shallow to deep, but rather sorts out the knowledge system from beginning to end,
The key is to connect the knowledge points of key nodes together, rather than just analyzing a certain part of the knowledge.
The content is: from the browser process, to the browser kernel running, to the JS engine single thread, and then to the JS event loop mechanism, a systematic review is carried out from beginning to end to break away from fragmentation and form a knowledge system
The goal is to have a certain understanding of browser multiprocessing, JS single threading, and JS event loop mechanisms after reading this article,
Having a framework of knowledge system, rather than a feeling of vague understanding.
In addition, this article is suitable for front-end personnel with certain experience. Novices should avoid it to avoid being overly impacted by concepts. You can save it first and watch it after you have a certain understanding, or you can watch it in multiple batches to avoid excessive fatigue.
outline
Distinguish between processes and threads
Browsers are multiprocessing
What processes do browsers contain?
The Advantages of Browser Multiprocessing
The focus is on the browser kernel (rendering process)
The communication process between the Browser process and the browser kernel (Renderer process)
Sort out the relationships between threads in the browser kernel
GUI rendering thread and JS engine thread are mutually exclusive
JS blocking page loading
WebWorker, multi threading of JS?
WebWorker and SharedWorker
Briefly sort out the browser rendering process
The sequence of the load event and the DOMContentLoaded event
Will CSS loading block dom tree rendering?
Normal and composite layers
On the Running Mechanism of JS from the Perspective of Event Loop
Further supplement the event cycle mechanism
Let's talk about the timer separately
SetTimeout instead of setInterval
Event loop advancement: macrotask and microtask
Written at the end of the sentence
Distinguish between processes and threads
The unclear distinction between threads and processes is a mistake that many beginners make, and it doesn't matter. This is very normal. Let's take a look at the following image metaphor:
-A process is a factory, and the factory has its own independent resources
-Factories are independent of each other
-Threads are workers in a factory, and multiple workers collaborate to complete tasks
-There are one or more workers in the factory
-Shared space between workers
Further improve the concept:
-Factory resources ->System allocated memory (a separate block of memory)
-Mutual independence between factories ->Mutual independence between processes
-Multiple workers collaborate to complete tasks ->Multiple threads collaborate in the process to complete tasks

-There are one or more workers in the factory ->A process is composed of one or more threads
-Shared space between workers ->Memory space for programs shared between threads within the same process (including code snippets, datasets, heaps, etc.)
Then consolidate:
If it is a Windows computer, you can open the Task Manager and see a list of background processes. Yes, that's where you can view the processes and see the memory resource information and CPU usage of each process.
So, it should be easier to understand that a process is the smallest unit of CPU resource allocation (the system will allocate memory to it)
Finally, describe it in more official terms:
Process is the smallest unit of CPU resource allocation (the smallest unit that can own resources and run independently)
Thread is the smallest unit of CPU scheduling (a thread is a program running unit based on a process, and there can be multiple threads in a process)
Tips
Different processes can also communicate with each other, but the cost is high
Nowadays, the commonly used term "single threaded" or "multi threaded" refers to both single and multiple threads within a process. (So the core still needs to belong to a process)
Browsers are multiprocessing
After understanding the differences between processes and threads, let's now have a certain understanding of the browser: (Let's take a look at simplifying the understanding first)
Browsers are multiprocessing
The reason why a browser can run is because the system allocates resources (CPU, memory) to its processes
Simply put, opening a Tab page is equivalent to creating an independent browser process.
Regarding the verification of the above points, please refer to the first image:
In the figure, multiple tabs of Chrome browser are opened, and you can see multiple processes in Chrome's task manager (each tab page has an independent process and a main process).
Interested parties can try it out on their own. If you open another tab page, the process will be+1 or more normally
Note: Here, the browser should also have its own optimization mechanism. Sometimes, after opening multiple tab pages, you can see in Chrome Task Manager that some processes have been merged
(So it is not necessarily absolute that each tab label corresponds to a process)
What processes do browsers contain?
After knowing that the browser is multiprocessing, let's take a look at which processes it actually contains: (To simplify understanding, only list the main processes)
Browser process: The main process of the browser (responsible for coordination and control), with only one. The effect is
Responsible for displaying browser interfaces and interacting with users. Such as forward, backward, etc
Responsible for managing various pages, creating and destroying other processes
Draw the Bitmap in memory obtained by the Renderer process onto the user interface
Management and download of network resources
Third party plugin process: Each type of plugin corresponds to a process, which is only created when using that plugin
GPU process: Up to one, used for 3D rendering, etc
Browser rendering process (browser kernel) (Renderer process, internally multi-threaded): default to one process per tab page, without affecting each other. The main function is to
Page rendering, script execution, event handling, etc
Enhanced memory: Opening a webpage in a browser is equivalent to starting a new process (with its own multithreading)
Of course, browsers sometimes merge multiple processes (such as opening multiple blank tabs and discovering that multiple blank tabs have been merged into one process), as shown in the figure
In addition, it can be self verified through Chrome's more tools ->Task Manager
The Advantages of Browser Multiprocessing
Compared to single process browsers, multi process browsers have the following advantages:
Avoid single page crashes affecting the entire browser
Avoid third-party plugin crashes affecting the entire browser
Multi process fully utilizing multi-core advantages
Convenient use of sandbox models to isolate plugins and other processes, improving browser stability
Simply put, if the browser is a single process, then if a tab page crashes, it will affect the entire browser, and how bad the experience is; Similarly, if it is a single process, a plugin crash will also affect the entire browser; Moreover, multi process has many other advantages...
Of course, the consumption of resources such as memory will also be greater, which means a bit of space for time.
The focus is on the browser kernel (rendering process)
Here comes the key point, as we can see, with so many processes mentioned above, what is ultimately required for ordinary front-end operations? The answer is the rendering process
It can be understood that page rendering, JS execution, and event loops all occur within this process. Next, we will focus on analyzing this process
Please remember that the rendering process of the browser is multi-threaded (if you don't understand this, please look back at the distinction between processes and threads)
Have you finally arrived at the concept of threading?, So friendly. So let's take a look at which threads it includes (list some of the main resident threads):
GUI rendering thread
Responsible for rendering browser interfaces, parsing HTML, CSS, building DOM and RenderObject trees, layout and rendering, etc.
When the interface needs to be repainted or reflow is triggered by some operation, the thread will execute
Note that the GUI rendering thread and the JS engine thread are mutually exclusive. When the JS engine executes, the GUI thread will be suspended (equivalent to being frozen), and GUI updates will be saved in a queue and executed immediately when the JS engine is idle.
JS Engine Thread
Also known as the JS kernel, responsible for handling Javascript script scripts. (e.g. V8 engine)
The JS engine thread is responsible for parsing Javascript scripts and running code.
The JS engine has been waiting for the arrival of tasks in the task queue and then processing them. At any time in a Tab page (renderer process), only one JS thread is running the JS program
Similarly, note that the GUI rendering thread and the JS engine thread are mutually exclusive, so if the JS execution time is too long, it will cause inconsistent page rendering, leading to page rendering load blocking.
Event triggered thread
Belonging to the browser rather than the JS engine, used to control event loops (it can be understood that the JS engine itself cannot be busy and requires the browser to open another thread for assistance)
When the JS engine executes code blocks such as setTimeOut (which can also come from other threads in the browser kernel, such as mouse clicks, AJAX asynchronous requests, etc.), the corresponding task will be added to the event thread
When the corresponding event meets the triggering criteria and is triggered, the thread will add the event to the end of the queue to be processed and wait for the JS engine to process it
Note that due to the single threaded nature of JS, the events in these pending queues must be queued for processing by the JS engine (only executed when the JS engine is idle)
Timer Trigger Thread
The thread where setInterval and setTimeout are located in the legend
The browser timing counter is not counted by the JavaScript engine (because the JavaScript engine is single threaded, and if it is in a blocked thread state, it will affect the accuracy of timing)
Therefore, timing is triggered through a separate thread (after the timing is completed, it is added to the event queue and executed after the JS engine is idle)
Note that W3C specifies in the HTML standard that time intervals below 4ms in setTimeout are considered as 4ms.
Asynchronous HTTP request thread
After connecting to XML HttpRequest, a new thread request is opened through the browser
When a state change is detected, if a callback function is set, the asynchronous thread will generate a state change event and place this callback in the event queue. Then executed by the JavaScript engine.
If you feel tired from seeing this, you can take a break first. These concepts need to be digested, after all, the event loop mechanism mentioned later is based on event triggered threads. Therefore, if you only look at some fragmented knowledge,
There may be a feeling of confusion. The sorting that needs to be completed can quickly settle and is not easy to forget. Let's consolidate with a picture:

One more thing, why is the JS engine single threaded? Well, there shouldn't be a standard answer to this question, for example, it may just be because of the complexity of multithreading, such as the fact that multithreading operations typically require locking, so the initial design chose single threading...
The communication process between the Browser process and the browser kernel (Renderer process)
Seeing this, first of all, you should have a certain understanding of the processes and threads within the browser. Then, let's talk about how the browser's Browser process (control process) communicates with the kernel,
After understanding this point, one can connect the knowledge in this section and have a complete concept from beginning to end.
If you open the Task Manager yourself and then open a browser, you can see that there are two processes in the Task Manager (one is the main process, and the other is the rendering process that opens the Tab page),
Then, under this premise, take a look at the entire process: (greatly simplified)
The Browser process receives a user request and first needs to retrieve the page content (such as downloading resources through the network), then passes the task to the RenderHost interface
The Renderer interface of the Renderer process receives a message, provides a brief explanation, hands it over to the rendering thread, and then starts rendering
The rendering thread receives requests, loads web pages, and renders them, which may require the Browser process to obtain resources and the GPU process to assist in rendering
Of course, there may be JS threads operating the DOM (which may cause reflow and redrawing)
Finally, the Render process passes the results to the Browser process
The Browser process receives the result and draws it out
Here is a simple diagram: (very simplified)
After reading this entire process, you should have a certain understanding of the operation of the browser, so that with a foundation of knowledge architecture, it is convenient to fill in the content later on.
If we delve deeper into this topic, it will involve parsing the browser kernel source code, which is not within the scope of this article.
If you want to delve deeper into this area, it is recommended to read some browser kernel source code analysis articles, or you can first take a look at the first article in the reference source, which is well written
Sort out the relationships between threads in the browser kernel
At this point, we have gained an overall understanding of the operation of the browser. Next, let's briefly summarize some concepts
GUI rendering thread and JS engine thread are mutually exclusive
Due to JavaScript's ability to manipulate DOM, if the interface is rendered while modifying these element attributes (i.e. JS thread and UI thread run simultaneously), the element data obtained before and after rendering may be inconsistent.
Therefore, in order to prevent unexpected results from rendering, the browser sets the GUI rendering thread and the JS engine to be mutually exclusive. When the JS engine executes, the GUI thread will be suspended,
GUI updates will be saved in a queue and executed immediately when the JS engine thread is idle.
JS blocking page loading
From the above mutual exclusion relationship, it can be inferred that if JS executes for too long, it will block the page.
For example, if the JS engine is performing a huge amount of computation, even if the GUI has updates, it will be saved to the queue and executed after the JS engine is idle.
Then, because of the huge amount of computing, the JS engine is likely to be idle for a long time. Naturally, you will feel that the too laggy is incomparable.
So, it is necessary to try to avoid JS execution time being too long, which can cause inconsistent page rendering and result in a feeling of page rendering loading blocking.
WebWorker, multi threading of JS?
As mentioned earlier, the JS engine is single threaded, and if the execution time of JS is too long, it will block the page. Is JS really powerless for CPU intensive computing?
So, later on, Web Worker was supported in HTML5.
The official explanation for MDN is:
Web Worker provides a simple way for web content to run scripts in backend threads. Threads can execute tasks without interfering with the user interface
A worker is an object created using a constructor (e.g. Worker()) to run a named JavaScript file
This file contains the code that will run in the worker thread; Workers run in another global context, different from the current window
Therefore, using the window shortcut to obtain the current global scope (instead of self) within a Worker will return an error
To understand this:
When creating a Worker, the JS engine applies to the browser to open a child thread (the child thread is opened by the browser, fully controlled by the main thread, and cannot operate the DOM)
JS engine threads communicate with worker threads through specific means (PostMessage API, which requires serialization of objects to interact with specific data)
So, if there is very time-consuming work, please open a separate Worker thread so that no matter how groundbreaking it is, it will not affect the main thread of the JS engine,
After calculating the results, communicate the results to the main thread, perfect!
Moreover, please note that the JS engine is single threaded, and the essence of this has not changed. Workers can understand it as a plug-in provided by the browser to the JS engine, specifically designed to solve those large-scale computing problems.
Other, the detailed explanation of Worker is not within the scope of this article, so it will not be further elaborated。

One more thing, why is the JS engine single threaded? Well, there shouldn't be a standard answer to this question, for example, it may just be because of the complexity of multithreading, such as the fact that multithreading operations typically require locking, so the initial design chose single threading...
The communication process between the Browser process and the browser kernel (Renderer process)
Seeing this, first of all, you should have a certain understanding of the processes and threads within the browser. Then, let's talk about how the browser's Browser process (control process) communicates with the kernel,
After understanding this point, one can connect the knowledge in this section and have a complete concept from beginning to end.
If you open the Task Manager yourself and then open a browser, you can see that there are two processes in the Task Manager (one is the main process, and the other is the rendering process that opens the Tab page),
Then, under this premise, take a look at the entire process: (greatly simplified)
The Browser process receives a user request and first needs to retrieve the page content (such as downloading resources through the network), then passes the task to the RenderHost interface
The Renderer interface of the Renderer process receives a message, provides a brief explanation, hands it over to the rendering thread, and then starts rendering
The rendering thread receives requests, loads web pages, and renders them, which may require the Browser process to obtain resources and the GPU process to assist in rendering
Of course, there may be JS threads operating the DOM (which may cause reflow and redrawing)
Finally, the Render process passes the results to the Browser process
The Browser process receives the result and draws it out
Here is a simple diagram: (very simplified)
After reading this entire process, you should have a certain understanding of the operation of the browser, so that with a foundation of knowledge architecture, it is convenient to fill in the content later on.
If we delve deeper into this topic, it will involve parsing the browser kernel source code, which is not within the scope of this article.
If you want to delve deeper into this area, it is recommended to read some browser kernel source code analysis articles, or you can first take a look at the first article in the reference source, which is well written
Sort out the relationships between threads in the browser kernel
At this point, we have gained an overall understanding of the operation of the browser. Next, let's briefly summarize some concepts
GUI rendering thread and JS engine thread are mutually exclusive
Due to JavaScript's ability to manipulate DOM, if the interface is rendered while modifying these element attributes (i.e. JS thread and UI thread run simultaneously), the element data obtained before and after rendering may be inconsistent.
Therefore, in order to prevent unexpected results from rendering, the browser sets the GUI rendering thread and the JS engine to be mutually exclusive. When the JS engine executes, the GUI thread will be suspended,
GUI updates will be saved in a queue and executed immediately when the JS engine thread is idle.
JS blocking page loading
From the above mutual exclusion relationship, it can be inferred that if JS executes for too long, it will block the page.
For example, if the JS engine is performing a huge amount of computation, even if the GUI has updates, it will be saved to the queue and executed after the JS engine is idle.
Then, because of the huge amount of computing, the JS engine is likely to be idle for a long time. Naturally, you will feel that the too laggy is incomparable.
So, it is necessary to try to avoid JS execution time being too long, which can cause inconsistent page rendering and result in a feeling of page rendering loading blocking.
WebWorker, multi threading of JS?
As mentioned earlier, the JS engine is single threaded, and if the execution time of JS is too long, it will block the page. Is JS really powerless for CPU intensive computing?
So, later on, Web Worker was supported in HTML5.
The official explanation for MDN is:
Web Worker provides a simple way for web content to run scripts in backend threads. Threads can execute tasks without interfering with the user interface
A worker is an object created using a constructor (e.g. Worker()) to run a named JavaScript file
This file contains the code that will run in the worker thread; Workers run in another global context, different from the current window
Therefore, using the window shortcut to obtain the current global scope (instead of self) within a Worker will return an error
To understand this:
When creating a Worker, the JS engine applies to the browser to open a child thread (the child thread is opened by the browser, fully controlled by the main thread, and cannot operate the DOM)
JS engine threads communicate with worker threads through specific means (PostMessage API, which requires serialization of objects to interact with specific data)
So, if there is very time-consuming work, please open a separate Worker thread so that no matter how groundbreaking it is, it will not affect the main thread of the JS engine,
After calculating the results, communicate the results to the main thread, perfect!
Moreover, please note that the JS engine is single threaded, and the essence of this has not changed. Workers can understand it as a plug-in provided by the browser to the JS engine, specifically designed to solve those large-scale computing problems.
Other, the detailed explanation of Worker is not within the scope of this article, so it will not be further elaborated

The above diagram roughly describes:
When the main thread runs, an execution stack is generated,
When the code in the stack calls certain APIs, they will add various events to the event queue (when the triggering conditions are met, such as Ajax request completion)
After the code in the stack is executed, it will read the events in the event queue and execute those callbacks
This cycle
Note that it is always necessary to wait for the code in the stack to execute before reading the events in the event queue
Let's talk about the timer separately
The core of the above event loop mechanism is: JS engine thread and event triggering thread
But there are also some hidden details in the event, such as how to wait for a specific time before adding them to the event queue after calling setTimeout?
Is it detected by the JS engine? Of course not. It is controlled by a timer thread (because the JS engine itself is too busy to be involved)
Why do we need a separate timer thread? Because JavaScript engines are single threaded, if they are in a blocked thread state, it will affect the accuracy of timing, so it is necessary to open a separate thread for timing.
When will timer threads be used? When using setTimeout or setInterval, it requires a timer thread to time, and after the timing is completed, specific events will be pushed into the event queue.
For example:
SetTimeout (function()){
Console. log ('hello! ');
}, 1000);
The purpose of this code is to push the callback function into the event queue and wait for the main thread to execute after the 1000 millisecond timer is completed (timed by the timer thread)
SetTimeout (function()){
Console. log ('hello! ');
}, 0);
Console. log ('begin ');
The effect of this code is to quickly push the callback function into the event queue and wait for the main thread to execute
Attention:
The execution result is: begin first and then hello!
Although the original intention of the code is to push the event queue after 0 milliseconds, W3C specifies in the HTML standard that time intervals below 4ms in setTimeout are considered 4ms.
(However, it is also said that different browsers have different minimum time settings)
Even if we don't wait for 4ms, even if we assume to push the event queue in 0 milliseconds, we will execute begin first (because only when the executable stack is empty will we actively read the event queue)
SetTimeout instead of setInterval
There is a difference between using setTimeout to simulate regular timing and directly using setInterval.
Because every time setTimeout is timed, it will be executed, and then it will continue to be executed for a period of time, resulting in an additional error in the middle
(The amount of error is related to the code execution time)
SetInterval, on the other hand, precisely pushes an event at intervals each time
(However, the actual execution time of an event may not be accurate, and it is also possible that the next event will arrive before the event is fully executed.)
And setInterval has some fatal issues:
The cumulative effect (mentioned above), if the setInterval code has not completed execution before (setInterval) is added to the queue again,
This will cause the timer code to run continuously several times without any gaps between them.
Even if executed at normal intervals, the code execution time for multiple setIntervals may be shorter than expected (because code execution requires a certain amount of time)
For example, in browsers such as iOS webview or Safari, there is a feature that does not execute JS when scrolling. If setInterval is used, it will be found that multiple JS accumulation callbacks will be executed after scrolling is completed. If the callback execution time is too long, it will cause container lag and some unknown errors (this section will be supplemented later, and setInterval comes with optimization, which will not add callbacks repeatedly)
And when minimizing browser display and other operations, setInterval does not mean that the program does not execute,
It will place the callback function of setInterval in the queue, and when the browser window opens again, it will execute it all in an instant
So, given all these issues, the best solution currently considered is to simulate setInterval using setTimeout, or directly use requestAnimationFrame in special situations
Supplement: As mentioned in the JS elevation, the JS engine will optimize setInterval. If there is a callback for setInterval in the current event queue, it will not be added repeatedly. However, there are still many issues...
Event loop advancement: macrotask and microtask
This paragraph refers to the second article (English version) from the reference source, which has been re described with my own understanding added,
It is strongly recommended that students with a foundation in English directly watch the original text. The author's description is very clear and the examples are also very good, as follows:
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
The JS event loop mechanism has been sorted out in the previous text, and it is sufficient in the case of ES5. However, in the current prevalence of ES6, there are still some problems to be encountered, such as the following question:
Console. log ('script start ');
SetTimeout (function()){
Console. log ('setTimeout ');
}, 0);
Promise. resolve(). then (function()){
Console. log ('promise1 ');
}. then (function()){
Console. log ('promise2 ');
});
Console. log ('script end ');
Hmm, the correct execution order is as follows:
Script start
Script end
Promise1
Promise2
SetTimeout
Why? Because there is a new concept in Promise: microtask
Alternatively, in JS, there are two types of tasks: macro task and micro task. In ECMAScript, micro task is called jobs, and macro task can be called task
What are their definitions? Difference? Simply put, it can be understood as follows:
Macrotask (also known as macro task) can be understood as a macro task that executes code every time it is executed on the stack (including obtaining an event callback from the event queue and placing it on the execution stack for execution)
Each task will be completed from start to finish, without executing any other tasks
In order to ensure the orderly execution of JS internal tasks and DOM tasks, the browser will re render the page after the completion of one task and before the start of the next task
( task>rendering>task>... )
Microtasks (also known as microtasks) can be understood as tasks that are executed immediately after the current task is completed
That is to say, after the current task, before the next task, before rendering
So its response speed is faster than setTimeout (setTimeout is a task) because there is no need to wait for rendering
That is to say, after a macro task is executed, all micro tasks generated during its execution will be executed (before rendering)
What are the different scenarios that create macro tasks and micro tasks?
Macrotask: Main code block, setTimeout, setInterval, etc. (as can be seen, each event in the event queue is a macrotask)
Microtask: Promise, process.nextTick, etc
__ Supplement: In the node environment, the priority of process.nextTick is higher than Promise_, It can be simply understood as: after the macro task ends, the nextTickQueue section in the micro task queue will be executed first, and then the Promise section in the micro task will be executed.
Reference: https://segmentfault.com/q/1010000011914016
Let's understand it based on the thread:
The events in macrotask are all placed in an event queue, which is maintained by the event triggering thread
All micro tasks in the micro task are added to the job queues and wait for the current macro task to complete execution, which is maintained by the JS engine thread
(This is inferred from one's own understanding and speculation, as it is seamlessly executed under the main process)
So, summarize the operating mechanism:
Execute a macro task (if not on the stack, retrieve it from the event queue)
If a microtask is encountered during execution, it is added to the task queue of the microtask
After the macro task is completed, immediately execute all the micro tasks in the current micro task queue (in sequence)
The current macro task is completed, the rendering is checked, and then the GUI thread takes over the rendering
After rendering, the JS thread continues to take over and starts the next macro task (obtained from the event queue)
As shown in the figure:
Also, please note the difference between Promise's polyfill and the official version:
In the official version, it is the standard microtask format
Polyfill is usually simulated through setTimeout, so it is in the form of macro task
Please pay special attention to these two differences
Note that some browsers may have different execution results (as they may have executed microtasks as macrotasks),
But for the sake of simplicity, I won't describe some non-standard browser scenarios here (but remember, some browsers may not be standard)
20180126 Supplement: Implementing microtasks using MutationObserver
MutationObserver can be used to implement microtasks
(It belongs to microtask and has a priority lower than Promise.),
This is usually done when Promise does not support it
It is a new feature in HTML5 that listens for a DOM change,
When there is any change in the DOM object tree, the Mutation Observer will be notified
Like in the previous Vue source code, it was used to simulate nextTick,
The specific principle is to create a TextNode and listen for changes in content,
Then when it comes to nextTick, change the text content of this node,
As follows: (Vue's source code, unmodified)
Var counter=1
Var observer=new MutationObserver (nextTickHandler)
Var textNode=document. createTextNode (String (counter))
Observer. observe (textNode{
CharacterData: true
})
TimerFunc=()=>{
Counter=(counter+1)% 2
TextNode. data=String (counter)
}
Corresponding Vue source code link
However, the nextTick implementation of Vue (2.5+) now removes MutationObserver (reportedly due to compatibility reasons),
Instead, use Message Channel
(Of course, the default is still Promise, which is not supported for compatibility).
Message Channel belongs to a macro task, with a priority of: Message Channel ->setTimeout,
So the nextTick inside Vue (2.5+) is different from the implementation before 2.4 and needs to be noted.
It doesn't unfold here, you can take a look https://juejin.im/post/5a1af88f5188254a701ec230
Written at the end of the sentence
Looking at this, I don't know if I have a better understanding of the operating mechanism of JS. It should be clearer to sort it out from beginning to end, rather than just a fragmented knowledge?
At the same time, it should also be noted that JS is not as simple as imagined, and the front-end knowledge is endless, with endless concepts, numerous forgettable knowledge points, and various frameworks
In terms of underlying principles, you can also dig infinitely deeper, and then you will find that you know too little...
In addition, this article also intends to conclude with a paragraph, and other concepts such as JS lexical parsing, executable context, and VO will not be further written in this article. In the future, a new article can be considered.
Finally, if you like it, please give it a like!

What can this article help you with?

  1. Understand the bidirectional data binding principle and core code modules of Vue
  2. Relieve curiosity while understanding how to achieve bidirectional binding
    In order to facilitate the explanation of the principle and implementation, the relevant code in this article is mainly extracted from the Vue source code and has been simplified and modified. It is relatively simple and does not take into account array processing, data circular dependencies, etc. It is also inevitable that there are some problems. We welcome everyone's correction. However, these will not affect everyone's reading and understanding. I believe that after reading this article, it will be more helpful for everyone when reading the Vue source code<
    All relevant codes in this article can be found on github https://github.com/DMQ/mvvm
    I believe everyone is familiar with the bidirectional binding of mvvm. A word of it doesn't fit the code. Let's take a look at the final implementation effect of this article, which is the same syntax as Vue. If you don't know about bidirectional binding yet, just stab Google

{{word}}

What can this article help you with?

  1. Understand the bidirectional data binding principle and core code modules of Vue
  2. Relieve curiosity while understanding how to achieve bidirectional binding
    In order to facilitate the explanation of the principle and implementation, the relevant code in this article is mainly extracted from the Vue source code and has been simplified and modified. It is relatively simple and does not take into account array processing, data circular dependencies, etc. It is also inevitable that there are some problems. We welcome everyone's correction. However, these will not affect everyone's reading and understanding. I believe that after reading this article, it will be more helpful for everyone when reading the Vue source code<
    All relevant codes in this article can be found on github https://github.com/DMQ/mvvm
    I believe everyone is familiar with the bidirectional binding of mvvm. A word of it doesn't fit the code. Let's take a look at the final implementation effect of this article, which is the same syntax as Vue. If you don't know about bidirectional binding yet, just stab Google
    DOM events, such as user input of text, clicking on a button, etc. (ng click)
    XHR Response Event ($http)
    Browser Location Change Event ($location)
    Timer event ($timeout, $interval)
    Execute $digest () or $apply ()
    Data hijacking: Vue.js adopts a combination of data hijacking and publisher subscriber mode, using Object. defineProperty() to hijack the setters and getters of various properties. When data changes, it publishes messages to subscribers and triggers corresponding listening callbacks.
    Train of thought organization
    It has been understood that Vue uses data hijacking to perform data binding, and the most core method is to hijack properties through Object. defineProperty() to achieve the purpose of monitoring data changes. Undoubtedly, this method is one of the most important and basic content in this article. If you are not familiar with defineProperty, please click here
    After organizing, in order to achieve bidirectional binding of mvvm, the following points must be implemented:
  3. Implement a data listener Observer that can listen to all attributes of data objects. If there are any changes, the latest values can be obtained and subscribers can be notified
  4. Implement an instruction parser Compile to scan and parse the instructions of each element node, replace data based on instruction templates, and bind corresponding update functions
  5. Implement a Watcher as a bridge between Observer and Compile, able to subscribe to and receive notifications of each attribute change, execute corresponding callback functions bound to instructions, and update views
  6. Mvvm entry function, integrating the above three
    The above process is shown in the figure:
    Picture description
  7. Implement Observer
    Okay, the ideas have been sorted out and the relevant logic and module functions have been clearly defined. Let's do it
    We know that we can use Obeject. defineProperty() to listen for property changes
    Then recursively traverse the data object that needs to be observed, including the attributes of the sub attribute object, with setters and getters added
    In this way, assigning a value to a certain value of this object will trigger a setter, and then it will be able to listen for data changes.. The relevant code can be as follows:

var data = {name: 'kindeng'};
observe(data);
data.name = 'dmq'; // Hahaha, a value change has been detected, kinding -->dmq

function observe(data) {

if (!data || typeof data !== 'object') {
    return;
}
// Retrieve all attribute traversal
Object.keys(data).forEach(function(key) {
    defineReactive(data, key, data[key]);
});

};

function defineReactive(data, key, val) {

observe(val); // Listening sub attributes
  Object. defineProperty (data, key{
  Enumerable: true,//enumerable
  Configurable: false,//cannot be defined again
    get: function() {
        return val;
    },
    set: function(newVal) {
        console.log('Hahaha, we have detected a change in value ', val, ' --> ', newVal);
        val = newVal;
    }
});

}
In this way, we can already listen for changes in each data, so how to notify subscribers after listening for changes? So, next we need to implement a message subscriber, which is very simple. We need to maintain an array to collect subscribers, and the data changes trigger notify, and then call the subscriber's update method. After the code improvement, it is as follows:
// ellipsis
function defineReactive(data, key, val) {

var dep = new Dep();
observe(val); // Listening sub attributes

Object.defineProperty(data, key, {
    // ... omit
    set: function(newVal) {
        if (val === newVal) return;
        console.log('Hahaha, we have detected a change in value ', val, ' --> ', newVal);
        val = newVal;
        dep.notify(); // Notify all subscribers
    }
});

}

function Dep() {

this.subs = [];

}
Dep.prototype = {

addSub: function(sub) {
    this.subs.push(sub);
},
notify: function() {
    this.subs.forEach(function(sub) {
        sub.update();
    });
}

};
So the question arises, who are the subscribers? How to add subscribers to the subscriber?
That's right, in the above thought organization, we have already made it clear that the subscriber should be a Watcher, and var dep=new Dep(); It is defined internally in the defineReactive method, so to add subscribers through dep, it is necessary to operate within the closure, so we can do something in the getter:
//Observer. js
// ellipsis
Object.defineProperty(data, key, {

get: function() {
    // Due to the need to add a watcher within the closure, a global target attribute is defined through Dep to temporarily store the watcher and remove it after adding it
    Dep.target && dep.addSub(Dep.target);
    return val;
}
// ... 省略

});

// Watcher.js
Watcher.prototype = {

get: function(key) {
    Dep.target = this;
    this.value = data[key];    // This will trigger the getter of the attribute to add a subscriber
    Dep.target = null;
}

}
We have already implemented an Observer here, which has the function of monitoring data and notifying subscribers of data changes. The complete code. So the next step is to implement Compile

  1. Implement Compile
    The main task of compile is to parse template instructions, replace variables in the template with data, initialize the rendering page view, bind update functions to the nodes corresponding to each instruction, add subscribers who listen to the data, and receive notifications to update the view once there is a change in the data, as shown in the figure:
    Picture description
    Because the process of traversing and parsing involves multiple operations on the dom node, in order to improve performance and efficiency, the following node el is first converted into a document fragment for parsing and compiling. After the parsing is completed, the fragment is added back to the original real dom node

function Compile(el) {

this.$el = this.isElementNode(el) ? el : document.querySelector(el);
if (this.$el) {
    this.$fragment = this.node2Fragment(this.$el);
    this.init();
    this.$el.appendChild(this.$fragment);
}

}
Compile.prototype = {

init: function() { this.compileElement(this.$fragment); },
node2Fragment: function(el) {
    var fragment = document.createDocumentFragment(), child;
    // 将原生节点拷贝到fragment
    while (child = el.firstChild) {
        fragment.appendChild(child);
    }
    return fragment;
}

};
The compileElement method will traverse all nodes and their child nodes, perform scanning, parsing, and compilation, call the corresponding instruction rendering function for data rendering, and call the corresponding instruction update function for binding. Please refer to the code and comments for details:

Compile.prototype = {

// ... omit
compileElement: function(el) {
    var childNodes = el.childNodes, me = this;
    [].slice.call(childNodes).forEach(function(node) {
        var text = node.textContent;
        var reg = /\{\{(.*)\}\}/;    //Expression Text
          //Compile as element nodes
        if (me.isElementNode(node)) {
            me.compile(node);
        } else if (me.isTextNode(node) && reg.test(text)) {
            me.compileText(node, RegExp.$1);
        }
        // Traverse Compile Child Nodes
        if (node.childNodes && node.childNodes.length) {
            me.compileElement(node);
        }
    });
},

compile: function(node) {
    var nodeAttrs = node.attributes, me = this;
    [].slice.call(nodeAttrs).forEach(function(attr) {
       //Regulation: Instructions are named with v-xxx
       //As in<span v-text="content"></span>, the instruction is v-text
        var attrName = attr.name;    // v-text
        if (me.isDirective(attrName)) {
            var exp = attr.value; // content
            var dir = attrName.substring(2);    // text
            if (me.isEventDirective(dir)) {
                // Event instruction v-on:click
                compileUtil.eventHandler(node, me.$vm, exp, dir);
            } else {
                // General instructions
                compileUtil[dir] && compileUtil[dir](node, me.$vm, exp);
            }
        }
    });
}

};

// Instruction processing set
var compileUtil = {

text: function(node, vm, exp) {
    this.bind(node, vm, exp, 'text');
},
// ...omit
bind: function(node, vm, exp, dir) {
    var updaterFn = updater[dir + 'Updater'];
    //Initializing the view for the first time
    UpdaterFn&&updaterFn (node, vm [exp]);
    //Instantiate the subscriber, which will add the subscriber watcher to the corresponding property message subscriber
     New Watcher (vm, exp, function (value, oldValue){
     //Once the attribute value changes, a notification will be received to execute this update function to update the view
        updaterFn && updaterFn(node, value, oldValue);
    });
}

};

// 更新函数
var updater = {

textUpdater: function(node, value) {
    node.textContent = typeof value == 'undefined' ? '' : value;
}
// ... Omitted

};
This ensures that every node and child node will be parsed and compiled through recursive traversal, including the text nodes declared by {{}} expressions. The declaration of instructions is marked by node attributes with specific prefixes, such as in<span v-text="content" other attr where v-text is an instruction, while other attr is not an instruction, but a regular attribute.
The processing of listening to data and binding update functions is done in the compileUtil. bind() method by adding a callback to the new Watcher() to receive notifications of data changes
At this point, a simple Compile is completed, complete with the code. Next, we need to take a look at the specific implementation of the Watcher subscriber

  1. Implement Watcher
    As a bridge for communication between Observer and Compile, Watcher subscribers mainly do the following:
  2. Adding oneself to the property subscriber (dep) during self instantiation
  3. Must have an update() method on its own
  4. When the attribute change dep.notice() is notified, the ability to call its own update() method and trigger the callback bound in Compile will result in success.
    If it's a bit messy, you can review the previous ideas and organize them
    function Watcher(vm, exp, cb) {
    this.cb = cb;
    this.vm = vm;
    this.exp = exp;
    // Here, in order to trigger the getter of the attribute and add oneself in the dep, combined with Observer, it is easier to understand
    this.value = this.get();
    }
    Watcher.prototype = {
    update: function() {

     this.run();    // Received notification of property value changes

    },
    run: function() {

     var value = this.get(); //Get the latest value 
     var oldVal = this.value;
     if (value !== oldVal) {
         this.value = value;
         this.cb.call(this.vm, value, oldVal); // Execute the callback bound in Compile and update the view
     }

    },
    get: function() {

     Dep.target = this;    //Point the current subscriber to oneself
     Var value=this. vm [exp]// Trigger getter to add oneself to the property subscriber
     Dep. target=null// Added, reset
     return value;

    }
    };
    // List Observer and Dep again here for easy understanding
    Object.defineProperty(data, key, {
    get: function() {

     // Due to the need to add a watcher within the closure, a global target attribute can be defined in Dep to temporarily store the watcher and remove it after adding it
     Dep.target && dep.addDep(Dep.target);
     return val;

    }
    // ... omit
    });
    Dep.prototype = {
    notify: function() {

     this.subs.forEach(function(sub) {
         sub.update(); // Call the subscriber's update method to notify changes
     });

    }
    };
    When instantiating a Watcher, call the get() method, mark the subscriber as the current Watcher instance through Dep. target=WatcherInstance, and forcibly trigger the getter method defined by the property. When the getter method is executed, it will add the current Watcher instance to the property's subscriber dep, so that when the property value changes, the WatcherInstance will receive an update notification.
    Okay, the Watcher has also been implemented, complete code.
    Basically, these are also the core modules related to data binding in Vue. Here, you can find the Vue source code in the src directory.
    Finally, let's talk about the relevant logic and implementation of MVVM entry files, which is relatively simple~

  5. Implementing MVVM
    MVVM serves as the entry point for data binding, integrating Observer, Compile, and Watcher. It listens to changes in its own model data through Observer, parses and compiles template instructions through Compile, and finally uses Watcher to build a communication bridge between Observer and Compile, achieving data transformation ->view update; The bidirectional binding effect of view interaction changes (input) ->data model changes.
    A simple MVVM constructor looks like this:

function MVVM(options) {

this.$options = options;
var data = this._data = this.$options.data;
observe(data, this);
this.$compile = new Compile(options.el || document.body, this)

}
When instantiating a Watcher, call the get() method, mark the subscriber as the current Watcher instance through Dep. target=WatcherInstance, and forcibly trigger the getter method defined by the property. When the getter method is executed, it will add the current Watcher instance to the property's subscriber dep, so that when the property value changes, the WatcherInstance will receive an update notification.
Okay, the Watcher has also been implemented, complete code.
Basically, these are also the core modules related to data binding in Vue. Here, you can find the Vue source code in the src directory.
Finally, let's talk about the relevant logic and implementation of MVVM entry files, which is relatively simple~

  1. Implementing MVVM
    MVVM serves as the entry point for data binding, integrating Observer, Compile, and Watcher. It listens to changes in its own model data through Observer, parses and compiles template instructions through Compile, and finally uses Watcher to build a communication bridge between Observer and Compile, achieving data transformation ->view update; The bidirectional binding effect of view interaction changes (input) ->data model changes.
    A simple MVVM constructor looks like this:
    function MVVM(options) {
    this.$options = options;
    var data = this._data = this.$options.data, me = this;
    // Attribute proxy, implementing vm.xxx -> vm._data.xxx
    Object.keys(data).forEach(function(key) {

     me._proxy(key);

    });
    observe(data, this);
    this.$compile = new Compile(options.el || document.body, this)
    }

MVVM.prototype = {

_proxy: function(key) {
    var me = this;
    Object.defineProperty(me, key, {
        configurable: false,
        enumerable: true,
        get: function proxyGetter() {
            return me._data[key];
        },
        set: function proxySetter(newVal) {
            me._data[key] = newVal;
        }
    });
}

};
The main method used here is Object. defineProperty(), which hijacks the read and write permissions of the properties of the VM instance object, causing the read and write of the properties of the VM instance to be converted to read and write of the VM_ The attribute values of data achieve a mixed effect, haha
At this point, all modules and functions have been completed, as promised at the beginning of this article. A simple MVVM module has been implemented, and most of its ideas and principles come from the simplified and modified Vue source code. You can see all the relevant code in this article here.
Due to the practicality of the content in this article, there is a large amount of code and it is not advisable to list lengthy code. Therefore, it is recommended that children's shoes who want to delve deeper can be read in conjunction with the source code in this article, which will be easier to understand and master.
summary
This article mainly elaborates on the principles and implementation of bidirectional binding around the modules of "Several Methods of Implementing Bidirectional Binding", "Implementing Observer", "Implementing Compile", "Implementing Watcher", and "Implementing MVVM". And gradually sorted out and explained some detailed ideas and key content points according to the process of thinking, as well as demonstrated how to gradually implement a bidirectional binding MVVM by displaying some key codes. There will definitely be some less rigorous thinking and errors in the article. We welcome everyone to correct them, and if you are interested, please discuss and improve together~
Finally, thank you for reading!

Recently updated on November 13, 2021

Records (mainly updated content):

[2021-11-13] There is a difference between adding or not adding async when returning the Promise object

When await is not waiting for Promise Like object

[2020-06-04] Explanation Promise. resolve ()

The async/await in JavaScript is a keyword in the AsyncFunction feature. So far, besides IE, commonly used browsers and nodes (v7.6+) have already supported this feature. The specific support situation can be viewed here.

The first time I saw the keywords' async/await 'was not in the JavaScript language, but in the syntax of C # 5.0. The async/await of C # needs to be used in versions 4.5 and above of the. NET Framework, so I was also saddened for a while - in order to be compatible with the XP system, the software we developed cannot use versions higher than 4.0 of the. NET Framework.
I previously discussed this issue in "Talking about Asynchronous Call Flattening". Whether in C # or JavaScript, async/await is a great feature, and they are also very sweet syntax sugars. The implementation of C #'s async/await cannot do without Task or Taskclasses, and the implementation of JavaScript's async/await cannot do without Promise.
Now, aside from C # and the. NET Framework, focus on studying JavaScript's async/await.

  1. What are async and await doing
    Any name is meaningful, let's first understand it literally. Async is the abbreviation for "asynchronous", while await can be considered as the abbreviation for async wait. So it should be well understood that async is used to declare a function to be asynchronous, while await is used to wait for an asynchronous method to complete execution.
    Another interesting syntax rule is that await can only appear in async functions. Then a careful friend may have a question, if await can only appear in async functions, how should this async function be called?
    If you need to call an async function through await, you must package another async function outside of this call, and then... enter an endless loop, never to emerge
    If the async function does not require await to be called, what exactly does async do?
    1.1. What does async do
    The key to this problem is how the async function handles its return value!
    We certainly hope that it can directly return the value we want through the return statement, but if that's the case, it seems like there's nothing wrong with waiting. So, write a piece of code to try and see what it will actually return:

async function testAsync() {

return "hello async";

}

const result = testAsync();
console.log(result);
When I saw the output, I suddenly realized that it was a Promise object.

c:\var\test> node --harmony_async_await .
Promise { 'hello async' }
1.3 So, the async function returns a Promise object. This information can also be obtained from the document. The async function (including function statements, function expressions, lambda expressions) returns a Promise object. If a direct quantity is returned in a function, async will encapsulate it as a Promise object through Promise. resolve ().
Supplementary knowledge points [2020 06 04]
Promise. resolve (x) can be seen as a shorthand for new Promise (resolve=>resolve (x)) and can be used to quickly encapsulate literal or other objects, encapsulating them as Promise instances.
The async function returns a Promise object, so in the case where the outermost layer cannot obtain its return value using await, we should naturally handle this Promise object in the original way: then() chain, like this
TestAsync(). then (v=>{
Console. log (v)// Output hello async
});
Looking back now, what if the async function does not return a value? It's easy to imagine that it will return Promise. resolve (undefined).
Imagine the characteristic of Promise - no waiting, so executing an async function without waiting will immediately execute, return a Promise object, and never block subsequent statements. This is no different from a regular function that returns a Promise object.
So the next key point is the await keyword.
1.2. What is await waiting for
Generally speaking, it is believed that await is waiting for an async function to complete. However, according to the syntax, await is waiting for an expression that evaluates to a Promise object or other value (in other words, without special restrictions).
Because the async function returns a Promise object, await can be used to wait for the return value of an async function - it can also be said that await is waiting for the async function, but it should be clear that it is actually waiting for a return value. Note that await is not only used to wait for Promise objects, but can also wait for the results of any expression. Therefore, await can actually be followed by ordinary function calls or direct variables. So the following example can completely run correctly

function getSomething() {

return "something";

}

async function testAsync() {

return Promise.resolve("hello async");

}

async function test() {

const v1 = await getSomething();
const v2 = await testAsync();
console.log(v1, v2);

}

test();
So, the async function returns a Promise object. This information can also be obtained from the document. The async function (including function statements, function expressions, lambda expressions) returns a Promise object. If a direct quantity is returned in a function, async will encapsulate it as a Promise object through Promise. resolve ().
Supplementary knowledge points [2020 06 04]
Promise. resolve (x) can be seen as a shorthand for new Promise (resolve=>resolve (x)) and can be used to quickly encapsulate literal or other objects, encapsulating them as Promise instances.
The async function returns a Promise object, so in the case where the outermost layer cannot obtain its return value using await, we should naturally handle this Promise object in the original way: then() chain, like this
TestAsync(). then (v=>{
Console. log (v)// Output hello async
});
Looking back now, what if the async function does not return a value? It's easy to imagine that it will return Promise. resolve (undefined).
Imagine the characteristic of Promise - no waiting, so executing an async function without waiting will immediately execute, return a Promise object, and never block subsequent statements. This is no different from a regular function that returns a Promise object.
So the next key point is the await keyword.
1.2. What is await waiting for
Generally speaking, it is believed that await is waiting for an async function to complete. However, according to the syntax, await is waiting for an expression that evaluates to a Promise object or other value (in other words, without special restrictions).
Because the async function returns a Promise object, await can be used to wait for the return value of an async function - it can also be said that await is waiting for the async function, but it should be clear that it is actually waiting for a return value. Note that await is not only used to wait for Promise objects, but can also wait for the results of any expression. Therefore, await can actually be followed by ordinary function calls or direct variables. So the following example can completely run correctly

function takeLongTime() {

return new Promise(resolve => {
    setTimeout(() => resolve("long_time_value"), 1000);
});

}

takeLongTime().then(v => {

console.log("got", v);

});
What if we switch to async/await, it would be like this

function takeLongTime() {

return new Promise(resolve => {
    setTimeout(() => resolve("long_time_value"), 1000);
});

}

async function test() {

const v = await takeLongTime();
console.log(v);

}

test();
The sharp eyed student has noticed that takeLongTime() has not been declared as asynchronous. In fact, takeLongTime() itself is the Promise object returned, and the result is the same with or without async. If you don't understand, please go back and see "what async does" above.
Supplement on November 13, 2021
If a function itself returns a Promise object, there is still a slight difference between adding async and not adding async: after adding async, the Promise object obtained externally is not the one that returns. Please refer to the code:

(() => {

let promise;
async function test() {
    promise = new Promise(resolve => resolve(0));
    promise.mark = "hello";
    return promise;
}

const gotPromise = test();
console.log(`is same object?: ${promise === gotPromise}`);  // false
console.log(`promise.mark: ${promise.mark}`);               // hello
console.log(`gotPromise.mark: ${gotPromise.mark}`);         // undefined

})();
After understanding this, if we need to attach something to the returned Promise object, such as cancel(), we need to be careful.
Another question arises. The difference between these two pieces of code in handling asynchronous calls (actually handling Promise objects) is not significant, and even using async/await requires writing more code. What are its advantages?
2.2. The advantage of async/await lies in handling then chains
A single Promise chain cannot discover the advantages of async/await, but when dealing with then chains composed of multiple Promises, the advantages can be reflected (interestingly, Promise uses then chains to solve the problem of multi-layer callbacks, and now uses async/await to further optimize it).
Assuming a business is completed in multiple steps, each step being asynchronous and dependent on the results of the previous step. We still use setTimeout to simulate asynchronous operations:
/**
*Pass in the parameter n, which represents the execution time of this function in milliseconds
*The result of execution is n+200, which will be used for the next step
*/
function takeLongTime(n) {

return new Promise(resolve => {
    setTimeout(() => resolve(n + 200), n);
});

}

function step1(n) {

console.log(`step1 with ${n}`);
return takeLongTime(n);

}

function step2(n) {

console.log(`step2 with ${n}`);
return takeLongTime(n);

}

function step3(n) {

console.log(`step3 with ${n}`);
return takeLongTime(n);

}
Now use the Promise method to implement the processing of these three steps

function doIt() {

console.time("doIt");
const time1 = 300;
step1(time1)
    .then(time2 => step2(time2))
    .then(time3 => step3(time3))
    .then(result => {
        console.log(`result is ${result}`);
        console.timeEnd("doIt");
    });

}

doIt();

// c:\var\test>node --harmony_async_await .
// step1 with 300
// step2 with 500
// step3 with 700
// result is 900
// doIt: 1507.251ms
The output result is the parameter 700+200=900 of step3(). DoIt() executed three steps in sequence, taking a total of 300+500+700=1500 milliseconds, which is consistent with the results calculated by console. time()/console. timeEnd().
What if using async/await to implement it would be like this

async function doIt() {

console.time("doIt");
const time1 = 300;
const time2 = await step1(time1);
const time3 = await step2(time2);
const result = await step3(time3);
console.log(`result is ${result}`);
console.timeEnd("doIt");

}

doIt();
The result is the same as the previous Promise implementation, but does this code look much clearer, almost identical to synchronous code
2.3. There are even cooler ones
Now, to change the business requirements, there are still three steps, but each step requires the results of each previous step.

function step1(n) {

console.log(`step1 with ${n}`);
return takeLongTime(n);

}

function step2(m, n) {

console.log(`step2 with ${m} and ${n}`);
return takeLongTime(m + n);

}

function step3(k, m, n) {

console.log(`step3 with ${k}, ${m} and ${n}`);
return takeLongTime(k + m + n);

}
This time, use async/await to write:

async function doIt() {

console.time("doIt");
const time1 = 300;
const time2 = await step1(time1);
const time3 = await step2(time1, time2);
const result = await step3(time1, time2, time3);
console.log(`result is ${result}`);
console.timeEnd("doIt");

}

doIt();

// c:\var\test>node --harmony_async_await .
// step1 with 300
// step2 with 800 = 300 + 500
// step3 with 1800 = 300 + 500 + 1000
// result is 2000
// doIt: 2907.387ms
Apart from feeling that the execution time has increased, it seems no different from the previous example! Don't worry, think carefully. What would it look like if it were implemented in Promise mode?

function doIt() {

console.time("doIt");
const time1 = 300;
step1(time1)
    .then(time2 => {
        return step2(time1, time2)
            .then(time3 => [time1, time2, time3]);
    })
    .then(times => {
        const [time1, time2, time3] = times;
        return step3(time1, time2, time3);
    })
    .then(result => {
        console.log(`result is ${result}`);
        console.timeEnd("doIt");
    });

}

doIt();
Do you feel a bit complicated? That bunch of parameter processing is the dead end of the Promise scheme - parameter transfer is too cumbersome, it makes me dizzy!!