Vue 3 REF syntaxic Sugar, adieu.value Writing

Wuliu 2021-09-15 09:32:14
vue ref syntaxic sugar adieu.value


Préface

À court terme,Vue3 J'en ai parlé. Ref Sugar De RFC,C'est - à - dire: ref Sucre grammatical,Il s'agit également de(Experimental)Phase.In RFC Motivation(Motivation)Moyenne,Evan You Introduction à Composition API Après l'introduction,L'un des principaux problèmes non résolus est refs Et reactive Utilisation des objets.Et l'utiliser partout .value Ça pourrait être difficile.,Si le système de type n'est pas utilisé,Et il sera facile de manquer:

let count = ref(1)
function add() {
count.value++
}

Alors...,Certains utilisateurs préfèrent utiliser uniquement reactive,De cette façon, il n'est pas nécessaire d'utiliser refs De .value Questions.Et ref Le sucre grammatical est utilisé pour ref Lors de la création de variables réactives,Les variables elles - mêmes peuvent être obtenues et modifiées directement,Au lieu d'utiliser .value Pour obtenir et modifier les valeurs correspondantes.En termes simples,Au niveau de l'utilisation,On peut dire adieu à l'utilisation refs Heure .value Questions:

let count = $ref(1)
function add() {
count++
}

Alors,ref Comment syntaxic Sugar est - il actuellement utilisé dans le projet? Comment cela a - t - il été réalisé? ? C'est la première fois que je vois ça. RFC Questions posées ,Je crois que c'est aussi une question que beaucoup d'étudiants ont.Alors..., Maintenant, nous allons le découvrir un par un. .

1 Ref Utilisation du sucre grammatical dans les projets

Parce que ref Le sucre grammatical est encore expérimental (Experimental)Phase,Donc, dans Vue3 Ne sera pas pris en charge par défaut ref Sucre grammatical.Alors, Ici, nous utilisons Vite + Vue3 Exemple de développement de projets , Regarde comment ça marche. ref Prise en charge du sucre grammatical .

En service Vite + Vue3 Au moment de l'élaboration du projet,C'est par @vitejs/plugin-vue Plug - in pour implémenter la paire .vue Conversion de code pour les fichiers (Transform)、Mise à jour thermique(HMR)Attendez..Alors...,Nous avons besoin de vite.config.js À mi - chemin. @vitejs/plugin-vue Options pour les plug - ins (Options)Entrée refTransform: true

// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue({
refTransform: true
})]
})

Alors,C'est comme ça. @vitejs/plugin-vue L'intérieur du plug - in est basé sur les options entrantes refTransform La valeur de ref Sucre grammatical pour une conversion de code spécifique .Parce que,Ici, nous mettons en place true, Apparemment, c'est vrai. ref Syntaxic Sugar effectue une conversion de code spécifique .

Et voilà.,On pourrait juste .vue Utilisation dans les fichiers ref Sucre grammatical, Voici un exemple simple :

<template>
<div>{{count}}</div>
<button @click="add">click me</button>
</template>
<script setup>
let count = $ref(1)
function add() {
count++
}
</script>

Rendu correspondant à la page :

Je vois.,On peut utiliser ref La façon dont le sucre grammatical crée des variables réactives, Sans penser à l'utiliser. .value La question de.En outre,ref Le sucre grammatical supporte également d'autres types d'écriture ,Les recommandations personnelles sont présentées ici $ref De la façon dont, Les étudiants intéressés peuvent y aller. RFC En savoir plus .

Alors,Après avoir appris ref Après l'utilisation du sucre grammatical dans le projet , Nous avons répondu à la première question. ( Comment utiliser dans un projet ).En bas, On va répondre à la deuxième question. , Comment cela a - t - il été réalisé? ,C'est ce qui a été fait dans le code source?

2 Ref Réalisation du sucre grammatical

Tout d'abord,,Nous passons Vue Playground Pour sentir intuitivement ,Devant ref Dans l'exemple du sucre grammatical <script setup> Bloc(Block) Résultats après Compilation :

