该材料的作者(我们今天发布的翻译是网络开发人员)说,他试图定期检查他使用的工具集。 他这样做是为了了解他是否可以通过解决日常任务来完成其中一些任务。 他最近决定进行一项实验,并在不使用JavaScript框架的情况下创建复杂的前端应用程序。
什么是框架?
如果您不定义细节就尝试定义JavaScript框架,那么事实证明该工具可用于开发复杂的Web应用程序,特别是单页应用程序(SPA)。
在过去,此类应用程序是基于纯JavaScript和jQuery库的功能构建的。 但是,随着前端应用程序复杂性的增加,相应的工具开始出现,使程序员的工作变得更轻松。 例如,这些是React,Angular和Vue。
这些天流行的框架有一些共同点。 因此,相对而言,从Vue到React,大多数前端框架和库都为开发人员提供了以下功能的某种组合:
- 状态和应用程序可视化表示的同步。
- 路由选择
- 模板系统。
- 适合重用的组件。
现代化的开发人员是否需要框架?
本节标题中提出的问题的答案取决于您如何与框架“必要性”的思想联系起来。 我敢肯定,很多人可以说Web开发人员工具包中不需要前端框架,也从来没有必要。 尽管毫无疑问,这些都是非常有用的工具。
实际上,我们的问题可以重新表述为:“这些框架是否有点像“现代jQuery库”? 是否有可能通过其他方法(例如在开发浏览器API的过程中出现在程序员手中的方法)解决它们针对的问题?”
jQuery的实际上,这个问题很难回答,但是可以说,JavaScript的开发,与Web组件一起使用的技术以及用于构建项目的工具使SPA的开发比以前更容易使用框架。
为了探索这个想法,我开发了仅使用JavaScript,标准Web组件和Parcel捆绑器的一页应用程序。 在此过程中,我遇到了一些问题和困难,特别是从这些问题和困难开始,他们开始了解现代JS框架的优势。
同时,当我处理最初的障碍时,我惊讶于用纯JavaScript创建一页应用程序如此容易。
实验应用概述
有问题的应用程序非常简单。 它是配方的电子集合,允许用户创建,查看和编辑配方。 此外,用户可以标记食谱,表明他喜欢食谱,还可以过滤和删除条目。
申请主页配方记录页Web组件
- — . , ,
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, .
, - . - , . , , - , .
- . , , , .
! -, ?