Der Autor des Materials, dessen Übersetzung wir heute veröffentlichen, ein Webentwickler, sagt, dass er versucht, die von ihm verwendeten Tools regelmäßig zu überprüfen. Er tut dies, um zu verstehen, ob er auf einige von ihnen verzichten kann, indem er seine üblichen Aufgaben löst. Kürzlich hat er beschlossen, ein Experiment durchzuführen und eine anspruchsvolle Front-End-Anwendung ohne Verwendung von JavaScript-Frameworks zu erstellen.
Was ist ein Framework?
Wenn Sie versuchen, ein JavaScript-Framework zu definieren, ohne auf Details einzugehen, stellt sich heraus, dass dies ein Tool ist, mit dem komplexe Webanwendungen, insbesondere Single Page Applications (SPA), entwickelt werden können.
In früheren Zeiten wurden solche Anwendungen basierend auf den Funktionen von reinem JavaScript und der jQuery-Bibliothek erstellt. Mit der zunehmenden Komplexität von Front-End-Anwendungen tauchten jedoch entsprechende Tools auf, die Programmierern das Leben erleichtern. Dies sind beispielsweise React, Angular und Vue.
Die heutzutage beliebten Frameworks haben einige Gemeinsamkeiten. Daher bieten die meisten Front-End-Frameworks und -Bibliotheken, relativ gesehen von Vue bis React, dem Entwickler eine bestimmte Kombination der folgenden Funktionen:
- Synchronisation des Status und visuelle Darstellung der Anwendung.
- Routing
- Vorlagensystem.
- Zur Wiederverwendung geeignete Komponenten.
Sind Frameworks fĂĽr einen modernen Entwickler notwendig?
Die Antwort auf die im Titel dieses Abschnitts gestellte Frage hängt davon ab, wie Sie sich auf die Idee der „Notwendigkeit“ von Frameworks beziehen. Ich bin sicher, dass viele sagen können, dass Front-End-Frameworks im Webentwickler-Toolkit nicht erforderlich sind und nie erforderlich waren. Obwohl es unbestreitbar ist, dass dies sehr nützliche Werkzeuge sind.
Tatsächlich kann unsere Frage wie folgt umformuliert werden: „Sind die Frameworks ein bisschen wie eine„ moderne jQuery-Bibliothek “? Ist es möglich, die Probleme, auf die sie abzielen, auf andere Weise zu lösen, beispielsweise durch Probleme, die Programmierern bei der Entwicklung von Browser-APIs zur Verfügung standen? “
jQueryTatsächlich ist diese Frage nicht einfach zu beantworten, aber wir können sagen, dass die Entwicklung von JavaScript, Technologien für die Arbeit mit Webkomponenten und Tools zum Erstellen von Projekten die Entwicklung von SPA ohne die Verwendung von Frameworks einfacher als je zuvor gemacht hat.
Um diese Idee zu untersuchen, habe ich eine einseitige Anwendung entwickelt, die nur JavaScript, Standard-Webkomponenten und den Paketbündler verwendet. Dabei bin ich auf einige Probleme und Schwierigkeiten gestoßen, bei denen Sie besonders deutlich die Stärken moderner JS-Frameworks erkennen.
Gleichzeitig war ich überrascht, wie einfach es ist, eine einseitige Anwendung in reinem JavaScript zu erstellen, sobald ich mich mit den anfänglichen Hindernissen befasst hatte.
Experimentelle AnwendungsĂĽbersicht
Die betreffende Anwendung ist recht einfach. Es ist eine elektronische Sammlung von Rezepten, mit der der Benutzer Rezepte erstellen, anzeigen und bearbeiten kann. Der Benutzer kann außerdem Rezepte markieren, um anzuzeigen, dass er sie mag, und Einträge filtern und löschen.
AnwendungshomepageRezeptaufzeichnungsseiteWebkomponenten
- — . , ,
HTMLElement
(
HTMLParagraphElement
, ) .
, - , ,
connectedCallback
,
disconnectedCallback
,
attributeChangedCallback
.
recipe-item
, .
import template from './recipe.html'
import DATA_SERVICE from '../../utils/data'
export default class Recipe extends HTMLElement {
constructor () {
// DOM, recipe DATA_SERVICE()
super()
this._shadowRoot = this.attachShadow({ mode: 'open' })
this._recipe = null
this.ds = new DATA_SERVICE()
}
connectedCallback () {
// html-
this._shadowRoot.innerHTML = template
// delete
this._shadowRoot
.querySelector('.delete')
.addEventListener('click', () => this._delete())
}
_render (title) {
// ,
this._shadowRoot.querySelector('.recipe-title').innerHTML = title
this._shadowRoot.querySelector('.favorite').innerHTML = this._recipe
.favorite
? 'Unfavorite'
: 'Favorite'
}
_delete () {
//
try {
await this.ds.deleteRecipe(this._recipe.id)
} catch (e) {
console.error(e)
alert(
'Sorry, there was a problem deleting the recipe. Please, try again.'
)
}
}
get recipe () {
//
return this._recipe
}
set recipe (recipe = {}) {
// , render
this._recipe = recipe
this._render(this._recipe.title)
}
}
window.customElements.define('recipe-item', Recipe)
. , , .
, , npm- Vanilla JS Router. ,
API History, , - 100 . , - , (route guard).
import './components/error/error'
import content404 from './components/404/404.html'
import DATA_SERVICE from './utils/data'
const ds = new DATA_SERVICE()
// , SPA
const $el = document.getElementById('app')
//
const home = async () => {
await import('./components/recipe/recipe')
await import('./components/recipe-list/recipe-list')
await import('./components/modal/modal.js')
$el.innerHTML = `<recipe-list></recipe-list>`
}
const create = async () => {
await import('./components/create-recipe/create-recipe')
$el.innerHTML = `<create-recipe></create-recipe>`
}
const edit = async () => {
await import('./components/edit-recipe/edit-recipe')
$el.innerHTML = `<edit-recipe></edit-recipe>`
}
const error404 = async () => {
$el.innerHTML = content404
}
//
// id
const routes = {
'/': home,
'/create': create,
'/error': error404,
'/edit': async function (params) {
const id = params.get('id')
const recipe = await ds.getRecipe(id)
await edit()
$el.querySelector('edit-recipe').recipe = recipe
}
}
// onpopstate URL
// - /error
window.onpopstate = async () => {
const url = new URL(
window.location.pathname + window.location.search,
window.location.origin
)
if (routes[window.location.pathname]) {
await routes[window.location.pathname](url.searchParams)
} else routes['/error']()
}
//
let onNavItemClick = async pathName => {
const url = new URL(pathName, window.location.origin)
const params = url.searchParams
if (routes[url.pathname]) {
window.history.pushState({}, pathName, window.location.origin + pathName)
await routes[url.pathname](params)
} else {
window.history.pushState({}, '404', window.location.origin + '/404')
routes['/error']()
}
}
//
;(async () => {
const url = new URL(
window.location.pathname + window.location.search,
window.location.origin
)
if (routes[window.location.pathname]) {
await routes[window.location.pathname](url.searchParams)
} else routes['/error']()
})()
// onNavItemClick()
const router = {
onNavItemClick,
routes
}
export { router }
, . , , , — .
JS
, , . , JS-.
â–Ť
-, , , , . , -
Fronteers Conference 2011. . . , HTML-
, .
â–Ť
-. , —
skatejs ssr web-component-tester. . , -, .
â–Ť
querySelector()- , , . Angular .
, — . , , , .
â–Ť Shadow DOM
Shadow DOM. . — , . , , , , , - . , . , ,
.
â–Ť DOM
Angular React , DOM. , .
Angular University: «Angular DOM, , HTML- ».
Angular, , jQuery, DOM. , HTML-, , DOM, . , HTML-. Virtual DOM , , .
JS . .
â–Ť
-, JS, ( «») , , . , , , Angular-.
Angular-,â–Ť
- CLI, , , . , , -. , , , , , , .
â–Ť
, , . . — . , , , , , . , , , , DOM. .
, React Angular, , . . , - React
shouldUpdate()
onPush
Angular.
â–Ť
— . , . . , , , . , .
Parcel. , , Webpack, , , ,
, Parcel , .
React, Vue Angular . «». , React «», Vue — « ».
Stencil Polymer? , , , , - , . , . , -.
, SPA - . , , , .
?
, - , , « ». , , , , , , . , , , , .
, — , , . « » . , , . , , , -.
, , , , , , , . — . , , , . — , -, , jQuery, .
, - . - , . , , - , .
- . , , , .
! -, ?