import { ref as _ref } from 'vue'
const __sfc__ = {
setup(__props) {
let count = _ref(1)
function add() {
count.value++
}
}

Je vois., Bien que nous utilisions ref Le sucre grammatical n'a pas besoin d'être manipulé .value, Mais il a été compilé Toujours utilisé .value.Alors, Ce processus n'est certainement pas inévitable. Compiler les dépendances Traitement de conversion de code pour .Parce que, Nous devons trouver un moyen de l'utiliser $ref Déclarations et variables pour , Remplacer le premier par _ref, Ajouter à ce dernier .value.

Et devant , Nous avons également mentionné @vitejs/plugin-vue Le plug - in aura raison .vue Fichier pour la conversion de code , Ce processus est utilisé Vue3 Fourni @vue/compiler-sfc Sac(Package), Il fournit respectivement <script><template><style> Fonctions liées à la compilation de blocs égaux .

Alors, Il est clair que nous devons nous concentrer ici <script> Fonctions liées à la compilation de blocs , Cela correspond à @vue/compiler-sfc Dans compileScript() Fonctions.

2.1 compileScript() Fonctions

compileScript() Définition de la fonction vue-next De packages/compiler-sfc/src/compileScript.ts Dans le document, Il est principalement responsable <script> Ou <script setup> Compilation du contenu du bloc , Il recevra 2 Paramètres:

  • sfc Contient .vue Contenu du fichier après analyse du Code ,Contient scriptscriptSetupsource Propriétés égales
  • options Contient des attributs optionnels et obligatoires , Par exemple, le composant correspondant scopeId Sera options.id、Comme mentionné précédemment refTransform Attendez.

compileScript() Définition de la fonction(Pseudo - Code):

// packages/compiler-sfc/src/compileScript.ts
export function compileScript(
sfc: SFCDescriptor,
options: SFCScriptCompileOptions
): SFCScriptBlock {
// ...
return {
...script,
content,
map,
bindings,
scriptAst: scriptAst.body
}
} 

Pour ref Grammatical Sugar ,compileScript() La fonction obtient d'abord les options (Option)Moyenne refTransform Valeur de,Et assigner une valeur à enableRefTransform

const enableRefTransform = !!options.refTransform

enableRefTransform Il est ensuite utilisé pour déterminer si ref Fonctions de conversion grammaticales liées au sucre .Alors, Nous avons également mentionné l'utilisation ref Sucre grammatical, Il faut d'abord le donner. @vite/plugin-vue Option plug - in refTransform La propriété est définie à true, Il sera transmis compileScript() Fonction options,C'est ici que options.refTransform.

Et voilà.,Ça vient de sfc Déconstruction moyenne scriptSetupsourcefilename Propriétés égales.Parmi eux, Utilise d'abord la chaîne de Code du fichier source source Créer un MagicString Exemple s,Il est principalement utilisé lors de la conversion ultérieure du Code Remplacer la chaîne de code source 、 Ajouter, etc. ,Et il appelle parse() Fonction pour analyser <script setup> Le contenu de,C'est - à - dire: scriptSetup.content, Pour générer l'Arbre syntaxique abstrait correspondant scriptSetupAst

let { script, scriptSetup, source, filename } = sfc
const s = new MagicString(source)
const startOffset = scriptSetup.loc.start.offset
const scriptSetupAst = parse(
scriptSetup.content,
{
plugins: [
...plugins,
'topLevelAwait'
],
sourceType: 'module'
},
startOffset
)

Et parse() Est utilisé à l'intérieur de la fonction @babel/parser Fourni parser Méthode pour analyser le Code et générer le AST. Pour notre exemple ci - dessus ,Produit AST Ce sera comme ça:

{
body: [ {...}, {...} ],
directives: [],
end: 50,
interpreter: null,
loc: {
start: {...},
end: {...},
filename: undefined,
identifierName: undefined
},
sourceType: 'module',
start: 0,
type: 'Program'
}
Attention!,Omis ici bodystartend Contenu de

Et puis, Sera défini précédemment enableRefTransform Et des appels shouldTransformRef() Valeur de retour de la fonction(true Ou false) Pour déterminer si oui ou non ref Conversion de code pour le sucre grammatical .Si, Conversion requise ,Est appelé transformRefAST() Fonction basée sur AST Pour effectuer la conversion de code appropriée :

if (enableRefTransform && shouldTransformRef(scriptSetup.content)) {
const { rootVars, importedHelpers } = transformRefAST(
scriptSetupAst,
s,
startOffset,
refBindings
)
}

À l'avant., Nous avons présenté enableRefTransform. Voyons voir. shouldTransformRef() Fonctions,Il correspond principalement au contenu du Code par des correspondances régulières scriptSetup.content Pour déterminer si elle a été utilisée ref Sucre grammatical:

// packages/ref-transform/src/refTransform.ts
const transformCheckRE = /[^\w]\$(?:\$|ref|computed|shallowRef)?\(/
export function shouldTransform(src: string): boolean {
return transformCheckRE.test(src)
}

Alors..., Quand vous avez spécifié refTransform Pour true,Mais votre code n'est pas vraiment utilisé pour ref Sucre grammatical, Est en cours de compilation <script> Ou <script setup> Dans le processus de Non.Et ref Opérations de conversion de code liées au sucre syntaxique ,Et ça aussi. Vue3 Considérez les détails ,Évitez les frais généraux de performance liés aux opérations inutiles de conversion de code.

Alors, Pour notre exemple, (Utilisé ref Sucre grammatical), Ça va frapper. transformRefAST() Fonctions.Et transformRefAST() La fonction correspond à packages/ref-transform/src/refTransform.ts Dans transformAST() Fonctions.

Alors...,Voyons voir. transformAST() Comment la fonction est basée sur AST C'est ça. ref Code grammatical lié au sucre pour l'opération de conversion.

2.2 transformAST() Fonctions

In transformAST() La fonction traverse principalement le code original entrant correspondant à AST,Qui est ensuite généré en manipulant la chaîne de code source MagicString Exemple s Pour effectuer une transformation spécifique du code source , Par exemple, réécriture $ref Pour _ref、Ajouter .value Attendez..

transformAST() Définition de la fonction(Pseudo - Code):

// packages/ref-transform/src/refTransform.ts
export function transformAST(
ast: Program,
s: MagicString,
offset: number = 0,
knownRootVars?: string[]
): {
// ...
walkScope(ast)
(walk as any)(ast, {
enter(node: Node, parent?: Node) {
if (
node.type === 'Identifier' &&
isReferencedIdentifier(node, parent!, parentStack) &&
!excludedIds.has(node)
) {
let i = scopeStack.length
while (i--) {
if (checkRefId(scopeStack[i], node, parent!, parentStack)) {
return
}
}
}
}
})
return {
rootVars: Object.keys(rootScope).filter(key => rootScope[key]),
importedHelpers: [...importedHelpers]
}
}

Je vois. transformAST() Sera appelé en premier walkScope() Pour gérer la portée racine (root scope),Puis appelez walk() Les fonctions sont traitées couche par couche AST Noeud,Et ici walk() La fonction est utilisée Rich Haris C'est écrit estree-walker.

En bas,Regardons ça séparément. walkScope() Et walk() Que fait la fonction .

walkScope() Fonctions

Tout d'abord,, Regardons d'abord l'utilisation précédente. ref Déclaration de sucre grammatical let count = $ref(1) Correspondant AST Structure:

Je vois. let De AST Type de noeud type Oui. VariableDeclaration, Le reste du Code correspond à AST Les noeuds sont placés dans declarations Moyenne.Parmi eux,Variables count De AST Le noeud sera utilisé comme declarations.id ,Et $ref(1) De AST Le noeud sera utilisé comme declarations.init.

Alors,Retour walkScope() Fonctions,Il sera basé sur AST Type de noeud type Effectuer un traitement spécifique , Pour notre exemple let Correspondant AST Noeud type Pour VariableDeclaration Ça va frapper cette logique. :

function walkScope(node: Program | BlockStatement) {
for (const stmt of node.body) {
if (stmt.type === 'VariableDeclaration') {
for (const decl of stmt.declarations) {
let toVarCall
if (
decl.init &&
decl.init.type === 'CallExpression' &&
decl.init.callee.type === 'Identifier' &&
(toVarCall = isToVarCall(decl.init.callee.name))
) {
processRefDeclaration(
toVarCall,
decl.init as CallExpression,
decl.id,
stmt
)
}
}
}
}
}

Ici. stmt Oui. let Correspondant AST Noeud, Et vous traverserez stmt.declarations,Parmi eux decl.init.callee.name Ça veut dire $ref, Puis l'appel isToVarCall() Fonction et assigner une valeur à toVarCall.

isToVarCall() Définition de la fonction:

// packages/ref-transform/src/refTransform.ts
const TO_VAR_SYMBOL = '$'
const shorthands = ['ref', 'computed', 'shallowRef']
function isToVarCall(callee: string): string | false {
if (callee === TO_VAR_SYMBOL) {
return TO_VAR_SYMBOL
}
if (callee[0] === TO_VAR_SYMBOL && shorthands.includes(callee.slice(1))) {
return callee
}
return false
}

Comme nous l'avons mentionné précédemment, ref Le sucre grammatical peut supporter d'autres styles ,Parce que nous utilisons $ref De la façon dont, Donc ça va frapper ici. callee[0] === TO_VAR_SYMBOL && shorthands.includes(callee.slice(1)) La logique de,C'est - à - dire: toVarCall Sera assigné comme $ref.

Et puis,Appelle processRefDeclaration() Fonctions, Il sera basé sur decl.init Informations de localisation fournies Pour correspondre au code source MagicString Exemple s Exécution des opérations,Bientôt $ref Remplacer par ref

// packages/ref-transform/src/refTransform.ts
function processRefDeclaration(
method: string,
call: CallExpression,
id: VariableDeclarator['id'],
statement: VariableDeclaration
) {
// ...
if (id.type === 'Identifier') {
registerRefBinding(id)
s.overwrite(
call.start! + offset,
call.start! + method.length + offset,
helper(method.slice(1))
)
}
// ...
}
Les informations de localisation se réfèrent à AST Emplacement du noeud dans le code source ,En général, on utilise startend Représentation,Par exemple, let count = $ref(1),Alors count Correspondant AST Node start Oui. 4、 end Oui. 9.

Parce que,En ce moment id Ce qui correspond count De AST Noeud, Ça va être comme ça. :

{
type: "Identifier",
start: 4,
end: 9,
name: "count"
}

Alors..., Ça va frapper. id.type === 'Identifier' La logique de.Tout d'abord,,Appelle registerRefBinding() Fonctions, Il est en fait appelé registerBinding(),Et registerBinding Il sera là. Champ d'application actuel currentScope Lier la variable id.name Et réglé à true , Ça veut dire que c'est une utilisation ref Variables créées par le sucre syntaxique ,Ceci est ensuite utilisé pour déterminer si une variable est ajoutée à .value

const registerRefBinding = (id: Identifier) => registerBinding(id, true)
function registerBinding(id: Identifier, isRef = false) {
excludedIds.add(id)
if (currentScope) {
currentScope[id.name] = isRef
} else {
error(
'registerBinding called without active scope, something is wrong.',
id
)
}
}

Je vois.,In registerBinding() Je te le donnerai. excludedIds Ajouter ceci AST Noeud,Et excludeIds C'est un WeekMap, Il sera utilisé pour les sauts ultérieurs qui n'ont pas besoin d'être effectués ref Le type de traitement grammatical du sucre est Identifier De AST Noeud.

Et puis,Appelle s.overwrite() La fonction $ref Remplacer par _ref, Il recevra 3 Paramètres, Est le début de la réécriture 、Position finale et chaîne à réécrire.Et call Correspond à $ref(1) De AST Noeud, Ça va être comme ça. :

{
type: "Identifier",
start: 12,
end: 19,
callee: {...}
arguments: {...},
optional: false
}

Et,Je pense qu'il est important de noter qu'il a été utilisé pour calculer la position de départ de la réécriture offset,Il représente la chaîne qui fonctionne maintenant dans la chaîne sourcePosition offset,Par exemple, le début de la chaîne dans la chaîne source, Alors l'offset sera 0.

Et helper() La fonction renvoie la chaîne _ref, Et dans ce processus, ref Ajouter à importedHelpers Moyenne,Ce sera compileScript() Utilisé pour générer le import Déclarations:

function helper(msg: string) {
importedHelpers.add(msg)
return `_${msg}`
}

Alors, C'est fait ici. $ref À _ref Réécriture de,C'est comme ça qu'on va coder:

let count = _ref(1)
function add() {
count++
}

Et voilà.,C'est par walk() La fonction count++ Convertir en count.value++.En bas,Voyons voir walk() Fonctions.

walk() Fonctions

Devant., Nous avons mentionné walk() La fonction utilise Rich Haris C'est écrit estree-walker, Il s'agit d'une correspondance transversale ESTree Standard AST Sac(Package).

walk() C'est ce qui se passe avec les fonctions :

import { walk } from 'estree-walker'
walk(ast, {
enter(node, parent, prop, index) {
// ...
},
leave(node, parent, prop, index) {
// ...
}
});

Je vois.,walk() Peut passer dans la fonction options,Parmi eux enter() À chaque visite AST Le noeud est appelé ,leave() C'est parti AST Appelé lorsque le noeud .

Alors, Retour à l'exemple précédent ,walk() La fonction fait principalement cela 2 Une chose.:

1.Entretien scopeStack、parentStack Et currentScope

scopeStack Pour le stockage en ce moment AST Chaîne de portée où se trouve le noeud , Le Haut de la pile est initialement la portée racine rootScope;parentStack Pour stocker la traversée AST Ancêtre dans le processus nodal AST Noeud(En haut de la pile AST Le noeud est courant AST Le père du noeud AST Noeud);currentScope Pointer vers la portée actuelle , Est égal à la portée racine au départ rootScope

const scopeStack: Scope[] = [rootScope]
const parentStack: Node[] = []
let currentScope: Scope = rootScope

Alors...,In enter() La phase de AST Si le type de noeud est une fonction 、Bloc, C'est vrai. En pile scopeStack

parent && parentStack.push(parent)
if (isFunctionType(node)) {
scopeStack.push((currentScope = {}))
// ...
return
}
if (node.type === 'BlockStatement' && !isFunctionType(parent!)) {
scopeStack.push((currentScope = {}))
// ...
return
}

Et puis,In leave() En ce moment AST Si le type de noeud est une fonction 、Bloc, C'est vrai. Hors de la pile scopeStack,Et mis à jour currentScope Après la pile scopeStack L'élément supérieur de la pile:

parent && parentStack.pop()
if (
(node.type === 'BlockStatement' && !isFunctionType(parent!)) ||
isFunctionType(node)
) {
scopeStack.pop()
currentScope = scopeStack[scopeStack.length - 1] || null
}

2.Traitement Identifier Type AST Noeud

Parce que,Dans notre cas ref Création de sucre grammatical count Variable AST Le type de noeud est Identifier, Donc ça va être enter() C'est logique. :

if (
node.type === 'Identifier' &&
isReferencedIdentifier(node, parent!, parentStack) &&
!excludedIds.has(node)
) {
let i = scopeStack.length
while (i--) {
if (checkRefId(scopeStack[i], node, parent!, parentStack)) {
return
}
}
}

In if Dans son jugement,Pour excludedIds On l'a déjà vu. ,Et isReferencedIdentifier() C'est par parenStack Pour déterminer si le type actuel est Identifier De AST Noeud node Est - ce une référence à un AST Noeud.

Et puis, Par la visite scopeStack Pour déterminer s'il y a id.name(Nom de la variable count) La propriété et la valeur de la propriété sont true, Cela signifie qu'il est utilisé ref Variables créées par le sucre syntaxique , L'opération se termine par ss.appendLeft) Pour ajouter à cette variable .value

function checkRefId(
scope: Scope,
id: Identifier,
parent: Node,
parentStack: Node[]
): boolean {
if (id.name in scope) {
if (scope[id.name]) {
// ...
s.appendLeft(id.end! + offset, '.value')
}
return true
}
return false
}

Conclusion

En comprenant ref Réalisation du sucre grammatical ,Je pense que vous devriez avoir une compréhension différente du terme sucre grammatical,Son essence est de traverser la phase de compilation AST Pour effectuer des opérations spécifiques de conversion de code .Et, Quelques outils pour ce processus de mise en œuvre (Package) C'est aussi très ingénieux. ,Par exemple MagicString Chaîne de code source d'action 、estree-walker Traversée AST Traitement des noeuds et des champs d'application, etc. .

Enfin,S'il y a quelque chose de mal ou d'incorrect dans le texte, Bienvenue à tous. Issue ~

- Oui.

En lisant cet article, S'il y a des gains ,C'est bon.Fais - moi plaisir., C'est ce qui me motive à continuer à partager ,Merci beaucoup.~

Je suis Wuliu. , Comme l'innovation 、 Code source du tambour , Se concentrer sur le code source (Vue3、Vite)、Ingénierie frontale、 Apprentissage et partage de technologies de bout en bout .En outre, Tous mes articles seront inclus dans https://github.com/WJCHumble/Blog,Bienvenue Watch Or Star!
版权声明
本文为[Wuliu]所创,转载请带上原文链接,感谢
https://qdmana.com/2021/09/20210915091722185F.html

  1. Vue learning -- watch listener
  2. Learn more about nexttick in Vue
  3. JavaScript genrator generator
  4. La dernière réponse à l'entrevue de développement Android, l'hiver froid de l'industrie
  5. Maserati's motorcycle has less than 10 in the world. It is definitely a work of art
  6. 2021 partage des questions du dernier examen écrit d'entrevue Android, pas d'accord
  7. Programmation asynchrone Java scirp, développement frontal de base
  8. 2021 dernier examen écrit d'entrevue Android, écrit trop bien
  9. Quels aspects doivent être pris en considération dans le tableau principal du distributeur libre - service?
  10. He inherited his mother's hundreds of millions of property for his boyfriend to squander. Unexpectedly, he was ruthlessly abandoned when he had 100 yuan left
  11. The fuel cost is half less than that of fuel vehicles at the same level. Is it really cheap to use song Pro DM?
  12. Le dernier résumé de l'expérience d'entrevue d'embauche de l'école Android de l'usine est nécessaire pour l'usine
  13. Le dernier dictionnaire avancé de programmeurs d'usine, l'expérience d'entrevue d'embauche de l'Agence de développement Android
  14. La dernière collection d'entrevues Android Golden nine Silver ten
  15. L'expérience d'entrevue de l'Ingénieur d'algorithme de saut d'octets, 2 mois d'entrevue Tencent, station B, Netease et ainsi de suite sur 11 entreprises résumé!
  16. La dernière collection d'entrevues d'Android Golden nine Silver ten recommande un projet github
  17. Yuan Li's recent situation revealed that he was obsessed with public welfare, dressed simply and fearless, grew fat, and married an 11-year-old husband
  18. Initial experience of template tool plop of [front end Engineering]
  19. Dernière question d'entrevue avancée et réponse d'Alibaba Android, Alibaba P8 vous apprendra en personne
  20. Partage des dernières questions d'entrevue pour Android haute fréquence, Introduction aux compétences d'entrevue pour le développement d'Android
  21. Partager les dernières questions d'entrevue Android haute fréquence avec quelques conseils
  22. About JavaScript modules
  23. Iteratable object and class arrays
  24. Function realization of Vue elementui exporting excel form
  25. Use canvas to realize a small screenshot function
  26. Object oriented programming (2)
  27. Several common value transfer methods between Vue components
  28. Démarrer avec le serveur de base zéro: Hello World
  29. J'a I construit un escalier pour aller sur la lune, combien de façons puis - je poursuivre la sœur Chang'e?
  30. CSS implémente la fonction d'expansion et d'arrimage du Texte multiligne
  31. Varlet CLI | vue3 Component Library Quick Prototyping Tool
  32. Belle vue sur les trois rivières Xiapu
  33. La partie Web qui déploie SharePoint ajoute son propre module de fonctionnalité
  34. React Native (mise à jour à long terme)
  35. La conception et le codage de l'arbre binaire requis pour chaque entrevue d'embauche de la société Java millet;
  36. 10 jours pour obtenir l'offre d'emploi Android d'Alibaba, entrevue Android
  37. A remporté avec succès Byte, Tencent, Pulse offer, 7 ans Java une expérience d'entrevue de baise,
  38. 10大前端常用算法,web应用与开发
  39. Nginx - minimum configuration! You deserve it
  40. Les questions d'entrevue couramment utilisées pour le cadre Java sont - elles prometteuses pour le développement Java?
  41. 10 algorithmes communs de première ligne, applications Web et développement
  42. 10大前端常用算法,移动web开发教程
  43. 10大前端常用算法,新手学web前端开发
  44. After brushing: wireless routing + USB offline Bt + remote management!
  45. Le vrai problème de l'entrevue Android d'Alibaba pour les années civiles 2019 - 2021, le tutoriel d'apprentissage Android
  46. 10 principaux algorithmes de première ligne couramment utilisés, nouveaux apprenants développement de première ligne Web
  47. 118页Vue面试题总结,web中间开发
  48. 118页Vue面试题总结,HTML列表标签
  49. Use vscode snippets to work with project members to improve development efficiency
  50. CentOS + Jenkins + nginx + gitlab front end automation deployment full record
  51. Build applet architecture from scratch
  52. Uni app series (V): hbuilderx runs the uniapp project to the page, views the preview and solves NPM: the error that the file cannot be loaded
  53. Front end interview daily 3 + 1 - day 877
  54. Understand the react lifecycle function
  55. 2020-2021前端面试题合集,web开发敏捷之道
  56. CSS - redraw reflow
  57. 90 lines of code to implement the module packer
  58. Front end internship interview preparation -- react others
  59. 118 page vue sommaire des questions d'entrevue, onglet liste HTML
  60. Vue sommaire des questions d'entrevue, développement intermédiaire du Web, 118 pages